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