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