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