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