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