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