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