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