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