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