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