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