##// END OF EJS Templates
rebase: fix crash when rebase aborts while rebasing obsolete revisions...
Laurent Charignon -
r28686:b212e01f default
parent child Browse files
Show More
@@ -1,1343 +1,1356
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 https://mercurial-scm.org/wiki/RebaseExtension
14 https://mercurial-scm.org/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, destutil, repoview, registrar, revset
19 from mercurial import copies, destutil, repoview, registrar, revset
20 from mercurial.commands import templateopts
20 from mercurial.commands import templateopts
21 from mercurial.node import nullrev, nullid, hex, short
21 from mercurial.node import nullrev, nullid, hex, short
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 # The following constants are used throughout the rebase module. The ordering of
26 # The following constants are used throughout the rebase module. The ordering of
27 # their values must be maintained.
27 # their values must be maintained.
28
28
29 # Indicates that a revision needs to be rebased
29 # Indicates that a revision needs to be rebased
30 revtodo = -1
30 revtodo = -1
31 nullmerge = -2
31 nullmerge = -2
32 revignored = -3
32 revignored = -3
33 # successor in rebase destination
33 # successor in rebase destination
34 revprecursor = -4
34 revprecursor = -4
35 # plain prune (no successor)
35 # plain prune (no successor)
36 revpruned = -5
36 revpruned = -5
37 revskipped = (revignored, revprecursor, revpruned)
37 revskipped = (revignored, revprecursor, revpruned)
38
38
39 cmdtable = {}
39 cmdtable = {}
40 command = cmdutil.command(cmdtable)
40 command = cmdutil.command(cmdtable)
41 # Note for extension authors: ONLY specify testedwith = 'internal' for
41 # Note for extension authors: ONLY specify testedwith = 'internal' for
42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
43 # be specifying the version(s) of Mercurial they are tested with, or
43 # be specifying the version(s) of Mercurial they are tested with, or
44 # leave the attribute unspecified.
44 # leave the attribute unspecified.
45 testedwith = 'internal'
45 testedwith = 'internal'
46
46
47 def _nothingtorebase():
47 def _nothingtorebase():
48 return 1
48 return 1
49
49
50 def _savegraft(ctx, extra):
50 def _savegraft(ctx, extra):
51 s = ctx.extra().get('source', None)
51 s = ctx.extra().get('source', None)
52 if s is not None:
52 if s is not None:
53 extra['source'] = s
53 extra['source'] = s
54 s = ctx.extra().get('intermediate-source', None)
54 s = ctx.extra().get('intermediate-source', None)
55 if s is not None:
55 if s is not None:
56 extra['intermediate-source'] = s
56 extra['intermediate-source'] = s
57
57
58 def _savebranch(ctx, extra):
58 def _savebranch(ctx, extra):
59 extra['branch'] = ctx.branch()
59 extra['branch'] = ctx.branch()
60
60
61 def _makeextrafn(copiers):
61 def _makeextrafn(copiers):
62 """make an extrafn out of the given copy-functions.
62 """make an extrafn out of the given copy-functions.
63
63
64 A copy function takes a context and an extra dict, and mutates the
64 A copy function takes a context and an extra dict, and mutates the
65 extra dict as needed based on the given context.
65 extra dict as needed based on the given context.
66 """
66 """
67 def extrafn(ctx, extra):
67 def extrafn(ctx, extra):
68 for c in copiers:
68 for c in copiers:
69 c(ctx, extra)
69 c(ctx, extra)
70 return extrafn
70 return extrafn
71
71
72 def _destrebase(repo, sourceset):
72 def _destrebase(repo, sourceset):
73 """small wrapper around destmerge to pass the right extra args
73 """small wrapper around destmerge to pass the right extra args
74
74
75 Please wrap destutil.destmerge instead."""
75 Please wrap destutil.destmerge instead."""
76 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
76 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
77 onheadcheck=False)
77 onheadcheck=False)
78
78
79 revsetpredicate = registrar.revsetpredicate()
79 revsetpredicate = registrar.revsetpredicate()
80
80
81 @revsetpredicate('_destrebase')
81 @revsetpredicate('_destrebase')
82 def _revsetdestrebase(repo, subset, x):
82 def _revsetdestrebase(repo, subset, x):
83 # ``_rebasedefaultdest()``
83 # ``_rebasedefaultdest()``
84
84
85 # default destination for rebase.
85 # default destination for rebase.
86 # # XXX: Currently private because I expect the signature to change.
86 # # XXX: Currently private because I expect the signature to change.
87 # # XXX: - bailing out in case of ambiguity vs returning all data.
87 # # XXX: - bailing out in case of ambiguity vs returning all data.
88 # i18n: "_rebasedefaultdest" is a keyword
88 # i18n: "_rebasedefaultdest" is a keyword
89 sourceset = None
89 sourceset = None
90 if x is not None:
90 if x is not None:
91 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
91 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
92 return subset & revset.baseset([_destrebase(repo, sourceset)])
92 return subset & revset.baseset([_destrebase(repo, sourceset)])
93
93
94 @command('rebase',
94 @command('rebase',
95 [('s', 'source', '',
95 [('s', 'source', '',
96 _('rebase the specified changeset and descendants'), _('REV')),
96 _('rebase the specified changeset and descendants'), _('REV')),
97 ('b', 'base', '',
97 ('b', 'base', '',
98 _('rebase everything from branching point of specified changeset'),
98 _('rebase everything from branching point of specified changeset'),
99 _('REV')),
99 _('REV')),
100 ('r', 'rev', [],
100 ('r', 'rev', [],
101 _('rebase these revisions'),
101 _('rebase these revisions'),
102 _('REV')),
102 _('REV')),
103 ('d', 'dest', '',
103 ('d', 'dest', '',
104 _('rebase onto the specified changeset'), _('REV')),
104 _('rebase onto the specified changeset'), _('REV')),
105 ('', 'collapse', False, _('collapse the rebased changesets')),
105 ('', 'collapse', False, _('collapse the rebased changesets')),
106 ('m', 'message', '',
106 ('m', 'message', '',
107 _('use text as collapse commit message'), _('TEXT')),
107 _('use text as collapse commit message'), _('TEXT')),
108 ('e', 'edit', False, _('invoke editor on commit messages')),
108 ('e', 'edit', False, _('invoke editor on commit messages')),
109 ('l', 'logfile', '',
109 ('l', 'logfile', '',
110 _('read collapse commit message from file'), _('FILE')),
110 _('read collapse commit message from file'), _('FILE')),
111 ('k', 'keep', False, _('keep original changesets')),
111 ('k', 'keep', False, _('keep original changesets')),
112 ('', 'keepbranches', False, _('keep original branch names')),
112 ('', 'keepbranches', False, _('keep original branch names')),
113 ('D', 'detach', False, _('(DEPRECATED)')),
113 ('D', 'detach', False, _('(DEPRECATED)')),
114 ('i', 'interactive', False, _('(DEPRECATED)')),
114 ('i', 'interactive', False, _('(DEPRECATED)')),
115 ('t', 'tool', '', _('specify merge tool')),
115 ('t', 'tool', '', _('specify merge tool')),
116 ('c', 'continue', False, _('continue an interrupted rebase')),
116 ('c', 'continue', False, _('continue an interrupted rebase')),
117 ('a', 'abort', False, _('abort an interrupted rebase'))] +
117 ('a', 'abort', False, _('abort an interrupted rebase'))] +
118 templateopts,
118 templateopts,
119 _('[-s REV | -b REV] [-d REV] [OPTION]'))
119 _('[-s REV | -b REV] [-d REV] [OPTION]'))
120 def rebase(ui, repo, **opts):
120 def rebase(ui, repo, **opts):
121 """move changeset (and descendants) to a different branch
121 """move changeset (and descendants) to a different branch
122
122
123 Rebase uses repeated merging to graft changesets from one part of
123 Rebase uses repeated merging to graft changesets from one part of
124 history (the source) onto another (the destination). This can be
124 history (the source) onto another (the destination). This can be
125 useful for linearizing *local* changes relative to a master
125 useful for linearizing *local* changes relative to a master
126 development tree.
126 development tree.
127
127
128 Published commits cannot be rebased (see :hg:`help phases`).
128 Published commits cannot be rebased (see :hg:`help phases`).
129 To copy commits, see :hg:`help graft`.
129 To copy commits, see :hg:`help graft`.
130
130
131 If you don't specify a destination changeset (``-d/--dest``), rebase
131 If you don't specify a destination changeset (``-d/--dest``), rebase
132 will use the same logic as :hg:`merge` to pick a destination. if
132 will use the same logic as :hg:`merge` to pick a destination. if
133 the current branch contains exactly one other head, the other head
133 the current branch contains exactly one other head, the other head
134 is merged with by default. Otherwise, an explicit revision with
134 is merged with by default. Otherwise, an explicit revision with
135 which to merge with must be provided. (destination changeset is not
135 which to merge with must be provided. (destination changeset is not
136 modified by rebasing, but new changesets are added as its
136 modified by rebasing, but new changesets are added as its
137 descendants.)
137 descendants.)
138
138
139 Here are the ways to select changesets:
139 Here are the ways to select changesets:
140
140
141 1. Explicitly select them using ``--rev``.
141 1. Explicitly select them using ``--rev``.
142
142
143 2. Use ``--source`` to select a root changeset and include all of its
143 2. Use ``--source`` to select a root changeset and include all of its
144 descendants.
144 descendants.
145
145
146 3. Use ``--base`` to select a changeset; rebase will find ancestors
146 3. Use ``--base`` to select a changeset; rebase will find ancestors
147 and their descendants which are not also ancestors of the destination.
147 and their descendants which are not also ancestors of the destination.
148
148
149 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
149 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
150 rebase will use ``--base .`` as above.
150 rebase will use ``--base .`` as above.
151
151
152 Rebase will destroy original changesets unless you use ``--keep``.
152 Rebase will destroy original changesets unless you use ``--keep``.
153 It will also move your bookmarks (even if you do).
153 It will also move your bookmarks (even if you do).
154
154
155 Some changesets may be dropped if they do not contribute changes
155 Some changesets may be dropped if they do not contribute changes
156 (e.g. merges from the destination branch).
156 (e.g. merges from the destination branch).
157
157
158 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
158 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
159 a named branch with two heads. You will need to explicitly specify source
159 a named branch with two heads. You will need to explicitly specify source
160 and/or destination.
160 and/or destination.
161
161
162 If you need to use a tool to automate merge/conflict decisions, you
162 If you need to use a tool to automate merge/conflict decisions, you
163 can specify one with ``--tool``, see :hg:`help merge-tools`.
163 can specify one with ``--tool``, see :hg:`help merge-tools`.
164 As a caveat: the tool will not be used to mediate when a file was
164 As a caveat: the tool will not be used to mediate when a file was
165 deleted, there is no hook presently available for this.
165 deleted, there is no hook presently available for this.
166
166
167 If a rebase is interrupted to manually resolve a conflict, it can be
167 If a rebase is interrupted to manually resolve a conflict, it can be
168 continued with --continue/-c or aborted with --abort/-a.
168 continued with --continue/-c or aborted with --abort/-a.
169
169
170 .. container:: verbose
170 .. container:: verbose
171
171
172 Examples:
172 Examples:
173
173
174 - move "local changes" (current commit back to branching point)
174 - move "local changes" (current commit back to branching point)
175 to the current branch tip after a pull::
175 to the current branch tip after a pull::
176
176
177 hg rebase
177 hg rebase
178
178
179 - move a single changeset to the stable branch::
179 - move a single changeset to the stable branch::
180
180
181 hg rebase -r 5f493448 -d stable
181 hg rebase -r 5f493448 -d stable
182
182
183 - splice a commit and all its descendants onto another part of history::
183 - splice a commit and all its descendants onto another part of history::
184
184
185 hg rebase --source c0c3 --dest 4cf9
185 hg rebase --source c0c3 --dest 4cf9
186
186
187 - rebase everything on a branch marked by a bookmark onto the
187 - rebase everything on a branch marked by a bookmark onto the
188 default branch::
188 default branch::
189
189
190 hg rebase --base myfeature --dest default
190 hg rebase --base myfeature --dest default
191
191
192 - collapse a sequence of changes into a single commit::
192 - collapse a sequence of changes into a single commit::
193
193
194 hg rebase --collapse -r 1520:1525 -d .
194 hg rebase --collapse -r 1520:1525 -d .
195
195
196 - move a named branch while preserving its name::
196 - move a named branch while preserving its name::
197
197
198 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
198 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
199
199
200 Returns 0 on success, 1 if nothing to rebase or there are
200 Returns 0 on success, 1 if nothing to rebase or there are
201 unresolved conflicts.
201 unresolved conflicts.
202
202
203 """
203 """
204 originalwd = target = None
204 originalwd = target = None
205 activebookmark = None
205 activebookmark = None
206 external = nullrev
206 external = nullrev
207 # Mapping between the old revision id and either what is the new rebased
207 # Mapping between the old revision id and either what is the new rebased
208 # revision or what needs to be done with the old revision. The state dict
208 # revision or what needs to be done with the old revision. The state dict
209 # will be what contains most of the rebase progress state.
209 # will be what contains most of the rebase progress state.
210 state = {}
210 state = {}
211 skipped = set()
211 skipped = set()
212 targetancestors = set()
212 targetancestors = set()
213
213
214
214
215 lock = wlock = None
215 lock = wlock = None
216 try:
216 try:
217 wlock = repo.wlock()
217 wlock = repo.wlock()
218 lock = repo.lock()
218 lock = repo.lock()
219
219
220 # Validate input and define rebasing points
220 # Validate input and define rebasing points
221 destf = opts.get('dest', None)
221 destf = opts.get('dest', None)
222 srcf = opts.get('source', None)
222 srcf = opts.get('source', None)
223 basef = opts.get('base', None)
223 basef = opts.get('base', None)
224 revf = opts.get('rev', [])
224 revf = opts.get('rev', [])
225 contf = opts.get('continue')
225 contf = opts.get('continue')
226 abortf = opts.get('abort')
226 abortf = opts.get('abort')
227 collapsef = opts.get('collapse', False)
227 collapsef = opts.get('collapse', False)
228 collapsemsg = cmdutil.logmessage(ui, opts)
228 collapsemsg = cmdutil.logmessage(ui, opts)
229 date = opts.get('date', None)
229 date = opts.get('date', None)
230 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
230 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
231 extrafns = [_savegraft]
231 extrafns = [_savegraft]
232 if e:
232 if e:
233 extrafns = [e]
233 extrafns = [e]
234 keepf = opts.get('keep', False)
234 keepf = opts.get('keep', False)
235 keepbranchesf = opts.get('keepbranches', False)
235 keepbranchesf = opts.get('keepbranches', False)
236 # keepopen is not meant for use on the command line, but by
236 # keepopen is not meant for use on the command line, but by
237 # other extensions
237 # other extensions
238 keepopen = opts.get('keepopen', False)
238 keepopen = opts.get('keepopen', False)
239
239
240 if opts.get('interactive'):
240 if opts.get('interactive'):
241 try:
241 try:
242 if extensions.find('histedit'):
242 if extensions.find('histedit'):
243 enablehistedit = ''
243 enablehistedit = ''
244 except KeyError:
244 except KeyError:
245 enablehistedit = " --config extensions.histedit="
245 enablehistedit = " --config extensions.histedit="
246 help = "hg%s help -e histedit" % enablehistedit
246 help = "hg%s help -e histedit" % enablehistedit
247 msg = _("interactive history editing is supported by the "
247 msg = _("interactive history editing is supported by the "
248 "'histedit' extension (see \"%s\")") % help
248 "'histedit' extension (see \"%s\")") % help
249 raise error.Abort(msg)
249 raise error.Abort(msg)
250
250
251 if collapsemsg and not collapsef:
251 if collapsemsg and not collapsef:
252 raise error.Abort(
252 raise error.Abort(
253 _('message can only be specified with collapse'))
253 _('message can only be specified with collapse'))
254
254
255 if contf or abortf:
255 if contf or abortf:
256 if contf and abortf:
256 if contf and abortf:
257 raise error.Abort(_('cannot use both abort and continue'))
257 raise error.Abort(_('cannot use both abort and continue'))
258 if collapsef:
258 if collapsef:
259 raise error.Abort(
259 raise error.Abort(
260 _('cannot use collapse with continue or abort'))
260 _('cannot use collapse with continue or abort'))
261 if srcf or basef or destf:
261 if srcf or basef or destf:
262 raise error.Abort(
262 raise error.Abort(
263 _('abort and continue do not allow specifying revisions'))
263 _('abort and continue do not allow specifying revisions'))
264 if abortf and opts.get('tool', False):
264 if abortf and opts.get('tool', False):
265 ui.warn(_('tool option will be ignored\n'))
265 ui.warn(_('tool option will be ignored\n'))
266
266
267 try:
267 try:
268 (originalwd, target, state, skipped, collapsef, keepf,
268 (originalwd, target, state, skipped, collapsef, keepf,
269 keepbranchesf, external, activebookmark) = restorestatus(repo)
269 keepbranchesf, external, activebookmark) = restorestatus(repo)
270 collapsemsg = restorecollapsemsg(repo)
270 collapsemsg = restorecollapsemsg(repo)
271 except error.RepoLookupError:
271 except error.RepoLookupError:
272 if abortf:
272 if abortf:
273 clearstatus(repo)
273 clearstatus(repo)
274 clearcollapsemsg(repo)
274 clearcollapsemsg(repo)
275 repo.ui.warn(_('rebase aborted (no revision is removed,'
275 repo.ui.warn(_('rebase aborted (no revision is removed,'
276 ' only broken state is cleared)\n'))
276 ' only broken state is cleared)\n'))
277 return 0
277 return 0
278 else:
278 else:
279 msg = _('cannot continue inconsistent rebase')
279 msg = _('cannot continue inconsistent rebase')
280 hint = _('use "hg rebase --abort" to clear broken state')
280 hint = _('use "hg rebase --abort" to clear broken state')
281 raise error.Abort(msg, hint=hint)
281 raise error.Abort(msg, hint=hint)
282 if abortf:
282 if abortf:
283 return abort(repo, originalwd, target, state,
283 return abort(repo, originalwd, target, state,
284 activebookmark=activebookmark)
284 activebookmark=activebookmark)
285
286 obsoletenotrebased = {}
287 if ui.configbool('experimental', 'rebaseskipobsolete',
288 default=True):
289 rebaseobsrevs = set([r for r, status in state.items()
290 if status == revprecursor])
291 rebasesetrevs = set(state.keys())
292 obsoletenotrebased = _computeobsoletenotrebased(repo,
293 rebaseobsrevs,
294 target)
295 rebaseobsskipped = set(obsoletenotrebased)
296 _checkobsrebase(repo, ui, rebaseobsrevs, rebasesetrevs,
297 rebaseobsskipped)
285 else:
298 else:
286 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf)
299 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf)
287 if dest is None:
300 if dest is None:
288 return _nothingtorebase()
301 return _nothingtorebase()
289
302
290 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
303 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
291 if (not (keepf or allowunstable)
304 if (not (keepf or allowunstable)
292 and repo.revs('first(children(%ld) - %ld)',
305 and repo.revs('first(children(%ld) - %ld)',
293 rebaseset, rebaseset)):
306 rebaseset, rebaseset)):
294 raise error.Abort(
307 raise error.Abort(
295 _("can't remove original changesets with"
308 _("can't remove original changesets with"
296 " unrebased descendants"),
309 " unrebased descendants"),
297 hint=_('use --keep to keep original changesets'))
310 hint=_('use --keep to keep original changesets'))
298
311
299 obsoletenotrebased = {}
312 obsoletenotrebased = {}
300 if ui.configbool('experimental', 'rebaseskipobsolete',
313 if ui.configbool('experimental', 'rebaseskipobsolete',
301 default=True):
314 default=True):
302 rebasesetrevs = set(rebaseset)
315 rebasesetrevs = set(rebaseset)
303 rebaseobsrevs = _filterobsoleterevs(repo, rebasesetrevs)
316 rebaseobsrevs = _filterobsoleterevs(repo, rebasesetrevs)
304 obsoletenotrebased = _computeobsoletenotrebased(repo,
317 obsoletenotrebased = _computeobsoletenotrebased(repo,
305 rebaseobsrevs,
318 rebaseobsrevs,
306 dest)
319 dest)
307 rebaseobsskipped = set(obsoletenotrebased)
320 rebaseobsskipped = set(obsoletenotrebased)
308 _checkobsrebase(repo, ui, rebaseobsrevs,
321 _checkobsrebase(repo, ui, rebaseobsrevs,
309 rebasesetrevs,
322 rebasesetrevs,
310 rebaseobsskipped)
323 rebaseobsskipped)
311
324
312 result = buildstate(repo, dest, rebaseset, collapsef,
325 result = buildstate(repo, dest, rebaseset, collapsef,
313 obsoletenotrebased)
326 obsoletenotrebased)
314
327
315 if not result:
328 if not result:
316 # Empty state built, nothing to rebase
329 # Empty state built, nothing to rebase
317 ui.status(_('nothing to rebase\n'))
330 ui.status(_('nothing to rebase\n'))
318 return _nothingtorebase()
331 return _nothingtorebase()
319
332
320 root = min(rebaseset)
333 root = min(rebaseset)
321 if not keepf and not repo[root].mutable():
334 if not keepf and not repo[root].mutable():
322 raise error.Abort(_("can't rebase public changeset %s")
335 raise error.Abort(_("can't rebase public changeset %s")
323 % repo[root],
336 % repo[root],
324 hint=_('see "hg help phases" for details'))
337 hint=_('see "hg help phases" for details'))
325
338
326 originalwd, target, state = result
339 originalwd, target, state = result
327 if collapsef:
340 if collapsef:
328 targetancestors = repo.changelog.ancestors([target],
341 targetancestors = repo.changelog.ancestors([target],
329 inclusive=True)
342 inclusive=True)
330 external = externalparent(repo, state, targetancestors)
343 external = externalparent(repo, state, targetancestors)
331
344
332 if dest.closesbranch() and not keepbranchesf:
345 if dest.closesbranch() and not keepbranchesf:
333 ui.status(_('reopening closed branch head %s\n') % dest)
346 ui.status(_('reopening closed branch head %s\n') % dest)
334
347
335 if keepbranchesf:
348 if keepbranchesf:
336 # insert _savebranch at the start of extrafns so if
349 # insert _savebranch at the start of extrafns so if
337 # there's a user-provided extrafn it can clobber branch if
350 # there's a user-provided extrafn it can clobber branch if
338 # desired
351 # desired
339 extrafns.insert(0, _savebranch)
352 extrafns.insert(0, _savebranch)
340 if collapsef:
353 if collapsef:
341 branches = set()
354 branches = set()
342 for rev in state:
355 for rev in state:
343 branches.add(repo[rev].branch())
356 branches.add(repo[rev].branch())
344 if len(branches) > 1:
357 if len(branches) > 1:
345 raise error.Abort(_('cannot collapse multiple named '
358 raise error.Abort(_('cannot collapse multiple named '
346 'branches'))
359 'branches'))
347
360
348 # Rebase
361 # Rebase
349 if not targetancestors:
362 if not targetancestors:
350 targetancestors = repo.changelog.ancestors([target], inclusive=True)
363 targetancestors = repo.changelog.ancestors([target], inclusive=True)
351
364
352 # Keep track of the current bookmarks in order to reset them later
365 # Keep track of the current bookmarks in order to reset them later
353 currentbookmarks = repo._bookmarks.copy()
366 currentbookmarks = repo._bookmarks.copy()
354 activebookmark = activebookmark or repo._activebookmark
367 activebookmark = activebookmark or repo._activebookmark
355 if activebookmark:
368 if activebookmark:
356 bookmarks.deactivate(repo)
369 bookmarks.deactivate(repo)
357
370
358 extrafn = _makeextrafn(extrafns)
371 extrafn = _makeextrafn(extrafns)
359
372
360 sortedstate = sorted(state)
373 sortedstate = sorted(state)
361 total = len(sortedstate)
374 total = len(sortedstate)
362 pos = 0
375 pos = 0
363 for rev in sortedstate:
376 for rev in sortedstate:
364 ctx = repo[rev]
377 ctx = repo[rev]
365 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
378 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
366 ctx.description().split('\n', 1)[0])
379 ctx.description().split('\n', 1)[0])
367 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
380 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
368 if names:
381 if names:
369 desc += ' (%s)' % ' '.join(names)
382 desc += ' (%s)' % ' '.join(names)
370 pos += 1
383 pos += 1
371 if state[rev] == revtodo:
384 if state[rev] == revtodo:
372 ui.status(_('rebasing %s\n') % desc)
385 ui.status(_('rebasing %s\n') % desc)
373 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
386 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
374 _('changesets'), total)
387 _('changesets'), total)
375 p1, p2, base = defineparents(repo, rev, target, state,
388 p1, p2, base = defineparents(repo, rev, target, state,
376 targetancestors)
389 targetancestors)
377 storestatus(repo, originalwd, target, state, collapsef, keepf,
390 storestatus(repo, originalwd, target, state, collapsef, keepf,
378 keepbranchesf, external, activebookmark)
391 keepbranchesf, external, activebookmark)
379 storecollapsemsg(repo, collapsemsg)
392 storecollapsemsg(repo, collapsemsg)
380 if len(repo[None].parents()) == 2:
393 if len(repo[None].parents()) == 2:
381 repo.ui.debug('resuming interrupted rebase\n')
394 repo.ui.debug('resuming interrupted rebase\n')
382 else:
395 else:
383 try:
396 try:
384 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
397 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
385 'rebase')
398 'rebase')
386 stats = rebasenode(repo, rev, p1, base, state,
399 stats = rebasenode(repo, rev, p1, base, state,
387 collapsef, target)
400 collapsef, target)
388 if stats and stats[3] > 0:
401 if stats and stats[3] > 0:
389 raise error.InterventionRequired(
402 raise error.InterventionRequired(
390 _('unresolved conflicts (see hg '
403 _('unresolved conflicts (see hg '
391 'resolve, then hg rebase --continue)'))
404 'resolve, then hg rebase --continue)'))
392 finally:
405 finally:
393 ui.setconfig('ui', 'forcemerge', '', 'rebase')
406 ui.setconfig('ui', 'forcemerge', '', 'rebase')
394 if not collapsef:
407 if not collapsef:
395 merging = p2 != nullrev
408 merging = p2 != nullrev
396 editform = cmdutil.mergeeditform(merging, 'rebase')
409 editform = cmdutil.mergeeditform(merging, 'rebase')
397 editor = cmdutil.getcommiteditor(editform=editform, **opts)
410 editor = cmdutil.getcommiteditor(editform=editform, **opts)
398 newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn,
411 newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn,
399 editor=editor,
412 editor=editor,
400 keepbranches=keepbranchesf,
413 keepbranches=keepbranchesf,
401 date=date)
414 date=date)
402 else:
415 else:
403 # Skip commit if we are collapsing
416 # Skip commit if we are collapsing
404 repo.dirstate.beginparentchange()
417 repo.dirstate.beginparentchange()
405 repo.setparents(repo[p1].node())
418 repo.setparents(repo[p1].node())
406 repo.dirstate.endparentchange()
419 repo.dirstate.endparentchange()
407 newnode = None
420 newnode = None
408 # Update the state
421 # Update the state
409 if newnode is not None:
422 if newnode is not None:
410 state[rev] = repo[newnode].rev()
423 state[rev] = repo[newnode].rev()
411 ui.debug('rebased as %s\n' % short(newnode))
424 ui.debug('rebased as %s\n' % short(newnode))
412 else:
425 else:
413 if not collapsef:
426 if not collapsef:
414 ui.warn(_('note: rebase of %d:%s created no changes '
427 ui.warn(_('note: rebase of %d:%s created no changes '
415 'to commit\n') % (rev, ctx))
428 'to commit\n') % (rev, ctx))
416 skipped.add(rev)
429 skipped.add(rev)
417 state[rev] = p1
430 state[rev] = p1
418 ui.debug('next revision set to %s\n' % p1)
431 ui.debug('next revision set to %s\n' % p1)
419 elif state[rev] == nullmerge:
432 elif state[rev] == nullmerge:
420 ui.debug('ignoring null merge rebase of %s\n' % rev)
433 ui.debug('ignoring null merge rebase of %s\n' % rev)
421 elif state[rev] == revignored:
434 elif state[rev] == revignored:
422 ui.status(_('not rebasing ignored %s\n') % desc)
435 ui.status(_('not rebasing ignored %s\n') % desc)
423 elif state[rev] == revprecursor:
436 elif state[rev] == revprecursor:
424 targetctx = repo[obsoletenotrebased[rev]]
437 targetctx = repo[obsoletenotrebased[rev]]
425 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
438 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
426 targetctx.description().split('\n', 1)[0])
439 targetctx.description().split('\n', 1)[0])
427 msg = _('note: not rebasing %s, already in destination as %s\n')
440 msg = _('note: not rebasing %s, already in destination as %s\n')
428 ui.status(msg % (desc, desctarget))
441 ui.status(msg % (desc, desctarget))
429 elif state[rev] == revpruned:
442 elif state[rev] == revpruned:
430 msg = _('note: not rebasing %s, it has no successor\n')
443 msg = _('note: not rebasing %s, it has no successor\n')
431 ui.status(msg % desc)
444 ui.status(msg % desc)
432 else:
445 else:
433 ui.status(_('already rebased %s as %s\n') %
446 ui.status(_('already rebased %s as %s\n') %
434 (desc, repo[state[rev]]))
447 (desc, repo[state[rev]]))
435
448
436 ui.progress(_('rebasing'), None)
449 ui.progress(_('rebasing'), None)
437 ui.note(_('rebase merging completed\n'))
450 ui.note(_('rebase merging completed\n'))
438
451
439 if collapsef and not keepopen:
452 if collapsef and not keepopen:
440 p1, p2, _base = defineparents(repo, min(state), target,
453 p1, p2, _base = defineparents(repo, min(state), target,
441 state, targetancestors)
454 state, targetancestors)
442 editopt = opts.get('edit')
455 editopt = opts.get('edit')
443 editform = 'rebase.collapse'
456 editform = 'rebase.collapse'
444 if collapsemsg:
457 if collapsemsg:
445 commitmsg = collapsemsg
458 commitmsg = collapsemsg
446 else:
459 else:
447 commitmsg = 'Collapsed revision'
460 commitmsg = 'Collapsed revision'
448 for rebased in state:
461 for rebased in state:
449 if rebased not in skipped and state[rebased] > nullmerge:
462 if rebased not in skipped and state[rebased] > nullmerge:
450 commitmsg += '\n* %s' % repo[rebased].description()
463 commitmsg += '\n* %s' % repo[rebased].description()
451 editopt = True
464 editopt = True
452 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
465 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
453 newnode = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
466 newnode = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
454 extrafn=extrafn, editor=editor,
467 extrafn=extrafn, editor=editor,
455 keepbranches=keepbranchesf,
468 keepbranches=keepbranchesf,
456 date=date)
469 date=date)
457 if newnode is None:
470 if newnode is None:
458 newrev = target
471 newrev = target
459 else:
472 else:
460 newrev = repo[newnode].rev()
473 newrev = repo[newnode].rev()
461 for oldrev in state.iterkeys():
474 for oldrev in state.iterkeys():
462 if state[oldrev] > nullmerge:
475 if state[oldrev] > nullmerge:
463 state[oldrev] = newrev
476 state[oldrev] = newrev
464
477
465 if 'qtip' in repo.tags():
478 if 'qtip' in repo.tags():
466 updatemq(repo, state, skipped, **opts)
479 updatemq(repo, state, skipped, **opts)
467
480
468 if currentbookmarks:
481 if currentbookmarks:
469 # Nodeids are needed to reset bookmarks
482 # Nodeids are needed to reset bookmarks
470 nstate = {}
483 nstate = {}
471 for k, v in state.iteritems():
484 for k, v in state.iteritems():
472 if v > nullmerge:
485 if v > nullmerge:
473 nstate[repo[k].node()] = repo[v].node()
486 nstate[repo[k].node()] = repo[v].node()
474 # XXX this is the same as dest.node() for the non-continue path --
487 # XXX this is the same as dest.node() for the non-continue path --
475 # this should probably be cleaned up
488 # this should probably be cleaned up
476 targetnode = repo[target].node()
489 targetnode = repo[target].node()
477
490
478 # restore original working directory
491 # restore original working directory
479 # (we do this before stripping)
492 # (we do this before stripping)
480 newwd = state.get(originalwd, originalwd)
493 newwd = state.get(originalwd, originalwd)
481 if newwd < 0:
494 if newwd < 0:
482 # original directory is a parent of rebase set root or ignored
495 # original directory is a parent of rebase set root or ignored
483 newwd = originalwd
496 newwd = originalwd
484 if newwd not in [c.rev() for c in repo[None].parents()]:
497 if newwd not in [c.rev() for c in repo[None].parents()]:
485 ui.note(_("update back to initial working directory parent\n"))
498 ui.note(_("update back to initial working directory parent\n"))
486 hg.updaterepo(repo, newwd, False)
499 hg.updaterepo(repo, newwd, False)
487
500
488 if not keepf:
501 if not keepf:
489 collapsedas = None
502 collapsedas = None
490 if collapsef:
503 if collapsef:
491 collapsedas = newnode
504 collapsedas = newnode
492 clearrebased(ui, repo, state, skipped, collapsedas)
505 clearrebased(ui, repo, state, skipped, collapsedas)
493
506
494 with repo.transaction('bookmark') as tr:
507 with repo.transaction('bookmark') as tr:
495 if currentbookmarks:
508 if currentbookmarks:
496 updatebookmarks(repo, targetnode, nstate, currentbookmarks, tr)
509 updatebookmarks(repo, targetnode, nstate, currentbookmarks, tr)
497 if activebookmark not in repo._bookmarks:
510 if activebookmark not in repo._bookmarks:
498 # active bookmark was divergent one and has been deleted
511 # active bookmark was divergent one and has been deleted
499 activebookmark = None
512 activebookmark = None
500 clearstatus(repo)
513 clearstatus(repo)
501 clearcollapsemsg(repo)
514 clearcollapsemsg(repo)
502
515
503 ui.note(_("rebase completed\n"))
516 ui.note(_("rebase completed\n"))
504 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
517 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
505 if skipped:
518 if skipped:
506 ui.note(_("%d revisions have been skipped\n") % len(skipped))
519 ui.note(_("%d revisions have been skipped\n") % len(skipped))
507
520
508 if (activebookmark and
521 if (activebookmark and
509 repo['.'].node() == repo._bookmarks[activebookmark]):
522 repo['.'].node() == repo._bookmarks[activebookmark]):
510 bookmarks.activate(repo, activebookmark)
523 bookmarks.activate(repo, activebookmark)
511
524
512 finally:
525 finally:
513 release(lock, wlock)
526 release(lock, wlock)
514
527
515 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[]):
528 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[]):
516 """use revisions argument to define destination and rebase set
529 """use revisions argument to define destination and rebase set
517 """
530 """
518 if srcf and basef:
531 if srcf and basef:
519 raise error.Abort(_('cannot specify both a source and a base'))
532 raise error.Abort(_('cannot specify both a source and a base'))
520 if revf and basef:
533 if revf and basef:
521 raise error.Abort(_('cannot specify both a revision and a base'))
534 raise error.Abort(_('cannot specify both a revision and a base'))
522 if revf and srcf:
535 if revf and srcf:
523 raise error.Abort(_('cannot specify both a revision and a source'))
536 raise error.Abort(_('cannot specify both a revision and a source'))
524
537
525 cmdutil.checkunfinished(repo)
538 cmdutil.checkunfinished(repo)
526 cmdutil.bailifchanged(repo)
539 cmdutil.bailifchanged(repo)
527
540
528 if destf:
541 if destf:
529 dest = scmutil.revsingle(repo, destf)
542 dest = scmutil.revsingle(repo, destf)
530
543
531 if revf:
544 if revf:
532 rebaseset = scmutil.revrange(repo, revf)
545 rebaseset = scmutil.revrange(repo, revf)
533 if not rebaseset:
546 if not rebaseset:
534 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
547 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
535 return None, None
548 return None, None
536 elif srcf:
549 elif srcf:
537 src = scmutil.revrange(repo, [srcf])
550 src = scmutil.revrange(repo, [srcf])
538 if not src:
551 if not src:
539 ui.status(_('empty "source" revision set - nothing to rebase\n'))
552 ui.status(_('empty "source" revision set - nothing to rebase\n'))
540 return None, None
553 return None, None
541 rebaseset = repo.revs('(%ld)::', src)
554 rebaseset = repo.revs('(%ld)::', src)
542 assert rebaseset
555 assert rebaseset
543 else:
556 else:
544 base = scmutil.revrange(repo, [basef or '.'])
557 base = scmutil.revrange(repo, [basef or '.'])
545 if not base:
558 if not base:
546 ui.status(_('empty "base" revision set - '
559 ui.status(_('empty "base" revision set - '
547 "can't compute rebase set\n"))
560 "can't compute rebase set\n"))
548 return None, None
561 return None, None
549 if not destf:
562 if not destf:
550 dest = repo[_destrebase(repo, base)]
563 dest = repo[_destrebase(repo, base)]
551 destf = str(dest)
564 destf = str(dest)
552
565
553 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
566 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
554 if commonanc is not None:
567 if commonanc is not None:
555 rebaseset = repo.revs('(%d::(%ld) - %d)::',
568 rebaseset = repo.revs('(%d::(%ld) - %d)::',
556 commonanc, base, commonanc)
569 commonanc, base, commonanc)
557 else:
570 else:
558 rebaseset = []
571 rebaseset = []
559
572
560 if not rebaseset:
573 if not rebaseset:
561 # transform to list because smartsets are not comparable to
574 # transform to list because smartsets are not comparable to
562 # lists. This should be improved to honor laziness of
575 # lists. This should be improved to honor laziness of
563 # smartset.
576 # smartset.
564 if list(base) == [dest.rev()]:
577 if list(base) == [dest.rev()]:
565 if basef:
578 if basef:
566 ui.status(_('nothing to rebase - %s is both "base"'
579 ui.status(_('nothing to rebase - %s is both "base"'
567 ' and destination\n') % dest)
580 ' and destination\n') % dest)
568 else:
581 else:
569 ui.status(_('nothing to rebase - working directory '
582 ui.status(_('nothing to rebase - working directory '
570 'parent is also destination\n'))
583 'parent is also destination\n'))
571 elif not repo.revs('%ld - ::%d', base, dest):
584 elif not repo.revs('%ld - ::%d', base, dest):
572 if basef:
585 if basef:
573 ui.status(_('nothing to rebase - "base" %s is '
586 ui.status(_('nothing to rebase - "base" %s is '
574 'already an ancestor of destination '
587 'already an ancestor of destination '
575 '%s\n') %
588 '%s\n') %
576 ('+'.join(str(repo[r]) for r in base),
589 ('+'.join(str(repo[r]) for r in base),
577 dest))
590 dest))
578 else:
591 else:
579 ui.status(_('nothing to rebase - working '
592 ui.status(_('nothing to rebase - working '
580 'directory parent is already an '
593 'directory parent is already an '
581 'ancestor of destination %s\n') % dest)
594 'ancestor of destination %s\n') % dest)
582 else: # can it happen?
595 else: # can it happen?
583 ui.status(_('nothing to rebase from %s to %s\n') %
596 ui.status(_('nothing to rebase from %s to %s\n') %
584 ('+'.join(str(repo[r]) for r in base), dest))
597 ('+'.join(str(repo[r]) for r in base), dest))
585 return None, None
598 return None, None
586
599
587 if not destf:
600 if not destf:
588 dest = repo[_destrebase(repo, rebaseset)]
601 dest = repo[_destrebase(repo, rebaseset)]
589 destf = str(dest)
602 destf = str(dest)
590
603
591 return dest, rebaseset
604 return dest, rebaseset
592
605
593 def externalparent(repo, state, targetancestors):
606 def externalparent(repo, state, targetancestors):
594 """Return the revision that should be used as the second parent
607 """Return the revision that should be used as the second parent
595 when the revisions in state is collapsed on top of targetancestors.
608 when the revisions in state is collapsed on top of targetancestors.
596 Abort if there is more than one parent.
609 Abort if there is more than one parent.
597 """
610 """
598 parents = set()
611 parents = set()
599 source = min(state)
612 source = min(state)
600 for rev in state:
613 for rev in state:
601 if rev == source:
614 if rev == source:
602 continue
615 continue
603 for p in repo[rev].parents():
616 for p in repo[rev].parents():
604 if (p.rev() not in state
617 if (p.rev() not in state
605 and p.rev() not in targetancestors):
618 and p.rev() not in targetancestors):
606 parents.add(p.rev())
619 parents.add(p.rev())
607 if not parents:
620 if not parents:
608 return nullrev
621 return nullrev
609 if len(parents) == 1:
622 if len(parents) == 1:
610 return parents.pop()
623 return parents.pop()
611 raise error.Abort(_('unable to collapse on top of %s, there is more '
624 raise error.Abort(_('unable to collapse on top of %s, there is more '
612 'than one external parent: %s') %
625 'than one external parent: %s') %
613 (max(targetancestors),
626 (max(targetancestors),
614 ', '.join(str(p) for p in sorted(parents))))
627 ', '.join(str(p) for p in sorted(parents))))
615
628
616 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
629 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
617 keepbranches=False, date=None):
630 keepbranches=False, date=None):
618 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
631 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
619 but also store useful information in extra.
632 but also store useful information in extra.
620 Return node of committed revision.'''
633 Return node of committed revision.'''
621 dsguard = cmdutil.dirstateguard(repo, 'rebase')
634 dsguard = cmdutil.dirstateguard(repo, 'rebase')
622 try:
635 try:
623 repo.setparents(repo[p1].node(), repo[p2].node())
636 repo.setparents(repo[p1].node(), repo[p2].node())
624 ctx = repo[rev]
637 ctx = repo[rev]
625 if commitmsg is None:
638 if commitmsg is None:
626 commitmsg = ctx.description()
639 commitmsg = ctx.description()
627 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
640 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
628 extra = {'rebase_source': ctx.hex()}
641 extra = {'rebase_source': ctx.hex()}
629 if extrafn:
642 if extrafn:
630 extrafn(ctx, extra)
643 extrafn(ctx, extra)
631
644
632 backup = repo.ui.backupconfig('phases', 'new-commit')
645 backup = repo.ui.backupconfig('phases', 'new-commit')
633 try:
646 try:
634 targetphase = max(ctx.phase(), phases.draft)
647 targetphase = max(ctx.phase(), phases.draft)
635 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
648 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
636 if keepbranch:
649 if keepbranch:
637 repo.ui.setconfig('ui', 'allowemptycommit', True)
650 repo.ui.setconfig('ui', 'allowemptycommit', True)
638 # Commit might fail if unresolved files exist
651 # Commit might fail if unresolved files exist
639 if date is None:
652 if date is None:
640 date = ctx.date()
653 date = ctx.date()
641 newnode = repo.commit(text=commitmsg, user=ctx.user(),
654 newnode = repo.commit(text=commitmsg, user=ctx.user(),
642 date=date, extra=extra, editor=editor)
655 date=date, extra=extra, editor=editor)
643 finally:
656 finally:
644 repo.ui.restoreconfig(backup)
657 repo.ui.restoreconfig(backup)
645
658
646 repo.dirstate.setbranch(repo[newnode].branch())
659 repo.dirstate.setbranch(repo[newnode].branch())
647 dsguard.close()
660 dsguard.close()
648 return newnode
661 return newnode
649 finally:
662 finally:
650 release(dsguard)
663 release(dsguard)
651
664
652 def rebasenode(repo, rev, p1, base, state, collapse, target):
665 def rebasenode(repo, rev, p1, base, state, collapse, target):
653 'Rebase a single revision rev on top of p1 using base as merge ancestor'
666 'Rebase a single revision rev on top of p1 using base as merge ancestor'
654 # Merge phase
667 # Merge phase
655 # Update to target and merge it with local
668 # Update to target and merge it with local
656 if repo['.'].rev() != p1:
669 if repo['.'].rev() != p1:
657 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
670 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
658 merge.update(repo, p1, False, True)
671 merge.update(repo, p1, False, True)
659 else:
672 else:
660 repo.ui.debug(" already in target\n")
673 repo.ui.debug(" already in target\n")
661 repo.dirstate.write(repo.currenttransaction())
674 repo.dirstate.write(repo.currenttransaction())
662 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
675 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
663 if base is not None:
676 if base is not None:
664 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
677 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
665 # When collapsing in-place, the parent is the common ancestor, we
678 # When collapsing in-place, the parent is the common ancestor, we
666 # have to allow merging with it.
679 # have to allow merging with it.
667 stats = merge.update(repo, rev, True, True, base, collapse,
680 stats = merge.update(repo, rev, True, True, base, collapse,
668 labels=['dest', 'source'])
681 labels=['dest', 'source'])
669 if collapse:
682 if collapse:
670 copies.duplicatecopies(repo, rev, target)
683 copies.duplicatecopies(repo, rev, target)
671 else:
684 else:
672 # If we're not using --collapse, we need to
685 # If we're not using --collapse, we need to
673 # duplicate copies between the revision we're
686 # duplicate copies between the revision we're
674 # rebasing and its first parent, but *not*
687 # rebasing and its first parent, but *not*
675 # duplicate any copies that have already been
688 # duplicate any copies that have already been
676 # performed in the destination.
689 # performed in the destination.
677 p1rev = repo[rev].p1().rev()
690 p1rev = repo[rev].p1().rev()
678 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
691 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
679 return stats
692 return stats
680
693
681 def nearestrebased(repo, rev, state):
694 def nearestrebased(repo, rev, state):
682 """return the nearest ancestors of rev in the rebase result"""
695 """return the nearest ancestors of rev in the rebase result"""
683 rebased = [r for r in state if state[r] > nullmerge]
696 rebased = [r for r in state if state[r] > nullmerge]
684 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
697 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
685 if candidates:
698 if candidates:
686 return state[candidates.first()]
699 return state[candidates.first()]
687 else:
700 else:
688 return None
701 return None
689
702
690 def _checkobsrebase(repo, ui,
703 def _checkobsrebase(repo, ui,
691 rebaseobsrevs,
704 rebaseobsrevs,
692 rebasesetrevs,
705 rebasesetrevs,
693 rebaseobsskipped):
706 rebaseobsskipped):
694 """
707 """
695 Abort if rebase will create divergence or rebase is noop because of markers
708 Abort if rebase will create divergence or rebase is noop because of markers
696
709
697 `rebaseobsrevs`: set of obsolete revision in source
710 `rebaseobsrevs`: set of obsolete revision in source
698 `rebasesetrevs`: set of revisions to be rebased from source
711 `rebasesetrevs`: set of revisions to be rebased from source
699 `rebaseobsskipped`: set of revisions from source skipped because they have
712 `rebaseobsskipped`: set of revisions from source skipped because they have
700 successors in destination
713 successors in destination
701 """
714 """
702 # Obsolete node with successors not in dest leads to divergence
715 # Obsolete node with successors not in dest leads to divergence
703 divergenceok = ui.configbool('experimental',
716 divergenceok = ui.configbool('experimental',
704 'allowdivergence')
717 'allowdivergence')
705 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
718 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
706
719
707 if divergencebasecandidates and not divergenceok:
720 if divergencebasecandidates and not divergenceok:
708 divhashes = (str(repo[r])
721 divhashes = (str(repo[r])
709 for r in divergencebasecandidates)
722 for r in divergencebasecandidates)
710 msg = _("this rebase will cause "
723 msg = _("this rebase will cause "
711 "divergences from: %s")
724 "divergences from: %s")
712 h = _("to force the rebase please set "
725 h = _("to force the rebase please set "
713 "experimental.allowdivergence=True")
726 "experimental.allowdivergence=True")
714 raise error.Abort(msg % (",".join(divhashes),), hint=h)
727 raise error.Abort(msg % (",".join(divhashes),), hint=h)
715
728
716 # - plain prune (no successor) changesets are rebased
729 # - plain prune (no successor) changesets are rebased
717 # - split changesets are not rebased if at least one of the
730 # - split changesets are not rebased if at least one of the
718 # changeset resulting from the split is an ancestor of dest
731 # changeset resulting from the split is an ancestor of dest
719 rebaseset = rebasesetrevs - rebaseobsskipped
732 rebaseset = rebasesetrevs - rebaseobsskipped
720 if rebasesetrevs and not rebaseset:
733 if rebasesetrevs and not rebaseset:
721 msg = _('all requested changesets have equivalents '
734 msg = _('all requested changesets have equivalents '
722 'or were marked as obsolete')
735 'or were marked as obsolete')
723 hint = _('to force the rebase, set the config '
736 hint = _('to force the rebase, set the config '
724 'experimental.rebaseskipobsolete to False')
737 'experimental.rebaseskipobsolete to False')
725 raise error.Abort(msg, hint=hint)
738 raise error.Abort(msg, hint=hint)
726
739
727 def defineparents(repo, rev, target, state, targetancestors):
740 def defineparents(repo, rev, target, state, targetancestors):
728 'Return the new parent relationship of the revision that will be rebased'
741 'Return the new parent relationship of the revision that will be rebased'
729 parents = repo[rev].parents()
742 parents = repo[rev].parents()
730 p1 = p2 = nullrev
743 p1 = p2 = nullrev
731
744
732 p1n = parents[0].rev()
745 p1n = parents[0].rev()
733 if p1n in targetancestors:
746 if p1n in targetancestors:
734 p1 = target
747 p1 = target
735 elif p1n in state:
748 elif p1n in state:
736 if state[p1n] == nullmerge:
749 if state[p1n] == nullmerge:
737 p1 = target
750 p1 = target
738 elif state[p1n] in revskipped:
751 elif state[p1n] in revskipped:
739 p1 = nearestrebased(repo, p1n, state)
752 p1 = nearestrebased(repo, p1n, state)
740 if p1 is None:
753 if p1 is None:
741 p1 = target
754 p1 = target
742 else:
755 else:
743 p1 = state[p1n]
756 p1 = state[p1n]
744 else: # p1n external
757 else: # p1n external
745 p1 = target
758 p1 = target
746 p2 = p1n
759 p2 = p1n
747
760
748 if len(parents) == 2 and parents[1].rev() not in targetancestors:
761 if len(parents) == 2 and parents[1].rev() not in targetancestors:
749 p2n = parents[1].rev()
762 p2n = parents[1].rev()
750 # interesting second parent
763 # interesting second parent
751 if p2n in state:
764 if p2n in state:
752 if p1 == target: # p1n in targetancestors or external
765 if p1 == target: # p1n in targetancestors or external
753 p1 = state[p2n]
766 p1 = state[p2n]
754 elif state[p2n] in revskipped:
767 elif state[p2n] in revskipped:
755 p2 = nearestrebased(repo, p2n, state)
768 p2 = nearestrebased(repo, p2n, state)
756 if p2 is None:
769 if p2 is None:
757 # no ancestors rebased yet, detach
770 # no ancestors rebased yet, detach
758 p2 = target
771 p2 = target
759 else:
772 else:
760 p2 = state[p2n]
773 p2 = state[p2n]
761 else: # p2n external
774 else: # p2n external
762 if p2 != nullrev: # p1n external too => rev is a merged revision
775 if p2 != nullrev: # p1n external too => rev is a merged revision
763 raise error.Abort(_('cannot use revision %d as base, result '
776 raise error.Abort(_('cannot use revision %d as base, result '
764 'would have 3 parents') % rev)
777 'would have 3 parents') % rev)
765 p2 = p2n
778 p2 = p2n
766 repo.ui.debug(" future parents are %d and %d\n" %
779 repo.ui.debug(" future parents are %d and %d\n" %
767 (repo[p1].rev(), repo[p2].rev()))
780 (repo[p1].rev(), repo[p2].rev()))
768
781
769 if not any(p.rev() in state for p in parents):
782 if not any(p.rev() in state for p in parents):
770 # Case (1) root changeset of a non-detaching rebase set.
783 # Case (1) root changeset of a non-detaching rebase set.
771 # Let the merge mechanism find the base itself.
784 # Let the merge mechanism find the base itself.
772 base = None
785 base = None
773 elif not repo[rev].p2():
786 elif not repo[rev].p2():
774 # Case (2) detaching the node with a single parent, use this parent
787 # Case (2) detaching the node with a single parent, use this parent
775 base = repo[rev].p1().rev()
788 base = repo[rev].p1().rev()
776 else:
789 else:
777 # Assuming there is a p1, this is the case where there also is a p2.
790 # Assuming there is a p1, this is the case where there also is a p2.
778 # We are thus rebasing a merge and need to pick the right merge base.
791 # We are thus rebasing a merge and need to pick the right merge base.
779 #
792 #
780 # Imagine we have:
793 # Imagine we have:
781 # - M: current rebase revision in this step
794 # - M: current rebase revision in this step
782 # - A: one parent of M
795 # - A: one parent of M
783 # - B: other parent of M
796 # - B: other parent of M
784 # - D: destination of this merge step (p1 var)
797 # - D: destination of this merge step (p1 var)
785 #
798 #
786 # Consider the case where D is a descendant of A or B and the other is
799 # Consider the case where D is a descendant of A or B and the other is
787 # 'outside'. In this case, the right merge base is the D ancestor.
800 # 'outside'. In this case, the right merge base is the D ancestor.
788 #
801 #
789 # An informal proof, assuming A is 'outside' and B is the D ancestor:
802 # An informal proof, assuming A is 'outside' and B is the D ancestor:
790 #
803 #
791 # If we pick B as the base, the merge involves:
804 # If we pick B as the base, the merge involves:
792 # - changes from B to M (actual changeset payload)
805 # - changes from B to M (actual changeset payload)
793 # - changes from B to D (induced by rebase) as D is a rebased
806 # - changes from B to D (induced by rebase) as D is a rebased
794 # version of B)
807 # version of B)
795 # Which exactly represent the rebase operation.
808 # Which exactly represent the rebase operation.
796 #
809 #
797 # If we pick A as the base, the merge involves:
810 # If we pick A as the base, the merge involves:
798 # - changes from A to M (actual changeset payload)
811 # - changes from A to M (actual changeset payload)
799 # - changes from A to D (with include changes between unrelated A and B
812 # - changes from A to D (with include changes between unrelated A and B
800 # plus changes induced by rebase)
813 # plus changes induced by rebase)
801 # Which does not represent anything sensible and creates a lot of
814 # Which does not represent anything sensible and creates a lot of
802 # conflicts. A is thus not the right choice - B is.
815 # conflicts. A is thus not the right choice - B is.
803 #
816 #
804 # Note: The base found in this 'proof' is only correct in the specified
817 # Note: The base found in this 'proof' is only correct in the specified
805 # case. This base does not make sense if is not D a descendant of A or B
818 # case. This base does not make sense if is not D a descendant of A or B
806 # or if the other is not parent 'outside' (especially not if the other
819 # or if the other is not parent 'outside' (especially not if the other
807 # parent has been rebased). The current implementation does not
820 # parent has been rebased). The current implementation does not
808 # make it feasible to consider different cases separately. In these
821 # make it feasible to consider different cases separately. In these
809 # other cases we currently just leave it to the user to correctly
822 # other cases we currently just leave it to the user to correctly
810 # resolve an impossible merge using a wrong ancestor.
823 # resolve an impossible merge using a wrong ancestor.
811 for p in repo[rev].parents():
824 for p in repo[rev].parents():
812 if state.get(p.rev()) == p1:
825 if state.get(p.rev()) == p1:
813 base = p.rev()
826 base = p.rev()
814 break
827 break
815 else: # fallback when base not found
828 else: # fallback when base not found
816 base = None
829 base = None
817
830
818 # Raise because this function is called wrong (see issue 4106)
831 # Raise because this function is called wrong (see issue 4106)
819 raise AssertionError('no base found to rebase on '
832 raise AssertionError('no base found to rebase on '
820 '(defineparents called wrong)')
833 '(defineparents called wrong)')
821 return p1, p2, base
834 return p1, p2, base
822
835
823 def isagitpatch(repo, patchname):
836 def isagitpatch(repo, patchname):
824 'Return true if the given patch is in git format'
837 'Return true if the given patch is in git format'
825 mqpatch = os.path.join(repo.mq.path, patchname)
838 mqpatch = os.path.join(repo.mq.path, patchname)
826 for line in patch.linereader(file(mqpatch, 'rb')):
839 for line in patch.linereader(file(mqpatch, 'rb')):
827 if line.startswith('diff --git'):
840 if line.startswith('diff --git'):
828 return True
841 return True
829 return False
842 return False
830
843
831 def updatemq(repo, state, skipped, **opts):
844 def updatemq(repo, state, skipped, **opts):
832 'Update rebased mq patches - finalize and then import them'
845 'Update rebased mq patches - finalize and then import them'
833 mqrebase = {}
846 mqrebase = {}
834 mq = repo.mq
847 mq = repo.mq
835 original_series = mq.fullseries[:]
848 original_series = mq.fullseries[:]
836 skippedpatches = set()
849 skippedpatches = set()
837
850
838 for p in mq.applied:
851 for p in mq.applied:
839 rev = repo[p.node].rev()
852 rev = repo[p.node].rev()
840 if rev in state:
853 if rev in state:
841 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
854 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
842 (rev, p.name))
855 (rev, p.name))
843 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
856 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
844 else:
857 else:
845 # Applied but not rebased, not sure this should happen
858 # Applied but not rebased, not sure this should happen
846 skippedpatches.add(p.name)
859 skippedpatches.add(p.name)
847
860
848 if mqrebase:
861 if mqrebase:
849 mq.finish(repo, mqrebase.keys())
862 mq.finish(repo, mqrebase.keys())
850
863
851 # We must start import from the newest revision
864 # We must start import from the newest revision
852 for rev in sorted(mqrebase, reverse=True):
865 for rev in sorted(mqrebase, reverse=True):
853 if rev not in skipped:
866 if rev not in skipped:
854 name, isgit = mqrebase[rev]
867 name, isgit = mqrebase[rev]
855 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
868 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
856 (name, state[rev], repo[state[rev]]))
869 (name, state[rev], repo[state[rev]]))
857 mq.qimport(repo, (), patchname=name, git=isgit,
870 mq.qimport(repo, (), patchname=name, git=isgit,
858 rev=[str(state[rev])])
871 rev=[str(state[rev])])
859 else:
872 else:
860 # Rebased and skipped
873 # Rebased and skipped
861 skippedpatches.add(mqrebase[rev][0])
874 skippedpatches.add(mqrebase[rev][0])
862
875
863 # Patches were either applied and rebased and imported in
876 # Patches were either applied and rebased and imported in
864 # order, applied and removed or unapplied. Discard the removed
877 # order, applied and removed or unapplied. Discard the removed
865 # ones while preserving the original series order and guards.
878 # ones while preserving the original series order and guards.
866 newseries = [s for s in original_series
879 newseries = [s for s in original_series
867 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
880 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
868 mq.fullseries[:] = newseries
881 mq.fullseries[:] = newseries
869 mq.seriesdirty = True
882 mq.seriesdirty = True
870 mq.savedirty()
883 mq.savedirty()
871
884
872 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
885 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
873 'Move bookmarks to their correct changesets, and delete divergent ones'
886 'Move bookmarks to their correct changesets, and delete divergent ones'
874 marks = repo._bookmarks
887 marks = repo._bookmarks
875 for k, v in originalbookmarks.iteritems():
888 for k, v in originalbookmarks.iteritems():
876 if v in nstate:
889 if v in nstate:
877 # update the bookmarks for revs that have moved
890 # update the bookmarks for revs that have moved
878 marks[k] = nstate[v]
891 marks[k] = nstate[v]
879 bookmarks.deletedivergent(repo, [targetnode], k)
892 bookmarks.deletedivergent(repo, [targetnode], k)
880 marks.recordchange(tr)
893 marks.recordchange(tr)
881
894
882 def storecollapsemsg(repo, collapsemsg):
895 def storecollapsemsg(repo, collapsemsg):
883 'Store the collapse message to allow recovery'
896 'Store the collapse message to allow recovery'
884 collapsemsg = collapsemsg or ''
897 collapsemsg = collapsemsg or ''
885 f = repo.vfs("last-message.txt", "w")
898 f = repo.vfs("last-message.txt", "w")
886 f.write("%s\n" % collapsemsg)
899 f.write("%s\n" % collapsemsg)
887 f.close()
900 f.close()
888
901
889 def clearcollapsemsg(repo):
902 def clearcollapsemsg(repo):
890 'Remove collapse message file'
903 'Remove collapse message file'
891 util.unlinkpath(repo.join("last-message.txt"), ignoremissing=True)
904 util.unlinkpath(repo.join("last-message.txt"), ignoremissing=True)
892
905
893 def restorecollapsemsg(repo):
906 def restorecollapsemsg(repo):
894 'Restore previously stored collapse message'
907 'Restore previously stored collapse message'
895 try:
908 try:
896 f = repo.vfs("last-message.txt")
909 f = repo.vfs("last-message.txt")
897 collapsemsg = f.readline().strip()
910 collapsemsg = f.readline().strip()
898 f.close()
911 f.close()
899 except IOError as err:
912 except IOError as err:
900 if err.errno != errno.ENOENT:
913 if err.errno != errno.ENOENT:
901 raise
914 raise
902 raise error.Abort(_('no rebase in progress'))
915 raise error.Abort(_('no rebase in progress'))
903 return collapsemsg
916 return collapsemsg
904
917
905 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
918 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
906 external, activebookmark):
919 external, activebookmark):
907 'Store the current status to allow recovery'
920 'Store the current status to allow recovery'
908 f = repo.vfs("rebasestate", "w")
921 f = repo.vfs("rebasestate", "w")
909 f.write(repo[originalwd].hex() + '\n')
922 f.write(repo[originalwd].hex() + '\n')
910 f.write(repo[target].hex() + '\n')
923 f.write(repo[target].hex() + '\n')
911 f.write(repo[external].hex() + '\n')
924 f.write(repo[external].hex() + '\n')
912 f.write('%d\n' % int(collapse))
925 f.write('%d\n' % int(collapse))
913 f.write('%d\n' % int(keep))
926 f.write('%d\n' % int(keep))
914 f.write('%d\n' % int(keepbranches))
927 f.write('%d\n' % int(keepbranches))
915 f.write('%s\n' % (activebookmark or ''))
928 f.write('%s\n' % (activebookmark or ''))
916 for d, v in state.iteritems():
929 for d, v in state.iteritems():
917 oldrev = repo[d].hex()
930 oldrev = repo[d].hex()
918 if v >= 0:
931 if v >= 0:
919 newrev = repo[v].hex()
932 newrev = repo[v].hex()
920 elif v == revtodo:
933 elif v == revtodo:
921 # To maintain format compatibility, we have to use nullid.
934 # To maintain format compatibility, we have to use nullid.
922 # Please do remove this special case when upgrading the format.
935 # Please do remove this special case when upgrading the format.
923 newrev = hex(nullid)
936 newrev = hex(nullid)
924 else:
937 else:
925 newrev = v
938 newrev = v
926 f.write("%s:%s\n" % (oldrev, newrev))
939 f.write("%s:%s\n" % (oldrev, newrev))
927 f.close()
940 f.close()
928 repo.ui.debug('rebase status stored\n')
941 repo.ui.debug('rebase status stored\n')
929
942
930 def clearstatus(repo):
943 def clearstatus(repo):
931 'Remove the status files'
944 'Remove the status files'
932 _clearrebasesetvisibiliy(repo)
945 _clearrebasesetvisibiliy(repo)
933 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
946 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
934
947
935 def restorestatus(repo):
948 def restorestatus(repo):
936 'Restore a previously stored status'
949 'Restore a previously stored status'
937 keepbranches = None
950 keepbranches = None
938 target = None
951 target = None
939 collapse = False
952 collapse = False
940 external = nullrev
953 external = nullrev
941 activebookmark = None
954 activebookmark = None
942 state = {}
955 state = {}
943
956
944 try:
957 try:
945 f = repo.vfs("rebasestate")
958 f = repo.vfs("rebasestate")
946 for i, l in enumerate(f.read().splitlines()):
959 for i, l in enumerate(f.read().splitlines()):
947 if i == 0:
960 if i == 0:
948 originalwd = repo[l].rev()
961 originalwd = repo[l].rev()
949 elif i == 1:
962 elif i == 1:
950 target = repo[l].rev()
963 target = repo[l].rev()
951 elif i == 2:
964 elif i == 2:
952 external = repo[l].rev()
965 external = repo[l].rev()
953 elif i == 3:
966 elif i == 3:
954 collapse = bool(int(l))
967 collapse = bool(int(l))
955 elif i == 4:
968 elif i == 4:
956 keep = bool(int(l))
969 keep = bool(int(l))
957 elif i == 5:
970 elif i == 5:
958 keepbranches = bool(int(l))
971 keepbranches = bool(int(l))
959 elif i == 6 and not (len(l) == 81 and ':' in l):
972 elif i == 6 and not (len(l) == 81 and ':' in l):
960 # line 6 is a recent addition, so for backwards compatibility
973 # line 6 is a recent addition, so for backwards compatibility
961 # check that the line doesn't look like the oldrev:newrev lines
974 # check that the line doesn't look like the oldrev:newrev lines
962 activebookmark = l
975 activebookmark = l
963 else:
976 else:
964 oldrev, newrev = l.split(':')
977 oldrev, newrev = l.split(':')
965 if newrev in (str(nullmerge), str(revignored),
978 if newrev in (str(nullmerge), str(revignored),
966 str(revprecursor), str(revpruned)):
979 str(revprecursor), str(revpruned)):
967 state[repo[oldrev].rev()] = int(newrev)
980 state[repo[oldrev].rev()] = int(newrev)
968 elif newrev == nullid:
981 elif newrev == nullid:
969 state[repo[oldrev].rev()] = revtodo
982 state[repo[oldrev].rev()] = revtodo
970 # Legacy compat special case
983 # Legacy compat special case
971 else:
984 else:
972 state[repo[oldrev].rev()] = repo[newrev].rev()
985 state[repo[oldrev].rev()] = repo[newrev].rev()
973
986
974 except IOError as err:
987 except IOError as err:
975 if err.errno != errno.ENOENT:
988 if err.errno != errno.ENOENT:
976 raise
989 raise
977 cmdutil.wrongtooltocontinue(repo, _('rebase'))
990 cmdutil.wrongtooltocontinue(repo, _('rebase'))
978
991
979 if keepbranches is None:
992 if keepbranches is None:
980 raise error.Abort(_('.hg/rebasestate is incomplete'))
993 raise error.Abort(_('.hg/rebasestate is incomplete'))
981
994
982 skipped = set()
995 skipped = set()
983 # recompute the set of skipped revs
996 # recompute the set of skipped revs
984 if not collapse:
997 if not collapse:
985 seen = set([target])
998 seen = set([target])
986 for old, new in sorted(state.items()):
999 for old, new in sorted(state.items()):
987 if new != revtodo and new in seen:
1000 if new != revtodo and new in seen:
988 skipped.add(old)
1001 skipped.add(old)
989 seen.add(new)
1002 seen.add(new)
990 repo.ui.debug('computed skipped revs: %s\n' %
1003 repo.ui.debug('computed skipped revs: %s\n' %
991 (' '.join(str(r) for r in sorted(skipped)) or None))
1004 (' '.join(str(r) for r in sorted(skipped)) or None))
992 repo.ui.debug('rebase status resumed\n')
1005 repo.ui.debug('rebase status resumed\n')
993 _setrebasesetvisibility(repo, state.keys())
1006 _setrebasesetvisibility(repo, state.keys())
994 return (originalwd, target, state, skipped,
1007 return (originalwd, target, state, skipped,
995 collapse, keep, keepbranches, external, activebookmark)
1008 collapse, keep, keepbranches, external, activebookmark)
996
1009
997 def needupdate(repo, state):
1010 def needupdate(repo, state):
998 '''check whether we should `update --clean` away from a merge, or if
1011 '''check whether we should `update --clean` away from a merge, or if
999 somehow the working dir got forcibly updated, e.g. by older hg'''
1012 somehow the working dir got forcibly updated, e.g. by older hg'''
1000 parents = [p.rev() for p in repo[None].parents()]
1013 parents = [p.rev() for p in repo[None].parents()]
1001
1014
1002 # Are we in a merge state at all?
1015 # Are we in a merge state at all?
1003 if len(parents) < 2:
1016 if len(parents) < 2:
1004 return False
1017 return False
1005
1018
1006 # We should be standing on the first as-of-yet unrebased commit.
1019 # We should be standing on the first as-of-yet unrebased commit.
1007 firstunrebased = min([old for old, new in state.iteritems()
1020 firstunrebased = min([old for old, new in state.iteritems()
1008 if new == nullrev])
1021 if new == nullrev])
1009 if firstunrebased in parents:
1022 if firstunrebased in parents:
1010 return True
1023 return True
1011
1024
1012 return False
1025 return False
1013
1026
1014 def abort(repo, originalwd, target, state, activebookmark=None):
1027 def abort(repo, originalwd, target, state, activebookmark=None):
1015 '''Restore the repository to its original state. Additional args:
1028 '''Restore the repository to its original state. Additional args:
1016
1029
1017 activebookmark: the name of the bookmark that should be active after the
1030 activebookmark: the name of the bookmark that should be active after the
1018 restore'''
1031 restore'''
1019
1032
1020 try:
1033 try:
1021 # If the first commits in the rebased set get skipped during the rebase,
1034 # If the first commits in the rebased set get skipped during the rebase,
1022 # their values within the state mapping will be the target rev id. The
1035 # their values within the state mapping will be the target rev id. The
1023 # dstates list must must not contain the target rev (issue4896)
1036 # dstates list must must not contain the target rev (issue4896)
1024 dstates = [s for s in state.values() if s >= 0 and s != target]
1037 dstates = [s for s in state.values() if s >= 0 and s != target]
1025 immutable = [d for d in dstates if not repo[d].mutable()]
1038 immutable = [d for d in dstates if not repo[d].mutable()]
1026 cleanup = True
1039 cleanup = True
1027 if immutable:
1040 if immutable:
1028 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1041 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1029 % ', '.join(str(repo[r]) for r in immutable),
1042 % ', '.join(str(repo[r]) for r in immutable),
1030 hint=_('see "hg help phases" for details'))
1043 hint=_('see "hg help phases" for details'))
1031 cleanup = False
1044 cleanup = False
1032
1045
1033 descendants = set()
1046 descendants = set()
1034 if dstates:
1047 if dstates:
1035 descendants = set(repo.changelog.descendants(dstates))
1048 descendants = set(repo.changelog.descendants(dstates))
1036 if descendants - set(dstates):
1049 if descendants - set(dstates):
1037 repo.ui.warn(_("warning: new changesets detected on target branch, "
1050 repo.ui.warn(_("warning: new changesets detected on target branch, "
1038 "can't strip\n"))
1051 "can't strip\n"))
1039 cleanup = False
1052 cleanup = False
1040
1053
1041 if cleanup:
1054 if cleanup:
1042 shouldupdate = False
1055 shouldupdate = False
1043 rebased = filter(lambda x: x >= 0 and x != target, state.values())
1056 rebased = filter(lambda x: x >= 0 and x != target, state.values())
1044 if rebased:
1057 if rebased:
1045 strippoints = [
1058 strippoints = [
1046 c.node() for c in repo.set('roots(%ld)', rebased)]
1059 c.node() for c in repo.set('roots(%ld)', rebased)]
1047 shouldupdate = len([
1060 shouldupdate = len([
1048 c.node() for c in repo.set('. & (%ld)', rebased)]) > 0
1061 c.node() for c in repo.set('. & (%ld)', rebased)]) > 0
1049
1062
1050 # Update away from the rebase if necessary
1063 # Update away from the rebase if necessary
1051 if shouldupdate or needupdate(repo, state):
1064 if shouldupdate or needupdate(repo, state):
1052 merge.update(repo, originalwd, False, True)
1065 merge.update(repo, originalwd, False, True)
1053
1066
1054 # Strip from the first rebased revision
1067 # Strip from the first rebased revision
1055 if rebased:
1068 if rebased:
1056 # no backup of rebased cset versions needed
1069 # no backup of rebased cset versions needed
1057 repair.strip(repo.ui, repo, strippoints)
1070 repair.strip(repo.ui, repo, strippoints)
1058
1071
1059 if activebookmark and activebookmark in repo._bookmarks:
1072 if activebookmark and activebookmark in repo._bookmarks:
1060 bookmarks.activate(repo, activebookmark)
1073 bookmarks.activate(repo, activebookmark)
1061
1074
1062 finally:
1075 finally:
1063 clearstatus(repo)
1076 clearstatus(repo)
1064 clearcollapsemsg(repo)
1077 clearcollapsemsg(repo)
1065 repo.ui.warn(_('rebase aborted\n'))
1078 repo.ui.warn(_('rebase aborted\n'))
1066 return 0
1079 return 0
1067
1080
1068 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1081 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1069 '''Define which revisions are going to be rebased and where
1082 '''Define which revisions are going to be rebased and where
1070
1083
1071 repo: repo
1084 repo: repo
1072 dest: context
1085 dest: context
1073 rebaseset: set of rev
1086 rebaseset: set of rev
1074 '''
1087 '''
1075 _setrebasesetvisibility(repo, rebaseset)
1088 _setrebasesetvisibility(repo, rebaseset)
1076
1089
1077 # This check isn't strictly necessary, since mq detects commits over an
1090 # This check isn't strictly necessary, since mq detects commits over an
1078 # applied patch. But it prevents messing up the working directory when
1091 # applied patch. But it prevents messing up the working directory when
1079 # a partially completed rebase is blocked by mq.
1092 # a partially completed rebase is blocked by mq.
1080 if 'qtip' in repo.tags() and (dest.node() in
1093 if 'qtip' in repo.tags() and (dest.node() in
1081 [s.node for s in repo.mq.applied]):
1094 [s.node for s in repo.mq.applied]):
1082 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1095 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1083
1096
1084 roots = list(repo.set('roots(%ld)', rebaseset))
1097 roots = list(repo.set('roots(%ld)', rebaseset))
1085 if not roots:
1098 if not roots:
1086 raise error.Abort(_('no matching revisions'))
1099 raise error.Abort(_('no matching revisions'))
1087 roots.sort()
1100 roots.sort()
1088 state = {}
1101 state = {}
1089 detachset = set()
1102 detachset = set()
1090 for root in roots:
1103 for root in roots:
1091 commonbase = root.ancestor(dest)
1104 commonbase = root.ancestor(dest)
1092 if commonbase == root:
1105 if commonbase == root:
1093 raise error.Abort(_('source is ancestor of destination'))
1106 raise error.Abort(_('source is ancestor of destination'))
1094 if commonbase == dest:
1107 if commonbase == dest:
1095 samebranch = root.branch() == dest.branch()
1108 samebranch = root.branch() == dest.branch()
1096 if not collapse and samebranch and root in dest.children():
1109 if not collapse and samebranch and root in dest.children():
1097 repo.ui.debug('source is a child of destination\n')
1110 repo.ui.debug('source is a child of destination\n')
1098 return None
1111 return None
1099
1112
1100 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
1113 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
1101 state.update(dict.fromkeys(rebaseset, revtodo))
1114 state.update(dict.fromkeys(rebaseset, revtodo))
1102 # Rebase tries to turn <dest> into a parent of <root> while
1115 # Rebase tries to turn <dest> into a parent of <root> while
1103 # preserving the number of parents of rebased changesets:
1116 # preserving the number of parents of rebased changesets:
1104 #
1117 #
1105 # - A changeset with a single parent will always be rebased as a
1118 # - A changeset with a single parent will always be rebased as a
1106 # changeset with a single parent.
1119 # changeset with a single parent.
1107 #
1120 #
1108 # - A merge will be rebased as merge unless its parents are both
1121 # - A merge will be rebased as merge unless its parents are both
1109 # ancestors of <dest> or are themselves in the rebased set and
1122 # ancestors of <dest> or are themselves in the rebased set and
1110 # pruned while rebased.
1123 # pruned while rebased.
1111 #
1124 #
1112 # If one parent of <root> is an ancestor of <dest>, the rebased
1125 # If one parent of <root> is an ancestor of <dest>, the rebased
1113 # version of this parent will be <dest>. This is always true with
1126 # version of this parent will be <dest>. This is always true with
1114 # --base option.
1127 # --base option.
1115 #
1128 #
1116 # Otherwise, we need to *replace* the original parents with
1129 # Otherwise, we need to *replace* the original parents with
1117 # <dest>. This "detaches" the rebased set from its former location
1130 # <dest>. This "detaches" the rebased set from its former location
1118 # and rebases it onto <dest>. Changes introduced by ancestors of
1131 # and rebases it onto <dest>. Changes introduced by ancestors of
1119 # <root> not common with <dest> (the detachset, marked as
1132 # <root> not common with <dest> (the detachset, marked as
1120 # nullmerge) are "removed" from the rebased changesets.
1133 # nullmerge) are "removed" from the rebased changesets.
1121 #
1134 #
1122 # - If <root> has a single parent, set it to <dest>.
1135 # - If <root> has a single parent, set it to <dest>.
1123 #
1136 #
1124 # - If <root> is a merge, we cannot decide which parent to
1137 # - If <root> is a merge, we cannot decide which parent to
1125 # replace, the rebase operation is not clearly defined.
1138 # replace, the rebase operation is not clearly defined.
1126 #
1139 #
1127 # The table below sums up this behavior:
1140 # The table below sums up this behavior:
1128 #
1141 #
1129 # +------------------+----------------------+-------------------------+
1142 # +------------------+----------------------+-------------------------+
1130 # | | one parent | merge |
1143 # | | one parent | merge |
1131 # +------------------+----------------------+-------------------------+
1144 # +------------------+----------------------+-------------------------+
1132 # | parent in | new parent is <dest> | parents in ::<dest> are |
1145 # | parent in | new parent is <dest> | parents in ::<dest> are |
1133 # | ::<dest> | | remapped to <dest> |
1146 # | ::<dest> | | remapped to <dest> |
1134 # +------------------+----------------------+-------------------------+
1147 # +------------------+----------------------+-------------------------+
1135 # | unrelated source | new parent is <dest> | ambiguous, abort |
1148 # | unrelated source | new parent is <dest> | ambiguous, abort |
1136 # +------------------+----------------------+-------------------------+
1149 # +------------------+----------------------+-------------------------+
1137 #
1150 #
1138 # The actual abort is handled by `defineparents`
1151 # The actual abort is handled by `defineparents`
1139 if len(root.parents()) <= 1:
1152 if len(root.parents()) <= 1:
1140 # ancestors of <root> not ancestors of <dest>
1153 # ancestors of <root> not ancestors of <dest>
1141 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1154 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1142 [root.rev()]))
1155 [root.rev()]))
1143 for r in detachset:
1156 for r in detachset:
1144 if r not in state:
1157 if r not in state:
1145 state[r] = nullmerge
1158 state[r] = nullmerge
1146 if len(roots) > 1:
1159 if len(roots) > 1:
1147 # If we have multiple roots, we may have "hole" in the rebase set.
1160 # If we have multiple roots, we may have "hole" in the rebase set.
1148 # Rebase roots that descend from those "hole" should not be detached as
1161 # Rebase roots that descend from those "hole" should not be detached as
1149 # other root are. We use the special `revignored` to inform rebase that
1162 # other root are. We use the special `revignored` to inform rebase that
1150 # the revision should be ignored but that `defineparents` should search
1163 # the revision should be ignored but that `defineparents` should search
1151 # a rebase destination that make sense regarding rebased topology.
1164 # a rebase destination that make sense regarding rebased topology.
1152 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1165 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1153 for ignored in set(rebasedomain) - set(rebaseset):
1166 for ignored in set(rebasedomain) - set(rebaseset):
1154 state[ignored] = revignored
1167 state[ignored] = revignored
1155 for r in obsoletenotrebased:
1168 for r in obsoletenotrebased:
1156 if obsoletenotrebased[r] is None:
1169 if obsoletenotrebased[r] is None:
1157 state[r] = revpruned
1170 state[r] = revpruned
1158 else:
1171 else:
1159 state[r] = revprecursor
1172 state[r] = revprecursor
1160 return repo['.'].rev(), dest.rev(), state
1173 return repo['.'].rev(), dest.rev(), state
1161
1174
1162 def clearrebased(ui, repo, state, skipped, collapsedas=None):
1175 def clearrebased(ui, repo, state, skipped, collapsedas=None):
1163 """dispose of rebased revision at the end of the rebase
1176 """dispose of rebased revision at the end of the rebase
1164
1177
1165 If `collapsedas` is not None, the rebase was a collapse whose result if the
1178 If `collapsedas` is not None, the rebase was a collapse whose result if the
1166 `collapsedas` node."""
1179 `collapsedas` node."""
1167 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1180 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1168 markers = []
1181 markers = []
1169 for rev, newrev in sorted(state.items()):
1182 for rev, newrev in sorted(state.items()):
1170 if newrev >= 0:
1183 if newrev >= 0:
1171 if rev in skipped:
1184 if rev in skipped:
1172 succs = ()
1185 succs = ()
1173 elif collapsedas is not None:
1186 elif collapsedas is not None:
1174 succs = (repo[collapsedas],)
1187 succs = (repo[collapsedas],)
1175 else:
1188 else:
1176 succs = (repo[newrev],)
1189 succs = (repo[newrev],)
1177 markers.append((repo[rev], succs))
1190 markers.append((repo[rev], succs))
1178 if markers:
1191 if markers:
1179 obsolete.createmarkers(repo, markers)
1192 obsolete.createmarkers(repo, markers)
1180 else:
1193 else:
1181 rebased = [rev for rev in state if state[rev] > nullmerge]
1194 rebased = [rev for rev in state if state[rev] > nullmerge]
1182 if rebased:
1195 if rebased:
1183 stripped = []
1196 stripped = []
1184 for root in repo.set('roots(%ld)', rebased):
1197 for root in repo.set('roots(%ld)', rebased):
1185 if set(repo.changelog.descendants([root.rev()])) - set(state):
1198 if set(repo.changelog.descendants([root.rev()])) - set(state):
1186 ui.warn(_("warning: new changesets detected "
1199 ui.warn(_("warning: new changesets detected "
1187 "on source branch, not stripping\n"))
1200 "on source branch, not stripping\n"))
1188 else:
1201 else:
1189 stripped.append(root.node())
1202 stripped.append(root.node())
1190 if stripped:
1203 if stripped:
1191 # backup the old csets by default
1204 # backup the old csets by default
1192 repair.strip(ui, repo, stripped, "all")
1205 repair.strip(ui, repo, stripped, "all")
1193
1206
1194
1207
1195 def pullrebase(orig, ui, repo, *args, **opts):
1208 def pullrebase(orig, ui, repo, *args, **opts):
1196 'Call rebase after pull if the latter has been invoked with --rebase'
1209 'Call rebase after pull if the latter has been invoked with --rebase'
1197 ret = None
1210 ret = None
1198 if opts.get('rebase'):
1211 if opts.get('rebase'):
1199 wlock = lock = None
1212 wlock = lock = None
1200 try:
1213 try:
1201 wlock = repo.wlock()
1214 wlock = repo.wlock()
1202 lock = repo.lock()
1215 lock = repo.lock()
1203 if opts.get('update'):
1216 if opts.get('update'):
1204 del opts['update']
1217 del opts['update']
1205 ui.debug('--update and --rebase are not compatible, ignoring '
1218 ui.debug('--update and --rebase are not compatible, ignoring '
1206 'the update flag\n')
1219 'the update flag\n')
1207
1220
1208 revsprepull = len(repo)
1221 revsprepull = len(repo)
1209 origpostincoming = commands.postincoming
1222 origpostincoming = commands.postincoming
1210 def _dummy(*args, **kwargs):
1223 def _dummy(*args, **kwargs):
1211 pass
1224 pass
1212 commands.postincoming = _dummy
1225 commands.postincoming = _dummy
1213 try:
1226 try:
1214 ret = orig(ui, repo, *args, **opts)
1227 ret = orig(ui, repo, *args, **opts)
1215 finally:
1228 finally:
1216 commands.postincoming = origpostincoming
1229 commands.postincoming = origpostincoming
1217 revspostpull = len(repo)
1230 revspostpull = len(repo)
1218 if revspostpull > revsprepull:
1231 if revspostpull > revsprepull:
1219 # --rev option from pull conflict with rebase own --rev
1232 # --rev option from pull conflict with rebase own --rev
1220 # dropping it
1233 # dropping it
1221 if 'rev' in opts:
1234 if 'rev' in opts:
1222 del opts['rev']
1235 del opts['rev']
1223 # positional argument from pull conflicts with rebase's own
1236 # positional argument from pull conflicts with rebase's own
1224 # --source.
1237 # --source.
1225 if 'source' in opts:
1238 if 'source' in opts:
1226 del opts['source']
1239 del opts['source']
1227 try:
1240 try:
1228 rebase(ui, repo, **opts)
1241 rebase(ui, repo, **opts)
1229 except error.NoMergeDestAbort:
1242 except error.NoMergeDestAbort:
1230 # we can maybe update instead
1243 # we can maybe update instead
1231 rev, _a, _b = destutil.destupdate(repo)
1244 rev, _a, _b = destutil.destupdate(repo)
1232 if rev == repo['.'].rev():
1245 if rev == repo['.'].rev():
1233 ui.status(_('nothing to rebase\n'))
1246 ui.status(_('nothing to rebase\n'))
1234 else:
1247 else:
1235 ui.status(_('nothing to rebase - updating instead\n'))
1248 ui.status(_('nothing to rebase - updating instead\n'))
1236 # not passing argument to get the bare update behavior
1249 # not passing argument to get the bare update behavior
1237 # with warning and trumpets
1250 # with warning and trumpets
1238 commands.update(ui, repo)
1251 commands.update(ui, repo)
1239 finally:
1252 finally:
1240 release(lock, wlock)
1253 release(lock, wlock)
1241 else:
1254 else:
1242 if opts.get('tool'):
1255 if opts.get('tool'):
1243 raise error.Abort(_('--tool can only be used with --rebase'))
1256 raise error.Abort(_('--tool can only be used with --rebase'))
1244 ret = orig(ui, repo, *args, **opts)
1257 ret = orig(ui, repo, *args, **opts)
1245
1258
1246 return ret
1259 return ret
1247
1260
1248 def _setrebasesetvisibility(repo, revs):
1261 def _setrebasesetvisibility(repo, revs):
1249 """store the currently rebased set on the repo object
1262 """store the currently rebased set on the repo object
1250
1263
1251 This is used by another function to prevent rebased revision to because
1264 This is used by another function to prevent rebased revision to because
1252 hidden (see issue4505)"""
1265 hidden (see issue4505)"""
1253 repo = repo.unfiltered()
1266 repo = repo.unfiltered()
1254 revs = set(revs)
1267 revs = set(revs)
1255 repo._rebaseset = revs
1268 repo._rebaseset = revs
1256 # invalidate cache if visibility changes
1269 # invalidate cache if visibility changes
1257 hiddens = repo.filteredrevcache.get('visible', set())
1270 hiddens = repo.filteredrevcache.get('visible', set())
1258 if revs & hiddens:
1271 if revs & hiddens:
1259 repo.invalidatevolatilesets()
1272 repo.invalidatevolatilesets()
1260
1273
1261 def _clearrebasesetvisibiliy(repo):
1274 def _clearrebasesetvisibiliy(repo):
1262 """remove rebaseset data from the repo"""
1275 """remove rebaseset data from the repo"""
1263 repo = repo.unfiltered()
1276 repo = repo.unfiltered()
1264 if '_rebaseset' in vars(repo):
1277 if '_rebaseset' in vars(repo):
1265 del repo._rebaseset
1278 del repo._rebaseset
1266
1279
1267 def _rebasedvisible(orig, repo):
1280 def _rebasedvisible(orig, repo):
1268 """ensure rebased revs stay visible (see issue4505)"""
1281 """ensure rebased revs stay visible (see issue4505)"""
1269 blockers = orig(repo)
1282 blockers = orig(repo)
1270 blockers.update(getattr(repo, '_rebaseset', ()))
1283 blockers.update(getattr(repo, '_rebaseset', ()))
1271 return blockers
1284 return blockers
1272
1285
1273 def _filterobsoleterevs(repo, revs):
1286 def _filterobsoleterevs(repo, revs):
1274 """returns a set of the obsolete revisions in revs"""
1287 """returns a set of the obsolete revisions in revs"""
1275 return set(r for r in revs if repo[r].obsolete())
1288 return set(r for r in revs if repo[r].obsolete())
1276
1289
1277 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1290 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1278 """return a mapping obsolete => successor for all obsolete nodes to be
1291 """return a mapping obsolete => successor for all obsolete nodes to be
1279 rebased that have a successors in the destination
1292 rebased that have a successors in the destination
1280
1293
1281 obsolete => None entries in the mapping indicate nodes with no succesor"""
1294 obsolete => None entries in the mapping indicate nodes with no succesor"""
1282 obsoletenotrebased = {}
1295 obsoletenotrebased = {}
1283
1296
1284 # Build a mapping successor => obsolete nodes for the obsolete
1297 # Build a mapping successor => obsolete nodes for the obsolete
1285 # nodes to be rebased
1298 # nodes to be rebased
1286 allsuccessors = {}
1299 allsuccessors = {}
1287 cl = repo.changelog
1300 cl = repo.changelog
1288 for r in rebaseobsrevs:
1301 for r in rebaseobsrevs:
1289 node = cl.node(r)
1302 node = cl.node(r)
1290 for s in obsolete.allsuccessors(repo.obsstore, [node]):
1303 for s in obsolete.allsuccessors(repo.obsstore, [node]):
1291 try:
1304 try:
1292 allsuccessors[cl.rev(s)] = cl.rev(node)
1305 allsuccessors[cl.rev(s)] = cl.rev(node)
1293 except LookupError:
1306 except LookupError:
1294 pass
1307 pass
1295
1308
1296 if allsuccessors:
1309 if allsuccessors:
1297 # Look for successors of obsolete nodes to be rebased among
1310 # Look for successors of obsolete nodes to be rebased among
1298 # the ancestors of dest
1311 # the ancestors of dest
1299 ancs = cl.ancestors([repo[dest].rev()],
1312 ancs = cl.ancestors([repo[dest].rev()],
1300 stoprev=min(allsuccessors),
1313 stoprev=min(allsuccessors),
1301 inclusive=True)
1314 inclusive=True)
1302 for s in allsuccessors:
1315 for s in allsuccessors:
1303 if s in ancs:
1316 if s in ancs:
1304 obsoletenotrebased[allsuccessors[s]] = s
1317 obsoletenotrebased[allsuccessors[s]] = s
1305 elif (s == allsuccessors[s] and
1318 elif (s == allsuccessors[s] and
1306 allsuccessors.values().count(s) == 1):
1319 allsuccessors.values().count(s) == 1):
1307 # plain prune
1320 # plain prune
1308 obsoletenotrebased[s] = None
1321 obsoletenotrebased[s] = None
1309
1322
1310 return obsoletenotrebased
1323 return obsoletenotrebased
1311
1324
1312 def summaryhook(ui, repo):
1325 def summaryhook(ui, repo):
1313 if not os.path.exists(repo.join('rebasestate')):
1326 if not os.path.exists(repo.join('rebasestate')):
1314 return
1327 return
1315 try:
1328 try:
1316 state = restorestatus(repo)[2]
1329 state = restorestatus(repo)[2]
1317 except error.RepoLookupError:
1330 except error.RepoLookupError:
1318 # i18n: column positioning for "hg summary"
1331 # i18n: column positioning for "hg summary"
1319 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1332 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1320 ui.write(msg)
1333 ui.write(msg)
1321 return
1334 return
1322 numrebased = len([i for i in state.itervalues() if i >= 0])
1335 numrebased = len([i for i in state.itervalues() if i >= 0])
1323 # i18n: column positioning for "hg summary"
1336 # i18n: column positioning for "hg summary"
1324 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1337 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1325 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1338 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1326 ui.label(_('%d remaining'), 'rebase.remaining') %
1339 ui.label(_('%d remaining'), 'rebase.remaining') %
1327 (len(state) - numrebased)))
1340 (len(state) - numrebased)))
1328
1341
1329 def uisetup(ui):
1342 def uisetup(ui):
1330 #Replace pull with a decorator to provide --rebase option
1343 #Replace pull with a decorator to provide --rebase option
1331 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1344 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1332 entry[1].append(('', 'rebase', None,
1345 entry[1].append(('', 'rebase', None,
1333 _("rebase working directory to branch head")))
1346 _("rebase working directory to branch head")))
1334 entry[1].append(('t', 'tool', '',
1347 entry[1].append(('t', 'tool', '',
1335 _("specify merge tool for rebase")))
1348 _("specify merge tool for rebase")))
1336 cmdutil.summaryhooks.add('rebase', summaryhook)
1349 cmdutil.summaryhooks.add('rebase', summaryhook)
1337 cmdutil.unfinishedstates.append(
1350 cmdutil.unfinishedstates.append(
1338 ['rebasestate', False, False, _('rebase in progress'),
1351 ['rebasestate', False, False, _('rebase in progress'),
1339 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1352 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1340 cmdutil.afterresolvedstates.append(
1353 cmdutil.afterresolvedstates.append(
1341 ['rebasestate', _('hg rebase --continue')])
1354 ['rebasestate', _('hg rebase --continue')])
1342 # ensure rebased rev are not hidden
1355 # ensure rebased rev are not hidden
1343 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
1356 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
@@ -1,807 +1,865
1 ==========================
1 ==========================
2 Test rebase with obsolete
2 Test rebase with obsolete
3 ==========================
3 ==========================
4
4
5 Enable obsolete
5 Enable obsolete
6
6
7 $ cat >> $HGRCPATH << EOF
7 $ cat >> $HGRCPATH << EOF
8 > [ui]
8 > [ui]
9 > logtemplate= {rev}:{node|short} {desc|firstline}
9 > logtemplate= {rev}:{node|short} {desc|firstline}
10 > [experimental]
10 > [experimental]
11 > evolution=createmarkers,allowunstable
11 > evolution=createmarkers,allowunstable
12 > [phases]
12 > [phases]
13 > publish=False
13 > publish=False
14 > [extensions]
14 > [extensions]
15 > rebase=
15 > rebase=
16 > EOF
16 > EOF
17
17
18 Setup rebase canonical repo
18 Setup rebase canonical repo
19
19
20 $ hg init base
20 $ hg init base
21 $ cd base
21 $ cd base
22 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
22 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
23 adding changesets
23 adding changesets
24 adding manifests
24 adding manifests
25 adding file changes
25 adding file changes
26 added 8 changesets with 7 changes to 7 files (+2 heads)
26 added 8 changesets with 7 changes to 7 files (+2 heads)
27 (run 'hg heads' to see heads, 'hg merge' to merge)
27 (run 'hg heads' to see heads, 'hg merge' to merge)
28 $ hg up tip
28 $ hg up tip
29 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 $ hg log -G
30 $ hg log -G
31 @ 7:02de42196ebe H
31 @ 7:02de42196ebe H
32 |
32 |
33 | o 6:eea13746799a G
33 | o 6:eea13746799a G
34 |/|
34 |/|
35 o | 5:24b6387c8c8c F
35 o | 5:24b6387c8c8c F
36 | |
36 | |
37 | o 4:9520eea781bc E
37 | o 4:9520eea781bc E
38 |/
38 |/
39 | o 3:32af7686d403 D
39 | o 3:32af7686d403 D
40 | |
40 | |
41 | o 2:5fddd98957c8 C
41 | o 2:5fddd98957c8 C
42 | |
42 | |
43 | o 1:42ccdea3bb16 B
43 | o 1:42ccdea3bb16 B
44 |/
44 |/
45 o 0:cd010b8cd998 A
45 o 0:cd010b8cd998 A
46
46
47 $ cd ..
47 $ cd ..
48
48
49 simple rebase
49 simple rebase
50 ---------------------------------
50 ---------------------------------
51
51
52 $ hg clone base simple
52 $ hg clone base simple
53 updating to branch default
53 updating to branch default
54 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 $ cd simple
55 $ cd simple
56 $ hg up 32af7686d403
56 $ hg up 32af7686d403
57 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
57 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
58 $ hg rebase -d eea13746799a
58 $ hg rebase -d eea13746799a
59 rebasing 1:42ccdea3bb16 "B"
59 rebasing 1:42ccdea3bb16 "B"
60 rebasing 2:5fddd98957c8 "C"
60 rebasing 2:5fddd98957c8 "C"
61 rebasing 3:32af7686d403 "D"
61 rebasing 3:32af7686d403 "D"
62 $ hg log -G
62 $ hg log -G
63 @ 10:8eeb3c33ad33 D
63 @ 10:8eeb3c33ad33 D
64 |
64 |
65 o 9:2327fea05063 C
65 o 9:2327fea05063 C
66 |
66 |
67 o 8:e4e5be0395b2 B
67 o 8:e4e5be0395b2 B
68 |
68 |
69 | o 7:02de42196ebe H
69 | o 7:02de42196ebe H
70 | |
70 | |
71 o | 6:eea13746799a G
71 o | 6:eea13746799a G
72 |\|
72 |\|
73 | o 5:24b6387c8c8c F
73 | o 5:24b6387c8c8c F
74 | |
74 | |
75 o | 4:9520eea781bc E
75 o | 4:9520eea781bc E
76 |/
76 |/
77 o 0:cd010b8cd998 A
77 o 0:cd010b8cd998 A
78
78
79 $ hg log --hidden -G
79 $ hg log --hidden -G
80 @ 10:8eeb3c33ad33 D
80 @ 10:8eeb3c33ad33 D
81 |
81 |
82 o 9:2327fea05063 C
82 o 9:2327fea05063 C
83 |
83 |
84 o 8:e4e5be0395b2 B
84 o 8:e4e5be0395b2 B
85 |
85 |
86 | o 7:02de42196ebe H
86 | o 7:02de42196ebe H
87 | |
87 | |
88 o | 6:eea13746799a G
88 o | 6:eea13746799a G
89 |\|
89 |\|
90 | o 5:24b6387c8c8c F
90 | o 5:24b6387c8c8c F
91 | |
91 | |
92 o | 4:9520eea781bc E
92 o | 4:9520eea781bc E
93 |/
93 |/
94 | x 3:32af7686d403 D
94 | x 3:32af7686d403 D
95 | |
95 | |
96 | x 2:5fddd98957c8 C
96 | x 2:5fddd98957c8 C
97 | |
97 | |
98 | x 1:42ccdea3bb16 B
98 | x 1:42ccdea3bb16 B
99 |/
99 |/
100 o 0:cd010b8cd998 A
100 o 0:cd010b8cd998 A
101
101
102 $ hg debugobsolete
102 $ hg debugobsolete
103 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (*) {'user': 'test'} (glob)
103 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (*) {'user': 'test'} (glob)
104 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (*) {'user': 'test'} (glob)
104 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (*) {'user': 'test'} (glob)
105 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (*) {'user': 'test'} (glob)
105 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (*) {'user': 'test'} (glob)
106
106
107
107
108 $ cd ..
108 $ cd ..
109
109
110 empty changeset
110 empty changeset
111 ---------------------------------
111 ---------------------------------
112
112
113 $ hg clone base empty
113 $ hg clone base empty
114 updating to branch default
114 updating to branch default
115 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 $ cd empty
116 $ cd empty
117 $ hg up eea13746799a
117 $ hg up eea13746799a
118 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
118 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
119
119
120 We make a copy of both the first changeset in the rebased and some other in the
120 We make a copy of both the first changeset in the rebased and some other in the
121 set.
121 set.
122
122
123 $ hg graft 42ccdea3bb16 32af7686d403
123 $ hg graft 42ccdea3bb16 32af7686d403
124 grafting 1:42ccdea3bb16 "B"
124 grafting 1:42ccdea3bb16 "B"
125 grafting 3:32af7686d403 "D"
125 grafting 3:32af7686d403 "D"
126 $ hg rebase -s 42ccdea3bb16 -d .
126 $ hg rebase -s 42ccdea3bb16 -d .
127 rebasing 1:42ccdea3bb16 "B"
127 rebasing 1:42ccdea3bb16 "B"
128 note: rebase of 1:42ccdea3bb16 created no changes to commit
128 note: rebase of 1:42ccdea3bb16 created no changes to commit
129 rebasing 2:5fddd98957c8 "C"
129 rebasing 2:5fddd98957c8 "C"
130 rebasing 3:32af7686d403 "D"
130 rebasing 3:32af7686d403 "D"
131 note: rebase of 3:32af7686d403 created no changes to commit
131 note: rebase of 3:32af7686d403 created no changes to commit
132 $ hg log -G
132 $ hg log -G
133 o 10:5ae4c968c6ac C
133 o 10:5ae4c968c6ac C
134 |
134 |
135 @ 9:08483444fef9 D
135 @ 9:08483444fef9 D
136 |
136 |
137 o 8:8877864f1edb B
137 o 8:8877864f1edb B
138 |
138 |
139 | o 7:02de42196ebe H
139 | o 7:02de42196ebe H
140 | |
140 | |
141 o | 6:eea13746799a G
141 o | 6:eea13746799a G
142 |\|
142 |\|
143 | o 5:24b6387c8c8c F
143 | o 5:24b6387c8c8c F
144 | |
144 | |
145 o | 4:9520eea781bc E
145 o | 4:9520eea781bc E
146 |/
146 |/
147 o 0:cd010b8cd998 A
147 o 0:cd010b8cd998 A
148
148
149 $ hg log --hidden -G
149 $ hg log --hidden -G
150 o 10:5ae4c968c6ac C
150 o 10:5ae4c968c6ac C
151 |
151 |
152 @ 9:08483444fef9 D
152 @ 9:08483444fef9 D
153 |
153 |
154 o 8:8877864f1edb B
154 o 8:8877864f1edb B
155 |
155 |
156 | o 7:02de42196ebe H
156 | o 7:02de42196ebe H
157 | |
157 | |
158 o | 6:eea13746799a G
158 o | 6:eea13746799a G
159 |\|
159 |\|
160 | o 5:24b6387c8c8c F
160 | o 5:24b6387c8c8c F
161 | |
161 | |
162 o | 4:9520eea781bc E
162 o | 4:9520eea781bc E
163 |/
163 |/
164 | x 3:32af7686d403 D
164 | x 3:32af7686d403 D
165 | |
165 | |
166 | x 2:5fddd98957c8 C
166 | x 2:5fddd98957c8 C
167 | |
167 | |
168 | x 1:42ccdea3bb16 B
168 | x 1:42ccdea3bb16 B
169 |/
169 |/
170 o 0:cd010b8cd998 A
170 o 0:cd010b8cd998 A
171
171
172 $ hg debugobsolete
172 $ hg debugobsolete
173 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
173 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
174 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
174 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
175 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
175 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
176
176
177
177
178 More complex case were part of the rebase set were already rebased
178 More complex case were part of the rebase set were already rebased
179
179
180 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
180 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
181 rebasing 9:08483444fef9 "D"
181 rebasing 9:08483444fef9 "D"
182 $ hg debugobsolete
182 $ hg debugobsolete
183 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
183 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
184 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
184 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
185 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
185 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
186 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
186 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
187 $ hg log -G
187 $ hg log -G
188 @ 11:4596109a6a43 D
188 @ 11:4596109a6a43 D
189 |
189 |
190 | o 10:5ae4c968c6ac C
190 | o 10:5ae4c968c6ac C
191 | |
191 | |
192 | x 9:08483444fef9 D
192 | x 9:08483444fef9 D
193 | |
193 | |
194 | o 8:8877864f1edb B
194 | o 8:8877864f1edb B
195 | |
195 | |
196 o | 7:02de42196ebe H
196 o | 7:02de42196ebe H
197 | |
197 | |
198 | o 6:eea13746799a G
198 | o 6:eea13746799a G
199 |/|
199 |/|
200 o | 5:24b6387c8c8c F
200 o | 5:24b6387c8c8c F
201 | |
201 | |
202 | o 4:9520eea781bc E
202 | o 4:9520eea781bc E
203 |/
203 |/
204 o 0:cd010b8cd998 A
204 o 0:cd010b8cd998 A
205
205
206 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
206 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
207 rebasing 8:8877864f1edb "B"
207 rebasing 8:8877864f1edb "B"
208 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D"
208 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D"
209 rebasing 10:5ae4c968c6ac "C"
209 rebasing 10:5ae4c968c6ac "C"
210 $ hg debugobsolete
210 $ hg debugobsolete
211 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
211 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
212 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
212 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
213 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
213 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
214 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
214 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
215 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob)
215 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob)
216 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob)
216 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob)
217 $ hg log --rev 'divergent()'
217 $ hg log --rev 'divergent()'
218 $ hg log -G
218 $ hg log -G
219 o 13:98f6af4ee953 C
219 o 13:98f6af4ee953 C
220 |
220 |
221 o 12:462a34d07e59 B
221 o 12:462a34d07e59 B
222 |
222 |
223 @ 11:4596109a6a43 D
223 @ 11:4596109a6a43 D
224 |
224 |
225 o 7:02de42196ebe H
225 o 7:02de42196ebe H
226 |
226 |
227 | o 6:eea13746799a G
227 | o 6:eea13746799a G
228 |/|
228 |/|
229 o | 5:24b6387c8c8c F
229 o | 5:24b6387c8c8c F
230 | |
230 | |
231 | o 4:9520eea781bc E
231 | o 4:9520eea781bc E
232 |/
232 |/
233 o 0:cd010b8cd998 A
233 o 0:cd010b8cd998 A
234
234
235 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
235 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
236 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
236 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
237 phase: draft
237 phase: draft
238 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
238 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
239 parent: -1:0000000000000000000000000000000000000000
239 parent: -1:0000000000000000000000000000000000000000
240 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
240 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
241 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
241 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
242 date: Sat Apr 30 15:24:48 2011 +0200
242 date: Sat Apr 30 15:24:48 2011 +0200
243 files+: D
243 files+: D
244 extra: branch=default
244 extra: branch=default
245 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
245 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
246 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
246 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
247 description:
247 description:
248 D
248 D
249
249
250
250
251 $ hg up -qr 'desc(G)'
251 $ hg up -qr 'desc(G)'
252 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
252 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
253 grafting 11:4596109a6a43 "D"
253 grafting 11:4596109a6a43 "D"
254 $ hg up -qr 'desc(E)'
254 $ hg up -qr 'desc(E)'
255 $ hg rebase -s tip -d .
255 $ hg rebase -s tip -d .
256 rebasing 14:9e36056a46e3 "D" (tip)
256 rebasing 14:9e36056a46e3 "D" (tip)
257 $ hg log --style default --debug -r tip
257 $ hg log --style default --debug -r tip
258 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
258 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
259 tag: tip
259 tag: tip
260 phase: draft
260 phase: draft
261 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
261 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
262 parent: -1:0000000000000000000000000000000000000000
262 parent: -1:0000000000000000000000000000000000000000
263 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
263 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
264 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
264 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
265 date: Sat Apr 30 15:24:48 2011 +0200
265 date: Sat Apr 30 15:24:48 2011 +0200
266 files+: D
266 files+: D
267 extra: branch=default
267 extra: branch=default
268 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
268 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
269 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
269 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
270 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
270 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
271 description:
271 description:
272 D
272 D
273
273
274
274
275 $ cd ..
275 $ cd ..
276
276
277 collapse rebase
277 collapse rebase
278 ---------------------------------
278 ---------------------------------
279
279
280 $ hg clone base collapse
280 $ hg clone base collapse
281 updating to branch default
281 updating to branch default
282 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
282 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
283 $ cd collapse
283 $ cd collapse
284 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
284 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
285 rebasing 1:42ccdea3bb16 "B"
285 rebasing 1:42ccdea3bb16 "B"
286 rebasing 2:5fddd98957c8 "C"
286 rebasing 2:5fddd98957c8 "C"
287 rebasing 3:32af7686d403 "D"
287 rebasing 3:32af7686d403 "D"
288 $ hg log -G
288 $ hg log -G
289 o 8:4dc2197e807b Collapsed revision
289 o 8:4dc2197e807b Collapsed revision
290 |
290 |
291 | @ 7:02de42196ebe H
291 | @ 7:02de42196ebe H
292 | |
292 | |
293 o | 6:eea13746799a G
293 o | 6:eea13746799a G
294 |\|
294 |\|
295 | o 5:24b6387c8c8c F
295 | o 5:24b6387c8c8c F
296 | |
296 | |
297 o | 4:9520eea781bc E
297 o | 4:9520eea781bc E
298 |/
298 |/
299 o 0:cd010b8cd998 A
299 o 0:cd010b8cd998 A
300
300
301 $ hg log --hidden -G
301 $ hg log --hidden -G
302 o 8:4dc2197e807b Collapsed revision
302 o 8:4dc2197e807b Collapsed revision
303 |
303 |
304 | @ 7:02de42196ebe H
304 | @ 7:02de42196ebe H
305 | |
305 | |
306 o | 6:eea13746799a G
306 o | 6:eea13746799a G
307 |\|
307 |\|
308 | o 5:24b6387c8c8c F
308 | o 5:24b6387c8c8c F
309 | |
309 | |
310 o | 4:9520eea781bc E
310 o | 4:9520eea781bc E
311 |/
311 |/
312 | x 3:32af7686d403 D
312 | x 3:32af7686d403 D
313 | |
313 | |
314 | x 2:5fddd98957c8 C
314 | x 2:5fddd98957c8 C
315 | |
315 | |
316 | x 1:42ccdea3bb16 B
316 | x 1:42ccdea3bb16 B
317 |/
317 |/
318 o 0:cd010b8cd998 A
318 o 0:cd010b8cd998 A
319
319
320 $ hg id --debug -r tip
320 $ hg id --debug -r tip
321 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
321 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
322 $ hg debugobsolete
322 $ hg debugobsolete
323 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
323 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
324 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
324 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
325 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
325 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
326
326
327 $ cd ..
327 $ cd ..
328
328
329 Rebase set has hidden descendants
329 Rebase set has hidden descendants
330 ---------------------------------
330 ---------------------------------
331
331
332 We rebase a changeset which has a hidden changeset. The hidden changeset must
332 We rebase a changeset which has a hidden changeset. The hidden changeset must
333 not be rebased.
333 not be rebased.
334
334
335 $ hg clone base hidden
335 $ hg clone base hidden
336 updating to branch default
336 updating to branch default
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 $ cd hidden
338 $ cd hidden
339 $ hg rebase -s 5fddd98957c8 -d eea13746799a
339 $ hg rebase -s 5fddd98957c8 -d eea13746799a
340 rebasing 2:5fddd98957c8 "C"
340 rebasing 2:5fddd98957c8 "C"
341 rebasing 3:32af7686d403 "D"
341 rebasing 3:32af7686d403 "D"
342 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
342 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
343 rebasing 1:42ccdea3bb16 "B"
343 rebasing 1:42ccdea3bb16 "B"
344 $ hg log -G
344 $ hg log -G
345 o 10:7c6027df6a99 B
345 o 10:7c6027df6a99 B
346 |
346 |
347 | o 9:cf44d2f5a9f4 D
347 | o 9:cf44d2f5a9f4 D
348 | |
348 | |
349 | o 8:e273c5e7d2d2 C
349 | o 8:e273c5e7d2d2 C
350 | |
350 | |
351 @ | 7:02de42196ebe H
351 @ | 7:02de42196ebe H
352 | |
352 | |
353 | o 6:eea13746799a G
353 | o 6:eea13746799a G
354 |/|
354 |/|
355 o | 5:24b6387c8c8c F
355 o | 5:24b6387c8c8c F
356 | |
356 | |
357 | o 4:9520eea781bc E
357 | o 4:9520eea781bc E
358 |/
358 |/
359 o 0:cd010b8cd998 A
359 o 0:cd010b8cd998 A
360
360
361 $ hg log --hidden -G
361 $ hg log --hidden -G
362 o 10:7c6027df6a99 B
362 o 10:7c6027df6a99 B
363 |
363 |
364 | o 9:cf44d2f5a9f4 D
364 | o 9:cf44d2f5a9f4 D
365 | |
365 | |
366 | o 8:e273c5e7d2d2 C
366 | o 8:e273c5e7d2d2 C
367 | |
367 | |
368 @ | 7:02de42196ebe H
368 @ | 7:02de42196ebe H
369 | |
369 | |
370 | o 6:eea13746799a G
370 | o 6:eea13746799a G
371 |/|
371 |/|
372 o | 5:24b6387c8c8c F
372 o | 5:24b6387c8c8c F
373 | |
373 | |
374 | o 4:9520eea781bc E
374 | o 4:9520eea781bc E
375 |/
375 |/
376 | x 3:32af7686d403 D
376 | x 3:32af7686d403 D
377 | |
377 | |
378 | x 2:5fddd98957c8 C
378 | x 2:5fddd98957c8 C
379 | |
379 | |
380 | x 1:42ccdea3bb16 B
380 | x 1:42ccdea3bb16 B
381 |/
381 |/
382 o 0:cd010b8cd998 A
382 o 0:cd010b8cd998 A
383
383
384 $ hg debugobsolete
384 $ hg debugobsolete
385 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (*) {'user': 'test'} (glob)
385 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (*) {'user': 'test'} (glob)
386 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (*) {'user': 'test'} (glob)
386 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (*) {'user': 'test'} (glob)
387 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (*) {'user': 'test'} (glob)
387 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (*) {'user': 'test'} (glob)
388
388
389 Test that rewriting leaving instability behind is allowed
389 Test that rewriting leaving instability behind is allowed
390 ---------------------------------------------------------------------
390 ---------------------------------------------------------------------
391
391
392 $ hg log -r 'children(8)'
392 $ hg log -r 'children(8)'
393 9:cf44d2f5a9f4 D (no-eol)
393 9:cf44d2f5a9f4 D (no-eol)
394 $ hg rebase -r 8
394 $ hg rebase -r 8
395 rebasing 8:e273c5e7d2d2 "C"
395 rebasing 8:e273c5e7d2d2 "C"
396 $ hg log -G
396 $ hg log -G
397 o 11:0d8f238b634c C
397 o 11:0d8f238b634c C
398 |
398 |
399 o 10:7c6027df6a99 B
399 o 10:7c6027df6a99 B
400 |
400 |
401 | o 9:cf44d2f5a9f4 D
401 | o 9:cf44d2f5a9f4 D
402 | |
402 | |
403 | x 8:e273c5e7d2d2 C
403 | x 8:e273c5e7d2d2 C
404 | |
404 | |
405 @ | 7:02de42196ebe H
405 @ | 7:02de42196ebe H
406 | |
406 | |
407 | o 6:eea13746799a G
407 | o 6:eea13746799a G
408 |/|
408 |/|
409 o | 5:24b6387c8c8c F
409 o | 5:24b6387c8c8c F
410 | |
410 | |
411 | o 4:9520eea781bc E
411 | o 4:9520eea781bc E
412 |/
412 |/
413 o 0:cd010b8cd998 A
413 o 0:cd010b8cd998 A
414
414
415
415
416
416
417 Test multiple root handling
417 Test multiple root handling
418 ------------------------------------
418 ------------------------------------
419
419
420 $ hg rebase --dest 4 --rev '7+11+9'
420 $ hg rebase --dest 4 --rev '7+11+9'
421 rebasing 7:02de42196ebe "H"
421 rebasing 7:02de42196ebe "H"
422 rebasing 9:cf44d2f5a9f4 "D"
422 rebasing 9:cf44d2f5a9f4 "D"
423 not rebasing ignored 10:7c6027df6a99 "B"
423 not rebasing ignored 10:7c6027df6a99 "B"
424 rebasing 11:0d8f238b634c "C" (tip)
424 rebasing 11:0d8f238b634c "C" (tip)
425 $ hg log -G
425 $ hg log -G
426 o 14:1e8370e38cca C
426 o 14:1e8370e38cca C
427 |
427 |
428 | o 13:102b4c1d889b D
428 | o 13:102b4c1d889b D
429 | |
429 | |
430 @ | 12:bfe264faf697 H
430 @ | 12:bfe264faf697 H
431 |/
431 |/
432 | o 10:7c6027df6a99 B
432 | o 10:7c6027df6a99 B
433 | |
433 | |
434 | x 7:02de42196ebe H
434 | x 7:02de42196ebe H
435 | |
435 | |
436 +---o 6:eea13746799a G
436 +---o 6:eea13746799a G
437 | |/
437 | |/
438 | o 5:24b6387c8c8c F
438 | o 5:24b6387c8c8c F
439 | |
439 | |
440 o | 4:9520eea781bc E
440 o | 4:9520eea781bc E
441 |/
441 |/
442 o 0:cd010b8cd998 A
442 o 0:cd010b8cd998 A
443
443
444 $ cd ..
444 $ cd ..
445
445
446 test on rebase dropping a merge
446 test on rebase dropping a merge
447
447
448 (setup)
448 (setup)
449
449
450 $ hg init dropmerge
450 $ hg init dropmerge
451 $ cd dropmerge
451 $ cd dropmerge
452 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
452 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
453 adding changesets
453 adding changesets
454 adding manifests
454 adding manifests
455 adding file changes
455 adding file changes
456 added 8 changesets with 7 changes to 7 files (+2 heads)
456 added 8 changesets with 7 changes to 7 files (+2 heads)
457 (run 'hg heads' to see heads, 'hg merge' to merge)
457 (run 'hg heads' to see heads, 'hg merge' to merge)
458 $ hg up 3
458 $ hg up 3
459 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
460 $ hg merge 7
460 $ hg merge 7
461 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
461 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 (branch merge, don't forget to commit)
462 (branch merge, don't forget to commit)
463 $ hg ci -m 'M'
463 $ hg ci -m 'M'
464 $ echo I > I
464 $ echo I > I
465 $ hg add I
465 $ hg add I
466 $ hg ci -m I
466 $ hg ci -m I
467 $ hg log -G
467 $ hg log -G
468 @ 9:4bde274eefcf I
468 @ 9:4bde274eefcf I
469 |
469 |
470 o 8:53a6a128b2b7 M
470 o 8:53a6a128b2b7 M
471 |\
471 |\
472 | o 7:02de42196ebe H
472 | o 7:02de42196ebe H
473 | |
473 | |
474 | | o 6:eea13746799a G
474 | | o 6:eea13746799a G
475 | |/|
475 | |/|
476 | o | 5:24b6387c8c8c F
476 | o | 5:24b6387c8c8c F
477 | | |
477 | | |
478 | | o 4:9520eea781bc E
478 | | o 4:9520eea781bc E
479 | |/
479 | |/
480 o | 3:32af7686d403 D
480 o | 3:32af7686d403 D
481 | |
481 | |
482 o | 2:5fddd98957c8 C
482 o | 2:5fddd98957c8 C
483 | |
483 | |
484 o | 1:42ccdea3bb16 B
484 o | 1:42ccdea3bb16 B
485 |/
485 |/
486 o 0:cd010b8cd998 A
486 o 0:cd010b8cd998 A
487
487
488 (actual test)
488 (actual test)
489
489
490 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
490 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
491 rebasing 3:32af7686d403 "D"
491 rebasing 3:32af7686d403 "D"
492 rebasing 7:02de42196ebe "H"
492 rebasing 7:02de42196ebe "H"
493 not rebasing ignored 8:53a6a128b2b7 "M"
493 not rebasing ignored 8:53a6a128b2b7 "M"
494 rebasing 9:4bde274eefcf "I" (tip)
494 rebasing 9:4bde274eefcf "I" (tip)
495 $ hg log -G
495 $ hg log -G
496 @ 12:acd174b7ab39 I
496 @ 12:acd174b7ab39 I
497 |
497 |
498 o 11:6c11a6218c97 H
498 o 11:6c11a6218c97 H
499 |
499 |
500 | o 10:b5313c85b22e D
500 | o 10:b5313c85b22e D
501 |/
501 |/
502 | o 8:53a6a128b2b7 M
502 | o 8:53a6a128b2b7 M
503 | |\
503 | |\
504 | | x 7:02de42196ebe H
504 | | x 7:02de42196ebe H
505 | | |
505 | | |
506 o---+ 6:eea13746799a G
506 o---+ 6:eea13746799a G
507 | | |
507 | | |
508 | | o 5:24b6387c8c8c F
508 | | o 5:24b6387c8c8c F
509 | | |
509 | | |
510 o---+ 4:9520eea781bc E
510 o---+ 4:9520eea781bc E
511 / /
511 / /
512 x | 3:32af7686d403 D
512 x | 3:32af7686d403 D
513 | |
513 | |
514 o | 2:5fddd98957c8 C
514 o | 2:5fddd98957c8 C
515 | |
515 | |
516 o | 1:42ccdea3bb16 B
516 o | 1:42ccdea3bb16 B
517 |/
517 |/
518 o 0:cd010b8cd998 A
518 o 0:cd010b8cd998 A
519
519
520
520
521 Test hidden changesets in the rebase set (issue4504)
521 Test hidden changesets in the rebase set (issue4504)
522
522
523 $ hg up --hidden 9
523 $ hg up --hidden 9
524 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
524 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
525 $ echo J > J
525 $ echo J > J
526 $ hg add J
526 $ hg add J
527 $ hg commit -m J
527 $ hg commit -m J
528 $ hg debugobsolete `hg log --rev . -T '{node}'`
528 $ hg debugobsolete `hg log --rev . -T '{node}'`
529
529
530 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
530 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
531 rebasing 9:4bde274eefcf "I"
531 rebasing 9:4bde274eefcf "I"
532 rebasing 13:06edfc82198f "J" (tip)
532 rebasing 13:06edfc82198f "J" (tip)
533 $ hg log -G
533 $ hg log -G
534 @ 15:5ae8a643467b J
534 @ 15:5ae8a643467b J
535 |
535 |
536 o 14:9ad579b4a5de I
536 o 14:9ad579b4a5de I
537 |
537 |
538 | o 12:acd174b7ab39 I
538 | o 12:acd174b7ab39 I
539 | |
539 | |
540 | o 11:6c11a6218c97 H
540 | o 11:6c11a6218c97 H
541 | |
541 | |
542 o | 10:b5313c85b22e D
542 o | 10:b5313c85b22e D
543 |/
543 |/
544 | o 8:53a6a128b2b7 M
544 | o 8:53a6a128b2b7 M
545 | |\
545 | |\
546 | | x 7:02de42196ebe H
546 | | x 7:02de42196ebe H
547 | | |
547 | | |
548 o---+ 6:eea13746799a G
548 o---+ 6:eea13746799a G
549 | | |
549 | | |
550 | | o 5:24b6387c8c8c F
550 | | o 5:24b6387c8c8c F
551 | | |
551 | | |
552 o---+ 4:9520eea781bc E
552 o---+ 4:9520eea781bc E
553 / /
553 / /
554 x | 3:32af7686d403 D
554 x | 3:32af7686d403 D
555 | |
555 | |
556 o | 2:5fddd98957c8 C
556 o | 2:5fddd98957c8 C
557 | |
557 | |
558 o | 1:42ccdea3bb16 B
558 o | 1:42ccdea3bb16 B
559 |/
559 |/
560 o 0:cd010b8cd998 A
560 o 0:cd010b8cd998 A
561
561
562 $ hg up 14 -C
562 $ hg up 14 -C
563 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
563 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
564 $ echo "K" > K
564 $ echo "K" > K
565 $ hg add K
565 $ hg add K
566 $ hg commit --amend -m "K"
566 $ hg commit --amend -m "K"
567 $ echo "L" > L
567 $ echo "L" > L
568 $ hg add L
568 $ hg add L
569 $ hg commit -m "L"
569 $ hg commit -m "L"
570 $ hg up '.^'
570 $ hg up '.^'
571 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
571 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
572 $ echo "M" > M
572 $ echo "M" > M
573 $ hg add M
573 $ hg add M
574 $ hg commit --amend -m "M"
574 $ hg commit --amend -m "M"
575 $ hg log -G
575 $ hg log -G
576 @ 20:bfaedf8eb73b M
576 @ 20:bfaedf8eb73b M
577 |
577 |
578 | o 18:97219452e4bd L
578 | o 18:97219452e4bd L
579 | |
579 | |
580 | x 17:fc37a630c901 K
580 | x 17:fc37a630c901 K
581 |/
581 |/
582 | o 15:5ae8a643467b J
582 | o 15:5ae8a643467b J
583 | |
583 | |
584 | x 14:9ad579b4a5de I
584 | x 14:9ad579b4a5de I
585 |/
585 |/
586 | o 12:acd174b7ab39 I
586 | o 12:acd174b7ab39 I
587 | |
587 | |
588 | o 11:6c11a6218c97 H
588 | o 11:6c11a6218c97 H
589 | |
589 | |
590 o | 10:b5313c85b22e D
590 o | 10:b5313c85b22e D
591 |/
591 |/
592 | o 8:53a6a128b2b7 M
592 | o 8:53a6a128b2b7 M
593 | |\
593 | |\
594 | | x 7:02de42196ebe H
594 | | x 7:02de42196ebe H
595 | | |
595 | | |
596 o---+ 6:eea13746799a G
596 o---+ 6:eea13746799a G
597 | | |
597 | | |
598 | | o 5:24b6387c8c8c F
598 | | o 5:24b6387c8c8c F
599 | | |
599 | | |
600 o---+ 4:9520eea781bc E
600 o---+ 4:9520eea781bc E
601 / /
601 / /
602 x | 3:32af7686d403 D
602 x | 3:32af7686d403 D
603 | |
603 | |
604 o | 2:5fddd98957c8 C
604 o | 2:5fddd98957c8 C
605 | |
605 | |
606 o | 1:42ccdea3bb16 B
606 o | 1:42ccdea3bb16 B
607 |/
607 |/
608 o 0:cd010b8cd998 A
608 o 0:cd010b8cd998 A
609
609
610 $ hg rebase -s 14 -d 18 --config experimental.rebaseskipobsolete=True
610 $ hg rebase -s 14 -d 18 --config experimental.rebaseskipobsolete=True
611 note: not rebasing 14:9ad579b4a5de "I", already in destination as 17:fc37a630c901 "K"
611 note: not rebasing 14:9ad579b4a5de "I", already in destination as 17:fc37a630c901 "K"
612 rebasing 15:5ae8a643467b "J"
612 rebasing 15:5ae8a643467b "J"
613
613
614 $ cd ..
614 $ cd ..
615
615
616 Skip obsolete changeset even with multiple hops
616 Skip obsolete changeset even with multiple hops
617 -----------------------------------------------
617 -----------------------------------------------
618
618
619 setup
619 setup
620
620
621 $ hg init obsskip
621 $ hg init obsskip
622 $ cd obsskip
622 $ cd obsskip
623 $ cat << EOF >> .hg/hgrc
623 $ cat << EOF >> .hg/hgrc
624 > [experimental]
624 > [experimental]
625 > rebaseskipobsolete = True
625 > rebaseskipobsolete = True
626 > [extensions]
626 > [extensions]
627 > strip =
627 > strip =
628 > EOF
628 > EOF
629 $ echo A > A
629 $ echo A > A
630 $ hg add A
630 $ hg add A
631 $ hg commit -m A
631 $ hg commit -m A
632 $ echo B > B
632 $ echo B > B
633 $ hg add B
633 $ hg add B
634 $ hg commit -m B0
634 $ hg commit -m B0
635 $ hg commit --amend -m B1
635 $ hg commit --amend -m B1
636 $ hg commit --amend -m B2
636 $ hg commit --amend -m B2
637 $ hg up --hidden 'desc(B0)'
637 $ hg up --hidden 'desc(B0)'
638 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
638 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
639 $ echo C > C
639 $ echo C > C
640 $ hg add C
640 $ hg add C
641 $ hg commit -m C
641 $ hg commit -m C
642
642
643 Rebase finds its way in a chain of marker
643 Rebase finds its way in a chain of marker
644
644
645 $ hg rebase -d 'desc(B2)'
645 $ hg rebase -d 'desc(B2)'
646 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
646 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
647 rebasing 4:212cb178bcbb "C" (tip)
647 rebasing 4:212cb178bcbb "C" (tip)
648
648
649 Even when the chain include missing node
649 Even when the chain include missing node
650
650
651 $ hg up --hidden 'desc(B0)'
651 $ hg up --hidden 'desc(B0)'
652 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
652 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
653 $ echo D > D
653 $ echo D > D
654 $ hg add D
654 $ hg add D
655 $ hg commit -m D
655 $ hg commit -m D
656 $ hg --hidden strip -r 'desc(B1)'
656 $ hg --hidden strip -r 'desc(B1)'
657 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg (glob)
657 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg (glob)
658
658
659 $ hg rebase -d 'desc(B2)'
659 $ hg rebase -d 'desc(B2)'
660 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
660 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
661 rebasing 5:1a79b7535141 "D" (tip)
661 rebasing 5:1a79b7535141 "D" (tip)
662 $ hg up 4
662 $ hg up 4
663 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
663 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
664 $ echo "O" > O
664 $ echo "O" > O
665 $ hg add O
665 $ hg add O
666 $ hg commit -m O
666 $ hg commit -m O
667 $ echo "P" > P
667 $ echo "P" > P
668 $ hg add P
668 $ hg add P
669 $ hg commit -m P
669 $ hg commit -m P
670 $ hg log -G
670 $ hg log -G
671 @ 8:8d47583e023f P
671 @ 8:8d47583e023f P
672 |
672 |
673 o 7:360bbaa7d3ce O
673 o 7:360bbaa7d3ce O
674 |
674 |
675 | o 6:9c48361117de D
675 | o 6:9c48361117de D
676 | |
676 | |
677 o | 4:ff2c4d47b71d C
677 o | 4:ff2c4d47b71d C
678 |/
678 |/
679 o 2:261e70097290 B2
679 o 2:261e70097290 B2
680 |
680 |
681 o 0:4a2df7238c3b A
681 o 0:4a2df7238c3b A
682
682
683 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.evolution=all
683 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.evolution=all
684 $ hg rebase -d 6 -r "4::"
684 $ hg rebase -d 6 -r "4::"
685 rebasing 4:ff2c4d47b71d "C"
685 rebasing 4:ff2c4d47b71d "C"
686 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
686 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
687 rebasing 8:8d47583e023f "P" (tip)
687 rebasing 8:8d47583e023f "P" (tip)
688
688
689 If all the changeset to be rebased are obsolete and present in the destination, we
689 If all the changeset to be rebased are obsolete and present in the destination, we
690 should display a friendly error message
690 should display a friendly error message
691
691
692 $ hg log -G
692 $ hg log -G
693 @ 10:121d9e3bc4c6 P
693 @ 10:121d9e3bc4c6 P
694 |
694 |
695 o 9:4be60e099a77 C
695 o 9:4be60e099a77 C
696 |
696 |
697 o 6:9c48361117de D
697 o 6:9c48361117de D
698 |
698 |
699 o 2:261e70097290 B2
699 o 2:261e70097290 B2
700 |
700 |
701 o 0:4a2df7238c3b A
701 o 0:4a2df7238c3b A
702
702
703
703
704 $ hg up 9
704 $ hg up 9
705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
706 $ echo "non-relevant change" > nonrelevant
706 $ echo "non-relevant change" > nonrelevant
707 $ hg add nonrelevant
707 $ hg add nonrelevant
708 $ hg commit -m nonrelevant
708 $ hg commit -m nonrelevant
709 created new head
709 created new head
710 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.evolution=all
710 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.evolution=all
711 $ hg rebase -r . -d 10
711 $ hg rebase -r . -d 10
712 abort: all requested changesets have equivalents or were marked as obsolete
712 abort: all requested changesets have equivalents or were marked as obsolete
713 (to force the rebase, set the config experimental.rebaseskipobsolete to False)
713 (to force the rebase, set the config experimental.rebaseskipobsolete to False)
714 [255]
714 [255]
715
715
716 If a rebase is going to create divergence, it should abort
716 If a rebase is going to create divergence, it should abort
717
717
718 $ hg log -G
718 $ hg log -G
719 @ 11:f44da1f4954c nonrelevant
719 @ 11:f44da1f4954c nonrelevant
720 |
720 |
721 | o 10:121d9e3bc4c6 P
721 | o 10:121d9e3bc4c6 P
722 |/
722 |/
723 o 9:4be60e099a77 C
723 o 9:4be60e099a77 C
724 |
724 |
725 o 6:9c48361117de D
725 o 6:9c48361117de D
726 |
726 |
727 o 2:261e70097290 B2
727 o 2:261e70097290 B2
728 |
728 |
729 o 0:4a2df7238c3b A
729 o 0:4a2df7238c3b A
730
730
731
731
732 $ hg up 9
732 $ hg up 9
733 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
733 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
734 $ echo "john" > doe
734 $ echo "john" > doe
735 $ hg add doe
735 $ hg add doe
736 $ hg commit -m "john doe"
736 $ hg commit -m "john doe"
737 created new head
737 created new head
738 $ hg up 10
738 $ hg up 10
739 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
739 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
740 $ echo "foo" > bar
740 $ echo "foo" > bar
741 $ hg add bar
741 $ hg add bar
742 $ hg commit --amend -m "10'"
742 $ hg commit --amend -m "10'"
743 $ hg up 10 --hidden
743 $ hg up 10 --hidden
744 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
744 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
745 $ echo "bar" > foo
745 $ echo "bar" > foo
746 $ hg add foo
746 $ hg add foo
747 $ hg commit -m "bar foo"
747 $ hg commit -m "bar foo"
748 $ hg log -G
748 $ hg log -G
749 @ 15:73568ab6879d bar foo
749 @ 15:73568ab6879d bar foo
750 |
750 |
751 | o 14:77d874d096a2 10'
751 | o 14:77d874d096a2 10'
752 | |
752 | |
753 | | o 12:3eb461388009 john doe
753 | | o 12:3eb461388009 john doe
754 | |/
754 | |/
755 x | 10:121d9e3bc4c6 P
755 x | 10:121d9e3bc4c6 P
756 |/
756 |/
757 o 9:4be60e099a77 C
757 o 9:4be60e099a77 C
758 |
758 |
759 o 6:9c48361117de D
759 o 6:9c48361117de D
760 |
760 |
761 o 2:261e70097290 B2
761 o 2:261e70097290 B2
762 |
762 |
763 o 0:4a2df7238c3b A
763 o 0:4a2df7238c3b A
764
764
765 $ hg summary
765 $ hg summary
766 parent: 15:73568ab6879d tip
766 parent: 15:73568ab6879d tip
767 bar foo
767 bar foo
768 branch: default
768 branch: default
769 commit: (clean)
769 commit: (clean)
770 update: 2 new changesets, 3 branch heads (merge)
770 update: 2 new changesets, 3 branch heads (merge)
771 phases: 8 draft
771 phases: 8 draft
772 unstable: 1 changesets
772 unstable: 1 changesets
773 $ hg rebase -s 10 -d 12
773 $ hg rebase -s 10 -d 12
774 abort: this rebase will cause divergences from: 121d9e3bc4c6
774 abort: this rebase will cause divergences from: 121d9e3bc4c6
775 (to force the rebase please set experimental.allowdivergence=True)
775 (to force the rebase please set experimental.allowdivergence=True)
776 [255]
776 [255]
777 $ hg log -G
777 $ hg log -G
778 @ 15:73568ab6879d bar foo
778 @ 15:73568ab6879d bar foo
779 |
779 |
780 | o 14:77d874d096a2 10'
780 | o 14:77d874d096a2 10'
781 | |
781 | |
782 | | o 12:3eb461388009 john doe
782 | | o 12:3eb461388009 john doe
783 | |/
783 | |/
784 x | 10:121d9e3bc4c6 P
784 x | 10:121d9e3bc4c6 P
785 |/
785 |/
786 o 9:4be60e099a77 C
786 o 9:4be60e099a77 C
787 |
787 |
788 o 6:9c48361117de D
788 o 6:9c48361117de D
789 |
789 |
790 o 2:261e70097290 B2
790 o 2:261e70097290 B2
791 |
791 |
792 o 0:4a2df7238c3b A
792 o 0:4a2df7238c3b A
793
793
794 With experimental.allowdivergence=True, rebase can create divergence
794 With experimental.allowdivergence=True, rebase can create divergence
795
795
796 $ hg rebase -s 10 -d 12 --config experimental.allowdivergence=True
796 $ hg rebase -s 10 -d 12 --config experimental.allowdivergence=True
797 rebasing 10:121d9e3bc4c6 "P"
797 rebasing 10:121d9e3bc4c6 "P"
798 rebasing 15:73568ab6879d "bar foo" (tip)
798 rebasing 15:73568ab6879d "bar foo" (tip)
799 $ hg summary
799 $ hg summary
800 parent: 17:61bd55f69bc4 tip
800 parent: 17:61bd55f69bc4 tip
801 bar foo
801 bar foo
802 branch: default
802 branch: default
803 commit: (clean)
803 commit: (clean)
804 update: 1 new changesets, 2 branch heads (merge)
804 update: 1 new changesets, 2 branch heads (merge)
805 phases: 8 draft
805 phases: 8 draft
806 divergent: 2 changesets
806 divergent: 2 changesets
807
807
808 rebase --continue + skipped rev because their successors are in destination
809 we make a change in trunk and work on conflicting changes to make rebase abort.
810
811 $ hg log -G -r 17::
812 @ 17:61bd55f69bc4 bar foo
813 |
814 ~
815
816 Create the two changes in trunk
817 $ printf "a" > willconflict
818 $ hg add willconflict
819 $ hg commit -m "willconflict first version"
820
821 $ printf "dummy" > C
822 $ hg commit -m "dummy change successor"
823
824 Create the changes that we will rebase
825 $ hg update -C 17 -q
826 $ printf "b" > willconflict
827 $ hg add willconflict
828 $ hg commit -m "willconflict second version"
829 created new head
830 $ printf "dummy" > K
831 $ hg add K
832 $ hg commit -m "dummy change"
833 $ printf "dummy" > L
834 $ hg add L
835 $ hg commit -m "dummy change"
836 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 19 -T '{node}'` --config experimental.evolution=all
837
838 $ hg log -G -r 17::
839 @ 22:7bdc8a87673d dummy change
840 |
841 x 21:8b31da3c4919 dummy change
842 |
843 o 20:b82fb57ea638 willconflict second version
844 |
845 | o 19:601db7a18f51 dummy change successor
846 | |
847 | o 18:357ddf1602d5 willconflict first version
848 |/
849 o 17:61bd55f69bc4 bar foo
850 |
851 ~
852 $ hg rebase -r ".^^ + .^ + ." -d 19
853 rebasing 20:b82fb57ea638 "willconflict second version"
854 merging willconflict
855 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
856 unresolved conflicts (see hg resolve, then hg rebase --continue)
857 [1]
858
859 $ hg resolve --mark willconflict
860 (no more unresolved files)
861 continue: hg rebase --continue
862 $ hg rebase --continue
863 rebasing 20:b82fb57ea638 "willconflict second version"
864 note: not rebasing 21:8b31da3c4919 "dummy change", already in destination as 19:601db7a18f51 "dummy change successor"
865 rebasing 22:7bdc8a87673d "dummy change" (tip)
General Comments 0
You need to be logged in to leave comments. Login now