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