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