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