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