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