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