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