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