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