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