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