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