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