##// END OF EJS Templates
rebase: do not crash rebasing merge with a parent having hidden successor...
Jun Wu -
r34094:8a8e7a94 default
parent child Browse files
Show More
@@ -1,1671 +1,1672
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 nullid,
24 nullid,
25 nullrev,
25 nullrev,
26 short,
26 short,
27 )
27 )
28 from mercurial import (
28 from mercurial import (
29 bookmarks,
29 bookmarks,
30 cmdutil,
30 cmdutil,
31 commands,
31 commands,
32 copies,
32 copies,
33 destutil,
33 destutil,
34 dirstateguard,
34 dirstateguard,
35 error,
35 error,
36 extensions,
36 extensions,
37 hg,
37 hg,
38 lock,
38 lock,
39 merge as mergemod,
39 merge as mergemod,
40 mergeutil,
40 mergeutil,
41 obsolete,
41 obsolete,
42 obsutil,
42 obsutil,
43 patch,
43 patch,
44 phases,
44 phases,
45 registrar,
45 registrar,
46 repair,
46 repair,
47 repoview,
47 repoview,
48 revset,
48 revset,
49 revsetlang,
49 revsetlang,
50 scmutil,
50 scmutil,
51 smartset,
51 smartset,
52 util,
52 util,
53 )
53 )
54
54
55 release = lock.release
55 release = lock.release
56 templateopts = cmdutil.templateopts
56 templateopts = cmdutil.templateopts
57
57
58 # The following constants are used throughout the rebase module. The ordering of
58 # The following constants are used throughout the rebase module. The ordering of
59 # their values must be maintained.
59 # their values must be maintained.
60
60
61 # Indicates that a revision needs to be rebased
61 # Indicates that a revision needs to be rebased
62 revtodo = -1
62 revtodo = -1
63 revtodostr = '-1'
63 revtodostr = '-1'
64
64
65 # legacy revstates no longer needed in current code
65 # legacy revstates no longer needed in current code
66 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
66 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
67 legacystates = {'-2', '-3', '-4', '-5'}
67 legacystates = {'-2', '-3', '-4', '-5'}
68
68
69 cmdtable = {}
69 cmdtable = {}
70 command = registrar.command(cmdtable)
70 command = registrar.command(cmdtable)
71 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
71 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
72 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
72 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
73 # be specifying the version(s) of Mercurial they are tested with, or
73 # be specifying the version(s) of Mercurial they are tested with, or
74 # leave the attribute unspecified.
74 # leave the attribute unspecified.
75 testedwith = 'ships-with-hg-core'
75 testedwith = 'ships-with-hg-core'
76
76
77 def _nothingtorebase():
77 def _nothingtorebase():
78 return 1
78 return 1
79
79
80 def _savegraft(ctx, extra):
80 def _savegraft(ctx, extra):
81 s = ctx.extra().get('source', None)
81 s = ctx.extra().get('source', None)
82 if s is not None:
82 if s is not None:
83 extra['source'] = s
83 extra['source'] = s
84 s = ctx.extra().get('intermediate-source', None)
84 s = ctx.extra().get('intermediate-source', None)
85 if s is not None:
85 if s is not None:
86 extra['intermediate-source'] = s
86 extra['intermediate-source'] = s
87
87
88 def _savebranch(ctx, extra):
88 def _savebranch(ctx, extra):
89 extra['branch'] = ctx.branch()
89 extra['branch'] = ctx.branch()
90
90
91 def _makeextrafn(copiers):
91 def _makeextrafn(copiers):
92 """make an extrafn out of the given copy-functions.
92 """make an extrafn out of the given copy-functions.
93
93
94 A copy function takes a context and an extra dict, and mutates the
94 A copy function takes a context and an extra dict, and mutates the
95 extra dict as needed based on the given context.
95 extra dict as needed based on the given context.
96 """
96 """
97 def extrafn(ctx, extra):
97 def extrafn(ctx, extra):
98 for c in copiers:
98 for c in copiers:
99 c(ctx, extra)
99 c(ctx, extra)
100 return extrafn
100 return extrafn
101
101
102 def _destrebase(repo, sourceset, destspace=None):
102 def _destrebase(repo, sourceset, destspace=None):
103 """small wrapper around destmerge to pass the right extra args
103 """small wrapper around destmerge to pass the right extra args
104
104
105 Please wrap destutil.destmerge instead."""
105 Please wrap destutil.destmerge instead."""
106 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
106 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
107 onheadcheck=False, destspace=destspace)
107 onheadcheck=False, destspace=destspace)
108
108
109 revsetpredicate = registrar.revsetpredicate()
109 revsetpredicate = registrar.revsetpredicate()
110
110
111 @revsetpredicate('_destrebase')
111 @revsetpredicate('_destrebase')
112 def _revsetdestrebase(repo, subset, x):
112 def _revsetdestrebase(repo, subset, x):
113 # ``_rebasedefaultdest()``
113 # ``_rebasedefaultdest()``
114
114
115 # default destination for rebase.
115 # default destination for rebase.
116 # # XXX: Currently private because I expect the signature to change.
116 # # XXX: Currently private because I expect the signature to change.
117 # # XXX: - bailing out in case of ambiguity vs returning all data.
117 # # XXX: - bailing out in case of ambiguity vs returning all data.
118 # i18n: "_rebasedefaultdest" is a keyword
118 # i18n: "_rebasedefaultdest" is a keyword
119 sourceset = None
119 sourceset = None
120 if x is not None:
120 if x is not None:
121 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
121 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
122 return subset & smartset.baseset([_destrebase(repo, sourceset)])
122 return subset & smartset.baseset([_destrebase(repo, sourceset)])
123
123
124 def _ctxdesc(ctx):
124 def _ctxdesc(ctx):
125 """short description for a context"""
125 """short description for a context"""
126 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
126 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
127 ctx.description().split('\n', 1)[0])
127 ctx.description().split('\n', 1)[0])
128 repo = ctx.repo()
128 repo = ctx.repo()
129 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
129 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
130 if names:
130 if names:
131 desc += ' (%s)' % ' '.join(names)
131 desc += ' (%s)' % ' '.join(names)
132 return desc
132 return desc
133
133
134 class rebaseruntime(object):
134 class rebaseruntime(object):
135 """This class is a container for rebase runtime state"""
135 """This class is a container for rebase runtime state"""
136 def __init__(self, repo, ui, opts=None):
136 def __init__(self, repo, ui, opts=None):
137 if opts is None:
137 if opts is None:
138 opts = {}
138 opts = {}
139
139
140 self.repo = repo
140 self.repo = repo
141 self.ui = ui
141 self.ui = ui
142 self.opts = opts
142 self.opts = opts
143 self.originalwd = None
143 self.originalwd = None
144 self.external = nullrev
144 self.external = nullrev
145 # Mapping between the old revision id and either what is the new rebased
145 # Mapping between the old revision id and either what is the new rebased
146 # revision or what needs to be done with the old revision. The state
146 # revision or what needs to be done with the old revision. The state
147 # dict will be what contains most of the rebase progress state.
147 # dict will be what contains most of the rebase progress state.
148 self.state = {}
148 self.state = {}
149 self.activebookmark = None
149 self.activebookmark = None
150 self.destmap = {}
150 self.destmap = {}
151 self.skipped = set()
151 self.skipped = set()
152
152
153 self.collapsef = opts.get('collapse', False)
153 self.collapsef = opts.get('collapse', False)
154 self.collapsemsg = cmdutil.logmessage(ui, opts)
154 self.collapsemsg = cmdutil.logmessage(ui, opts)
155 self.date = opts.get('date', None)
155 self.date = opts.get('date', None)
156
156
157 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
157 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
158 self.extrafns = [_savegraft]
158 self.extrafns = [_savegraft]
159 if e:
159 if e:
160 self.extrafns = [e]
160 self.extrafns = [e]
161
161
162 self.keepf = opts.get('keep', False)
162 self.keepf = opts.get('keep', False)
163 self.keepbranchesf = opts.get('keepbranches', False)
163 self.keepbranchesf = opts.get('keepbranches', False)
164 # keepopen is not meant for use on the command line, but by
164 # keepopen is not meant for use on the command line, but by
165 # other extensions
165 # other extensions
166 self.keepopen = opts.get('keepopen', False)
166 self.keepopen = opts.get('keepopen', False)
167 self.obsoletenotrebased = {}
167 self.obsoletenotrebased = {}
168
168
169 def storestatus(self, tr=None):
169 def storestatus(self, tr=None):
170 """Store the current status to allow recovery"""
170 """Store the current status to allow recovery"""
171 if tr:
171 if tr:
172 tr.addfilegenerator('rebasestate', ('rebasestate',),
172 tr.addfilegenerator('rebasestate', ('rebasestate',),
173 self._writestatus, location='plain')
173 self._writestatus, location='plain')
174 else:
174 else:
175 with self.repo.vfs("rebasestate", "w") as f:
175 with self.repo.vfs("rebasestate", "w") as f:
176 self._writestatus(f)
176 self._writestatus(f)
177
177
178 def _writestatus(self, f):
178 def _writestatus(self, f):
179 repo = self.repo.unfiltered()
179 repo = self.repo.unfiltered()
180 f.write(repo[self.originalwd].hex() + '\n')
180 f.write(repo[self.originalwd].hex() + '\n')
181 # was "dest". we now write dest per src root below.
181 # was "dest". we now write dest per src root below.
182 f.write('\n')
182 f.write('\n')
183 f.write(repo[self.external].hex() + '\n')
183 f.write(repo[self.external].hex() + '\n')
184 f.write('%d\n' % int(self.collapsef))
184 f.write('%d\n' % int(self.collapsef))
185 f.write('%d\n' % int(self.keepf))
185 f.write('%d\n' % int(self.keepf))
186 f.write('%d\n' % int(self.keepbranchesf))
186 f.write('%d\n' % int(self.keepbranchesf))
187 f.write('%s\n' % (self.activebookmark or ''))
187 f.write('%s\n' % (self.activebookmark or ''))
188 destmap = self.destmap
188 destmap = self.destmap
189 for d, v in self.state.iteritems():
189 for d, v in self.state.iteritems():
190 oldrev = repo[d].hex()
190 oldrev = repo[d].hex()
191 if v >= 0:
191 if v >= 0:
192 newrev = repo[v].hex()
192 newrev = repo[v].hex()
193 else:
193 else:
194 newrev = v
194 newrev = v
195 destnode = repo[destmap[d]].hex()
195 destnode = repo[destmap[d]].hex()
196 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
196 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
197 repo.ui.debug('rebase status stored\n')
197 repo.ui.debug('rebase status stored\n')
198
198
199 def restorestatus(self):
199 def restorestatus(self):
200 """Restore a previously stored status"""
200 """Restore a previously stored status"""
201 repo = self.repo
201 repo = self.repo
202 keepbranches = None
202 keepbranches = None
203 legacydest = None
203 legacydest = None
204 collapse = False
204 collapse = False
205 external = nullrev
205 external = nullrev
206 activebookmark = None
206 activebookmark = None
207 state = {}
207 state = {}
208 destmap = {}
208 destmap = {}
209
209
210 try:
210 try:
211 f = repo.vfs("rebasestate")
211 f = repo.vfs("rebasestate")
212 for i, l in enumerate(f.read().splitlines()):
212 for i, l in enumerate(f.read().splitlines()):
213 if i == 0:
213 if i == 0:
214 originalwd = repo[l].rev()
214 originalwd = repo[l].rev()
215 elif i == 1:
215 elif i == 1:
216 # this line should be empty in newer version. but legacy
216 # this line should be empty in newer version. but legacy
217 # clients may still use it
217 # clients may still use it
218 if l:
218 if l:
219 legacydest = repo[l].rev()
219 legacydest = repo[l].rev()
220 elif i == 2:
220 elif i == 2:
221 external = repo[l].rev()
221 external = repo[l].rev()
222 elif i == 3:
222 elif i == 3:
223 collapse = bool(int(l))
223 collapse = bool(int(l))
224 elif i == 4:
224 elif i == 4:
225 keep = bool(int(l))
225 keep = bool(int(l))
226 elif i == 5:
226 elif i == 5:
227 keepbranches = bool(int(l))
227 keepbranches = bool(int(l))
228 elif i == 6 and not (len(l) == 81 and ':' in l):
228 elif i == 6 and not (len(l) == 81 and ':' in l):
229 # line 6 is a recent addition, so for backwards
229 # line 6 is a recent addition, so for backwards
230 # compatibility check that the line doesn't look like the
230 # compatibility check that the line doesn't look like the
231 # oldrev:newrev lines
231 # oldrev:newrev lines
232 activebookmark = l
232 activebookmark = l
233 else:
233 else:
234 args = l.split(':')
234 args = l.split(':')
235 oldrev = args[0]
235 oldrev = args[0]
236 newrev = args[1]
236 newrev = args[1]
237 if newrev in legacystates:
237 if newrev in legacystates:
238 continue
238 continue
239 if len(args) > 2:
239 if len(args) > 2:
240 destnode = args[2]
240 destnode = args[2]
241 else:
241 else:
242 destnode = legacydest
242 destnode = legacydest
243 destmap[repo[oldrev].rev()] = repo[destnode].rev()
243 destmap[repo[oldrev].rev()] = repo[destnode].rev()
244 if newrev in (nullid, revtodostr):
244 if newrev in (nullid, revtodostr):
245 state[repo[oldrev].rev()] = revtodo
245 state[repo[oldrev].rev()] = revtodo
246 # Legacy compat special case
246 # Legacy compat special case
247 else:
247 else:
248 state[repo[oldrev].rev()] = repo[newrev].rev()
248 state[repo[oldrev].rev()] = repo[newrev].rev()
249
249
250 except IOError as err:
250 except IOError as err:
251 if err.errno != errno.ENOENT:
251 if err.errno != errno.ENOENT:
252 raise
252 raise
253 cmdutil.wrongtooltocontinue(repo, _('rebase'))
253 cmdutil.wrongtooltocontinue(repo, _('rebase'))
254
254
255 if keepbranches is None:
255 if keepbranches is None:
256 raise error.Abort(_('.hg/rebasestate is incomplete'))
256 raise error.Abort(_('.hg/rebasestate is incomplete'))
257
257
258 skipped = set()
258 skipped = set()
259 # recompute the set of skipped revs
259 # recompute the set of skipped revs
260 if not collapse:
260 if not collapse:
261 seen = set(destmap.values())
261 seen = set(destmap.values())
262 for old, new in sorted(state.items()):
262 for old, new in sorted(state.items()):
263 if new != revtodo and new in seen:
263 if new != revtodo and new in seen:
264 skipped.add(old)
264 skipped.add(old)
265 seen.add(new)
265 seen.add(new)
266 repo.ui.debug('computed skipped revs: %s\n' %
266 repo.ui.debug('computed skipped revs: %s\n' %
267 (' '.join(str(r) for r in sorted(skipped)) or None))
267 (' '.join(str(r) for r in sorted(skipped)) or None))
268 repo.ui.debug('rebase status resumed\n')
268 repo.ui.debug('rebase status resumed\n')
269 _setrebasesetvisibility(repo, set(state.keys()) | {originalwd})
269 _setrebasesetvisibility(repo, set(state.keys()) | {originalwd})
270
270
271 self.originalwd = originalwd
271 self.originalwd = originalwd
272 self.destmap = destmap
272 self.destmap = destmap
273 self.state = state
273 self.state = state
274 self.skipped = skipped
274 self.skipped = skipped
275 self.collapsef = collapse
275 self.collapsef = collapse
276 self.keepf = keep
276 self.keepf = keep
277 self.keepbranchesf = keepbranches
277 self.keepbranchesf = keepbranches
278 self.external = external
278 self.external = external
279 self.activebookmark = activebookmark
279 self.activebookmark = activebookmark
280
280
281 def _handleskippingobsolete(self, obsoleterevs, destmap):
281 def _handleskippingobsolete(self, obsoleterevs, destmap):
282 """Compute structures necessary for skipping obsolete revisions
282 """Compute structures necessary for skipping obsolete revisions
283
283
284 obsoleterevs: iterable of all obsolete revisions in rebaseset
284 obsoleterevs: iterable of all obsolete revisions in rebaseset
285 destmap: {srcrev: destrev} destination revisions
285 destmap: {srcrev: destrev} destination revisions
286 """
286 """
287 self.obsoletenotrebased = {}
287 self.obsoletenotrebased = {}
288 if not self.ui.configbool('experimental', 'rebaseskipobsolete',
288 if not self.ui.configbool('experimental', 'rebaseskipobsolete',
289 default=True):
289 default=True):
290 return
290 return
291 obsoleteset = set(obsoleterevs)
291 obsoleteset = set(obsoleterevs)
292 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
292 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
293 obsoleteset, destmap)
293 obsoleteset, destmap)
294 skippedset = set(self.obsoletenotrebased)
294 skippedset = set(self.obsoletenotrebased)
295 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
295 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
296
296
297 def _prepareabortorcontinue(self, isabort):
297 def _prepareabortorcontinue(self, isabort):
298 try:
298 try:
299 self.restorestatus()
299 self.restorestatus()
300 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
300 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
301 except error.RepoLookupError:
301 except error.RepoLookupError:
302 if isabort:
302 if isabort:
303 clearstatus(self.repo)
303 clearstatus(self.repo)
304 clearcollapsemsg(self.repo)
304 clearcollapsemsg(self.repo)
305 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
305 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
306 ' only broken state is cleared)\n'))
306 ' only broken state is cleared)\n'))
307 return 0
307 return 0
308 else:
308 else:
309 msg = _('cannot continue inconsistent rebase')
309 msg = _('cannot continue inconsistent rebase')
310 hint = _('use "hg rebase --abort" to clear broken state')
310 hint = _('use "hg rebase --abort" to clear broken state')
311 raise error.Abort(msg, hint=hint)
311 raise error.Abort(msg, hint=hint)
312 if isabort:
312 if isabort:
313 return abort(self.repo, self.originalwd, self.destmap,
313 return abort(self.repo, self.originalwd, self.destmap,
314 self.state, activebookmark=self.activebookmark)
314 self.state, activebookmark=self.activebookmark)
315
315
316 def _preparenewrebase(self, destmap):
316 def _preparenewrebase(self, destmap):
317 if not destmap:
317 if not destmap:
318 return _nothingtorebase()
318 return _nothingtorebase()
319
319
320 rebaseset = destmap.keys()
320 rebaseset = destmap.keys()
321 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
321 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
322 if (not (self.keepf or allowunstable)
322 if (not (self.keepf or allowunstable)
323 and self.repo.revs('first(children(%ld) - %ld)',
323 and self.repo.revs('first(children(%ld) - %ld)',
324 rebaseset, rebaseset)):
324 rebaseset, rebaseset)):
325 raise error.Abort(
325 raise error.Abort(
326 _("can't remove original changesets with"
326 _("can't remove original changesets with"
327 " unrebased descendants"),
327 " unrebased descendants"),
328 hint=_('use --keep to keep original changesets'))
328 hint=_('use --keep to keep original changesets'))
329
329
330 result = buildstate(self.repo, destmap, self.collapsef)
330 result = buildstate(self.repo, destmap, self.collapsef)
331
331
332 if not result:
332 if not result:
333 # Empty state built, nothing to rebase
333 # Empty state built, nothing to rebase
334 self.ui.status(_('nothing to rebase\n'))
334 self.ui.status(_('nothing to rebase\n'))
335 return _nothingtorebase()
335 return _nothingtorebase()
336
336
337 for root in self.repo.set('roots(%ld)', rebaseset):
337 for root in self.repo.set('roots(%ld)', rebaseset):
338 if not self.keepf and not root.mutable():
338 if not self.keepf and not root.mutable():
339 raise error.Abort(_("can't rebase public changeset %s")
339 raise error.Abort(_("can't rebase public changeset %s")
340 % root,
340 % root,
341 hint=_("see 'hg help phases' for details"))
341 hint=_("see 'hg help phases' for details"))
342
342
343 (self.originalwd, self.destmap, self.state) = result
343 (self.originalwd, self.destmap, self.state) = result
344 if self.collapsef:
344 if self.collapsef:
345 dests = set(self.destmap.values())
345 dests = set(self.destmap.values())
346 if len(dests) != 1:
346 if len(dests) != 1:
347 raise error.Abort(
347 raise error.Abort(
348 _('--collapse does not work with multiple destinations'))
348 _('--collapse does not work with multiple destinations'))
349 destrev = next(iter(dests))
349 destrev = next(iter(dests))
350 destancestors = self.repo.changelog.ancestors([destrev],
350 destancestors = self.repo.changelog.ancestors([destrev],
351 inclusive=True)
351 inclusive=True)
352 self.external = externalparent(self.repo, self.state, destancestors)
352 self.external = externalparent(self.repo, self.state, destancestors)
353
353
354 for destrev in sorted(set(destmap.values())):
354 for destrev in sorted(set(destmap.values())):
355 dest = self.repo[destrev]
355 dest = self.repo[destrev]
356 if dest.closesbranch() and not self.keepbranchesf:
356 if dest.closesbranch() and not self.keepbranchesf:
357 self.ui.status(_('reopening closed branch head %s\n') % dest)
357 self.ui.status(_('reopening closed branch head %s\n') % dest)
358
358
359 def _performrebase(self, tr):
359 def _performrebase(self, tr):
360 repo, ui = self.repo, self.ui
360 repo, ui = self.repo, self.ui
361 if self.keepbranchesf:
361 if self.keepbranchesf:
362 # insert _savebranch at the start of extrafns so if
362 # insert _savebranch at the start of extrafns so if
363 # there's a user-provided extrafn it can clobber branch if
363 # there's a user-provided extrafn it can clobber branch if
364 # desired
364 # desired
365 self.extrafns.insert(0, _savebranch)
365 self.extrafns.insert(0, _savebranch)
366 if self.collapsef:
366 if self.collapsef:
367 branches = set()
367 branches = set()
368 for rev in self.state:
368 for rev in self.state:
369 branches.add(repo[rev].branch())
369 branches.add(repo[rev].branch())
370 if len(branches) > 1:
370 if len(branches) > 1:
371 raise error.Abort(_('cannot collapse multiple named '
371 raise error.Abort(_('cannot collapse multiple named '
372 'branches'))
372 'branches'))
373
373
374 # Calculate self.obsoletenotrebased
374 # Calculate self.obsoletenotrebased
375 obsrevs = _filterobsoleterevs(self.repo, self.state)
375 obsrevs = _filterobsoleterevs(self.repo, self.state)
376 self._handleskippingobsolete(obsrevs, self.destmap)
376 self._handleskippingobsolete(obsrevs, self.destmap)
377
377
378 # Keep track of the active bookmarks in order to reset them later
378 # Keep track of the active bookmarks in order to reset them later
379 self.activebookmark = self.activebookmark or repo._activebookmark
379 self.activebookmark = self.activebookmark or repo._activebookmark
380 if self.activebookmark:
380 if self.activebookmark:
381 bookmarks.deactivate(repo)
381 bookmarks.deactivate(repo)
382
382
383 # Store the state before we begin so users can run 'hg rebase --abort'
383 # Store the state before we begin so users can run 'hg rebase --abort'
384 # if we fail before the transaction closes.
384 # if we fail before the transaction closes.
385 self.storestatus()
385 self.storestatus()
386
386
387 cands = [k for k, v in self.state.iteritems() if v == revtodo]
387 cands = [k for k, v in self.state.iteritems() if v == revtodo]
388 total = len(cands)
388 total = len(cands)
389 pos = 0
389 pos = 0
390 for subset in sortsource(self.destmap):
390 for subset in sortsource(self.destmap):
391 pos = self._performrebasesubset(tr, subset, pos, total)
391 pos = self._performrebasesubset(tr, subset, pos, total)
392 ui.progress(_('rebasing'), None)
392 ui.progress(_('rebasing'), None)
393 ui.note(_('rebase merging completed\n'))
393 ui.note(_('rebase merging completed\n'))
394
394
395 def _performrebasesubset(self, tr, subset, pos, total):
395 def _performrebasesubset(self, tr, subset, pos, total):
396 repo, ui, opts = self.repo, self.ui, self.opts
396 repo, ui, opts = self.repo, self.ui, self.opts
397 sortedrevs = repo.revs('sort(%ld, -topo)', subset)
397 sortedrevs = repo.revs('sort(%ld, -topo)', subset)
398 for rev in sortedrevs:
398 for rev in sortedrevs:
399 dest = self.destmap[rev]
399 dest = self.destmap[rev]
400 ctx = repo[rev]
400 ctx = repo[rev]
401 desc = _ctxdesc(ctx)
401 desc = _ctxdesc(ctx)
402 if self.state[rev] == rev:
402 if self.state[rev] == rev:
403 ui.status(_('already rebased %s\n') % desc)
403 ui.status(_('already rebased %s\n') % desc)
404 elif rev in self.obsoletenotrebased:
404 elif rev in self.obsoletenotrebased:
405 succ = self.obsoletenotrebased[rev]
405 succ = self.obsoletenotrebased[rev]
406 if succ is None:
406 if succ is None:
407 msg = _('note: not rebasing %s, it has no '
407 msg = _('note: not rebasing %s, it has no '
408 'successor\n') % desc
408 'successor\n') % desc
409 else:
409 else:
410 succdesc = _ctxdesc(repo[succ])
410 succdesc = _ctxdesc(repo[succ])
411 msg = (_('note: not rebasing %s, already in '
411 msg = (_('note: not rebasing %s, already in '
412 'destination as %s\n') % (desc, succdesc))
412 'destination as %s\n') % (desc, succdesc))
413 repo.ui.status(msg)
413 repo.ui.status(msg)
414 # Make clearrebased aware state[rev] is not a true successor
414 # Make clearrebased aware state[rev] is not a true successor
415 self.skipped.add(rev)
415 self.skipped.add(rev)
416 # Record rev as moved to its desired destination in self.state.
416 # Record rev as moved to its desired destination in self.state.
417 # This helps bookmark and working parent movement.
417 # This helps bookmark and working parent movement.
418 dest = max(adjustdest(repo, rev, self.destmap, self.state,
418 dest = max(adjustdest(repo, rev, self.destmap, self.state,
419 self.skipped))
419 self.skipped))
420 self.state[rev] = dest
420 self.state[rev] = dest
421 elif self.state[rev] == revtodo:
421 elif self.state[rev] == revtodo:
422 pos += 1
422 pos += 1
423 ui.status(_('rebasing %s\n') % desc)
423 ui.status(_('rebasing %s\n') % desc)
424 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
424 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
425 _('changesets'), total)
425 _('changesets'), total)
426 p1, p2, base = defineparents(repo, rev, self.destmap,
426 p1, p2, base = defineparents(repo, rev, self.destmap,
427 self.state, self.skipped,
427 self.state, self.skipped,
428 self.obsoletenotrebased)
428 self.obsoletenotrebased)
429 self.storestatus(tr=tr)
429 self.storestatus(tr=tr)
430 storecollapsemsg(repo, self.collapsemsg)
430 storecollapsemsg(repo, self.collapsemsg)
431 if len(repo[None].parents()) == 2:
431 if len(repo[None].parents()) == 2:
432 repo.ui.debug('resuming interrupted rebase\n')
432 repo.ui.debug('resuming interrupted rebase\n')
433 else:
433 else:
434 try:
434 try:
435 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
435 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
436 'rebase')
436 'rebase')
437 stats = rebasenode(repo, rev, p1, base, self.state,
437 stats = rebasenode(repo, rev, p1, base, self.state,
438 self.collapsef, dest)
438 self.collapsef, dest)
439 if stats and stats[3] > 0:
439 if stats and stats[3] > 0:
440 raise error.InterventionRequired(
440 raise error.InterventionRequired(
441 _('unresolved conflicts (see hg '
441 _('unresolved conflicts (see hg '
442 'resolve, then hg rebase --continue)'))
442 'resolve, then hg rebase --continue)'))
443 finally:
443 finally:
444 ui.setconfig('ui', 'forcemerge', '', 'rebase')
444 ui.setconfig('ui', 'forcemerge', '', 'rebase')
445 if not self.collapsef:
445 if not self.collapsef:
446 merging = p2 != nullrev
446 merging = p2 != nullrev
447 editform = cmdutil.mergeeditform(merging, 'rebase')
447 editform = cmdutil.mergeeditform(merging, 'rebase')
448 editor = cmdutil.getcommiteditor(editform=editform, **opts)
448 editor = cmdutil.getcommiteditor(editform=editform, **opts)
449 newnode = concludenode(repo, rev, p1, p2,
449 newnode = concludenode(repo, rev, p1, p2,
450 extrafn=_makeextrafn(self.extrafns),
450 extrafn=_makeextrafn(self.extrafns),
451 editor=editor,
451 editor=editor,
452 keepbranches=self.keepbranchesf,
452 keepbranches=self.keepbranchesf,
453 date=self.date)
453 date=self.date)
454 if newnode is None:
454 if newnode is None:
455 # If it ended up being a no-op commit, then the normal
455 # If it ended up being a no-op commit, then the normal
456 # merge state clean-up path doesn't happen, so do it
456 # merge state clean-up path doesn't happen, so do it
457 # here. Fix issue5494
457 # here. Fix issue5494
458 mergemod.mergestate.clean(repo)
458 mergemod.mergestate.clean(repo)
459 else:
459 else:
460 # Skip commit if we are collapsing
460 # Skip commit if we are collapsing
461 repo.setparents(repo[p1].node())
461 repo.setparents(repo[p1].node())
462 newnode = None
462 newnode = None
463 # Update the state
463 # Update the state
464 if newnode is not None:
464 if newnode is not None:
465 self.state[rev] = repo[newnode].rev()
465 self.state[rev] = repo[newnode].rev()
466 ui.debug('rebased as %s\n' % short(newnode))
466 ui.debug('rebased as %s\n' % short(newnode))
467 else:
467 else:
468 if not self.collapsef:
468 if not self.collapsef:
469 ui.warn(_('note: rebase of %d:%s created no changes '
469 ui.warn(_('note: rebase of %d:%s created no changes '
470 'to commit\n') % (rev, ctx))
470 'to commit\n') % (rev, ctx))
471 self.skipped.add(rev)
471 self.skipped.add(rev)
472 self.state[rev] = p1
472 self.state[rev] = p1
473 ui.debug('next revision set to %s\n' % p1)
473 ui.debug('next revision set to %s\n' % p1)
474 else:
474 else:
475 ui.status(_('already rebased %s as %s\n') %
475 ui.status(_('already rebased %s as %s\n') %
476 (desc, repo[self.state[rev]]))
476 (desc, repo[self.state[rev]]))
477 return pos
477 return pos
478
478
479 def _finishrebase(self):
479 def _finishrebase(self):
480 repo, ui, opts = self.repo, self.ui, self.opts
480 repo, ui, opts = self.repo, self.ui, self.opts
481 if self.collapsef and not self.keepopen:
481 if self.collapsef and not self.keepopen:
482 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
482 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
483 self.state, self.skipped,
483 self.state, self.skipped,
484 self.obsoletenotrebased)
484 self.obsoletenotrebased)
485 editopt = opts.get('edit')
485 editopt = opts.get('edit')
486 editform = 'rebase.collapse'
486 editform = 'rebase.collapse'
487 if self.collapsemsg:
487 if self.collapsemsg:
488 commitmsg = self.collapsemsg
488 commitmsg = self.collapsemsg
489 else:
489 else:
490 commitmsg = 'Collapsed revision'
490 commitmsg = 'Collapsed revision'
491 for rebased in sorted(self.state):
491 for rebased in sorted(self.state):
492 if rebased not in self.skipped:
492 if rebased not in self.skipped:
493 commitmsg += '\n* %s' % repo[rebased].description()
493 commitmsg += '\n* %s' % repo[rebased].description()
494 editopt = True
494 editopt = True
495 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
495 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
496 revtoreuse = max(self.state)
496 revtoreuse = max(self.state)
497
497
498 dsguard = None
498 dsguard = None
499 if ui.configbool('rebase', 'singletransaction'):
499 if ui.configbool('rebase', 'singletransaction'):
500 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
500 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
501 with util.acceptintervention(dsguard):
501 with util.acceptintervention(dsguard):
502 newnode = concludenode(repo, revtoreuse, p1, self.external,
502 newnode = concludenode(repo, revtoreuse, p1, self.external,
503 commitmsg=commitmsg,
503 commitmsg=commitmsg,
504 extrafn=_makeextrafn(self.extrafns),
504 extrafn=_makeextrafn(self.extrafns),
505 editor=editor,
505 editor=editor,
506 keepbranches=self.keepbranchesf,
506 keepbranches=self.keepbranchesf,
507 date=self.date)
507 date=self.date)
508 if newnode is not None:
508 if newnode is not None:
509 newrev = repo[newnode].rev()
509 newrev = repo[newnode].rev()
510 for oldrev in self.state.iterkeys():
510 for oldrev in self.state.iterkeys():
511 self.state[oldrev] = newrev
511 self.state[oldrev] = newrev
512
512
513 if 'qtip' in repo.tags():
513 if 'qtip' in repo.tags():
514 updatemq(repo, self.state, self.skipped, **opts)
514 updatemq(repo, self.state, self.skipped, **opts)
515
515
516 # restore original working directory
516 # restore original working directory
517 # (we do this before stripping)
517 # (we do this before stripping)
518 newwd = self.state.get(self.originalwd, self.originalwd)
518 newwd = self.state.get(self.originalwd, self.originalwd)
519 if newwd < 0:
519 if newwd < 0:
520 # original directory is a parent of rebase set root or ignored
520 # original directory is a parent of rebase set root or ignored
521 newwd = self.originalwd
521 newwd = self.originalwd
522 if newwd not in [c.rev() for c in repo[None].parents()]:
522 if newwd not in [c.rev() for c in repo[None].parents()]:
523 ui.note(_("update back to initial working directory parent\n"))
523 ui.note(_("update back to initial working directory parent\n"))
524 hg.updaterepo(repo, newwd, False)
524 hg.updaterepo(repo, newwd, False)
525
525
526 if not self.keepf:
526 if not self.keepf:
527 collapsedas = None
527 collapsedas = None
528 if self.collapsef:
528 if self.collapsef:
529 collapsedas = newnode
529 collapsedas = newnode
530 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
530 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
531 collapsedas)
531 collapsedas)
532
532
533 clearstatus(repo)
533 clearstatus(repo)
534 clearcollapsemsg(repo)
534 clearcollapsemsg(repo)
535
535
536 ui.note(_("rebase completed\n"))
536 ui.note(_("rebase completed\n"))
537 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
537 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
538 if self.skipped:
538 if self.skipped:
539 skippedlen = len(self.skipped)
539 skippedlen = len(self.skipped)
540 ui.note(_("%d revisions have been skipped\n") % skippedlen)
540 ui.note(_("%d revisions have been skipped\n") % skippedlen)
541
541
542 if (self.activebookmark and self.activebookmark in repo._bookmarks and
542 if (self.activebookmark and self.activebookmark in repo._bookmarks and
543 repo['.'].node() == repo._bookmarks[self.activebookmark]):
543 repo['.'].node() == repo._bookmarks[self.activebookmark]):
544 bookmarks.activate(repo, self.activebookmark)
544 bookmarks.activate(repo, self.activebookmark)
545
545
546 @command('rebase',
546 @command('rebase',
547 [('s', 'source', '',
547 [('s', 'source', '',
548 _('rebase the specified changeset and descendants'), _('REV')),
548 _('rebase the specified changeset and descendants'), _('REV')),
549 ('b', 'base', '',
549 ('b', 'base', '',
550 _('rebase everything from branching point of specified changeset'),
550 _('rebase everything from branching point of specified changeset'),
551 _('REV')),
551 _('REV')),
552 ('r', 'rev', [],
552 ('r', 'rev', [],
553 _('rebase these revisions'),
553 _('rebase these revisions'),
554 _('REV')),
554 _('REV')),
555 ('d', 'dest', '',
555 ('d', 'dest', '',
556 _('rebase onto the specified changeset'), _('REV')),
556 _('rebase onto the specified changeset'), _('REV')),
557 ('', 'collapse', False, _('collapse the rebased changesets')),
557 ('', 'collapse', False, _('collapse the rebased changesets')),
558 ('m', 'message', '',
558 ('m', 'message', '',
559 _('use text as collapse commit message'), _('TEXT')),
559 _('use text as collapse commit message'), _('TEXT')),
560 ('e', 'edit', False, _('invoke editor on commit messages')),
560 ('e', 'edit', False, _('invoke editor on commit messages')),
561 ('l', 'logfile', '',
561 ('l', 'logfile', '',
562 _('read collapse commit message from file'), _('FILE')),
562 _('read collapse commit message from file'), _('FILE')),
563 ('k', 'keep', False, _('keep original changesets')),
563 ('k', 'keep', False, _('keep original changesets')),
564 ('', 'keepbranches', False, _('keep original branch names')),
564 ('', 'keepbranches', False, _('keep original branch names')),
565 ('D', 'detach', False, _('(DEPRECATED)')),
565 ('D', 'detach', False, _('(DEPRECATED)')),
566 ('i', 'interactive', False, _('(DEPRECATED)')),
566 ('i', 'interactive', False, _('(DEPRECATED)')),
567 ('t', 'tool', '', _('specify merge tool')),
567 ('t', 'tool', '', _('specify merge tool')),
568 ('c', 'continue', False, _('continue an interrupted rebase')),
568 ('c', 'continue', False, _('continue an interrupted rebase')),
569 ('a', 'abort', False, _('abort an interrupted rebase'))] +
569 ('a', 'abort', False, _('abort an interrupted rebase'))] +
570 templateopts,
570 templateopts,
571 _('[-s REV | -b REV] [-d REV] [OPTION]'))
571 _('[-s REV | -b REV] [-d REV] [OPTION]'))
572 def rebase(ui, repo, **opts):
572 def rebase(ui, repo, **opts):
573 """move changeset (and descendants) to a different branch
573 """move changeset (and descendants) to a different branch
574
574
575 Rebase uses repeated merging to graft changesets from one part of
575 Rebase uses repeated merging to graft changesets from one part of
576 history (the source) onto another (the destination). This can be
576 history (the source) onto another (the destination). This can be
577 useful for linearizing *local* changes relative to a master
577 useful for linearizing *local* changes relative to a master
578 development tree.
578 development tree.
579
579
580 Published commits cannot be rebased (see :hg:`help phases`).
580 Published commits cannot be rebased (see :hg:`help phases`).
581 To copy commits, see :hg:`help graft`.
581 To copy commits, see :hg:`help graft`.
582
582
583 If you don't specify a destination changeset (``-d/--dest``), rebase
583 If you don't specify a destination changeset (``-d/--dest``), rebase
584 will use the same logic as :hg:`merge` to pick a destination. if
584 will use the same logic as :hg:`merge` to pick a destination. if
585 the current branch contains exactly one other head, the other head
585 the current branch contains exactly one other head, the other head
586 is merged with by default. Otherwise, an explicit revision with
586 is merged with by default. Otherwise, an explicit revision with
587 which to merge with must be provided. (destination changeset is not
587 which to merge with must be provided. (destination changeset is not
588 modified by rebasing, but new changesets are added as its
588 modified by rebasing, but new changesets are added as its
589 descendants.)
589 descendants.)
590
590
591 Here are the ways to select changesets:
591 Here are the ways to select changesets:
592
592
593 1. Explicitly select them using ``--rev``.
593 1. Explicitly select them using ``--rev``.
594
594
595 2. Use ``--source`` to select a root changeset and include all of its
595 2. Use ``--source`` to select a root changeset and include all of its
596 descendants.
596 descendants.
597
597
598 3. Use ``--base`` to select a changeset; rebase will find ancestors
598 3. Use ``--base`` to select a changeset; rebase will find ancestors
599 and their descendants which are not also ancestors of the destination.
599 and their descendants which are not also ancestors of the destination.
600
600
601 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
601 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
602 rebase will use ``--base .`` as above.
602 rebase will use ``--base .`` as above.
603
603
604 Rebase will destroy original changesets unless you use ``--keep``.
604 Rebase will destroy original changesets unless you use ``--keep``.
605 It will also move your bookmarks (even if you do).
605 It will also move your bookmarks (even if you do).
606
606
607 Some changesets may be dropped if they do not contribute changes
607 Some changesets may be dropped if they do not contribute changes
608 (e.g. merges from the destination branch).
608 (e.g. merges from the destination branch).
609
609
610 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
610 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
611 a named branch with two heads. You will need to explicitly specify source
611 a named branch with two heads. You will need to explicitly specify source
612 and/or destination.
612 and/or destination.
613
613
614 If you need to use a tool to automate merge/conflict decisions, you
614 If you need to use a tool to automate merge/conflict decisions, you
615 can specify one with ``--tool``, see :hg:`help merge-tools`.
615 can specify one with ``--tool``, see :hg:`help merge-tools`.
616 As a caveat: the tool will not be used to mediate when a file was
616 As a caveat: the tool will not be used to mediate when a file was
617 deleted, there is no hook presently available for this.
617 deleted, there is no hook presently available for this.
618
618
619 If a rebase is interrupted to manually resolve a conflict, it can be
619 If a rebase is interrupted to manually resolve a conflict, it can be
620 continued with --continue/-c or aborted with --abort/-a.
620 continued with --continue/-c or aborted with --abort/-a.
621
621
622 .. container:: verbose
622 .. container:: verbose
623
623
624 Examples:
624 Examples:
625
625
626 - move "local changes" (current commit back to branching point)
626 - move "local changes" (current commit back to branching point)
627 to the current branch tip after a pull::
627 to the current branch tip after a pull::
628
628
629 hg rebase
629 hg rebase
630
630
631 - move a single changeset to the stable branch::
631 - move a single changeset to the stable branch::
632
632
633 hg rebase -r 5f493448 -d stable
633 hg rebase -r 5f493448 -d stable
634
634
635 - splice a commit and all its descendants onto another part of history::
635 - splice a commit and all its descendants onto another part of history::
636
636
637 hg rebase --source c0c3 --dest 4cf9
637 hg rebase --source c0c3 --dest 4cf9
638
638
639 - rebase everything on a branch marked by a bookmark onto the
639 - rebase everything on a branch marked by a bookmark onto the
640 default branch::
640 default branch::
641
641
642 hg rebase --base myfeature --dest default
642 hg rebase --base myfeature --dest default
643
643
644 - collapse a sequence of changes into a single commit::
644 - collapse a sequence of changes into a single commit::
645
645
646 hg rebase --collapse -r 1520:1525 -d .
646 hg rebase --collapse -r 1520:1525 -d .
647
647
648 - move a named branch while preserving its name::
648 - move a named branch while preserving its name::
649
649
650 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
650 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
651
651
652 Configuration Options:
652 Configuration Options:
653
653
654 You can make rebase require a destination if you set the following config
654 You can make rebase require a destination if you set the following config
655 option::
655 option::
656
656
657 [commands]
657 [commands]
658 rebase.requiredest = True
658 rebase.requiredest = True
659
659
660 By default, rebase will close the transaction after each commit. For
660 By default, rebase will close the transaction after each commit. For
661 performance purposes, you can configure rebase to use a single transaction
661 performance purposes, you can configure rebase to use a single transaction
662 across the entire rebase. WARNING: This setting introduces a significant
662 across the entire rebase. WARNING: This setting introduces a significant
663 risk of losing the work you've done in a rebase if the rebase aborts
663 risk of losing the work you've done in a rebase if the rebase aborts
664 unexpectedly::
664 unexpectedly::
665
665
666 [rebase]
666 [rebase]
667 singletransaction = True
667 singletransaction = True
668
668
669 Return Values:
669 Return Values:
670
670
671 Returns 0 on success, 1 if nothing to rebase or there are
671 Returns 0 on success, 1 if nothing to rebase or there are
672 unresolved conflicts.
672 unresolved conflicts.
673
673
674 """
674 """
675 rbsrt = rebaseruntime(repo, ui, opts)
675 rbsrt = rebaseruntime(repo, ui, opts)
676
676
677 with repo.wlock(), repo.lock():
677 with repo.wlock(), repo.lock():
678 # Validate input and define rebasing points
678 # Validate input and define rebasing points
679 destf = opts.get('dest', None)
679 destf = opts.get('dest', None)
680 srcf = opts.get('source', None)
680 srcf = opts.get('source', None)
681 basef = opts.get('base', None)
681 basef = opts.get('base', None)
682 revf = opts.get('rev', [])
682 revf = opts.get('rev', [])
683 # search default destination in this space
683 # search default destination in this space
684 # used in the 'hg pull --rebase' case, see issue 5214.
684 # used in the 'hg pull --rebase' case, see issue 5214.
685 destspace = opts.get('_destspace')
685 destspace = opts.get('_destspace')
686 contf = opts.get('continue')
686 contf = opts.get('continue')
687 abortf = opts.get('abort')
687 abortf = opts.get('abort')
688 if opts.get('interactive'):
688 if opts.get('interactive'):
689 try:
689 try:
690 if extensions.find('histedit'):
690 if extensions.find('histedit'):
691 enablehistedit = ''
691 enablehistedit = ''
692 except KeyError:
692 except KeyError:
693 enablehistedit = " --config extensions.histedit="
693 enablehistedit = " --config extensions.histedit="
694 help = "hg%s help -e histedit" % enablehistedit
694 help = "hg%s help -e histedit" % enablehistedit
695 msg = _("interactive history editing is supported by the "
695 msg = _("interactive history editing is supported by the "
696 "'histedit' extension (see \"%s\")") % help
696 "'histedit' extension (see \"%s\")") % help
697 raise error.Abort(msg)
697 raise error.Abort(msg)
698
698
699 if rbsrt.collapsemsg and not rbsrt.collapsef:
699 if rbsrt.collapsemsg and not rbsrt.collapsef:
700 raise error.Abort(
700 raise error.Abort(
701 _('message can only be specified with collapse'))
701 _('message can only be specified with collapse'))
702
702
703 if contf or abortf:
703 if contf or abortf:
704 if contf and abortf:
704 if contf and abortf:
705 raise error.Abort(_('cannot use both abort and continue'))
705 raise error.Abort(_('cannot use both abort and continue'))
706 if rbsrt.collapsef:
706 if rbsrt.collapsef:
707 raise error.Abort(
707 raise error.Abort(
708 _('cannot use collapse with continue or abort'))
708 _('cannot use collapse with continue or abort'))
709 if srcf or basef or destf:
709 if srcf or basef or destf:
710 raise error.Abort(
710 raise error.Abort(
711 _('abort and continue do not allow specifying revisions'))
711 _('abort and continue do not allow specifying revisions'))
712 if abortf and opts.get('tool', False):
712 if abortf and opts.get('tool', False):
713 ui.warn(_('tool option will be ignored\n'))
713 ui.warn(_('tool option will be ignored\n'))
714 if contf:
714 if contf:
715 ms = mergemod.mergestate.read(repo)
715 ms = mergemod.mergestate.read(repo)
716 mergeutil.checkunresolved(ms)
716 mergeutil.checkunresolved(ms)
717
717
718 retcode = rbsrt._prepareabortorcontinue(abortf)
718 retcode = rbsrt._prepareabortorcontinue(abortf)
719 if retcode is not None:
719 if retcode is not None:
720 return retcode
720 return retcode
721 else:
721 else:
722 destmap = _definedestmap(ui, repo, destf, srcf, basef, revf,
722 destmap = _definedestmap(ui, repo, destf, srcf, basef, revf,
723 destspace=destspace)
723 destspace=destspace)
724 retcode = rbsrt._preparenewrebase(destmap)
724 retcode = rbsrt._preparenewrebase(destmap)
725 if retcode is not None:
725 if retcode is not None:
726 return retcode
726 return retcode
727
727
728 tr = None
728 tr = None
729 dsguard = None
729 dsguard = None
730
730
731 singletr = ui.configbool('rebase', 'singletransaction')
731 singletr = ui.configbool('rebase', 'singletransaction')
732 if singletr:
732 if singletr:
733 tr = repo.transaction('rebase')
733 tr = repo.transaction('rebase')
734 with util.acceptintervention(tr):
734 with util.acceptintervention(tr):
735 if singletr:
735 if singletr:
736 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
736 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
737 with util.acceptintervention(dsguard):
737 with util.acceptintervention(dsguard):
738 rbsrt._performrebase(tr)
738 rbsrt._performrebase(tr)
739
739
740 rbsrt._finishrebase()
740 rbsrt._finishrebase()
741
741
742 def _definedestmap(ui, repo, destf=None, srcf=None, basef=None, revf=None,
742 def _definedestmap(ui, repo, destf=None, srcf=None, basef=None, revf=None,
743 destspace=None):
743 destspace=None):
744 """use revisions argument to define destmap {srcrev: destrev}"""
744 """use revisions argument to define destmap {srcrev: destrev}"""
745 if revf is None:
745 if revf is None:
746 revf = []
746 revf = []
747
747
748 # destspace is here to work around issues with `hg pull --rebase` see
748 # destspace is here to work around issues with `hg pull --rebase` see
749 # issue5214 for details
749 # issue5214 for details
750 if srcf and basef:
750 if srcf and basef:
751 raise error.Abort(_('cannot specify both a source and a base'))
751 raise error.Abort(_('cannot specify both a source and a base'))
752 if revf and basef:
752 if revf and basef:
753 raise error.Abort(_('cannot specify both a revision and a base'))
753 raise error.Abort(_('cannot specify both a revision and a base'))
754 if revf and srcf:
754 if revf and srcf:
755 raise error.Abort(_('cannot specify both a revision and a source'))
755 raise error.Abort(_('cannot specify both a revision and a source'))
756
756
757 cmdutil.checkunfinished(repo)
757 cmdutil.checkunfinished(repo)
758 cmdutil.bailifchanged(repo)
758 cmdutil.bailifchanged(repo)
759
759
760 if ui.configbool('commands', 'rebase.requiredest') and not destf:
760 if ui.configbool('commands', 'rebase.requiredest') and not destf:
761 raise error.Abort(_('you must specify a destination'),
761 raise error.Abort(_('you must specify a destination'),
762 hint=_('use: hg rebase -d REV'))
762 hint=_('use: hg rebase -d REV'))
763
763
764 dest = None
764 dest = None
765
765
766 if revf:
766 if revf:
767 rebaseset = scmutil.revrange(repo, revf)
767 rebaseset = scmutil.revrange(repo, revf)
768 if not rebaseset:
768 if not rebaseset:
769 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
769 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
770 return None
770 return None
771 elif srcf:
771 elif srcf:
772 src = scmutil.revrange(repo, [srcf])
772 src = scmutil.revrange(repo, [srcf])
773 if not src:
773 if not src:
774 ui.status(_('empty "source" revision set - nothing to rebase\n'))
774 ui.status(_('empty "source" revision set - nothing to rebase\n'))
775 return None
775 return None
776 rebaseset = repo.revs('(%ld)::', src)
776 rebaseset = repo.revs('(%ld)::', src)
777 assert rebaseset
777 assert rebaseset
778 else:
778 else:
779 base = scmutil.revrange(repo, [basef or '.'])
779 base = scmutil.revrange(repo, [basef or '.'])
780 if not base:
780 if not base:
781 ui.status(_('empty "base" revision set - '
781 ui.status(_('empty "base" revision set - '
782 "can't compute rebase set\n"))
782 "can't compute rebase set\n"))
783 return None
783 return None
784 if destf:
784 if destf:
785 # --base does not support multiple destinations
785 # --base does not support multiple destinations
786 dest = scmutil.revsingle(repo, destf)
786 dest = scmutil.revsingle(repo, destf)
787 else:
787 else:
788 dest = repo[_destrebase(repo, base, destspace=destspace)]
788 dest = repo[_destrebase(repo, base, destspace=destspace)]
789 destf = str(dest)
789 destf = str(dest)
790
790
791 roots = [] # selected children of branching points
791 roots = [] # selected children of branching points
792 bpbase = {} # {branchingpoint: [origbase]}
792 bpbase = {} # {branchingpoint: [origbase]}
793 for b in base: # group bases by branching points
793 for b in base: # group bases by branching points
794 bp = repo.revs('ancestor(%d, %d)', b, dest).first()
794 bp = repo.revs('ancestor(%d, %d)', b, dest).first()
795 bpbase[bp] = bpbase.get(bp, []) + [b]
795 bpbase[bp] = bpbase.get(bp, []) + [b]
796 if None in bpbase:
796 if None in bpbase:
797 # emulate the old behavior, showing "nothing to rebase" (a better
797 # emulate the old behavior, showing "nothing to rebase" (a better
798 # behavior may be abort with "cannot find branching point" error)
798 # behavior may be abort with "cannot find branching point" error)
799 bpbase.clear()
799 bpbase.clear()
800 for bp, bs in bpbase.iteritems(): # calculate roots
800 for bp, bs in bpbase.iteritems(): # calculate roots
801 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
801 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
802
802
803 rebaseset = repo.revs('%ld::', roots)
803 rebaseset = repo.revs('%ld::', roots)
804
804
805 if not rebaseset:
805 if not rebaseset:
806 # transform to list because smartsets are not comparable to
806 # transform to list because smartsets are not comparable to
807 # lists. This should be improved to honor laziness of
807 # lists. This should be improved to honor laziness of
808 # smartset.
808 # smartset.
809 if list(base) == [dest.rev()]:
809 if list(base) == [dest.rev()]:
810 if basef:
810 if basef:
811 ui.status(_('nothing to rebase - %s is both "base"'
811 ui.status(_('nothing to rebase - %s is both "base"'
812 ' and destination\n') % dest)
812 ' and destination\n') % dest)
813 else:
813 else:
814 ui.status(_('nothing to rebase - working directory '
814 ui.status(_('nothing to rebase - working directory '
815 'parent is also destination\n'))
815 'parent is also destination\n'))
816 elif not repo.revs('%ld - ::%d', base, dest):
816 elif not repo.revs('%ld - ::%d', base, dest):
817 if basef:
817 if basef:
818 ui.status(_('nothing to rebase - "base" %s is '
818 ui.status(_('nothing to rebase - "base" %s is '
819 'already an ancestor of destination '
819 'already an ancestor of destination '
820 '%s\n') %
820 '%s\n') %
821 ('+'.join(str(repo[r]) for r in base),
821 ('+'.join(str(repo[r]) for r in base),
822 dest))
822 dest))
823 else:
823 else:
824 ui.status(_('nothing to rebase - working '
824 ui.status(_('nothing to rebase - working '
825 'directory parent is already an '
825 'directory parent is already an '
826 'ancestor of destination %s\n') % dest)
826 'ancestor of destination %s\n') % dest)
827 else: # can it happen?
827 else: # can it happen?
828 ui.status(_('nothing to rebase from %s to %s\n') %
828 ui.status(_('nothing to rebase from %s to %s\n') %
829 ('+'.join(str(repo[r]) for r in base), dest))
829 ('+'.join(str(repo[r]) for r in base), dest))
830 return None
830 return None
831
831
832 if not destf:
832 if not destf:
833 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
833 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
834 destf = str(dest)
834 destf = str(dest)
835
835
836 allsrc = revsetlang.formatspec('%ld', rebaseset)
836 allsrc = revsetlang.formatspec('%ld', rebaseset)
837 alias = {'ALLSRC': allsrc}
837 alias = {'ALLSRC': allsrc}
838
838
839 if dest is None:
839 if dest is None:
840 try:
840 try:
841 # fast path: try to resolve dest without SRC alias
841 # fast path: try to resolve dest without SRC alias
842 dest = scmutil.revsingle(repo, destf, localalias=alias)
842 dest = scmutil.revsingle(repo, destf, localalias=alias)
843 except error.RepoLookupError:
843 except error.RepoLookupError:
844 if not ui.configbool('experimental', 'rebase.multidest'):
844 if not ui.configbool('experimental', 'rebase.multidest'):
845 raise
845 raise
846 # multi-dest path: resolve dest for each SRC separately
846 # multi-dest path: resolve dest for each SRC separately
847 destmap = {}
847 destmap = {}
848 for r in rebaseset:
848 for r in rebaseset:
849 alias['SRC'] = revsetlang.formatspec('%d', r)
849 alias['SRC'] = revsetlang.formatspec('%d', r)
850 # use repo.anyrevs instead of scmutil.revsingle because we
850 # use repo.anyrevs instead of scmutil.revsingle because we
851 # don't want to abort if destset is empty.
851 # don't want to abort if destset is empty.
852 destset = repo.anyrevs([destf], user=True, localalias=alias)
852 destset = repo.anyrevs([destf], user=True, localalias=alias)
853 size = len(destset)
853 size = len(destset)
854 if size == 1:
854 if size == 1:
855 destmap[r] = destset.first()
855 destmap[r] = destset.first()
856 elif size == 0:
856 elif size == 0:
857 ui.note(_('skipping %s - empty destination\n') % repo[r])
857 ui.note(_('skipping %s - empty destination\n') % repo[r])
858 else:
858 else:
859 raise error.Abort(_('rebase destination for %s is not '
859 raise error.Abort(_('rebase destination for %s is not '
860 'unique') % repo[r])
860 'unique') % repo[r])
861
861
862 if dest is not None:
862 if dest is not None:
863 # single-dest case: assign dest to each rev in rebaseset
863 # single-dest case: assign dest to each rev in rebaseset
864 destrev = dest.rev()
864 destrev = dest.rev()
865 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
865 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
866
866
867 if not destmap:
867 if not destmap:
868 ui.status(_('nothing to rebase - empty destination\n'))
868 ui.status(_('nothing to rebase - empty destination\n'))
869 return None
869 return None
870
870
871 return destmap
871 return destmap
872
872
873 def externalparent(repo, state, destancestors):
873 def externalparent(repo, state, destancestors):
874 """Return the revision that should be used as the second parent
874 """Return the revision that should be used as the second parent
875 when the revisions in state is collapsed on top of destancestors.
875 when the revisions in state is collapsed on top of destancestors.
876 Abort if there is more than one parent.
876 Abort if there is more than one parent.
877 """
877 """
878 parents = set()
878 parents = set()
879 source = min(state)
879 source = min(state)
880 for rev in state:
880 for rev in state:
881 if rev == source:
881 if rev == source:
882 continue
882 continue
883 for p in repo[rev].parents():
883 for p in repo[rev].parents():
884 if (p.rev() not in state
884 if (p.rev() not in state
885 and p.rev() not in destancestors):
885 and p.rev() not in destancestors):
886 parents.add(p.rev())
886 parents.add(p.rev())
887 if not parents:
887 if not parents:
888 return nullrev
888 return nullrev
889 if len(parents) == 1:
889 if len(parents) == 1:
890 return parents.pop()
890 return parents.pop()
891 raise error.Abort(_('unable to collapse on top of %s, there is more '
891 raise error.Abort(_('unable to collapse on top of %s, there is more '
892 'than one external parent: %s') %
892 'than one external parent: %s') %
893 (max(destancestors),
893 (max(destancestors),
894 ', '.join(str(p) for p in sorted(parents))))
894 ', '.join(str(p) for p in sorted(parents))))
895
895
896 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
896 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
897 keepbranches=False, date=None):
897 keepbranches=False, date=None):
898 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
898 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
899 but also store useful information in extra.
899 but also store useful information in extra.
900 Return node of committed revision.'''
900 Return node of committed revision.'''
901 dsguard = util.nullcontextmanager()
901 dsguard = util.nullcontextmanager()
902 if not repo.ui.configbool('rebase', 'singletransaction'):
902 if not repo.ui.configbool('rebase', 'singletransaction'):
903 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
903 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
904 with dsguard:
904 with dsguard:
905 repo.setparents(repo[p1].node(), repo[p2].node())
905 repo.setparents(repo[p1].node(), repo[p2].node())
906 ctx = repo[rev]
906 ctx = repo[rev]
907 if commitmsg is None:
907 if commitmsg is None:
908 commitmsg = ctx.description()
908 commitmsg = ctx.description()
909 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
909 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
910 extra = {'rebase_source': ctx.hex()}
910 extra = {'rebase_source': ctx.hex()}
911 if extrafn:
911 if extrafn:
912 extrafn(ctx, extra)
912 extrafn(ctx, extra)
913
913
914 destphase = max(ctx.phase(), phases.draft)
914 destphase = max(ctx.phase(), phases.draft)
915 overrides = {('phases', 'new-commit'): destphase}
915 overrides = {('phases', 'new-commit'): destphase}
916 with repo.ui.configoverride(overrides, 'rebase'):
916 with repo.ui.configoverride(overrides, 'rebase'):
917 if keepbranch:
917 if keepbranch:
918 repo.ui.setconfig('ui', 'allowemptycommit', True)
918 repo.ui.setconfig('ui', 'allowemptycommit', True)
919 # Commit might fail if unresolved files exist
919 # Commit might fail if unresolved files exist
920 if date is None:
920 if date is None:
921 date = ctx.date()
921 date = ctx.date()
922 newnode = repo.commit(text=commitmsg, user=ctx.user(),
922 newnode = repo.commit(text=commitmsg, user=ctx.user(),
923 date=date, extra=extra, editor=editor)
923 date=date, extra=extra, editor=editor)
924
924
925 repo.dirstate.setbranch(repo[newnode].branch())
925 repo.dirstate.setbranch(repo[newnode].branch())
926 return newnode
926 return newnode
927
927
928 def rebasenode(repo, rev, p1, base, state, collapse, dest):
928 def rebasenode(repo, rev, p1, base, state, collapse, dest):
929 'Rebase a single revision rev on top of p1 using base as merge ancestor'
929 'Rebase a single revision rev on top of p1 using base as merge ancestor'
930 # Merge phase
930 # Merge phase
931 # Update to destination and merge it with local
931 # Update to destination and merge it with local
932 if repo['.'].rev() != p1:
932 if repo['.'].rev() != p1:
933 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
933 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
934 mergemod.update(repo, p1, False, True)
934 mergemod.update(repo, p1, False, True)
935 else:
935 else:
936 repo.ui.debug(" already in destination\n")
936 repo.ui.debug(" already in destination\n")
937 repo.dirstate.write(repo.currenttransaction())
937 repo.dirstate.write(repo.currenttransaction())
938 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
938 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
939 if base is not None:
939 if base is not None:
940 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
940 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
941 # When collapsing in-place, the parent is the common ancestor, we
941 # When collapsing in-place, the parent is the common ancestor, we
942 # have to allow merging with it.
942 # have to allow merging with it.
943 stats = mergemod.update(repo, rev, True, True, base, collapse,
943 stats = mergemod.update(repo, rev, True, True, base, collapse,
944 labels=['dest', 'source'])
944 labels=['dest', 'source'])
945 if collapse:
945 if collapse:
946 copies.duplicatecopies(repo, rev, dest)
946 copies.duplicatecopies(repo, rev, dest)
947 else:
947 else:
948 # If we're not using --collapse, we need to
948 # If we're not using --collapse, we need to
949 # duplicate copies between the revision we're
949 # duplicate copies between the revision we're
950 # rebasing and its first parent, but *not*
950 # rebasing and its first parent, but *not*
951 # duplicate any copies that have already been
951 # duplicate any copies that have already been
952 # performed in the destination.
952 # performed in the destination.
953 p1rev = repo[rev].p1().rev()
953 p1rev = repo[rev].p1().rev()
954 copies.duplicatecopies(repo, rev, p1rev, skiprev=dest)
954 copies.duplicatecopies(repo, rev, p1rev, skiprev=dest)
955 return stats
955 return stats
956
956
957 def adjustdest(repo, rev, destmap, state, skipped):
957 def adjustdest(repo, rev, destmap, state, skipped):
958 """adjust rebase destination given the current rebase state
958 """adjust rebase destination given the current rebase state
959
959
960 rev is what is being rebased. Return a list of two revs, which are the
960 rev is what is being rebased. Return a list of two revs, which are the
961 adjusted destinations for rev's p1 and p2, respectively. If a parent is
961 adjusted destinations for rev's p1 and p2, respectively. If a parent is
962 nullrev, return dest without adjustment for it.
962 nullrev, return dest without adjustment for it.
963
963
964 For example, when doing rebasing B+E to F, C to G, rebase will first move B
964 For example, when doing rebasing B+E to F, C to G, rebase will first move B
965 to B1, and E's destination will be adjusted from F to B1.
965 to B1, and E's destination will be adjusted from F to B1.
966
966
967 B1 <- written during rebasing B
967 B1 <- written during rebasing B
968 |
968 |
969 F <- original destination of B, E
969 F <- original destination of B, E
970 |
970 |
971 | E <- rev, which is being rebased
971 | E <- rev, which is being rebased
972 | |
972 | |
973 | D <- prev, one parent of rev being checked
973 | D <- prev, one parent of rev being checked
974 | |
974 | |
975 | x <- skipped, ex. no successor or successor in (::dest)
975 | x <- skipped, ex. no successor or successor in (::dest)
976 | |
976 | |
977 | C <- rebased as C', different destination
977 | C <- rebased as C', different destination
978 | |
978 | |
979 | B <- rebased as B1 C'
979 | B <- rebased as B1 C'
980 |/ |
980 |/ |
981 A G <- destination of C, different
981 A G <- destination of C, different
982
982
983 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
983 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
984 first move C to C1, G to G1, and when it's checking H, the adjusted
984 first move C to C1, G to G1, and when it's checking H, the adjusted
985 destinations will be [C1, G1].
985 destinations will be [C1, G1].
986
986
987 H C1 G1
987 H C1 G1
988 /| | /
988 /| | /
989 F G |/
989 F G |/
990 K | | -> K
990 K | | -> K
991 | C D |
991 | C D |
992 | |/ |
992 | |/ |
993 | B | ...
993 | B | ...
994 |/ |/
994 |/ |/
995 A A
995 A A
996
996
997 Besides, adjust dest according to existing rebase information. For example,
997 Besides, adjust dest according to existing rebase information. For example,
998
998
999 B C D B needs to be rebased on top of C, C needs to be rebased on top
999 B C D B needs to be rebased on top of C, C needs to be rebased on top
1000 \|/ of D. We will rebase C first.
1000 \|/ of D. We will rebase C first.
1001 A
1001 A
1002
1002
1003 C' After rebasing C, when considering B's destination, use C'
1003 C' After rebasing C, when considering B's destination, use C'
1004 | instead of the original C.
1004 | instead of the original C.
1005 B D
1005 B D
1006 \ /
1006 \ /
1007 A
1007 A
1008 """
1008 """
1009 # pick already rebased revs with same dest from state as interesting source
1009 # pick already rebased revs with same dest from state as interesting source
1010 dest = destmap[rev]
1010 dest = destmap[rev]
1011 source = [s for s, d in state.items()
1011 source = [s for s, d in state.items()
1012 if d > 0 and destmap[s] == dest and s not in skipped]
1012 if d > 0 and destmap[s] == dest and s not in skipped]
1013
1013
1014 result = []
1014 result = []
1015 for prev in repo.changelog.parentrevs(rev):
1015 for prev in repo.changelog.parentrevs(rev):
1016 adjusted = dest
1016 adjusted = dest
1017 if prev != nullrev:
1017 if prev != nullrev:
1018 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1018 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1019 if candidate is not None:
1019 if candidate is not None:
1020 adjusted = state[candidate]
1020 adjusted = state[candidate]
1021 if adjusted == dest and dest in state:
1021 if adjusted == dest and dest in state:
1022 adjusted = state[dest]
1022 adjusted = state[dest]
1023 if adjusted == revtodo:
1023 if adjusted == revtodo:
1024 # sortsource should produce an order that makes this impossible
1024 # sortsource should produce an order that makes this impossible
1025 raise error.ProgrammingError(
1025 raise error.ProgrammingError(
1026 'rev %d should be rebased already at this time' % dest)
1026 'rev %d should be rebased already at this time' % dest)
1027 result.append(adjusted)
1027 result.append(adjusted)
1028 return result
1028 return result
1029
1029
1030 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1030 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1031 """
1031 """
1032 Abort if rebase will create divergence or rebase is noop because of markers
1032 Abort if rebase will create divergence or rebase is noop because of markers
1033
1033
1034 `rebaseobsrevs`: set of obsolete revision in source
1034 `rebaseobsrevs`: set of obsolete revision in source
1035 `rebaseobsskipped`: set of revisions from source skipped because they have
1035 `rebaseobsskipped`: set of revisions from source skipped because they have
1036 successors in destination
1036 successors in destination
1037 """
1037 """
1038 # Obsolete node with successors not in dest leads to divergence
1038 # Obsolete node with successors not in dest leads to divergence
1039 divergenceok = ui.configbool('experimental',
1039 divergenceok = ui.configbool('experimental',
1040 'allowdivergence')
1040 'allowdivergence')
1041 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1041 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1042
1042
1043 if divergencebasecandidates and not divergenceok:
1043 if divergencebasecandidates and not divergenceok:
1044 divhashes = (str(repo[r])
1044 divhashes = (str(repo[r])
1045 for r in divergencebasecandidates)
1045 for r in divergencebasecandidates)
1046 msg = _("this rebase will cause "
1046 msg = _("this rebase will cause "
1047 "divergences from: %s")
1047 "divergences from: %s")
1048 h = _("to force the rebase please set "
1048 h = _("to force the rebase please set "
1049 "experimental.allowdivergence=True")
1049 "experimental.allowdivergence=True")
1050 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1050 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1051
1051
1052 def successorrevs(repo, rev):
1052 def successorrevs(repo, rev):
1053 """yield revision numbers for successors of rev"""
1053 """yield revision numbers for successors of rev"""
1054 unfi = repo.unfiltered()
1054 unfi = repo.unfiltered()
1055 nodemap = unfi.changelog.nodemap
1055 nodemap = unfi.changelog.nodemap
1056 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1056 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1057 if s in nodemap:
1057 if s in nodemap:
1058 yield nodemap[s]
1058 yield nodemap[s]
1059
1059
1060 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1060 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1061 """Return new parents and optionally a merge base for rev being rebased
1061 """Return new parents and optionally a merge base for rev being rebased
1062
1062
1063 The destination specified by "dest" cannot always be used directly because
1063 The destination specified by "dest" cannot always be used directly because
1064 previously rebase result could affect destination. For example,
1064 previously rebase result could affect destination. For example,
1065
1065
1066 D E rebase -r C+D+E -d B
1066 D E rebase -r C+D+E -d B
1067 |/ C will be rebased to C'
1067 |/ C will be rebased to C'
1068 B C D's new destination will be C' instead of B
1068 B C D's new destination will be C' instead of B
1069 |/ E's new destination will be C' instead of B
1069 |/ E's new destination will be C' instead of B
1070 A
1070 A
1071
1071
1072 The new parents of a merge is slightly more complicated. See the comment
1072 The new parents of a merge is slightly more complicated. See the comment
1073 block below.
1073 block below.
1074 """
1074 """
1075 cl = repo.changelog
1075 # use unfiltered changelog since successorrevs may return filtered nodes
1076 cl = repo.unfiltered().changelog
1076 def isancestor(a, b):
1077 def isancestor(a, b):
1077 # take revision numbers instead of nodes
1078 # take revision numbers instead of nodes
1078 if a == b:
1079 if a == b:
1079 return True
1080 return True
1080 elif a > b:
1081 elif a > b:
1081 return False
1082 return False
1082 return cl.isancestor(cl.node(a), cl.node(b))
1083 return cl.isancestor(cl.node(a), cl.node(b))
1083
1084
1084 dest = destmap[rev]
1085 dest = destmap[rev]
1085 oldps = repo.changelog.parentrevs(rev) # old parents
1086 oldps = repo.changelog.parentrevs(rev) # old parents
1086 newps = [nullrev, nullrev] # new parents
1087 newps = [nullrev, nullrev] # new parents
1087 dests = adjustdest(repo, rev, destmap, state, skipped)
1088 dests = adjustdest(repo, rev, destmap, state, skipped)
1088 bases = list(oldps) # merge base candidates, initially just old parents
1089 bases = list(oldps) # merge base candidates, initially just old parents
1089
1090
1090 if all(r == nullrev for r in oldps[1:]):
1091 if all(r == nullrev for r in oldps[1:]):
1091 # For non-merge changeset, just move p to adjusted dest as requested.
1092 # For non-merge changeset, just move p to adjusted dest as requested.
1092 newps[0] = dests[0]
1093 newps[0] = dests[0]
1093 else:
1094 else:
1094 # For merge changeset, if we move p to dests[i] unconditionally, both
1095 # For merge changeset, if we move p to dests[i] unconditionally, both
1095 # parents may change and the end result looks like "the merge loses a
1096 # parents may change and the end result looks like "the merge loses a
1096 # parent", which is a surprise. This is a limit because "--dest" only
1097 # parent", which is a surprise. This is a limit because "--dest" only
1097 # accepts one dest per src.
1098 # accepts one dest per src.
1098 #
1099 #
1099 # Therefore, only move p with reasonable conditions (in this order):
1100 # Therefore, only move p with reasonable conditions (in this order):
1100 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1101 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1101 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1102 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1102 #
1103 #
1103 # Comparing with adjustdest, the logic here does some additional work:
1104 # Comparing with adjustdest, the logic here does some additional work:
1104 # 1. decide which parents will not be moved towards dest
1105 # 1. decide which parents will not be moved towards dest
1105 # 2. if the above decision is "no", should a parent still be moved
1106 # 2. if the above decision is "no", should a parent still be moved
1106 # because it was rebased?
1107 # because it was rebased?
1107 #
1108 #
1108 # For example:
1109 # For example:
1109 #
1110 #
1110 # C # "rebase -r C -d D" is an error since none of the parents
1111 # C # "rebase -r C -d D" is an error since none of the parents
1111 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1112 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1112 # A B D # B (using rule "2."), since B will be rebased.
1113 # A B D # B (using rule "2."), since B will be rebased.
1113 #
1114 #
1114 # The loop tries to be not rely on the fact that a Mercurial node has
1115 # The loop tries to be not rely on the fact that a Mercurial node has
1115 # at most 2 parents.
1116 # at most 2 parents.
1116 for i, p in enumerate(oldps):
1117 for i, p in enumerate(oldps):
1117 np = p # new parent
1118 np = p # new parent
1118 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1119 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1119 np = dests[i]
1120 np = dests[i]
1120 elif p in state and state[p] > 0:
1121 elif p in state and state[p] > 0:
1121 np = state[p]
1122 np = state[p]
1122
1123
1123 # "bases" only record "special" merge bases that cannot be
1124 # "bases" only record "special" merge bases that cannot be
1124 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1125 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1125 # For example:
1126 # For example:
1126 #
1127 #
1127 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1128 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1128 # | C # is B', but merge base for C is B, instead of
1129 # | C # is B', but merge base for C is B, instead of
1129 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1130 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1130 # | B # "state" edges are merged (so there will be an edge from
1131 # | B # "state" edges are merged (so there will be an edge from
1131 # |/ # B to B'), the merge base is still ancestor(C, B') in
1132 # |/ # B to B'), the merge base is still ancestor(C, B') in
1132 # A # the merged graph.
1133 # A # the merged graph.
1133 #
1134 #
1134 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1135 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1135 # which uses "virtual null merge" to explain this situation.
1136 # which uses "virtual null merge" to explain this situation.
1136 if isancestor(p, np):
1137 if isancestor(p, np):
1137 bases[i] = nullrev
1138 bases[i] = nullrev
1138
1139
1139 # If one parent becomes an ancestor of the other, drop the ancestor
1140 # If one parent becomes an ancestor of the other, drop the ancestor
1140 for j, x in enumerate(newps[:i]):
1141 for j, x in enumerate(newps[:i]):
1141 if x == nullrev:
1142 if x == nullrev:
1142 continue
1143 continue
1143 if isancestor(np, x): # CASE-1
1144 if isancestor(np, x): # CASE-1
1144 np = nullrev
1145 np = nullrev
1145 elif isancestor(x, np): # CASE-2
1146 elif isancestor(x, np): # CASE-2
1146 newps[j] = np
1147 newps[j] = np
1147 np = nullrev
1148 np = nullrev
1148 # New parents forming an ancestor relationship does not
1149 # New parents forming an ancestor relationship does not
1149 # mean the old parents have a similar relationship. Do not
1150 # mean the old parents have a similar relationship. Do not
1150 # set bases[x] to nullrev.
1151 # set bases[x] to nullrev.
1151 bases[j], bases[i] = bases[i], bases[j]
1152 bases[j], bases[i] = bases[i], bases[j]
1152
1153
1153 newps[i] = np
1154 newps[i] = np
1154
1155
1155 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1156 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1156 # base. If only p2 changes, merging using unchanged p1 as merge base is
1157 # base. If only p2 changes, merging using unchanged p1 as merge base is
1157 # suboptimal. Therefore swap parents to make the merge sane.
1158 # suboptimal. Therefore swap parents to make the merge sane.
1158 if newps[1] != nullrev and oldps[0] == newps[0]:
1159 if newps[1] != nullrev and oldps[0] == newps[0]:
1159 assert len(newps) == 2 and len(oldps) == 2
1160 assert len(newps) == 2 and len(oldps) == 2
1160 newps.reverse()
1161 newps.reverse()
1161 bases.reverse()
1162 bases.reverse()
1162
1163
1163 # No parent change might be an error because we fail to make rev a
1164 # No parent change might be an error because we fail to make rev a
1164 # descendent of requested dest. This can happen, for example:
1165 # descendent of requested dest. This can happen, for example:
1165 #
1166 #
1166 # C # rebase -r C -d D
1167 # C # rebase -r C -d D
1167 # /| # None of A and B will be changed to D and rebase fails.
1168 # /| # None of A and B will be changed to D and rebase fails.
1168 # A B D
1169 # A B D
1169 if set(newps) == set(oldps) and dest not in newps:
1170 if set(newps) == set(oldps) and dest not in newps:
1170 raise error.Abort(_('cannot rebase %d:%s without '
1171 raise error.Abort(_('cannot rebase %d:%s without '
1171 'moving at least one of its parents')
1172 'moving at least one of its parents')
1172 % (rev, repo[rev]))
1173 % (rev, repo[rev]))
1173
1174
1174 # Source should not be ancestor of dest. The check here guarantees it's
1175 # Source should not be ancestor of dest. The check here guarantees it's
1175 # impossible. With multi-dest, the initial check does not cover complex
1176 # impossible. With multi-dest, the initial check does not cover complex
1176 # cases since we don't have abstractions to dry-run rebase cheaply.
1177 # cases since we don't have abstractions to dry-run rebase cheaply.
1177 if any(p != nullrev and isancestor(rev, p) for p in newps):
1178 if any(p != nullrev and isancestor(rev, p) for p in newps):
1178 raise error.Abort(_('source is ancestor of destination'))
1179 raise error.Abort(_('source is ancestor of destination'))
1179
1180
1180 # "rebasenode" updates to new p1, use the corresponding merge base.
1181 # "rebasenode" updates to new p1, use the corresponding merge base.
1181 if bases[0] != nullrev:
1182 if bases[0] != nullrev:
1182 base = bases[0]
1183 base = bases[0]
1183 else:
1184 else:
1184 base = None
1185 base = None
1185
1186
1186 # Check if the merge will contain unwanted changes. That may happen if
1187 # Check if the merge will contain unwanted changes. That may happen if
1187 # there are multiple special (non-changelog ancestor) merge bases, which
1188 # there are multiple special (non-changelog ancestor) merge bases, which
1188 # cannot be handled well by the 3-way merge algorithm. For example:
1189 # cannot be handled well by the 3-way merge algorithm. For example:
1189 #
1190 #
1190 # F
1191 # F
1191 # /|
1192 # /|
1192 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1193 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1193 # | | # as merge base, the difference between D and F will include
1194 # | | # as merge base, the difference between D and F will include
1194 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1195 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1195 # |/ # chosen, the rebased F will contain B.
1196 # |/ # chosen, the rebased F will contain B.
1196 # A Z
1197 # A Z
1197 #
1198 #
1198 # But our merge base candidates (D and E in above case) could still be
1199 # But our merge base candidates (D and E in above case) could still be
1199 # better than the default (ancestor(F, Z) == null). Therefore still
1200 # better than the default (ancestor(F, Z) == null). Therefore still
1200 # pick one (so choose p1 above).
1201 # pick one (so choose p1 above).
1201 if sum(1 for b in bases if b != nullrev) > 1:
1202 if sum(1 for b in bases if b != nullrev) > 1:
1202 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1203 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1203 for i, base in enumerate(bases):
1204 for i, base in enumerate(bases):
1204 if base == nullrev:
1205 if base == nullrev:
1205 continue
1206 continue
1206 # Revisions in the side (not chosen as merge base) branch that
1207 # Revisions in the side (not chosen as merge base) branch that
1207 # might contain "surprising" contents
1208 # might contain "surprising" contents
1208 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1209 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1209 bases, base, base, dest))
1210 bases, base, base, dest))
1210
1211
1211 # If those revisions are covered by rebaseset, the result is good.
1212 # If those revisions are covered by rebaseset, the result is good.
1212 # A merge in rebaseset would be considered to cover its ancestors.
1213 # A merge in rebaseset would be considered to cover its ancestors.
1213 if siderevs:
1214 if siderevs:
1214 rebaseset = [r for r, d in state.items()
1215 rebaseset = [r for r, d in state.items()
1215 if d > 0 and r not in obsskipped]
1216 if d > 0 and r not in obsskipped]
1216 merges = [r for r in rebaseset
1217 merges = [r for r in rebaseset
1217 if cl.parentrevs(r)[1] != nullrev]
1218 if cl.parentrevs(r)[1] != nullrev]
1218 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1219 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1219 siderevs, merges, rebaseset))
1220 siderevs, merges, rebaseset))
1220
1221
1221 # Choose a merge base that has a minimal number of unwanted revs.
1222 # Choose a merge base that has a minimal number of unwanted revs.
1222 l, i = min((len(revs), i)
1223 l, i = min((len(revs), i)
1223 for i, revs in enumerate(unwanted) if revs is not None)
1224 for i, revs in enumerate(unwanted) if revs is not None)
1224 base = bases[i]
1225 base = bases[i]
1225
1226
1226 # newps[0] should match merge base if possible. Currently, if newps[i]
1227 # newps[0] should match merge base if possible. Currently, if newps[i]
1227 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1228 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1228 # the other's ancestor. In that case, it's fine to not swap newps here.
1229 # the other's ancestor. In that case, it's fine to not swap newps here.
1229 # (see CASE-1 and CASE-2 above)
1230 # (see CASE-1 and CASE-2 above)
1230 if i != 0 and newps[i] != nullrev:
1231 if i != 0 and newps[i] != nullrev:
1231 newps[0], newps[i] = newps[i], newps[0]
1232 newps[0], newps[i] = newps[i], newps[0]
1232
1233
1233 # The merge will include unwanted revisions. Abort now. Revisit this if
1234 # The merge will include unwanted revisions. Abort now. Revisit this if
1234 # we have a more advanced merge algorithm that handles multiple bases.
1235 # we have a more advanced merge algorithm that handles multiple bases.
1235 if l > 0:
1236 if l > 0:
1236 unwanteddesc = _(' or ').join(
1237 unwanteddesc = _(' or ').join(
1237 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1238 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1238 for revs in unwanted if revs is not None))
1239 for revs in unwanted if revs is not None))
1239 raise error.Abort(
1240 raise error.Abort(
1240 _('rebasing %d:%s will include unwanted changes from %s')
1241 _('rebasing %d:%s will include unwanted changes from %s')
1241 % (rev, repo[rev], unwanteddesc))
1242 % (rev, repo[rev], unwanteddesc))
1242
1243
1243 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1244 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1244
1245
1245 return newps[0], newps[1], base
1246 return newps[0], newps[1], base
1246
1247
1247 def isagitpatch(repo, patchname):
1248 def isagitpatch(repo, patchname):
1248 'Return true if the given patch is in git format'
1249 'Return true if the given patch is in git format'
1249 mqpatch = os.path.join(repo.mq.path, patchname)
1250 mqpatch = os.path.join(repo.mq.path, patchname)
1250 for line in patch.linereader(file(mqpatch, 'rb')):
1251 for line in patch.linereader(file(mqpatch, 'rb')):
1251 if line.startswith('diff --git'):
1252 if line.startswith('diff --git'):
1252 return True
1253 return True
1253 return False
1254 return False
1254
1255
1255 def updatemq(repo, state, skipped, **opts):
1256 def updatemq(repo, state, skipped, **opts):
1256 'Update rebased mq patches - finalize and then import them'
1257 'Update rebased mq patches - finalize and then import them'
1257 mqrebase = {}
1258 mqrebase = {}
1258 mq = repo.mq
1259 mq = repo.mq
1259 original_series = mq.fullseries[:]
1260 original_series = mq.fullseries[:]
1260 skippedpatches = set()
1261 skippedpatches = set()
1261
1262
1262 for p in mq.applied:
1263 for p in mq.applied:
1263 rev = repo[p.node].rev()
1264 rev = repo[p.node].rev()
1264 if rev in state:
1265 if rev in state:
1265 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1266 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1266 (rev, p.name))
1267 (rev, p.name))
1267 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1268 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1268 else:
1269 else:
1269 # Applied but not rebased, not sure this should happen
1270 # Applied but not rebased, not sure this should happen
1270 skippedpatches.add(p.name)
1271 skippedpatches.add(p.name)
1271
1272
1272 if mqrebase:
1273 if mqrebase:
1273 mq.finish(repo, mqrebase.keys())
1274 mq.finish(repo, mqrebase.keys())
1274
1275
1275 # We must start import from the newest revision
1276 # We must start import from the newest revision
1276 for rev in sorted(mqrebase, reverse=True):
1277 for rev in sorted(mqrebase, reverse=True):
1277 if rev not in skipped:
1278 if rev not in skipped:
1278 name, isgit = mqrebase[rev]
1279 name, isgit = mqrebase[rev]
1279 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1280 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1280 (name, state[rev], repo[state[rev]]))
1281 (name, state[rev], repo[state[rev]]))
1281 mq.qimport(repo, (), patchname=name, git=isgit,
1282 mq.qimport(repo, (), patchname=name, git=isgit,
1282 rev=[str(state[rev])])
1283 rev=[str(state[rev])])
1283 else:
1284 else:
1284 # Rebased and skipped
1285 # Rebased and skipped
1285 skippedpatches.add(mqrebase[rev][0])
1286 skippedpatches.add(mqrebase[rev][0])
1286
1287
1287 # Patches were either applied and rebased and imported in
1288 # Patches were either applied and rebased and imported in
1288 # order, applied and removed or unapplied. Discard the removed
1289 # order, applied and removed or unapplied. Discard the removed
1289 # ones while preserving the original series order and guards.
1290 # ones while preserving the original series order and guards.
1290 newseries = [s for s in original_series
1291 newseries = [s for s in original_series
1291 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1292 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1292 mq.fullseries[:] = newseries
1293 mq.fullseries[:] = newseries
1293 mq.seriesdirty = True
1294 mq.seriesdirty = True
1294 mq.savedirty()
1295 mq.savedirty()
1295
1296
1296 def storecollapsemsg(repo, collapsemsg):
1297 def storecollapsemsg(repo, collapsemsg):
1297 'Store the collapse message to allow recovery'
1298 'Store the collapse message to allow recovery'
1298 collapsemsg = collapsemsg or ''
1299 collapsemsg = collapsemsg or ''
1299 f = repo.vfs("last-message.txt", "w")
1300 f = repo.vfs("last-message.txt", "w")
1300 f.write("%s\n" % collapsemsg)
1301 f.write("%s\n" % collapsemsg)
1301 f.close()
1302 f.close()
1302
1303
1303 def clearcollapsemsg(repo):
1304 def clearcollapsemsg(repo):
1304 'Remove collapse message file'
1305 'Remove collapse message file'
1305 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1306 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1306
1307
1307 def restorecollapsemsg(repo, isabort):
1308 def restorecollapsemsg(repo, isabort):
1308 'Restore previously stored collapse message'
1309 'Restore previously stored collapse message'
1309 try:
1310 try:
1310 f = repo.vfs("last-message.txt")
1311 f = repo.vfs("last-message.txt")
1311 collapsemsg = f.readline().strip()
1312 collapsemsg = f.readline().strip()
1312 f.close()
1313 f.close()
1313 except IOError as err:
1314 except IOError as err:
1314 if err.errno != errno.ENOENT:
1315 if err.errno != errno.ENOENT:
1315 raise
1316 raise
1316 if isabort:
1317 if isabort:
1317 # Oh well, just abort like normal
1318 # Oh well, just abort like normal
1318 collapsemsg = ''
1319 collapsemsg = ''
1319 else:
1320 else:
1320 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1321 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1321 return collapsemsg
1322 return collapsemsg
1322
1323
1323 def clearstatus(repo):
1324 def clearstatus(repo):
1324 'Remove the status files'
1325 'Remove the status files'
1325 _clearrebasesetvisibiliy(repo)
1326 _clearrebasesetvisibiliy(repo)
1326 # Make sure the active transaction won't write the state file
1327 # Make sure the active transaction won't write the state file
1327 tr = repo.currenttransaction()
1328 tr = repo.currenttransaction()
1328 if tr:
1329 if tr:
1329 tr.removefilegenerator('rebasestate')
1330 tr.removefilegenerator('rebasestate')
1330 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1331 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1331
1332
1332 def needupdate(repo, state):
1333 def needupdate(repo, state):
1333 '''check whether we should `update --clean` away from a merge, or if
1334 '''check whether we should `update --clean` away from a merge, or if
1334 somehow the working dir got forcibly updated, e.g. by older hg'''
1335 somehow the working dir got forcibly updated, e.g. by older hg'''
1335 parents = [p.rev() for p in repo[None].parents()]
1336 parents = [p.rev() for p in repo[None].parents()]
1336
1337
1337 # Are we in a merge state at all?
1338 # Are we in a merge state at all?
1338 if len(parents) < 2:
1339 if len(parents) < 2:
1339 return False
1340 return False
1340
1341
1341 # We should be standing on the first as-of-yet unrebased commit.
1342 # We should be standing on the first as-of-yet unrebased commit.
1342 firstunrebased = min([old for old, new in state.iteritems()
1343 firstunrebased = min([old for old, new in state.iteritems()
1343 if new == nullrev])
1344 if new == nullrev])
1344 if firstunrebased in parents:
1345 if firstunrebased in parents:
1345 return True
1346 return True
1346
1347
1347 return False
1348 return False
1348
1349
1349 def abort(repo, originalwd, destmap, state, activebookmark=None):
1350 def abort(repo, originalwd, destmap, state, activebookmark=None):
1350 '''Restore the repository to its original state. Additional args:
1351 '''Restore the repository to its original state. Additional args:
1351
1352
1352 activebookmark: the name of the bookmark that should be active after the
1353 activebookmark: the name of the bookmark that should be active after the
1353 restore'''
1354 restore'''
1354
1355
1355 try:
1356 try:
1356 # If the first commits in the rebased set get skipped during the rebase,
1357 # If the first commits in the rebased set get skipped during the rebase,
1357 # their values within the state mapping will be the dest rev id. The
1358 # their values within the state mapping will be the dest rev id. The
1358 # dstates list must must not contain the dest rev (issue4896)
1359 # dstates list must must not contain the dest rev (issue4896)
1359 dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
1360 dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
1360 immutable = [d for d in dstates if not repo[d].mutable()]
1361 immutable = [d for d in dstates if not repo[d].mutable()]
1361 cleanup = True
1362 cleanup = True
1362 if immutable:
1363 if immutable:
1363 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1364 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1364 % ', '.join(str(repo[r]) for r in immutable),
1365 % ', '.join(str(repo[r]) for r in immutable),
1365 hint=_("see 'hg help phases' for details"))
1366 hint=_("see 'hg help phases' for details"))
1366 cleanup = False
1367 cleanup = False
1367
1368
1368 descendants = set()
1369 descendants = set()
1369 if dstates:
1370 if dstates:
1370 descendants = set(repo.changelog.descendants(dstates))
1371 descendants = set(repo.changelog.descendants(dstates))
1371 if descendants - set(dstates):
1372 if descendants - set(dstates):
1372 repo.ui.warn(_("warning: new changesets detected on destination "
1373 repo.ui.warn(_("warning: new changesets detected on destination "
1373 "branch, can't strip\n"))
1374 "branch, can't strip\n"))
1374 cleanup = False
1375 cleanup = False
1375
1376
1376 if cleanup:
1377 if cleanup:
1377 shouldupdate = False
1378 shouldupdate = False
1378 rebased = [s for r, s in state.items()
1379 rebased = [s for r, s in state.items()
1379 if s >= 0 and s != destmap[r]]
1380 if s >= 0 and s != destmap[r]]
1380 if rebased:
1381 if rebased:
1381 strippoints = [
1382 strippoints = [
1382 c.node() for c in repo.set('roots(%ld)', rebased)]
1383 c.node() for c in repo.set('roots(%ld)', rebased)]
1383
1384
1384 updateifonnodes = set(rebased)
1385 updateifonnodes = set(rebased)
1385 updateifonnodes.update(destmap.values())
1386 updateifonnodes.update(destmap.values())
1386 updateifonnodes.add(originalwd)
1387 updateifonnodes.add(originalwd)
1387 shouldupdate = repo['.'].rev() in updateifonnodes
1388 shouldupdate = repo['.'].rev() in updateifonnodes
1388
1389
1389 # Update away from the rebase if necessary
1390 # Update away from the rebase if necessary
1390 if shouldupdate or needupdate(repo, state):
1391 if shouldupdate or needupdate(repo, state):
1391 mergemod.update(repo, originalwd, False, True)
1392 mergemod.update(repo, originalwd, False, True)
1392
1393
1393 # Strip from the first rebased revision
1394 # Strip from the first rebased revision
1394 if rebased:
1395 if rebased:
1395 # no backup of rebased cset versions needed
1396 # no backup of rebased cset versions needed
1396 repair.strip(repo.ui, repo, strippoints)
1397 repair.strip(repo.ui, repo, strippoints)
1397
1398
1398 if activebookmark and activebookmark in repo._bookmarks:
1399 if activebookmark and activebookmark in repo._bookmarks:
1399 bookmarks.activate(repo, activebookmark)
1400 bookmarks.activate(repo, activebookmark)
1400
1401
1401 finally:
1402 finally:
1402 clearstatus(repo)
1403 clearstatus(repo)
1403 clearcollapsemsg(repo)
1404 clearcollapsemsg(repo)
1404 repo.ui.warn(_('rebase aborted\n'))
1405 repo.ui.warn(_('rebase aborted\n'))
1405 return 0
1406 return 0
1406
1407
1407 def sortsource(destmap):
1408 def sortsource(destmap):
1408 """yield source revisions in an order that we only rebase things once
1409 """yield source revisions in an order that we only rebase things once
1409
1410
1410 If source and destination overlaps, we should filter out revisions
1411 If source and destination overlaps, we should filter out revisions
1411 depending on other revisions which hasn't been rebased yet.
1412 depending on other revisions which hasn't been rebased yet.
1412
1413
1413 Yield a sorted list of revisions each time.
1414 Yield a sorted list of revisions each time.
1414
1415
1415 For example, when rebasing A to B, B to C. This function yields [B], then
1416 For example, when rebasing A to B, B to C. This function yields [B], then
1416 [A], indicating B needs to be rebased first.
1417 [A], indicating B needs to be rebased first.
1417
1418
1418 Raise if there is a cycle so the rebase is impossible.
1419 Raise if there is a cycle so the rebase is impossible.
1419 """
1420 """
1420 srcset = set(destmap)
1421 srcset = set(destmap)
1421 while srcset:
1422 while srcset:
1422 srclist = sorted(srcset)
1423 srclist = sorted(srcset)
1423 result = []
1424 result = []
1424 for r in srclist:
1425 for r in srclist:
1425 if destmap[r] not in srcset:
1426 if destmap[r] not in srcset:
1426 result.append(r)
1427 result.append(r)
1427 if not result:
1428 if not result:
1428 raise error.Abort(_('source and destination form a cycle'))
1429 raise error.Abort(_('source and destination form a cycle'))
1429 srcset -= set(result)
1430 srcset -= set(result)
1430 yield result
1431 yield result
1431
1432
1432 def buildstate(repo, destmap, collapse):
1433 def buildstate(repo, destmap, collapse):
1433 '''Define which revisions are going to be rebased and where
1434 '''Define which revisions are going to be rebased and where
1434
1435
1435 repo: repo
1436 repo: repo
1436 destmap: {srcrev: destrev}
1437 destmap: {srcrev: destrev}
1437 '''
1438 '''
1438 rebaseset = destmap.keys()
1439 rebaseset = destmap.keys()
1439 originalwd = repo['.'].rev()
1440 originalwd = repo['.'].rev()
1440 _setrebasesetvisibility(repo, set(rebaseset) | {originalwd})
1441 _setrebasesetvisibility(repo, set(rebaseset) | {originalwd})
1441
1442
1442 # This check isn't strictly necessary, since mq detects commits over an
1443 # This check isn't strictly necessary, since mq detects commits over an
1443 # applied patch. But it prevents messing up the working directory when
1444 # applied patch. But it prevents messing up the working directory when
1444 # a partially completed rebase is blocked by mq.
1445 # a partially completed rebase is blocked by mq.
1445 if 'qtip' in repo.tags():
1446 if 'qtip' in repo.tags():
1446 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1447 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1447 if set(destmap.values()) & mqapplied:
1448 if set(destmap.values()) & mqapplied:
1448 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1449 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1449
1450
1450 # Get "cycle" error early by exhausting the generator.
1451 # Get "cycle" error early by exhausting the generator.
1451 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1452 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1452 if not sortedsrc:
1453 if not sortedsrc:
1453 raise error.Abort(_('no matching revisions'))
1454 raise error.Abort(_('no matching revisions'))
1454
1455
1455 # Only check the first batch of revisions to rebase not depending on other
1456 # Only check the first batch of revisions to rebase not depending on other
1456 # rebaseset. This means "source is ancestor of destination" for the second
1457 # rebaseset. This means "source is ancestor of destination" for the second
1457 # (and following) batches of revisions are not checked here. We rely on
1458 # (and following) batches of revisions are not checked here. We rely on
1458 # "defineparents" to do that check.
1459 # "defineparents" to do that check.
1459 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1460 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1460 if not roots:
1461 if not roots:
1461 raise error.Abort(_('no matching revisions'))
1462 raise error.Abort(_('no matching revisions'))
1462 roots.sort()
1463 roots.sort()
1463 state = dict.fromkeys(rebaseset, revtodo)
1464 state = dict.fromkeys(rebaseset, revtodo)
1464 emptyrebase = (len(sortedsrc) == 1)
1465 emptyrebase = (len(sortedsrc) == 1)
1465 for root in roots:
1466 for root in roots:
1466 dest = repo[destmap[root.rev()]]
1467 dest = repo[destmap[root.rev()]]
1467 commonbase = root.ancestor(dest)
1468 commonbase = root.ancestor(dest)
1468 if commonbase == root:
1469 if commonbase == root:
1469 raise error.Abort(_('source is ancestor of destination'))
1470 raise error.Abort(_('source is ancestor of destination'))
1470 if commonbase == dest:
1471 if commonbase == dest:
1471 wctx = repo[None]
1472 wctx = repo[None]
1472 if dest == wctx.p1():
1473 if dest == wctx.p1():
1473 # when rebasing to '.', it will use the current wd branch name
1474 # when rebasing to '.', it will use the current wd branch name
1474 samebranch = root.branch() == wctx.branch()
1475 samebranch = root.branch() == wctx.branch()
1475 else:
1476 else:
1476 samebranch = root.branch() == dest.branch()
1477 samebranch = root.branch() == dest.branch()
1477 if not collapse and samebranch and dest in root.parents():
1478 if not collapse and samebranch and dest in root.parents():
1478 # mark the revision as done by setting its new revision
1479 # mark the revision as done by setting its new revision
1479 # equal to its old (current) revisions
1480 # equal to its old (current) revisions
1480 state[root.rev()] = root.rev()
1481 state[root.rev()] = root.rev()
1481 repo.ui.debug('source is a child of destination\n')
1482 repo.ui.debug('source is a child of destination\n')
1482 continue
1483 continue
1483
1484
1484 emptyrebase = False
1485 emptyrebase = False
1485 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1486 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1486 if emptyrebase:
1487 if emptyrebase:
1487 return None
1488 return None
1488 for rev in sorted(state):
1489 for rev in sorted(state):
1489 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1490 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1490 # if all parents of this revision are done, then so is this revision
1491 # if all parents of this revision are done, then so is this revision
1491 if parents and all((state.get(p) == p for p in parents)):
1492 if parents and all((state.get(p) == p for p in parents)):
1492 state[rev] = rev
1493 state[rev] = rev
1493 return originalwd, destmap, state
1494 return originalwd, destmap, state
1494
1495
1495 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None):
1496 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None):
1496 """dispose of rebased revision at the end of the rebase
1497 """dispose of rebased revision at the end of the rebase
1497
1498
1498 If `collapsedas` is not None, the rebase was a collapse whose result if the
1499 If `collapsedas` is not None, the rebase was a collapse whose result if the
1499 `collapsedas` node."""
1500 `collapsedas` node."""
1500 tonode = repo.changelog.node
1501 tonode = repo.changelog.node
1501 # Move bookmark of skipped nodes to destination. This cannot be handled
1502 # Move bookmark of skipped nodes to destination. This cannot be handled
1502 # by scmutil.cleanupnodes since it will treat rev as removed (no successor)
1503 # by scmutil.cleanupnodes since it will treat rev as removed (no successor)
1503 # and move bookmark backwards.
1504 # and move bookmark backwards.
1504 bmchanges = [(name, tonode(state[rev]))
1505 bmchanges = [(name, tonode(state[rev]))
1505 for rev in skipped
1506 for rev in skipped
1506 for name in repo.nodebookmarks(tonode(rev))]
1507 for name in repo.nodebookmarks(tonode(rev))]
1507 if bmchanges:
1508 if bmchanges:
1508 with repo.transaction('rebase') as tr:
1509 with repo.transaction('rebase') as tr:
1509 repo._bookmarks.applychanges(repo, tr, bmchanges)
1510 repo._bookmarks.applychanges(repo, tr, bmchanges)
1510 mapping = {}
1511 mapping = {}
1511 for rev, newrev in sorted(state.items()):
1512 for rev, newrev in sorted(state.items()):
1512 if newrev >= 0 and newrev != rev:
1513 if newrev >= 0 and newrev != rev:
1513 if rev in skipped:
1514 if rev in skipped:
1514 succs = ()
1515 succs = ()
1515 elif collapsedas is not None:
1516 elif collapsedas is not None:
1516 succs = (collapsedas,)
1517 succs = (collapsedas,)
1517 else:
1518 else:
1518 succs = (tonode(newrev),)
1519 succs = (tonode(newrev),)
1519 mapping[tonode(rev)] = succs
1520 mapping[tonode(rev)] = succs
1520 scmutil.cleanupnodes(repo, mapping, 'rebase')
1521 scmutil.cleanupnodes(repo, mapping, 'rebase')
1521
1522
1522 def pullrebase(orig, ui, repo, *args, **opts):
1523 def pullrebase(orig, ui, repo, *args, **opts):
1523 'Call rebase after pull if the latter has been invoked with --rebase'
1524 'Call rebase after pull if the latter has been invoked with --rebase'
1524 ret = None
1525 ret = None
1525 if opts.get('rebase'):
1526 if opts.get('rebase'):
1526 if ui.configbool('commands', 'rebase.requiredest'):
1527 if ui.configbool('commands', 'rebase.requiredest'):
1527 msg = _('rebase destination required by configuration')
1528 msg = _('rebase destination required by configuration')
1528 hint = _('use hg pull followed by hg rebase -d DEST')
1529 hint = _('use hg pull followed by hg rebase -d DEST')
1529 raise error.Abort(msg, hint=hint)
1530 raise error.Abort(msg, hint=hint)
1530
1531
1531 with repo.wlock(), repo.lock():
1532 with repo.wlock(), repo.lock():
1532 if opts.get('update'):
1533 if opts.get('update'):
1533 del opts['update']
1534 del opts['update']
1534 ui.debug('--update and --rebase are not compatible, ignoring '
1535 ui.debug('--update and --rebase are not compatible, ignoring '
1535 'the update flag\n')
1536 'the update flag\n')
1536
1537
1537 cmdutil.checkunfinished(repo)
1538 cmdutil.checkunfinished(repo)
1538 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1539 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1539 'please commit or shelve your changes first'))
1540 'please commit or shelve your changes first'))
1540
1541
1541 revsprepull = len(repo)
1542 revsprepull = len(repo)
1542 origpostincoming = commands.postincoming
1543 origpostincoming = commands.postincoming
1543 def _dummy(*args, **kwargs):
1544 def _dummy(*args, **kwargs):
1544 pass
1545 pass
1545 commands.postincoming = _dummy
1546 commands.postincoming = _dummy
1546 try:
1547 try:
1547 ret = orig(ui, repo, *args, **opts)
1548 ret = orig(ui, repo, *args, **opts)
1548 finally:
1549 finally:
1549 commands.postincoming = origpostincoming
1550 commands.postincoming = origpostincoming
1550 revspostpull = len(repo)
1551 revspostpull = len(repo)
1551 if revspostpull > revsprepull:
1552 if revspostpull > revsprepull:
1552 # --rev option from pull conflict with rebase own --rev
1553 # --rev option from pull conflict with rebase own --rev
1553 # dropping it
1554 # dropping it
1554 if 'rev' in opts:
1555 if 'rev' in opts:
1555 del opts['rev']
1556 del opts['rev']
1556 # positional argument from pull conflicts with rebase's own
1557 # positional argument from pull conflicts with rebase's own
1557 # --source.
1558 # --source.
1558 if 'source' in opts:
1559 if 'source' in opts:
1559 del opts['source']
1560 del opts['source']
1560 # revsprepull is the len of the repo, not revnum of tip.
1561 # revsprepull is the len of the repo, not revnum of tip.
1561 destspace = list(repo.changelog.revs(start=revsprepull))
1562 destspace = list(repo.changelog.revs(start=revsprepull))
1562 opts['_destspace'] = destspace
1563 opts['_destspace'] = destspace
1563 try:
1564 try:
1564 rebase(ui, repo, **opts)
1565 rebase(ui, repo, **opts)
1565 except error.NoMergeDestAbort:
1566 except error.NoMergeDestAbort:
1566 # we can maybe update instead
1567 # we can maybe update instead
1567 rev, _a, _b = destutil.destupdate(repo)
1568 rev, _a, _b = destutil.destupdate(repo)
1568 if rev == repo['.'].rev():
1569 if rev == repo['.'].rev():
1569 ui.status(_('nothing to rebase\n'))
1570 ui.status(_('nothing to rebase\n'))
1570 else:
1571 else:
1571 ui.status(_('nothing to rebase - updating instead\n'))
1572 ui.status(_('nothing to rebase - updating instead\n'))
1572 # not passing argument to get the bare update behavior
1573 # not passing argument to get the bare update behavior
1573 # with warning and trumpets
1574 # with warning and trumpets
1574 commands.update(ui, repo)
1575 commands.update(ui, repo)
1575 else:
1576 else:
1576 if opts.get('tool'):
1577 if opts.get('tool'):
1577 raise error.Abort(_('--tool can only be used with --rebase'))
1578 raise error.Abort(_('--tool can only be used with --rebase'))
1578 ret = orig(ui, repo, *args, **opts)
1579 ret = orig(ui, repo, *args, **opts)
1579
1580
1580 return ret
1581 return ret
1581
1582
1582 def _setrebasesetvisibility(repo, revs):
1583 def _setrebasesetvisibility(repo, revs):
1583 """store the currently rebased set on the repo object
1584 """store the currently rebased set on the repo object
1584
1585
1585 This is used by another function to prevent rebased revision to because
1586 This is used by another function to prevent rebased revision to because
1586 hidden (see issue4504)"""
1587 hidden (see issue4504)"""
1587 repo = repo.unfiltered()
1588 repo = repo.unfiltered()
1588 repo._rebaseset = revs
1589 repo._rebaseset = revs
1589 # invalidate cache if visibility changes
1590 # invalidate cache if visibility changes
1590 hiddens = repo.filteredrevcache.get('visible', set())
1591 hiddens = repo.filteredrevcache.get('visible', set())
1591 if revs & hiddens:
1592 if revs & hiddens:
1592 repo.invalidatevolatilesets()
1593 repo.invalidatevolatilesets()
1593
1594
1594 def _clearrebasesetvisibiliy(repo):
1595 def _clearrebasesetvisibiliy(repo):
1595 """remove rebaseset data from the repo"""
1596 """remove rebaseset data from the repo"""
1596 repo = repo.unfiltered()
1597 repo = repo.unfiltered()
1597 if '_rebaseset' in vars(repo):
1598 if '_rebaseset' in vars(repo):
1598 del repo._rebaseset
1599 del repo._rebaseset
1599
1600
1600 def _rebasedvisible(orig, repo):
1601 def _rebasedvisible(orig, repo):
1601 """ensure rebased revs stay visible (see issue4504)"""
1602 """ensure rebased revs stay visible (see issue4504)"""
1602 blockers = orig(repo)
1603 blockers = orig(repo)
1603 blockers.update(getattr(repo, '_rebaseset', ()))
1604 blockers.update(getattr(repo, '_rebaseset', ()))
1604 return blockers
1605 return blockers
1605
1606
1606 def _filterobsoleterevs(repo, revs):
1607 def _filterobsoleterevs(repo, revs):
1607 """returns a set of the obsolete revisions in revs"""
1608 """returns a set of the obsolete revisions in revs"""
1608 return set(r for r in revs if repo[r].obsolete())
1609 return set(r for r in revs if repo[r].obsolete())
1609
1610
1610 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1611 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1611 """return a mapping obsolete => successor for all obsolete nodes to be
1612 """return a mapping obsolete => successor for all obsolete nodes to be
1612 rebased that have a successors in the destination
1613 rebased that have a successors in the destination
1613
1614
1614 obsolete => None entries in the mapping indicate nodes with no successor"""
1615 obsolete => None entries in the mapping indicate nodes with no successor"""
1615 obsoletenotrebased = {}
1616 obsoletenotrebased = {}
1616
1617
1617 cl = repo.unfiltered().changelog
1618 cl = repo.unfiltered().changelog
1618 nodemap = cl.nodemap
1619 nodemap = cl.nodemap
1619 for srcrev in rebaseobsrevs:
1620 for srcrev in rebaseobsrevs:
1620 srcnode = cl.node(srcrev)
1621 srcnode = cl.node(srcrev)
1621 destnode = cl.node(destmap[srcrev])
1622 destnode = cl.node(destmap[srcrev])
1622 # XXX: more advanced APIs are required to handle split correctly
1623 # XXX: more advanced APIs are required to handle split correctly
1623 successors = list(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1624 successors = list(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1624 if len(successors) == 1:
1625 if len(successors) == 1:
1625 # obsutil.allsuccessors includes node itself. When the list only
1626 # obsutil.allsuccessors includes node itself. When the list only
1626 # contains one element, it means there are no successors.
1627 # contains one element, it means there are no successors.
1627 obsoletenotrebased[srcrev] = None
1628 obsoletenotrebased[srcrev] = None
1628 else:
1629 else:
1629 for succnode in successors:
1630 for succnode in successors:
1630 if succnode == srcnode or succnode not in nodemap:
1631 if succnode == srcnode or succnode not in nodemap:
1631 continue
1632 continue
1632 if cl.isancestor(succnode, destnode):
1633 if cl.isancestor(succnode, destnode):
1633 obsoletenotrebased[srcrev] = nodemap[succnode]
1634 obsoletenotrebased[srcrev] = nodemap[succnode]
1634 break
1635 break
1635
1636
1636 return obsoletenotrebased
1637 return obsoletenotrebased
1637
1638
1638 def summaryhook(ui, repo):
1639 def summaryhook(ui, repo):
1639 if not repo.vfs.exists('rebasestate'):
1640 if not repo.vfs.exists('rebasestate'):
1640 return
1641 return
1641 try:
1642 try:
1642 rbsrt = rebaseruntime(repo, ui, {})
1643 rbsrt = rebaseruntime(repo, ui, {})
1643 rbsrt.restorestatus()
1644 rbsrt.restorestatus()
1644 state = rbsrt.state
1645 state = rbsrt.state
1645 except error.RepoLookupError:
1646 except error.RepoLookupError:
1646 # i18n: column positioning for "hg summary"
1647 # i18n: column positioning for "hg summary"
1647 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1648 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1648 ui.write(msg)
1649 ui.write(msg)
1649 return
1650 return
1650 numrebased = len([i for i in state.itervalues() if i >= 0])
1651 numrebased = len([i for i in state.itervalues() if i >= 0])
1651 # i18n: column positioning for "hg summary"
1652 # i18n: column positioning for "hg summary"
1652 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1653 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1653 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1654 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1654 ui.label(_('%d remaining'), 'rebase.remaining') %
1655 ui.label(_('%d remaining'), 'rebase.remaining') %
1655 (len(state) - numrebased)))
1656 (len(state) - numrebased)))
1656
1657
1657 def uisetup(ui):
1658 def uisetup(ui):
1658 #Replace pull with a decorator to provide --rebase option
1659 #Replace pull with a decorator to provide --rebase option
1659 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1660 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1660 entry[1].append(('', 'rebase', None,
1661 entry[1].append(('', 'rebase', None,
1661 _("rebase working directory to branch head")))
1662 _("rebase working directory to branch head")))
1662 entry[1].append(('t', 'tool', '',
1663 entry[1].append(('t', 'tool', '',
1663 _("specify merge tool for rebase")))
1664 _("specify merge tool for rebase")))
1664 cmdutil.summaryhooks.add('rebase', summaryhook)
1665 cmdutil.summaryhooks.add('rebase', summaryhook)
1665 cmdutil.unfinishedstates.append(
1666 cmdutil.unfinishedstates.append(
1666 ['rebasestate', False, False, _('rebase in progress'),
1667 ['rebasestate', False, False, _('rebase in progress'),
1667 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1668 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1668 cmdutil.afterresolvedstates.append(
1669 cmdutil.afterresolvedstates.append(
1669 ['rebasestate', _('hg rebase --continue')])
1670 ['rebasestate', _('hg rebase --continue')])
1670 # ensure rebased rev are not hidden
1671 # ensure rebased rev are not hidden
1671 extensions.wrapfunction(repoview, 'pinnedrevs', _rebasedvisible)
1672 extensions.wrapfunction(repoview, 'pinnedrevs', _rebasedvisible)
@@ -1,1269 +1,1303
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 > stabilization=createmarkers,allowunstable
11 > stabilization=createmarkers,allowunstable
12 > [phases]
12 > [phases]
13 > publish=False
13 > publish=False
14 > [extensions]
14 > [extensions]
15 > rebase=
15 > rebase=
16 > drawdag=$TESTDIR/drawdag.py
16 > drawdag=$TESTDIR/drawdag.py
17 > EOF
17 > EOF
18
18
19 Setup rebase canonical repo
19 Setup rebase canonical repo
20
20
21 $ hg init base
21 $ hg init base
22 $ cd base
22 $ cd base
23 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
23 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
24 adding changesets
24 adding changesets
25 adding manifests
25 adding manifests
26 adding file changes
26 adding file changes
27 added 8 changesets with 7 changes to 7 files (+2 heads)
27 added 8 changesets with 7 changes to 7 files (+2 heads)
28 (run 'hg heads' to see heads, 'hg merge' to merge)
28 (run 'hg heads' to see heads, 'hg merge' to merge)
29 $ hg up tip
29 $ hg up tip
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 $ hg log -G
31 $ hg log -G
32 @ 7:02de42196ebe H
32 @ 7:02de42196ebe H
33 |
33 |
34 | o 6:eea13746799a G
34 | o 6:eea13746799a G
35 |/|
35 |/|
36 o | 5:24b6387c8c8c F
36 o | 5:24b6387c8c8c F
37 | |
37 | |
38 | o 4:9520eea781bc E
38 | o 4:9520eea781bc E
39 |/
39 |/
40 | o 3:32af7686d403 D
40 | o 3:32af7686d403 D
41 | |
41 | |
42 | o 2:5fddd98957c8 C
42 | o 2:5fddd98957c8 C
43 | |
43 | |
44 | o 1:42ccdea3bb16 B
44 | o 1:42ccdea3bb16 B
45 |/
45 |/
46 o 0:cd010b8cd998 A
46 o 0:cd010b8cd998 A
47
47
48 $ cd ..
48 $ cd ..
49
49
50 simple rebase
50 simple rebase
51 ---------------------------------
51 ---------------------------------
52
52
53 $ hg clone base simple
53 $ hg clone base simple
54 updating to branch default
54 updating to branch default
55 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 $ cd simple
56 $ cd simple
57 $ hg up 32af7686d403
57 $ hg up 32af7686d403
58 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
58 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
59 $ hg rebase -d eea13746799a
59 $ hg rebase -d eea13746799a
60 rebasing 1:42ccdea3bb16 "B"
60 rebasing 1:42ccdea3bb16 "B"
61 rebasing 2:5fddd98957c8 "C"
61 rebasing 2:5fddd98957c8 "C"
62 rebasing 3:32af7686d403 "D"
62 rebasing 3:32af7686d403 "D"
63 $ hg log -G
63 $ hg log -G
64 @ 10:8eeb3c33ad33 D
64 @ 10:8eeb3c33ad33 D
65 |
65 |
66 o 9:2327fea05063 C
66 o 9:2327fea05063 C
67 |
67 |
68 o 8:e4e5be0395b2 B
68 o 8:e4e5be0395b2 B
69 |
69 |
70 | o 7:02de42196ebe H
70 | o 7:02de42196ebe H
71 | |
71 | |
72 o | 6:eea13746799a G
72 o | 6:eea13746799a G
73 |\|
73 |\|
74 | o 5:24b6387c8c8c F
74 | o 5:24b6387c8c8c F
75 | |
75 | |
76 o | 4:9520eea781bc E
76 o | 4:9520eea781bc E
77 |/
77 |/
78 o 0:cd010b8cd998 A
78 o 0:cd010b8cd998 A
79
79
80 $ hg log --hidden -G
80 $ hg log --hidden -G
81 @ 10:8eeb3c33ad33 D
81 @ 10:8eeb3c33ad33 D
82 |
82 |
83 o 9:2327fea05063 C
83 o 9:2327fea05063 C
84 |
84 |
85 o 8:e4e5be0395b2 B
85 o 8:e4e5be0395b2 B
86 |
86 |
87 | o 7:02de42196ebe H
87 | o 7:02de42196ebe H
88 | |
88 | |
89 o | 6:eea13746799a G
89 o | 6:eea13746799a G
90 |\|
90 |\|
91 | o 5:24b6387c8c8c F
91 | o 5:24b6387c8c8c F
92 | |
92 | |
93 o | 4:9520eea781bc E
93 o | 4:9520eea781bc E
94 |/
94 |/
95 | x 3:32af7686d403 D
95 | x 3:32af7686d403 D
96 | |
96 | |
97 | x 2:5fddd98957c8 C
97 | x 2:5fddd98957c8 C
98 | |
98 | |
99 | x 1:42ccdea3bb16 B
99 | x 1:42ccdea3bb16 B
100 |/
100 |/
101 o 0:cd010b8cd998 A
101 o 0:cd010b8cd998 A
102
102
103 $ hg debugobsolete
103 $ hg debugobsolete
104 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (*) {'user': 'test'} (glob)
104 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (*) {'user': 'test'} (glob)
105 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (*) {'user': 'test'} (glob)
105 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (*) {'user': 'test'} (glob)
106 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (*) {'user': 'test'} (glob)
106 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (*) {'user': 'test'} (glob)
107
107
108
108
109 $ cd ..
109 $ cd ..
110
110
111 empty changeset
111 empty changeset
112 ---------------------------------
112 ---------------------------------
113
113
114 $ hg clone base empty
114 $ hg clone base empty
115 updating to branch default
115 updating to branch default
116 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 $ cd empty
117 $ cd empty
118 $ hg up eea13746799a
118 $ hg up eea13746799a
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
120
120
121 We make a copy of both the first changeset in the rebased and some other in the
121 We make a copy of both the first changeset in the rebased and some other in the
122 set.
122 set.
123
123
124 $ hg graft 42ccdea3bb16 32af7686d403
124 $ hg graft 42ccdea3bb16 32af7686d403
125 grafting 1:42ccdea3bb16 "B"
125 grafting 1:42ccdea3bb16 "B"
126 grafting 3:32af7686d403 "D"
126 grafting 3:32af7686d403 "D"
127 $ hg rebase -s 42ccdea3bb16 -d .
127 $ hg rebase -s 42ccdea3bb16 -d .
128 rebasing 1:42ccdea3bb16 "B"
128 rebasing 1:42ccdea3bb16 "B"
129 note: rebase of 1:42ccdea3bb16 created no changes to commit
129 note: rebase of 1:42ccdea3bb16 created no changes to commit
130 rebasing 2:5fddd98957c8 "C"
130 rebasing 2:5fddd98957c8 "C"
131 rebasing 3:32af7686d403 "D"
131 rebasing 3:32af7686d403 "D"
132 note: rebase of 3:32af7686d403 created no changes to commit
132 note: rebase of 3:32af7686d403 created no changes to commit
133 $ hg log -G
133 $ hg log -G
134 o 10:5ae4c968c6ac C
134 o 10:5ae4c968c6ac C
135 |
135 |
136 @ 9:08483444fef9 D
136 @ 9:08483444fef9 D
137 |
137 |
138 o 8:8877864f1edb B
138 o 8:8877864f1edb B
139 |
139 |
140 | o 7:02de42196ebe H
140 | o 7:02de42196ebe H
141 | |
141 | |
142 o | 6:eea13746799a G
142 o | 6:eea13746799a G
143 |\|
143 |\|
144 | o 5:24b6387c8c8c F
144 | o 5:24b6387c8c8c F
145 | |
145 | |
146 o | 4:9520eea781bc E
146 o | 4:9520eea781bc E
147 |/
147 |/
148 o 0:cd010b8cd998 A
148 o 0:cd010b8cd998 A
149
149
150 $ hg log --hidden -G
150 $ hg log --hidden -G
151 o 10:5ae4c968c6ac C
151 o 10:5ae4c968c6ac C
152 |
152 |
153 @ 9:08483444fef9 D
153 @ 9:08483444fef9 D
154 |
154 |
155 o 8:8877864f1edb B
155 o 8:8877864f1edb B
156 |
156 |
157 | o 7:02de42196ebe H
157 | o 7:02de42196ebe H
158 | |
158 | |
159 o | 6:eea13746799a G
159 o | 6:eea13746799a G
160 |\|
160 |\|
161 | o 5:24b6387c8c8c F
161 | o 5:24b6387c8c8c F
162 | |
162 | |
163 o | 4:9520eea781bc E
163 o | 4:9520eea781bc E
164 |/
164 |/
165 | x 3:32af7686d403 D
165 | x 3:32af7686d403 D
166 | |
166 | |
167 | x 2:5fddd98957c8 C
167 | x 2:5fddd98957c8 C
168 | |
168 | |
169 | x 1:42ccdea3bb16 B
169 | x 1:42ccdea3bb16 B
170 |/
170 |/
171 o 0:cd010b8cd998 A
171 o 0:cd010b8cd998 A
172
172
173 $ hg debugobsolete
173 $ hg debugobsolete
174 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
174 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
175 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
175 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
176 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
176 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
177
177
178
178
179 More complex case where part of the rebase set were already rebased
179 More complex case where part of the rebase set were already rebased
180
180
181 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
181 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
182 rebasing 9:08483444fef9 "D"
182 rebasing 9:08483444fef9 "D"
183 $ hg debugobsolete
183 $ hg debugobsolete
184 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
184 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
185 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
185 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
186 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
186 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
187 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
187 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
188 $ hg log -G
188 $ hg log -G
189 @ 11:4596109a6a43 D
189 @ 11:4596109a6a43 D
190 |
190 |
191 | o 10:5ae4c968c6ac C
191 | o 10:5ae4c968c6ac C
192 | |
192 | |
193 | x 9:08483444fef9 D
193 | x 9:08483444fef9 D
194 | |
194 | |
195 | o 8:8877864f1edb B
195 | o 8:8877864f1edb B
196 | |
196 | |
197 o | 7:02de42196ebe H
197 o | 7:02de42196ebe H
198 | |
198 | |
199 | o 6:eea13746799a G
199 | o 6:eea13746799a G
200 |/|
200 |/|
201 o | 5:24b6387c8c8c F
201 o | 5:24b6387c8c8c F
202 | |
202 | |
203 | o 4:9520eea781bc E
203 | o 4:9520eea781bc E
204 |/
204 |/
205 o 0:cd010b8cd998 A
205 o 0:cd010b8cd998 A
206
206
207 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
207 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
208 rebasing 8:8877864f1edb "B"
208 rebasing 8:8877864f1edb "B"
209 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D" (tip)
209 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D" (tip)
210 rebasing 10:5ae4c968c6ac "C"
210 rebasing 10:5ae4c968c6ac "C"
211 $ hg debugobsolete
211 $ hg debugobsolete
212 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
212 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
213 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
213 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
214 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
214 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
215 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
215 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
216 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob)
216 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob)
217 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob)
217 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob)
218 $ hg log --rev 'contentdivergent()'
218 $ hg log --rev 'contentdivergent()'
219 $ hg log -G
219 $ hg log -G
220 o 13:98f6af4ee953 C
220 o 13:98f6af4ee953 C
221 |
221 |
222 o 12:462a34d07e59 B
222 o 12:462a34d07e59 B
223 |
223 |
224 @ 11:4596109a6a43 D
224 @ 11:4596109a6a43 D
225 |
225 |
226 o 7:02de42196ebe H
226 o 7:02de42196ebe H
227 |
227 |
228 | o 6:eea13746799a G
228 | o 6:eea13746799a G
229 |/|
229 |/|
230 o | 5:24b6387c8c8c F
230 o | 5:24b6387c8c8c F
231 | |
231 | |
232 | o 4:9520eea781bc E
232 | o 4:9520eea781bc E
233 |/
233 |/
234 o 0:cd010b8cd998 A
234 o 0:cd010b8cd998 A
235
235
236 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
236 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
237 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
237 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
238 phase: draft
238 phase: draft
239 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
239 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
240 parent: -1:0000000000000000000000000000000000000000
240 parent: -1:0000000000000000000000000000000000000000
241 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
241 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
242 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
242 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
243 date: Sat Apr 30 15:24:48 2011 +0200
243 date: Sat Apr 30 15:24:48 2011 +0200
244 files+: D
244 files+: D
245 extra: branch=default
245 extra: branch=default
246 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
246 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
247 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
247 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
248 description:
248 description:
249 D
249 D
250
250
251
251
252 $ hg up -qr 'desc(G)'
252 $ hg up -qr 'desc(G)'
253 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
253 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
254 grafting 11:4596109a6a43 "D"
254 grafting 11:4596109a6a43 "D"
255 $ hg up -qr 'desc(E)'
255 $ hg up -qr 'desc(E)'
256 $ hg rebase -s tip -d .
256 $ hg rebase -s tip -d .
257 rebasing 14:9e36056a46e3 "D" (tip)
257 rebasing 14:9e36056a46e3 "D" (tip)
258 $ hg log --style default --debug -r tip
258 $ hg log --style default --debug -r tip
259 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
259 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
260 tag: tip
260 tag: tip
261 phase: draft
261 phase: draft
262 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
262 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
263 parent: -1:0000000000000000000000000000000000000000
263 parent: -1:0000000000000000000000000000000000000000
264 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
264 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
265 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
265 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
266 date: Sat Apr 30 15:24:48 2011 +0200
266 date: Sat Apr 30 15:24:48 2011 +0200
267 files+: D
267 files+: D
268 extra: branch=default
268 extra: branch=default
269 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
269 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
270 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
270 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
271 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
271 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
272 description:
272 description:
273 D
273 D
274
274
275
275
276 Start rebase from a commit that is obsolete but not hidden only because it's
276 Start rebase from a commit that is obsolete but not hidden only because it's
277 a working copy parent. We should be moved back to the starting commit as usual
277 a working copy parent. We should be moved back to the starting commit as usual
278 even though it is hidden (until we're moved there).
278 even though it is hidden (until we're moved there).
279
279
280 $ hg --hidden up -qr 'first(hidden())'
280 $ hg --hidden up -qr 'first(hidden())'
281 $ hg rebase --rev 13 --dest 15
281 $ hg rebase --rev 13 --dest 15
282 rebasing 13:98f6af4ee953 "C"
282 rebasing 13:98f6af4ee953 "C"
283 $ hg log -G
283 $ hg log -G
284 o 16:294a2b93eb4d C
284 o 16:294a2b93eb4d C
285 |
285 |
286 o 15:627d46148090 D
286 o 15:627d46148090 D
287 |
287 |
288 | o 12:462a34d07e59 B
288 | o 12:462a34d07e59 B
289 | |
289 | |
290 | o 11:4596109a6a43 D
290 | o 11:4596109a6a43 D
291 | |
291 | |
292 | o 7:02de42196ebe H
292 | o 7:02de42196ebe H
293 | |
293 | |
294 +---o 6:eea13746799a G
294 +---o 6:eea13746799a G
295 | |/
295 | |/
296 | o 5:24b6387c8c8c F
296 | o 5:24b6387c8c8c F
297 | |
297 | |
298 o | 4:9520eea781bc E
298 o | 4:9520eea781bc E
299 |/
299 |/
300 | @ 1:42ccdea3bb16 B
300 | @ 1:42ccdea3bb16 B
301 |/
301 |/
302 o 0:cd010b8cd998 A
302 o 0:cd010b8cd998 A
303
303
304
304
305 $ cd ..
305 $ cd ..
306
306
307 collapse rebase
307 collapse rebase
308 ---------------------------------
308 ---------------------------------
309
309
310 $ hg clone base collapse
310 $ hg clone base collapse
311 updating to branch default
311 updating to branch default
312 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 $ cd collapse
313 $ cd collapse
314 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
314 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
315 rebasing 1:42ccdea3bb16 "B"
315 rebasing 1:42ccdea3bb16 "B"
316 rebasing 2:5fddd98957c8 "C"
316 rebasing 2:5fddd98957c8 "C"
317 rebasing 3:32af7686d403 "D"
317 rebasing 3:32af7686d403 "D"
318 $ hg log -G
318 $ hg log -G
319 o 8:4dc2197e807b Collapsed revision
319 o 8:4dc2197e807b Collapsed revision
320 |
320 |
321 | @ 7:02de42196ebe H
321 | @ 7:02de42196ebe H
322 | |
322 | |
323 o | 6:eea13746799a G
323 o | 6:eea13746799a G
324 |\|
324 |\|
325 | o 5:24b6387c8c8c F
325 | o 5:24b6387c8c8c F
326 | |
326 | |
327 o | 4:9520eea781bc E
327 o | 4:9520eea781bc E
328 |/
328 |/
329 o 0:cd010b8cd998 A
329 o 0:cd010b8cd998 A
330
330
331 $ hg log --hidden -G
331 $ hg log --hidden -G
332 o 8:4dc2197e807b Collapsed revision
332 o 8:4dc2197e807b Collapsed revision
333 |
333 |
334 | @ 7:02de42196ebe H
334 | @ 7:02de42196ebe H
335 | |
335 | |
336 o | 6:eea13746799a G
336 o | 6:eea13746799a G
337 |\|
337 |\|
338 | o 5:24b6387c8c8c F
338 | o 5:24b6387c8c8c F
339 | |
339 | |
340 o | 4:9520eea781bc E
340 o | 4:9520eea781bc E
341 |/
341 |/
342 | x 3:32af7686d403 D
342 | x 3:32af7686d403 D
343 | |
343 | |
344 | x 2:5fddd98957c8 C
344 | x 2:5fddd98957c8 C
345 | |
345 | |
346 | x 1:42ccdea3bb16 B
346 | x 1:42ccdea3bb16 B
347 |/
347 |/
348 o 0:cd010b8cd998 A
348 o 0:cd010b8cd998 A
349
349
350 $ hg id --debug -r tip
350 $ hg id --debug -r tip
351 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
351 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
352 $ hg debugobsolete
352 $ hg debugobsolete
353 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
353 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
354 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
354 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
355 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
355 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
356
356
357 $ cd ..
357 $ cd ..
358
358
359 Rebase set has hidden descendants
359 Rebase set has hidden descendants
360 ---------------------------------
360 ---------------------------------
361
361
362 We rebase a changeset which has a hidden changeset. The hidden changeset must
362 We rebase a changeset which has a hidden changeset. The hidden changeset must
363 not be rebased.
363 not be rebased.
364
364
365 $ hg clone base hidden
365 $ hg clone base hidden
366 updating to branch default
366 updating to branch default
367 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 $ cd hidden
368 $ cd hidden
369 $ hg rebase -s 5fddd98957c8 -d eea13746799a
369 $ hg rebase -s 5fddd98957c8 -d eea13746799a
370 rebasing 2:5fddd98957c8 "C"
370 rebasing 2:5fddd98957c8 "C"
371 rebasing 3:32af7686d403 "D"
371 rebasing 3:32af7686d403 "D"
372 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
372 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
373 rebasing 1:42ccdea3bb16 "B"
373 rebasing 1:42ccdea3bb16 "B"
374 $ hg log -G
374 $ hg log -G
375 o 10:7c6027df6a99 B
375 o 10:7c6027df6a99 B
376 |
376 |
377 | o 9:cf44d2f5a9f4 D
377 | o 9:cf44d2f5a9f4 D
378 | |
378 | |
379 | o 8:e273c5e7d2d2 C
379 | o 8:e273c5e7d2d2 C
380 | |
380 | |
381 @ | 7:02de42196ebe H
381 @ | 7:02de42196ebe H
382 | |
382 | |
383 | o 6:eea13746799a G
383 | o 6:eea13746799a G
384 |/|
384 |/|
385 o | 5:24b6387c8c8c F
385 o | 5:24b6387c8c8c F
386 | |
386 | |
387 | o 4:9520eea781bc E
387 | o 4:9520eea781bc E
388 |/
388 |/
389 o 0:cd010b8cd998 A
389 o 0:cd010b8cd998 A
390
390
391 $ hg log --hidden -G
391 $ hg log --hidden -G
392 o 10:7c6027df6a99 B
392 o 10:7c6027df6a99 B
393 |
393 |
394 | o 9:cf44d2f5a9f4 D
394 | o 9:cf44d2f5a9f4 D
395 | |
395 | |
396 | o 8:e273c5e7d2d2 C
396 | o 8:e273c5e7d2d2 C
397 | |
397 | |
398 @ | 7:02de42196ebe H
398 @ | 7:02de42196ebe H
399 | |
399 | |
400 | o 6:eea13746799a G
400 | o 6:eea13746799a G
401 |/|
401 |/|
402 o | 5:24b6387c8c8c F
402 o | 5:24b6387c8c8c F
403 | |
403 | |
404 | o 4:9520eea781bc E
404 | o 4:9520eea781bc E
405 |/
405 |/
406 | x 3:32af7686d403 D
406 | x 3:32af7686d403 D
407 | |
407 | |
408 | x 2:5fddd98957c8 C
408 | x 2:5fddd98957c8 C
409 | |
409 | |
410 | x 1:42ccdea3bb16 B
410 | x 1:42ccdea3bb16 B
411 |/
411 |/
412 o 0:cd010b8cd998 A
412 o 0:cd010b8cd998 A
413
413
414 $ hg debugobsolete
414 $ hg debugobsolete
415 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (*) {'user': 'test'} (glob)
415 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (*) {'user': 'test'} (glob)
416 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (*) {'user': 'test'} (glob)
416 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (*) {'user': 'test'} (glob)
417 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (*) {'user': 'test'} (glob)
417 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (*) {'user': 'test'} (glob)
418
418
419 Test that rewriting leaving instability behind is allowed
419 Test that rewriting leaving instability behind is allowed
420 ---------------------------------------------------------------------
420 ---------------------------------------------------------------------
421
421
422 $ hg log -r 'children(8)'
422 $ hg log -r 'children(8)'
423 9:cf44d2f5a9f4 D (no-eol)
423 9:cf44d2f5a9f4 D (no-eol)
424 $ hg rebase -r 8
424 $ hg rebase -r 8
425 rebasing 8:e273c5e7d2d2 "C"
425 rebasing 8:e273c5e7d2d2 "C"
426 $ hg log -G
426 $ hg log -G
427 o 11:0d8f238b634c C
427 o 11:0d8f238b634c C
428 |
428 |
429 o 10:7c6027df6a99 B
429 o 10:7c6027df6a99 B
430 |
430 |
431 | o 9:cf44d2f5a9f4 D
431 | o 9:cf44d2f5a9f4 D
432 | |
432 | |
433 | x 8:e273c5e7d2d2 C
433 | x 8:e273c5e7d2d2 C
434 | |
434 | |
435 @ | 7:02de42196ebe H
435 @ | 7:02de42196ebe H
436 | |
436 | |
437 | o 6:eea13746799a G
437 | o 6:eea13746799a G
438 |/|
438 |/|
439 o | 5:24b6387c8c8c F
439 o | 5:24b6387c8c8c F
440 | |
440 | |
441 | o 4:9520eea781bc E
441 | o 4:9520eea781bc E
442 |/
442 |/
443 o 0:cd010b8cd998 A
443 o 0:cd010b8cd998 A
444
444
445
445
446
446
447 Test multiple root handling
447 Test multiple root handling
448 ------------------------------------
448 ------------------------------------
449
449
450 $ hg rebase --dest 4 --rev '7+11+9'
450 $ hg rebase --dest 4 --rev '7+11+9'
451 rebasing 9:cf44d2f5a9f4 "D"
451 rebasing 9:cf44d2f5a9f4 "D"
452 rebasing 7:02de42196ebe "H"
452 rebasing 7:02de42196ebe "H"
453 rebasing 11:0d8f238b634c "C" (tip)
453 rebasing 11:0d8f238b634c "C" (tip)
454 $ hg log -G
454 $ hg log -G
455 o 14:1e8370e38cca C
455 o 14:1e8370e38cca C
456 |
456 |
457 @ 13:bfe264faf697 H
457 @ 13:bfe264faf697 H
458 |
458 |
459 | o 12:102b4c1d889b D
459 | o 12:102b4c1d889b D
460 |/
460 |/
461 | o 10:7c6027df6a99 B
461 | o 10:7c6027df6a99 B
462 | |
462 | |
463 | x 7:02de42196ebe H
463 | x 7:02de42196ebe H
464 | |
464 | |
465 +---o 6:eea13746799a G
465 +---o 6:eea13746799a G
466 | |/
466 | |/
467 | o 5:24b6387c8c8c F
467 | o 5:24b6387c8c8c F
468 | |
468 | |
469 o | 4:9520eea781bc E
469 o | 4:9520eea781bc E
470 |/
470 |/
471 o 0:cd010b8cd998 A
471 o 0:cd010b8cd998 A
472
472
473 $ cd ..
473 $ cd ..
474
474
475 Detach both parents
475 Detach both parents
476
476
477 $ hg init double-detach
477 $ hg init double-detach
478 $ cd double-detach
478 $ cd double-detach
479
479
480 $ hg debugdrawdag <<EOF
480 $ hg debugdrawdag <<EOF
481 > F
481 > F
482 > /|
482 > /|
483 > C E
483 > C E
484 > | |
484 > | |
485 > B D G
485 > B D G
486 > \|/
486 > \|/
487 > A
487 > A
488 > EOF
488 > EOF
489
489
490 $ hg rebase -d G -r 'B + D + F'
490 $ hg rebase -d G -r 'B + D + F'
491 rebasing 1:112478962961 "B" (B)
491 rebasing 1:112478962961 "B" (B)
492 rebasing 2:b18e25de2cf5 "D" (D)
492 rebasing 2:b18e25de2cf5 "D" (D)
493 rebasing 6:f15c3adaf214 "F" (F tip)
493 rebasing 6:f15c3adaf214 "F" (F tip)
494 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
494 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
495 [255]
495 [255]
496
496
497 $ cd ..
497 $ cd ..
498
498
499 test on rebase dropping a merge
499 test on rebase dropping a merge
500
500
501 (setup)
501 (setup)
502
502
503 $ hg init dropmerge
503 $ hg init dropmerge
504 $ cd dropmerge
504 $ cd dropmerge
505 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
505 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
506 adding changesets
506 adding changesets
507 adding manifests
507 adding manifests
508 adding file changes
508 adding file changes
509 added 8 changesets with 7 changes to 7 files (+2 heads)
509 added 8 changesets with 7 changes to 7 files (+2 heads)
510 (run 'hg heads' to see heads, 'hg merge' to merge)
510 (run 'hg heads' to see heads, 'hg merge' to merge)
511 $ hg up 3
511 $ hg up 3
512 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
512 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 $ hg merge 7
513 $ hg merge 7
514 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
515 (branch merge, don't forget to commit)
515 (branch merge, don't forget to commit)
516 $ hg ci -m 'M'
516 $ hg ci -m 'M'
517 $ echo I > I
517 $ echo I > I
518 $ hg add I
518 $ hg add I
519 $ hg ci -m I
519 $ hg ci -m I
520 $ hg log -G
520 $ hg log -G
521 @ 9:4bde274eefcf I
521 @ 9:4bde274eefcf I
522 |
522 |
523 o 8:53a6a128b2b7 M
523 o 8:53a6a128b2b7 M
524 |\
524 |\
525 | o 7:02de42196ebe H
525 | o 7:02de42196ebe H
526 | |
526 | |
527 | | o 6:eea13746799a G
527 | | o 6:eea13746799a G
528 | |/|
528 | |/|
529 | o | 5:24b6387c8c8c F
529 | o | 5:24b6387c8c8c F
530 | | |
530 | | |
531 | | o 4:9520eea781bc E
531 | | o 4:9520eea781bc E
532 | |/
532 | |/
533 o | 3:32af7686d403 D
533 o | 3:32af7686d403 D
534 | |
534 | |
535 o | 2:5fddd98957c8 C
535 o | 2:5fddd98957c8 C
536 | |
536 | |
537 o | 1:42ccdea3bb16 B
537 o | 1:42ccdea3bb16 B
538 |/
538 |/
539 o 0:cd010b8cd998 A
539 o 0:cd010b8cd998 A
540
540
541 (actual test)
541 (actual test)
542
542
543 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
543 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
544 rebasing 3:32af7686d403 "D"
544 rebasing 3:32af7686d403 "D"
545 rebasing 7:02de42196ebe "H"
545 rebasing 7:02de42196ebe "H"
546 rebasing 9:4bde274eefcf "I" (tip)
546 rebasing 9:4bde274eefcf "I" (tip)
547 $ hg log -G
547 $ hg log -G
548 @ 12:acd174b7ab39 I
548 @ 12:acd174b7ab39 I
549 |
549 |
550 o 11:6c11a6218c97 H
550 o 11:6c11a6218c97 H
551 |
551 |
552 | o 10:b5313c85b22e D
552 | o 10:b5313c85b22e D
553 |/
553 |/
554 | o 8:53a6a128b2b7 M
554 | o 8:53a6a128b2b7 M
555 | |\
555 | |\
556 | | x 7:02de42196ebe H
556 | | x 7:02de42196ebe H
557 | | |
557 | | |
558 o---+ 6:eea13746799a G
558 o---+ 6:eea13746799a G
559 | | |
559 | | |
560 | | o 5:24b6387c8c8c F
560 | | o 5:24b6387c8c8c F
561 | | |
561 | | |
562 o---+ 4:9520eea781bc E
562 o---+ 4:9520eea781bc E
563 / /
563 / /
564 x | 3:32af7686d403 D
564 x | 3:32af7686d403 D
565 | |
565 | |
566 o | 2:5fddd98957c8 C
566 o | 2:5fddd98957c8 C
567 | |
567 | |
568 o | 1:42ccdea3bb16 B
568 o | 1:42ccdea3bb16 B
569 |/
569 |/
570 o 0:cd010b8cd998 A
570 o 0:cd010b8cd998 A
571
571
572
572
573 Test hidden changesets in the rebase set (issue4504)
573 Test hidden changesets in the rebase set (issue4504)
574
574
575 $ hg up --hidden 9
575 $ hg up --hidden 9
576 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
576 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
577 $ echo J > J
577 $ echo J > J
578 $ hg add J
578 $ hg add J
579 $ hg commit -m J
579 $ hg commit -m J
580 $ hg debugobsolete `hg log --rev . -T '{node}'`
580 $ hg debugobsolete `hg log --rev . -T '{node}'`
581 obsoleted 1 changesets
581 obsoleted 1 changesets
582
582
583 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
583 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
584 rebasing 9:4bde274eefcf "I"
584 rebasing 9:4bde274eefcf "I"
585 rebasing 13:06edfc82198f "J" (tip)
585 rebasing 13:06edfc82198f "J" (tip)
586 $ hg log -G
586 $ hg log -G
587 @ 15:5ae8a643467b J
587 @ 15:5ae8a643467b J
588 |
588 |
589 o 14:9ad579b4a5de I
589 o 14:9ad579b4a5de I
590 |
590 |
591 | o 12:acd174b7ab39 I
591 | o 12:acd174b7ab39 I
592 | |
592 | |
593 | o 11:6c11a6218c97 H
593 | o 11:6c11a6218c97 H
594 | |
594 | |
595 o | 10:b5313c85b22e D
595 o | 10:b5313c85b22e D
596 |/
596 |/
597 | o 8:53a6a128b2b7 M
597 | o 8:53a6a128b2b7 M
598 | |\
598 | |\
599 | | x 7:02de42196ebe H
599 | | x 7:02de42196ebe H
600 | | |
600 | | |
601 o---+ 6:eea13746799a G
601 o---+ 6:eea13746799a G
602 | | |
602 | | |
603 | | o 5:24b6387c8c8c F
603 | | o 5:24b6387c8c8c F
604 | | |
604 | | |
605 o---+ 4:9520eea781bc E
605 o---+ 4:9520eea781bc E
606 / /
606 / /
607 x | 3:32af7686d403 D
607 x | 3:32af7686d403 D
608 | |
608 | |
609 o | 2:5fddd98957c8 C
609 o | 2:5fddd98957c8 C
610 | |
610 | |
611 o | 1:42ccdea3bb16 B
611 o | 1:42ccdea3bb16 B
612 |/
612 |/
613 o 0:cd010b8cd998 A
613 o 0:cd010b8cd998 A
614
614
615 $ hg up 14 -C
615 $ hg up 14 -C
616 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
616 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
617 $ echo "K" > K
617 $ echo "K" > K
618 $ hg add K
618 $ hg add K
619 $ hg commit --amend -m "K"
619 $ hg commit --amend -m "K"
620 $ echo "L" > L
620 $ echo "L" > L
621 $ hg add L
621 $ hg add L
622 $ hg commit -m "L"
622 $ hg commit -m "L"
623 $ hg up '.^'
623 $ hg up '.^'
624 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
624 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
625 $ echo "M" > M
625 $ echo "M" > M
626 $ hg add M
626 $ hg add M
627 $ hg commit --amend -m "M"
627 $ hg commit --amend -m "M"
628 $ hg log -G
628 $ hg log -G
629 @ 18:bfaedf8eb73b M
629 @ 18:bfaedf8eb73b M
630 |
630 |
631 | o 17:97219452e4bd L
631 | o 17:97219452e4bd L
632 | |
632 | |
633 | x 16:fc37a630c901 K
633 | x 16:fc37a630c901 K
634 |/
634 |/
635 | o 15:5ae8a643467b J
635 | o 15:5ae8a643467b J
636 | |
636 | |
637 | x 14:9ad579b4a5de I
637 | x 14:9ad579b4a5de I
638 |/
638 |/
639 | o 12:acd174b7ab39 I
639 | o 12:acd174b7ab39 I
640 | |
640 | |
641 | o 11:6c11a6218c97 H
641 | o 11:6c11a6218c97 H
642 | |
642 | |
643 o | 10:b5313c85b22e D
643 o | 10:b5313c85b22e D
644 |/
644 |/
645 | o 8:53a6a128b2b7 M
645 | o 8:53a6a128b2b7 M
646 | |\
646 | |\
647 | | x 7:02de42196ebe H
647 | | x 7:02de42196ebe H
648 | | |
648 | | |
649 o---+ 6:eea13746799a G
649 o---+ 6:eea13746799a G
650 | | |
650 | | |
651 | | o 5:24b6387c8c8c F
651 | | o 5:24b6387c8c8c F
652 | | |
652 | | |
653 o---+ 4:9520eea781bc E
653 o---+ 4:9520eea781bc E
654 / /
654 / /
655 x | 3:32af7686d403 D
655 x | 3:32af7686d403 D
656 | |
656 | |
657 o | 2:5fddd98957c8 C
657 o | 2:5fddd98957c8 C
658 | |
658 | |
659 o | 1:42ccdea3bb16 B
659 o | 1:42ccdea3bb16 B
660 |/
660 |/
661 o 0:cd010b8cd998 A
661 o 0:cd010b8cd998 A
662
662
663 $ hg rebase -s 14 -d 17 --config experimental.rebaseskipobsolete=True
663 $ hg rebase -s 14 -d 17 --config experimental.rebaseskipobsolete=True
664 note: not rebasing 14:9ad579b4a5de "I", already in destination as 16:fc37a630c901 "K"
664 note: not rebasing 14:9ad579b4a5de "I", already in destination as 16:fc37a630c901 "K"
665 rebasing 15:5ae8a643467b "J"
665 rebasing 15:5ae8a643467b "J"
666
666
667 $ cd ..
667 $ cd ..
668
668
669 Skip obsolete changeset even with multiple hops
669 Skip obsolete changeset even with multiple hops
670 -----------------------------------------------
670 -----------------------------------------------
671
671
672 setup
672 setup
673
673
674 $ hg init obsskip
674 $ hg init obsskip
675 $ cd obsskip
675 $ cd obsskip
676 $ cat << EOF >> .hg/hgrc
676 $ cat << EOF >> .hg/hgrc
677 > [experimental]
677 > [experimental]
678 > rebaseskipobsolete = True
678 > rebaseskipobsolete = True
679 > [extensions]
679 > [extensions]
680 > strip =
680 > strip =
681 > EOF
681 > EOF
682 $ echo A > A
682 $ echo A > A
683 $ hg add A
683 $ hg add A
684 $ hg commit -m A
684 $ hg commit -m A
685 $ echo B > B
685 $ echo B > B
686 $ hg add B
686 $ hg add B
687 $ hg commit -m B0
687 $ hg commit -m B0
688 $ hg commit --amend -m B1
688 $ hg commit --amend -m B1
689 $ hg commit --amend -m B2
689 $ hg commit --amend -m B2
690 $ hg up --hidden 'desc(B0)'
690 $ hg up --hidden 'desc(B0)'
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
692 $ echo C > C
692 $ echo C > C
693 $ hg add C
693 $ hg add C
694 $ hg commit -m C
694 $ hg commit -m C
695
695
696 Rebase finds its way in a chain of marker
696 Rebase finds its way in a chain of marker
697
697
698 $ hg rebase -d 'desc(B2)'
698 $ hg rebase -d 'desc(B2)'
699 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
699 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
700 rebasing 4:212cb178bcbb "C" (tip)
700 rebasing 4:212cb178bcbb "C" (tip)
701
701
702 Even when the chain include missing node
702 Even when the chain include missing node
703
703
704 $ hg up --hidden 'desc(B0)'
704 $ hg up --hidden 'desc(B0)'
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 D > D
706 $ echo D > D
707 $ hg add D
707 $ hg add D
708 $ hg commit -m D
708 $ hg commit -m D
709 $ hg --hidden strip -r 'desc(B1)'
709 $ hg --hidden strip -r 'desc(B1)'
710 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg (glob)
710 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg (glob)
711
711
712 $ hg rebase -d 'desc(B2)'
712 $ hg rebase -d 'desc(B2)'
713 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
713 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
714 rebasing 5:1a79b7535141 "D" (tip)
714 rebasing 5:1a79b7535141 "D" (tip)
715 $ hg up 4
715 $ hg up 4
716 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
716 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
717 $ echo "O" > O
717 $ echo "O" > O
718 $ hg add O
718 $ hg add O
719 $ hg commit -m O
719 $ hg commit -m O
720 $ echo "P" > P
720 $ echo "P" > P
721 $ hg add P
721 $ hg add P
722 $ hg commit -m P
722 $ hg commit -m P
723 $ hg log -G
723 $ hg log -G
724 @ 8:8d47583e023f P
724 @ 8:8d47583e023f P
725 |
725 |
726 o 7:360bbaa7d3ce O
726 o 7:360bbaa7d3ce O
727 |
727 |
728 | o 6:9c48361117de D
728 | o 6:9c48361117de D
729 | |
729 | |
730 o | 4:ff2c4d47b71d C
730 o | 4:ff2c4d47b71d C
731 |/
731 |/
732 o 2:261e70097290 B2
732 o 2:261e70097290 B2
733 |
733 |
734 o 0:4a2df7238c3b A
734 o 0:4a2df7238c3b A
735
735
736 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.stabilization=all
736 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.stabilization=all
737 obsoleted 1 changesets
737 obsoleted 1 changesets
738 $ hg rebase -d 6 -r "4::"
738 $ hg rebase -d 6 -r "4::"
739 rebasing 4:ff2c4d47b71d "C"
739 rebasing 4:ff2c4d47b71d "C"
740 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
740 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
741 rebasing 8:8d47583e023f "P" (tip)
741 rebasing 8:8d47583e023f "P" (tip)
742
742
743 If all the changeset to be rebased are obsolete and present in the destination, we
743 If all the changeset to be rebased are obsolete and present in the destination, we
744 should display a friendly error message
744 should display a friendly error message
745
745
746 $ hg log -G
746 $ hg log -G
747 @ 10:121d9e3bc4c6 P
747 @ 10:121d9e3bc4c6 P
748 |
748 |
749 o 9:4be60e099a77 C
749 o 9:4be60e099a77 C
750 |
750 |
751 o 6:9c48361117de D
751 o 6:9c48361117de D
752 |
752 |
753 o 2:261e70097290 B2
753 o 2:261e70097290 B2
754 |
754 |
755 o 0:4a2df7238c3b A
755 o 0:4a2df7238c3b A
756
756
757
757
758 $ hg up 9
758 $ hg up 9
759 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
759 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
760 $ echo "non-relevant change" > nonrelevant
760 $ echo "non-relevant change" > nonrelevant
761 $ hg add nonrelevant
761 $ hg add nonrelevant
762 $ hg commit -m nonrelevant
762 $ hg commit -m nonrelevant
763 created new head
763 created new head
764 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.stabilization=all
764 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.stabilization=all
765 obsoleted 1 changesets
765 obsoleted 1 changesets
766 $ hg rebase -r . -d 10
766 $ hg rebase -r . -d 10
767 note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor
767 note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor
768
768
769 If a rebase is going to create divergence, it should abort
769 If a rebase is going to create divergence, it should abort
770
770
771 $ hg log -G
771 $ hg log -G
772 @ 10:121d9e3bc4c6 P
772 @ 10:121d9e3bc4c6 P
773 |
773 |
774 o 9:4be60e099a77 C
774 o 9:4be60e099a77 C
775 |
775 |
776 o 6:9c48361117de D
776 o 6:9c48361117de D
777 |
777 |
778 o 2:261e70097290 B2
778 o 2:261e70097290 B2
779 |
779 |
780 o 0:4a2df7238c3b A
780 o 0:4a2df7238c3b A
781
781
782
782
783 $ hg up 9
783 $ hg up 9
784 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
784 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
785 $ echo "john" > doe
785 $ echo "john" > doe
786 $ hg add doe
786 $ hg add doe
787 $ hg commit -m "john doe"
787 $ hg commit -m "john doe"
788 created new head
788 created new head
789 $ hg up 10
789 $ hg up 10
790 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
790 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
791 $ echo "foo" > bar
791 $ echo "foo" > bar
792 $ hg add bar
792 $ hg add bar
793 $ hg commit --amend -m "10'"
793 $ hg commit --amend -m "10'"
794 $ hg up 10 --hidden
794 $ hg up 10 --hidden
795 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
795 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
796 $ echo "bar" > foo
796 $ echo "bar" > foo
797 $ hg add foo
797 $ hg add foo
798 $ hg commit -m "bar foo"
798 $ hg commit -m "bar foo"
799 $ hg log -G
799 $ hg log -G
800 @ 14:73568ab6879d bar foo
800 @ 14:73568ab6879d bar foo
801 |
801 |
802 | o 13:77d874d096a2 10'
802 | o 13:77d874d096a2 10'
803 | |
803 | |
804 | | o 12:3eb461388009 john doe
804 | | o 12:3eb461388009 john doe
805 | |/
805 | |/
806 x | 10:121d9e3bc4c6 P
806 x | 10:121d9e3bc4c6 P
807 |/
807 |/
808 o 9:4be60e099a77 C
808 o 9:4be60e099a77 C
809 |
809 |
810 o 6:9c48361117de D
810 o 6:9c48361117de D
811 |
811 |
812 o 2:261e70097290 B2
812 o 2:261e70097290 B2
813 |
813 |
814 o 0:4a2df7238c3b A
814 o 0:4a2df7238c3b A
815
815
816 $ hg summary
816 $ hg summary
817 parent: 14:73568ab6879d tip (orphan)
817 parent: 14:73568ab6879d tip (orphan)
818 bar foo
818 bar foo
819 branch: default
819 branch: default
820 commit: (clean)
820 commit: (clean)
821 update: 2 new changesets, 3 branch heads (merge)
821 update: 2 new changesets, 3 branch heads (merge)
822 phases: 8 draft
822 phases: 8 draft
823 orphan: 1 changesets
823 orphan: 1 changesets
824 $ hg rebase -s 10 -d 12
824 $ hg rebase -s 10 -d 12
825 abort: this rebase will cause divergences from: 121d9e3bc4c6
825 abort: this rebase will cause divergences from: 121d9e3bc4c6
826 (to force the rebase please set experimental.allowdivergence=True)
826 (to force the rebase please set experimental.allowdivergence=True)
827 [255]
827 [255]
828 $ hg log -G
828 $ hg log -G
829 @ 14:73568ab6879d bar foo
829 @ 14:73568ab6879d bar foo
830 |
830 |
831 | o 13:77d874d096a2 10'
831 | o 13:77d874d096a2 10'
832 | |
832 | |
833 | | o 12:3eb461388009 john doe
833 | | o 12:3eb461388009 john doe
834 | |/
834 | |/
835 x | 10:121d9e3bc4c6 P
835 x | 10:121d9e3bc4c6 P
836 |/
836 |/
837 o 9:4be60e099a77 C
837 o 9:4be60e099a77 C
838 |
838 |
839 o 6:9c48361117de D
839 o 6:9c48361117de D
840 |
840 |
841 o 2:261e70097290 B2
841 o 2:261e70097290 B2
842 |
842 |
843 o 0:4a2df7238c3b A
843 o 0:4a2df7238c3b A
844
844
845 With experimental.allowdivergence=True, rebase can create divergence
845 With experimental.allowdivergence=True, rebase can create divergence
846
846
847 $ hg rebase -s 10 -d 12 --config experimental.allowdivergence=True
847 $ hg rebase -s 10 -d 12 --config experimental.allowdivergence=True
848 rebasing 10:121d9e3bc4c6 "P"
848 rebasing 10:121d9e3bc4c6 "P"
849 rebasing 14:73568ab6879d "bar foo" (tip)
849 rebasing 14:73568ab6879d "bar foo" (tip)
850 $ hg summary
850 $ hg summary
851 parent: 16:61bd55f69bc4 tip
851 parent: 16:61bd55f69bc4 tip
852 bar foo
852 bar foo
853 branch: default
853 branch: default
854 commit: (clean)
854 commit: (clean)
855 update: 1 new changesets, 2 branch heads (merge)
855 update: 1 new changesets, 2 branch heads (merge)
856 phases: 8 draft
856 phases: 8 draft
857 content-divergent: 2 changesets
857 content-divergent: 2 changesets
858
858
859 rebase --continue + skipped rev because their successors are in destination
859 rebase --continue + skipped rev because their successors are in destination
860 we make a change in trunk and work on conflicting changes to make rebase abort.
860 we make a change in trunk and work on conflicting changes to make rebase abort.
861
861
862 $ hg log -G -r 16::
862 $ hg log -G -r 16::
863 @ 16:61bd55f69bc4 bar foo
863 @ 16:61bd55f69bc4 bar foo
864 |
864 |
865 ~
865 ~
866
866
867 Create the two changes in trunk
867 Create the two changes in trunk
868 $ printf "a" > willconflict
868 $ printf "a" > willconflict
869 $ hg add willconflict
869 $ hg add willconflict
870 $ hg commit -m "willconflict first version"
870 $ hg commit -m "willconflict first version"
871
871
872 $ printf "dummy" > C
872 $ printf "dummy" > C
873 $ hg commit -m "dummy change successor"
873 $ hg commit -m "dummy change successor"
874
874
875 Create the changes that we will rebase
875 Create the changes that we will rebase
876 $ hg update -C 16 -q
876 $ hg update -C 16 -q
877 $ printf "b" > willconflict
877 $ printf "b" > willconflict
878 $ hg add willconflict
878 $ hg add willconflict
879 $ hg commit -m "willconflict second version"
879 $ hg commit -m "willconflict second version"
880 created new head
880 created new head
881 $ printf "dummy" > K
881 $ printf "dummy" > K
882 $ hg add K
882 $ hg add K
883 $ hg commit -m "dummy change"
883 $ hg commit -m "dummy change"
884 $ printf "dummy" > L
884 $ printf "dummy" > L
885 $ hg add L
885 $ hg add L
886 $ hg commit -m "dummy change"
886 $ hg commit -m "dummy change"
887 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 18 -T '{node}'` --config experimental.stabilization=all
887 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 18 -T '{node}'` --config experimental.stabilization=all
888 obsoleted 1 changesets
888 obsoleted 1 changesets
889
889
890 $ hg log -G -r 16::
890 $ hg log -G -r 16::
891 @ 21:7bdc8a87673d dummy change
891 @ 21:7bdc8a87673d dummy change
892 |
892 |
893 x 20:8b31da3c4919 dummy change
893 x 20:8b31da3c4919 dummy change
894 |
894 |
895 o 19:b82fb57ea638 willconflict second version
895 o 19:b82fb57ea638 willconflict second version
896 |
896 |
897 | o 18:601db7a18f51 dummy change successor
897 | o 18:601db7a18f51 dummy change successor
898 | |
898 | |
899 | o 17:357ddf1602d5 willconflict first version
899 | o 17:357ddf1602d5 willconflict first version
900 |/
900 |/
901 o 16:61bd55f69bc4 bar foo
901 o 16:61bd55f69bc4 bar foo
902 |
902 |
903 ~
903 ~
904 $ hg rebase -r ".^^ + .^ + ." -d 18
904 $ hg rebase -r ".^^ + .^ + ." -d 18
905 rebasing 19:b82fb57ea638 "willconflict second version"
905 rebasing 19:b82fb57ea638 "willconflict second version"
906 merging willconflict
906 merging willconflict
907 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
907 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
908 unresolved conflicts (see hg resolve, then hg rebase --continue)
908 unresolved conflicts (see hg resolve, then hg rebase --continue)
909 [1]
909 [1]
910
910
911 $ hg resolve --mark willconflict
911 $ hg resolve --mark willconflict
912 (no more unresolved files)
912 (no more unresolved files)
913 continue: hg rebase --continue
913 continue: hg rebase --continue
914 $ hg rebase --continue
914 $ hg rebase --continue
915 rebasing 19:b82fb57ea638 "willconflict second version"
915 rebasing 19:b82fb57ea638 "willconflict second version"
916 note: not rebasing 20:8b31da3c4919 "dummy change", already in destination as 18:601db7a18f51 "dummy change successor"
916 note: not rebasing 20:8b31da3c4919 "dummy change", already in destination as 18:601db7a18f51 "dummy change successor"
917 rebasing 21:7bdc8a87673d "dummy change" (tip)
917 rebasing 21:7bdc8a87673d "dummy change" (tip)
918 $ cd ..
918 $ cd ..
919
919
920 Rebase merge where successor of one parent is equal to destination (issue5198)
920 Rebase merge where successor of one parent is equal to destination (issue5198)
921
921
922 $ hg init p1-succ-is-dest
922 $ hg init p1-succ-is-dest
923 $ cd p1-succ-is-dest
923 $ cd p1-succ-is-dest
924
924
925 $ hg debugdrawdag <<EOF
925 $ hg debugdrawdag <<EOF
926 > F
926 > F
927 > /|
927 > /|
928 > E D B # replace: D -> B
928 > E D B # replace: D -> B
929 > \|/
929 > \|/
930 > A
930 > A
931 > EOF
931 > EOF
932
932
933 $ hg rebase -d B -s D
933 $ hg rebase -d B -s D
934 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
934 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
935 rebasing 4:66f1a38021c9 "F" (F tip)
935 rebasing 4:66f1a38021c9 "F" (F tip)
936 $ hg log -G
936 $ hg log -G
937 o 5:50e9d60b99c6 F
937 o 5:50e9d60b99c6 F
938 |\
938 |\
939 | | x 4:66f1a38021c9 F
939 | | x 4:66f1a38021c9 F
940 | |/|
940 | |/|
941 | o | 3:7fb047a69f22 E
941 | o | 3:7fb047a69f22 E
942 | | |
942 | | |
943 | | x 2:b18e25de2cf5 D
943 | | x 2:b18e25de2cf5 D
944 | |/
944 | |/
945 o | 1:112478962961 B
945 o | 1:112478962961 B
946 |/
946 |/
947 o 0:426bada5c675 A
947 o 0:426bada5c675 A
948
948
949 $ cd ..
949 $ cd ..
950
950
951 Rebase merge where successor of other parent is equal to destination
951 Rebase merge where successor of other parent is equal to destination
952
952
953 $ hg init p2-succ-is-dest
953 $ hg init p2-succ-is-dest
954 $ cd p2-succ-is-dest
954 $ cd p2-succ-is-dest
955
955
956 $ hg debugdrawdag <<EOF
956 $ hg debugdrawdag <<EOF
957 > F
957 > F
958 > /|
958 > /|
959 > E D B # replace: E -> B
959 > E D B # replace: E -> B
960 > \|/
960 > \|/
961 > A
961 > A
962 > EOF
962 > EOF
963
963
964 $ hg rebase -d B -s E
964 $ hg rebase -d B -s E
965 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
965 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
966 rebasing 4:66f1a38021c9 "F" (F tip)
966 rebasing 4:66f1a38021c9 "F" (F tip)
967 $ hg log -G
967 $ hg log -G
968 o 5:aae1787dacee F
968 o 5:aae1787dacee F
969 |\
969 |\
970 | | x 4:66f1a38021c9 F
970 | | x 4:66f1a38021c9 F
971 | |/|
971 | |/|
972 | | x 3:7fb047a69f22 E
972 | | x 3:7fb047a69f22 E
973 | | |
973 | | |
974 | o | 2:b18e25de2cf5 D
974 | o | 2:b18e25de2cf5 D
975 | |/
975 | |/
976 o / 1:112478962961 B
976 o / 1:112478962961 B
977 |/
977 |/
978 o 0:426bada5c675 A
978 o 0:426bada5c675 A
979
979
980 $ cd ..
980 $ cd ..
981
981
982 Rebase merge where successor of one parent is ancestor of destination
982 Rebase merge where successor of one parent is ancestor of destination
983
983
984 $ hg init p1-succ-in-dest
984 $ hg init p1-succ-in-dest
985 $ cd p1-succ-in-dest
985 $ cd p1-succ-in-dest
986
986
987 $ hg debugdrawdag <<EOF
987 $ hg debugdrawdag <<EOF
988 > F C
988 > F C
989 > /| |
989 > /| |
990 > E D B # replace: D -> B
990 > E D B # replace: D -> B
991 > \|/
991 > \|/
992 > A
992 > A
993 > EOF
993 > EOF
994
994
995 $ hg rebase -d C -s D
995 $ hg rebase -d C -s D
996 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
996 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
997 rebasing 5:66f1a38021c9 "F" (F tip)
997 rebasing 5:66f1a38021c9 "F" (F tip)
998
998
999 $ hg log -G
999 $ hg log -G
1000 o 6:0913febf6439 F
1000 o 6:0913febf6439 F
1001 |\
1001 |\
1002 +---x 5:66f1a38021c9 F
1002 +---x 5:66f1a38021c9 F
1003 | | |
1003 | | |
1004 | o | 4:26805aba1e60 C
1004 | o | 4:26805aba1e60 C
1005 | | |
1005 | | |
1006 o | | 3:7fb047a69f22 E
1006 o | | 3:7fb047a69f22 E
1007 | | |
1007 | | |
1008 +---x 2:b18e25de2cf5 D
1008 +---x 2:b18e25de2cf5 D
1009 | |
1009 | |
1010 | o 1:112478962961 B
1010 | o 1:112478962961 B
1011 |/
1011 |/
1012 o 0:426bada5c675 A
1012 o 0:426bada5c675 A
1013
1013
1014 $ cd ..
1014 $ cd ..
1015
1015
1016 Rebase merge where successor of other parent is ancestor of destination
1016 Rebase merge where successor of other parent is ancestor of destination
1017
1017
1018 $ hg init p2-succ-in-dest
1018 $ hg init p2-succ-in-dest
1019 $ cd p2-succ-in-dest
1019 $ cd p2-succ-in-dest
1020
1020
1021 $ hg debugdrawdag <<EOF
1021 $ hg debugdrawdag <<EOF
1022 > F C
1022 > F C
1023 > /| |
1023 > /| |
1024 > E D B # replace: E -> B
1024 > E D B # replace: E -> B
1025 > \|/
1025 > \|/
1026 > A
1026 > A
1027 > EOF
1027 > EOF
1028
1028
1029 $ hg rebase -d C -s E
1029 $ hg rebase -d C -s E
1030 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1030 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1031 rebasing 5:66f1a38021c9 "F" (F tip)
1031 rebasing 5:66f1a38021c9 "F" (F tip)
1032 $ hg log -G
1032 $ hg log -G
1033 o 6:c6ab0cc6d220 F
1033 o 6:c6ab0cc6d220 F
1034 |\
1034 |\
1035 +---x 5:66f1a38021c9 F
1035 +---x 5:66f1a38021c9 F
1036 | | |
1036 | | |
1037 | o | 4:26805aba1e60 C
1037 | o | 4:26805aba1e60 C
1038 | | |
1038 | | |
1039 | | x 3:7fb047a69f22 E
1039 | | x 3:7fb047a69f22 E
1040 | | |
1040 | | |
1041 o---+ 2:b18e25de2cf5 D
1041 o---+ 2:b18e25de2cf5 D
1042 / /
1042 / /
1043 o / 1:112478962961 B
1043 o / 1:112478962961 B
1044 |/
1044 |/
1045 o 0:426bada5c675 A
1045 o 0:426bada5c675 A
1046
1046
1047 $ cd ..
1047 $ cd ..
1048
1048
1049 Rebase merge where successor of one parent is ancestor of destination
1049 Rebase merge where successor of one parent is ancestor of destination
1050
1050
1051 $ hg init p1-succ-in-dest-b
1051 $ hg init p1-succ-in-dest-b
1052 $ cd p1-succ-in-dest-b
1052 $ cd p1-succ-in-dest-b
1053
1053
1054 $ hg debugdrawdag <<EOF
1054 $ hg debugdrawdag <<EOF
1055 > F C
1055 > F C
1056 > /| |
1056 > /| |
1057 > E D B # replace: E -> B
1057 > E D B # replace: E -> B
1058 > \|/
1058 > \|/
1059 > A
1059 > A
1060 > EOF
1060 > EOF
1061
1061
1062 $ hg rebase -d C -b F
1062 $ hg rebase -d C -b F
1063 rebasing 2:b18e25de2cf5 "D" (D)
1063 rebasing 2:b18e25de2cf5 "D" (D)
1064 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1064 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B" (B)
1065 rebasing 5:66f1a38021c9 "F" (F tip)
1065 rebasing 5:66f1a38021c9 "F" (F tip)
1066 note: rebase of 5:66f1a38021c9 created no changes to commit
1066 note: rebase of 5:66f1a38021c9 created no changes to commit
1067 $ hg log -G
1067 $ hg log -G
1068 o 6:8f47515dda15 D
1068 o 6:8f47515dda15 D
1069 |
1069 |
1070 | x 5:66f1a38021c9 F
1070 | x 5:66f1a38021c9 F
1071 | |\
1071 | |\
1072 o | | 4:26805aba1e60 C
1072 o | | 4:26805aba1e60 C
1073 | | |
1073 | | |
1074 | | x 3:7fb047a69f22 E
1074 | | x 3:7fb047a69f22 E
1075 | | |
1075 | | |
1076 | x | 2:b18e25de2cf5 D
1076 | x | 2:b18e25de2cf5 D
1077 | |/
1077 | |/
1078 o / 1:112478962961 B
1078 o / 1:112478962961 B
1079 |/
1079 |/
1080 o 0:426bada5c675 A
1080 o 0:426bada5c675 A
1081
1081
1082 $ cd ..
1082 $ cd ..
1083
1083
1084 Rebase merge where successor of other parent is ancestor of destination
1084 Rebase merge where successor of other parent is ancestor of destination
1085
1085
1086 $ hg init p2-succ-in-dest-b
1086 $ hg init p2-succ-in-dest-b
1087 $ cd p2-succ-in-dest-b
1087 $ cd p2-succ-in-dest-b
1088
1088
1089 $ hg debugdrawdag <<EOF
1089 $ hg debugdrawdag <<EOF
1090 > F C
1090 > F C
1091 > /| |
1091 > /| |
1092 > E D B # replace: D -> B
1092 > E D B # replace: D -> B
1093 > \|/
1093 > \|/
1094 > A
1094 > A
1095 > EOF
1095 > EOF
1096
1096
1097 $ hg rebase -d C -b F
1097 $ hg rebase -d C -b F
1098 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1098 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B" (B)
1099 rebasing 3:7fb047a69f22 "E" (E)
1099 rebasing 3:7fb047a69f22 "E" (E)
1100 rebasing 5:66f1a38021c9 "F" (F tip)
1100 rebasing 5:66f1a38021c9 "F" (F tip)
1101 note: rebase of 5:66f1a38021c9 created no changes to commit
1101 note: rebase of 5:66f1a38021c9 created no changes to commit
1102
1102
1103 $ hg log -G
1103 $ hg log -G
1104 o 6:533690786a86 E
1104 o 6:533690786a86 E
1105 |
1105 |
1106 | x 5:66f1a38021c9 F
1106 | x 5:66f1a38021c9 F
1107 | |\
1107 | |\
1108 o | | 4:26805aba1e60 C
1108 o | | 4:26805aba1e60 C
1109 | | |
1109 | | |
1110 | | x 3:7fb047a69f22 E
1110 | | x 3:7fb047a69f22 E
1111 | | |
1111 | | |
1112 | x | 2:b18e25de2cf5 D
1112 | x | 2:b18e25de2cf5 D
1113 | |/
1113 | |/
1114 o / 1:112478962961 B
1114 o / 1:112478962961 B
1115 |/
1115 |/
1116 o 0:426bada5c675 A
1116 o 0:426bada5c675 A
1117
1117
1118 $ cd ..
1118 $ cd ..
1119
1119
1120 Rebase merge where both parents have successors in destination
1120 Rebase merge where both parents have successors in destination
1121
1121
1122 $ hg init p12-succ-in-dest
1122 $ hg init p12-succ-in-dest
1123 $ cd p12-succ-in-dest
1123 $ cd p12-succ-in-dest
1124 $ hg debugdrawdag <<'EOS'
1124 $ hg debugdrawdag <<'EOS'
1125 > E F
1125 > E F
1126 > /| /| # replace: A -> C
1126 > /| /| # replace: A -> C
1127 > A B C D # replace: B -> D
1127 > A B C D # replace: B -> D
1128 > | |
1128 > | |
1129 > X Y
1129 > X Y
1130 > EOS
1130 > EOS
1131 $ hg rebase -r A+B+E -d F
1131 $ hg rebase -r A+B+E -d F
1132 note: not rebasing 4:a3d17304151f "A" (A), already in destination as 0:96cc3511f894 "C" (C)
1132 note: not rebasing 4:a3d17304151f "A" (A), already in destination as 0:96cc3511f894 "C" (C)
1133 note: not rebasing 5:b23a2cc00842 "B" (B), already in destination as 1:058c1e1fb10a "D" (D)
1133 note: not rebasing 5:b23a2cc00842 "B" (B), already in destination as 1:058c1e1fb10a "D" (D)
1134 rebasing 7:dac5d11c5a7d "E" (E tip)
1134 rebasing 7:dac5d11c5a7d "E" (E tip)
1135 abort: rebasing 7:dac5d11c5a7d will include unwanted changes from 3:59c792af609c, 5:b23a2cc00842 or 2:ba2b7fa7166d, 4:a3d17304151f
1135 abort: rebasing 7:dac5d11c5a7d will include unwanted changes from 3:59c792af609c, 5:b23a2cc00842 or 2:ba2b7fa7166d, 4:a3d17304151f
1136 [255]
1136 [255]
1137 $ cd ..
1137 $ cd ..
1138
1138
1139 Rebase a non-clean merge. One parent has successor in destination, the other
1139 Rebase a non-clean merge. One parent has successor in destination, the other
1140 parent moves as requested.
1140 parent moves as requested.
1141
1141
1142 $ hg init p1-succ-p2-move
1142 $ hg init p1-succ-p2-move
1143 $ cd p1-succ-p2-move
1143 $ cd p1-succ-p2-move
1144 $ hg debugdrawdag <<'EOS'
1144 $ hg debugdrawdag <<'EOS'
1145 > D Z
1145 > D Z
1146 > /| | # replace: A -> C
1146 > /| | # replace: A -> C
1147 > A B C # D/D = D
1147 > A B C # D/D = D
1148 > EOS
1148 > EOS
1149 $ hg rebase -r A+B+D -d Z
1149 $ hg rebase -r A+B+D -d Z
1150 note: not rebasing 0:426bada5c675 "A" (A), already in destination as 2:96cc3511f894 "C" (C)
1150 note: not rebasing 0:426bada5c675 "A" (A), already in destination as 2:96cc3511f894 "C" (C)
1151 rebasing 1:fc2b737bb2e5 "B" (B)
1151 rebasing 1:fc2b737bb2e5 "B" (B)
1152 rebasing 3:b8ed089c80ad "D" (D)
1152 rebasing 3:b8ed089c80ad "D" (D)
1153
1153
1154 $ rm .hg/localtags
1154 $ rm .hg/localtags
1155 $ hg log -G
1155 $ hg log -G
1156 o 6:e4f78693cc88 D
1156 o 6:e4f78693cc88 D
1157 |
1157 |
1158 o 5:76840d832e98 B
1158 o 5:76840d832e98 B
1159 |
1159 |
1160 o 4:50e41c1f3950 Z
1160 o 4:50e41c1f3950 Z
1161 |
1161 |
1162 o 2:96cc3511f894 C
1162 o 2:96cc3511f894 C
1163
1163
1164 $ hg files -r tip
1164 $ hg files -r tip
1165 B
1165 B
1166 C
1166 C
1167 D
1167 D
1168 Z
1168 Z
1169
1169
1170 $ cd ..
1170 $ cd ..
1171
1171
1172 $ hg init p1-move-p2-succ
1172 $ hg init p1-move-p2-succ
1173 $ cd p1-move-p2-succ
1173 $ cd p1-move-p2-succ
1174 $ hg debugdrawdag <<'EOS'
1174 $ hg debugdrawdag <<'EOS'
1175 > D Z
1175 > D Z
1176 > /| | # replace: B -> C
1176 > /| | # replace: B -> C
1177 > A B C # D/D = D
1177 > A B C # D/D = D
1178 > EOS
1178 > EOS
1179 $ hg rebase -r B+A+D -d Z
1179 $ hg rebase -r B+A+D -d Z
1180 rebasing 0:426bada5c675 "A" (A)
1180 rebasing 0:426bada5c675 "A" (A)
1181 note: not rebasing 1:fc2b737bb2e5 "B" (B), already in destination as 2:96cc3511f894 "C" (C)
1181 note: not rebasing 1:fc2b737bb2e5 "B" (B), already in destination as 2:96cc3511f894 "C" (C)
1182 rebasing 3:b8ed089c80ad "D" (D)
1182 rebasing 3:b8ed089c80ad "D" (D)
1183
1183
1184 $ rm .hg/localtags
1184 $ rm .hg/localtags
1185 $ hg log -G
1185 $ hg log -G
1186 o 6:1b355ed94d82 D
1186 o 6:1b355ed94d82 D
1187 |
1187 |
1188 o 5:a81a74d764a6 A
1188 o 5:a81a74d764a6 A
1189 |
1189 |
1190 o 4:50e41c1f3950 Z
1190 o 4:50e41c1f3950 Z
1191 |
1191 |
1192 o 2:96cc3511f894 C
1192 o 2:96cc3511f894 C
1193
1193
1194 $ hg files -r tip
1194 $ hg files -r tip
1195 A
1195 A
1196 C
1196 C
1197 D
1197 D
1198 Z
1198 Z
1199
1199
1200 $ cd ..
1200 $ cd ..
1201
1201
1202 Test that bookmark is moved and working dir is updated when all changesets have
1202 Test that bookmark is moved and working dir is updated when all changesets have
1203 equivalents in destination
1203 equivalents in destination
1204 $ hg init rbsrepo && cd rbsrepo
1204 $ hg init rbsrepo && cd rbsrepo
1205 $ echo "[experimental]" > .hg/hgrc
1205 $ echo "[experimental]" > .hg/hgrc
1206 $ echo "stabilization=all" >> .hg/hgrc
1206 $ echo "stabilization=all" >> .hg/hgrc
1207 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1207 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1208 $ echo root > root && hg ci -Am root
1208 $ echo root > root && hg ci -Am root
1209 adding root
1209 adding root
1210 $ echo a > a && hg ci -Am a
1210 $ echo a > a && hg ci -Am a
1211 adding a
1211 adding a
1212 $ hg up 0
1212 $ hg up 0
1213 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1213 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1214 $ echo b > b && hg ci -Am b
1214 $ echo b > b && hg ci -Am b
1215 adding b
1215 adding b
1216 created new head
1216 created new head
1217 $ hg rebase -r 2 -d 1
1217 $ hg rebase -r 2 -d 1
1218 rebasing 2:1e9a3c00cbe9 "b" (tip)
1218 rebasing 2:1e9a3c00cbe9 "b" (tip)
1219 $ hg log -r . # working dir is at rev 3 (successor of 2)
1219 $ hg log -r . # working dir is at rev 3 (successor of 2)
1220 3:be1832deae9a b (no-eol)
1220 3:be1832deae9a b (no-eol)
1221 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1221 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1222 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1222 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1223 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1223 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1224 2:1e9a3c00cbe9 b (no-eol)
1224 2:1e9a3c00cbe9 b (no-eol)
1225 $ hg rebase -r 2 -d 3 --config experimental.stabilization.track-operation=1
1225 $ hg rebase -r 2 -d 3 --config experimental.stabilization.track-operation=1
1226 note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b" (tip)
1226 note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b" (tip)
1227 Check that working directory and bookmark was updated to rev 3 although rev 2
1227 Check that working directory and bookmark was updated to rev 3 although rev 2
1228 was skipped
1228 was skipped
1229 $ hg log -r .
1229 $ hg log -r .
1230 3:be1832deae9a b (no-eol)
1230 3:be1832deae9a b (no-eol)
1231 $ hg bookmarks
1231 $ hg bookmarks
1232 mybook 3:be1832deae9a
1232 mybook 3:be1832deae9a
1233 $ hg debugobsolete --rev tip
1233 $ hg debugobsolete --rev tip
1234 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (*) {'user': 'test'} (glob)
1234 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (*) {'user': 'test'} (glob)
1235
1235
1236 Obsoleted working parent and bookmark could be moved if an ancestor of working
1236 Obsoleted working parent and bookmark could be moved if an ancestor of working
1237 parent gets moved:
1237 parent gets moved:
1238
1238
1239 $ hg init $TESTTMP/ancestor-wd-move
1239 $ hg init $TESTTMP/ancestor-wd-move
1240 $ cd $TESTTMP/ancestor-wd-move
1240 $ cd $TESTTMP/ancestor-wd-move
1241 $ hg debugdrawdag <<'EOS'
1241 $ hg debugdrawdag <<'EOS'
1242 > E D1 # rebase: D1 -> D2
1242 > E D1 # rebase: D1 -> D2
1243 > | |
1243 > | |
1244 > | C
1244 > | C
1245 > D2 |
1245 > D2 |
1246 > | B
1246 > | B
1247 > |/
1247 > |/
1248 > A
1248 > A
1249 > EOS
1249 > EOS
1250 $ hg update D1 -q
1250 $ hg update D1 -q
1251 $ hg bookmark book -i
1251 $ hg bookmark book -i
1252 $ hg rebase -r B+D1 -d E
1252 $ hg rebase -r B+D1 -d E
1253 rebasing 1:112478962961 "B" (B)
1253 rebasing 1:112478962961 "B" (B)
1254 note: not rebasing 5:15ecf15e0114 "D1" (D1 tip book), already in destination as 2:0807738e0be9 "D2" (D2)
1254 note: not rebasing 5:15ecf15e0114 "D1" (D1 tip book), already in destination as 2:0807738e0be9 "D2" (D2)
1255 $ hg log -G -T '{desc} {bookmarks}'
1255 $ hg log -G -T '{desc} {bookmarks}'
1256 @ B book
1256 @ B book
1257 |
1257 |
1258 | x D1
1258 | x D1
1259 | |
1259 | |
1260 o | E
1260 o | E
1261 | |
1261 | |
1262 | o C
1262 | o C
1263 | |
1263 | |
1264 o | D2
1264 o | D2
1265 | |
1265 | |
1266 | x B
1266 | x B
1267 |/
1267 |/
1268 o A
1268 o A
1269
1269
1270 Rebasing a merge with one of its parent having a hidden successor
1271
1272 $ hg init $TESTTMP/merge-p1-hidden-successor
1273 $ cd $TESTTMP/merge-p1-hidden-successor
1274
1275 $ hg debugdrawdag <<'EOS'
1276 > E
1277 > |
1278 > B3 B2 # amend: B1 -> B2 -> B3
1279 > |/ # B2 is hidden
1280 > | D
1281 > | |\
1282 > | B1 C
1283 > |/
1284 > A
1285 > EOS
1286
1287 $ eval `hg tags -T '{tag}={node}\n'`
1288 $ rm .hg/localtags
1289
1290 $ hg rebase -r $D -d $E
1291 rebasing 5:9e62094e4d94 "D"
1292
1293 $ hg log -G
1294 o 7:a699d059adcf D
1295 |\
1296 | o 6:ecc93090a95c E
1297 | |
1298 | o 4:0dc878468a23 B3
1299 | |
1300 o | 1:96cc3511f894 C
1301 /
1302 o 0:426bada5c675 A
1303
General Comments 0
You need to be logged in to leave comments. Login now