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