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