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