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