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