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