##// END OF EJS Templates
rebase: specify custom conflict marker labels for rebase (BC)...
Durham Goode -
r21526:3b40e32e default
parent child Browse files
Show More
@@ -1,960 +1,961 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 http://mercurial.selenic.com/wiki/RebaseExtension
14 http://mercurial.selenic.com/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 nullmerge = -2
25 nullmerge = -2
26 revignored = -3
26 revignored = -3
27
27
28 cmdtable = {}
28 cmdtable = {}
29 command = cmdutil.command(cmdtable)
29 command = cmdutil.command(cmdtable)
30 testedwith = 'internal'
30 testedwith = 'internal'
31
31
32 def _savegraft(ctx, extra):
32 def _savegraft(ctx, extra):
33 s = ctx.extra().get('source', None)
33 s = ctx.extra().get('source', None)
34 if s is not None:
34 if s is not None:
35 extra['source'] = s
35 extra['source'] = s
36
36
37 def _savebranch(ctx, extra):
37 def _savebranch(ctx, extra):
38 extra['branch'] = ctx.branch()
38 extra['branch'] = ctx.branch()
39
39
40 def _makeextrafn(copiers):
40 def _makeextrafn(copiers):
41 """make an extrafn out of the given copy-functions.
41 """make an extrafn out of the given copy-functions.
42
42
43 A copy function takes a context and an extra dict, and mutates the
43 A copy function takes a context and an extra dict, and mutates the
44 extra dict as needed based on the given context.
44 extra dict as needed based on the given context.
45 """
45 """
46 def extrafn(ctx, extra):
46 def extrafn(ctx, extra):
47 for c in copiers:
47 for c in copiers:
48 c(ctx, extra)
48 c(ctx, extra)
49 return extrafn
49 return extrafn
50
50
51 @command('rebase',
51 @command('rebase',
52 [('s', 'source', '',
52 [('s', 'source', '',
53 _('rebase from the specified changeset'), _('REV')),
53 _('rebase from the specified changeset'), _('REV')),
54 ('b', 'base', '',
54 ('b', 'base', '',
55 _('rebase from the base of the specified changeset '
55 _('rebase from the base of the specified changeset '
56 '(up to greatest common ancestor of base and dest)'),
56 '(up to greatest common ancestor of base and dest)'),
57 _('REV')),
57 _('REV')),
58 ('r', 'rev', [],
58 ('r', 'rev', [],
59 _('rebase these revisions'),
59 _('rebase these revisions'),
60 _('REV')),
60 _('REV')),
61 ('d', 'dest', '',
61 ('d', 'dest', '',
62 _('rebase onto the specified changeset'), _('REV')),
62 _('rebase onto the specified changeset'), _('REV')),
63 ('', 'collapse', False, _('collapse the rebased changesets')),
63 ('', 'collapse', False, _('collapse the rebased changesets')),
64 ('m', 'message', '',
64 ('m', 'message', '',
65 _('use text as collapse commit message'), _('TEXT')),
65 _('use text as collapse commit message'), _('TEXT')),
66 ('e', 'edit', False, _('invoke editor on commit messages')),
66 ('e', 'edit', False, _('invoke editor on commit messages')),
67 ('l', 'logfile', '',
67 ('l', 'logfile', '',
68 _('read collapse commit message from file'), _('FILE')),
68 _('read collapse commit message from file'), _('FILE')),
69 ('', 'keep', False, _('keep original changesets')),
69 ('', 'keep', False, _('keep original changesets')),
70 ('', 'keepbranches', False, _('keep original branch names')),
70 ('', 'keepbranches', False, _('keep original branch names')),
71 ('D', 'detach', False, _('(DEPRECATED)')),
71 ('D', 'detach', False, _('(DEPRECATED)')),
72 ('t', 'tool', '', _('specify merge tool')),
72 ('t', 'tool', '', _('specify merge tool')),
73 ('c', 'continue', False, _('continue an interrupted rebase')),
73 ('c', 'continue', False, _('continue an interrupted rebase')),
74 ('a', 'abort', False, _('abort an interrupted rebase'))] +
74 ('a', 'abort', False, _('abort an interrupted rebase'))] +
75 templateopts,
75 templateopts,
76 _('[-s REV | -b REV] [-d REV] [OPTION]'))
76 _('[-s REV | -b REV] [-d REV] [OPTION]'))
77 def rebase(ui, repo, **opts):
77 def rebase(ui, repo, **opts):
78 """move changeset (and descendants) to a different branch
78 """move changeset (and descendants) to a different branch
79
79
80 Rebase uses repeated merging to graft changesets from one part of
80 Rebase uses repeated merging to graft changesets from one part of
81 history (the source) onto another (the destination). This can be
81 history (the source) onto another (the destination). This can be
82 useful for linearizing *local* changes relative to a master
82 useful for linearizing *local* changes relative to a master
83 development tree.
83 development tree.
84
84
85 You should not rebase changesets that have already been shared
85 You should not rebase changesets that have already been shared
86 with others. Doing so will force everybody else to perform the
86 with others. Doing so will force everybody else to perform the
87 same rebase or they will end up with duplicated changesets after
87 same rebase or they will end up with duplicated changesets after
88 pulling in your rebased changesets.
88 pulling in your rebased changesets.
89
89
90 In its default configuration, Mercurial will prevent you from
90 In its default configuration, Mercurial will prevent you from
91 rebasing published changes. See :hg:`help phases` for details.
91 rebasing published changes. See :hg:`help phases` for details.
92
92
93 If you don't specify a destination changeset (``-d/--dest``),
93 If you don't specify a destination changeset (``-d/--dest``),
94 rebase uses the current branch tip as the destination. (The
94 rebase uses the current branch tip as the destination. (The
95 destination changeset is not modified by rebasing, but new
95 destination changeset is not modified by rebasing, but new
96 changesets are added as its descendants.)
96 changesets are added as its descendants.)
97
97
98 You can specify which changesets to rebase in two ways: as a
98 You can specify which changesets to rebase in two ways: as a
99 "source" changeset or as a "base" changeset. Both are shorthand
99 "source" changeset or as a "base" changeset. Both are shorthand
100 for a topologically related set of changesets (the "source
100 for a topologically related set of changesets (the "source
101 branch"). If you specify source (``-s/--source``), rebase will
101 branch"). If you specify source (``-s/--source``), rebase will
102 rebase that changeset and all of its descendants onto dest. If you
102 rebase that changeset and all of its descendants onto dest. If you
103 specify base (``-b/--base``), rebase will select ancestors of base
103 specify base (``-b/--base``), rebase will select ancestors of base
104 back to but not including the common ancestor with dest. Thus,
104 back to but not including the common ancestor with dest. Thus,
105 ``-b`` is less precise but more convenient than ``-s``: you can
105 ``-b`` is less precise but more convenient than ``-s``: you can
106 specify any changeset in the source branch, and rebase will select
106 specify any changeset in the source branch, and rebase will select
107 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
107 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
108 uses the parent of the working directory as the base.
108 uses the parent of the working directory as the base.
109
109
110 For advanced usage, a third way is available through the ``--rev``
110 For advanced usage, a third way is available through the ``--rev``
111 option. It allows you to specify an arbitrary set of changesets to
111 option. It allows you to specify an arbitrary set of changesets to
112 rebase. Descendants of revs you specify with this option are not
112 rebase. Descendants of revs you specify with this option are not
113 automatically included in the rebase.
113 automatically included in the rebase.
114
114
115 By default, rebase recreates the changesets in the source branch
115 By default, rebase recreates the changesets in the source branch
116 as descendants of dest and then destroys the originals. Use
116 as descendants of dest and then destroys the originals. Use
117 ``--keep`` to preserve the original source changesets. Some
117 ``--keep`` to preserve the original source changesets. Some
118 changesets in the source branch (e.g. merges from the destination
118 changesets in the source branch (e.g. merges from the destination
119 branch) may be dropped if they no longer contribute any change.
119 branch) may be dropped if they no longer contribute any change.
120
120
121 One result of the rules for selecting the destination changeset
121 One result of the rules for selecting the destination changeset
122 and source branch is that, unlike ``merge``, rebase will do
122 and source branch is that, unlike ``merge``, rebase will do
123 nothing if you are at the branch tip of a named branch
123 nothing if you are at the branch tip of a named branch
124 with two heads. You need to explicitly specify source and/or
124 with two heads. You need to explicitly specify source and/or
125 destination (or ``update`` to the other head, if it's the head of
125 destination (or ``update`` to the other head, if it's the head of
126 the intended source branch).
126 the intended source branch).
127
127
128 If a rebase is interrupted to manually resolve a merge, it can be
128 If a rebase is interrupted to manually resolve a merge, it can be
129 continued with --continue/-c or aborted with --abort/-a.
129 continued with --continue/-c or aborted with --abort/-a.
130
130
131 Returns 0 on success, 1 if nothing to rebase or there are
131 Returns 0 on success, 1 if nothing to rebase or there are
132 unresolved conflicts.
132 unresolved conflicts.
133 """
133 """
134 originalwd = target = None
134 originalwd = target = None
135 activebookmark = None
135 activebookmark = None
136 external = nullrev
136 external = nullrev
137 state = {}
137 state = {}
138 skipped = set()
138 skipped = set()
139 targetancestors = set()
139 targetancestors = set()
140
140
141 editor = cmdutil.getcommiteditor(**opts)
141 editor = cmdutil.getcommiteditor(**opts)
142
142
143 lock = wlock = None
143 lock = wlock = None
144 try:
144 try:
145 wlock = repo.wlock()
145 wlock = repo.wlock()
146 lock = repo.lock()
146 lock = repo.lock()
147
147
148 # Validate input and define rebasing points
148 # Validate input and define rebasing points
149 destf = opts.get('dest', None)
149 destf = opts.get('dest', None)
150 srcf = opts.get('source', None)
150 srcf = opts.get('source', None)
151 basef = opts.get('base', None)
151 basef = opts.get('base', None)
152 revf = opts.get('rev', [])
152 revf = opts.get('rev', [])
153 contf = opts.get('continue')
153 contf = opts.get('continue')
154 abortf = opts.get('abort')
154 abortf = opts.get('abort')
155 collapsef = opts.get('collapse', False)
155 collapsef = opts.get('collapse', False)
156 collapsemsg = cmdutil.logmessage(ui, opts)
156 collapsemsg = cmdutil.logmessage(ui, opts)
157 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
157 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
158 extrafns = [_savegraft]
158 extrafns = [_savegraft]
159 if e:
159 if e:
160 extrafns = [e]
160 extrafns = [e]
161 keepf = opts.get('keep', False)
161 keepf = opts.get('keep', False)
162 keepbranchesf = opts.get('keepbranches', False)
162 keepbranchesf = opts.get('keepbranches', False)
163 # keepopen is not meant for use on the command line, but by
163 # keepopen is not meant for use on the command line, but by
164 # other extensions
164 # other extensions
165 keepopen = opts.get('keepopen', False)
165 keepopen = opts.get('keepopen', False)
166
166
167 if collapsemsg and not collapsef:
167 if collapsemsg and not collapsef:
168 raise util.Abort(
168 raise util.Abort(
169 _('message can only be specified with collapse'))
169 _('message can only be specified with collapse'))
170
170
171 if contf or abortf:
171 if contf or abortf:
172 if contf and abortf:
172 if contf and abortf:
173 raise util.Abort(_('cannot use both abort and continue'))
173 raise util.Abort(_('cannot use both abort and continue'))
174 if collapsef:
174 if collapsef:
175 raise util.Abort(
175 raise util.Abort(
176 _('cannot use collapse with continue or abort'))
176 _('cannot use collapse with continue or abort'))
177 if srcf or basef or destf:
177 if srcf or basef or destf:
178 raise util.Abort(
178 raise util.Abort(
179 _('abort and continue do not allow specifying revisions'))
179 _('abort and continue do not allow specifying revisions'))
180 if opts.get('tool', False):
180 if opts.get('tool', False):
181 ui.warn(_('tool option will be ignored\n'))
181 ui.warn(_('tool option will be ignored\n'))
182
182
183 try:
183 try:
184 (originalwd, target, state, skipped, collapsef, keepf,
184 (originalwd, target, state, skipped, collapsef, keepf,
185 keepbranchesf, external, activebookmark) = restorestatus(repo)
185 keepbranchesf, external, activebookmark) = restorestatus(repo)
186 except error.RepoLookupError:
186 except error.RepoLookupError:
187 if abortf:
187 if abortf:
188 clearstatus(repo)
188 clearstatus(repo)
189 repo.ui.warn(_('rebase aborted (no revision is removed,'
189 repo.ui.warn(_('rebase aborted (no revision is removed,'
190 ' only broken state is cleared)\n'))
190 ' only broken state is cleared)\n'))
191 return 0
191 return 0
192 else:
192 else:
193 msg = _('cannot continue inconsistent rebase')
193 msg = _('cannot continue inconsistent rebase')
194 hint = _('use "hg rebase --abort" to clear broken state')
194 hint = _('use "hg rebase --abort" to clear broken state')
195 raise util.Abort(msg, hint=hint)
195 raise util.Abort(msg, hint=hint)
196 if abortf:
196 if abortf:
197 return abort(repo, originalwd, target, state)
197 return abort(repo, originalwd, target, state)
198 else:
198 else:
199 if srcf and basef:
199 if srcf and basef:
200 raise util.Abort(_('cannot specify both a '
200 raise util.Abort(_('cannot specify both a '
201 'source and a base'))
201 'source and a base'))
202 if revf and basef:
202 if revf and basef:
203 raise util.Abort(_('cannot specify both a '
203 raise util.Abort(_('cannot specify both a '
204 'revision and a base'))
204 'revision and a base'))
205 if revf and srcf:
205 if revf and srcf:
206 raise util.Abort(_('cannot specify both a '
206 raise util.Abort(_('cannot specify both a '
207 'revision and a source'))
207 'revision and a source'))
208
208
209 cmdutil.checkunfinished(repo)
209 cmdutil.checkunfinished(repo)
210 cmdutil.bailifchanged(repo)
210 cmdutil.bailifchanged(repo)
211
211
212 if not destf:
212 if not destf:
213 # Destination defaults to the latest revision in the
213 # Destination defaults to the latest revision in the
214 # current branch
214 # current branch
215 branch = repo[None].branch()
215 branch = repo[None].branch()
216 dest = repo[branch]
216 dest = repo[branch]
217 else:
217 else:
218 dest = scmutil.revsingle(repo, destf)
218 dest = scmutil.revsingle(repo, destf)
219
219
220 if revf:
220 if revf:
221 rebaseset = scmutil.revrange(repo, revf)
221 rebaseset = scmutil.revrange(repo, revf)
222 if not rebaseset:
222 if not rebaseset:
223 ui.status(_('empty "rev" revision set - '
223 ui.status(_('empty "rev" revision set - '
224 'nothing to rebase\n'))
224 'nothing to rebase\n'))
225 return 1
225 return 1
226 elif srcf:
226 elif srcf:
227 src = scmutil.revrange(repo, [srcf])
227 src = scmutil.revrange(repo, [srcf])
228 if not src:
228 if not src:
229 ui.status(_('empty "source" revision set - '
229 ui.status(_('empty "source" revision set - '
230 'nothing to rebase\n'))
230 'nothing to rebase\n'))
231 return 1
231 return 1
232 rebaseset = repo.revs('(%ld)::', src)
232 rebaseset = repo.revs('(%ld)::', src)
233 assert rebaseset
233 assert rebaseset
234 else:
234 else:
235 base = scmutil.revrange(repo, [basef or '.'])
235 base = scmutil.revrange(repo, [basef or '.'])
236 if not base:
236 if not base:
237 ui.status(_('empty "base" revision set - '
237 ui.status(_('empty "base" revision set - '
238 "can't compute rebase set\n"))
238 "can't compute rebase set\n"))
239 return 1
239 return 1
240 rebaseset = repo.revs(
240 rebaseset = repo.revs(
241 '(children(ancestor(%ld, %d)) and ::(%ld))::',
241 '(children(ancestor(%ld, %d)) and ::(%ld))::',
242 base, dest, base)
242 base, dest, base)
243 if not rebaseset:
243 if not rebaseset:
244 if base == [dest.rev()]:
244 if base == [dest.rev()]:
245 if basef:
245 if basef:
246 ui.status(_('nothing to rebase - %s is both "base"'
246 ui.status(_('nothing to rebase - %s is both "base"'
247 ' and destination\n') % dest)
247 ' and destination\n') % dest)
248 else:
248 else:
249 ui.status(_('nothing to rebase - working directory '
249 ui.status(_('nothing to rebase - working directory '
250 'parent is also destination\n'))
250 'parent is also destination\n'))
251 elif not repo.revs('%ld - ::%d', base, dest):
251 elif not repo.revs('%ld - ::%d', base, dest):
252 if basef:
252 if basef:
253 ui.status(_('nothing to rebase - "base" %s is '
253 ui.status(_('nothing to rebase - "base" %s is '
254 'already an ancestor of destination '
254 'already an ancestor of destination '
255 '%s\n') %
255 '%s\n') %
256 ('+'.join(str(repo[r]) for r in base),
256 ('+'.join(str(repo[r]) for r in base),
257 dest))
257 dest))
258 else:
258 else:
259 ui.status(_('nothing to rebase - working '
259 ui.status(_('nothing to rebase - working '
260 'directory parent is already an '
260 'directory parent is already an '
261 'ancestor of destination %s\n') % dest)
261 'ancestor of destination %s\n') % dest)
262 else: # can it happen?
262 else: # can it happen?
263 ui.status(_('nothing to rebase from %s to %s\n') %
263 ui.status(_('nothing to rebase from %s to %s\n') %
264 ('+'.join(str(repo[r]) for r in base), dest))
264 ('+'.join(str(repo[r]) for r in base), dest))
265 return 1
265 return 1
266
266
267 if (not (keepf or obsolete._enabled)
267 if (not (keepf or obsolete._enabled)
268 and repo.revs('first(children(%ld) - %ld)',
268 and repo.revs('first(children(%ld) - %ld)',
269 rebaseset, rebaseset)):
269 rebaseset, rebaseset)):
270 raise util.Abort(
270 raise util.Abort(
271 _("can't remove original changesets with"
271 _("can't remove original changesets with"
272 " unrebased descendants"),
272 " unrebased descendants"),
273 hint=_('use --keep to keep original changesets'))
273 hint=_('use --keep to keep original changesets'))
274
274
275 result = buildstate(repo, dest, rebaseset, collapsef)
275 result = buildstate(repo, dest, rebaseset, collapsef)
276 if not result:
276 if not result:
277 # Empty state built, nothing to rebase
277 # Empty state built, nothing to rebase
278 ui.status(_('nothing to rebase\n'))
278 ui.status(_('nothing to rebase\n'))
279 return 1
279 return 1
280
280
281 root = min(rebaseset)
281 root = min(rebaseset)
282 if not keepf and not repo[root].mutable():
282 if not keepf and not repo[root].mutable():
283 raise util.Abort(_("can't rebase immutable changeset %s")
283 raise util.Abort(_("can't rebase immutable changeset %s")
284 % repo[root],
284 % repo[root],
285 hint=_('see hg help phases for details'))
285 hint=_('see hg help phases for details'))
286
286
287 originalwd, target, state = result
287 originalwd, target, state = result
288 if collapsef:
288 if collapsef:
289 targetancestors = repo.changelog.ancestors([target],
289 targetancestors = repo.changelog.ancestors([target],
290 inclusive=True)
290 inclusive=True)
291 external = externalparent(repo, state, targetancestors)
291 external = externalparent(repo, state, targetancestors)
292
292
293 if dest.closesbranch() and not keepbranchesf:
293 if dest.closesbranch() and not keepbranchesf:
294 ui.status(_('reopening closed branch head %s\n') % dest)
294 ui.status(_('reopening closed branch head %s\n') % dest)
295
295
296 if keepbranchesf:
296 if keepbranchesf:
297 # insert _savebranch at the start of extrafns so if
297 # insert _savebranch at the start of extrafns so if
298 # there's a user-provided extrafn it can clobber branch if
298 # there's a user-provided extrafn it can clobber branch if
299 # desired
299 # desired
300 extrafns.insert(0, _savebranch)
300 extrafns.insert(0, _savebranch)
301 if collapsef:
301 if collapsef:
302 branches = set()
302 branches = set()
303 for rev in state:
303 for rev in state:
304 branches.add(repo[rev].branch())
304 branches.add(repo[rev].branch())
305 if len(branches) > 1:
305 if len(branches) > 1:
306 raise util.Abort(_('cannot collapse multiple named '
306 raise util.Abort(_('cannot collapse multiple named '
307 'branches'))
307 'branches'))
308
308
309 # Rebase
309 # Rebase
310 if not targetancestors:
310 if not targetancestors:
311 targetancestors = repo.changelog.ancestors([target], inclusive=True)
311 targetancestors = repo.changelog.ancestors([target], inclusive=True)
312
312
313 # Keep track of the current bookmarks in order to reset them later
313 # Keep track of the current bookmarks in order to reset them later
314 currentbookmarks = repo._bookmarks.copy()
314 currentbookmarks = repo._bookmarks.copy()
315 activebookmark = activebookmark or repo._bookmarkcurrent
315 activebookmark = activebookmark or repo._bookmarkcurrent
316 if activebookmark:
316 if activebookmark:
317 bookmarks.unsetcurrent(repo)
317 bookmarks.unsetcurrent(repo)
318
318
319 extrafn = _makeextrafn(extrafns)
319 extrafn = _makeextrafn(extrafns)
320
320
321 sortedstate = sorted(state)
321 sortedstate = sorted(state)
322 total = len(sortedstate)
322 total = len(sortedstate)
323 pos = 0
323 pos = 0
324 for rev in sortedstate:
324 for rev in sortedstate:
325 pos += 1
325 pos += 1
326 if state[rev] == -1:
326 if state[rev] == -1:
327 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
327 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
328 _('changesets'), total)
328 _('changesets'), total)
329 p1, p2 = defineparents(repo, rev, target, state,
329 p1, p2 = defineparents(repo, rev, target, state,
330 targetancestors)
330 targetancestors)
331 storestatus(repo, originalwd, target, state, collapsef, keepf,
331 storestatus(repo, originalwd, target, state, collapsef, keepf,
332 keepbranchesf, external, activebookmark)
332 keepbranchesf, external, activebookmark)
333 if len(repo.parents()) == 2:
333 if len(repo.parents()) == 2:
334 repo.ui.debug('resuming interrupted rebase\n')
334 repo.ui.debug('resuming interrupted rebase\n')
335 else:
335 else:
336 try:
336 try:
337 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
337 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
338 'rebase')
338 'rebase')
339 stats = rebasenode(repo, rev, p1, state, collapsef)
339 stats = rebasenode(repo, rev, p1, state, collapsef)
340 if stats and stats[3] > 0:
340 if stats and stats[3] > 0:
341 raise error.InterventionRequired(
341 raise error.InterventionRequired(
342 _('unresolved conflicts (see hg '
342 _('unresolved conflicts (see hg '
343 'resolve, then hg rebase --continue)'))
343 'resolve, then hg rebase --continue)'))
344 finally:
344 finally:
345 ui.setconfig('ui', 'forcemerge', '', 'rebase')
345 ui.setconfig('ui', 'forcemerge', '', 'rebase')
346 cmdutil.duplicatecopies(repo, rev, target)
346 cmdutil.duplicatecopies(repo, rev, target)
347 if not collapsef:
347 if not collapsef:
348 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
348 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
349 editor=editor)
349 editor=editor)
350 else:
350 else:
351 # Skip commit if we are collapsing
351 # Skip commit if we are collapsing
352 repo.setparents(repo[p1].node())
352 repo.setparents(repo[p1].node())
353 newrev = None
353 newrev = None
354 # Update the state
354 # Update the state
355 if newrev is not None:
355 if newrev is not None:
356 state[rev] = repo[newrev].rev()
356 state[rev] = repo[newrev].rev()
357 else:
357 else:
358 if not collapsef:
358 if not collapsef:
359 ui.note(_('no changes, revision %d skipped\n') % rev)
359 ui.note(_('no changes, revision %d skipped\n') % rev)
360 ui.debug('next revision set to %s\n' % p1)
360 ui.debug('next revision set to %s\n' % p1)
361 skipped.add(rev)
361 skipped.add(rev)
362 state[rev] = p1
362 state[rev] = p1
363
363
364 ui.progress(_('rebasing'), None)
364 ui.progress(_('rebasing'), None)
365 ui.note(_('rebase merging completed\n'))
365 ui.note(_('rebase merging completed\n'))
366
366
367 if collapsef and not keepopen:
367 if collapsef and not keepopen:
368 p1, p2 = defineparents(repo, min(state), target,
368 p1, p2 = defineparents(repo, min(state), target,
369 state, targetancestors)
369 state, targetancestors)
370 if collapsemsg:
370 if collapsemsg:
371 commitmsg = collapsemsg
371 commitmsg = collapsemsg
372 else:
372 else:
373 commitmsg = 'Collapsed revision'
373 commitmsg = 'Collapsed revision'
374 for rebased in state:
374 for rebased in state:
375 if rebased not in skipped and state[rebased] > nullmerge:
375 if rebased not in skipped and state[rebased] > nullmerge:
376 commitmsg += '\n* %s' % repo[rebased].description()
376 commitmsg += '\n* %s' % repo[rebased].description()
377 editor = cmdutil.getcommiteditor(edit=True)
377 editor = cmdutil.getcommiteditor(edit=True)
378 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
378 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
379 extrafn=extrafn, editor=editor)
379 extrafn=extrafn, editor=editor)
380 for oldrev in state.iterkeys():
380 for oldrev in state.iterkeys():
381 if state[oldrev] > nullmerge:
381 if state[oldrev] > nullmerge:
382 state[oldrev] = newrev
382 state[oldrev] = newrev
383
383
384 if 'qtip' in repo.tags():
384 if 'qtip' in repo.tags():
385 updatemq(repo, state, skipped, **opts)
385 updatemq(repo, state, skipped, **opts)
386
386
387 if currentbookmarks:
387 if currentbookmarks:
388 # Nodeids are needed to reset bookmarks
388 # Nodeids are needed to reset bookmarks
389 nstate = {}
389 nstate = {}
390 for k, v in state.iteritems():
390 for k, v in state.iteritems():
391 if v > nullmerge:
391 if v > nullmerge:
392 nstate[repo[k].node()] = repo[v].node()
392 nstate[repo[k].node()] = repo[v].node()
393 # XXX this is the same as dest.node() for the non-continue path --
393 # XXX this is the same as dest.node() for the non-continue path --
394 # this should probably be cleaned up
394 # this should probably be cleaned up
395 targetnode = repo[target].node()
395 targetnode = repo[target].node()
396
396
397 # restore original working directory
397 # restore original working directory
398 # (we do this before stripping)
398 # (we do this before stripping)
399 newwd = state.get(originalwd, originalwd)
399 newwd = state.get(originalwd, originalwd)
400 if newwd not in [c.rev() for c in repo[None].parents()]:
400 if newwd not in [c.rev() for c in repo[None].parents()]:
401 ui.note(_("update back to initial working directory parent\n"))
401 ui.note(_("update back to initial working directory parent\n"))
402 hg.updaterepo(repo, newwd, False)
402 hg.updaterepo(repo, newwd, False)
403
403
404 if not keepf:
404 if not keepf:
405 collapsedas = None
405 collapsedas = None
406 if collapsef:
406 if collapsef:
407 collapsedas = newrev
407 collapsedas = newrev
408 clearrebased(ui, repo, state, skipped, collapsedas)
408 clearrebased(ui, repo, state, skipped, collapsedas)
409
409
410 if currentbookmarks:
410 if currentbookmarks:
411 updatebookmarks(repo, targetnode, nstate, currentbookmarks)
411 updatebookmarks(repo, targetnode, nstate, currentbookmarks)
412 if activebookmark not in repo._bookmarks:
412 if activebookmark not in repo._bookmarks:
413 # active bookmark was divergent one and has been deleted
413 # active bookmark was divergent one and has been deleted
414 activebookmark = None
414 activebookmark = None
415
415
416 clearstatus(repo)
416 clearstatus(repo)
417 ui.note(_("rebase completed\n"))
417 ui.note(_("rebase completed\n"))
418 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
418 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
419 if skipped:
419 if skipped:
420 ui.note(_("%d revisions have been skipped\n") % len(skipped))
420 ui.note(_("%d revisions have been skipped\n") % len(skipped))
421
421
422 if (activebookmark and
422 if (activebookmark and
423 repo['.'].node() == repo._bookmarks[activebookmark]):
423 repo['.'].node() == repo._bookmarks[activebookmark]):
424 bookmarks.setcurrent(repo, activebookmark)
424 bookmarks.setcurrent(repo, activebookmark)
425
425
426 finally:
426 finally:
427 release(lock, wlock)
427 release(lock, wlock)
428
428
429 def externalparent(repo, state, targetancestors):
429 def externalparent(repo, state, targetancestors):
430 """Return the revision that should be used as the second parent
430 """Return the revision that should be used as the second parent
431 when the revisions in state is collapsed on top of targetancestors.
431 when the revisions in state is collapsed on top of targetancestors.
432 Abort if there is more than one parent.
432 Abort if there is more than one parent.
433 """
433 """
434 parents = set()
434 parents = set()
435 source = min(state)
435 source = min(state)
436 for rev in state:
436 for rev in state:
437 if rev == source:
437 if rev == source:
438 continue
438 continue
439 for p in repo[rev].parents():
439 for p in repo[rev].parents():
440 if (p.rev() not in state
440 if (p.rev() not in state
441 and p.rev() not in targetancestors):
441 and p.rev() not in targetancestors):
442 parents.add(p.rev())
442 parents.add(p.rev())
443 if not parents:
443 if not parents:
444 return nullrev
444 return nullrev
445 if len(parents) == 1:
445 if len(parents) == 1:
446 return parents.pop()
446 return parents.pop()
447 raise util.Abort(_('unable to collapse on top of %s, there is more '
447 raise util.Abort(_('unable to collapse on top of %s, there is more '
448 'than one external parent: %s') %
448 'than one external parent: %s') %
449 (max(targetancestors),
449 (max(targetancestors),
450 ', '.join(str(p) for p in sorted(parents))))
450 ', '.join(str(p) for p in sorted(parents))))
451
451
452 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
452 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
453 'Commit the changes and store useful information in extra'
453 'Commit the changes and store useful information in extra'
454 try:
454 try:
455 repo.setparents(repo[p1].node(), repo[p2].node())
455 repo.setparents(repo[p1].node(), repo[p2].node())
456 ctx = repo[rev]
456 ctx = repo[rev]
457 if commitmsg is None:
457 if commitmsg is None:
458 commitmsg = ctx.description()
458 commitmsg = ctx.description()
459 extra = {'rebase_source': ctx.hex()}
459 extra = {'rebase_source': ctx.hex()}
460 if extrafn:
460 if extrafn:
461 extrafn(ctx, extra)
461 extrafn(ctx, extra)
462 # Commit might fail if unresolved files exist
462 # Commit might fail if unresolved files exist
463 newrev = repo.commit(text=commitmsg, user=ctx.user(),
463 newrev = repo.commit(text=commitmsg, user=ctx.user(),
464 date=ctx.date(), extra=extra, editor=editor)
464 date=ctx.date(), extra=extra, editor=editor)
465 repo.dirstate.setbranch(repo[newrev].branch())
465 repo.dirstate.setbranch(repo[newrev].branch())
466 targetphase = max(ctx.phase(), phases.draft)
466 targetphase = max(ctx.phase(), phases.draft)
467 # retractboundary doesn't overwrite upper phase inherited from parent
467 # retractboundary doesn't overwrite upper phase inherited from parent
468 newnode = repo[newrev].node()
468 newnode = repo[newrev].node()
469 if newnode:
469 if newnode:
470 phases.retractboundary(repo, targetphase, [newnode])
470 phases.retractboundary(repo, targetphase, [newnode])
471 return newrev
471 return newrev
472 except util.Abort:
472 except util.Abort:
473 # Invalidate the previous setparents
473 # Invalidate the previous setparents
474 repo.dirstate.invalidate()
474 repo.dirstate.invalidate()
475 raise
475 raise
476
476
477 def rebasenode(repo, rev, p1, state, collapse):
477 def rebasenode(repo, rev, p1, state, collapse):
478 'Rebase a single revision'
478 'Rebase a single revision'
479 # Merge phase
479 # Merge phase
480 # Update to target and merge it with local
480 # Update to target and merge it with local
481 if repo['.'].rev() != repo[p1].rev():
481 if repo['.'].rev() != repo[p1].rev():
482 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
482 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
483 merge.update(repo, p1, False, True, False)
483 merge.update(repo, p1, False, True, False)
484 else:
484 else:
485 repo.ui.debug(" already in target\n")
485 repo.ui.debug(" already in target\n")
486 repo.dirstate.write()
486 repo.dirstate.write()
487 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
487 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
488 if repo[rev].rev() == repo[min(state)].rev():
488 if repo[rev].rev() == repo[min(state)].rev():
489 # Case (1) initial changeset of a non-detaching rebase.
489 # Case (1) initial changeset of a non-detaching rebase.
490 # Let the merge mechanism find the base itself.
490 # Let the merge mechanism find the base itself.
491 base = None
491 base = None
492 elif not repo[rev].p2():
492 elif not repo[rev].p2():
493 # Case (2) detaching the node with a single parent, use this parent
493 # Case (2) detaching the node with a single parent, use this parent
494 base = repo[rev].p1().node()
494 base = repo[rev].p1().node()
495 else:
495 else:
496 # In case of merge, we need to pick the right parent as merge base.
496 # In case of merge, we need to pick the right parent as merge base.
497 #
497 #
498 # Imagine we have:
498 # Imagine we have:
499 # - M: currently rebase revision in this step
499 # - M: currently rebase revision in this step
500 # - A: one parent of M
500 # - A: one parent of M
501 # - B: second parent of M
501 # - B: second parent of M
502 # - D: destination of this merge step (p1 var)
502 # - D: destination of this merge step (p1 var)
503 #
503 #
504 # If we are rebasing on D, D is the successors of A or B. The right
504 # If we are rebasing on D, D is the successors of A or B. The right
505 # merge base is the one D succeed to. We pretend it is B for the rest
505 # merge base is the one D succeed to. We pretend it is B for the rest
506 # of this comment
506 # of this comment
507 #
507 #
508 # If we pick B as the base, the merge involves:
508 # If we pick B as the base, the merge involves:
509 # - changes from B to M (actual changeset payload)
509 # - changes from B to M (actual changeset payload)
510 # - changes from B to D (induced by rebase) as D is a rebased
510 # - changes from B to D (induced by rebase) as D is a rebased
511 # version of B)
511 # version of B)
512 # Which exactly represent the rebase operation.
512 # Which exactly represent the rebase operation.
513 #
513 #
514 # If we pick the A as the base, the merge involves
514 # If we pick the A as the base, the merge involves
515 # - changes from A to M (actual changeset payload)
515 # - changes from A to M (actual changeset payload)
516 # - changes from A to D (with include changes between unrelated A and B
516 # - changes from A to D (with include changes between unrelated A and B
517 # plus changes induced by rebase)
517 # plus changes induced by rebase)
518 # Which does not represent anything sensible and creates a lot of
518 # Which does not represent anything sensible and creates a lot of
519 # conflicts.
519 # conflicts.
520 for p in repo[rev].parents():
520 for p in repo[rev].parents():
521 if state.get(p.rev()) == repo[p1].rev():
521 if state.get(p.rev()) == repo[p1].rev():
522 base = p.node()
522 base = p.node()
523 break
523 break
524 else: # fallback when base not found
524 else: # fallback when base not found
525 base = None
525 base = None
526
526
527 # Raise because this function is called wrong (see issue 4106)
527 # Raise because this function is called wrong (see issue 4106)
528 raise AssertionError('no base found to rebase on '
528 raise AssertionError('no base found to rebase on '
529 '(rebasenode called wrong)')
529 '(rebasenode called wrong)')
530 if base is not None:
530 if base is not None:
531 repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base]))
531 repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base]))
532 # When collapsing in-place, the parent is the common ancestor, we
532 # When collapsing in-place, the parent is the common ancestor, we
533 # have to allow merging with it.
533 # have to allow merging with it.
534 return merge.update(repo, rev, True, True, False, base, collapse)
534 return merge.update(repo, rev, True, True, False, base, collapse,
535 labels=['dest', 'source'])
535
536
536 def nearestrebased(repo, rev, state):
537 def nearestrebased(repo, rev, state):
537 """return the nearest ancestors of rev in the rebase result"""
538 """return the nearest ancestors of rev in the rebase result"""
538 rebased = [r for r in state if state[r] > nullmerge]
539 rebased = [r for r in state if state[r] > nullmerge]
539 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
540 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
540 if candidates:
541 if candidates:
541 return state[candidates[0]]
542 return state[candidates[0]]
542 else:
543 else:
543 return None
544 return None
544
545
545 def defineparents(repo, rev, target, state, targetancestors):
546 def defineparents(repo, rev, target, state, targetancestors):
546 'Return the new parent relationship of the revision that will be rebased'
547 'Return the new parent relationship of the revision that will be rebased'
547 parents = repo[rev].parents()
548 parents = repo[rev].parents()
548 p1 = p2 = nullrev
549 p1 = p2 = nullrev
549
550
550 P1n = parents[0].rev()
551 P1n = parents[0].rev()
551 if P1n in targetancestors:
552 if P1n in targetancestors:
552 p1 = target
553 p1 = target
553 elif P1n in state:
554 elif P1n in state:
554 if state[P1n] == nullmerge:
555 if state[P1n] == nullmerge:
555 p1 = target
556 p1 = target
556 elif state[P1n] == revignored:
557 elif state[P1n] == revignored:
557 p1 = nearestrebased(repo, P1n, state)
558 p1 = nearestrebased(repo, P1n, state)
558 if p1 is None:
559 if p1 is None:
559 p1 = target
560 p1 = target
560 else:
561 else:
561 p1 = state[P1n]
562 p1 = state[P1n]
562 else: # P1n external
563 else: # P1n external
563 p1 = target
564 p1 = target
564 p2 = P1n
565 p2 = P1n
565
566
566 if len(parents) == 2 and parents[1].rev() not in targetancestors:
567 if len(parents) == 2 and parents[1].rev() not in targetancestors:
567 P2n = parents[1].rev()
568 P2n = parents[1].rev()
568 # interesting second parent
569 # interesting second parent
569 if P2n in state:
570 if P2n in state:
570 if p1 == target: # P1n in targetancestors or external
571 if p1 == target: # P1n in targetancestors or external
571 p1 = state[P2n]
572 p1 = state[P2n]
572 elif state[P2n] == revignored:
573 elif state[P2n] == revignored:
573 p2 = nearestrebased(repo, P2n, state)
574 p2 = nearestrebased(repo, P2n, state)
574 if p2 is None:
575 if p2 is None:
575 # no ancestors rebased yet, detach
576 # no ancestors rebased yet, detach
576 p2 = target
577 p2 = target
577 else:
578 else:
578 p2 = state[P2n]
579 p2 = state[P2n]
579 else: # P2n external
580 else: # P2n external
580 if p2 != nullrev: # P1n external too => rev is a merged revision
581 if p2 != nullrev: # P1n external too => rev is a merged revision
581 raise util.Abort(_('cannot use revision %d as base, result '
582 raise util.Abort(_('cannot use revision %d as base, result '
582 'would have 3 parents') % rev)
583 'would have 3 parents') % rev)
583 p2 = P2n
584 p2 = P2n
584 repo.ui.debug(" future parents are %d and %d\n" %
585 repo.ui.debug(" future parents are %d and %d\n" %
585 (repo[p1].rev(), repo[p2].rev()))
586 (repo[p1].rev(), repo[p2].rev()))
586 return p1, p2
587 return p1, p2
587
588
588 def isagitpatch(repo, patchname):
589 def isagitpatch(repo, patchname):
589 'Return true if the given patch is in git format'
590 'Return true if the given patch is in git format'
590 mqpatch = os.path.join(repo.mq.path, patchname)
591 mqpatch = os.path.join(repo.mq.path, patchname)
591 for line in patch.linereader(file(mqpatch, 'rb')):
592 for line in patch.linereader(file(mqpatch, 'rb')):
592 if line.startswith('diff --git'):
593 if line.startswith('diff --git'):
593 return True
594 return True
594 return False
595 return False
595
596
596 def updatemq(repo, state, skipped, **opts):
597 def updatemq(repo, state, skipped, **opts):
597 'Update rebased mq patches - finalize and then import them'
598 'Update rebased mq patches - finalize and then import them'
598 mqrebase = {}
599 mqrebase = {}
599 mq = repo.mq
600 mq = repo.mq
600 original_series = mq.fullseries[:]
601 original_series = mq.fullseries[:]
601 skippedpatches = set()
602 skippedpatches = set()
602
603
603 for p in mq.applied:
604 for p in mq.applied:
604 rev = repo[p.node].rev()
605 rev = repo[p.node].rev()
605 if rev in state:
606 if rev in state:
606 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
607 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
607 (rev, p.name))
608 (rev, p.name))
608 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
609 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
609 else:
610 else:
610 # Applied but not rebased, not sure this should happen
611 # Applied but not rebased, not sure this should happen
611 skippedpatches.add(p.name)
612 skippedpatches.add(p.name)
612
613
613 if mqrebase:
614 if mqrebase:
614 mq.finish(repo, mqrebase.keys())
615 mq.finish(repo, mqrebase.keys())
615
616
616 # We must start import from the newest revision
617 # We must start import from the newest revision
617 for rev in sorted(mqrebase, reverse=True):
618 for rev in sorted(mqrebase, reverse=True):
618 if rev not in skipped:
619 if rev not in skipped:
619 name, isgit = mqrebase[rev]
620 name, isgit = mqrebase[rev]
620 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
621 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
621 mq.qimport(repo, (), patchname=name, git=isgit,
622 mq.qimport(repo, (), patchname=name, git=isgit,
622 rev=[str(state[rev])])
623 rev=[str(state[rev])])
623 else:
624 else:
624 # Rebased and skipped
625 # Rebased and skipped
625 skippedpatches.add(mqrebase[rev][0])
626 skippedpatches.add(mqrebase[rev][0])
626
627
627 # Patches were either applied and rebased and imported in
628 # Patches were either applied and rebased and imported in
628 # order, applied and removed or unapplied. Discard the removed
629 # order, applied and removed or unapplied. Discard the removed
629 # ones while preserving the original series order and guards.
630 # ones while preserving the original series order and guards.
630 newseries = [s for s in original_series
631 newseries = [s for s in original_series
631 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
632 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
632 mq.fullseries[:] = newseries
633 mq.fullseries[:] = newseries
633 mq.seriesdirty = True
634 mq.seriesdirty = True
634 mq.savedirty()
635 mq.savedirty()
635
636
636 def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
637 def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
637 'Move bookmarks to their correct changesets, and delete divergent ones'
638 'Move bookmarks to their correct changesets, and delete divergent ones'
638 marks = repo._bookmarks
639 marks = repo._bookmarks
639 for k, v in originalbookmarks.iteritems():
640 for k, v in originalbookmarks.iteritems():
640 if v in nstate:
641 if v in nstate:
641 # update the bookmarks for revs that have moved
642 # update the bookmarks for revs that have moved
642 marks[k] = nstate[v]
643 marks[k] = nstate[v]
643 bookmarks.deletedivergent(repo, [targetnode], k)
644 bookmarks.deletedivergent(repo, [targetnode], k)
644
645
645 marks.write()
646 marks.write()
646
647
647 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
648 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
648 external, activebookmark):
649 external, activebookmark):
649 'Store the current status to allow recovery'
650 'Store the current status to allow recovery'
650 f = repo.opener("rebasestate", "w")
651 f = repo.opener("rebasestate", "w")
651 f.write(repo[originalwd].hex() + '\n')
652 f.write(repo[originalwd].hex() + '\n')
652 f.write(repo[target].hex() + '\n')
653 f.write(repo[target].hex() + '\n')
653 f.write(repo[external].hex() + '\n')
654 f.write(repo[external].hex() + '\n')
654 f.write('%d\n' % int(collapse))
655 f.write('%d\n' % int(collapse))
655 f.write('%d\n' % int(keep))
656 f.write('%d\n' % int(keep))
656 f.write('%d\n' % int(keepbranches))
657 f.write('%d\n' % int(keepbranches))
657 f.write('%s\n' % (activebookmark or ''))
658 f.write('%s\n' % (activebookmark or ''))
658 for d, v in state.iteritems():
659 for d, v in state.iteritems():
659 oldrev = repo[d].hex()
660 oldrev = repo[d].hex()
660 if v > nullmerge:
661 if v > nullmerge:
661 newrev = repo[v].hex()
662 newrev = repo[v].hex()
662 else:
663 else:
663 newrev = v
664 newrev = v
664 f.write("%s:%s\n" % (oldrev, newrev))
665 f.write("%s:%s\n" % (oldrev, newrev))
665 f.close()
666 f.close()
666 repo.ui.debug('rebase status stored\n')
667 repo.ui.debug('rebase status stored\n')
667
668
668 def clearstatus(repo):
669 def clearstatus(repo):
669 'Remove the status files'
670 'Remove the status files'
670 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
671 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
671
672
672 def restorestatus(repo):
673 def restorestatus(repo):
673 'Restore a previously stored status'
674 'Restore a previously stored status'
674 try:
675 try:
675 keepbranches = None
676 keepbranches = None
676 target = None
677 target = None
677 collapse = False
678 collapse = False
678 external = nullrev
679 external = nullrev
679 activebookmark = None
680 activebookmark = None
680 state = {}
681 state = {}
681 f = repo.opener("rebasestate")
682 f = repo.opener("rebasestate")
682 for i, l in enumerate(f.read().splitlines()):
683 for i, l in enumerate(f.read().splitlines()):
683 if i == 0:
684 if i == 0:
684 originalwd = repo[l].rev()
685 originalwd = repo[l].rev()
685 elif i == 1:
686 elif i == 1:
686 target = repo[l].rev()
687 target = repo[l].rev()
687 elif i == 2:
688 elif i == 2:
688 external = repo[l].rev()
689 external = repo[l].rev()
689 elif i == 3:
690 elif i == 3:
690 collapse = bool(int(l))
691 collapse = bool(int(l))
691 elif i == 4:
692 elif i == 4:
692 keep = bool(int(l))
693 keep = bool(int(l))
693 elif i == 5:
694 elif i == 5:
694 keepbranches = bool(int(l))
695 keepbranches = bool(int(l))
695 elif i == 6 and not (len(l) == 81 and ':' in l):
696 elif i == 6 and not (len(l) == 81 and ':' in l):
696 # line 6 is a recent addition, so for backwards compatibility
697 # line 6 is a recent addition, so for backwards compatibility
697 # check that the line doesn't look like the oldrev:newrev lines
698 # check that the line doesn't look like the oldrev:newrev lines
698 activebookmark = l
699 activebookmark = l
699 else:
700 else:
700 oldrev, newrev = l.split(':')
701 oldrev, newrev = l.split(':')
701 if newrev in (str(nullmerge), str(revignored)):
702 if newrev in (str(nullmerge), str(revignored)):
702 state[repo[oldrev].rev()] = int(newrev)
703 state[repo[oldrev].rev()] = int(newrev)
703 else:
704 else:
704 state[repo[oldrev].rev()] = repo[newrev].rev()
705 state[repo[oldrev].rev()] = repo[newrev].rev()
705
706
706 if keepbranches is None:
707 if keepbranches is None:
707 raise util.Abort(_('.hg/rebasestate is incomplete'))
708 raise util.Abort(_('.hg/rebasestate is incomplete'))
708
709
709 skipped = set()
710 skipped = set()
710 # recompute the set of skipped revs
711 # recompute the set of skipped revs
711 if not collapse:
712 if not collapse:
712 seen = set([target])
713 seen = set([target])
713 for old, new in sorted(state.items()):
714 for old, new in sorted(state.items()):
714 if new != nullrev and new in seen:
715 if new != nullrev and new in seen:
715 skipped.add(old)
716 skipped.add(old)
716 seen.add(new)
717 seen.add(new)
717 repo.ui.debug('computed skipped revs: %s\n' %
718 repo.ui.debug('computed skipped revs: %s\n' %
718 (' '.join(str(r) for r in sorted(skipped)) or None))
719 (' '.join(str(r) for r in sorted(skipped)) or None))
719 repo.ui.debug('rebase status resumed\n')
720 repo.ui.debug('rebase status resumed\n')
720 return (originalwd, target, state, skipped,
721 return (originalwd, target, state, skipped,
721 collapse, keep, keepbranches, external, activebookmark)
722 collapse, keep, keepbranches, external, activebookmark)
722 except IOError, err:
723 except IOError, err:
723 if err.errno != errno.ENOENT:
724 if err.errno != errno.ENOENT:
724 raise
725 raise
725 raise util.Abort(_('no rebase in progress'))
726 raise util.Abort(_('no rebase in progress'))
726
727
727 def inrebase(repo, originalwd, state):
728 def inrebase(repo, originalwd, state):
728 '''check whether the working dir is in an interrupted rebase'''
729 '''check whether the working dir is in an interrupted rebase'''
729 parents = [p.rev() for p in repo.parents()]
730 parents = [p.rev() for p in repo.parents()]
730 if originalwd in parents:
731 if originalwd in parents:
731 return True
732 return True
732
733
733 for newrev in state.itervalues():
734 for newrev in state.itervalues():
734 if newrev in parents:
735 if newrev in parents:
735 return True
736 return True
736
737
737 return False
738 return False
738
739
739 def abort(repo, originalwd, target, state):
740 def abort(repo, originalwd, target, state):
740 'Restore the repository to its original state'
741 'Restore the repository to its original state'
741 dstates = [s for s in state.values() if s > nullrev]
742 dstates = [s for s in state.values() if s > nullrev]
742 immutable = [d for d in dstates if not repo[d].mutable()]
743 immutable = [d for d in dstates if not repo[d].mutable()]
743 cleanup = True
744 cleanup = True
744 if immutable:
745 if immutable:
745 repo.ui.warn(_("warning: can't clean up immutable changesets %s\n")
746 repo.ui.warn(_("warning: can't clean up immutable changesets %s\n")
746 % ', '.join(str(repo[r]) for r in immutable),
747 % ', '.join(str(repo[r]) for r in immutable),
747 hint=_('see hg help phases for details'))
748 hint=_('see hg help phases for details'))
748 cleanup = False
749 cleanup = False
749
750
750 descendants = set()
751 descendants = set()
751 if dstates:
752 if dstates:
752 descendants = set(repo.changelog.descendants(dstates))
753 descendants = set(repo.changelog.descendants(dstates))
753 if descendants - set(dstates):
754 if descendants - set(dstates):
754 repo.ui.warn(_("warning: new changesets detected on target branch, "
755 repo.ui.warn(_("warning: new changesets detected on target branch, "
755 "can't strip\n"))
756 "can't strip\n"))
756 cleanup = False
757 cleanup = False
757
758
758 if cleanup:
759 if cleanup:
759 # Update away from the rebase if necessary
760 # Update away from the rebase if necessary
760 if inrebase(repo, originalwd, state):
761 if inrebase(repo, originalwd, state):
761 merge.update(repo, repo[originalwd].rev(), False, True, False)
762 merge.update(repo, repo[originalwd].rev(), False, True, False)
762
763
763 # Strip from the first rebased revision
764 # Strip from the first rebased revision
764 rebased = filter(lambda x: x > -1 and x != target, state.values())
765 rebased = filter(lambda x: x > -1 and x != target, state.values())
765 if rebased:
766 if rebased:
766 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
767 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
767 # no backup of rebased cset versions needed
768 # no backup of rebased cset versions needed
768 repair.strip(repo.ui, repo, strippoints)
769 repair.strip(repo.ui, repo, strippoints)
769
770
770 clearstatus(repo)
771 clearstatus(repo)
771 repo.ui.warn(_('rebase aborted\n'))
772 repo.ui.warn(_('rebase aborted\n'))
772 return 0
773 return 0
773
774
774 def buildstate(repo, dest, rebaseset, collapse):
775 def buildstate(repo, dest, rebaseset, collapse):
775 '''Define which revisions are going to be rebased and where
776 '''Define which revisions are going to be rebased and where
776
777
777 repo: repo
778 repo: repo
778 dest: context
779 dest: context
779 rebaseset: set of rev
780 rebaseset: set of rev
780 '''
781 '''
781
782
782 # This check isn't strictly necessary, since mq detects commits over an
783 # This check isn't strictly necessary, since mq detects commits over an
783 # applied patch. But it prevents messing up the working directory when
784 # applied patch. But it prevents messing up the working directory when
784 # a partially completed rebase is blocked by mq.
785 # a partially completed rebase is blocked by mq.
785 if 'qtip' in repo.tags() and (dest.node() in
786 if 'qtip' in repo.tags() and (dest.node() in
786 [s.node for s in repo.mq.applied]):
787 [s.node for s in repo.mq.applied]):
787 raise util.Abort(_('cannot rebase onto an applied mq patch'))
788 raise util.Abort(_('cannot rebase onto an applied mq patch'))
788
789
789 roots = list(repo.set('roots(%ld)', rebaseset))
790 roots = list(repo.set('roots(%ld)', rebaseset))
790 if not roots:
791 if not roots:
791 raise util.Abort(_('no matching revisions'))
792 raise util.Abort(_('no matching revisions'))
792 roots.sort()
793 roots.sort()
793 state = {}
794 state = {}
794 detachset = set()
795 detachset = set()
795 for root in roots:
796 for root in roots:
796 commonbase = root.ancestor(dest)
797 commonbase = root.ancestor(dest)
797 if commonbase == root:
798 if commonbase == root:
798 raise util.Abort(_('source is ancestor of destination'))
799 raise util.Abort(_('source is ancestor of destination'))
799 if commonbase == dest:
800 if commonbase == dest:
800 samebranch = root.branch() == dest.branch()
801 samebranch = root.branch() == dest.branch()
801 if not collapse and samebranch and root in dest.children():
802 if not collapse and samebranch and root in dest.children():
802 repo.ui.debug('source is a child of destination\n')
803 repo.ui.debug('source is a child of destination\n')
803 return None
804 return None
804
805
805 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
806 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
806 state.update(dict.fromkeys(rebaseset, nullrev))
807 state.update(dict.fromkeys(rebaseset, nullrev))
807 # Rebase tries to turn <dest> into a parent of <root> while
808 # Rebase tries to turn <dest> into a parent of <root> while
808 # preserving the number of parents of rebased changesets:
809 # preserving the number of parents of rebased changesets:
809 #
810 #
810 # - A changeset with a single parent will always be rebased as a
811 # - A changeset with a single parent will always be rebased as a
811 # changeset with a single parent.
812 # changeset with a single parent.
812 #
813 #
813 # - A merge will be rebased as merge unless its parents are both
814 # - A merge will be rebased as merge unless its parents are both
814 # ancestors of <dest> or are themselves in the rebased set and
815 # ancestors of <dest> or are themselves in the rebased set and
815 # pruned while rebased.
816 # pruned while rebased.
816 #
817 #
817 # If one parent of <root> is an ancestor of <dest>, the rebased
818 # If one parent of <root> is an ancestor of <dest>, the rebased
818 # version of this parent will be <dest>. This is always true with
819 # version of this parent will be <dest>. This is always true with
819 # --base option.
820 # --base option.
820 #
821 #
821 # Otherwise, we need to *replace* the original parents with
822 # Otherwise, we need to *replace* the original parents with
822 # <dest>. This "detaches" the rebased set from its former location
823 # <dest>. This "detaches" the rebased set from its former location
823 # and rebases it onto <dest>. Changes introduced by ancestors of
824 # and rebases it onto <dest>. Changes introduced by ancestors of
824 # <root> not common with <dest> (the detachset, marked as
825 # <root> not common with <dest> (the detachset, marked as
825 # nullmerge) are "removed" from the rebased changesets.
826 # nullmerge) are "removed" from the rebased changesets.
826 #
827 #
827 # - If <root> has a single parent, set it to <dest>.
828 # - If <root> has a single parent, set it to <dest>.
828 #
829 #
829 # - If <root> is a merge, we cannot decide which parent to
830 # - If <root> is a merge, we cannot decide which parent to
830 # replace, the rebase operation is not clearly defined.
831 # replace, the rebase operation is not clearly defined.
831 #
832 #
832 # The table below sums up this behavior:
833 # The table below sums up this behavior:
833 #
834 #
834 # +------------------+----------------------+-------------------------+
835 # +------------------+----------------------+-------------------------+
835 # | | one parent | merge |
836 # | | one parent | merge |
836 # +------------------+----------------------+-------------------------+
837 # +------------------+----------------------+-------------------------+
837 # | parent in | new parent is <dest> | parents in ::<dest> are |
838 # | parent in | new parent is <dest> | parents in ::<dest> are |
838 # | ::<dest> | | remapped to <dest> |
839 # | ::<dest> | | remapped to <dest> |
839 # +------------------+----------------------+-------------------------+
840 # +------------------+----------------------+-------------------------+
840 # | unrelated source | new parent is <dest> | ambiguous, abort |
841 # | unrelated source | new parent is <dest> | ambiguous, abort |
841 # +------------------+----------------------+-------------------------+
842 # +------------------+----------------------+-------------------------+
842 #
843 #
843 # The actual abort is handled by `defineparents`
844 # The actual abort is handled by `defineparents`
844 if len(root.parents()) <= 1:
845 if len(root.parents()) <= 1:
845 # ancestors of <root> not ancestors of <dest>
846 # ancestors of <root> not ancestors of <dest>
846 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
847 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
847 [root.rev()]))
848 [root.rev()]))
848 for r in detachset:
849 for r in detachset:
849 if r not in state:
850 if r not in state:
850 state[r] = nullmerge
851 state[r] = nullmerge
851 if len(roots) > 1:
852 if len(roots) > 1:
852 # If we have multiple roots, we may have "hole" in the rebase set.
853 # If we have multiple roots, we may have "hole" in the rebase set.
853 # Rebase roots that descend from those "hole" should not be detached as
854 # Rebase roots that descend from those "hole" should not be detached as
854 # other root are. We use the special `revignored` to inform rebase that
855 # other root are. We use the special `revignored` to inform rebase that
855 # the revision should be ignored but that `defineparents` should search
856 # the revision should be ignored but that `defineparents` should search
856 # a rebase destination that make sense regarding rebased topology.
857 # a rebase destination that make sense regarding rebased topology.
857 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
858 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
858 for ignored in set(rebasedomain) - set(rebaseset):
859 for ignored in set(rebasedomain) - set(rebaseset):
859 state[ignored] = revignored
860 state[ignored] = revignored
860 return repo['.'].rev(), dest.rev(), state
861 return repo['.'].rev(), dest.rev(), state
861
862
862 def clearrebased(ui, repo, state, skipped, collapsedas=None):
863 def clearrebased(ui, repo, state, skipped, collapsedas=None):
863 """dispose of rebased revision at the end of the rebase
864 """dispose of rebased revision at the end of the rebase
864
865
865 If `collapsedas` is not None, the rebase was a collapse whose result if the
866 If `collapsedas` is not None, the rebase was a collapse whose result if the
866 `collapsedas` node."""
867 `collapsedas` node."""
867 if obsolete._enabled:
868 if obsolete._enabled:
868 markers = []
869 markers = []
869 for rev, newrev in sorted(state.items()):
870 for rev, newrev in sorted(state.items()):
870 if newrev >= 0:
871 if newrev >= 0:
871 if rev in skipped:
872 if rev in skipped:
872 succs = ()
873 succs = ()
873 elif collapsedas is not None:
874 elif collapsedas is not None:
874 succs = (repo[collapsedas],)
875 succs = (repo[collapsedas],)
875 else:
876 else:
876 succs = (repo[newrev],)
877 succs = (repo[newrev],)
877 markers.append((repo[rev], succs))
878 markers.append((repo[rev], succs))
878 if markers:
879 if markers:
879 obsolete.createmarkers(repo, markers)
880 obsolete.createmarkers(repo, markers)
880 else:
881 else:
881 rebased = [rev for rev in state if state[rev] > nullmerge]
882 rebased = [rev for rev in state if state[rev] > nullmerge]
882 if rebased:
883 if rebased:
883 stripped = []
884 stripped = []
884 for root in repo.set('roots(%ld)', rebased):
885 for root in repo.set('roots(%ld)', rebased):
885 if set(repo.changelog.descendants([root.rev()])) - set(state):
886 if set(repo.changelog.descendants([root.rev()])) - set(state):
886 ui.warn(_("warning: new changesets detected "
887 ui.warn(_("warning: new changesets detected "
887 "on source branch, not stripping\n"))
888 "on source branch, not stripping\n"))
888 else:
889 else:
889 stripped.append(root.node())
890 stripped.append(root.node())
890 if stripped:
891 if stripped:
891 # backup the old csets by default
892 # backup the old csets by default
892 repair.strip(ui, repo, stripped, "all")
893 repair.strip(ui, repo, stripped, "all")
893
894
894
895
895 def pullrebase(orig, ui, repo, *args, **opts):
896 def pullrebase(orig, ui, repo, *args, **opts):
896 'Call rebase after pull if the latter has been invoked with --rebase'
897 'Call rebase after pull if the latter has been invoked with --rebase'
897 if opts.get('rebase'):
898 if opts.get('rebase'):
898 if opts.get('update'):
899 if opts.get('update'):
899 del opts['update']
900 del opts['update']
900 ui.debug('--update and --rebase are not compatible, ignoring '
901 ui.debug('--update and --rebase are not compatible, ignoring '
901 'the update flag\n')
902 'the update flag\n')
902
903
903 movemarkfrom = repo['.'].node()
904 movemarkfrom = repo['.'].node()
904 revsprepull = len(repo)
905 revsprepull = len(repo)
905 origpostincoming = commands.postincoming
906 origpostincoming = commands.postincoming
906 def _dummy(*args, **kwargs):
907 def _dummy(*args, **kwargs):
907 pass
908 pass
908 commands.postincoming = _dummy
909 commands.postincoming = _dummy
909 try:
910 try:
910 orig(ui, repo, *args, **opts)
911 orig(ui, repo, *args, **opts)
911 finally:
912 finally:
912 commands.postincoming = origpostincoming
913 commands.postincoming = origpostincoming
913 revspostpull = len(repo)
914 revspostpull = len(repo)
914 if revspostpull > revsprepull:
915 if revspostpull > revsprepull:
915 # --rev option from pull conflict with rebase own --rev
916 # --rev option from pull conflict with rebase own --rev
916 # dropping it
917 # dropping it
917 if 'rev' in opts:
918 if 'rev' in opts:
918 del opts['rev']
919 del opts['rev']
919 rebase(ui, repo, **opts)
920 rebase(ui, repo, **opts)
920 branch = repo[None].branch()
921 branch = repo[None].branch()
921 dest = repo[branch].rev()
922 dest = repo[branch].rev()
922 if dest != repo['.'].rev():
923 if dest != repo['.'].rev():
923 # there was nothing to rebase we force an update
924 # there was nothing to rebase we force an update
924 hg.update(repo, dest)
925 hg.update(repo, dest)
925 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
926 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
926 ui.status(_("updating bookmark %s\n")
927 ui.status(_("updating bookmark %s\n")
927 % repo._bookmarkcurrent)
928 % repo._bookmarkcurrent)
928 else:
929 else:
929 if opts.get('tool'):
930 if opts.get('tool'):
930 raise util.Abort(_('--tool can only be used with --rebase'))
931 raise util.Abort(_('--tool can only be used with --rebase'))
931 orig(ui, repo, *args, **opts)
932 orig(ui, repo, *args, **opts)
932
933
933 def summaryhook(ui, repo):
934 def summaryhook(ui, repo):
934 if not os.path.exists(repo.join('rebasestate')):
935 if not os.path.exists(repo.join('rebasestate')):
935 return
936 return
936 try:
937 try:
937 state = restorestatus(repo)[2]
938 state = restorestatus(repo)[2]
938 except error.RepoLookupError:
939 except error.RepoLookupError:
939 # i18n: column positioning for "hg summary"
940 # i18n: column positioning for "hg summary"
940 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
941 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
941 ui.write(msg)
942 ui.write(msg)
942 return
943 return
943 numrebased = len([i for i in state.itervalues() if i != -1])
944 numrebased = len([i for i in state.itervalues() if i != -1])
944 # i18n: column positioning for "hg summary"
945 # i18n: column positioning for "hg summary"
945 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
946 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
946 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
947 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
947 ui.label(_('%d remaining'), 'rebase.remaining') %
948 ui.label(_('%d remaining'), 'rebase.remaining') %
948 (len(state) - numrebased)))
949 (len(state) - numrebased)))
949
950
950 def uisetup(ui):
951 def uisetup(ui):
951 'Replace pull with a decorator to provide --rebase option'
952 'Replace pull with a decorator to provide --rebase option'
952 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
953 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
953 entry[1].append(('', 'rebase', None,
954 entry[1].append(('', 'rebase', None,
954 _("rebase working directory to branch head")))
955 _("rebase working directory to branch head")))
955 entry[1].append(('t', 'tool', '',
956 entry[1].append(('t', 'tool', '',
956 _("specify merge tool for rebase")))
957 _("specify merge tool for rebase")))
957 cmdutil.summaryhooks.add('rebase', summaryhook)
958 cmdutil.summaryhooks.add('rebase', summaryhook)
958 cmdutil.unfinishedstates.append(
959 cmdutil.unfinishedstates.append(
959 ['rebasestate', False, False, _('rebase in progress'),
960 ['rebasestate', False, False, _('rebase in progress'),
960 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
961 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
@@ -1,669 +1,669 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "mq=" >> $HGRCPATH
2 $ echo "mq=" >> $HGRCPATH
3 $ echo "shelve=" >> $HGRCPATH
3 $ echo "shelve=" >> $HGRCPATH
4 $ echo "[defaults]" >> $HGRCPATH
4 $ echo "[defaults]" >> $HGRCPATH
5 $ echo "diff = --nodates --git" >> $HGRCPATH
5 $ echo "diff = --nodates --git" >> $HGRCPATH
6 $ echo "qnew = --date '0 0'" >> $HGRCPATH
6 $ echo "qnew = --date '0 0'" >> $HGRCPATH
7
7
8 $ hg init repo
8 $ hg init repo
9 $ cd repo
9 $ cd repo
10 $ mkdir a b
10 $ mkdir a b
11 $ echo a > a/a
11 $ echo a > a/a
12 $ echo b > b/b
12 $ echo b > b/b
13 $ echo c > c
13 $ echo c > c
14 $ echo d > d
14 $ echo d > d
15 $ echo x > x
15 $ echo x > x
16 $ hg addremove -q
16 $ hg addremove -q
17
17
18 shelving in an empty repo should be possible
18 shelving in an empty repo should be possible
19
19
20 $ hg shelve
20 $ hg shelve
21 shelved as default
21 shelved as default
22 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
22 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
23
23
24 $ hg unshelve
24 $ hg unshelve
25 unshelving change 'default'
25 unshelving change 'default'
26
26
27 $ hg commit -q -m 'initial commit'
27 $ hg commit -q -m 'initial commit'
28
28
29 $ hg shelve
29 $ hg shelve
30 nothing changed
30 nothing changed
31 [1]
31 [1]
32
32
33 create an mq patch - shelving should work fine with a patch applied
33 create an mq patch - shelving should work fine with a patch applied
34
34
35 $ echo n > n
35 $ echo n > n
36 $ hg add n
36 $ hg add n
37 $ hg commit n -m second
37 $ hg commit n -m second
38 $ hg qnew second.patch
38 $ hg qnew second.patch
39
39
40 shelve a change that we will delete later
40 shelve a change that we will delete later
41
41
42 $ echo a >> a/a
42 $ echo a >> a/a
43 $ hg shelve
43 $ hg shelve
44 shelved as default
44 shelved as default
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46
46
47 set up some more complex changes to shelve
47 set up some more complex changes to shelve
48
48
49 $ echo a >> a/a
49 $ echo a >> a/a
50 $ hg mv b b.rename
50 $ hg mv b b.rename
51 moving b/b to b.rename/b (glob)
51 moving b/b to b.rename/b (glob)
52 $ hg cp c c.copy
52 $ hg cp c c.copy
53 $ hg status -C
53 $ hg status -C
54 M a/a
54 M a/a
55 A b.rename/b
55 A b.rename/b
56 b/b
56 b/b
57 A c.copy
57 A c.copy
58 c
58 c
59 R b/b
59 R b/b
60
60
61 prevent some foot-shooting
61 prevent some foot-shooting
62
62
63 $ hg shelve -n foo/bar
63 $ hg shelve -n foo/bar
64 abort: shelved change names may not contain slashes
64 abort: shelved change names may not contain slashes
65 [255]
65 [255]
66 $ hg shelve -n .baz
66 $ hg shelve -n .baz
67 abort: shelved change names may not start with '.'
67 abort: shelved change names may not start with '.'
68 [255]
68 [255]
69
69
70 the common case - no options or filenames
70 the common case - no options or filenames
71
71
72 $ hg shelve
72 $ hg shelve
73 shelved as default-01
73 shelved as default-01
74 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
74 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
75 $ hg status -C
75 $ hg status -C
76
76
77 ensure that our shelved changes exist
77 ensure that our shelved changes exist
78
78
79 $ hg shelve -l
79 $ hg shelve -l
80 default-01 (*) changes to '[mq]: second.patch' (glob)
80 default-01 (*) changes to '[mq]: second.patch' (glob)
81 default (*) changes to '[mq]: second.patch' (glob)
81 default (*) changes to '[mq]: second.patch' (glob)
82
82
83 $ hg shelve -l -p default
83 $ hg shelve -l -p default
84 default (*) changes to '[mq]: second.patch' (glob)
84 default (*) changes to '[mq]: second.patch' (glob)
85
85
86 diff --git a/a/a b/a/a
86 diff --git a/a/a b/a/a
87 --- a/a/a
87 --- a/a/a
88 +++ b/a/a
88 +++ b/a/a
89 @@ -1,1 +1,2 @@
89 @@ -1,1 +1,2 @@
90 a
90 a
91 +a
91 +a
92
92
93 delete our older shelved change
93 delete our older shelved change
94
94
95 $ hg shelve -d default
95 $ hg shelve -d default
96 $ hg qfinish -a -q
96 $ hg qfinish -a -q
97
97
98 local edits should not prevent a shelved change from applying
98 local edits should not prevent a shelved change from applying
99
99
100 $ printf "z\na\n" > a/a
100 $ printf "z\na\n" > a/a
101 $ hg unshelve --keep
101 $ hg unshelve --keep
102 unshelving change 'default-01'
102 unshelving change 'default-01'
103 temporarily committing pending changes (restore with 'hg unshelve --abort')
103 temporarily committing pending changes (restore with 'hg unshelve --abort')
104 rebasing shelved changes
104 rebasing shelved changes
105 merging a/a
105 merging a/a
106
106
107 $ hg revert --all -q
107 $ hg revert --all -q
108 $ rm a/a.orig b.rename/b c.copy
108 $ rm a/a.orig b.rename/b c.copy
109
109
110 apply it and make sure our state is as expected
110 apply it and make sure our state is as expected
111
111
112 $ hg unshelve
112 $ hg unshelve
113 unshelving change 'default-01'
113 unshelving change 'default-01'
114 $ hg status -C
114 $ hg status -C
115 M a/a
115 M a/a
116 A b.rename/b
116 A b.rename/b
117 b/b
117 b/b
118 A c.copy
118 A c.copy
119 c
119 c
120 R b/b
120 R b/b
121 $ hg shelve -l
121 $ hg shelve -l
122
122
123 $ hg unshelve
123 $ hg unshelve
124 abort: no shelved changes to apply!
124 abort: no shelved changes to apply!
125 [255]
125 [255]
126 $ hg unshelve foo
126 $ hg unshelve foo
127 abort: shelved change 'foo' not found
127 abort: shelved change 'foo' not found
128 [255]
128 [255]
129
129
130 named shelves, specific filenames, and "commit messages" should all work
130 named shelves, specific filenames, and "commit messages" should all work
131
131
132 $ hg status -C
132 $ hg status -C
133 M a/a
133 M a/a
134 A b.rename/b
134 A b.rename/b
135 b/b
135 b/b
136 A c.copy
136 A c.copy
137 c
137 c
138 R b/b
138 R b/b
139 $ hg shelve -q -n wibble -m wat a
139 $ hg shelve -q -n wibble -m wat a
140
140
141 expect "a" to no longer be present, but status otherwise unchanged
141 expect "a" to no longer be present, but status otherwise unchanged
142
142
143 $ hg status -C
143 $ hg status -C
144 A b.rename/b
144 A b.rename/b
145 b/b
145 b/b
146 A c.copy
146 A c.copy
147 c
147 c
148 R b/b
148 R b/b
149 $ hg shelve -l --stat
149 $ hg shelve -l --stat
150 wibble (*) wat (glob)
150 wibble (*) wat (glob)
151 a/a | 1 +
151 a/a | 1 +
152 1 files changed, 1 insertions(+), 0 deletions(-)
152 1 files changed, 1 insertions(+), 0 deletions(-)
153
153
154 and now "a/a" should reappear
154 and now "a/a" should reappear
155
155
156 $ cd a
156 $ cd a
157 $ hg unshelve -q wibble
157 $ hg unshelve -q wibble
158 $ cd ..
158 $ cd ..
159 $ hg status -C
159 $ hg status -C
160 M a/a
160 M a/a
161 A b.rename/b
161 A b.rename/b
162 b/b
162 b/b
163 A c.copy
163 A c.copy
164 c
164 c
165 R b/b
165 R b/b
166
166
167 cause unshelving to result in a merge with 'a' conflicting
167 cause unshelving to result in a merge with 'a' conflicting
168
168
169 $ hg shelve -q
169 $ hg shelve -q
170 $ echo c>>a/a
170 $ echo c>>a/a
171 $ hg commit -m second
171 $ hg commit -m second
172 $ hg tip --template '{files}\n'
172 $ hg tip --template '{files}\n'
173 a/a
173 a/a
174
174
175 add an unrelated change that should be preserved
175 add an unrelated change that should be preserved
176
176
177 $ mkdir foo
177 $ mkdir foo
178 $ echo foo > foo/foo
178 $ echo foo > foo/foo
179 $ hg add foo/foo
179 $ hg add foo/foo
180
180
181 force a conflicted merge to occur
181 force a conflicted merge to occur
182
182
183 $ hg unshelve
183 $ hg unshelve
184 unshelving change 'default'
184 unshelving change 'default'
185 temporarily committing pending changes (restore with 'hg unshelve --abort')
185 temporarily committing pending changes (restore with 'hg unshelve --abort')
186 rebasing shelved changes
186 rebasing shelved changes
187 merging a/a
187 merging a/a
188 warning: conflicts during merge.
188 warning: conflicts during merge.
189 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
189 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
190 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
190 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
191 [1]
191 [1]
192
192
193 ensure that we have a merge with unresolved conflicts
193 ensure that we have a merge with unresolved conflicts
194
194
195 $ hg heads -q --template '{rev}\n'
195 $ hg heads -q --template '{rev}\n'
196 5
196 5
197 4
197 4
198 $ hg parents -q --template '{rev}\n'
198 $ hg parents -q --template '{rev}\n'
199 4
199 4
200 5
200 5
201 $ hg status
201 $ hg status
202 M a/a
202 M a/a
203 M b.rename/b
203 M b.rename/b
204 M c.copy
204 M c.copy
205 R b/b
205 R b/b
206 ? a/a.orig
206 ? a/a.orig
207 $ hg diff
207 $ hg diff
208 diff --git a/a/a b/a/a
208 diff --git a/a/a b/a/a
209 --- a/a/a
209 --- a/a/a
210 +++ b/a/a
210 +++ b/a/a
211 @@ -1,2 +1,6 @@
211 @@ -1,2 +1,6 @@
212 a
212 a
213 +<<<<<<< local: * - shelve: "pending changes temporary commit" (glob)
213 +<<<<<<< dest: * - shelve: "pending changes temporary commit" (glob)
214 c
214 c
215 +=======
215 +=======
216 +a
216 +a
217 +>>>>>>> other: * - shelve: "changes to '[mq]: second.patch'" (glob)
217 +>>>>>>> source: * - shelve: "changes to '[mq]: second.patch'" (glob)
218 diff --git a/b.rename/b b/b.rename/b
218 diff --git a/b.rename/b b/b.rename/b
219 new file mode 100644
219 new file mode 100644
220 --- /dev/null
220 --- /dev/null
221 +++ b/b.rename/b
221 +++ b/b.rename/b
222 @@ -0,0 +1,1 @@
222 @@ -0,0 +1,1 @@
223 +b
223 +b
224 diff --git a/b/b b/b/b
224 diff --git a/b/b b/b/b
225 deleted file mode 100644
225 deleted file mode 100644
226 --- a/b/b
226 --- a/b/b
227 +++ /dev/null
227 +++ /dev/null
228 @@ -1,1 +0,0 @@
228 @@ -1,1 +0,0 @@
229 -b
229 -b
230 diff --git a/c.copy b/c.copy
230 diff --git a/c.copy b/c.copy
231 new file mode 100644
231 new file mode 100644
232 --- /dev/null
232 --- /dev/null
233 +++ b/c.copy
233 +++ b/c.copy
234 @@ -0,0 +1,1 @@
234 @@ -0,0 +1,1 @@
235 +c
235 +c
236 $ hg resolve -l
236 $ hg resolve -l
237 U a/a
237 U a/a
238
238
239 $ hg shelve
239 $ hg shelve
240 abort: unshelve already in progress
240 abort: unshelve already in progress
241 (use 'hg unshelve --continue' or 'hg unshelve --abort')
241 (use 'hg unshelve --continue' or 'hg unshelve --abort')
242 [255]
242 [255]
243
243
244 abort the unshelve and be happy
244 abort the unshelve and be happy
245
245
246 $ hg status
246 $ hg status
247 M a/a
247 M a/a
248 M b.rename/b
248 M b.rename/b
249 M c.copy
249 M c.copy
250 R b/b
250 R b/b
251 ? a/a.orig
251 ? a/a.orig
252 $ hg unshelve -a
252 $ hg unshelve -a
253 rebase aborted
253 rebase aborted
254 unshelve of 'default' aborted
254 unshelve of 'default' aborted
255 $ hg heads -q
255 $ hg heads -q
256 3:2e69b451d1ea
256 3:2e69b451d1ea
257 $ hg parents
257 $ hg parents
258 changeset: 3:2e69b451d1ea
258 changeset: 3:2e69b451d1ea
259 tag: tip
259 tag: tip
260 user: test
260 user: test
261 date: Thu Jan 01 00:00:00 1970 +0000
261 date: Thu Jan 01 00:00:00 1970 +0000
262 summary: second
262 summary: second
263
263
264 $ hg resolve -l
264 $ hg resolve -l
265 abort: resolve command not applicable when not merging
265 abort: resolve command not applicable when not merging
266 [255]
266 [255]
267 $ hg status
267 $ hg status
268 A foo/foo
268 A foo/foo
269 ? a/a.orig
269 ? a/a.orig
270
270
271 try to continue with no unshelve underway
271 try to continue with no unshelve underway
272
272
273 $ hg unshelve -c
273 $ hg unshelve -c
274 abort: no unshelve operation underway
274 abort: no unshelve operation underway
275 [255]
275 [255]
276 $ hg status
276 $ hg status
277 A foo/foo
277 A foo/foo
278 ? a/a.orig
278 ? a/a.orig
279
279
280 redo the unshelve to get a conflict
280 redo the unshelve to get a conflict
281
281
282 $ hg unshelve -q
282 $ hg unshelve -q
283 warning: conflicts during merge.
283 warning: conflicts during merge.
284 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
284 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
285 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
285 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
286 [1]
286 [1]
287
287
288 attempt to continue
288 attempt to continue
289
289
290 $ hg unshelve -c
290 $ hg unshelve -c
291 abort: unresolved conflicts, can't continue
291 abort: unresolved conflicts, can't continue
292 (see 'hg resolve', then 'hg unshelve --continue')
292 (see 'hg resolve', then 'hg unshelve --continue')
293 [255]
293 [255]
294
294
295 $ hg revert -r . a/a
295 $ hg revert -r . a/a
296 $ hg resolve -m a/a
296 $ hg resolve -m a/a
297 no more unresolved files
297 no more unresolved files
298
298
299 $ hg commit -m 'commit while unshelve in progress'
299 $ hg commit -m 'commit while unshelve in progress'
300 abort: unshelve already in progress
300 abort: unshelve already in progress
301 (use 'hg unshelve --continue' or 'hg unshelve --abort')
301 (use 'hg unshelve --continue' or 'hg unshelve --abort')
302 [255]
302 [255]
303
303
304 $ hg unshelve -c
304 $ hg unshelve -c
305 unshelve of 'default' complete
305 unshelve of 'default' complete
306
306
307 ensure the repo is as we hope
307 ensure the repo is as we hope
308
308
309 $ hg parents
309 $ hg parents
310 changeset: 3:2e69b451d1ea
310 changeset: 3:2e69b451d1ea
311 tag: tip
311 tag: tip
312 user: test
312 user: test
313 date: Thu Jan 01 00:00:00 1970 +0000
313 date: Thu Jan 01 00:00:00 1970 +0000
314 summary: second
314 summary: second
315
315
316 $ hg heads -q
316 $ hg heads -q
317 3:2e69b451d1ea
317 3:2e69b451d1ea
318
318
319 $ hg status -C
319 $ hg status -C
320 A b.rename/b
320 A b.rename/b
321 b/b
321 b/b
322 A c.copy
322 A c.copy
323 c
323 c
324 A foo/foo
324 A foo/foo
325 R b/b
325 R b/b
326 ? a/a.orig
326 ? a/a.orig
327
327
328 there should be no shelves left
328 there should be no shelves left
329
329
330 $ hg shelve -l
330 $ hg shelve -l
331
331
332 #if execbit
332 #if execbit
333
333
334 ensure that metadata-only changes are shelved
334 ensure that metadata-only changes are shelved
335
335
336 $ chmod +x a/a
336 $ chmod +x a/a
337 $ hg shelve -q -n execbit a/a
337 $ hg shelve -q -n execbit a/a
338 $ hg status a/a
338 $ hg status a/a
339 $ hg unshelve -q execbit
339 $ hg unshelve -q execbit
340 $ hg status a/a
340 $ hg status a/a
341 M a/a
341 M a/a
342 $ hg revert a/a
342 $ hg revert a/a
343
343
344 #endif
344 #endif
345
345
346 #if symlink
346 #if symlink
347
347
348 $ rm a/a
348 $ rm a/a
349 $ ln -s foo a/a
349 $ ln -s foo a/a
350 $ hg shelve -q -n symlink a/a
350 $ hg shelve -q -n symlink a/a
351 $ hg status a/a
351 $ hg status a/a
352 $ hg unshelve -q symlink
352 $ hg unshelve -q symlink
353 $ hg status a/a
353 $ hg status a/a
354 M a/a
354 M a/a
355 $ hg revert a/a
355 $ hg revert a/a
356
356
357 #endif
357 #endif
358
358
359 set up another conflict between a commit and a shelved change
359 set up another conflict between a commit and a shelved change
360
360
361 $ hg revert -q -C -a
361 $ hg revert -q -C -a
362 $ rm a/a.orig b.rename/b c.copy
362 $ rm a/a.orig b.rename/b c.copy
363 $ echo a >> a/a
363 $ echo a >> a/a
364 $ hg shelve -q
364 $ hg shelve -q
365 $ echo x >> a/a
365 $ echo x >> a/a
366 $ hg ci -m 'create conflict'
366 $ hg ci -m 'create conflict'
367 $ hg add foo/foo
367 $ hg add foo/foo
368
368
369 if we resolve a conflict while unshelving, the unshelve should succeed
369 if we resolve a conflict while unshelving, the unshelve should succeed
370
370
371 $ HGMERGE=true hg unshelve
371 $ HGMERGE=true hg unshelve
372 unshelving change 'default'
372 unshelving change 'default'
373 temporarily committing pending changes (restore with 'hg unshelve --abort')
373 temporarily committing pending changes (restore with 'hg unshelve --abort')
374 rebasing shelved changes
374 rebasing shelved changes
375 merging a/a
375 merging a/a
376 $ hg parents -q
376 $ hg parents -q
377 4:33f7f61e6c5e
377 4:33f7f61e6c5e
378 $ hg shelve -l
378 $ hg shelve -l
379 $ hg status
379 $ hg status
380 A foo/foo
380 A foo/foo
381 $ cat a/a
381 $ cat a/a
382 a
382 a
383 c
383 c
384 x
384 x
385
385
386 test keep and cleanup
386 test keep and cleanup
387
387
388 $ hg shelve
388 $ hg shelve
389 shelved as default
389 shelved as default
390 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
390 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
391 $ hg shelve --list
391 $ hg shelve --list
392 default (*) changes to 'create conflict' (glob)
392 default (*) changes to 'create conflict' (glob)
393 $ hg unshelve --keep
393 $ hg unshelve --keep
394 unshelving change 'default'
394 unshelving change 'default'
395 $ hg shelve --list
395 $ hg shelve --list
396 default (*) changes to 'create conflict' (glob)
396 default (*) changes to 'create conflict' (glob)
397 $ hg shelve --cleanup
397 $ hg shelve --cleanup
398 $ hg shelve --list
398 $ hg shelve --list
399
399
400 test bookmarks
400 test bookmarks
401
401
402 $ hg bookmark test
402 $ hg bookmark test
403 $ hg bookmark
403 $ hg bookmark
404 * test 4:33f7f61e6c5e
404 * test 4:33f7f61e6c5e
405 $ hg shelve
405 $ hg shelve
406 shelved as test
406 shelved as test
407 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
407 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
408 $ hg bookmark
408 $ hg bookmark
409 * test 4:33f7f61e6c5e
409 * test 4:33f7f61e6c5e
410 $ hg unshelve
410 $ hg unshelve
411 unshelving change 'test'
411 unshelving change 'test'
412 $ hg bookmark
412 $ hg bookmark
413 * test 4:33f7f61e6c5e
413 * test 4:33f7f61e6c5e
414
414
415 shelve should still work even if mq is disabled
415 shelve should still work even if mq is disabled
416
416
417 $ hg --config extensions.mq=! shelve
417 $ hg --config extensions.mq=! shelve
418 shelved as test
418 shelved as test
419 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
419 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
420 $ hg --config extensions.mq=! shelve --list
420 $ hg --config extensions.mq=! shelve --list
421 test (*) changes to 'create conflict' (glob)
421 test (*) changes to 'create conflict' (glob)
422 $ hg --config extensions.mq=! unshelve
422 $ hg --config extensions.mq=! unshelve
423 unshelving change 'test'
423 unshelving change 'test'
424
424
425 shelve should leave dirstate clean (issue 4055)
425 shelve should leave dirstate clean (issue 4055)
426
426
427 $ cd ..
427 $ cd ..
428 $ hg init shelverebase
428 $ hg init shelverebase
429 $ cd shelverebase
429 $ cd shelverebase
430 $ printf 'x\ny\n' > x
430 $ printf 'x\ny\n' > x
431 $ echo z > z
431 $ echo z > z
432 $ hg commit -Aqm xy
432 $ hg commit -Aqm xy
433 $ echo z >> x
433 $ echo z >> x
434 $ hg commit -Aqm z
434 $ hg commit -Aqm z
435 $ hg up 0
435 $ hg up 0
436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 $ printf 'a\nx\ny\nz\n' > x
437 $ printf 'a\nx\ny\nz\n' > x
438 $ hg commit -Aqm xyz
438 $ hg commit -Aqm xyz
439 $ echo c >> z
439 $ echo c >> z
440 $ hg shelve
440 $ hg shelve
441 shelved as default
441 shelved as default
442 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 $ hg rebase -d 1 --config extensions.rebase=
443 $ hg rebase -d 1 --config extensions.rebase=
444 merging x
444 merging x
445 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-backup.hg (glob)
445 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-backup.hg (glob)
446 $ hg unshelve
446 $ hg unshelve
447 unshelving change 'default'
447 unshelving change 'default'
448 rebasing shelved changes
448 rebasing shelved changes
449 $ hg status
449 $ hg status
450 M z
450 M z
451
451
452 $ cd ..
452 $ cd ..
453
453
454 shelve should only unshelve pending changes (issue 4068)
454 shelve should only unshelve pending changes (issue 4068)
455
455
456 $ hg init onlypendingchanges
456 $ hg init onlypendingchanges
457 $ cd onlypendingchanges
457 $ cd onlypendingchanges
458 $ touch a
458 $ touch a
459 $ hg ci -Aqm a
459 $ hg ci -Aqm a
460 $ touch b
460 $ touch b
461 $ hg ci -Aqm b
461 $ hg ci -Aqm b
462 $ hg up -q 0
462 $ hg up -q 0
463 $ touch c
463 $ touch c
464 $ hg ci -Aqm c
464 $ hg ci -Aqm c
465
465
466 $ touch d
466 $ touch d
467 $ hg add d
467 $ hg add d
468 $ hg shelve
468 $ hg shelve
469 shelved as default
469 shelved as default
470 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
470 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
471 $ hg up -q 1
471 $ hg up -q 1
472 $ hg unshelve
472 $ hg unshelve
473 unshelving change 'default'
473 unshelving change 'default'
474 rebasing shelved changes
474 rebasing shelved changes
475 $ hg status
475 $ hg status
476 A d
476 A d
477
477
478 unshelve should work on an ancestor of the original commit
478 unshelve should work on an ancestor of the original commit
479
479
480 $ hg shelve
480 $ hg shelve
481 shelved as default
481 shelved as default
482 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
482 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
483 $ hg up 0
483 $ hg up 0
484 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
484 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
485 $ hg unshelve
485 $ hg unshelve
486 unshelving change 'default'
486 unshelving change 'default'
487 rebasing shelved changes
487 rebasing shelved changes
488 $ hg status
488 $ hg status
489 A d
489 A d
490
490
491 test bug 4073 we need to enable obsolete markers for it
491 test bug 4073 we need to enable obsolete markers for it
492
492
493 $ cat > ../obs.py << EOF
493 $ cat > ../obs.py << EOF
494 > import mercurial.obsolete
494 > import mercurial.obsolete
495 > mercurial.obsolete._enabled = True
495 > mercurial.obsolete._enabled = True
496 > EOF
496 > EOF
497 $ echo '[extensions]' >> $HGRCPATH
497 $ echo '[extensions]' >> $HGRCPATH
498 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
498 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
499 $ hg shelve
499 $ hg shelve
500 shelved as default
500 shelved as default
501 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
501 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
502 $ hg debugobsolete `hg --debug id -i -r 1`
502 $ hg debugobsolete `hg --debug id -i -r 1`
503 $ hg unshelve
503 $ hg unshelve
504 unshelving change 'default'
504 unshelving change 'default'
505
505
506 unshelve should leave unknown files alone (issue4113)
506 unshelve should leave unknown files alone (issue4113)
507
507
508 $ echo e > e
508 $ echo e > e
509 $ hg shelve
509 $ hg shelve
510 shelved as default
510 shelved as default
511 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
511 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
512 $ hg status
512 $ hg status
513 ? e
513 ? e
514 $ hg unshelve
514 $ hg unshelve
515 unshelving change 'default'
515 unshelving change 'default'
516 $ hg status
516 $ hg status
517 A d
517 A d
518 ? e
518 ? e
519 $ cat e
519 $ cat e
520 e
520 e
521
521
522 unshelve should keep a copy of unknown files
522 unshelve should keep a copy of unknown files
523
523
524 $ hg add e
524 $ hg add e
525 $ hg shelve
525 $ hg shelve
526 shelved as default
526 shelved as default
527 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
527 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
528 $ echo z > e
528 $ echo z > e
529 $ hg unshelve
529 $ hg unshelve
530 unshelving change 'default'
530 unshelving change 'default'
531 $ cat e
531 $ cat e
532 e
532 e
533 $ cat e.orig
533 $ cat e.orig
534 z
534 z
535
535
536
536
537 unshelve and conflicts with tracked and untracked files
537 unshelve and conflicts with tracked and untracked files
538
538
539 preparing:
539 preparing:
540
540
541 $ rm *.orig
541 $ rm *.orig
542 $ hg ci -qm 'commit stuff'
542 $ hg ci -qm 'commit stuff'
543 $ hg phase -p null:
543 $ hg phase -p null:
544
544
545 no other changes - no merge:
545 no other changes - no merge:
546
546
547 $ echo f > f
547 $ echo f > f
548 $ hg add f
548 $ hg add f
549 $ hg shelve
549 $ hg shelve
550 shelved as default
550 shelved as default
551 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
551 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
552 $ echo g > f
552 $ echo g > f
553 $ hg unshelve
553 $ hg unshelve
554 unshelving change 'default'
554 unshelving change 'default'
555 $ hg st
555 $ hg st
556 A f
556 A f
557 ? f.orig
557 ? f.orig
558 $ cat f
558 $ cat f
559 f
559 f
560 $ cat f.orig
560 $ cat f.orig
561 g
561 g
562
562
563 other uncommitted changes - merge:
563 other uncommitted changes - merge:
564
564
565 $ hg st
565 $ hg st
566 A f
566 A f
567 ? f.orig
567 ? f.orig
568 $ hg shelve
568 $ hg shelve
569 shelved as default
569 shelved as default
570 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
570 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
571 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
571 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
572 o 4 changes to 'commit stuff' shelve@localhost
572 o 4 changes to 'commit stuff' shelve@localhost
573 |
573 |
574 $ hg log -G --template '{rev} {desc|firstline} {author}'
574 $ hg log -G --template '{rev} {desc|firstline} {author}'
575 @ 3 commit stuff test
575 @ 3 commit stuff test
576 |
576 |
577 | o 2 c test
577 | o 2 c test
578 |/
578 |/
579 o 0 a test
579 o 0 a test
580
580
581 $ mv f.orig f
581 $ mv f.orig f
582 $ echo 1 > a
582 $ echo 1 > a
583 $ hg unshelve --date '1073741824 0'
583 $ hg unshelve --date '1073741824 0'
584 unshelving change 'default'
584 unshelving change 'default'
585 temporarily committing pending changes (restore with 'hg unshelve --abort')
585 temporarily committing pending changes (restore with 'hg unshelve --abort')
586 rebasing shelved changes
586 rebasing shelved changes
587 merging f
587 merging f
588 warning: conflicts during merge.
588 warning: conflicts during merge.
589 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
589 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
590 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
590 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
591 [1]
591 [1]
592 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
592 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
593 @ 5 changes to 'commit stuff' shelve@localhost 1970-01-01 00:00 +0000
593 @ 5 changes to 'commit stuff' shelve@localhost 1970-01-01 00:00 +0000
594 |
594 |
595 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
595 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
596 |/
596 |/
597 o 3 commit stuff test 1970-01-01 00:00 +0000
597 o 3 commit stuff test 1970-01-01 00:00 +0000
598 |
598 |
599 | o 2 c test 1970-01-01 00:00 +0000
599 | o 2 c test 1970-01-01 00:00 +0000
600 |/
600 |/
601 o 0 a test 1970-01-01 00:00 +0000
601 o 0 a test 1970-01-01 00:00 +0000
602
602
603 $ hg st
603 $ hg st
604 M f
604 M f
605 ? f.orig
605 ? f.orig
606 $ cat f
606 $ cat f
607 <<<<<<< local: 5f6b880e719b - shelve: "pending changes temporary commit"
607 <<<<<<< dest: 5f6b880e719b - shelve: "pending changes temporary commit"
608 g
608 g
609 =======
609 =======
610 f
610 f
611 >>>>>>> other: 23b29cada8ba - shelve: "changes to 'commit stuff'"
611 >>>>>>> source: 23b29cada8ba - shelve: "changes to 'commit stuff'"
612 $ cat f.orig
612 $ cat f.orig
613 g
613 g
614 $ hg unshelve --abort
614 $ hg unshelve --abort
615 rebase aborted
615 rebase aborted
616 unshelve of 'default' aborted
616 unshelve of 'default' aborted
617 $ hg st
617 $ hg st
618 M a
618 M a
619 ? f.orig
619 ? f.orig
620 $ cat f.orig
620 $ cat f.orig
621 g
621 g
622 $ hg unshelve
622 $ hg unshelve
623 unshelving change 'default'
623 unshelving change 'default'
624 temporarily committing pending changes (restore with 'hg unshelve --abort')
624 temporarily committing pending changes (restore with 'hg unshelve --abort')
625 rebasing shelved changes
625 rebasing shelved changes
626 $ hg st
626 $ hg st
627 M a
627 M a
628 A f
628 A f
629 ? f.orig
629 ? f.orig
630
630
631 other committed changes - merge:
631 other committed changes - merge:
632
632
633 $ hg shelve f
633 $ hg shelve f
634 shelved as default
634 shelved as default
635 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
635 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
636 $ hg ci a -m 'intermediate other change'
636 $ hg ci a -m 'intermediate other change'
637 $ mv f.orig f
637 $ mv f.orig f
638 $ hg unshelve
638 $ hg unshelve
639 unshelving change 'default'
639 unshelving change 'default'
640 rebasing shelved changes
640 rebasing shelved changes
641 merging f
641 merging f
642 warning: conflicts during merge.
642 warning: conflicts during merge.
643 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
643 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
644 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
644 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
645 [1]
645 [1]
646 $ hg st
646 $ hg st
647 M f
647 M f
648 ? f.orig
648 ? f.orig
649 $ cat f
649 $ cat f
650 <<<<<<< local: 6b563750f973 - test: "intermediate other change"
650 <<<<<<< dest: 6b563750f973 - test: "intermediate other change"
651 g
651 g
652 =======
652 =======
653 f
653 f
654 >>>>>>> other: 23b29cada8ba - shelve: "changes to 'commit stuff'"
654 >>>>>>> source: 23b29cada8ba - shelve: "changes to 'commit stuff'"
655 $ cat f.orig
655 $ cat f.orig
656 g
656 g
657 $ hg unshelve --abort
657 $ hg unshelve --abort
658 rebase aborted
658 rebase aborted
659 no changes needed to a
659 no changes needed to a
660 no changes needed to d
660 no changes needed to d
661 no changes needed to e
661 no changes needed to e
662 unshelve of 'default' aborted
662 unshelve of 'default' aborted
663 $ hg st
663 $ hg st
664 ? f.orig
664 ? f.orig
665 $ cat f.orig
665 $ cat f.orig
666 g
666 g
667 $ hg shelve --delete default
667 $ hg shelve --delete default
668
668
669 $ cd ..
669 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now