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