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