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