##// END OF EJS Templates
rebase: use cmdutil.check_at_most_one_arg() for -b/-s/-r...
Martin von Zweigbergk -
r44381:08b8bf4a default
parent child Browse files
Show More
@@ -1,2306 +1,2301 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_at_most_one_arg(opts, b'confirm', b'dry_run')
1027 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')
1028
1029
1029 if action or repo.currenttransaction() is not None:
1030 if action or repo.currenttransaction() is not None:
1030 # in-memory rebase is not compatible with resuming rebases.
1031 # in-memory rebase is not compatible with resuming rebases.
1031 # (Or if it is run within a transaction, since the restart logic can
1032 # (Or if it is run within a transaction, since the restart logic can
1032 # fail the entire transaction.)
1033 # fail the entire transaction.)
1033 inmemory = False
1034 inmemory = False
1034
1035
1035 if opts.get(b'auto_orphans'):
1036 if opts.get(b'auto_orphans'):
1036 for key in opts:
1037 for key in opts:
1037 if key != b'auto_orphans' and opts.get(key):
1038 if key != b'auto_orphans' and opts.get(key):
1038 raise error.Abort(
1039 raise error.Abort(
1039 _(b'--auto-orphans is incompatible with %s') % (b'--' + key)
1040 _(b'--auto-orphans is incompatible with %s') % (b'--' + key)
1040 )
1041 )
1041 userrevs = list(repo.revs(opts.get(b'auto_orphans')))
1042 userrevs = list(repo.revs(opts.get(b'auto_orphans')))
1042 opts[b'rev'] = [revsetlang.formatspec(b'%ld and orphan()', userrevs)]
1043 opts[b'rev'] = [revsetlang.formatspec(b'%ld and orphan()', userrevs)]
1043 opts[b'dest'] = b'_destautoorphanrebase(SRC)'
1044 opts[b'dest'] = b'_destautoorphanrebase(SRC)'
1044
1045
1045 if opts.get(b'dry_run') or opts.get(b'confirm'):
1046 if opts.get(b'dry_run') or opts.get(b'confirm'):
1046 return _dryrunrebase(ui, repo, action, opts)
1047 return _dryrunrebase(ui, repo, action, opts)
1047 elif action == b'stop':
1048 elif action == b'stop':
1048 rbsrt = rebaseruntime(repo, ui)
1049 rbsrt = rebaseruntime(repo, ui)
1049 with repo.wlock(), repo.lock():
1050 with repo.wlock(), repo.lock():
1050 rbsrt.restorestatus()
1051 rbsrt.restorestatus()
1051 if rbsrt.collapsef:
1052 if rbsrt.collapsef:
1052 raise error.Abort(_(b"cannot stop in --collapse session"))
1053 raise error.Abort(_(b"cannot stop in --collapse session"))
1053 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1054 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1054 if not (rbsrt.keepf or allowunstable):
1055 if not (rbsrt.keepf or allowunstable):
1055 raise error.Abort(
1056 raise error.Abort(
1056 _(
1057 _(
1057 b"cannot remove original changesets with"
1058 b"cannot remove original changesets with"
1058 b" unrebased descendants"
1059 b" unrebased descendants"
1059 ),
1060 ),
1060 hint=_(
1061 hint=_(
1061 b'either enable obsmarkers to allow unstable '
1062 b'either enable obsmarkers to allow unstable '
1062 b'revisions or use --keep to keep original '
1063 b'revisions or use --keep to keep original '
1063 b'changesets'
1064 b'changesets'
1064 ),
1065 ),
1065 )
1066 )
1066 if needupdate(repo, rbsrt.state):
1067 if needupdate(repo, rbsrt.state):
1067 # update to the current working revision
1068 # update to the current working revision
1068 # to clear interrupted merge
1069 # to clear interrupted merge
1069 hg.updaterepo(repo, rbsrt.originalwd, overwrite=True)
1070 hg.updaterepo(repo, rbsrt.originalwd, overwrite=True)
1070 rbsrt._finishrebase()
1071 rbsrt._finishrebase()
1071 return 0
1072 return 0
1072 elif inmemory:
1073 elif inmemory:
1073 try:
1074 try:
1074 # in-memory merge doesn't support conflicts, so if we hit any, abort
1075 # in-memory merge doesn't support conflicts, so if we hit any, abort
1075 # and re-run as an on-disk merge.
1076 # and re-run as an on-disk merge.
1076 overrides = {(b'rebase', b'singletransaction'): True}
1077 overrides = {(b'rebase', b'singletransaction'): True}
1077 with ui.configoverride(overrides, b'rebase'):
1078 with ui.configoverride(overrides, b'rebase'):
1078 return _dorebase(ui, repo, action, opts, inmemory=inmemory)
1079 return _dorebase(ui, repo, action, opts, inmemory=inmemory)
1079 except error.InMemoryMergeConflictsError:
1080 except error.InMemoryMergeConflictsError:
1080 ui.warn(
1081 ui.warn(
1081 _(
1082 _(
1082 b'hit merge conflicts; re-running rebase without in-memory'
1083 b'hit merge conflicts; re-running rebase without in-memory'
1083 b' merge\n'
1084 b' merge\n'
1084 )
1085 )
1085 )
1086 )
1086 # TODO: Make in-memory merge not use the on-disk merge state, so
1087 # TODO: Make in-memory merge not use the on-disk merge state, so
1087 # we don't have to clean it here
1088 # we don't have to clean it here
1088 mergemod.mergestate.clean(repo)
1089 mergemod.mergestate.clean(repo)
1089 clearstatus(repo)
1090 clearstatus(repo)
1090 clearcollapsemsg(repo)
1091 clearcollapsemsg(repo)
1091 return _dorebase(ui, repo, action, opts, inmemory=False)
1092 return _dorebase(ui, repo, action, opts, inmemory=False)
1092 else:
1093 else:
1093 return _dorebase(ui, repo, action, opts)
1094 return _dorebase(ui, repo, action, opts)
1094
1095
1095
1096
1096 def _dryrunrebase(ui, repo, action, opts):
1097 def _dryrunrebase(ui, repo, action, opts):
1097 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
1098 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
1098 confirm = opts.get(b'confirm')
1099 confirm = opts.get(b'confirm')
1099 if confirm:
1100 if confirm:
1100 ui.status(_(b'starting in-memory rebase\n'))
1101 ui.status(_(b'starting in-memory rebase\n'))
1101 else:
1102 else:
1102 ui.status(
1103 ui.status(
1103 _(b'starting dry-run rebase; repository will not be changed\n')
1104 _(b'starting dry-run rebase; repository will not be changed\n')
1104 )
1105 )
1105 with repo.wlock(), repo.lock():
1106 with repo.wlock(), repo.lock():
1106 needsabort = True
1107 needsabort = True
1107 try:
1108 try:
1108 overrides = {(b'rebase', b'singletransaction'): True}
1109 overrides = {(b'rebase', b'singletransaction'): True}
1109 with ui.configoverride(overrides, b'rebase'):
1110 with ui.configoverride(overrides, b'rebase'):
1110 _origrebase(
1111 _origrebase(
1111 ui,
1112 ui,
1112 repo,
1113 repo,
1113 action,
1114 action,
1114 opts,
1115 opts,
1115 rbsrt,
1116 rbsrt,
1116 inmemory=True,
1117 inmemory=True,
1117 leaveunfinished=True,
1118 leaveunfinished=True,
1118 )
1119 )
1119 except error.InMemoryMergeConflictsError:
1120 except error.InMemoryMergeConflictsError:
1120 ui.status(_(b'hit a merge conflict\n'))
1121 ui.status(_(b'hit a merge conflict\n'))
1121 return 1
1122 return 1
1122 except error.Abort:
1123 except error.Abort:
1123 needsabort = False
1124 needsabort = False
1124 raise
1125 raise
1125 else:
1126 else:
1126 if confirm:
1127 if confirm:
1127 ui.status(_(b'rebase completed successfully\n'))
1128 ui.status(_(b'rebase completed successfully\n'))
1128 if not ui.promptchoice(_(b'apply changes (yn)?$$ &Yes $$ &No')):
1129 if not ui.promptchoice(_(b'apply changes (yn)?$$ &Yes $$ &No')):
1129 # finish unfinished rebase
1130 # finish unfinished rebase
1130 rbsrt._finishrebase()
1131 rbsrt._finishrebase()
1131 else:
1132 else:
1132 rbsrt._prepareabortorcontinue(
1133 rbsrt._prepareabortorcontinue(
1133 isabort=True, backup=False, suppwarns=True
1134 isabort=True, backup=False, suppwarns=True
1134 )
1135 )
1135 needsabort = False
1136 needsabort = False
1136 else:
1137 else:
1137 ui.status(
1138 ui.status(
1138 _(
1139 _(
1139 b'dry-run rebase completed successfully; run without'
1140 b'dry-run rebase completed successfully; run without'
1140 b' -n/--dry-run to perform this rebase\n'
1141 b' -n/--dry-run to perform this rebase\n'
1141 )
1142 )
1142 )
1143 )
1143 return 0
1144 return 0
1144 finally:
1145 finally:
1145 if needsabort:
1146 if needsabort:
1146 # no need to store backup in case of dryrun
1147 # no need to store backup in case of dryrun
1147 rbsrt._prepareabortorcontinue(
1148 rbsrt._prepareabortorcontinue(
1148 isabort=True, backup=False, suppwarns=True
1149 isabort=True, backup=False, suppwarns=True
1149 )
1150 )
1150
1151
1151
1152
1152 def _dorebase(ui, repo, action, opts, inmemory=False):
1153 def _dorebase(ui, repo, action, opts, inmemory=False):
1153 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
1154 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
1154 return _origrebase(ui, repo, action, opts, rbsrt, inmemory=inmemory)
1155 return _origrebase(ui, repo, action, opts, rbsrt, inmemory=inmemory)
1155
1156
1156
1157
1157 def _origrebase(
1158 def _origrebase(
1158 ui, repo, action, opts, rbsrt, inmemory=False, leaveunfinished=False
1159 ui, repo, action, opts, rbsrt, inmemory=False, leaveunfinished=False
1159 ):
1160 ):
1160 assert action != b'stop'
1161 assert action != b'stop'
1161 with repo.wlock(), repo.lock():
1162 with repo.wlock(), repo.lock():
1162 # Validate input and define rebasing points
1163 # Validate input and define rebasing points
1163 destf = opts.get(b'dest', None)
1164 destf = opts.get(b'dest', None)
1164 srcf = opts.get(b'source', None)
1165 srcf = opts.get(b'source', None)
1165 basef = opts.get(b'base', None)
1166 basef = opts.get(b'base', None)
1166 revf = opts.get(b'rev', [])
1167 revf = opts.get(b'rev', [])
1167 # search default destination in this space
1168 # search default destination in this space
1168 # used in the 'hg pull --rebase' case, see issue 5214.
1169 # used in the 'hg pull --rebase' case, see issue 5214.
1169 destspace = opts.get(b'_destspace')
1170 destspace = opts.get(b'_destspace')
1170 if opts.get(b'interactive'):
1171 if opts.get(b'interactive'):
1171 try:
1172 try:
1172 if extensions.find(b'histedit'):
1173 if extensions.find(b'histedit'):
1173 enablehistedit = b''
1174 enablehistedit = b''
1174 except KeyError:
1175 except KeyError:
1175 enablehistedit = b" --config extensions.histedit="
1176 enablehistedit = b" --config extensions.histedit="
1176 help = b"hg%s help -e histedit" % enablehistedit
1177 help = b"hg%s help -e histedit" % enablehistedit
1177 msg = (
1178 msg = (
1178 _(
1179 _(
1179 b"interactive history editing is supported by the "
1180 b"interactive history editing is supported by the "
1180 b"'histedit' extension (see \"%s\")"
1181 b"'histedit' extension (see \"%s\")"
1181 )
1182 )
1182 % help
1183 % help
1183 )
1184 )
1184 raise error.Abort(msg)
1185 raise error.Abort(msg)
1185
1186
1186 if rbsrt.collapsemsg and not rbsrt.collapsef:
1187 if rbsrt.collapsemsg and not rbsrt.collapsef:
1187 raise error.Abort(_(b'message can only be specified with collapse'))
1188 raise error.Abort(_(b'message can only be specified with collapse'))
1188
1189
1189 if action:
1190 if action:
1190 if rbsrt.collapsef:
1191 if rbsrt.collapsef:
1191 raise error.Abort(
1192 raise error.Abort(
1192 _(b'cannot use collapse with continue or abort')
1193 _(b'cannot use collapse with continue or abort')
1193 )
1194 )
1194 if srcf or basef or destf:
1195 if srcf or basef or destf:
1195 raise error.Abort(
1196 raise error.Abort(
1196 _(b'abort and continue do not allow specifying revisions')
1197 _(b'abort and continue do not allow specifying revisions')
1197 )
1198 )
1198 if action == b'abort' and opts.get(b'tool', False):
1199 if action == b'abort' and opts.get(b'tool', False):
1199 ui.warn(_(b'tool option will be ignored\n'))
1200 ui.warn(_(b'tool option will be ignored\n'))
1200 if action == b'continue':
1201 if action == b'continue':
1201 ms = mergemod.mergestate.read(repo)
1202 ms = mergemod.mergestate.read(repo)
1202 mergeutil.checkunresolved(ms)
1203 mergeutil.checkunresolved(ms)
1203
1204
1204 retcode = rbsrt._prepareabortorcontinue(
1205 retcode = rbsrt._prepareabortorcontinue(
1205 isabort=(action == b'abort')
1206 isabort=(action == b'abort')
1206 )
1207 )
1207 if retcode is not None:
1208 if retcode is not None:
1208 return retcode
1209 return retcode
1209 else:
1210 else:
1210 destmap = _definedestmap(
1211 destmap = _definedestmap(
1211 ui,
1212 ui,
1212 repo,
1213 repo,
1213 inmemory,
1214 inmemory,
1214 destf,
1215 destf,
1215 srcf,
1216 srcf,
1216 basef,
1217 basef,
1217 revf,
1218 revf,
1218 destspace=destspace,
1219 destspace=destspace,
1219 )
1220 )
1220 retcode = rbsrt._preparenewrebase(destmap)
1221 retcode = rbsrt._preparenewrebase(destmap)
1221 if retcode is not None:
1222 if retcode is not None:
1222 return retcode
1223 return retcode
1223 storecollapsemsg(repo, rbsrt.collapsemsg)
1224 storecollapsemsg(repo, rbsrt.collapsemsg)
1224
1225
1225 tr = None
1226 tr = None
1226
1227
1227 singletr = ui.configbool(b'rebase', b'singletransaction')
1228 singletr = ui.configbool(b'rebase', b'singletransaction')
1228 if singletr:
1229 if singletr:
1229 tr = repo.transaction(b'rebase')
1230 tr = repo.transaction(b'rebase')
1230
1231
1231 # If `rebase.singletransaction` is enabled, wrap the entire operation in
1232 # If `rebase.singletransaction` is enabled, wrap the entire operation in
1232 # one transaction here. Otherwise, transactions are obtained when
1233 # one transaction here. Otherwise, transactions are obtained when
1233 # committing each node, which is slower but allows partial success.
1234 # committing each node, which is slower but allows partial success.
1234 with util.acceptintervention(tr):
1235 with util.acceptintervention(tr):
1235 # Same logic for the dirstate guard, except we don't create one when
1236 # Same logic for the dirstate guard, except we don't create one when
1236 # rebasing in-memory (it's not needed).
1237 # rebasing in-memory (it's not needed).
1237 dsguard = None
1238 dsguard = None
1238 if singletr and not inmemory:
1239 if singletr and not inmemory:
1239 dsguard = dirstateguard.dirstateguard(repo, b'rebase')
1240 dsguard = dirstateguard.dirstateguard(repo, b'rebase')
1240 with util.acceptintervention(dsguard):
1241 with util.acceptintervention(dsguard):
1241 rbsrt._performrebase(tr)
1242 rbsrt._performrebase(tr)
1242 if not leaveunfinished:
1243 if not leaveunfinished:
1243 rbsrt._finishrebase()
1244 rbsrt._finishrebase()
1244
1245
1245
1246
1246 def _definedestmap(
1247 def _definedestmap(
1247 ui,
1248 ui,
1248 repo,
1249 repo,
1249 inmemory,
1250 inmemory,
1250 destf=None,
1251 destf=None,
1251 srcf=None,
1252 srcf=None,
1252 basef=None,
1253 basef=None,
1253 revf=None,
1254 revf=None,
1254 destspace=None,
1255 destspace=None,
1255 ):
1256 ):
1256 """use revisions argument to define destmap {srcrev: destrev}"""
1257 """use revisions argument to define destmap {srcrev: destrev}"""
1257 if revf is None:
1258 if revf is None:
1258 revf = []
1259 revf = []
1259
1260
1260 # destspace is here to work around issues with `hg pull --rebase` see
1261 # destspace is here to work around issues with `hg pull --rebase` see
1261 # issue5214 for details
1262 # issue5214 for details
1262 if srcf and basef:
1263 raise error.Abort(_(b'cannot specify both a source and a base'))
1264 if revf and basef:
1265 raise error.Abort(_(b'cannot specify both a revision and a base'))
1266 if revf and srcf:
1267 raise error.Abort(_(b'cannot specify both a revision and a source'))
1268
1263
1269 cmdutil.checkunfinished(repo)
1264 cmdutil.checkunfinished(repo)
1270 if not inmemory:
1265 if not inmemory:
1271 cmdutil.bailifchanged(repo)
1266 cmdutil.bailifchanged(repo)
1272
1267
1273 if ui.configbool(b'commands', b'rebase.requiredest') and not destf:
1268 if ui.configbool(b'commands', b'rebase.requiredest') and not destf:
1274 raise error.Abort(
1269 raise error.Abort(
1275 _(b'you must specify a destination'),
1270 _(b'you must specify a destination'),
1276 hint=_(b'use: hg rebase -d REV'),
1271 hint=_(b'use: hg rebase -d REV'),
1277 )
1272 )
1278
1273
1279 dest = None
1274 dest = None
1280
1275
1281 if revf:
1276 if revf:
1282 rebaseset = scmutil.revrange(repo, revf)
1277 rebaseset = scmutil.revrange(repo, revf)
1283 if not rebaseset:
1278 if not rebaseset:
1284 ui.status(_(b'empty "rev" revision set - nothing to rebase\n'))
1279 ui.status(_(b'empty "rev" revision set - nothing to rebase\n'))
1285 return None
1280 return None
1286 elif srcf:
1281 elif srcf:
1287 src = scmutil.revrange(repo, [srcf])
1282 src = scmutil.revrange(repo, [srcf])
1288 if not src:
1283 if not src:
1289 ui.status(_(b'empty "source" revision set - nothing to rebase\n'))
1284 ui.status(_(b'empty "source" revision set - nothing to rebase\n'))
1290 return None
1285 return None
1291 rebaseset = repo.revs(b'(%ld)::', src)
1286 rebaseset = repo.revs(b'(%ld)::', src)
1292 assert rebaseset
1287 assert rebaseset
1293 else:
1288 else:
1294 base = scmutil.revrange(repo, [basef or b'.'])
1289 base = scmutil.revrange(repo, [basef or b'.'])
1295 if not base:
1290 if not base:
1296 ui.status(
1291 ui.status(
1297 _(b'empty "base" revision set - ' b"can't compute rebase set\n")
1292 _(b'empty "base" revision set - ' b"can't compute rebase set\n")
1298 )
1293 )
1299 return None
1294 return None
1300 if destf:
1295 if destf:
1301 # --base does not support multiple destinations
1296 # --base does not support multiple destinations
1302 dest = scmutil.revsingle(repo, destf)
1297 dest = scmutil.revsingle(repo, destf)
1303 else:
1298 else:
1304 dest = repo[_destrebase(repo, base, destspace=destspace)]
1299 dest = repo[_destrebase(repo, base, destspace=destspace)]
1305 destf = bytes(dest)
1300 destf = bytes(dest)
1306
1301
1307 roots = [] # selected children of branching points
1302 roots = [] # selected children of branching points
1308 bpbase = {} # {branchingpoint: [origbase]}
1303 bpbase = {} # {branchingpoint: [origbase]}
1309 for b in base: # group bases by branching points
1304 for b in base: # group bases by branching points
1310 bp = repo.revs(b'ancestor(%d, %d)', b, dest.rev()).first()
1305 bp = repo.revs(b'ancestor(%d, %d)', b, dest.rev()).first()
1311 bpbase[bp] = bpbase.get(bp, []) + [b]
1306 bpbase[bp] = bpbase.get(bp, []) + [b]
1312 if None in bpbase:
1307 if None in bpbase:
1313 # emulate the old behavior, showing "nothing to rebase" (a better
1308 # emulate the old behavior, showing "nothing to rebase" (a better
1314 # behavior may be abort with "cannot find branching point" error)
1309 # behavior may be abort with "cannot find branching point" error)
1315 bpbase.clear()
1310 bpbase.clear()
1316 for bp, bs in pycompat.iteritems(bpbase): # calculate roots
1311 for bp, bs in pycompat.iteritems(bpbase): # calculate roots
1317 roots += list(repo.revs(b'children(%d) & ancestors(%ld)', bp, bs))
1312 roots += list(repo.revs(b'children(%d) & ancestors(%ld)', bp, bs))
1318
1313
1319 rebaseset = repo.revs(b'%ld::', roots)
1314 rebaseset = repo.revs(b'%ld::', roots)
1320
1315
1321 if not rebaseset:
1316 if not rebaseset:
1322 # transform to list because smartsets are not comparable to
1317 # transform to list because smartsets are not comparable to
1323 # lists. This should be improved to honor laziness of
1318 # lists. This should be improved to honor laziness of
1324 # smartset.
1319 # smartset.
1325 if list(base) == [dest.rev()]:
1320 if list(base) == [dest.rev()]:
1326 if basef:
1321 if basef:
1327 ui.status(
1322 ui.status(
1328 _(
1323 _(
1329 b'nothing to rebase - %s is both "base"'
1324 b'nothing to rebase - %s is both "base"'
1330 b' and destination\n'
1325 b' and destination\n'
1331 )
1326 )
1332 % dest
1327 % dest
1333 )
1328 )
1334 else:
1329 else:
1335 ui.status(
1330 ui.status(
1336 _(
1331 _(
1337 b'nothing to rebase - working directory '
1332 b'nothing to rebase - working directory '
1338 b'parent is also destination\n'
1333 b'parent is also destination\n'
1339 )
1334 )
1340 )
1335 )
1341 elif not repo.revs(b'%ld - ::%d', base, dest.rev()):
1336 elif not repo.revs(b'%ld - ::%d', base, dest.rev()):
1342 if basef:
1337 if basef:
1343 ui.status(
1338 ui.status(
1344 _(
1339 _(
1345 b'nothing to rebase - "base" %s is '
1340 b'nothing to rebase - "base" %s is '
1346 b'already an ancestor of destination '
1341 b'already an ancestor of destination '
1347 b'%s\n'
1342 b'%s\n'
1348 )
1343 )
1349 % (b'+'.join(bytes(repo[r]) for r in base), dest)
1344 % (b'+'.join(bytes(repo[r]) for r in base), dest)
1350 )
1345 )
1351 else:
1346 else:
1352 ui.status(
1347 ui.status(
1353 _(
1348 _(
1354 b'nothing to rebase - working '
1349 b'nothing to rebase - working '
1355 b'directory parent is already an '
1350 b'directory parent is already an '
1356 b'ancestor of destination %s\n'
1351 b'ancestor of destination %s\n'
1357 )
1352 )
1358 % dest
1353 % dest
1359 )
1354 )
1360 else: # can it happen?
1355 else: # can it happen?
1361 ui.status(
1356 ui.status(
1362 _(b'nothing to rebase from %s to %s\n')
1357 _(b'nothing to rebase from %s to %s\n')
1363 % (b'+'.join(bytes(repo[r]) for r in base), dest)
1358 % (b'+'.join(bytes(repo[r]) for r in base), dest)
1364 )
1359 )
1365 return None
1360 return None
1366
1361
1367 rebasingwcp = repo[b'.'].rev() in rebaseset
1362 rebasingwcp = repo[b'.'].rev() in rebaseset
1368 ui.log(
1363 ui.log(
1369 b"rebase",
1364 b"rebase",
1370 b"rebasing working copy parent: %r\n",
1365 b"rebasing working copy parent: %r\n",
1371 rebasingwcp,
1366 rebasingwcp,
1372 rebase_rebasing_wcp=rebasingwcp,
1367 rebase_rebasing_wcp=rebasingwcp,
1373 )
1368 )
1374 if inmemory and rebasingwcp:
1369 if inmemory and rebasingwcp:
1375 # Check these since we did not before.
1370 # Check these since we did not before.
1376 cmdutil.checkunfinished(repo)
1371 cmdutil.checkunfinished(repo)
1377 cmdutil.bailifchanged(repo)
1372 cmdutil.bailifchanged(repo)
1378
1373
1379 if not destf:
1374 if not destf:
1380 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
1375 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
1381 destf = bytes(dest)
1376 destf = bytes(dest)
1382
1377
1383 allsrc = revsetlang.formatspec(b'%ld', rebaseset)
1378 allsrc = revsetlang.formatspec(b'%ld', rebaseset)
1384 alias = {b'ALLSRC': allsrc}
1379 alias = {b'ALLSRC': allsrc}
1385
1380
1386 if dest is None:
1381 if dest is None:
1387 try:
1382 try:
1388 # fast path: try to resolve dest without SRC alias
1383 # fast path: try to resolve dest without SRC alias
1389 dest = scmutil.revsingle(repo, destf, localalias=alias)
1384 dest = scmutil.revsingle(repo, destf, localalias=alias)
1390 except error.RepoLookupError:
1385 except error.RepoLookupError:
1391 # multi-dest path: resolve dest for each SRC separately
1386 # multi-dest path: resolve dest for each SRC separately
1392 destmap = {}
1387 destmap = {}
1393 for r in rebaseset:
1388 for r in rebaseset:
1394 alias[b'SRC'] = revsetlang.formatspec(b'%d', r)
1389 alias[b'SRC'] = revsetlang.formatspec(b'%d', r)
1395 # use repo.anyrevs instead of scmutil.revsingle because we
1390 # use repo.anyrevs instead of scmutil.revsingle because we
1396 # don't want to abort if destset is empty.
1391 # don't want to abort if destset is empty.
1397 destset = repo.anyrevs([destf], user=True, localalias=alias)
1392 destset = repo.anyrevs([destf], user=True, localalias=alias)
1398 size = len(destset)
1393 size = len(destset)
1399 if size == 1:
1394 if size == 1:
1400 destmap[r] = destset.first()
1395 destmap[r] = destset.first()
1401 elif size == 0:
1396 elif size == 0:
1402 ui.note(_(b'skipping %s - empty destination\n') % repo[r])
1397 ui.note(_(b'skipping %s - empty destination\n') % repo[r])
1403 else:
1398 else:
1404 raise error.Abort(
1399 raise error.Abort(
1405 _(b'rebase destination for %s is not unique') % repo[r]
1400 _(b'rebase destination for %s is not unique') % repo[r]
1406 )
1401 )
1407
1402
1408 if dest is not None:
1403 if dest is not None:
1409 # single-dest case: assign dest to each rev in rebaseset
1404 # single-dest case: assign dest to each rev in rebaseset
1410 destrev = dest.rev()
1405 destrev = dest.rev()
1411 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1406 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1412
1407
1413 if not destmap:
1408 if not destmap:
1414 ui.status(_(b'nothing to rebase - empty destination\n'))
1409 ui.status(_(b'nothing to rebase - empty destination\n'))
1415 return None
1410 return None
1416
1411
1417 return destmap
1412 return destmap
1418
1413
1419
1414
1420 def externalparent(repo, state, destancestors):
1415 def externalparent(repo, state, destancestors):
1421 """Return the revision that should be used as the second parent
1416 """Return the revision that should be used as the second parent
1422 when the revisions in state is collapsed on top of destancestors.
1417 when the revisions in state is collapsed on top of destancestors.
1423 Abort if there is more than one parent.
1418 Abort if there is more than one parent.
1424 """
1419 """
1425 parents = set()
1420 parents = set()
1426 source = min(state)
1421 source = min(state)
1427 for rev in state:
1422 for rev in state:
1428 if rev == source:
1423 if rev == source:
1429 continue
1424 continue
1430 for p in repo[rev].parents():
1425 for p in repo[rev].parents():
1431 if p.rev() not in state and p.rev() not in destancestors:
1426 if p.rev() not in state and p.rev() not in destancestors:
1432 parents.add(p.rev())
1427 parents.add(p.rev())
1433 if not parents:
1428 if not parents:
1434 return nullrev
1429 return nullrev
1435 if len(parents) == 1:
1430 if len(parents) == 1:
1436 return parents.pop()
1431 return parents.pop()
1437 raise error.Abort(
1432 raise error.Abort(
1438 _(
1433 _(
1439 b'unable to collapse on top of %d, there is more '
1434 b'unable to collapse on top of %d, there is more '
1440 b'than one external parent: %s'
1435 b'than one external parent: %s'
1441 )
1436 )
1442 % (max(destancestors), b', '.join(b"%d" % p for p in sorted(parents)))
1437 % (max(destancestors), b', '.join(b"%d" % p for p in sorted(parents)))
1443 )
1438 )
1444
1439
1445
1440
1446 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg):
1441 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg):
1447 '''Commit the memory changes with parents p1 and p2.
1442 '''Commit the memory changes with parents p1 and p2.
1448 Return node of committed revision.'''
1443 Return node of committed revision.'''
1449 # Replicates the empty check in ``repo.commit``.
1444 # Replicates the empty check in ``repo.commit``.
1450 if wctx.isempty() and not repo.ui.configbool(b'ui', b'allowemptycommit'):
1445 if wctx.isempty() and not repo.ui.configbool(b'ui', b'allowemptycommit'):
1451 return None
1446 return None
1452
1447
1453 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1448 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1454 # ``branch`` (used when passing ``--keepbranches``).
1449 # ``branch`` (used when passing ``--keepbranches``).
1455 branch = repo[p1].branch()
1450 branch = repo[p1].branch()
1456 if b'branch' in extra:
1451 if b'branch' in extra:
1457 branch = extra[b'branch']
1452 branch = extra[b'branch']
1458
1453
1459 memctx = wctx.tomemctx(
1454 memctx = wctx.tomemctx(
1460 commitmsg,
1455 commitmsg,
1461 parents=(p1, p2),
1456 parents=(p1, p2),
1462 date=date,
1457 date=date,
1463 extra=extra,
1458 extra=extra,
1464 user=user,
1459 user=user,
1465 branch=branch,
1460 branch=branch,
1466 editor=editor,
1461 editor=editor,
1467 )
1462 )
1468 commitres = repo.commitctx(memctx)
1463 commitres = repo.commitctx(memctx)
1469 wctx.clean() # Might be reused
1464 wctx.clean() # Might be reused
1470 return commitres
1465 return commitres
1471
1466
1472
1467
1473 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg):
1468 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg):
1474 '''Commit the wd changes with parents p1 and p2.
1469 '''Commit the wd changes with parents p1 and p2.
1475 Return node of committed revision.'''
1470 Return node of committed revision.'''
1476 dsguard = util.nullcontextmanager()
1471 dsguard = util.nullcontextmanager()
1477 if not repo.ui.configbool(b'rebase', b'singletransaction'):
1472 if not repo.ui.configbool(b'rebase', b'singletransaction'):
1478 dsguard = dirstateguard.dirstateguard(repo, b'rebase')
1473 dsguard = dirstateguard.dirstateguard(repo, b'rebase')
1479 with dsguard:
1474 with dsguard:
1480 repo.setparents(repo[p1].node(), repo[p2].node())
1475 repo.setparents(repo[p1].node(), repo[p2].node())
1481
1476
1482 # Commit might fail if unresolved files exist
1477 # Commit might fail if unresolved files exist
1483 newnode = repo.commit(
1478 newnode = repo.commit(
1484 text=commitmsg, user=user, date=date, extra=extra, editor=editor
1479 text=commitmsg, user=user, date=date, extra=extra, editor=editor
1485 )
1480 )
1486
1481
1487 repo.dirstate.setbranch(repo[newnode].branch())
1482 repo.dirstate.setbranch(repo[newnode].branch())
1488 return newnode
1483 return newnode
1489
1484
1490
1485
1491 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1486 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1492 """Rebase a single revision rev on top of p1 using base as merge ancestor"""
1487 """Rebase a single revision rev on top of p1 using base as merge ancestor"""
1493 # Merge phase
1488 # Merge phase
1494 # Update to destination and merge it with local
1489 # Update to destination and merge it with local
1495 if wctx.isinmemory():
1490 if wctx.isinmemory():
1496 wctx.setbase(repo[p1])
1491 wctx.setbase(repo[p1])
1497 else:
1492 else:
1498 if repo[b'.'].rev() != p1:
1493 if repo[b'.'].rev() != p1:
1499 repo.ui.debug(b" update to %d:%s\n" % (p1, repo[p1]))
1494 repo.ui.debug(b" update to %d:%s\n" % (p1, repo[p1]))
1500 mergemod.update(repo, p1, branchmerge=False, force=True)
1495 mergemod.update(repo, p1, branchmerge=False, force=True)
1501 else:
1496 else:
1502 repo.ui.debug(b" already in destination\n")
1497 repo.ui.debug(b" already in destination\n")
1503 # This is, alas, necessary to invalidate workingctx's manifest cache,
1498 # This is, alas, necessary to invalidate workingctx's manifest cache,
1504 # as well as other data we litter on it in other places.
1499 # as well as other data we litter on it in other places.
1505 wctx = repo[None]
1500 wctx = repo[None]
1506 repo.dirstate.write(repo.currenttransaction())
1501 repo.dirstate.write(repo.currenttransaction())
1507 repo.ui.debug(b" merge against %d:%s\n" % (rev, repo[rev]))
1502 repo.ui.debug(b" merge against %d:%s\n" % (rev, repo[rev]))
1508 if base is not None:
1503 if base is not None:
1509 repo.ui.debug(b" detach base %d:%s\n" % (base, repo[base]))
1504 repo.ui.debug(b" detach base %d:%s\n" % (base, repo[base]))
1510 # When collapsing in-place, the parent is the common ancestor, we
1505 # When collapsing in-place, the parent is the common ancestor, we
1511 # have to allow merging with it.
1506 # have to allow merging with it.
1512 stats = mergemod.update(
1507 stats = mergemod.update(
1513 repo,
1508 repo,
1514 rev,
1509 rev,
1515 branchmerge=True,
1510 branchmerge=True,
1516 force=True,
1511 force=True,
1517 ancestor=base,
1512 ancestor=base,
1518 mergeancestor=collapse,
1513 mergeancestor=collapse,
1519 labels=[b'dest', b'source'],
1514 labels=[b'dest', b'source'],
1520 wc=wctx,
1515 wc=wctx,
1521 )
1516 )
1522 if collapse:
1517 if collapse:
1523 copies.duplicatecopies(repo, wctx, rev, dest)
1518 copies.duplicatecopies(repo, wctx, rev, dest)
1524 else:
1519 else:
1525 # If we're not using --collapse, we need to
1520 # If we're not using --collapse, we need to
1526 # duplicate copies between the revision we're
1521 # duplicate copies between the revision we're
1527 # rebasing and its first parent, but *not*
1522 # rebasing and its first parent, but *not*
1528 # duplicate any copies that have already been
1523 # duplicate any copies that have already been
1529 # performed in the destination.
1524 # performed in the destination.
1530 p1rev = repo[rev].p1().rev()
1525 p1rev = repo[rev].p1().rev()
1531 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1526 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1532 return stats
1527 return stats
1533
1528
1534
1529
1535 def adjustdest(repo, rev, destmap, state, skipped):
1530 def adjustdest(repo, rev, destmap, state, skipped):
1536 r"""adjust rebase destination given the current rebase state
1531 r"""adjust rebase destination given the current rebase state
1537
1532
1538 rev is what is being rebased. Return a list of two revs, which are the
1533 rev is what is being rebased. Return a list of two revs, which are the
1539 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1534 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1540 nullrev, return dest without adjustment for it.
1535 nullrev, return dest without adjustment for it.
1541
1536
1542 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1537 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1543 to B1, and E's destination will be adjusted from F to B1.
1538 to B1, and E's destination will be adjusted from F to B1.
1544
1539
1545 B1 <- written during rebasing B
1540 B1 <- written during rebasing B
1546 |
1541 |
1547 F <- original destination of B, E
1542 F <- original destination of B, E
1548 |
1543 |
1549 | E <- rev, which is being rebased
1544 | E <- rev, which is being rebased
1550 | |
1545 | |
1551 | D <- prev, one parent of rev being checked
1546 | D <- prev, one parent of rev being checked
1552 | |
1547 | |
1553 | x <- skipped, ex. no successor or successor in (::dest)
1548 | x <- skipped, ex. no successor or successor in (::dest)
1554 | |
1549 | |
1555 | C <- rebased as C', different destination
1550 | C <- rebased as C', different destination
1556 | |
1551 | |
1557 | B <- rebased as B1 C'
1552 | B <- rebased as B1 C'
1558 |/ |
1553 |/ |
1559 A G <- destination of C, different
1554 A G <- destination of C, different
1560
1555
1561 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1556 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1562 first move C to C1, G to G1, and when it's checking H, the adjusted
1557 first move C to C1, G to G1, and when it's checking H, the adjusted
1563 destinations will be [C1, G1].
1558 destinations will be [C1, G1].
1564
1559
1565 H C1 G1
1560 H C1 G1
1566 /| | /
1561 /| | /
1567 F G |/
1562 F G |/
1568 K | | -> K
1563 K | | -> K
1569 | C D |
1564 | C D |
1570 | |/ |
1565 | |/ |
1571 | B | ...
1566 | B | ...
1572 |/ |/
1567 |/ |/
1573 A A
1568 A A
1574
1569
1575 Besides, adjust dest according to existing rebase information. For example,
1570 Besides, adjust dest according to existing rebase information. For example,
1576
1571
1577 B C D B needs to be rebased on top of C, C needs to be rebased on top
1572 B C D B needs to be rebased on top of C, C needs to be rebased on top
1578 \|/ of D. We will rebase C first.
1573 \|/ of D. We will rebase C first.
1579 A
1574 A
1580
1575
1581 C' After rebasing C, when considering B's destination, use C'
1576 C' After rebasing C, when considering B's destination, use C'
1582 | instead of the original C.
1577 | instead of the original C.
1583 B D
1578 B D
1584 \ /
1579 \ /
1585 A
1580 A
1586 """
1581 """
1587 # pick already rebased revs with same dest from state as interesting source
1582 # pick already rebased revs with same dest from state as interesting source
1588 dest = destmap[rev]
1583 dest = destmap[rev]
1589 source = [
1584 source = [
1590 s
1585 s
1591 for s, d in state.items()
1586 for s, d in state.items()
1592 if d > 0 and destmap[s] == dest and s not in skipped
1587 if d > 0 and destmap[s] == dest and s not in skipped
1593 ]
1588 ]
1594
1589
1595 result = []
1590 result = []
1596 for prev in repo.changelog.parentrevs(rev):
1591 for prev in repo.changelog.parentrevs(rev):
1597 adjusted = dest
1592 adjusted = dest
1598 if prev != nullrev:
1593 if prev != nullrev:
1599 candidate = repo.revs(b'max(%ld and (::%d))', source, prev).first()
1594 candidate = repo.revs(b'max(%ld and (::%d))', source, prev).first()
1600 if candidate is not None:
1595 if candidate is not None:
1601 adjusted = state[candidate]
1596 adjusted = state[candidate]
1602 if adjusted == dest and dest in state:
1597 if adjusted == dest and dest in state:
1603 adjusted = state[dest]
1598 adjusted = state[dest]
1604 if adjusted == revtodo:
1599 if adjusted == revtodo:
1605 # sortsource should produce an order that makes this impossible
1600 # sortsource should produce an order that makes this impossible
1606 raise error.ProgrammingError(
1601 raise error.ProgrammingError(
1607 b'rev %d should be rebased already at this time' % dest
1602 b'rev %d should be rebased already at this time' % dest
1608 )
1603 )
1609 result.append(adjusted)
1604 result.append(adjusted)
1610 return result
1605 return result
1611
1606
1612
1607
1613 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1608 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1614 """
1609 """
1615 Abort if rebase will create divergence or rebase is noop because of markers
1610 Abort if rebase will create divergence or rebase is noop because of markers
1616
1611
1617 `rebaseobsrevs`: set of obsolete revision in source
1612 `rebaseobsrevs`: set of obsolete revision in source
1618 `rebaseobsskipped`: set of revisions from source skipped because they have
1613 `rebaseobsskipped`: set of revisions from source skipped because they have
1619 successors in destination or no non-obsolete successor.
1614 successors in destination or no non-obsolete successor.
1620 """
1615 """
1621 # Obsolete node with successors not in dest leads to divergence
1616 # Obsolete node with successors not in dest leads to divergence
1622 divergenceok = ui.configbool(b'experimental', b'evolution.allowdivergence')
1617 divergenceok = ui.configbool(b'experimental', b'evolution.allowdivergence')
1623 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1618 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1624
1619
1625 if divergencebasecandidates and not divergenceok:
1620 if divergencebasecandidates and not divergenceok:
1626 divhashes = (bytes(repo[r]) for r in divergencebasecandidates)
1621 divhashes = (bytes(repo[r]) for r in divergencebasecandidates)
1627 msg = _(b"this rebase will cause divergences from: %s")
1622 msg = _(b"this rebase will cause divergences from: %s")
1628 h = _(
1623 h = _(
1629 b"to force the rebase please set "
1624 b"to force the rebase please set "
1630 b"experimental.evolution.allowdivergence=True"
1625 b"experimental.evolution.allowdivergence=True"
1631 )
1626 )
1632 raise error.Abort(msg % (b",".join(divhashes),), hint=h)
1627 raise error.Abort(msg % (b",".join(divhashes),), hint=h)
1633
1628
1634
1629
1635 def successorrevs(unfi, rev):
1630 def successorrevs(unfi, rev):
1636 """yield revision numbers for successors of rev"""
1631 """yield revision numbers for successors of rev"""
1637 assert unfi.filtername is None
1632 assert unfi.filtername is None
1638 get_rev = unfi.changelog.index.get_rev
1633 get_rev = unfi.changelog.index.get_rev
1639 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1634 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1640 r = get_rev(s)
1635 r = get_rev(s)
1641 if r is not None:
1636 if r is not None:
1642 yield r
1637 yield r
1643
1638
1644
1639
1645 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1640 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1646 """Return new parents and optionally a merge base for rev being rebased
1641 """Return new parents and optionally a merge base for rev being rebased
1647
1642
1648 The destination specified by "dest" cannot always be used directly because
1643 The destination specified by "dest" cannot always be used directly because
1649 previously rebase result could affect destination. For example,
1644 previously rebase result could affect destination. For example,
1650
1645
1651 D E rebase -r C+D+E -d B
1646 D E rebase -r C+D+E -d B
1652 |/ C will be rebased to C'
1647 |/ C will be rebased to C'
1653 B C D's new destination will be C' instead of B
1648 B C D's new destination will be C' instead of B
1654 |/ E's new destination will be C' instead of B
1649 |/ E's new destination will be C' instead of B
1655 A
1650 A
1656
1651
1657 The new parents of a merge is slightly more complicated. See the comment
1652 The new parents of a merge is slightly more complicated. See the comment
1658 block below.
1653 block below.
1659 """
1654 """
1660 # use unfiltered changelog since successorrevs may return filtered nodes
1655 # use unfiltered changelog since successorrevs may return filtered nodes
1661 assert repo.filtername is None
1656 assert repo.filtername is None
1662 cl = repo.changelog
1657 cl = repo.changelog
1663 isancestor = cl.isancestorrev
1658 isancestor = cl.isancestorrev
1664
1659
1665 dest = destmap[rev]
1660 dest = destmap[rev]
1666 oldps = repo.changelog.parentrevs(rev) # old parents
1661 oldps = repo.changelog.parentrevs(rev) # old parents
1667 newps = [nullrev, nullrev] # new parents
1662 newps = [nullrev, nullrev] # new parents
1668 dests = adjustdest(repo, rev, destmap, state, skipped)
1663 dests = adjustdest(repo, rev, destmap, state, skipped)
1669 bases = list(oldps) # merge base candidates, initially just old parents
1664 bases = list(oldps) # merge base candidates, initially just old parents
1670
1665
1671 if all(r == nullrev for r in oldps[1:]):
1666 if all(r == nullrev for r in oldps[1:]):
1672 # For non-merge changeset, just move p to adjusted dest as requested.
1667 # For non-merge changeset, just move p to adjusted dest as requested.
1673 newps[0] = dests[0]
1668 newps[0] = dests[0]
1674 else:
1669 else:
1675 # For merge changeset, if we move p to dests[i] unconditionally, both
1670 # For merge changeset, if we move p to dests[i] unconditionally, both
1676 # parents may change and the end result looks like "the merge loses a
1671 # parents may change and the end result looks like "the merge loses a
1677 # parent", which is a surprise. This is a limit because "--dest" only
1672 # parent", which is a surprise. This is a limit because "--dest" only
1678 # accepts one dest per src.
1673 # accepts one dest per src.
1679 #
1674 #
1680 # Therefore, only move p with reasonable conditions (in this order):
1675 # Therefore, only move p with reasonable conditions (in this order):
1681 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1676 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1682 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1677 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1683 #
1678 #
1684 # Comparing with adjustdest, the logic here does some additional work:
1679 # Comparing with adjustdest, the logic here does some additional work:
1685 # 1. decide which parents will not be moved towards dest
1680 # 1. decide which parents will not be moved towards dest
1686 # 2. if the above decision is "no", should a parent still be moved
1681 # 2. if the above decision is "no", should a parent still be moved
1687 # because it was rebased?
1682 # because it was rebased?
1688 #
1683 #
1689 # For example:
1684 # For example:
1690 #
1685 #
1691 # C # "rebase -r C -d D" is an error since none of the parents
1686 # C # "rebase -r C -d D" is an error since none of the parents
1692 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1687 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1693 # A B D # B (using rule "2."), since B will be rebased.
1688 # A B D # B (using rule "2."), since B will be rebased.
1694 #
1689 #
1695 # The loop tries to be not rely on the fact that a Mercurial node has
1690 # The loop tries to be not rely on the fact that a Mercurial node has
1696 # at most 2 parents.
1691 # at most 2 parents.
1697 for i, p in enumerate(oldps):
1692 for i, p in enumerate(oldps):
1698 np = p # new parent
1693 np = p # new parent
1699 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1694 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1700 np = dests[i]
1695 np = dests[i]
1701 elif p in state and state[p] > 0:
1696 elif p in state and state[p] > 0:
1702 np = state[p]
1697 np = state[p]
1703
1698
1704 # "bases" only record "special" merge bases that cannot be
1699 # "bases" only record "special" merge bases that cannot be
1705 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1700 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1706 # For example:
1701 # For example:
1707 #
1702 #
1708 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1703 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1709 # | C # is B', but merge base for C is B, instead of
1704 # | C # is B', but merge base for C is B, instead of
1710 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1705 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1711 # | B # "state" edges are merged (so there will be an edge from
1706 # | B # "state" edges are merged (so there will be an edge from
1712 # |/ # B to B'), the merge base is still ancestor(C, B') in
1707 # |/ # B to B'), the merge base is still ancestor(C, B') in
1713 # A # the merged graph.
1708 # A # the merged graph.
1714 #
1709 #
1715 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1710 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1716 # which uses "virtual null merge" to explain this situation.
1711 # which uses "virtual null merge" to explain this situation.
1717 if isancestor(p, np):
1712 if isancestor(p, np):
1718 bases[i] = nullrev
1713 bases[i] = nullrev
1719
1714
1720 # If one parent becomes an ancestor of the other, drop the ancestor
1715 # If one parent becomes an ancestor of the other, drop the ancestor
1721 for j, x in enumerate(newps[:i]):
1716 for j, x in enumerate(newps[:i]):
1722 if x == nullrev:
1717 if x == nullrev:
1723 continue
1718 continue
1724 if isancestor(np, x): # CASE-1
1719 if isancestor(np, x): # CASE-1
1725 np = nullrev
1720 np = nullrev
1726 elif isancestor(x, np): # CASE-2
1721 elif isancestor(x, np): # CASE-2
1727 newps[j] = np
1722 newps[j] = np
1728 np = nullrev
1723 np = nullrev
1729 # New parents forming an ancestor relationship does not
1724 # New parents forming an ancestor relationship does not
1730 # mean the old parents have a similar relationship. Do not
1725 # mean the old parents have a similar relationship. Do not
1731 # set bases[x] to nullrev.
1726 # set bases[x] to nullrev.
1732 bases[j], bases[i] = bases[i], bases[j]
1727 bases[j], bases[i] = bases[i], bases[j]
1733
1728
1734 newps[i] = np
1729 newps[i] = np
1735
1730
1736 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1731 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1737 # base. If only p2 changes, merging using unchanged p1 as merge base is
1732 # base. If only p2 changes, merging using unchanged p1 as merge base is
1738 # suboptimal. Therefore swap parents to make the merge sane.
1733 # suboptimal. Therefore swap parents to make the merge sane.
1739 if newps[1] != nullrev and oldps[0] == newps[0]:
1734 if newps[1] != nullrev and oldps[0] == newps[0]:
1740 assert len(newps) == 2 and len(oldps) == 2
1735 assert len(newps) == 2 and len(oldps) == 2
1741 newps.reverse()
1736 newps.reverse()
1742 bases.reverse()
1737 bases.reverse()
1743
1738
1744 # No parent change might be an error because we fail to make rev a
1739 # No parent change might be an error because we fail to make rev a
1745 # descendent of requested dest. This can happen, for example:
1740 # descendent of requested dest. This can happen, for example:
1746 #
1741 #
1747 # C # rebase -r C -d D
1742 # C # rebase -r C -d D
1748 # /| # None of A and B will be changed to D and rebase fails.
1743 # /| # None of A and B will be changed to D and rebase fails.
1749 # A B D
1744 # A B D
1750 if set(newps) == set(oldps) and dest not in newps:
1745 if set(newps) == set(oldps) and dest not in newps:
1751 raise error.Abort(
1746 raise error.Abort(
1752 _(
1747 _(
1753 b'cannot rebase %d:%s without '
1748 b'cannot rebase %d:%s without '
1754 b'moving at least one of its parents'
1749 b'moving at least one of its parents'
1755 )
1750 )
1756 % (rev, repo[rev])
1751 % (rev, repo[rev])
1757 )
1752 )
1758
1753
1759 # Source should not be ancestor of dest. The check here guarantees it's
1754 # Source should not be ancestor of dest. The check here guarantees it's
1760 # impossible. With multi-dest, the initial check does not cover complex
1755 # impossible. With multi-dest, the initial check does not cover complex
1761 # cases since we don't have abstractions to dry-run rebase cheaply.
1756 # cases since we don't have abstractions to dry-run rebase cheaply.
1762 if any(p != nullrev and isancestor(rev, p) for p in newps):
1757 if any(p != nullrev and isancestor(rev, p) for p in newps):
1763 raise error.Abort(_(b'source is ancestor of destination'))
1758 raise error.Abort(_(b'source is ancestor of destination'))
1764
1759
1765 # "rebasenode" updates to new p1, use the corresponding merge base.
1760 # "rebasenode" updates to new p1, use the corresponding merge base.
1766 if bases[0] != nullrev:
1761 if bases[0] != nullrev:
1767 base = bases[0]
1762 base = bases[0]
1768 else:
1763 else:
1769 base = None
1764 base = None
1770
1765
1771 # Check if the merge will contain unwanted changes. That may happen if
1766 # Check if the merge will contain unwanted changes. That may happen if
1772 # there are multiple special (non-changelog ancestor) merge bases, which
1767 # there are multiple special (non-changelog ancestor) merge bases, which
1773 # cannot be handled well by the 3-way merge algorithm. For example:
1768 # cannot be handled well by the 3-way merge algorithm. For example:
1774 #
1769 #
1775 # F
1770 # F
1776 # /|
1771 # /|
1777 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1772 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1778 # | | # as merge base, the difference between D and F will include
1773 # | | # as merge base, the difference between D and F will include
1779 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1774 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1780 # |/ # chosen, the rebased F will contain B.
1775 # |/ # chosen, the rebased F will contain B.
1781 # A Z
1776 # A Z
1782 #
1777 #
1783 # But our merge base candidates (D and E in above case) could still be
1778 # But our merge base candidates (D and E in above case) could still be
1784 # better than the default (ancestor(F, Z) == null). Therefore still
1779 # better than the default (ancestor(F, Z) == null). Therefore still
1785 # pick one (so choose p1 above).
1780 # pick one (so choose p1 above).
1786 if sum(1 for b in set(bases) if b != nullrev) > 1:
1781 if sum(1 for b in set(bases) if b != nullrev) > 1:
1787 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1782 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1788 for i, base in enumerate(bases):
1783 for i, base in enumerate(bases):
1789 if base == nullrev:
1784 if base == nullrev:
1790 continue
1785 continue
1791 # Revisions in the side (not chosen as merge base) branch that
1786 # Revisions in the side (not chosen as merge base) branch that
1792 # might contain "surprising" contents
1787 # might contain "surprising" contents
1793 siderevs = list(
1788 siderevs = list(
1794 repo.revs(b'((%ld-%d) %% (%d+%d))', bases, base, base, dest)
1789 repo.revs(b'((%ld-%d) %% (%d+%d))', bases, base, base, dest)
1795 )
1790 )
1796
1791
1797 # If those revisions are covered by rebaseset, the result is good.
1792 # If those revisions are covered by rebaseset, the result is good.
1798 # A merge in rebaseset would be considered to cover its ancestors.
1793 # A merge in rebaseset would be considered to cover its ancestors.
1799 if siderevs:
1794 if siderevs:
1800 rebaseset = [
1795 rebaseset = [
1801 r for r, d in state.items() if d > 0 and r not in obsskipped
1796 r for r, d in state.items() if d > 0 and r not in obsskipped
1802 ]
1797 ]
1803 merges = [
1798 merges = [
1804 r for r in rebaseset if cl.parentrevs(r)[1] != nullrev
1799 r for r in rebaseset if cl.parentrevs(r)[1] != nullrev
1805 ]
1800 ]
1806 unwanted[i] = list(
1801 unwanted[i] = list(
1807 repo.revs(
1802 repo.revs(
1808 b'%ld - (::%ld) - %ld', siderevs, merges, rebaseset
1803 b'%ld - (::%ld) - %ld', siderevs, merges, rebaseset
1809 )
1804 )
1810 )
1805 )
1811
1806
1812 # Choose a merge base that has a minimal number of unwanted revs.
1807 # Choose a merge base that has a minimal number of unwanted revs.
1813 l, i = min(
1808 l, i = min(
1814 (len(revs), i)
1809 (len(revs), i)
1815 for i, revs in enumerate(unwanted)
1810 for i, revs in enumerate(unwanted)
1816 if revs is not None
1811 if revs is not None
1817 )
1812 )
1818 base = bases[i]
1813 base = bases[i]
1819
1814
1820 # newps[0] should match merge base if possible. Currently, if newps[i]
1815 # newps[0] should match merge base if possible. Currently, if newps[i]
1821 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1816 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1822 # the other's ancestor. In that case, it's fine to not swap newps here.
1817 # the other's ancestor. In that case, it's fine to not swap newps here.
1823 # (see CASE-1 and CASE-2 above)
1818 # (see CASE-1 and CASE-2 above)
1824 if i != 0 and newps[i] != nullrev:
1819 if i != 0 and newps[i] != nullrev:
1825 newps[0], newps[i] = newps[i], newps[0]
1820 newps[0], newps[i] = newps[i], newps[0]
1826
1821
1827 # The merge will include unwanted revisions. Abort now. Revisit this if
1822 # The merge will include unwanted revisions. Abort now. Revisit this if
1828 # we have a more advanced merge algorithm that handles multiple bases.
1823 # we have a more advanced merge algorithm that handles multiple bases.
1829 if l > 0:
1824 if l > 0:
1830 unwanteddesc = _(b' or ').join(
1825 unwanteddesc = _(b' or ').join(
1831 (
1826 (
1832 b', '.join(b'%d:%s' % (r, repo[r]) for r in revs)
1827 b', '.join(b'%d:%s' % (r, repo[r]) for r in revs)
1833 for revs in unwanted
1828 for revs in unwanted
1834 if revs is not None
1829 if revs is not None
1835 )
1830 )
1836 )
1831 )
1837 raise error.Abort(
1832 raise error.Abort(
1838 _(b'rebasing %d:%s will include unwanted changes from %s')
1833 _(b'rebasing %d:%s will include unwanted changes from %s')
1839 % (rev, repo[rev], unwanteddesc)
1834 % (rev, repo[rev], unwanteddesc)
1840 )
1835 )
1841
1836
1842 repo.ui.debug(b" future parents are %d and %d\n" % tuple(newps))
1837 repo.ui.debug(b" future parents are %d and %d\n" % tuple(newps))
1843
1838
1844 return newps[0], newps[1], base
1839 return newps[0], newps[1], base
1845
1840
1846
1841
1847 def isagitpatch(repo, patchname):
1842 def isagitpatch(repo, patchname):
1848 """Return true if the given patch is in git format"""
1843 """Return true if the given patch is in git format"""
1849 mqpatch = os.path.join(repo.mq.path, patchname)
1844 mqpatch = os.path.join(repo.mq.path, patchname)
1850 for line in patch.linereader(open(mqpatch, b'rb')):
1845 for line in patch.linereader(open(mqpatch, b'rb')):
1851 if line.startswith(b'diff --git'):
1846 if line.startswith(b'diff --git'):
1852 return True
1847 return True
1853 return False
1848 return False
1854
1849
1855
1850
1856 def updatemq(repo, state, skipped, **opts):
1851 def updatemq(repo, state, skipped, **opts):
1857 """Update rebased mq patches - finalize and then import them"""
1852 """Update rebased mq patches - finalize and then import them"""
1858 mqrebase = {}
1853 mqrebase = {}
1859 mq = repo.mq
1854 mq = repo.mq
1860 original_series = mq.fullseries[:]
1855 original_series = mq.fullseries[:]
1861 skippedpatches = set()
1856 skippedpatches = set()
1862
1857
1863 for p in mq.applied:
1858 for p in mq.applied:
1864 rev = repo[p.node].rev()
1859 rev = repo[p.node].rev()
1865 if rev in state:
1860 if rev in state:
1866 repo.ui.debug(
1861 repo.ui.debug(
1867 b'revision %d is an mq patch (%s), finalize it.\n'
1862 b'revision %d is an mq patch (%s), finalize it.\n'
1868 % (rev, p.name)
1863 % (rev, p.name)
1869 )
1864 )
1870 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1865 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1871 else:
1866 else:
1872 # Applied but not rebased, not sure this should happen
1867 # Applied but not rebased, not sure this should happen
1873 skippedpatches.add(p.name)
1868 skippedpatches.add(p.name)
1874
1869
1875 if mqrebase:
1870 if mqrebase:
1876 mq.finish(repo, mqrebase.keys())
1871 mq.finish(repo, mqrebase.keys())
1877
1872
1878 # We must start import from the newest revision
1873 # We must start import from the newest revision
1879 for rev in sorted(mqrebase, reverse=True):
1874 for rev in sorted(mqrebase, reverse=True):
1880 if rev not in skipped:
1875 if rev not in skipped:
1881 name, isgit = mqrebase[rev]
1876 name, isgit = mqrebase[rev]
1882 repo.ui.note(
1877 repo.ui.note(
1883 _(b'updating mq patch %s to %d:%s\n')
1878 _(b'updating mq patch %s to %d:%s\n')
1884 % (name, state[rev], repo[state[rev]])
1879 % (name, state[rev], repo[state[rev]])
1885 )
1880 )
1886 mq.qimport(
1881 mq.qimport(
1887 repo,
1882 repo,
1888 (),
1883 (),
1889 patchname=name,
1884 patchname=name,
1890 git=isgit,
1885 git=isgit,
1891 rev=[b"%d" % state[rev]],
1886 rev=[b"%d" % state[rev]],
1892 )
1887 )
1893 else:
1888 else:
1894 # Rebased and skipped
1889 # Rebased and skipped
1895 skippedpatches.add(mqrebase[rev][0])
1890 skippedpatches.add(mqrebase[rev][0])
1896
1891
1897 # Patches were either applied and rebased and imported in
1892 # Patches were either applied and rebased and imported in
1898 # order, applied and removed or unapplied. Discard the removed
1893 # order, applied and removed or unapplied. Discard the removed
1899 # ones while preserving the original series order and guards.
1894 # ones while preserving the original series order and guards.
1900 newseries = [
1895 newseries = [
1901 s
1896 s
1902 for s in original_series
1897 for s in original_series
1903 if mq.guard_re.split(s, 1)[0] not in skippedpatches
1898 if mq.guard_re.split(s, 1)[0] not in skippedpatches
1904 ]
1899 ]
1905 mq.fullseries[:] = newseries
1900 mq.fullseries[:] = newseries
1906 mq.seriesdirty = True
1901 mq.seriesdirty = True
1907 mq.savedirty()
1902 mq.savedirty()
1908
1903
1909
1904
1910 def storecollapsemsg(repo, collapsemsg):
1905 def storecollapsemsg(repo, collapsemsg):
1911 """Store the collapse message to allow recovery"""
1906 """Store the collapse message to allow recovery"""
1912 collapsemsg = collapsemsg or b''
1907 collapsemsg = collapsemsg or b''
1913 f = repo.vfs(b"last-message.txt", b"w")
1908 f = repo.vfs(b"last-message.txt", b"w")
1914 f.write(b"%s\n" % collapsemsg)
1909 f.write(b"%s\n" % collapsemsg)
1915 f.close()
1910 f.close()
1916
1911
1917
1912
1918 def clearcollapsemsg(repo):
1913 def clearcollapsemsg(repo):
1919 """Remove collapse message file"""
1914 """Remove collapse message file"""
1920 repo.vfs.unlinkpath(b"last-message.txt", ignoremissing=True)
1915 repo.vfs.unlinkpath(b"last-message.txt", ignoremissing=True)
1921
1916
1922
1917
1923 def restorecollapsemsg(repo, isabort):
1918 def restorecollapsemsg(repo, isabort):
1924 """Restore previously stored collapse message"""
1919 """Restore previously stored collapse message"""
1925 try:
1920 try:
1926 f = repo.vfs(b"last-message.txt")
1921 f = repo.vfs(b"last-message.txt")
1927 collapsemsg = f.readline().strip()
1922 collapsemsg = f.readline().strip()
1928 f.close()
1923 f.close()
1929 except IOError as err:
1924 except IOError as err:
1930 if err.errno != errno.ENOENT:
1925 if err.errno != errno.ENOENT:
1931 raise
1926 raise
1932 if isabort:
1927 if isabort:
1933 # Oh well, just abort like normal
1928 # Oh well, just abort like normal
1934 collapsemsg = b''
1929 collapsemsg = b''
1935 else:
1930 else:
1936 raise error.Abort(_(b'missing .hg/last-message.txt for rebase'))
1931 raise error.Abort(_(b'missing .hg/last-message.txt for rebase'))
1937 return collapsemsg
1932 return collapsemsg
1938
1933
1939
1934
1940 def clearstatus(repo):
1935 def clearstatus(repo):
1941 """Remove the status files"""
1936 """Remove the status files"""
1942 # Make sure the active transaction won't write the state file
1937 # Make sure the active transaction won't write the state file
1943 tr = repo.currenttransaction()
1938 tr = repo.currenttransaction()
1944 if tr:
1939 if tr:
1945 tr.removefilegenerator(b'rebasestate')
1940 tr.removefilegenerator(b'rebasestate')
1946 repo.vfs.unlinkpath(b"rebasestate", ignoremissing=True)
1941 repo.vfs.unlinkpath(b"rebasestate", ignoremissing=True)
1947
1942
1948
1943
1949 def needupdate(repo, state):
1944 def needupdate(repo, state):
1950 '''check whether we should `update --clean` away from a merge, or if
1945 '''check whether we should `update --clean` away from a merge, or if
1951 somehow the working dir got forcibly updated, e.g. by older hg'''
1946 somehow the working dir got forcibly updated, e.g. by older hg'''
1952 parents = [p.rev() for p in repo[None].parents()]
1947 parents = [p.rev() for p in repo[None].parents()]
1953
1948
1954 # Are we in a merge state at all?
1949 # Are we in a merge state at all?
1955 if len(parents) < 2:
1950 if len(parents) < 2:
1956 return False
1951 return False
1957
1952
1958 # We should be standing on the first as-of-yet unrebased commit.
1953 # We should be standing on the first as-of-yet unrebased commit.
1959 firstunrebased = min(
1954 firstunrebased = min(
1960 [old for old, new in pycompat.iteritems(state) if new == nullrev]
1955 [old for old, new in pycompat.iteritems(state) if new == nullrev]
1961 )
1956 )
1962 if firstunrebased in parents:
1957 if firstunrebased in parents:
1963 return True
1958 return True
1964
1959
1965 return False
1960 return False
1966
1961
1967
1962
1968 def sortsource(destmap):
1963 def sortsource(destmap):
1969 """yield source revisions in an order that we only rebase things once
1964 """yield source revisions in an order that we only rebase things once
1970
1965
1971 If source and destination overlaps, we should filter out revisions
1966 If source and destination overlaps, we should filter out revisions
1972 depending on other revisions which hasn't been rebased yet.
1967 depending on other revisions which hasn't been rebased yet.
1973
1968
1974 Yield a sorted list of revisions each time.
1969 Yield a sorted list of revisions each time.
1975
1970
1976 For example, when rebasing A to B, B to C. This function yields [B], then
1971 For example, when rebasing A to B, B to C. This function yields [B], then
1977 [A], indicating B needs to be rebased first.
1972 [A], indicating B needs to be rebased first.
1978
1973
1979 Raise if there is a cycle so the rebase is impossible.
1974 Raise if there is a cycle so the rebase is impossible.
1980 """
1975 """
1981 srcset = set(destmap)
1976 srcset = set(destmap)
1982 while srcset:
1977 while srcset:
1983 srclist = sorted(srcset)
1978 srclist = sorted(srcset)
1984 result = []
1979 result = []
1985 for r in srclist:
1980 for r in srclist:
1986 if destmap[r] not in srcset:
1981 if destmap[r] not in srcset:
1987 result.append(r)
1982 result.append(r)
1988 if not result:
1983 if not result:
1989 raise error.Abort(_(b'source and destination form a cycle'))
1984 raise error.Abort(_(b'source and destination form a cycle'))
1990 srcset -= set(result)
1985 srcset -= set(result)
1991 yield result
1986 yield result
1992
1987
1993
1988
1994 def buildstate(repo, destmap, collapse):
1989 def buildstate(repo, destmap, collapse):
1995 '''Define which revisions are going to be rebased and where
1990 '''Define which revisions are going to be rebased and where
1996
1991
1997 repo: repo
1992 repo: repo
1998 destmap: {srcrev: destrev}
1993 destmap: {srcrev: destrev}
1999 '''
1994 '''
2000 rebaseset = destmap.keys()
1995 rebaseset = destmap.keys()
2001 originalwd = repo[b'.'].rev()
1996 originalwd = repo[b'.'].rev()
2002
1997
2003 # This check isn't strictly necessary, since mq detects commits over an
1998 # This check isn't strictly necessary, since mq detects commits over an
2004 # applied patch. But it prevents messing up the working directory when
1999 # applied patch. But it prevents messing up the working directory when
2005 # a partially completed rebase is blocked by mq.
2000 # a partially completed rebase is blocked by mq.
2006 if b'qtip' in repo.tags():
2001 if b'qtip' in repo.tags():
2007 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
2002 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
2008 if set(destmap.values()) & mqapplied:
2003 if set(destmap.values()) & mqapplied:
2009 raise error.Abort(_(b'cannot rebase onto an applied mq patch'))
2004 raise error.Abort(_(b'cannot rebase onto an applied mq patch'))
2010
2005
2011 # Get "cycle" error early by exhausting the generator.
2006 # Get "cycle" error early by exhausting the generator.
2012 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
2007 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
2013 if not sortedsrc:
2008 if not sortedsrc:
2014 raise error.Abort(_(b'no matching revisions'))
2009 raise error.Abort(_(b'no matching revisions'))
2015
2010
2016 # Only check the first batch of revisions to rebase not depending on other
2011 # Only check the first batch of revisions to rebase not depending on other
2017 # rebaseset. This means "source is ancestor of destination" for the second
2012 # rebaseset. This means "source is ancestor of destination" for the second
2018 # (and following) batches of revisions are not checked here. We rely on
2013 # (and following) batches of revisions are not checked here. We rely on
2019 # "defineparents" to do that check.
2014 # "defineparents" to do that check.
2020 roots = list(repo.set(b'roots(%ld)', sortedsrc[0]))
2015 roots = list(repo.set(b'roots(%ld)', sortedsrc[0]))
2021 if not roots:
2016 if not roots:
2022 raise error.Abort(_(b'no matching revisions'))
2017 raise error.Abort(_(b'no matching revisions'))
2023
2018
2024 def revof(r):
2019 def revof(r):
2025 return r.rev()
2020 return r.rev()
2026
2021
2027 roots = sorted(roots, key=revof)
2022 roots = sorted(roots, key=revof)
2028 state = dict.fromkeys(rebaseset, revtodo)
2023 state = dict.fromkeys(rebaseset, revtodo)
2029 emptyrebase = len(sortedsrc) == 1
2024 emptyrebase = len(sortedsrc) == 1
2030 for root in roots:
2025 for root in roots:
2031 dest = repo[destmap[root.rev()]]
2026 dest = repo[destmap[root.rev()]]
2032 commonbase = root.ancestor(dest)
2027 commonbase = root.ancestor(dest)
2033 if commonbase == root:
2028 if commonbase == root:
2034 raise error.Abort(_(b'source is ancestor of destination'))
2029 raise error.Abort(_(b'source is ancestor of destination'))
2035 if commonbase == dest:
2030 if commonbase == dest:
2036 wctx = repo[None]
2031 wctx = repo[None]
2037 if dest == wctx.p1():
2032 if dest == wctx.p1():
2038 # when rebasing to '.', it will use the current wd branch name
2033 # when rebasing to '.', it will use the current wd branch name
2039 samebranch = root.branch() == wctx.branch()
2034 samebranch = root.branch() == wctx.branch()
2040 else:
2035 else:
2041 samebranch = root.branch() == dest.branch()
2036 samebranch = root.branch() == dest.branch()
2042 if not collapse and samebranch and dest in root.parents():
2037 if not collapse and samebranch and dest in root.parents():
2043 # mark the revision as done by setting its new revision
2038 # mark the revision as done by setting its new revision
2044 # equal to its old (current) revisions
2039 # equal to its old (current) revisions
2045 state[root.rev()] = root.rev()
2040 state[root.rev()] = root.rev()
2046 repo.ui.debug(b'source is a child of destination\n')
2041 repo.ui.debug(b'source is a child of destination\n')
2047 continue
2042 continue
2048
2043
2049 emptyrebase = False
2044 emptyrebase = False
2050 repo.ui.debug(b'rebase onto %s starting from %s\n' % (dest, root))
2045 repo.ui.debug(b'rebase onto %s starting from %s\n' % (dest, root))
2051 if emptyrebase:
2046 if emptyrebase:
2052 return None
2047 return None
2053 for rev in sorted(state):
2048 for rev in sorted(state):
2054 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
2049 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
2055 # if all parents of this revision are done, then so is this revision
2050 # if all parents of this revision are done, then so is this revision
2056 if parents and all((state.get(p) == p for p in parents)):
2051 if parents and all((state.get(p) == p for p in parents)):
2057 state[rev] = rev
2052 state[rev] = rev
2058 return originalwd, destmap, state
2053 return originalwd, destmap, state
2059
2054
2060
2055
2061 def clearrebased(
2056 def clearrebased(
2062 ui,
2057 ui,
2063 repo,
2058 repo,
2064 destmap,
2059 destmap,
2065 state,
2060 state,
2066 skipped,
2061 skipped,
2067 collapsedas=None,
2062 collapsedas=None,
2068 keepf=False,
2063 keepf=False,
2069 fm=None,
2064 fm=None,
2070 backup=True,
2065 backup=True,
2071 ):
2066 ):
2072 """dispose of rebased revision at the end of the rebase
2067 """dispose of rebased revision at the end of the rebase
2073
2068
2074 If `collapsedas` is not None, the rebase was a collapse whose result if the
2069 If `collapsedas` is not None, the rebase was a collapse whose result if the
2075 `collapsedas` node.
2070 `collapsedas` node.
2076
2071
2077 If `keepf` is not True, the rebase has --keep set and no nodes should be
2072 If `keepf` is not True, the rebase has --keep set and no nodes should be
2078 removed (but bookmarks still need to be moved).
2073 removed (but bookmarks still need to be moved).
2079
2074
2080 If `backup` is False, no backup will be stored when stripping rebased
2075 If `backup` is False, no backup will be stored when stripping rebased
2081 revisions.
2076 revisions.
2082 """
2077 """
2083 tonode = repo.changelog.node
2078 tonode = repo.changelog.node
2084 replacements = {}
2079 replacements = {}
2085 moves = {}
2080 moves = {}
2086 stripcleanup = not obsolete.isenabled(repo, obsolete.createmarkersopt)
2081 stripcleanup = not obsolete.isenabled(repo, obsolete.createmarkersopt)
2087
2082
2088 collapsednodes = []
2083 collapsednodes = []
2089 for rev, newrev in sorted(state.items()):
2084 for rev, newrev in sorted(state.items()):
2090 if newrev >= 0 and newrev != rev:
2085 if newrev >= 0 and newrev != rev:
2091 oldnode = tonode(rev)
2086 oldnode = tonode(rev)
2092 newnode = collapsedas or tonode(newrev)
2087 newnode = collapsedas or tonode(newrev)
2093 moves[oldnode] = newnode
2088 moves[oldnode] = newnode
2094 succs = None
2089 succs = None
2095 if rev in skipped:
2090 if rev in skipped:
2096 if stripcleanup or not repo[rev].obsolete():
2091 if stripcleanup or not repo[rev].obsolete():
2097 succs = ()
2092 succs = ()
2098 elif collapsedas:
2093 elif collapsedas:
2099 collapsednodes.append(oldnode)
2094 collapsednodes.append(oldnode)
2100 else:
2095 else:
2101 succs = (newnode,)
2096 succs = (newnode,)
2102 if succs is not None:
2097 if succs is not None:
2103 replacements[(oldnode,)] = succs
2098 replacements[(oldnode,)] = succs
2104 if collapsednodes:
2099 if collapsednodes:
2105 replacements[tuple(collapsednodes)] = (collapsedas,)
2100 replacements[tuple(collapsednodes)] = (collapsedas,)
2106 if fm:
2101 if fm:
2107 hf = fm.hexfunc
2102 hf = fm.hexfunc
2108 fl = fm.formatlist
2103 fl = fm.formatlist
2109 fd = fm.formatdict
2104 fd = fm.formatdict
2110 changes = {}
2105 changes = {}
2111 for oldns, newn in pycompat.iteritems(replacements):
2106 for oldns, newn in pycompat.iteritems(replacements):
2112 for oldn in oldns:
2107 for oldn in oldns:
2113 changes[hf(oldn)] = fl([hf(n) for n in newn], name=b'node')
2108 changes[hf(oldn)] = fl([hf(n) for n in newn], name=b'node')
2114 nodechanges = fd(changes, key=b"oldnode", value=b"newnodes")
2109 nodechanges = fd(changes, key=b"oldnode", value=b"newnodes")
2115 fm.data(nodechanges=nodechanges)
2110 fm.data(nodechanges=nodechanges)
2116 if keepf:
2111 if keepf:
2117 replacements = {}
2112 replacements = {}
2118 scmutil.cleanupnodes(repo, replacements, b'rebase', moves, backup=backup)
2113 scmutil.cleanupnodes(repo, replacements, b'rebase', moves, backup=backup)
2119
2114
2120
2115
2121 def pullrebase(orig, ui, repo, *args, **opts):
2116 def pullrebase(orig, ui, repo, *args, **opts):
2122 """Call rebase after pull if the latter has been invoked with --rebase"""
2117 """Call rebase after pull if the latter has been invoked with --rebase"""
2123 if opts.get('rebase'):
2118 if opts.get('rebase'):
2124 if ui.configbool(b'commands', b'rebase.requiredest'):
2119 if ui.configbool(b'commands', b'rebase.requiredest'):
2125 msg = _(b'rebase destination required by configuration')
2120 msg = _(b'rebase destination required by configuration')
2126 hint = _(b'use hg pull followed by hg rebase -d DEST')
2121 hint = _(b'use hg pull followed by hg rebase -d DEST')
2127 raise error.Abort(msg, hint=hint)
2122 raise error.Abort(msg, hint=hint)
2128
2123
2129 with repo.wlock(), repo.lock():
2124 with repo.wlock(), repo.lock():
2130 if opts.get('update'):
2125 if opts.get('update'):
2131 del opts['update']
2126 del opts['update']
2132 ui.debug(
2127 ui.debug(
2133 b'--update and --rebase are not compatible, ignoring '
2128 b'--update and --rebase are not compatible, ignoring '
2134 b'the update flag\n'
2129 b'the update flag\n'
2135 )
2130 )
2136
2131
2137 cmdutil.checkunfinished(repo, skipmerge=True)
2132 cmdutil.checkunfinished(repo, skipmerge=True)
2138 cmdutil.bailifchanged(
2133 cmdutil.bailifchanged(
2139 repo,
2134 repo,
2140 hint=_(
2135 hint=_(
2141 b'cannot pull with rebase: '
2136 b'cannot pull with rebase: '
2142 b'please commit or shelve your changes first'
2137 b'please commit or shelve your changes first'
2143 ),
2138 ),
2144 )
2139 )
2145
2140
2146 revsprepull = len(repo)
2141 revsprepull = len(repo)
2147 origpostincoming = commands.postincoming
2142 origpostincoming = commands.postincoming
2148
2143
2149 def _dummy(*args, **kwargs):
2144 def _dummy(*args, **kwargs):
2150 pass
2145 pass
2151
2146
2152 commands.postincoming = _dummy
2147 commands.postincoming = _dummy
2153 try:
2148 try:
2154 ret = orig(ui, repo, *args, **opts)
2149 ret = orig(ui, repo, *args, **opts)
2155 finally:
2150 finally:
2156 commands.postincoming = origpostincoming
2151 commands.postincoming = origpostincoming
2157 revspostpull = len(repo)
2152 revspostpull = len(repo)
2158 if revspostpull > revsprepull:
2153 if revspostpull > revsprepull:
2159 # --rev option from pull conflict with rebase own --rev
2154 # --rev option from pull conflict with rebase own --rev
2160 # dropping it
2155 # dropping it
2161 if 'rev' in opts:
2156 if 'rev' in opts:
2162 del opts['rev']
2157 del opts['rev']
2163 # positional argument from pull conflicts with rebase's own
2158 # positional argument from pull conflicts with rebase's own
2164 # --source.
2159 # --source.
2165 if 'source' in opts:
2160 if 'source' in opts:
2166 del opts['source']
2161 del opts['source']
2167 # revsprepull is the len of the repo, not revnum of tip.
2162 # revsprepull is the len of the repo, not revnum of tip.
2168 destspace = list(repo.changelog.revs(start=revsprepull))
2163 destspace = list(repo.changelog.revs(start=revsprepull))
2169 opts['_destspace'] = destspace
2164 opts['_destspace'] = destspace
2170 try:
2165 try:
2171 rebase(ui, repo, **opts)
2166 rebase(ui, repo, **opts)
2172 except error.NoMergeDestAbort:
2167 except error.NoMergeDestAbort:
2173 # we can maybe update instead
2168 # we can maybe update instead
2174 rev, _a, _b = destutil.destupdate(repo)
2169 rev, _a, _b = destutil.destupdate(repo)
2175 if rev == repo[b'.'].rev():
2170 if rev == repo[b'.'].rev():
2176 ui.status(_(b'nothing to rebase\n'))
2171 ui.status(_(b'nothing to rebase\n'))
2177 else:
2172 else:
2178 ui.status(_(b'nothing to rebase - updating instead\n'))
2173 ui.status(_(b'nothing to rebase - updating instead\n'))
2179 # not passing argument to get the bare update behavior
2174 # not passing argument to get the bare update behavior
2180 # with warning and trumpets
2175 # with warning and trumpets
2181 commands.update(ui, repo)
2176 commands.update(ui, repo)
2182 else:
2177 else:
2183 if opts.get('tool'):
2178 if opts.get('tool'):
2184 raise error.Abort(_(b'--tool can only be used with --rebase'))
2179 raise error.Abort(_(b'--tool can only be used with --rebase'))
2185 ret = orig(ui, repo, *args, **opts)
2180 ret = orig(ui, repo, *args, **opts)
2186
2181
2187 return ret
2182 return ret
2188
2183
2189
2184
2190 def _filterobsoleterevs(repo, revs):
2185 def _filterobsoleterevs(repo, revs):
2191 """returns a set of the obsolete revisions in revs"""
2186 """returns a set of the obsolete revisions in revs"""
2192 return set(r for r in revs if repo[r].obsolete())
2187 return set(r for r in revs if repo[r].obsolete())
2193
2188
2194
2189
2195 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
2190 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
2196 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
2191 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
2197
2192
2198 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
2193 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
2199 obsolete nodes to be rebased given in `rebaseobsrevs`.
2194 obsolete nodes to be rebased given in `rebaseobsrevs`.
2200
2195
2201 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
2196 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
2202 without a successor in destination.
2197 without a successor in destination.
2203
2198
2204 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
2199 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
2205 obsolete successors.
2200 obsolete successors.
2206 """
2201 """
2207 obsoletenotrebased = {}
2202 obsoletenotrebased = {}
2208 obsoletewithoutsuccessorindestination = set()
2203 obsoletewithoutsuccessorindestination = set()
2209 obsoleteextinctsuccessors = set()
2204 obsoleteextinctsuccessors = set()
2210
2205
2211 assert repo.filtername is None
2206 assert repo.filtername is None
2212 cl = repo.changelog
2207 cl = repo.changelog
2213 get_rev = cl.index.get_rev
2208 get_rev = cl.index.get_rev
2214 extinctrevs = set(repo.revs(b'extinct()'))
2209 extinctrevs = set(repo.revs(b'extinct()'))
2215 for srcrev in rebaseobsrevs:
2210 for srcrev in rebaseobsrevs:
2216 srcnode = cl.node(srcrev)
2211 srcnode = cl.node(srcrev)
2217 # XXX: more advanced APIs are required to handle split correctly
2212 # XXX: more advanced APIs are required to handle split correctly
2218 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
2213 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
2219 # obsutil.allsuccessors includes node itself
2214 # obsutil.allsuccessors includes node itself
2220 successors.remove(srcnode)
2215 successors.remove(srcnode)
2221 succrevs = {get_rev(s) for s in successors}
2216 succrevs = {get_rev(s) for s in successors}
2222 succrevs.discard(None)
2217 succrevs.discard(None)
2223 if succrevs.issubset(extinctrevs):
2218 if succrevs.issubset(extinctrevs):
2224 # all successors are extinct
2219 # all successors are extinct
2225 obsoleteextinctsuccessors.add(srcrev)
2220 obsoleteextinctsuccessors.add(srcrev)
2226 if not successors:
2221 if not successors:
2227 # no successor
2222 # no successor
2228 obsoletenotrebased[srcrev] = None
2223 obsoletenotrebased[srcrev] = None
2229 else:
2224 else:
2230 dstrev = destmap[srcrev]
2225 dstrev = destmap[srcrev]
2231 for succrev in succrevs:
2226 for succrev in succrevs:
2232 if cl.isancestorrev(succrev, dstrev):
2227 if cl.isancestorrev(succrev, dstrev):
2233 obsoletenotrebased[srcrev] = succrev
2228 obsoletenotrebased[srcrev] = succrev
2234 break
2229 break
2235 else:
2230 else:
2236 # If 'srcrev' has a successor in rebase set but none in
2231 # If 'srcrev' has a successor in rebase set but none in
2237 # destination (which would be catched above), we shall skip it
2232 # destination (which would be catched above), we shall skip it
2238 # and its descendants to avoid divergence.
2233 # and its descendants to avoid divergence.
2239 if srcrev in extinctrevs or any(s in destmap for s in succrevs):
2234 if srcrev in extinctrevs or any(s in destmap for s in succrevs):
2240 obsoletewithoutsuccessorindestination.add(srcrev)
2235 obsoletewithoutsuccessorindestination.add(srcrev)
2241
2236
2242 return (
2237 return (
2243 obsoletenotrebased,
2238 obsoletenotrebased,
2244 obsoletewithoutsuccessorindestination,
2239 obsoletewithoutsuccessorindestination,
2245 obsoleteextinctsuccessors,
2240 obsoleteextinctsuccessors,
2246 )
2241 )
2247
2242
2248
2243
2249 def abortrebase(ui, repo):
2244 def abortrebase(ui, repo):
2250 with repo.wlock(), repo.lock():
2245 with repo.wlock(), repo.lock():
2251 rbsrt = rebaseruntime(repo, ui)
2246 rbsrt = rebaseruntime(repo, ui)
2252 rbsrt._prepareabortorcontinue(isabort=True)
2247 rbsrt._prepareabortorcontinue(isabort=True)
2253
2248
2254
2249
2255 def continuerebase(ui, repo):
2250 def continuerebase(ui, repo):
2256 with repo.wlock(), repo.lock():
2251 with repo.wlock(), repo.lock():
2257 rbsrt = rebaseruntime(repo, ui)
2252 rbsrt = rebaseruntime(repo, ui)
2258 ms = mergemod.mergestate.read(repo)
2253 ms = mergemod.mergestate.read(repo)
2259 mergeutil.checkunresolved(ms)
2254 mergeutil.checkunresolved(ms)
2260 retcode = rbsrt._prepareabortorcontinue(isabort=False)
2255 retcode = rbsrt._prepareabortorcontinue(isabort=False)
2261 if retcode is not None:
2256 if retcode is not None:
2262 return retcode
2257 return retcode
2263 rbsrt._performrebase(None)
2258 rbsrt._performrebase(None)
2264 rbsrt._finishrebase()
2259 rbsrt._finishrebase()
2265
2260
2266
2261
2267 def summaryhook(ui, repo):
2262 def summaryhook(ui, repo):
2268 if not repo.vfs.exists(b'rebasestate'):
2263 if not repo.vfs.exists(b'rebasestate'):
2269 return
2264 return
2270 try:
2265 try:
2271 rbsrt = rebaseruntime(repo, ui, {})
2266 rbsrt = rebaseruntime(repo, ui, {})
2272 rbsrt.restorestatus()
2267 rbsrt.restorestatus()
2273 state = rbsrt.state
2268 state = rbsrt.state
2274 except error.RepoLookupError:
2269 except error.RepoLookupError:
2275 # i18n: column positioning for "hg summary"
2270 # i18n: column positioning for "hg summary"
2276 msg = _(b'rebase: (use "hg rebase --abort" to clear broken state)\n')
2271 msg = _(b'rebase: (use "hg rebase --abort" to clear broken state)\n')
2277 ui.write(msg)
2272 ui.write(msg)
2278 return
2273 return
2279 numrebased = len([i for i in pycompat.itervalues(state) if i >= 0])
2274 numrebased = len([i for i in pycompat.itervalues(state) if i >= 0])
2280 # i18n: column positioning for "hg summary"
2275 # i18n: column positioning for "hg summary"
2281 ui.write(
2276 ui.write(
2282 _(b'rebase: %s, %s (rebase --continue)\n')
2277 _(b'rebase: %s, %s (rebase --continue)\n')
2283 % (
2278 % (
2284 ui.label(_(b'%d rebased'), b'rebase.rebased') % numrebased,
2279 ui.label(_(b'%d rebased'), b'rebase.rebased') % numrebased,
2285 ui.label(_(b'%d remaining'), b'rebase.remaining')
2280 ui.label(_(b'%d remaining'), b'rebase.remaining')
2286 % (len(state) - numrebased),
2281 % (len(state) - numrebased),
2287 )
2282 )
2288 )
2283 )
2289
2284
2290
2285
2291 def uisetup(ui):
2286 def uisetup(ui):
2292 # Replace pull with a decorator to provide --rebase option
2287 # Replace pull with a decorator to provide --rebase option
2293 entry = extensions.wrapcommand(commands.table, b'pull', pullrebase)
2288 entry = extensions.wrapcommand(commands.table, b'pull', pullrebase)
2294 entry[1].append(
2289 entry[1].append(
2295 (b'', b'rebase', None, _(b"rebase working directory to branch head"))
2290 (b'', b'rebase', None, _(b"rebase working directory to branch head"))
2296 )
2291 )
2297 entry[1].append((b't', b'tool', b'', _(b"specify merge tool for rebase")))
2292 entry[1].append((b't', b'tool', b'', _(b"specify merge tool for rebase")))
2298 cmdutil.summaryhooks.add(b'rebase', summaryhook)
2293 cmdutil.summaryhooks.add(b'rebase', summaryhook)
2299 statemod.addunfinished(
2294 statemod.addunfinished(
2300 b'rebase',
2295 b'rebase',
2301 fname=b'rebasestate',
2296 fname=b'rebasestate',
2302 stopflag=True,
2297 stopflag=True,
2303 continueflag=True,
2298 continueflag=True,
2304 abortfunc=abortrebase,
2299 abortfunc=abortrebase,
2305 continuefunc=continuerebase,
2300 continuefunc=continuerebase,
2306 )
2301 )
@@ -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: abort and continue do not allow specifying revisions
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 a source and a 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 a revision and a 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 a revision and a 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