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