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