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