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