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