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