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