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