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