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