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