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