##// END OF EJS Templates
rebase: store rebase state after each commit...
Martin von Zweigbergk -
r37050:98663bed default
parent child Browse files
Show More
@@ -1,1859 +1,1863
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 nullid,
24 nullid,
25 nullrev,
25 nullrev,
26 short,
26 short,
27 )
27 )
28 from mercurial import (
28 from mercurial import (
29 bookmarks,
29 bookmarks,
30 cmdutil,
30 cmdutil,
31 commands,
31 commands,
32 copies,
32 copies,
33 destutil,
33 destutil,
34 dirstateguard,
34 dirstateguard,
35 error,
35 error,
36 extensions,
36 extensions,
37 hg,
37 hg,
38 lock,
38 lock,
39 merge as mergemod,
39 merge as mergemod,
40 mergeutil,
40 mergeutil,
41 obsolete,
41 obsolete,
42 obsutil,
42 obsutil,
43 patch,
43 patch,
44 phases,
44 phases,
45 pycompat,
45 pycompat,
46 registrar,
46 registrar,
47 repair,
47 repair,
48 revset,
48 revset,
49 revsetlang,
49 revsetlang,
50 scmutil,
50 scmutil,
51 smartset,
51 smartset,
52 util,
52 util,
53 )
53 )
54
54
55 release = lock.release
55 release = lock.release
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 = '-1'
62 revtodostr = '-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 = {'-2', '-3', '-4', '-5'}
66 legacystates = {'-2', '-3', '-4', '-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 = 'ships-with-hg-core'
74 testedwith = 'ships-with-hg-core'
75
75
76 def _nothingtorebase():
76 def _nothingtorebase():
77 return 1
77 return 1
78
78
79 def _savegraft(ctx, extra):
79 def _savegraft(ctx, extra):
80 s = ctx.extra().get('source', None)
80 s = ctx.extra().get('source', None)
81 if s is not None:
81 if s is not None:
82 extra['source'] = s
82 extra['source'] = s
83 s = ctx.extra().get('intermediate-source', None)
83 s = ctx.extra().get('intermediate-source', None)
84 if s is not None:
84 if s is not None:
85 extra['intermediate-source'] = s
85 extra['intermediate-source'] = s
86
86
87 def _savebranch(ctx, extra):
87 def _savebranch(ctx, extra):
88 extra['branch'] = ctx.branch()
88 extra['branch'] = ctx.branch()
89
89
90 def _makeextrafn(copiers):
90 def _makeextrafn(copiers):
91 """make an extrafn out of the given copy-functions.
91 """make an extrafn out of the given copy-functions.
92
92
93 A copy function takes a context and an extra dict, and mutates the
93 A copy function takes a context and an extra dict, and mutates the
94 extra dict as needed based on the given context.
94 extra dict as needed based on the given context.
95 """
95 """
96 def extrafn(ctx, extra):
96 def extrafn(ctx, extra):
97 for c in copiers:
97 for c in copiers:
98 c(ctx, extra)
98 c(ctx, extra)
99 return extrafn
99 return extrafn
100
100
101 def _destrebase(repo, sourceset, destspace=None):
101 def _destrebase(repo, sourceset, destspace=None):
102 """small wrapper around destmerge to pass the right extra args
102 """small wrapper around destmerge to pass the right extra args
103
103
104 Please wrap destutil.destmerge instead."""
104 Please wrap destutil.destmerge instead."""
105 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
105 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
106 onheadcheck=False, destspace=destspace)
106 onheadcheck=False, destspace=destspace)
107
107
108 revsetpredicate = registrar.revsetpredicate()
108 revsetpredicate = registrar.revsetpredicate()
109
109
110 @revsetpredicate('_destrebase')
110 @revsetpredicate('_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 def _ctxdesc(ctx):
123 def _ctxdesc(ctx):
124 """short description for a context"""
124 """short description for a context"""
125 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
125 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
126 ctx.description().split('\n', 1)[0])
126 ctx.description().split('\n', 1)[0])
127 repo = ctx.repo()
127 repo = ctx.repo()
128 names = []
128 names = []
129 for nsname, ns in repo.names.iteritems():
129 for nsname, ns in repo.names.iteritems():
130 if nsname == 'branches':
130 if nsname == 'branches':
131 continue
131 continue
132 names.extend(ns.names(repo, ctx.node()))
132 names.extend(ns.names(repo, ctx.node()))
133 if names:
133 if names:
134 desc += ' (%s)' % ' '.join(names)
134 desc += ' (%s)' % ' '.join(names)
135 return desc
135 return desc
136
136
137 class rebaseruntime(object):
137 class rebaseruntime(object):
138 """This class is a container for rebase runtime state"""
138 """This class is a container for rebase runtime state"""
139 def __init__(self, repo, ui, inmemory=False, opts=None):
139 def __init__(self, repo, ui, inmemory=False, opts=None):
140 if opts is None:
140 if opts is None:
141 opts = {}
141 opts = {}
142
142
143 # prepared: whether we have rebasestate prepared or not. Currently it
143 # prepared: whether we have rebasestate prepared or not. Currently it
144 # decides whether "self.repo" is unfiltered or not.
144 # decides whether "self.repo" is unfiltered or not.
145 # The rebasestate has explicit hash to hash instructions not depending
145 # The rebasestate has explicit hash to hash instructions not depending
146 # on visibility. If rebasestate exists (in-memory or on-disk), use
146 # on visibility. If rebasestate exists (in-memory or on-disk), use
147 # unfiltered repo to avoid visibility issues.
147 # unfiltered repo to avoid visibility issues.
148 # Before knowing rebasestate (i.e. when starting a new rebase (not
148 # Before knowing rebasestate (i.e. when starting a new rebase (not
149 # --continue or --abort)), the original repo should be used so
149 # --continue or --abort)), the original repo should be used so
150 # visibility-dependent revsets are correct.
150 # visibility-dependent revsets are correct.
151 self.prepared = False
151 self.prepared = False
152 self._repo = repo
152 self._repo = repo
153
153
154 self.ui = ui
154 self.ui = ui
155 self.opts = opts
155 self.opts = opts
156 self.originalwd = None
156 self.originalwd = None
157 self.external = nullrev
157 self.external = nullrev
158 # Mapping between the old revision id and either what is the new rebased
158 # Mapping between the old revision id and either what is the new rebased
159 # revision or what needs to be done with the old revision. The state
159 # revision or what needs to be done with the old revision. The state
160 # dict will be what contains most of the rebase progress state.
160 # dict will be what contains most of the rebase progress state.
161 self.state = {}
161 self.state = {}
162 self.activebookmark = None
162 self.activebookmark = None
163 self.destmap = {}
163 self.destmap = {}
164 self.skipped = set()
164 self.skipped = set()
165
165
166 self.collapsef = opts.get('collapse', False)
166 self.collapsef = opts.get('collapse', False)
167 self.collapsemsg = cmdutil.logmessage(ui, opts)
167 self.collapsemsg = cmdutil.logmessage(ui, opts)
168 self.date = opts.get('date', None)
168 self.date = opts.get('date', None)
169
169
170 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
170 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
171 self.extrafns = [_savegraft]
171 self.extrafns = [_savegraft]
172 if e:
172 if e:
173 self.extrafns = [e]
173 self.extrafns = [e]
174
174
175 self.keepf = opts.get('keep', False)
175 self.keepf = opts.get('keep', False)
176 self.keepbranchesf = opts.get('keepbranches', False)
176 self.keepbranchesf = opts.get('keepbranches', False)
177 self.obsoletenotrebased = {}
177 self.obsoletenotrebased = {}
178 self.obsoletewithoutsuccessorindestination = set()
178 self.obsoletewithoutsuccessorindestination = set()
179 self.inmemory = inmemory
179 self.inmemory = inmemory
180
180
181 @property
181 @property
182 def repo(self):
182 def repo(self):
183 if self.prepared:
183 if self.prepared:
184 return self._repo.unfiltered()
184 return self._repo.unfiltered()
185 else:
185 else:
186 return self._repo
186 return self._repo
187
187
188 def storestatus(self, tr=None):
188 def storestatus(self, tr=None):
189 """Store the current status to allow recovery"""
189 """Store the current status to allow recovery"""
190 if tr:
190 if tr:
191 tr.addfilegenerator('rebasestate', ('rebasestate',),
191 tr.addfilegenerator('rebasestate', ('rebasestate',),
192 self._writestatus, location='plain')
192 self._writestatus, location='plain')
193 else:
193 else:
194 with self.repo.vfs("rebasestate", "w") as f:
194 with self.repo.vfs("rebasestate", "w") as f:
195 self._writestatus(f)
195 self._writestatus(f)
196
196
197 def _writestatus(self, f):
197 def _writestatus(self, f):
198 repo = self.repo
198 repo = self.repo
199 assert repo.filtername is None
199 assert repo.filtername is None
200 f.write(repo[self.originalwd].hex() + '\n')
200 f.write(repo[self.originalwd].hex() + '\n')
201 # was "dest". we now write dest per src root below.
201 # was "dest". we now write dest per src root below.
202 f.write('\n')
202 f.write('\n')
203 f.write(repo[self.external].hex() + '\n')
203 f.write(repo[self.external].hex() + '\n')
204 f.write('%d\n' % int(self.collapsef))
204 f.write('%d\n' % int(self.collapsef))
205 f.write('%d\n' % int(self.keepf))
205 f.write('%d\n' % int(self.keepf))
206 f.write('%d\n' % int(self.keepbranchesf))
206 f.write('%d\n' % int(self.keepbranchesf))
207 f.write('%s\n' % (self.activebookmark or ''))
207 f.write('%s\n' % (self.activebookmark or ''))
208 destmap = self.destmap
208 destmap = self.destmap
209 for d, v in self.state.iteritems():
209 for d, v in self.state.iteritems():
210 oldrev = repo[d].hex()
210 oldrev = repo[d].hex()
211 if v >= 0:
211 if v >= 0:
212 newrev = repo[v].hex()
212 newrev = repo[v].hex()
213 else:
213 else:
214 newrev = "%d" % v
214 newrev = "%d" % v
215 destnode = repo[destmap[d]].hex()
215 destnode = repo[destmap[d]].hex()
216 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
216 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
217 repo.ui.debug('rebase status stored\n')
217 repo.ui.debug('rebase status stored\n')
218
218
219 def restorestatus(self):
219 def restorestatus(self):
220 """Restore a previously stored status"""
220 """Restore a previously stored status"""
221 self.prepared = True
221 self.prepared = True
222 repo = self.repo
222 repo = self.repo
223 assert repo.filtername is None
223 assert repo.filtername is None
224 keepbranches = None
224 keepbranches = None
225 legacydest = None
225 legacydest = None
226 collapse = False
226 collapse = False
227 external = nullrev
227 external = nullrev
228 activebookmark = None
228 activebookmark = None
229 state = {}
229 state = {}
230 destmap = {}
230 destmap = {}
231
231
232 try:
232 try:
233 f = repo.vfs("rebasestate")
233 f = repo.vfs("rebasestate")
234 for i, l in enumerate(f.read().splitlines()):
234 for i, l in enumerate(f.read().splitlines()):
235 if i == 0:
235 if i == 0:
236 originalwd = repo[l].rev()
236 originalwd = repo[l].rev()
237 elif i == 1:
237 elif i == 1:
238 # this line should be empty in newer version. but legacy
238 # this line should be empty in newer version. but legacy
239 # clients may still use it
239 # clients may still use it
240 if l:
240 if l:
241 legacydest = repo[l].rev()
241 legacydest = repo[l].rev()
242 elif i == 2:
242 elif i == 2:
243 external = repo[l].rev()
243 external = repo[l].rev()
244 elif i == 3:
244 elif i == 3:
245 collapse = bool(int(l))
245 collapse = bool(int(l))
246 elif i == 4:
246 elif i == 4:
247 keep = bool(int(l))
247 keep = bool(int(l))
248 elif i == 5:
248 elif i == 5:
249 keepbranches = bool(int(l))
249 keepbranches = bool(int(l))
250 elif i == 6 and not (len(l) == 81 and ':' in l):
250 elif i == 6 and not (len(l) == 81 and ':' in l):
251 # line 6 is a recent addition, so for backwards
251 # line 6 is a recent addition, so for backwards
252 # compatibility check that the line doesn't look like the
252 # compatibility check that the line doesn't look like the
253 # oldrev:newrev lines
253 # oldrev:newrev lines
254 activebookmark = l
254 activebookmark = l
255 else:
255 else:
256 args = l.split(':')
256 args = l.split(':')
257 oldrev = args[0]
257 oldrev = args[0]
258 newrev = args[1]
258 newrev = args[1]
259 if newrev in legacystates:
259 if newrev in legacystates:
260 continue
260 continue
261 if len(args) > 2:
261 if len(args) > 2:
262 destnode = args[2]
262 destnode = args[2]
263 else:
263 else:
264 destnode = legacydest
264 destnode = legacydest
265 destmap[repo[oldrev].rev()] = repo[destnode].rev()
265 destmap[repo[oldrev].rev()] = repo[destnode].rev()
266 if newrev in (nullid, revtodostr):
266 if newrev in (nullid, revtodostr):
267 state[repo[oldrev].rev()] = revtodo
267 state[repo[oldrev].rev()] = revtodo
268 # Legacy compat special case
268 # Legacy compat special case
269 else:
269 else:
270 state[repo[oldrev].rev()] = repo[newrev].rev()
270 state[repo[oldrev].rev()] = repo[newrev].rev()
271
271
272 except IOError as err:
272 except IOError as err:
273 if err.errno != errno.ENOENT:
273 if err.errno != errno.ENOENT:
274 raise
274 raise
275 cmdutil.wrongtooltocontinue(repo, _('rebase'))
275 cmdutil.wrongtooltocontinue(repo, _('rebase'))
276
276
277 if keepbranches is None:
277 if keepbranches is None:
278 raise error.Abort(_('.hg/rebasestate is incomplete'))
278 raise error.Abort(_('.hg/rebasestate is incomplete'))
279
279
280 skipped = set()
280 skipped = set()
281 # recompute the set of skipped revs
281 # recompute the set of skipped revs
282 if not collapse:
282 if not collapse:
283 seen = set(destmap.values())
283 seen = set(destmap.values())
284 for old, new in sorted(state.items()):
284 for old, new in sorted(state.items()):
285 if new != revtodo and new in seen:
285 if new != revtodo and new in seen:
286 skipped.add(old)
286 skipped.add(old)
287 seen.add(new)
287 seen.add(new)
288 repo.ui.debug('computed skipped revs: %s\n' %
288 repo.ui.debug('computed skipped revs: %s\n' %
289 (' '.join('%d' % r for r in sorted(skipped)) or ''))
289 (' '.join('%d' % r for r in sorted(skipped)) or ''))
290 repo.ui.debug('rebase status resumed\n')
290 repo.ui.debug('rebase status resumed\n')
291
291
292 self.originalwd = originalwd
292 self.originalwd = originalwd
293 self.destmap = destmap
293 self.destmap = destmap
294 self.state = state
294 self.state = state
295 self.skipped = skipped
295 self.skipped = skipped
296 self.collapsef = collapse
296 self.collapsef = collapse
297 self.keepf = keep
297 self.keepf = keep
298 self.keepbranchesf = keepbranches
298 self.keepbranchesf = keepbranches
299 self.external = external
299 self.external = external
300 self.activebookmark = activebookmark
300 self.activebookmark = activebookmark
301
301
302 def _handleskippingobsolete(self, obsoleterevs, destmap):
302 def _handleskippingobsolete(self, obsoleterevs, destmap):
303 """Compute structures necessary for skipping obsolete revisions
303 """Compute structures necessary for skipping obsolete revisions
304
304
305 obsoleterevs: iterable of all obsolete revisions in rebaseset
305 obsoleterevs: iterable of all obsolete revisions in rebaseset
306 destmap: {srcrev: destrev} destination revisions
306 destmap: {srcrev: destrev} destination revisions
307 """
307 """
308 self.obsoletenotrebased = {}
308 self.obsoletenotrebased = {}
309 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
309 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
310 return
310 return
311 obsoleteset = set(obsoleterevs)
311 obsoleteset = set(obsoleterevs)
312 (self.obsoletenotrebased,
312 (self.obsoletenotrebased,
313 self.obsoletewithoutsuccessorindestination,
313 self.obsoletewithoutsuccessorindestination,
314 obsoleteextinctsuccessors) = _computeobsoletenotrebased(
314 obsoleteextinctsuccessors) = _computeobsoletenotrebased(
315 self.repo, obsoleteset, destmap)
315 self.repo, obsoleteset, destmap)
316 skippedset = set(self.obsoletenotrebased)
316 skippedset = set(self.obsoletenotrebased)
317 skippedset.update(self.obsoletewithoutsuccessorindestination)
317 skippedset.update(self.obsoletewithoutsuccessorindestination)
318 skippedset.update(obsoleteextinctsuccessors)
318 skippedset.update(obsoleteextinctsuccessors)
319 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
319 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
320
320
321 def _prepareabortorcontinue(self, isabort):
321 def _prepareabortorcontinue(self, isabort):
322 try:
322 try:
323 self.restorestatus()
323 self.restorestatus()
324 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
324 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
325 except error.RepoLookupError:
325 except error.RepoLookupError:
326 if isabort:
326 if isabort:
327 clearstatus(self.repo)
327 clearstatus(self.repo)
328 clearcollapsemsg(self.repo)
328 clearcollapsemsg(self.repo)
329 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
329 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
330 ' only broken state is cleared)\n'))
330 ' only broken state is cleared)\n'))
331 return 0
331 return 0
332 else:
332 else:
333 msg = _('cannot continue inconsistent rebase')
333 msg = _('cannot continue inconsistent rebase')
334 hint = _('use "hg rebase --abort" to clear broken state')
334 hint = _('use "hg rebase --abort" to clear broken state')
335 raise error.Abort(msg, hint=hint)
335 raise error.Abort(msg, hint=hint)
336 if isabort:
336 if isabort:
337 return abort(self.repo, self.originalwd, self.destmap,
337 return abort(self.repo, self.originalwd, self.destmap,
338 self.state, activebookmark=self.activebookmark)
338 self.state, activebookmark=self.activebookmark)
339
339
340 def _preparenewrebase(self, destmap):
340 def _preparenewrebase(self, destmap):
341 if not destmap:
341 if not destmap:
342 return _nothingtorebase()
342 return _nothingtorebase()
343
343
344 rebaseset = destmap.keys()
344 rebaseset = destmap.keys()
345 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
345 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
346 if (not (self.keepf or allowunstable)
346 if (not (self.keepf or allowunstable)
347 and self.repo.revs('first(children(%ld) - %ld)',
347 and self.repo.revs('first(children(%ld) - %ld)',
348 rebaseset, rebaseset)):
348 rebaseset, rebaseset)):
349 raise error.Abort(
349 raise error.Abort(
350 _("can't remove original changesets with"
350 _("can't remove original changesets with"
351 " unrebased descendants"),
351 " unrebased descendants"),
352 hint=_('use --keep to keep original changesets'))
352 hint=_('use --keep to keep original changesets'))
353
353
354 result = buildstate(self.repo, destmap, self.collapsef)
354 result = buildstate(self.repo, destmap, self.collapsef)
355
355
356 if not result:
356 if not result:
357 # Empty state built, nothing to rebase
357 # Empty state built, nothing to rebase
358 self.ui.status(_('nothing to rebase\n'))
358 self.ui.status(_('nothing to rebase\n'))
359 return _nothingtorebase()
359 return _nothingtorebase()
360
360
361 for root in self.repo.set('roots(%ld)', rebaseset):
361 for root in self.repo.set('roots(%ld)', rebaseset):
362 if not self.keepf and not root.mutable():
362 if not self.keepf and not root.mutable():
363 raise error.Abort(_("can't rebase public changeset %s")
363 raise error.Abort(_("can't rebase public changeset %s")
364 % root,
364 % root,
365 hint=_("see 'hg help phases' for details"))
365 hint=_("see 'hg help phases' for details"))
366
366
367 (self.originalwd, self.destmap, self.state) = result
367 (self.originalwd, self.destmap, self.state) = result
368 if self.collapsef:
368 if self.collapsef:
369 dests = set(self.destmap.values())
369 dests = set(self.destmap.values())
370 if len(dests) != 1:
370 if len(dests) != 1:
371 raise error.Abort(
371 raise error.Abort(
372 _('--collapse does not work with multiple destinations'))
372 _('--collapse does not work with multiple destinations'))
373 destrev = next(iter(dests))
373 destrev = next(iter(dests))
374 destancestors = self.repo.changelog.ancestors([destrev],
374 destancestors = self.repo.changelog.ancestors([destrev],
375 inclusive=True)
375 inclusive=True)
376 self.external = externalparent(self.repo, self.state, destancestors)
376 self.external = externalparent(self.repo, self.state, destancestors)
377
377
378 for destrev in sorted(set(destmap.values())):
378 for destrev in sorted(set(destmap.values())):
379 dest = self.repo[destrev]
379 dest = self.repo[destrev]
380 if dest.closesbranch() and not self.keepbranchesf:
380 if dest.closesbranch() and not self.keepbranchesf:
381 self.ui.status(_('reopening closed branch head %s\n') % dest)
381 self.ui.status(_('reopening closed branch head %s\n') % dest)
382
382
383 self.prepared = True
383 self.prepared = True
384
384
385 def _assignworkingcopy(self):
385 def _assignworkingcopy(self):
386 if self.inmemory:
386 if self.inmemory:
387 from mercurial.context import overlayworkingctx
387 from mercurial.context import overlayworkingctx
388 self.wctx = overlayworkingctx(self.repo)
388 self.wctx = overlayworkingctx(self.repo)
389 self.repo.ui.debug("rebasing in-memory\n")
389 self.repo.ui.debug("rebasing in-memory\n")
390 else:
390 else:
391 self.wctx = self.repo[None]
391 self.wctx = self.repo[None]
392 self.repo.ui.debug("rebasing on disk\n")
392 self.repo.ui.debug("rebasing on disk\n")
393 self.repo.ui.log("rebase", "", rebase_imm_used=self.inmemory)
393 self.repo.ui.log("rebase", "", rebase_imm_used=self.inmemory)
394
394
395 def _performrebase(self, tr):
395 def _performrebase(self, tr):
396 self._assignworkingcopy()
396 self._assignworkingcopy()
397 repo, ui = self.repo, self.ui
397 repo, ui = self.repo, self.ui
398 if self.keepbranchesf:
398 if self.keepbranchesf:
399 # insert _savebranch at the start of extrafns so if
399 # insert _savebranch at the start of extrafns so if
400 # there's a user-provided extrafn it can clobber branch if
400 # there's a user-provided extrafn it can clobber branch if
401 # desired
401 # desired
402 self.extrafns.insert(0, _savebranch)
402 self.extrafns.insert(0, _savebranch)
403 if self.collapsef:
403 if self.collapsef:
404 branches = set()
404 branches = set()
405 for rev in self.state:
405 for rev in self.state:
406 branches.add(repo[rev].branch())
406 branches.add(repo[rev].branch())
407 if len(branches) > 1:
407 if len(branches) > 1:
408 raise error.Abort(_('cannot collapse multiple named '
408 raise error.Abort(_('cannot collapse multiple named '
409 'branches'))
409 'branches'))
410
410
411 # Calculate self.obsoletenotrebased
411 # Calculate self.obsoletenotrebased
412 obsrevs = _filterobsoleterevs(self.repo, self.state)
412 obsrevs = _filterobsoleterevs(self.repo, self.state)
413 self._handleskippingobsolete(obsrevs, self.destmap)
413 self._handleskippingobsolete(obsrevs, self.destmap)
414
414
415 # Keep track of the active bookmarks in order to reset them later
415 # Keep track of the active bookmarks in order to reset them later
416 self.activebookmark = self.activebookmark or repo._activebookmark
416 self.activebookmark = self.activebookmark or repo._activebookmark
417 if self.activebookmark:
417 if self.activebookmark:
418 bookmarks.deactivate(repo)
418 bookmarks.deactivate(repo)
419
419
420 # Store the state before we begin so users can run 'hg rebase --abort'
420 # Store the state before we begin so users can run 'hg rebase --abort'
421 # if we fail before the transaction closes.
421 # if we fail before the transaction closes.
422 self.storestatus()
422 self.storestatus()
423 if tr:
423 if tr:
424 # When using single transaction, store state when transaction
424 # When using single transaction, store state when transaction
425 # commits.
425 # commits.
426 self.storestatus(tr)
426 self.storestatus(tr)
427
427
428 cands = [k for k, v in self.state.iteritems() if v == revtodo]
428 cands = [k for k, v in self.state.iteritems() if v == revtodo]
429 total = len(cands)
429 total = len(cands)
430 posholder = [0]
430 posholder = [0]
431 def progress(ctx):
431 def progress(ctx):
432 posholder[0] += 1
432 posholder[0] += 1
433 self.repo.ui.progress(_("rebasing"), posholder[0],
433 self.repo.ui.progress(_("rebasing"), posholder[0],
434 ("%d:%s" % (ctx.rev(), ctx)),
434 ("%d:%s" % (ctx.rev(), ctx)),
435 _('changesets'), total)
435 _('changesets'), total)
436 allowdivergence = self.ui.configbool(
436 allowdivergence = self.ui.configbool(
437 'experimental', 'evolution.allowdivergence')
437 'experimental', 'evolution.allowdivergence')
438 for subset in sortsource(self.destmap):
438 for subset in sortsource(self.destmap):
439 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
439 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
440 if not allowdivergence:
440 if not allowdivergence:
441 sortedrevs -= self.repo.revs(
441 sortedrevs -= self.repo.revs(
442 'descendants(%ld) and not %ld',
442 'descendants(%ld) and not %ld',
443 self.obsoletewithoutsuccessorindestination,
443 self.obsoletewithoutsuccessorindestination,
444 self.obsoletewithoutsuccessorindestination,
444 self.obsoletewithoutsuccessorindestination,
445 )
445 )
446 for rev in sortedrevs:
446 for rev in sortedrevs:
447 self._rebasenode(tr, rev, allowdivergence, progress)
447 self._rebasenode(tr, rev, allowdivergence, progress)
448 ui.progress(_('rebasing'), None)
448 ui.progress(_('rebasing'), None)
449 ui.note(_('rebase merging completed\n'))
449 ui.note(_('rebase merging completed\n'))
450
450
451 def _rebasenode(self, tr, rev, allowdivergence, progressfn):
451 def _rebasenode(self, tr, rev, allowdivergence, progressfn):
452 repo, ui, opts = self.repo, self.ui, self.opts
452 repo, ui, opts = self.repo, self.ui, self.opts
453 dest = self.destmap[rev]
453 dest = self.destmap[rev]
454 ctx = repo[rev]
454 ctx = repo[rev]
455 desc = _ctxdesc(ctx)
455 desc = _ctxdesc(ctx)
456 if self.state[rev] == rev:
456 if self.state[rev] == rev:
457 ui.status(_('already rebased %s\n') % desc)
457 ui.status(_('already rebased %s\n') % desc)
458 elif (not allowdivergence
458 elif (not allowdivergence
459 and rev in self.obsoletewithoutsuccessorindestination):
459 and rev in self.obsoletewithoutsuccessorindestination):
460 msg = _('note: not rebasing %s and its descendants as '
460 msg = _('note: not rebasing %s and its descendants as '
461 'this would cause divergence\n') % desc
461 'this would cause divergence\n') % desc
462 repo.ui.status(msg)
462 repo.ui.status(msg)
463 self.skipped.add(rev)
463 self.skipped.add(rev)
464 elif rev in self.obsoletenotrebased:
464 elif rev in self.obsoletenotrebased:
465 succ = self.obsoletenotrebased[rev]
465 succ = self.obsoletenotrebased[rev]
466 if succ is None:
466 if succ is None:
467 msg = _('note: not rebasing %s, it has no '
467 msg = _('note: not rebasing %s, it has no '
468 'successor\n') % desc
468 'successor\n') % desc
469 else:
469 else:
470 succdesc = _ctxdesc(repo[succ])
470 succdesc = _ctxdesc(repo[succ])
471 msg = (_('note: not rebasing %s, already in '
471 msg = (_('note: not rebasing %s, already in '
472 'destination as %s\n') % (desc, succdesc))
472 'destination as %s\n') % (desc, succdesc))
473 repo.ui.status(msg)
473 repo.ui.status(msg)
474 # Make clearrebased aware state[rev] is not a true successor
474 # Make clearrebased aware state[rev] is not a true successor
475 self.skipped.add(rev)
475 self.skipped.add(rev)
476 # Record rev as moved to its desired destination in self.state.
476 # Record rev as moved to its desired destination in self.state.
477 # This helps bookmark and working parent movement.
477 # This helps bookmark and working parent movement.
478 dest = max(adjustdest(repo, rev, self.destmap, self.state,
478 dest = max(adjustdest(repo, rev, self.destmap, self.state,
479 self.skipped))
479 self.skipped))
480 self.state[rev] = dest
480 self.state[rev] = dest
481 elif self.state[rev] == revtodo:
481 elif self.state[rev] == revtodo:
482 ui.status(_('rebasing %s\n') % desc)
482 ui.status(_('rebasing %s\n') % desc)
483 progressfn(ctx)
483 progressfn(ctx)
484 p1, p2, base = defineparents(repo, rev, self.destmap,
484 p1, p2, base = defineparents(repo, rev, self.destmap,
485 self.state, self.skipped,
485 self.state, self.skipped,
486 self.obsoletenotrebased)
486 self.obsoletenotrebased)
487 if not tr:
488 self.storestatus()
489 if len(repo[None].parents()) == 2:
487 if len(repo[None].parents()) == 2:
490 repo.ui.debug('resuming interrupted rebase\n')
488 repo.ui.debug('resuming interrupted rebase\n')
491 else:
489 else:
492 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
490 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
493 with ui.configoverride(overrides, 'rebase'):
491 with ui.configoverride(overrides, 'rebase'):
494 stats = rebasenode(repo, rev, p1, base, self.collapsef,
492 stats = rebasenode(repo, rev, p1, base, self.collapsef,
495 dest, wctx=self.wctx)
493 dest, wctx=self.wctx)
496 if stats[3] > 0:
494 if stats[3] > 0:
497 if self.inmemory:
495 if self.inmemory:
498 raise error.InMemoryMergeConflictsError()
496 raise error.InMemoryMergeConflictsError()
499 else:
497 else:
500 raise error.InterventionRequired(
498 raise error.InterventionRequired(
501 _('unresolved conflicts (see hg '
499 _('unresolved conflicts (see hg '
502 'resolve, then hg rebase --continue)'))
500 'resolve, then hg rebase --continue)'))
503 if not self.collapsef:
501 if not self.collapsef:
504 merging = p2 != nullrev
502 merging = p2 != nullrev
505 editform = cmdutil.mergeeditform(merging, 'rebase')
503 editform = cmdutil.mergeeditform(merging, 'rebase')
506 editor = cmdutil.getcommiteditor(editform=editform,
504 editor = cmdutil.getcommiteditor(editform=editform,
507 **pycompat.strkwargs(opts))
505 **pycompat.strkwargs(opts))
508 if self.inmemory:
506 if self.inmemory:
509 newnode = concludememorynode(repo, rev, p1, p2,
507 newnode = concludememorynode(repo, rev, p1, p2,
510 wctx=self.wctx,
508 wctx=self.wctx,
511 extrafn=_makeextrafn(self.extrafns),
509 extrafn=_makeextrafn(self.extrafns),
512 editor=editor,
510 editor=editor,
513 keepbranches=self.keepbranchesf,
511 keepbranches=self.keepbranchesf,
514 date=self.date)
512 date=self.date)
515 mergemod.mergestate.clean(repo)
513 mergemod.mergestate.clean(repo)
516 else:
514 else:
517 newnode = concludenode(repo, rev, p1, p2,
515 newnode = concludenode(repo, rev, p1, p2,
518 extrafn=_makeextrafn(self.extrafns),
516 extrafn=_makeextrafn(self.extrafns),
519 editor=editor,
517 editor=editor,
520 keepbranches=self.keepbranchesf,
518 keepbranches=self.keepbranchesf,
521 date=self.date)
519 date=self.date)
522
520
523 if newnode is None:
521 if newnode is None:
524 # If it ended up being a no-op commit, then the normal
522 # If it ended up being a no-op commit, then the normal
525 # merge state clean-up path doesn't happen, so do it
523 # merge state clean-up path doesn't happen, so do it
526 # here. Fix issue5494
524 # here. Fix issue5494
527 mergemod.mergestate.clean(repo)
525 mergemod.mergestate.clean(repo)
528 else:
526 else:
529 # Skip commit if we are collapsing
527 # Skip commit if we are collapsing
530 if self.inmemory:
528 if self.inmemory:
531 self.wctx.setbase(repo[p1])
529 self.wctx.setbase(repo[p1])
532 else:
530 else:
533 repo.setparents(repo[p1].node())
531 repo.setparents(repo[p1].node())
534 newnode = None
532 newnode = None
535 # Update the state
533 # Update the state
536 if newnode is not None:
534 if newnode is not None:
537 self.state[rev] = repo[newnode].rev()
535 self.state[rev] = repo[newnode].rev()
538 ui.debug('rebased as %s\n' % short(newnode))
536 ui.debug('rebased as %s\n' % short(newnode))
539 else:
537 else:
540 if not self.collapsef:
538 if not self.collapsef:
541 ui.warn(_('note: rebase of %d:%s created no changes '
539 ui.warn(_('note: rebase of %d:%s created no changes '
542 'to commit\n') % (rev, ctx))
540 'to commit\n') % (rev, ctx))
543 self.skipped.add(rev)
541 self.skipped.add(rev)
544 self.state[rev] = p1
542 self.state[rev] = p1
545 ui.debug('next revision set to %d\n' % p1)
543 ui.debug('next revision set to %d\n' % p1)
546 else:
544 else:
547 ui.status(_('already rebased %s as %s\n') %
545 ui.status(_('already rebased %s as %s\n') %
548 (desc, repo[self.state[rev]]))
546 (desc, repo[self.state[rev]]))
547 if not tr:
548 # When not using single transaction, store state after each
549 # commit is completely done. On InterventionRequired, we thus
550 # won't store the status. Instead, we'll hit the "len(parents) == 2"
551 # case and realize that the commit was in progress.
552 self.storestatus()
549
553
550 def _finishrebase(self):
554 def _finishrebase(self):
551 repo, ui, opts = self.repo, self.ui, self.opts
555 repo, ui, opts = self.repo, self.ui, self.opts
552 fm = ui.formatter('rebase', opts)
556 fm = ui.formatter('rebase', opts)
553 fm.startitem()
557 fm.startitem()
554 if self.collapsef:
558 if self.collapsef:
555 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
559 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
556 self.state, self.skipped,
560 self.state, self.skipped,
557 self.obsoletenotrebased)
561 self.obsoletenotrebased)
558 editopt = opts.get('edit')
562 editopt = opts.get('edit')
559 editform = 'rebase.collapse'
563 editform = 'rebase.collapse'
560 if self.collapsemsg:
564 if self.collapsemsg:
561 commitmsg = self.collapsemsg
565 commitmsg = self.collapsemsg
562 else:
566 else:
563 commitmsg = 'Collapsed revision'
567 commitmsg = 'Collapsed revision'
564 for rebased in sorted(self.state):
568 for rebased in sorted(self.state):
565 if rebased not in self.skipped:
569 if rebased not in self.skipped:
566 commitmsg += '\n* %s' % repo[rebased].description()
570 commitmsg += '\n* %s' % repo[rebased].description()
567 editopt = True
571 editopt = True
568 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
572 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
569 revtoreuse = max(self.state)
573 revtoreuse = max(self.state)
570
574
571 if self.inmemory:
575 if self.inmemory:
572 newnode = concludememorynode(repo, revtoreuse, p1,
576 newnode = concludememorynode(repo, revtoreuse, p1,
573 self.external,
577 self.external,
574 commitmsg=commitmsg,
578 commitmsg=commitmsg,
575 extrafn=_makeextrafn(self.extrafns),
579 extrafn=_makeextrafn(self.extrafns),
576 editor=editor,
580 editor=editor,
577 keepbranches=self.keepbranchesf,
581 keepbranches=self.keepbranchesf,
578 date=self.date, wctx=self.wctx)
582 date=self.date, wctx=self.wctx)
579 else:
583 else:
580 newnode = concludenode(repo, revtoreuse, p1, self.external,
584 newnode = concludenode(repo, revtoreuse, p1, self.external,
581 commitmsg=commitmsg,
585 commitmsg=commitmsg,
582 extrafn=_makeextrafn(self.extrafns),
586 extrafn=_makeextrafn(self.extrafns),
583 editor=editor,
587 editor=editor,
584 keepbranches=self.keepbranchesf,
588 keepbranches=self.keepbranchesf,
585 date=self.date)
589 date=self.date)
586
590
587 if newnode is None:
591 if newnode is None:
588 # If it ended up being a no-op commit, then the normal
592 # If it ended up being a no-op commit, then the normal
589 # merge state clean-up path doesn't happen, so do it
593 # merge state clean-up path doesn't happen, so do it
590 # here. Fix issue5494
594 # here. Fix issue5494
591 mergemod.mergestate.clean(repo)
595 mergemod.mergestate.clean(repo)
592 if newnode is not None:
596 if newnode is not None:
593 newrev = repo[newnode].rev()
597 newrev = repo[newnode].rev()
594 for oldrev in self.state:
598 for oldrev in self.state:
595 self.state[oldrev] = newrev
599 self.state[oldrev] = newrev
596
600
597 if 'qtip' in repo.tags():
601 if 'qtip' in repo.tags():
598 updatemq(repo, self.state, self.skipped,
602 updatemq(repo, self.state, self.skipped,
599 **pycompat.strkwargs(opts))
603 **pycompat.strkwargs(opts))
600
604
601 # restore original working directory
605 # restore original working directory
602 # (we do this before stripping)
606 # (we do this before stripping)
603 newwd = self.state.get(self.originalwd, self.originalwd)
607 newwd = self.state.get(self.originalwd, self.originalwd)
604 if newwd < 0:
608 if newwd < 0:
605 # original directory is a parent of rebase set root or ignored
609 # original directory is a parent of rebase set root or ignored
606 newwd = self.originalwd
610 newwd = self.originalwd
607 if newwd not in [c.rev() for c in repo[None].parents()]:
611 if newwd not in [c.rev() for c in repo[None].parents()]:
608 ui.note(_("update back to initial working directory parent\n"))
612 ui.note(_("update back to initial working directory parent\n"))
609 hg.updaterepo(repo, newwd, False)
613 hg.updaterepo(repo, newwd, False)
610
614
611 collapsedas = None
615 collapsedas = None
612 if self.collapsef and not self.keepf:
616 if self.collapsef and not self.keepf:
613 collapsedas = newnode
617 collapsedas = newnode
614 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
618 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
615 collapsedas, self.keepf, fm=fm)
619 collapsedas, self.keepf, fm=fm)
616
620
617 clearstatus(repo)
621 clearstatus(repo)
618 clearcollapsemsg(repo)
622 clearcollapsemsg(repo)
619
623
620 ui.note(_("rebase completed\n"))
624 ui.note(_("rebase completed\n"))
621 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
625 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
622 if self.skipped:
626 if self.skipped:
623 skippedlen = len(self.skipped)
627 skippedlen = len(self.skipped)
624 ui.note(_("%d revisions have been skipped\n") % skippedlen)
628 ui.note(_("%d revisions have been skipped\n") % skippedlen)
625 fm.end()
629 fm.end()
626
630
627 if (self.activebookmark and self.activebookmark in repo._bookmarks and
631 if (self.activebookmark and self.activebookmark in repo._bookmarks and
628 repo['.'].node() == repo._bookmarks[self.activebookmark]):
632 repo['.'].node() == repo._bookmarks[self.activebookmark]):
629 bookmarks.activate(repo, self.activebookmark)
633 bookmarks.activate(repo, self.activebookmark)
630
634
631 @command('rebase',
635 @command('rebase',
632 [('s', 'source', '',
636 [('s', 'source', '',
633 _('rebase the specified changeset and descendants'), _('REV')),
637 _('rebase the specified changeset and descendants'), _('REV')),
634 ('b', 'base', '',
638 ('b', 'base', '',
635 _('rebase everything from branching point of specified changeset'),
639 _('rebase everything from branching point of specified changeset'),
636 _('REV')),
640 _('REV')),
637 ('r', 'rev', [],
641 ('r', 'rev', [],
638 _('rebase these revisions'),
642 _('rebase these revisions'),
639 _('REV')),
643 _('REV')),
640 ('d', 'dest', '',
644 ('d', 'dest', '',
641 _('rebase onto the specified changeset'), _('REV')),
645 _('rebase onto the specified changeset'), _('REV')),
642 ('', 'collapse', False, _('collapse the rebased changesets')),
646 ('', 'collapse', False, _('collapse the rebased changesets')),
643 ('m', 'message', '',
647 ('m', 'message', '',
644 _('use text as collapse commit message'), _('TEXT')),
648 _('use text as collapse commit message'), _('TEXT')),
645 ('e', 'edit', False, _('invoke editor on commit messages')),
649 ('e', 'edit', False, _('invoke editor on commit messages')),
646 ('l', 'logfile', '',
650 ('l', 'logfile', '',
647 _('read collapse commit message from file'), _('FILE')),
651 _('read collapse commit message from file'), _('FILE')),
648 ('k', 'keep', False, _('keep original changesets')),
652 ('k', 'keep', False, _('keep original changesets')),
649 ('', 'keepbranches', False, _('keep original branch names')),
653 ('', 'keepbranches', False, _('keep original branch names')),
650 ('D', 'detach', False, _('(DEPRECATED)')),
654 ('D', 'detach', False, _('(DEPRECATED)')),
651 ('i', 'interactive', False, _('(DEPRECATED)')),
655 ('i', 'interactive', False, _('(DEPRECATED)')),
652 ('t', 'tool', '', _('specify merge tool')),
656 ('t', 'tool', '', _('specify merge tool')),
653 ('c', 'continue', False, _('continue an interrupted rebase')),
657 ('c', 'continue', False, _('continue an interrupted rebase')),
654 ('a', 'abort', False, _('abort an interrupted rebase'))] +
658 ('a', 'abort', False, _('abort an interrupted rebase'))] +
655 cmdutil.formatteropts,
659 cmdutil.formatteropts,
656 _('[-s REV | -b REV] [-d REV] [OPTION]'))
660 _('[-s REV | -b REV] [-d REV] [OPTION]'))
657 def rebase(ui, repo, **opts):
661 def rebase(ui, repo, **opts):
658 """move changeset (and descendants) to a different branch
662 """move changeset (and descendants) to a different branch
659
663
660 Rebase uses repeated merging to graft changesets from one part of
664 Rebase uses repeated merging to graft changesets from one part of
661 history (the source) onto another (the destination). This can be
665 history (the source) onto another (the destination). This can be
662 useful for linearizing *local* changes relative to a master
666 useful for linearizing *local* changes relative to a master
663 development tree.
667 development tree.
664
668
665 Published commits cannot be rebased (see :hg:`help phases`).
669 Published commits cannot be rebased (see :hg:`help phases`).
666 To copy commits, see :hg:`help graft`.
670 To copy commits, see :hg:`help graft`.
667
671
668 If you don't specify a destination changeset (``-d/--dest``), rebase
672 If you don't specify a destination changeset (``-d/--dest``), rebase
669 will use the same logic as :hg:`merge` to pick a destination. if
673 will use the same logic as :hg:`merge` to pick a destination. if
670 the current branch contains exactly one other head, the other head
674 the current branch contains exactly one other head, the other head
671 is merged with by default. Otherwise, an explicit revision with
675 is merged with by default. Otherwise, an explicit revision with
672 which to merge with must be provided. (destination changeset is not
676 which to merge with must be provided. (destination changeset is not
673 modified by rebasing, but new changesets are added as its
677 modified by rebasing, but new changesets are added as its
674 descendants.)
678 descendants.)
675
679
676 Here are the ways to select changesets:
680 Here are the ways to select changesets:
677
681
678 1. Explicitly select them using ``--rev``.
682 1. Explicitly select them using ``--rev``.
679
683
680 2. Use ``--source`` to select a root changeset and include all of its
684 2. Use ``--source`` to select a root changeset and include all of its
681 descendants.
685 descendants.
682
686
683 3. Use ``--base`` to select a changeset; rebase will find ancestors
687 3. Use ``--base`` to select a changeset; rebase will find ancestors
684 and their descendants which are not also ancestors of the destination.
688 and their descendants which are not also ancestors of the destination.
685
689
686 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
690 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
687 rebase will use ``--base .`` as above.
691 rebase will use ``--base .`` as above.
688
692
689 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
693 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
690 can be used in ``--dest``. Destination would be calculated per source
694 can be used in ``--dest``. Destination would be calculated per source
691 revision with ``SRC`` substituted by that single source revision and
695 revision with ``SRC`` substituted by that single source revision and
692 ``ALLSRC`` substituted by all source revisions.
696 ``ALLSRC`` substituted by all source revisions.
693
697
694 Rebase will destroy original changesets unless you use ``--keep``.
698 Rebase will destroy original changesets unless you use ``--keep``.
695 It will also move your bookmarks (even if you do).
699 It will also move your bookmarks (even if you do).
696
700
697 Some changesets may be dropped if they do not contribute changes
701 Some changesets may be dropped if they do not contribute changes
698 (e.g. merges from the destination branch).
702 (e.g. merges from the destination branch).
699
703
700 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
704 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
701 a named branch with two heads. You will need to explicitly specify source
705 a named branch with two heads. You will need to explicitly specify source
702 and/or destination.
706 and/or destination.
703
707
704 If you need to use a tool to automate merge/conflict decisions, you
708 If you need to use a tool to automate merge/conflict decisions, you
705 can specify one with ``--tool``, see :hg:`help merge-tools`.
709 can specify one with ``--tool``, see :hg:`help merge-tools`.
706 As a caveat: the tool will not be used to mediate when a file was
710 As a caveat: the tool will not be used to mediate when a file was
707 deleted, there is no hook presently available for this.
711 deleted, there is no hook presently available for this.
708
712
709 If a rebase is interrupted to manually resolve a conflict, it can be
713 If a rebase is interrupted to manually resolve a conflict, it can be
710 continued with --continue/-c or aborted with --abort/-a.
714 continued with --continue/-c or aborted with --abort/-a.
711
715
712 .. container:: verbose
716 .. container:: verbose
713
717
714 Examples:
718 Examples:
715
719
716 - move "local changes" (current commit back to branching point)
720 - move "local changes" (current commit back to branching point)
717 to the current branch tip after a pull::
721 to the current branch tip after a pull::
718
722
719 hg rebase
723 hg rebase
720
724
721 - move a single changeset to the stable branch::
725 - move a single changeset to the stable branch::
722
726
723 hg rebase -r 5f493448 -d stable
727 hg rebase -r 5f493448 -d stable
724
728
725 - splice a commit and all its descendants onto another part of history::
729 - splice a commit and all its descendants onto another part of history::
726
730
727 hg rebase --source c0c3 --dest 4cf9
731 hg rebase --source c0c3 --dest 4cf9
728
732
729 - rebase everything on a branch marked by a bookmark onto the
733 - rebase everything on a branch marked by a bookmark onto the
730 default branch::
734 default branch::
731
735
732 hg rebase --base myfeature --dest default
736 hg rebase --base myfeature --dest default
733
737
734 - collapse a sequence of changes into a single commit::
738 - collapse a sequence of changes into a single commit::
735
739
736 hg rebase --collapse -r 1520:1525 -d .
740 hg rebase --collapse -r 1520:1525 -d .
737
741
738 - move a named branch while preserving its name::
742 - move a named branch while preserving its name::
739
743
740 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
744 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
741
745
742 - stabilize orphaned changesets so history looks linear::
746 - stabilize orphaned changesets so history looks linear::
743
747
744 hg rebase -r 'orphan()-obsolete()'\
748 hg rebase -r 'orphan()-obsolete()'\
745 -d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
749 -d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
746 max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
750 max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
747
751
748 Configuration Options:
752 Configuration Options:
749
753
750 You can make rebase require a destination if you set the following config
754 You can make rebase require a destination if you set the following config
751 option::
755 option::
752
756
753 [commands]
757 [commands]
754 rebase.requiredest = True
758 rebase.requiredest = True
755
759
756 By default, rebase will close the transaction after each commit. For
760 By default, rebase will close the transaction after each commit. For
757 performance purposes, you can configure rebase to use a single transaction
761 performance purposes, you can configure rebase to use a single transaction
758 across the entire rebase. WARNING: This setting introduces a significant
762 across the entire rebase. WARNING: This setting introduces a significant
759 risk of losing the work you've done in a rebase if the rebase aborts
763 risk of losing the work you've done in a rebase if the rebase aborts
760 unexpectedly::
764 unexpectedly::
761
765
762 [rebase]
766 [rebase]
763 singletransaction = True
767 singletransaction = True
764
768
765 By default, rebase writes to the working copy, but you can configure it to
769 By default, rebase writes to the working copy, but you can configure it to
766 run in-memory for for better performance, and to allow it to run if the
770 run in-memory for for better performance, and to allow it to run if the
767 working copy is dirty::
771 working copy is dirty::
768
772
769 [rebase]
773 [rebase]
770 experimental.inmemory = True
774 experimental.inmemory = True
771
775
772 Return Values:
776 Return Values:
773
777
774 Returns 0 on success, 1 if nothing to rebase or there are
778 Returns 0 on success, 1 if nothing to rebase or there are
775 unresolved conflicts.
779 unresolved conflicts.
776
780
777 """
781 """
778 inmemory = ui.configbool('rebase', 'experimental.inmemory')
782 inmemory = ui.configbool('rebase', 'experimental.inmemory')
779 if (opts.get('continue') or opts.get('abort') or
783 if (opts.get('continue') or opts.get('abort') or
780 repo.currenttransaction() is not None):
784 repo.currenttransaction() is not None):
781 # in-memory rebase is not compatible with resuming rebases.
785 # in-memory rebase is not compatible with resuming rebases.
782 # (Or if it is run within a transaction, since the restart logic can
786 # (Or if it is run within a transaction, since the restart logic can
783 # fail the entire transaction.)
787 # fail the entire transaction.)
784 inmemory = False
788 inmemory = False
785
789
786 if inmemory:
790 if inmemory:
787 try:
791 try:
788 # in-memory merge doesn't support conflicts, so if we hit any, abort
792 # in-memory merge doesn't support conflicts, so if we hit any, abort
789 # and re-run as an on-disk merge.
793 # and re-run as an on-disk merge.
790 return _origrebase(ui, repo, inmemory=inmemory, **opts)
794 return _origrebase(ui, repo, inmemory=inmemory, **opts)
791 except error.InMemoryMergeConflictsError:
795 except error.InMemoryMergeConflictsError:
792 ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
796 ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
793 ' merge\n'))
797 ' merge\n'))
794 _origrebase(ui, repo, **{'abort': True})
798 _origrebase(ui, repo, **{'abort': True})
795 return _origrebase(ui, repo, inmemory=False, **opts)
799 return _origrebase(ui, repo, inmemory=False, **opts)
796 else:
800 else:
797 return _origrebase(ui, repo, **opts)
801 return _origrebase(ui, repo, **opts)
798
802
799 def _origrebase(ui, repo, inmemory=False, **opts):
803 def _origrebase(ui, repo, inmemory=False, **opts):
800 opts = pycompat.byteskwargs(opts)
804 opts = pycompat.byteskwargs(opts)
801 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
805 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
802
806
803 with repo.wlock(), repo.lock():
807 with repo.wlock(), repo.lock():
804 # Validate input and define rebasing points
808 # Validate input and define rebasing points
805 destf = opts.get('dest', None)
809 destf = opts.get('dest', None)
806 srcf = opts.get('source', None)
810 srcf = opts.get('source', None)
807 basef = opts.get('base', None)
811 basef = opts.get('base', None)
808 revf = opts.get('rev', [])
812 revf = opts.get('rev', [])
809 # search default destination in this space
813 # search default destination in this space
810 # used in the 'hg pull --rebase' case, see issue 5214.
814 # used in the 'hg pull --rebase' case, see issue 5214.
811 destspace = opts.get('_destspace')
815 destspace = opts.get('_destspace')
812 contf = opts.get('continue')
816 contf = opts.get('continue')
813 abortf = opts.get('abort')
817 abortf = opts.get('abort')
814 if opts.get('interactive'):
818 if opts.get('interactive'):
815 try:
819 try:
816 if extensions.find('histedit'):
820 if extensions.find('histedit'):
817 enablehistedit = ''
821 enablehistedit = ''
818 except KeyError:
822 except KeyError:
819 enablehistedit = " --config extensions.histedit="
823 enablehistedit = " --config extensions.histedit="
820 help = "hg%s help -e histedit" % enablehistedit
824 help = "hg%s help -e histedit" % enablehistedit
821 msg = _("interactive history editing is supported by the "
825 msg = _("interactive history editing is supported by the "
822 "'histedit' extension (see \"%s\")") % help
826 "'histedit' extension (see \"%s\")") % help
823 raise error.Abort(msg)
827 raise error.Abort(msg)
824
828
825 if rbsrt.collapsemsg and not rbsrt.collapsef:
829 if rbsrt.collapsemsg and not rbsrt.collapsef:
826 raise error.Abort(
830 raise error.Abort(
827 _('message can only be specified with collapse'))
831 _('message can only be specified with collapse'))
828
832
829 if contf or abortf:
833 if contf or abortf:
830 if contf and abortf:
834 if contf and abortf:
831 raise error.Abort(_('cannot use both abort and continue'))
835 raise error.Abort(_('cannot use both abort and continue'))
832 if rbsrt.collapsef:
836 if rbsrt.collapsef:
833 raise error.Abort(
837 raise error.Abort(
834 _('cannot use collapse with continue or abort'))
838 _('cannot use collapse with continue or abort'))
835 if srcf or basef or destf:
839 if srcf or basef or destf:
836 raise error.Abort(
840 raise error.Abort(
837 _('abort and continue do not allow specifying revisions'))
841 _('abort and continue do not allow specifying revisions'))
838 if abortf and opts.get('tool', False):
842 if abortf and opts.get('tool', False):
839 ui.warn(_('tool option will be ignored\n'))
843 ui.warn(_('tool option will be ignored\n'))
840 if contf:
844 if contf:
841 ms = mergemod.mergestate.read(repo)
845 ms = mergemod.mergestate.read(repo)
842 mergeutil.checkunresolved(ms)
846 mergeutil.checkunresolved(ms)
843
847
844 retcode = rbsrt._prepareabortorcontinue(abortf)
848 retcode = rbsrt._prepareabortorcontinue(abortf)
845 if retcode is not None:
849 if retcode is not None:
846 return retcode
850 return retcode
847 else:
851 else:
848 destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef,
852 destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef,
849 revf, destspace=destspace)
853 revf, destspace=destspace)
850 retcode = rbsrt._preparenewrebase(destmap)
854 retcode = rbsrt._preparenewrebase(destmap)
851 if retcode is not None:
855 if retcode is not None:
852 return retcode
856 return retcode
853 storecollapsemsg(repo, rbsrt.collapsemsg)
857 storecollapsemsg(repo, rbsrt.collapsemsg)
854
858
855 tr = None
859 tr = None
856
860
857 singletr = ui.configbool('rebase', 'singletransaction')
861 singletr = ui.configbool('rebase', 'singletransaction')
858 if singletr:
862 if singletr:
859 tr = repo.transaction('rebase')
863 tr = repo.transaction('rebase')
860
864
861 # If `rebase.singletransaction` is enabled, wrap the entire operation in
865 # If `rebase.singletransaction` is enabled, wrap the entire operation in
862 # one transaction here. Otherwise, transactions are obtained when
866 # one transaction here. Otherwise, transactions are obtained when
863 # committing each node, which is slower but allows partial success.
867 # committing each node, which is slower but allows partial success.
864 with util.acceptintervention(tr):
868 with util.acceptintervention(tr):
865 # Same logic for the dirstate guard, except we don't create one when
869 # Same logic for the dirstate guard, except we don't create one when
866 # rebasing in-memory (it's not needed).
870 # rebasing in-memory (it's not needed).
867 dsguard = None
871 dsguard = None
868 if singletr and not inmemory:
872 if singletr and not inmemory:
869 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
873 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
870 with util.acceptintervention(dsguard):
874 with util.acceptintervention(dsguard):
871 rbsrt._performrebase(tr)
875 rbsrt._performrebase(tr)
872 rbsrt._finishrebase()
876 rbsrt._finishrebase()
873
877
874 def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None,
878 def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None,
875 revf=None, destspace=None):
879 revf=None, destspace=None):
876 """use revisions argument to define destmap {srcrev: destrev}"""
880 """use revisions argument to define destmap {srcrev: destrev}"""
877 if revf is None:
881 if revf is None:
878 revf = []
882 revf = []
879
883
880 # destspace is here to work around issues with `hg pull --rebase` see
884 # destspace is here to work around issues with `hg pull --rebase` see
881 # issue5214 for details
885 # issue5214 for details
882 if srcf and basef:
886 if srcf and basef:
883 raise error.Abort(_('cannot specify both a source and a base'))
887 raise error.Abort(_('cannot specify both a source and a base'))
884 if revf and basef:
888 if revf and basef:
885 raise error.Abort(_('cannot specify both a revision and a base'))
889 raise error.Abort(_('cannot specify both a revision and a base'))
886 if revf and srcf:
890 if revf and srcf:
887 raise error.Abort(_('cannot specify both a revision and a source'))
891 raise error.Abort(_('cannot specify both a revision and a source'))
888
892
889 if not inmemory:
893 if not inmemory:
890 cmdutil.checkunfinished(repo)
894 cmdutil.checkunfinished(repo)
891 cmdutil.bailifchanged(repo)
895 cmdutil.bailifchanged(repo)
892
896
893 if ui.configbool('commands', 'rebase.requiredest') and not destf:
897 if ui.configbool('commands', 'rebase.requiredest') and not destf:
894 raise error.Abort(_('you must specify a destination'),
898 raise error.Abort(_('you must specify a destination'),
895 hint=_('use: hg rebase -d REV'))
899 hint=_('use: hg rebase -d REV'))
896
900
897 dest = None
901 dest = None
898
902
899 if revf:
903 if revf:
900 rebaseset = scmutil.revrange(repo, revf)
904 rebaseset = scmutil.revrange(repo, revf)
901 if not rebaseset:
905 if not rebaseset:
902 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
906 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
903 return None
907 return None
904 elif srcf:
908 elif srcf:
905 src = scmutil.revrange(repo, [srcf])
909 src = scmutil.revrange(repo, [srcf])
906 if not src:
910 if not src:
907 ui.status(_('empty "source" revision set - nothing to rebase\n'))
911 ui.status(_('empty "source" revision set - nothing to rebase\n'))
908 return None
912 return None
909 rebaseset = repo.revs('(%ld)::', src)
913 rebaseset = repo.revs('(%ld)::', src)
910 assert rebaseset
914 assert rebaseset
911 else:
915 else:
912 base = scmutil.revrange(repo, [basef or '.'])
916 base = scmutil.revrange(repo, [basef or '.'])
913 if not base:
917 if not base:
914 ui.status(_('empty "base" revision set - '
918 ui.status(_('empty "base" revision set - '
915 "can't compute rebase set\n"))
919 "can't compute rebase set\n"))
916 return None
920 return None
917 if destf:
921 if destf:
918 # --base does not support multiple destinations
922 # --base does not support multiple destinations
919 dest = scmutil.revsingle(repo, destf)
923 dest = scmutil.revsingle(repo, destf)
920 else:
924 else:
921 dest = repo[_destrebase(repo, base, destspace=destspace)]
925 dest = repo[_destrebase(repo, base, destspace=destspace)]
922 destf = bytes(dest)
926 destf = bytes(dest)
923
927
924 roots = [] # selected children of branching points
928 roots = [] # selected children of branching points
925 bpbase = {} # {branchingpoint: [origbase]}
929 bpbase = {} # {branchingpoint: [origbase]}
926 for b in base: # group bases by branching points
930 for b in base: # group bases by branching points
927 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
931 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
928 bpbase[bp] = bpbase.get(bp, []) + [b]
932 bpbase[bp] = bpbase.get(bp, []) + [b]
929 if None in bpbase:
933 if None in bpbase:
930 # emulate the old behavior, showing "nothing to rebase" (a better
934 # emulate the old behavior, showing "nothing to rebase" (a better
931 # behavior may be abort with "cannot find branching point" error)
935 # behavior may be abort with "cannot find branching point" error)
932 bpbase.clear()
936 bpbase.clear()
933 for bp, bs in bpbase.iteritems(): # calculate roots
937 for bp, bs in bpbase.iteritems(): # calculate roots
934 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
938 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
935
939
936 rebaseset = repo.revs('%ld::', roots)
940 rebaseset = repo.revs('%ld::', roots)
937
941
938 if not rebaseset:
942 if not rebaseset:
939 # transform to list because smartsets are not comparable to
943 # transform to list because smartsets are not comparable to
940 # lists. This should be improved to honor laziness of
944 # lists. This should be improved to honor laziness of
941 # smartset.
945 # smartset.
942 if list(base) == [dest.rev()]:
946 if list(base) == [dest.rev()]:
943 if basef:
947 if basef:
944 ui.status(_('nothing to rebase - %s is both "base"'
948 ui.status(_('nothing to rebase - %s is both "base"'
945 ' and destination\n') % dest)
949 ' and destination\n') % dest)
946 else:
950 else:
947 ui.status(_('nothing to rebase - working directory '
951 ui.status(_('nothing to rebase - working directory '
948 'parent is also destination\n'))
952 'parent is also destination\n'))
949 elif not repo.revs('%ld - ::%d', base, dest.rev()):
953 elif not repo.revs('%ld - ::%d', base, dest.rev()):
950 if basef:
954 if basef:
951 ui.status(_('nothing to rebase - "base" %s is '
955 ui.status(_('nothing to rebase - "base" %s is '
952 'already an ancestor of destination '
956 'already an ancestor of destination '
953 '%s\n') %
957 '%s\n') %
954 ('+'.join(bytes(repo[r]) for r in base),
958 ('+'.join(bytes(repo[r]) for r in base),
955 dest))
959 dest))
956 else:
960 else:
957 ui.status(_('nothing to rebase - working '
961 ui.status(_('nothing to rebase - working '
958 'directory parent is already an '
962 'directory parent is already an '
959 'ancestor of destination %s\n') % dest)
963 'ancestor of destination %s\n') % dest)
960 else: # can it happen?
964 else: # can it happen?
961 ui.status(_('nothing to rebase from %s to %s\n') %
965 ui.status(_('nothing to rebase from %s to %s\n') %
962 ('+'.join(bytes(repo[r]) for r in base), dest))
966 ('+'.join(bytes(repo[r]) for r in base), dest))
963 return None
967 return None
964
968
965 rebasingwcp = repo['.'].rev() in rebaseset
969 rebasingwcp = repo['.'].rev() in rebaseset
966 ui.log("rebase", "", rebase_rebasing_wcp=rebasingwcp)
970 ui.log("rebase", "", rebase_rebasing_wcp=rebasingwcp)
967 if inmemory and rebasingwcp:
971 if inmemory and rebasingwcp:
968 # Check these since we did not before.
972 # Check these since we did not before.
969 cmdutil.checkunfinished(repo)
973 cmdutil.checkunfinished(repo)
970 cmdutil.bailifchanged(repo)
974 cmdutil.bailifchanged(repo)
971
975
972 if not destf:
976 if not destf:
973 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
977 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
974 destf = bytes(dest)
978 destf = bytes(dest)
975
979
976 allsrc = revsetlang.formatspec('%ld', rebaseset)
980 allsrc = revsetlang.formatspec('%ld', rebaseset)
977 alias = {'ALLSRC': allsrc}
981 alias = {'ALLSRC': allsrc}
978
982
979 if dest is None:
983 if dest is None:
980 try:
984 try:
981 # fast path: try to resolve dest without SRC alias
985 # fast path: try to resolve dest without SRC alias
982 dest = scmutil.revsingle(repo, destf, localalias=alias)
986 dest = scmutil.revsingle(repo, destf, localalias=alias)
983 except error.RepoLookupError:
987 except error.RepoLookupError:
984 # multi-dest path: resolve dest for each SRC separately
988 # multi-dest path: resolve dest for each SRC separately
985 destmap = {}
989 destmap = {}
986 for r in rebaseset:
990 for r in rebaseset:
987 alias['SRC'] = revsetlang.formatspec('%d', r)
991 alias['SRC'] = revsetlang.formatspec('%d', r)
988 # use repo.anyrevs instead of scmutil.revsingle because we
992 # use repo.anyrevs instead of scmutil.revsingle because we
989 # don't want to abort if destset is empty.
993 # don't want to abort if destset is empty.
990 destset = repo.anyrevs([destf], user=True, localalias=alias)
994 destset = repo.anyrevs([destf], user=True, localalias=alias)
991 size = len(destset)
995 size = len(destset)
992 if size == 1:
996 if size == 1:
993 destmap[r] = destset.first()
997 destmap[r] = destset.first()
994 elif size == 0:
998 elif size == 0:
995 ui.note(_('skipping %s - empty destination\n') % repo[r])
999 ui.note(_('skipping %s - empty destination\n') % repo[r])
996 else:
1000 else:
997 raise error.Abort(_('rebase destination for %s is not '
1001 raise error.Abort(_('rebase destination for %s is not '
998 'unique') % repo[r])
1002 'unique') % repo[r])
999
1003
1000 if dest is not None:
1004 if dest is not None:
1001 # single-dest case: assign dest to each rev in rebaseset
1005 # single-dest case: assign dest to each rev in rebaseset
1002 destrev = dest.rev()
1006 destrev = dest.rev()
1003 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1007 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1004
1008
1005 if not destmap:
1009 if not destmap:
1006 ui.status(_('nothing to rebase - empty destination\n'))
1010 ui.status(_('nothing to rebase - empty destination\n'))
1007 return None
1011 return None
1008
1012
1009 return destmap
1013 return destmap
1010
1014
1011 def externalparent(repo, state, destancestors):
1015 def externalparent(repo, state, destancestors):
1012 """Return the revision that should be used as the second parent
1016 """Return the revision that should be used as the second parent
1013 when the revisions in state is collapsed on top of destancestors.
1017 when the revisions in state is collapsed on top of destancestors.
1014 Abort if there is more than one parent.
1018 Abort if there is more than one parent.
1015 """
1019 """
1016 parents = set()
1020 parents = set()
1017 source = min(state)
1021 source = min(state)
1018 for rev in state:
1022 for rev in state:
1019 if rev == source:
1023 if rev == source:
1020 continue
1024 continue
1021 for p in repo[rev].parents():
1025 for p in repo[rev].parents():
1022 if (p.rev() not in state
1026 if (p.rev() not in state
1023 and p.rev() not in destancestors):
1027 and p.rev() not in destancestors):
1024 parents.add(p.rev())
1028 parents.add(p.rev())
1025 if not parents:
1029 if not parents:
1026 return nullrev
1030 return nullrev
1027 if len(parents) == 1:
1031 if len(parents) == 1:
1028 return parents.pop()
1032 return parents.pop()
1029 raise error.Abort(_('unable to collapse on top of %d, there is more '
1033 raise error.Abort(_('unable to collapse on top of %d, there is more '
1030 'than one external parent: %s') %
1034 'than one external parent: %s') %
1031 (max(destancestors),
1035 (max(destancestors),
1032 ', '.join("%d" % p for p in sorted(parents))))
1036 ', '.join("%d" % p for p in sorted(parents))))
1033
1037
1034 def concludememorynode(repo, rev, p1, p2, wctx, editor, extrafn, keepbranches,
1038 def concludememorynode(repo, rev, p1, p2, wctx, editor, extrafn, keepbranches,
1035 date, commitmsg=None):
1039 date, commitmsg=None):
1036 '''Commit the memory changes with parents p1 and p2. Reuse commit info from
1040 '''Commit the memory changes with parents p1 and p2. Reuse commit info from
1037 rev but also store useful information in extra.
1041 rev but also store useful information in extra.
1038 Return node of committed revision.'''
1042 Return node of committed revision.'''
1039 ctx = repo[rev]
1043 ctx = repo[rev]
1040 if commitmsg is None:
1044 if commitmsg is None:
1041 commitmsg = ctx.description()
1045 commitmsg = ctx.description()
1042 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1046 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1043 extra = {'rebase_source': ctx.hex()}
1047 extra = {'rebase_source': ctx.hex()}
1044 if extrafn:
1048 if extrafn:
1045 extrafn(ctx, extra)
1049 extrafn(ctx, extra)
1046
1050
1047 destphase = max(ctx.phase(), phases.draft)
1051 destphase = max(ctx.phase(), phases.draft)
1048 overrides = {('phases', 'new-commit'): destphase}
1052 overrides = {('phases', 'new-commit'): destphase}
1049 if keepbranch:
1053 if keepbranch:
1050 overrides[('ui', 'allowemptycommit')] = True
1054 overrides[('ui', 'allowemptycommit')] = True
1051 with repo.ui.configoverride(overrides, 'rebase'):
1055 with repo.ui.configoverride(overrides, 'rebase'):
1052 # Replicates the empty check in ``repo.commit``.
1056 # Replicates the empty check in ``repo.commit``.
1053 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
1057 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
1054 return None
1058 return None
1055
1059
1056 if date is None:
1060 if date is None:
1057 date = ctx.date()
1061 date = ctx.date()
1058
1062
1059 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1063 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1060 # ``branch`` (used when passing ``--keepbranches``).
1064 # ``branch`` (used when passing ``--keepbranches``).
1061 branch = repo[p1].branch()
1065 branch = repo[p1].branch()
1062 if 'branch' in extra:
1066 if 'branch' in extra:
1063 branch = extra['branch']
1067 branch = extra['branch']
1064
1068
1065 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
1069 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
1066 extra=extra, user=ctx.user(), branch=branch, editor=editor)
1070 extra=extra, user=ctx.user(), branch=branch, editor=editor)
1067 commitres = repo.commitctx(memctx)
1071 commitres = repo.commitctx(memctx)
1068 wctx.clean() # Might be reused
1072 wctx.clean() # Might be reused
1069 return commitres
1073 return commitres
1070
1074
1071 def concludenode(repo, rev, p1, p2, editor, extrafn, keepbranches, date,
1075 def concludenode(repo, rev, p1, p2, editor, extrafn, keepbranches, date,
1072 commitmsg=None):
1076 commitmsg=None):
1073 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
1077 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
1074 but also store useful information in extra.
1078 but also store useful information in extra.
1075 Return node of committed revision.'''
1079 Return node of committed revision.'''
1076 dsguard = util.nullcontextmanager()
1080 dsguard = util.nullcontextmanager()
1077 if not repo.ui.configbool('rebase', 'singletransaction'):
1081 if not repo.ui.configbool('rebase', 'singletransaction'):
1078 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1082 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1079 with dsguard:
1083 with dsguard:
1080 repo.setparents(repo[p1].node(), repo[p2].node())
1084 repo.setparents(repo[p1].node(), repo[p2].node())
1081 ctx = repo[rev]
1085 ctx = repo[rev]
1082 if commitmsg is None:
1086 if commitmsg is None:
1083 commitmsg = ctx.description()
1087 commitmsg = ctx.description()
1084 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1088 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1085 extra = {'rebase_source': ctx.hex()}
1089 extra = {'rebase_source': ctx.hex()}
1086 if extrafn:
1090 if extrafn:
1087 extrafn(ctx, extra)
1091 extrafn(ctx, extra)
1088
1092
1089 destphase = max(ctx.phase(), phases.draft)
1093 destphase = max(ctx.phase(), phases.draft)
1090 overrides = {('phases', 'new-commit'): destphase}
1094 overrides = {('phases', 'new-commit'): destphase}
1091 if keepbranch:
1095 if keepbranch:
1092 overrides[('ui', 'allowemptycommit')] = True
1096 overrides[('ui', 'allowemptycommit')] = True
1093 with repo.ui.configoverride(overrides, 'rebase'):
1097 with repo.ui.configoverride(overrides, 'rebase'):
1094 # Commit might fail if unresolved files exist
1098 # Commit might fail if unresolved files exist
1095 if date is None:
1099 if date is None:
1096 date = ctx.date()
1100 date = ctx.date()
1097 newnode = repo.commit(text=commitmsg, user=ctx.user(),
1101 newnode = repo.commit(text=commitmsg, user=ctx.user(),
1098 date=date, extra=extra, editor=editor)
1102 date=date, extra=extra, editor=editor)
1099
1103
1100 repo.dirstate.setbranch(repo[newnode].branch())
1104 repo.dirstate.setbranch(repo[newnode].branch())
1101 return newnode
1105 return newnode
1102
1106
1103 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1107 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1104 'Rebase a single revision rev on top of p1 using base as merge ancestor'
1108 'Rebase a single revision rev on top of p1 using base as merge ancestor'
1105 # Merge phase
1109 # Merge phase
1106 # Update to destination and merge it with local
1110 # Update to destination and merge it with local
1107 if wctx.isinmemory():
1111 if wctx.isinmemory():
1108 wctx.setbase(repo[p1])
1112 wctx.setbase(repo[p1])
1109 else:
1113 else:
1110 if repo['.'].rev() != p1:
1114 if repo['.'].rev() != p1:
1111 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
1115 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
1112 mergemod.update(repo, p1, False, True)
1116 mergemod.update(repo, p1, False, True)
1113 else:
1117 else:
1114 repo.ui.debug(" already in destination\n")
1118 repo.ui.debug(" already in destination\n")
1115 # This is, alas, necessary to invalidate workingctx's manifest cache,
1119 # This is, alas, necessary to invalidate workingctx's manifest cache,
1116 # as well as other data we litter on it in other places.
1120 # as well as other data we litter on it in other places.
1117 wctx = repo[None]
1121 wctx = repo[None]
1118 repo.dirstate.write(repo.currenttransaction())
1122 repo.dirstate.write(repo.currenttransaction())
1119 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
1123 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
1120 if base is not None:
1124 if base is not None:
1121 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
1125 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
1122 # When collapsing in-place, the parent is the common ancestor, we
1126 # When collapsing in-place, the parent is the common ancestor, we
1123 # have to allow merging with it.
1127 # have to allow merging with it.
1124 stats = mergemod.update(repo, rev, True, True, base, collapse,
1128 stats = mergemod.update(repo, rev, True, True, base, collapse,
1125 labels=['dest', 'source'], wc=wctx)
1129 labels=['dest', 'source'], wc=wctx)
1126 if collapse:
1130 if collapse:
1127 copies.duplicatecopies(repo, wctx, rev, dest)
1131 copies.duplicatecopies(repo, wctx, rev, dest)
1128 else:
1132 else:
1129 # If we're not using --collapse, we need to
1133 # If we're not using --collapse, we need to
1130 # duplicate copies between the revision we're
1134 # duplicate copies between the revision we're
1131 # rebasing and its first parent, but *not*
1135 # rebasing and its first parent, but *not*
1132 # duplicate any copies that have already been
1136 # duplicate any copies that have already been
1133 # performed in the destination.
1137 # performed in the destination.
1134 p1rev = repo[rev].p1().rev()
1138 p1rev = repo[rev].p1().rev()
1135 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1139 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1136 return stats
1140 return stats
1137
1141
1138 def adjustdest(repo, rev, destmap, state, skipped):
1142 def adjustdest(repo, rev, destmap, state, skipped):
1139 """adjust rebase destination given the current rebase state
1143 """adjust rebase destination given the current rebase state
1140
1144
1141 rev is what is being rebased. Return a list of two revs, which are the
1145 rev is what is being rebased. Return a list of two revs, which are the
1142 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1146 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1143 nullrev, return dest without adjustment for it.
1147 nullrev, return dest without adjustment for it.
1144
1148
1145 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1149 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1146 to B1, and E's destination will be adjusted from F to B1.
1150 to B1, and E's destination will be adjusted from F to B1.
1147
1151
1148 B1 <- written during rebasing B
1152 B1 <- written during rebasing B
1149 |
1153 |
1150 F <- original destination of B, E
1154 F <- original destination of B, E
1151 |
1155 |
1152 | E <- rev, which is being rebased
1156 | E <- rev, which is being rebased
1153 | |
1157 | |
1154 | D <- prev, one parent of rev being checked
1158 | D <- prev, one parent of rev being checked
1155 | |
1159 | |
1156 | x <- skipped, ex. no successor or successor in (::dest)
1160 | x <- skipped, ex. no successor or successor in (::dest)
1157 | |
1161 | |
1158 | C <- rebased as C', different destination
1162 | C <- rebased as C', different destination
1159 | |
1163 | |
1160 | B <- rebased as B1 C'
1164 | B <- rebased as B1 C'
1161 |/ |
1165 |/ |
1162 A G <- destination of C, different
1166 A G <- destination of C, different
1163
1167
1164 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1168 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1165 first move C to C1, G to G1, and when it's checking H, the adjusted
1169 first move C to C1, G to G1, and when it's checking H, the adjusted
1166 destinations will be [C1, G1].
1170 destinations will be [C1, G1].
1167
1171
1168 H C1 G1
1172 H C1 G1
1169 /| | /
1173 /| | /
1170 F G |/
1174 F G |/
1171 K | | -> K
1175 K | | -> K
1172 | C D |
1176 | C D |
1173 | |/ |
1177 | |/ |
1174 | B | ...
1178 | B | ...
1175 |/ |/
1179 |/ |/
1176 A A
1180 A A
1177
1181
1178 Besides, adjust dest according to existing rebase information. For example,
1182 Besides, adjust dest according to existing rebase information. For example,
1179
1183
1180 B C D B needs to be rebased on top of C, C needs to be rebased on top
1184 B C D B needs to be rebased on top of C, C needs to be rebased on top
1181 \|/ of D. We will rebase C first.
1185 \|/ of D. We will rebase C first.
1182 A
1186 A
1183
1187
1184 C' After rebasing C, when considering B's destination, use C'
1188 C' After rebasing C, when considering B's destination, use C'
1185 | instead of the original C.
1189 | instead of the original C.
1186 B D
1190 B D
1187 \ /
1191 \ /
1188 A
1192 A
1189 """
1193 """
1190 # pick already rebased revs with same dest from state as interesting source
1194 # pick already rebased revs with same dest from state as interesting source
1191 dest = destmap[rev]
1195 dest = destmap[rev]
1192 source = [s for s, d in state.items()
1196 source = [s for s, d in state.items()
1193 if d > 0 and destmap[s] == dest and s not in skipped]
1197 if d > 0 and destmap[s] == dest and s not in skipped]
1194
1198
1195 result = []
1199 result = []
1196 for prev in repo.changelog.parentrevs(rev):
1200 for prev in repo.changelog.parentrevs(rev):
1197 adjusted = dest
1201 adjusted = dest
1198 if prev != nullrev:
1202 if prev != nullrev:
1199 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1203 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1200 if candidate is not None:
1204 if candidate is not None:
1201 adjusted = state[candidate]
1205 adjusted = state[candidate]
1202 if adjusted == dest and dest in state:
1206 if adjusted == dest and dest in state:
1203 adjusted = state[dest]
1207 adjusted = state[dest]
1204 if adjusted == revtodo:
1208 if adjusted == revtodo:
1205 # sortsource should produce an order that makes this impossible
1209 # sortsource should produce an order that makes this impossible
1206 raise error.ProgrammingError(
1210 raise error.ProgrammingError(
1207 'rev %d should be rebased already at this time' % dest)
1211 'rev %d should be rebased already at this time' % dest)
1208 result.append(adjusted)
1212 result.append(adjusted)
1209 return result
1213 return result
1210
1214
1211 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1215 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1212 """
1216 """
1213 Abort if rebase will create divergence or rebase is noop because of markers
1217 Abort if rebase will create divergence or rebase is noop because of markers
1214
1218
1215 `rebaseobsrevs`: set of obsolete revision in source
1219 `rebaseobsrevs`: set of obsolete revision in source
1216 `rebaseobsskipped`: set of revisions from source skipped because they have
1220 `rebaseobsskipped`: set of revisions from source skipped because they have
1217 successors in destination or no non-obsolete successor.
1221 successors in destination or no non-obsolete successor.
1218 """
1222 """
1219 # Obsolete node with successors not in dest leads to divergence
1223 # Obsolete node with successors not in dest leads to divergence
1220 divergenceok = ui.configbool('experimental',
1224 divergenceok = ui.configbool('experimental',
1221 'evolution.allowdivergence')
1225 'evolution.allowdivergence')
1222 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1226 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1223
1227
1224 if divergencebasecandidates and not divergenceok:
1228 if divergencebasecandidates and not divergenceok:
1225 divhashes = (bytes(repo[r])
1229 divhashes = (bytes(repo[r])
1226 for r in divergencebasecandidates)
1230 for r in divergencebasecandidates)
1227 msg = _("this rebase will cause "
1231 msg = _("this rebase will cause "
1228 "divergences from: %s")
1232 "divergences from: %s")
1229 h = _("to force the rebase please set "
1233 h = _("to force the rebase please set "
1230 "experimental.evolution.allowdivergence=True")
1234 "experimental.evolution.allowdivergence=True")
1231 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1235 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1232
1236
1233 def successorrevs(unfi, rev):
1237 def successorrevs(unfi, rev):
1234 """yield revision numbers for successors of rev"""
1238 """yield revision numbers for successors of rev"""
1235 assert unfi.filtername is None
1239 assert unfi.filtername is None
1236 nodemap = unfi.changelog.nodemap
1240 nodemap = unfi.changelog.nodemap
1237 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1241 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1238 if s in nodemap:
1242 if s in nodemap:
1239 yield nodemap[s]
1243 yield nodemap[s]
1240
1244
1241 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1245 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1242 """Return new parents and optionally a merge base for rev being rebased
1246 """Return new parents and optionally a merge base for rev being rebased
1243
1247
1244 The destination specified by "dest" cannot always be used directly because
1248 The destination specified by "dest" cannot always be used directly because
1245 previously rebase result could affect destination. For example,
1249 previously rebase result could affect destination. For example,
1246
1250
1247 D E rebase -r C+D+E -d B
1251 D E rebase -r C+D+E -d B
1248 |/ C will be rebased to C'
1252 |/ C will be rebased to C'
1249 B C D's new destination will be C' instead of B
1253 B C D's new destination will be C' instead of B
1250 |/ E's new destination will be C' instead of B
1254 |/ E's new destination will be C' instead of B
1251 A
1255 A
1252
1256
1253 The new parents of a merge is slightly more complicated. See the comment
1257 The new parents of a merge is slightly more complicated. See the comment
1254 block below.
1258 block below.
1255 """
1259 """
1256 # use unfiltered changelog since successorrevs may return filtered nodes
1260 # use unfiltered changelog since successorrevs may return filtered nodes
1257 assert repo.filtername is None
1261 assert repo.filtername is None
1258 cl = repo.changelog
1262 cl = repo.changelog
1259 def isancestor(a, b):
1263 def isancestor(a, b):
1260 # take revision numbers instead of nodes
1264 # take revision numbers instead of nodes
1261 if a == b:
1265 if a == b:
1262 return True
1266 return True
1263 elif a > b:
1267 elif a > b:
1264 return False
1268 return False
1265 return cl.isancestor(cl.node(a), cl.node(b))
1269 return cl.isancestor(cl.node(a), cl.node(b))
1266
1270
1267 dest = destmap[rev]
1271 dest = destmap[rev]
1268 oldps = repo.changelog.parentrevs(rev) # old parents
1272 oldps = repo.changelog.parentrevs(rev) # old parents
1269 newps = [nullrev, nullrev] # new parents
1273 newps = [nullrev, nullrev] # new parents
1270 dests = adjustdest(repo, rev, destmap, state, skipped)
1274 dests = adjustdest(repo, rev, destmap, state, skipped)
1271 bases = list(oldps) # merge base candidates, initially just old parents
1275 bases = list(oldps) # merge base candidates, initially just old parents
1272
1276
1273 if all(r == nullrev for r in oldps[1:]):
1277 if all(r == nullrev for r in oldps[1:]):
1274 # For non-merge changeset, just move p to adjusted dest as requested.
1278 # For non-merge changeset, just move p to adjusted dest as requested.
1275 newps[0] = dests[0]
1279 newps[0] = dests[0]
1276 else:
1280 else:
1277 # For merge changeset, if we move p to dests[i] unconditionally, both
1281 # For merge changeset, if we move p to dests[i] unconditionally, both
1278 # parents may change and the end result looks like "the merge loses a
1282 # parents may change and the end result looks like "the merge loses a
1279 # parent", which is a surprise. This is a limit because "--dest" only
1283 # parent", which is a surprise. This is a limit because "--dest" only
1280 # accepts one dest per src.
1284 # accepts one dest per src.
1281 #
1285 #
1282 # Therefore, only move p with reasonable conditions (in this order):
1286 # Therefore, only move p with reasonable conditions (in this order):
1283 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1287 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1284 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1288 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1285 #
1289 #
1286 # Comparing with adjustdest, the logic here does some additional work:
1290 # Comparing with adjustdest, the logic here does some additional work:
1287 # 1. decide which parents will not be moved towards dest
1291 # 1. decide which parents will not be moved towards dest
1288 # 2. if the above decision is "no", should a parent still be moved
1292 # 2. if the above decision is "no", should a parent still be moved
1289 # because it was rebased?
1293 # because it was rebased?
1290 #
1294 #
1291 # For example:
1295 # For example:
1292 #
1296 #
1293 # C # "rebase -r C -d D" is an error since none of the parents
1297 # C # "rebase -r C -d D" is an error since none of the parents
1294 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1298 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1295 # A B D # B (using rule "2."), since B will be rebased.
1299 # A B D # B (using rule "2."), since B will be rebased.
1296 #
1300 #
1297 # The loop tries to be not rely on the fact that a Mercurial node has
1301 # The loop tries to be not rely on the fact that a Mercurial node has
1298 # at most 2 parents.
1302 # at most 2 parents.
1299 for i, p in enumerate(oldps):
1303 for i, p in enumerate(oldps):
1300 np = p # new parent
1304 np = p # new parent
1301 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1305 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1302 np = dests[i]
1306 np = dests[i]
1303 elif p in state and state[p] > 0:
1307 elif p in state and state[p] > 0:
1304 np = state[p]
1308 np = state[p]
1305
1309
1306 # "bases" only record "special" merge bases that cannot be
1310 # "bases" only record "special" merge bases that cannot be
1307 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1311 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1308 # For example:
1312 # For example:
1309 #
1313 #
1310 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1314 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1311 # | C # is B', but merge base for C is B, instead of
1315 # | C # is B', but merge base for C is B, instead of
1312 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1316 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1313 # | B # "state" edges are merged (so there will be an edge from
1317 # | B # "state" edges are merged (so there will be an edge from
1314 # |/ # B to B'), the merge base is still ancestor(C, B') in
1318 # |/ # B to B'), the merge base is still ancestor(C, B') in
1315 # A # the merged graph.
1319 # A # the merged graph.
1316 #
1320 #
1317 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1321 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1318 # which uses "virtual null merge" to explain this situation.
1322 # which uses "virtual null merge" to explain this situation.
1319 if isancestor(p, np):
1323 if isancestor(p, np):
1320 bases[i] = nullrev
1324 bases[i] = nullrev
1321
1325
1322 # If one parent becomes an ancestor of the other, drop the ancestor
1326 # If one parent becomes an ancestor of the other, drop the ancestor
1323 for j, x in enumerate(newps[:i]):
1327 for j, x in enumerate(newps[:i]):
1324 if x == nullrev:
1328 if x == nullrev:
1325 continue
1329 continue
1326 if isancestor(np, x): # CASE-1
1330 if isancestor(np, x): # CASE-1
1327 np = nullrev
1331 np = nullrev
1328 elif isancestor(x, np): # CASE-2
1332 elif isancestor(x, np): # CASE-2
1329 newps[j] = np
1333 newps[j] = np
1330 np = nullrev
1334 np = nullrev
1331 # New parents forming an ancestor relationship does not
1335 # New parents forming an ancestor relationship does not
1332 # mean the old parents have a similar relationship. Do not
1336 # mean the old parents have a similar relationship. Do not
1333 # set bases[x] to nullrev.
1337 # set bases[x] to nullrev.
1334 bases[j], bases[i] = bases[i], bases[j]
1338 bases[j], bases[i] = bases[i], bases[j]
1335
1339
1336 newps[i] = np
1340 newps[i] = np
1337
1341
1338 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1342 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1339 # base. If only p2 changes, merging using unchanged p1 as merge base is
1343 # base. If only p2 changes, merging using unchanged p1 as merge base is
1340 # suboptimal. Therefore swap parents to make the merge sane.
1344 # suboptimal. Therefore swap parents to make the merge sane.
1341 if newps[1] != nullrev and oldps[0] == newps[0]:
1345 if newps[1] != nullrev and oldps[0] == newps[0]:
1342 assert len(newps) == 2 and len(oldps) == 2
1346 assert len(newps) == 2 and len(oldps) == 2
1343 newps.reverse()
1347 newps.reverse()
1344 bases.reverse()
1348 bases.reverse()
1345
1349
1346 # No parent change might be an error because we fail to make rev a
1350 # No parent change might be an error because we fail to make rev a
1347 # descendent of requested dest. This can happen, for example:
1351 # descendent of requested dest. This can happen, for example:
1348 #
1352 #
1349 # C # rebase -r C -d D
1353 # C # rebase -r C -d D
1350 # /| # None of A and B will be changed to D and rebase fails.
1354 # /| # None of A and B will be changed to D and rebase fails.
1351 # A B D
1355 # A B D
1352 if set(newps) == set(oldps) and dest not in newps:
1356 if set(newps) == set(oldps) and dest not in newps:
1353 raise error.Abort(_('cannot rebase %d:%s without '
1357 raise error.Abort(_('cannot rebase %d:%s without '
1354 'moving at least one of its parents')
1358 'moving at least one of its parents')
1355 % (rev, repo[rev]))
1359 % (rev, repo[rev]))
1356
1360
1357 # Source should not be ancestor of dest. The check here guarantees it's
1361 # Source should not be ancestor of dest. The check here guarantees it's
1358 # impossible. With multi-dest, the initial check does not cover complex
1362 # impossible. With multi-dest, the initial check does not cover complex
1359 # cases since we don't have abstractions to dry-run rebase cheaply.
1363 # cases since we don't have abstractions to dry-run rebase cheaply.
1360 if any(p != nullrev and isancestor(rev, p) for p in newps):
1364 if any(p != nullrev and isancestor(rev, p) for p in newps):
1361 raise error.Abort(_('source is ancestor of destination'))
1365 raise error.Abort(_('source is ancestor of destination'))
1362
1366
1363 # "rebasenode" updates to new p1, use the corresponding merge base.
1367 # "rebasenode" updates to new p1, use the corresponding merge base.
1364 if bases[0] != nullrev:
1368 if bases[0] != nullrev:
1365 base = bases[0]
1369 base = bases[0]
1366 else:
1370 else:
1367 base = None
1371 base = None
1368
1372
1369 # Check if the merge will contain unwanted changes. That may happen if
1373 # Check if the merge will contain unwanted changes. That may happen if
1370 # there are multiple special (non-changelog ancestor) merge bases, which
1374 # there are multiple special (non-changelog ancestor) merge bases, which
1371 # cannot be handled well by the 3-way merge algorithm. For example:
1375 # cannot be handled well by the 3-way merge algorithm. For example:
1372 #
1376 #
1373 # F
1377 # F
1374 # /|
1378 # /|
1375 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1379 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1376 # | | # as merge base, the difference between D and F will include
1380 # | | # as merge base, the difference between D and F will include
1377 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1381 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1378 # |/ # chosen, the rebased F will contain B.
1382 # |/ # chosen, the rebased F will contain B.
1379 # A Z
1383 # A Z
1380 #
1384 #
1381 # But our merge base candidates (D and E in above case) could still be
1385 # But our merge base candidates (D and E in above case) could still be
1382 # better than the default (ancestor(F, Z) == null). Therefore still
1386 # better than the default (ancestor(F, Z) == null). Therefore still
1383 # pick one (so choose p1 above).
1387 # pick one (so choose p1 above).
1384 if sum(1 for b in bases if b != nullrev) > 1:
1388 if sum(1 for b in bases if b != nullrev) > 1:
1385 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1389 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1386 for i, base in enumerate(bases):
1390 for i, base in enumerate(bases):
1387 if base == nullrev:
1391 if base == nullrev:
1388 continue
1392 continue
1389 # Revisions in the side (not chosen as merge base) branch that
1393 # Revisions in the side (not chosen as merge base) branch that
1390 # might contain "surprising" contents
1394 # might contain "surprising" contents
1391 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1395 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1392 bases, base, base, dest))
1396 bases, base, base, dest))
1393
1397
1394 # If those revisions are covered by rebaseset, the result is good.
1398 # If those revisions are covered by rebaseset, the result is good.
1395 # A merge in rebaseset would be considered to cover its ancestors.
1399 # A merge in rebaseset would be considered to cover its ancestors.
1396 if siderevs:
1400 if siderevs:
1397 rebaseset = [r for r, d in state.items()
1401 rebaseset = [r for r, d in state.items()
1398 if d > 0 and r not in obsskipped]
1402 if d > 0 and r not in obsskipped]
1399 merges = [r for r in rebaseset
1403 merges = [r for r in rebaseset
1400 if cl.parentrevs(r)[1] != nullrev]
1404 if cl.parentrevs(r)[1] != nullrev]
1401 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1405 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1402 siderevs, merges, rebaseset))
1406 siderevs, merges, rebaseset))
1403
1407
1404 # Choose a merge base that has a minimal number of unwanted revs.
1408 # Choose a merge base that has a minimal number of unwanted revs.
1405 l, i = min((len(revs), i)
1409 l, i = min((len(revs), i)
1406 for i, revs in enumerate(unwanted) if revs is not None)
1410 for i, revs in enumerate(unwanted) if revs is not None)
1407 base = bases[i]
1411 base = bases[i]
1408
1412
1409 # newps[0] should match merge base if possible. Currently, if newps[i]
1413 # newps[0] should match merge base if possible. Currently, if newps[i]
1410 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1414 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1411 # the other's ancestor. In that case, it's fine to not swap newps here.
1415 # the other's ancestor. In that case, it's fine to not swap newps here.
1412 # (see CASE-1 and CASE-2 above)
1416 # (see CASE-1 and CASE-2 above)
1413 if i != 0 and newps[i] != nullrev:
1417 if i != 0 and newps[i] != nullrev:
1414 newps[0], newps[i] = newps[i], newps[0]
1418 newps[0], newps[i] = newps[i], newps[0]
1415
1419
1416 # The merge will include unwanted revisions. Abort now. Revisit this if
1420 # The merge will include unwanted revisions. Abort now. Revisit this if
1417 # we have a more advanced merge algorithm that handles multiple bases.
1421 # we have a more advanced merge algorithm that handles multiple bases.
1418 if l > 0:
1422 if l > 0:
1419 unwanteddesc = _(' or ').join(
1423 unwanteddesc = _(' or ').join(
1420 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1424 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1421 for revs in unwanted if revs is not None))
1425 for revs in unwanted if revs is not None))
1422 raise error.Abort(
1426 raise error.Abort(
1423 _('rebasing %d:%s will include unwanted changes from %s')
1427 _('rebasing %d:%s will include unwanted changes from %s')
1424 % (rev, repo[rev], unwanteddesc))
1428 % (rev, repo[rev], unwanteddesc))
1425
1429
1426 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1430 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1427
1431
1428 return newps[0], newps[1], base
1432 return newps[0], newps[1], base
1429
1433
1430 def isagitpatch(repo, patchname):
1434 def isagitpatch(repo, patchname):
1431 'Return true if the given patch is in git format'
1435 'Return true if the given patch is in git format'
1432 mqpatch = os.path.join(repo.mq.path, patchname)
1436 mqpatch = os.path.join(repo.mq.path, patchname)
1433 for line in patch.linereader(open(mqpatch, 'rb')):
1437 for line in patch.linereader(open(mqpatch, 'rb')):
1434 if line.startswith('diff --git'):
1438 if line.startswith('diff --git'):
1435 return True
1439 return True
1436 return False
1440 return False
1437
1441
1438 def updatemq(repo, state, skipped, **opts):
1442 def updatemq(repo, state, skipped, **opts):
1439 'Update rebased mq patches - finalize and then import them'
1443 'Update rebased mq patches - finalize and then import them'
1440 mqrebase = {}
1444 mqrebase = {}
1441 mq = repo.mq
1445 mq = repo.mq
1442 original_series = mq.fullseries[:]
1446 original_series = mq.fullseries[:]
1443 skippedpatches = set()
1447 skippedpatches = set()
1444
1448
1445 for p in mq.applied:
1449 for p in mq.applied:
1446 rev = repo[p.node].rev()
1450 rev = repo[p.node].rev()
1447 if rev in state:
1451 if rev in state:
1448 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1452 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1449 (rev, p.name))
1453 (rev, p.name))
1450 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1454 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1451 else:
1455 else:
1452 # Applied but not rebased, not sure this should happen
1456 # Applied but not rebased, not sure this should happen
1453 skippedpatches.add(p.name)
1457 skippedpatches.add(p.name)
1454
1458
1455 if mqrebase:
1459 if mqrebase:
1456 mq.finish(repo, mqrebase.keys())
1460 mq.finish(repo, mqrebase.keys())
1457
1461
1458 # We must start import from the newest revision
1462 # We must start import from the newest revision
1459 for rev in sorted(mqrebase, reverse=True):
1463 for rev in sorted(mqrebase, reverse=True):
1460 if rev not in skipped:
1464 if rev not in skipped:
1461 name, isgit = mqrebase[rev]
1465 name, isgit = mqrebase[rev]
1462 repo.ui.note(_('updating mq patch %s to %d:%s\n') %
1466 repo.ui.note(_('updating mq patch %s to %d:%s\n') %
1463 (name, state[rev], repo[state[rev]]))
1467 (name, state[rev], repo[state[rev]]))
1464 mq.qimport(repo, (), patchname=name, git=isgit,
1468 mq.qimport(repo, (), patchname=name, git=isgit,
1465 rev=["%d" % state[rev]])
1469 rev=["%d" % state[rev]])
1466 else:
1470 else:
1467 # Rebased and skipped
1471 # Rebased and skipped
1468 skippedpatches.add(mqrebase[rev][0])
1472 skippedpatches.add(mqrebase[rev][0])
1469
1473
1470 # Patches were either applied and rebased and imported in
1474 # Patches were either applied and rebased and imported in
1471 # order, applied and removed or unapplied. Discard the removed
1475 # order, applied and removed or unapplied. Discard the removed
1472 # ones while preserving the original series order and guards.
1476 # ones while preserving the original series order and guards.
1473 newseries = [s for s in original_series
1477 newseries = [s for s in original_series
1474 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1478 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1475 mq.fullseries[:] = newseries
1479 mq.fullseries[:] = newseries
1476 mq.seriesdirty = True
1480 mq.seriesdirty = True
1477 mq.savedirty()
1481 mq.savedirty()
1478
1482
1479 def storecollapsemsg(repo, collapsemsg):
1483 def storecollapsemsg(repo, collapsemsg):
1480 'Store the collapse message to allow recovery'
1484 'Store the collapse message to allow recovery'
1481 collapsemsg = collapsemsg or ''
1485 collapsemsg = collapsemsg or ''
1482 f = repo.vfs("last-message.txt", "w")
1486 f = repo.vfs("last-message.txt", "w")
1483 f.write("%s\n" % collapsemsg)
1487 f.write("%s\n" % collapsemsg)
1484 f.close()
1488 f.close()
1485
1489
1486 def clearcollapsemsg(repo):
1490 def clearcollapsemsg(repo):
1487 'Remove collapse message file'
1491 'Remove collapse message file'
1488 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1492 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1489
1493
1490 def restorecollapsemsg(repo, isabort):
1494 def restorecollapsemsg(repo, isabort):
1491 'Restore previously stored collapse message'
1495 'Restore previously stored collapse message'
1492 try:
1496 try:
1493 f = repo.vfs("last-message.txt")
1497 f = repo.vfs("last-message.txt")
1494 collapsemsg = f.readline().strip()
1498 collapsemsg = f.readline().strip()
1495 f.close()
1499 f.close()
1496 except IOError as err:
1500 except IOError as err:
1497 if err.errno != errno.ENOENT:
1501 if err.errno != errno.ENOENT:
1498 raise
1502 raise
1499 if isabort:
1503 if isabort:
1500 # Oh well, just abort like normal
1504 # Oh well, just abort like normal
1501 collapsemsg = ''
1505 collapsemsg = ''
1502 else:
1506 else:
1503 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1507 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1504 return collapsemsg
1508 return collapsemsg
1505
1509
1506 def clearstatus(repo):
1510 def clearstatus(repo):
1507 'Remove the status files'
1511 'Remove the status files'
1508 # Make sure the active transaction won't write the state file
1512 # Make sure the active transaction won't write the state file
1509 tr = repo.currenttransaction()
1513 tr = repo.currenttransaction()
1510 if tr:
1514 if tr:
1511 tr.removefilegenerator('rebasestate')
1515 tr.removefilegenerator('rebasestate')
1512 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1516 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1513
1517
1514 def needupdate(repo, state):
1518 def needupdate(repo, state):
1515 '''check whether we should `update --clean` away from a merge, or if
1519 '''check whether we should `update --clean` away from a merge, or if
1516 somehow the working dir got forcibly updated, e.g. by older hg'''
1520 somehow the working dir got forcibly updated, e.g. by older hg'''
1517 parents = [p.rev() for p in repo[None].parents()]
1521 parents = [p.rev() for p in repo[None].parents()]
1518
1522
1519 # Are we in a merge state at all?
1523 # Are we in a merge state at all?
1520 if len(parents) < 2:
1524 if len(parents) < 2:
1521 return False
1525 return False
1522
1526
1523 # We should be standing on the first as-of-yet unrebased commit.
1527 # We should be standing on the first as-of-yet unrebased commit.
1524 firstunrebased = min([old for old, new in state.iteritems()
1528 firstunrebased = min([old for old, new in state.iteritems()
1525 if new == nullrev])
1529 if new == nullrev])
1526 if firstunrebased in parents:
1530 if firstunrebased in parents:
1527 return True
1531 return True
1528
1532
1529 return False
1533 return False
1530
1534
1531 def abort(repo, originalwd, destmap, state, activebookmark=None):
1535 def abort(repo, originalwd, destmap, state, activebookmark=None):
1532 '''Restore the repository to its original state. Additional args:
1536 '''Restore the repository to its original state. Additional args:
1533
1537
1534 activebookmark: the name of the bookmark that should be active after the
1538 activebookmark: the name of the bookmark that should be active after the
1535 restore'''
1539 restore'''
1536
1540
1537 try:
1541 try:
1538 # If the first commits in the rebased set get skipped during the rebase,
1542 # If the first commits in the rebased set get skipped during the rebase,
1539 # their values within the state mapping will be the dest rev id. The
1543 # their values within the state mapping will be the dest rev id. The
1540 # rebased list must must not contain the dest rev (issue4896)
1544 # rebased list must must not contain the dest rev (issue4896)
1541 rebased = [s for r, s in state.items()
1545 rebased = [s for r, s in state.items()
1542 if s >= 0 and s != r and s != destmap[r]]
1546 if s >= 0 and s != r and s != destmap[r]]
1543 immutable = [d for d in rebased if not repo[d].mutable()]
1547 immutable = [d for d in rebased if not repo[d].mutable()]
1544 cleanup = True
1548 cleanup = True
1545 if immutable:
1549 if immutable:
1546 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1550 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1547 % ', '.join(bytes(repo[r]) for r in immutable),
1551 % ', '.join(bytes(repo[r]) for r in immutable),
1548 hint=_("see 'hg help phases' for details"))
1552 hint=_("see 'hg help phases' for details"))
1549 cleanup = False
1553 cleanup = False
1550
1554
1551 descendants = set()
1555 descendants = set()
1552 if rebased:
1556 if rebased:
1553 descendants = set(repo.changelog.descendants(rebased))
1557 descendants = set(repo.changelog.descendants(rebased))
1554 if descendants - set(rebased):
1558 if descendants - set(rebased):
1555 repo.ui.warn(_("warning: new changesets detected on destination "
1559 repo.ui.warn(_("warning: new changesets detected on destination "
1556 "branch, can't strip\n"))
1560 "branch, can't strip\n"))
1557 cleanup = False
1561 cleanup = False
1558
1562
1559 if cleanup:
1563 if cleanup:
1560 shouldupdate = False
1564 shouldupdate = False
1561 if rebased:
1565 if rebased:
1562 strippoints = [
1566 strippoints = [
1563 c.node() for c in repo.set('roots(%ld)', rebased)]
1567 c.node() for c in repo.set('roots(%ld)', rebased)]
1564
1568
1565 updateifonnodes = set(rebased)
1569 updateifonnodes = set(rebased)
1566 updateifonnodes.update(destmap.values())
1570 updateifonnodes.update(destmap.values())
1567 updateifonnodes.add(originalwd)
1571 updateifonnodes.add(originalwd)
1568 shouldupdate = repo['.'].rev() in updateifonnodes
1572 shouldupdate = repo['.'].rev() in updateifonnodes
1569
1573
1570 # Update away from the rebase if necessary
1574 # Update away from the rebase if necessary
1571 if shouldupdate or needupdate(repo, state):
1575 if shouldupdate or needupdate(repo, state):
1572 mergemod.update(repo, originalwd, False, True)
1576 mergemod.update(repo, originalwd, False, True)
1573
1577
1574 # Strip from the first rebased revision
1578 # Strip from the first rebased revision
1575 if rebased:
1579 if rebased:
1576 # no backup of rebased cset versions needed
1580 # no backup of rebased cset versions needed
1577 repair.strip(repo.ui, repo, strippoints)
1581 repair.strip(repo.ui, repo, strippoints)
1578
1582
1579 if activebookmark and activebookmark in repo._bookmarks:
1583 if activebookmark and activebookmark in repo._bookmarks:
1580 bookmarks.activate(repo, activebookmark)
1584 bookmarks.activate(repo, activebookmark)
1581
1585
1582 finally:
1586 finally:
1583 clearstatus(repo)
1587 clearstatus(repo)
1584 clearcollapsemsg(repo)
1588 clearcollapsemsg(repo)
1585 repo.ui.warn(_('rebase aborted\n'))
1589 repo.ui.warn(_('rebase aborted\n'))
1586 return 0
1590 return 0
1587
1591
1588 def sortsource(destmap):
1592 def sortsource(destmap):
1589 """yield source revisions in an order that we only rebase things once
1593 """yield source revisions in an order that we only rebase things once
1590
1594
1591 If source and destination overlaps, we should filter out revisions
1595 If source and destination overlaps, we should filter out revisions
1592 depending on other revisions which hasn't been rebased yet.
1596 depending on other revisions which hasn't been rebased yet.
1593
1597
1594 Yield a sorted list of revisions each time.
1598 Yield a sorted list of revisions each time.
1595
1599
1596 For example, when rebasing A to B, B to C. This function yields [B], then
1600 For example, when rebasing A to B, B to C. This function yields [B], then
1597 [A], indicating B needs to be rebased first.
1601 [A], indicating B needs to be rebased first.
1598
1602
1599 Raise if there is a cycle so the rebase is impossible.
1603 Raise if there is a cycle so the rebase is impossible.
1600 """
1604 """
1601 srcset = set(destmap)
1605 srcset = set(destmap)
1602 while srcset:
1606 while srcset:
1603 srclist = sorted(srcset)
1607 srclist = sorted(srcset)
1604 result = []
1608 result = []
1605 for r in srclist:
1609 for r in srclist:
1606 if destmap[r] not in srcset:
1610 if destmap[r] not in srcset:
1607 result.append(r)
1611 result.append(r)
1608 if not result:
1612 if not result:
1609 raise error.Abort(_('source and destination form a cycle'))
1613 raise error.Abort(_('source and destination form a cycle'))
1610 srcset -= set(result)
1614 srcset -= set(result)
1611 yield result
1615 yield result
1612
1616
1613 def buildstate(repo, destmap, collapse):
1617 def buildstate(repo, destmap, collapse):
1614 '''Define which revisions are going to be rebased and where
1618 '''Define which revisions are going to be rebased and where
1615
1619
1616 repo: repo
1620 repo: repo
1617 destmap: {srcrev: destrev}
1621 destmap: {srcrev: destrev}
1618 '''
1622 '''
1619 rebaseset = destmap.keys()
1623 rebaseset = destmap.keys()
1620 originalwd = repo['.'].rev()
1624 originalwd = repo['.'].rev()
1621
1625
1622 # This check isn't strictly necessary, since mq detects commits over an
1626 # This check isn't strictly necessary, since mq detects commits over an
1623 # applied patch. But it prevents messing up the working directory when
1627 # applied patch. But it prevents messing up the working directory when
1624 # a partially completed rebase is blocked by mq.
1628 # a partially completed rebase is blocked by mq.
1625 if 'qtip' in repo.tags():
1629 if 'qtip' in repo.tags():
1626 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1630 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1627 if set(destmap.values()) & mqapplied:
1631 if set(destmap.values()) & mqapplied:
1628 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1632 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1629
1633
1630 # Get "cycle" error early by exhausting the generator.
1634 # Get "cycle" error early by exhausting the generator.
1631 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1635 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1632 if not sortedsrc:
1636 if not sortedsrc:
1633 raise error.Abort(_('no matching revisions'))
1637 raise error.Abort(_('no matching revisions'))
1634
1638
1635 # Only check the first batch of revisions to rebase not depending on other
1639 # Only check the first batch of revisions to rebase not depending on other
1636 # rebaseset. This means "source is ancestor of destination" for the second
1640 # rebaseset. This means "source is ancestor of destination" for the second
1637 # (and following) batches of revisions are not checked here. We rely on
1641 # (and following) batches of revisions are not checked here. We rely on
1638 # "defineparents" to do that check.
1642 # "defineparents" to do that check.
1639 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1643 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1640 if not roots:
1644 if not roots:
1641 raise error.Abort(_('no matching revisions'))
1645 raise error.Abort(_('no matching revisions'))
1642 def revof(r):
1646 def revof(r):
1643 return r.rev()
1647 return r.rev()
1644 roots = sorted(roots, key=revof)
1648 roots = sorted(roots, key=revof)
1645 state = dict.fromkeys(rebaseset, revtodo)
1649 state = dict.fromkeys(rebaseset, revtodo)
1646 emptyrebase = (len(sortedsrc) == 1)
1650 emptyrebase = (len(sortedsrc) == 1)
1647 for root in roots:
1651 for root in roots:
1648 dest = repo[destmap[root.rev()]]
1652 dest = repo[destmap[root.rev()]]
1649 commonbase = root.ancestor(dest)
1653 commonbase = root.ancestor(dest)
1650 if commonbase == root:
1654 if commonbase == root:
1651 raise error.Abort(_('source is ancestor of destination'))
1655 raise error.Abort(_('source is ancestor of destination'))
1652 if commonbase == dest:
1656 if commonbase == dest:
1653 wctx = repo[None]
1657 wctx = repo[None]
1654 if dest == wctx.p1():
1658 if dest == wctx.p1():
1655 # when rebasing to '.', it will use the current wd branch name
1659 # when rebasing to '.', it will use the current wd branch name
1656 samebranch = root.branch() == wctx.branch()
1660 samebranch = root.branch() == wctx.branch()
1657 else:
1661 else:
1658 samebranch = root.branch() == dest.branch()
1662 samebranch = root.branch() == dest.branch()
1659 if not collapse and samebranch and dest in root.parents():
1663 if not collapse and samebranch and dest in root.parents():
1660 # mark the revision as done by setting its new revision
1664 # mark the revision as done by setting its new revision
1661 # equal to its old (current) revisions
1665 # equal to its old (current) revisions
1662 state[root.rev()] = root.rev()
1666 state[root.rev()] = root.rev()
1663 repo.ui.debug('source is a child of destination\n')
1667 repo.ui.debug('source is a child of destination\n')
1664 continue
1668 continue
1665
1669
1666 emptyrebase = False
1670 emptyrebase = False
1667 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1671 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1668 if emptyrebase:
1672 if emptyrebase:
1669 return None
1673 return None
1670 for rev in sorted(state):
1674 for rev in sorted(state):
1671 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1675 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1672 # if all parents of this revision are done, then so is this revision
1676 # if all parents of this revision are done, then so is this revision
1673 if parents and all((state.get(p) == p for p in parents)):
1677 if parents and all((state.get(p) == p for p in parents)):
1674 state[rev] = rev
1678 state[rev] = rev
1675 return originalwd, destmap, state
1679 return originalwd, destmap, state
1676
1680
1677 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1681 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1678 keepf=False, fm=None):
1682 keepf=False, fm=None):
1679 """dispose of rebased revision at the end of the rebase
1683 """dispose of rebased revision at the end of the rebase
1680
1684
1681 If `collapsedas` is not None, the rebase was a collapse whose result if the
1685 If `collapsedas` is not None, the rebase was a collapse whose result if the
1682 `collapsedas` node.
1686 `collapsedas` node.
1683
1687
1684 If `keepf` is not True, the rebase has --keep set and no nodes should be
1688 If `keepf` is not True, the rebase has --keep set and no nodes should be
1685 removed (but bookmarks still need to be moved).
1689 removed (but bookmarks still need to be moved).
1686 """
1690 """
1687 tonode = repo.changelog.node
1691 tonode = repo.changelog.node
1688 replacements = {}
1692 replacements = {}
1689 moves = {}
1693 moves = {}
1690 for rev, newrev in sorted(state.items()):
1694 for rev, newrev in sorted(state.items()):
1691 if newrev >= 0 and newrev != rev:
1695 if newrev >= 0 and newrev != rev:
1692 oldnode = tonode(rev)
1696 oldnode = tonode(rev)
1693 newnode = collapsedas or tonode(newrev)
1697 newnode = collapsedas or tonode(newrev)
1694 moves[oldnode] = newnode
1698 moves[oldnode] = newnode
1695 if not keepf:
1699 if not keepf:
1696 if rev in skipped:
1700 if rev in skipped:
1697 succs = ()
1701 succs = ()
1698 else:
1702 else:
1699 succs = (newnode,)
1703 succs = (newnode,)
1700 replacements[oldnode] = succs
1704 replacements[oldnode] = succs
1701 scmutil.cleanupnodes(repo, replacements, 'rebase', moves)
1705 scmutil.cleanupnodes(repo, replacements, 'rebase', moves)
1702 if fm:
1706 if fm:
1703 hf = fm.hexfunc
1707 hf = fm.hexfunc
1704 fl = fm.formatlist
1708 fl = fm.formatlist
1705 fd = fm.formatdict
1709 fd = fm.formatdict
1706 nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
1710 nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
1707 for oldn, newn in replacements.iteritems()},
1711 for oldn, newn in replacements.iteritems()},
1708 key="oldnode", value="newnodes")
1712 key="oldnode", value="newnodes")
1709 fm.data(nodechanges=nodechanges)
1713 fm.data(nodechanges=nodechanges)
1710
1714
1711 def pullrebase(orig, ui, repo, *args, **opts):
1715 def pullrebase(orig, ui, repo, *args, **opts):
1712 'Call rebase after pull if the latter has been invoked with --rebase'
1716 'Call rebase after pull if the latter has been invoked with --rebase'
1713 ret = None
1717 ret = None
1714 if opts.get(r'rebase'):
1718 if opts.get(r'rebase'):
1715 if ui.configbool('commands', 'rebase.requiredest'):
1719 if ui.configbool('commands', 'rebase.requiredest'):
1716 msg = _('rebase destination required by configuration')
1720 msg = _('rebase destination required by configuration')
1717 hint = _('use hg pull followed by hg rebase -d DEST')
1721 hint = _('use hg pull followed by hg rebase -d DEST')
1718 raise error.Abort(msg, hint=hint)
1722 raise error.Abort(msg, hint=hint)
1719
1723
1720 with repo.wlock(), repo.lock():
1724 with repo.wlock(), repo.lock():
1721 if opts.get(r'update'):
1725 if opts.get(r'update'):
1722 del opts[r'update']
1726 del opts[r'update']
1723 ui.debug('--update and --rebase are not compatible, ignoring '
1727 ui.debug('--update and --rebase are not compatible, ignoring '
1724 'the update flag\n')
1728 'the update flag\n')
1725
1729
1726 cmdutil.checkunfinished(repo)
1730 cmdutil.checkunfinished(repo)
1727 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1731 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1728 'please commit or shelve your changes first'))
1732 'please commit or shelve your changes first'))
1729
1733
1730 revsprepull = len(repo)
1734 revsprepull = len(repo)
1731 origpostincoming = commands.postincoming
1735 origpostincoming = commands.postincoming
1732 def _dummy(*args, **kwargs):
1736 def _dummy(*args, **kwargs):
1733 pass
1737 pass
1734 commands.postincoming = _dummy
1738 commands.postincoming = _dummy
1735 try:
1739 try:
1736 ret = orig(ui, repo, *args, **opts)
1740 ret = orig(ui, repo, *args, **opts)
1737 finally:
1741 finally:
1738 commands.postincoming = origpostincoming
1742 commands.postincoming = origpostincoming
1739 revspostpull = len(repo)
1743 revspostpull = len(repo)
1740 if revspostpull > revsprepull:
1744 if revspostpull > revsprepull:
1741 # --rev option from pull conflict with rebase own --rev
1745 # --rev option from pull conflict with rebase own --rev
1742 # dropping it
1746 # dropping it
1743 if r'rev' in opts:
1747 if r'rev' in opts:
1744 del opts[r'rev']
1748 del opts[r'rev']
1745 # positional argument from pull conflicts with rebase's own
1749 # positional argument from pull conflicts with rebase's own
1746 # --source.
1750 # --source.
1747 if r'source' in opts:
1751 if r'source' in opts:
1748 del opts[r'source']
1752 del opts[r'source']
1749 # revsprepull is the len of the repo, not revnum of tip.
1753 # revsprepull is the len of the repo, not revnum of tip.
1750 destspace = list(repo.changelog.revs(start=revsprepull))
1754 destspace = list(repo.changelog.revs(start=revsprepull))
1751 opts[r'_destspace'] = destspace
1755 opts[r'_destspace'] = destspace
1752 try:
1756 try:
1753 rebase(ui, repo, **opts)
1757 rebase(ui, repo, **opts)
1754 except error.NoMergeDestAbort:
1758 except error.NoMergeDestAbort:
1755 # we can maybe update instead
1759 # we can maybe update instead
1756 rev, _a, _b = destutil.destupdate(repo)
1760 rev, _a, _b = destutil.destupdate(repo)
1757 if rev == repo['.'].rev():
1761 if rev == repo['.'].rev():
1758 ui.status(_('nothing to rebase\n'))
1762 ui.status(_('nothing to rebase\n'))
1759 else:
1763 else:
1760 ui.status(_('nothing to rebase - updating instead\n'))
1764 ui.status(_('nothing to rebase - updating instead\n'))
1761 # not passing argument to get the bare update behavior
1765 # not passing argument to get the bare update behavior
1762 # with warning and trumpets
1766 # with warning and trumpets
1763 commands.update(ui, repo)
1767 commands.update(ui, repo)
1764 else:
1768 else:
1765 if opts.get(r'tool'):
1769 if opts.get(r'tool'):
1766 raise error.Abort(_('--tool can only be used with --rebase'))
1770 raise error.Abort(_('--tool can only be used with --rebase'))
1767 ret = orig(ui, repo, *args, **opts)
1771 ret = orig(ui, repo, *args, **opts)
1768
1772
1769 return ret
1773 return ret
1770
1774
1771 def _filterobsoleterevs(repo, revs):
1775 def _filterobsoleterevs(repo, revs):
1772 """returns a set of the obsolete revisions in revs"""
1776 """returns a set of the obsolete revisions in revs"""
1773 return set(r for r in revs if repo[r].obsolete())
1777 return set(r for r in revs if repo[r].obsolete())
1774
1778
1775 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1779 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1776 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
1780 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
1777
1781
1778 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
1782 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
1779 obsolete nodes to be rebased given in `rebaseobsrevs`.
1783 obsolete nodes to be rebased given in `rebaseobsrevs`.
1780
1784
1781 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
1785 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
1782 without a successor in destination.
1786 without a successor in destination.
1783
1787
1784 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
1788 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
1785 obsolete successors.
1789 obsolete successors.
1786 """
1790 """
1787 obsoletenotrebased = {}
1791 obsoletenotrebased = {}
1788 obsoletewithoutsuccessorindestination = set([])
1792 obsoletewithoutsuccessorindestination = set([])
1789 obsoleteextinctsuccessors = set([])
1793 obsoleteextinctsuccessors = set([])
1790
1794
1791 assert repo.filtername is None
1795 assert repo.filtername is None
1792 cl = repo.changelog
1796 cl = repo.changelog
1793 nodemap = cl.nodemap
1797 nodemap = cl.nodemap
1794 extinctnodes = set(cl.node(r) for r in repo.revs('extinct()'))
1798 extinctnodes = set(cl.node(r) for r in repo.revs('extinct()'))
1795 for srcrev in rebaseobsrevs:
1799 for srcrev in rebaseobsrevs:
1796 srcnode = cl.node(srcrev)
1800 srcnode = cl.node(srcrev)
1797 destnode = cl.node(destmap[srcrev])
1801 destnode = cl.node(destmap[srcrev])
1798 # XXX: more advanced APIs are required to handle split correctly
1802 # XXX: more advanced APIs are required to handle split correctly
1799 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1803 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1800 # obsutil.allsuccessors includes node itself
1804 # obsutil.allsuccessors includes node itself
1801 successors.remove(srcnode)
1805 successors.remove(srcnode)
1802 if successors.issubset(extinctnodes):
1806 if successors.issubset(extinctnodes):
1803 # all successors are extinct
1807 # all successors are extinct
1804 obsoleteextinctsuccessors.add(srcrev)
1808 obsoleteextinctsuccessors.add(srcrev)
1805 if not successors:
1809 if not successors:
1806 # no successor
1810 # no successor
1807 obsoletenotrebased[srcrev] = None
1811 obsoletenotrebased[srcrev] = None
1808 else:
1812 else:
1809 for succnode in successors:
1813 for succnode in successors:
1810 if succnode not in nodemap:
1814 if succnode not in nodemap:
1811 continue
1815 continue
1812 if cl.isancestor(succnode, destnode):
1816 if cl.isancestor(succnode, destnode):
1813 obsoletenotrebased[srcrev] = nodemap[succnode]
1817 obsoletenotrebased[srcrev] = nodemap[succnode]
1814 break
1818 break
1815 else:
1819 else:
1816 # If 'srcrev' has a successor in rebase set but none in
1820 # If 'srcrev' has a successor in rebase set but none in
1817 # destination (which would be catched above), we shall skip it
1821 # destination (which would be catched above), we shall skip it
1818 # and its descendants to avoid divergence.
1822 # and its descendants to avoid divergence.
1819 if any(nodemap[s] in destmap for s in successors):
1823 if any(nodemap[s] in destmap for s in successors):
1820 obsoletewithoutsuccessorindestination.add(srcrev)
1824 obsoletewithoutsuccessorindestination.add(srcrev)
1821
1825
1822 return (
1826 return (
1823 obsoletenotrebased,
1827 obsoletenotrebased,
1824 obsoletewithoutsuccessorindestination,
1828 obsoletewithoutsuccessorindestination,
1825 obsoleteextinctsuccessors,
1829 obsoleteextinctsuccessors,
1826 )
1830 )
1827
1831
1828 def summaryhook(ui, repo):
1832 def summaryhook(ui, repo):
1829 if not repo.vfs.exists('rebasestate'):
1833 if not repo.vfs.exists('rebasestate'):
1830 return
1834 return
1831 try:
1835 try:
1832 rbsrt = rebaseruntime(repo, ui, {})
1836 rbsrt = rebaseruntime(repo, ui, {})
1833 rbsrt.restorestatus()
1837 rbsrt.restorestatus()
1834 state = rbsrt.state
1838 state = rbsrt.state
1835 except error.RepoLookupError:
1839 except error.RepoLookupError:
1836 # i18n: column positioning for "hg summary"
1840 # i18n: column positioning for "hg summary"
1837 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1841 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1838 ui.write(msg)
1842 ui.write(msg)
1839 return
1843 return
1840 numrebased = len([i for i in state.itervalues() if i >= 0])
1844 numrebased = len([i for i in state.itervalues() if i >= 0])
1841 # i18n: column positioning for "hg summary"
1845 # i18n: column positioning for "hg summary"
1842 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1846 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1843 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1847 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1844 ui.label(_('%d remaining'), 'rebase.remaining') %
1848 ui.label(_('%d remaining'), 'rebase.remaining') %
1845 (len(state) - numrebased)))
1849 (len(state) - numrebased)))
1846
1850
1847 def uisetup(ui):
1851 def uisetup(ui):
1848 #Replace pull with a decorator to provide --rebase option
1852 #Replace pull with a decorator to provide --rebase option
1849 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1853 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1850 entry[1].append(('', 'rebase', None,
1854 entry[1].append(('', 'rebase', None,
1851 _("rebase working directory to branch head")))
1855 _("rebase working directory to branch head")))
1852 entry[1].append(('t', 'tool', '',
1856 entry[1].append(('t', 'tool', '',
1853 _("specify merge tool for rebase")))
1857 _("specify merge tool for rebase")))
1854 cmdutil.summaryhooks.add('rebase', summaryhook)
1858 cmdutil.summaryhooks.add('rebase', summaryhook)
1855 cmdutil.unfinishedstates.append(
1859 cmdutil.unfinishedstates.append(
1856 ['rebasestate', False, False, _('rebase in progress'),
1860 ['rebasestate', False, False, _('rebase in progress'),
1857 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1861 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1858 cmdutil.afterresolvedstates.append(
1862 cmdutil.afterresolvedstates.append(
1859 ['rebasestate', _('hg rebase --continue')])
1863 ['rebasestate', _('hg rebase --continue')])
@@ -1,831 +1,819
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > rebase=
3 > rebase=
4 > mq=
4 > mq=
5 > drawdag=$TESTDIR/drawdag.py
5 > drawdag=$TESTDIR/drawdag.py
6 >
6 >
7 > [phases]
7 > [phases]
8 > publish=False
8 > publish=False
9 >
9 >
10 > [alias]
10 > [alias]
11 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
11 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
12 > tglogp = log -G --template "{rev}: {node|short} {phase} '{desc}' {branches}\n"
12 > tglogp = log -G --template "{rev}: {node|short} {phase} '{desc}' {branches}\n"
13 > EOF
13 > EOF
14
14
15 Highest phase of source commits is used:
15 Highest phase of source commits is used:
16
16
17 $ hg init phase
17 $ hg init phase
18 $ cd phase
18 $ cd phase
19 $ hg debugdrawdag << 'EOF'
19 $ hg debugdrawdag << 'EOF'
20 > D
20 > D
21 > |
21 > |
22 > F C
22 > F C
23 > | |
23 > | |
24 > E B
24 > E B
25 > |/
25 > |/
26 > A
26 > A
27 > EOF
27 > EOF
28
28
29 $ hg phase --force --secret D
29 $ hg phase --force --secret D
30
30
31 $ cat > $TESTTMP/editor.sh <<EOF
31 $ cat > $TESTTMP/editor.sh <<EOF
32 > echo "==== before editing"
32 > echo "==== before editing"
33 > cat \$1
33 > cat \$1
34 > echo "===="
34 > echo "===="
35 > echo "edited manually" >> \$1
35 > echo "edited manually" >> \$1
36 > EOF
36 > EOF
37 $ HGEDITOR="sh $TESTTMP/editor.sh" hg rebase --collapse --keepbranches -e --dest F
37 $ HGEDITOR="sh $TESTTMP/editor.sh" hg rebase --collapse --keepbranches -e --dest F
38 rebasing 1:112478962961 "B" (B)
38 rebasing 1:112478962961 "B" (B)
39 rebasing 3:26805aba1e60 "C" (C)
39 rebasing 3:26805aba1e60 "C" (C)
40 rebasing 5:f585351a92f8 "D" (D tip)
40 rebasing 5:f585351a92f8 "D" (D tip)
41 ==== before editing
41 ==== before editing
42 Collapsed revision
42 Collapsed revision
43 * B
43 * B
44 * C
44 * C
45 * D
45 * D
46
46
47
47
48 HG: Enter commit message. Lines beginning with 'HG:' are removed.
48 HG: Enter commit message. Lines beginning with 'HG:' are removed.
49 HG: Leave message empty to abort commit.
49 HG: Leave message empty to abort commit.
50 HG: --
50 HG: --
51 HG: user: test
51 HG: user: test
52 HG: branch 'default'
52 HG: branch 'default'
53 HG: added B
53 HG: added B
54 HG: added C
54 HG: added C
55 HG: added D
55 HG: added D
56 ====
56 ====
57 saved backup bundle to $TESTTMP/phase/.hg/strip-backup/112478962961-cb2a9b47-rebase.hg
57 saved backup bundle to $TESTTMP/phase/.hg/strip-backup/112478962961-cb2a9b47-rebase.hg
58
58
59 $ hg tglogp
59 $ hg tglogp
60 o 3: 92fa5f5fe108 secret 'Collapsed revision
60 o 3: 92fa5f5fe108 secret 'Collapsed revision
61 | * B
61 | * B
62 | * C
62 | * C
63 | * D
63 | * D
64 |
64 |
65 |
65 |
66 | edited manually'
66 | edited manually'
67 o 2: 64a8289d2492 draft 'F'
67 o 2: 64a8289d2492 draft 'F'
68 |
68 |
69 o 1: 7fb047a69f22 draft 'E'
69 o 1: 7fb047a69f22 draft 'E'
70 |
70 |
71 o 0: 426bada5c675 draft 'A'
71 o 0: 426bada5c675 draft 'A'
72
72
73 $ hg manifest --rev tip
73 $ hg manifest --rev tip
74 A
74 A
75 B
75 B
76 C
76 C
77 D
77 D
78 E
78 E
79 F
79 F
80
80
81 $ cd ..
81 $ cd ..
82
82
83
83
84 Merge gets linearized:
84 Merge gets linearized:
85
85
86 $ hg init linearized-merge
86 $ hg init linearized-merge
87 $ cd linearized-merge
87 $ cd linearized-merge
88
88
89 $ hg debugdrawdag << 'EOF'
89 $ hg debugdrawdag << 'EOF'
90 > F D
90 > F D
91 > |/|
91 > |/|
92 > C B
92 > C B
93 > |/
93 > |/
94 > A
94 > A
95 > EOF
95 > EOF
96
96
97 $ hg phase --force --secret D
97 $ hg phase --force --secret D
98 $ hg rebase --source B --collapse --dest F
98 $ hg rebase --source B --collapse --dest F
99 rebasing 1:112478962961 "B" (B)
99 rebasing 1:112478962961 "B" (B)
100 rebasing 3:4e4f9194f9f1 "D" (D)
100 rebasing 3:4e4f9194f9f1 "D" (D)
101 saved backup bundle to $TESTTMP/linearized-merge/.hg/strip-backup/112478962961-e389075b-rebase.hg
101 saved backup bundle to $TESTTMP/linearized-merge/.hg/strip-backup/112478962961-e389075b-rebase.hg
102
102
103 $ hg tglog
103 $ hg tglog
104 o 3: 5bdc08b7da2b 'Collapsed revision
104 o 3: 5bdc08b7da2b 'Collapsed revision
105 | * B
105 | * B
106 | * D'
106 | * D'
107 o 2: afc707c82df0 'F'
107 o 2: afc707c82df0 'F'
108 |
108 |
109 o 1: dc0947a82db8 'C'
109 o 1: dc0947a82db8 'C'
110 |
110 |
111 o 0: 426bada5c675 'A'
111 o 0: 426bada5c675 'A'
112
112
113 $ hg manifest --rev tip
113 $ hg manifest --rev tip
114 A
114 A
115 B
115 B
116 C
116 C
117 F
117 F
118
118
119 $ cd ..
119 $ cd ..
120
120
121 Custom message:
121 Custom message:
122
122
123 $ hg init message
123 $ hg init message
124 $ cd message
124 $ cd message
125
125
126 $ hg debugdrawdag << 'EOF'
126 $ hg debugdrawdag << 'EOF'
127 > C
127 > C
128 > |
128 > |
129 > D B
129 > D B
130 > |/
130 > |/
131 > A
131 > A
132 > EOF
132 > EOF
133
133
134
134
135 $ hg rebase --base B -m 'custom message'
135 $ hg rebase --base B -m 'custom message'
136 abort: message can only be specified with collapse
136 abort: message can only be specified with collapse
137 [255]
137 [255]
138
138
139 $ cat > $TESTTMP/checkeditform.sh <<EOF
139 $ cat > $TESTTMP/checkeditform.sh <<EOF
140 > env | grep HGEDITFORM
140 > env | grep HGEDITFORM
141 > true
141 > true
142 > EOF
142 > EOF
143 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg rebase --source B --collapse -m 'custom message' -e --dest D
143 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg rebase --source B --collapse -m 'custom message' -e --dest D
144 rebasing 1:112478962961 "B" (B)
144 rebasing 1:112478962961 "B" (B)
145 rebasing 3:26805aba1e60 "C" (C tip)
145 rebasing 3:26805aba1e60 "C" (C tip)
146 HGEDITFORM=rebase.collapse
146 HGEDITFORM=rebase.collapse
147 saved backup bundle to $TESTTMP/message/.hg/strip-backup/112478962961-f4131707-rebase.hg
147 saved backup bundle to $TESTTMP/message/.hg/strip-backup/112478962961-f4131707-rebase.hg
148
148
149 $ hg tglog
149 $ hg tglog
150 o 2: 2f197b9a08f3 'custom message'
150 o 2: 2f197b9a08f3 'custom message'
151 |
151 |
152 o 1: b18e25de2cf5 'D'
152 o 1: b18e25de2cf5 'D'
153 |
153 |
154 o 0: 426bada5c675 'A'
154 o 0: 426bada5c675 'A'
155
155
156 $ hg manifest --rev tip
156 $ hg manifest --rev tip
157 A
157 A
158 B
158 B
159 C
159 C
160 D
160 D
161
161
162 $ cd ..
162 $ cd ..
163
163
164 Rebase and collapse - more than one external (fail):
164 Rebase and collapse - more than one external (fail):
165
165
166 $ hg init multiple-external-parents
166 $ hg init multiple-external-parents
167 $ cd multiple-external-parents
167 $ cd multiple-external-parents
168
168
169 $ hg debugdrawdag << 'EOF'
169 $ hg debugdrawdag << 'EOF'
170 > G
170 > G
171 > |\
171 > |\
172 > | F
172 > | F
173 > | |
173 > | |
174 > D E
174 > D E
175 > |\|
175 > |\|
176 > H C B
176 > H C B
177 > \|/
177 > \|/
178 > A
178 > A
179 > EOF
179 > EOF
180
180
181 $ hg rebase -s C --dest H --collapse
181 $ hg rebase -s C --dest H --collapse
182 abort: unable to collapse on top of 3, there is more than one external parent: 1, 6
182 abort: unable to collapse on top of 3, there is more than one external parent: 1, 6
183 [255]
183 [255]
184
184
185 Rebase and collapse - E onto H:
185 Rebase and collapse - E onto H:
186
186
187 $ hg rebase -s E --dest I --collapse # root (E) is not a merge
187 $ hg rebase -s E --dest I --collapse # root (E) is not a merge
188 abort: unknown revision 'I'!
188 abort: unknown revision 'I'!
189 [255]
189 [255]
190
190
191 $ hg tglog
191 $ hg tglog
192 o 7: 64e264db77f0 'G'
192 o 7: 64e264db77f0 'G'
193 |\
193 |\
194 | o 6: 11abe3fb10b8 'F'
194 | o 6: 11abe3fb10b8 'F'
195 | |
195 | |
196 | o 5: 49cb92066bfd 'E'
196 | o 5: 49cb92066bfd 'E'
197 | |
197 | |
198 o | 4: 4e4f9194f9f1 'D'
198 o | 4: 4e4f9194f9f1 'D'
199 |\|
199 |\|
200 | | o 3: 575c4b5ec114 'H'
200 | | o 3: 575c4b5ec114 'H'
201 | | |
201 | | |
202 o---+ 2: dc0947a82db8 'C'
202 o---+ 2: dc0947a82db8 'C'
203 / /
203 / /
204 o / 1: 112478962961 'B'
204 o / 1: 112478962961 'B'
205 |/
205 |/
206 o 0: 426bada5c675 'A'
206 o 0: 426bada5c675 'A'
207
207
208 $ hg manifest --rev tip
208 $ hg manifest --rev tip
209 A
209 A
210 B
210 B
211 C
211 C
212 E
212 E
213 F
213 F
214
214
215 $ cd ..
215 $ cd ..
216
216
217
217
218
218
219
219
220 Test that branchheads cache is updated correctly when doing a strip in which
220 Test that branchheads cache is updated correctly when doing a strip in which
221 the parent of the ancestor node to be stripped does not become a head and also,
221 the parent of the ancestor node to be stripped does not become a head and also,
222 the parent of a node that is a child of the node stripped becomes a head (node
222 the parent of a node that is a child of the node stripped becomes a head (node
223 3). The code is now much simpler and we could just test a simpler scenario
223 3). The code is now much simpler and we could just test a simpler scenario
224 We keep it the test this way in case new complexity is injected.
224 We keep it the test this way in case new complexity is injected.
225
225
226 Create repo b:
226 Create repo b:
227
227
228 $ hg init branch-heads
228 $ hg init branch-heads
229 $ cd branch-heads
229 $ cd branch-heads
230
230
231 $ hg debugdrawdag << 'EOF'
231 $ hg debugdrawdag << 'EOF'
232 > G
232 > G
233 > |\
233 > |\
234 > | F
234 > | F
235 > | |
235 > | |
236 > D E
236 > D E
237 > |\|
237 > |\|
238 > H C B
238 > H C B
239 > \|/
239 > \|/
240 > A
240 > A
241 > EOF
241 > EOF
242
242
243 $ hg heads --template="{rev}:{node} {branch}\n"
243 $ hg heads --template="{rev}:{node} {branch}\n"
244 7:64e264db77f061f16d9132b70c5a58e2461fb630 default
244 7:64e264db77f061f16d9132b70c5a58e2461fb630 default
245 3:575c4b5ec114d64b681d33f8792853568bfb2b2c default
245 3:575c4b5ec114d64b681d33f8792853568bfb2b2c default
246
246
247 $ cat $TESTTMP/branch-heads/.hg/cache/branch2-served
247 $ cat $TESTTMP/branch-heads/.hg/cache/branch2-served
248 64e264db77f061f16d9132b70c5a58e2461fb630 7
248 64e264db77f061f16d9132b70c5a58e2461fb630 7
249 575c4b5ec114d64b681d33f8792853568bfb2b2c o default
249 575c4b5ec114d64b681d33f8792853568bfb2b2c o default
250 64e264db77f061f16d9132b70c5a58e2461fb630 o default
250 64e264db77f061f16d9132b70c5a58e2461fb630 o default
251
251
252 $ hg strip 4
252 $ hg strip 4
253 saved backup bundle to $TESTTMP/branch-heads/.hg/strip-backup/4e4f9194f9f1-5ec4b5e6-backup.hg
253 saved backup bundle to $TESTTMP/branch-heads/.hg/strip-backup/4e4f9194f9f1-5ec4b5e6-backup.hg
254
254
255 $ cat $TESTTMP/branch-heads/.hg/cache/branch2-served
255 $ cat $TESTTMP/branch-heads/.hg/cache/branch2-served
256 11abe3fb10b8689b560681094b17fe161871d043 5
256 11abe3fb10b8689b560681094b17fe161871d043 5
257 dc0947a82db884575bb76ea10ac97b08536bfa03 o default
257 dc0947a82db884575bb76ea10ac97b08536bfa03 o default
258 575c4b5ec114d64b681d33f8792853568bfb2b2c o default
258 575c4b5ec114d64b681d33f8792853568bfb2b2c o default
259 11abe3fb10b8689b560681094b17fe161871d043 o default
259 11abe3fb10b8689b560681094b17fe161871d043 o default
260
260
261 $ hg heads --template="{rev}:{node} {branch}\n"
261 $ hg heads --template="{rev}:{node} {branch}\n"
262 5:11abe3fb10b8689b560681094b17fe161871d043 default
262 5:11abe3fb10b8689b560681094b17fe161871d043 default
263 3:575c4b5ec114d64b681d33f8792853568bfb2b2c default
263 3:575c4b5ec114d64b681d33f8792853568bfb2b2c default
264 2:dc0947a82db884575bb76ea10ac97b08536bfa03 default
264 2:dc0947a82db884575bb76ea10ac97b08536bfa03 default
265
265
266 $ cd ..
266 $ cd ..
267
267
268
268
269
269
270 Preserves external parent
270 Preserves external parent
271
271
272 $ hg init external-parent
272 $ hg init external-parent
273 $ cd external-parent
273 $ cd external-parent
274
274
275 $ hg debugdrawdag << 'EOF'
275 $ hg debugdrawdag << 'EOF'
276 > H
276 > H
277 > |\
277 > |\
278 > | G
278 > | G
279 > | |
279 > | |
280 > | F # F/E = F\n
280 > | F # F/E = F\n
281 > | |
281 > | |
282 > D E # D/D = D\n
282 > D E # D/D = D\n
283 > |\|
283 > |\|
284 > I C B
284 > I C B
285 > \|/
285 > \|/
286 > A
286 > A
287 > EOF
287 > EOF
288
288
289 $ hg rebase -s F --dest I --collapse # root (F) is not a merge
289 $ hg rebase -s F --dest I --collapse # root (F) is not a merge
290 rebasing 6:c82b08f646f1 "F" (F)
290 rebasing 6:c82b08f646f1 "F" (F)
291 rebasing 7:a6db7fa104e1 "G" (G)
291 rebasing 7:a6db7fa104e1 "G" (G)
292 rebasing 8:e1d201b72d91 "H" (H tip)
292 rebasing 8:e1d201b72d91 "H" (H tip)
293 saved backup bundle to $TESTTMP/external-parent/.hg/strip-backup/c82b08f646f1-f2721fbf-rebase.hg
293 saved backup bundle to $TESTTMP/external-parent/.hg/strip-backup/c82b08f646f1-f2721fbf-rebase.hg
294
294
295 $ hg tglog
295 $ hg tglog
296 o 6: 681daa3e686d 'Collapsed revision
296 o 6: 681daa3e686d 'Collapsed revision
297 |\ * F
297 |\ * F
298 | | * G
298 | | * G
299 | | * H'
299 | | * H'
300 | | o 5: 49cb92066bfd 'E'
300 | | o 5: 49cb92066bfd 'E'
301 | | |
301 | | |
302 | o | 4: 09143c0bf13e 'D'
302 | o | 4: 09143c0bf13e 'D'
303 | |\|
303 | |\|
304 o | | 3: 08ebfeb61bac 'I'
304 o | | 3: 08ebfeb61bac 'I'
305 | | |
305 | | |
306 | o | 2: dc0947a82db8 'C'
306 | o | 2: dc0947a82db8 'C'
307 |/ /
307 |/ /
308 | o 1: 112478962961 'B'
308 | o 1: 112478962961 'B'
309 |/
309 |/
310 o 0: 426bada5c675 'A'
310 o 0: 426bada5c675 'A'
311
311
312 $ hg manifest --rev tip
312 $ hg manifest --rev tip
313 A
313 A
314 C
314 C
315 D
315 D
316 E
316 E
317 F
317 F
318 G
318 G
319 I
319 I
320
320
321 $ hg up tip -q
321 $ hg up tip -q
322 $ cat E
322 $ cat E
323 F
323 F
324
324
325 $ cd ..
325 $ cd ..
326
326
327 Rebasing from multiple bases:
327 Rebasing from multiple bases:
328
328
329 $ hg init multiple-bases
329 $ hg init multiple-bases
330 $ cd multiple-bases
330 $ cd multiple-bases
331 $ hg debugdrawdag << 'EOF'
331 $ hg debugdrawdag << 'EOF'
332 > C B
332 > C B
333 > D |/
333 > D |/
334 > |/
334 > |/
335 > A
335 > A
336 > EOF
336 > EOF
337 $ hg rebase --collapse -r 'B+C' -d D
337 $ hg rebase --collapse -r 'B+C' -d D
338 rebasing 1:fc2b737bb2e5 "B" (B)
338 rebasing 1:fc2b737bb2e5 "B" (B)
339 rebasing 2:dc0947a82db8 "C" (C)
339 rebasing 2:dc0947a82db8 "C" (C)
340 saved backup bundle to $TESTTMP/multiple-bases/.hg/strip-backup/dc0947a82db8-b0c1a7ea-rebase.hg
340 saved backup bundle to $TESTTMP/multiple-bases/.hg/strip-backup/dc0947a82db8-b0c1a7ea-rebase.hg
341 $ hg tglog
341 $ hg tglog
342 o 2: 2127ae44d291 'Collapsed revision
342 o 2: 2127ae44d291 'Collapsed revision
343 | * B
343 | * B
344 | * C'
344 | * C'
345 o 1: b18e25de2cf5 'D'
345 o 1: b18e25de2cf5 'D'
346 |
346 |
347 o 0: 426bada5c675 'A'
347 o 0: 426bada5c675 'A'
348
348
349 $ cd ..
349 $ cd ..
350
350
351 With non-contiguous commits:
351 With non-contiguous commits:
352
352
353 $ hg init non-contiguous
353 $ hg init non-contiguous
354 $ cd non-contiguous
354 $ cd non-contiguous
355 $ cat >> .hg/hgrc <<EOF
355 $ cat >> .hg/hgrc <<EOF
356 > [experimental]
356 > [experimental]
357 > evolution=all
357 > evolution=all
358 > EOF
358 > EOF
359
359
360 $ hg debugdrawdag << 'EOF'
360 $ hg debugdrawdag << 'EOF'
361 > F
361 > F
362 > |
362 > |
363 > E
363 > E
364 > |
364 > |
365 > D
365 > D
366 > |
366 > |
367 > C
367 > C
368 > |
368 > |
369 > B G
369 > B G
370 > |/
370 > |/
371 > A
371 > A
372 > EOF
372 > EOF
373
373
374 BROKEN: should be allowed
374 BROKEN: should be allowed
375 $ hg rebase --collapse -r 'B+D+F' -d G
375 $ hg rebase --collapse -r 'B+D+F' -d G
376 abort: unable to collapse on top of 2, there is more than one external parent: 3, 5
376 abort: unable to collapse on top of 2, there is more than one external parent: 3, 5
377 [255]
377 [255]
378 $ cd ..
378 $ cd ..
379
379
380
380
381 $ hg init multiple-external-parents-2
381 $ hg init multiple-external-parents-2
382 $ cd multiple-external-parents-2
382 $ cd multiple-external-parents-2
383 $ hg debugdrawdag << 'EOF'
383 $ hg debugdrawdag << 'EOF'
384 > D G
384 > D G
385 > |\ /|
385 > |\ /|
386 > B C E F
386 > B C E F
387 > \| |/
387 > \| |/
388 > \ H /
388 > \ H /
389 > \|/
389 > \|/
390 > A
390 > A
391 > EOF
391 > EOF
392
392
393 $ hg rebase --collapse -d H -s 'B+F'
393 $ hg rebase --collapse -d H -s 'B+F'
394 abort: unable to collapse on top of 5, there is more than one external parent: 1, 3
394 abort: unable to collapse on top of 5, there is more than one external parent: 1, 3
395 [255]
395 [255]
396 $ cd ..
396 $ cd ..
397
397
398 With internal merge:
398 With internal merge:
399
399
400 $ hg init internal-merge
400 $ hg init internal-merge
401 $ cd internal-merge
401 $ cd internal-merge
402
402
403 $ hg debugdrawdag << 'EOF'
403 $ hg debugdrawdag << 'EOF'
404 > E
404 > E
405 > |\
405 > |\
406 > C D
406 > C D
407 > |/
407 > |/
408 > F B
408 > F B
409 > |/
409 > |/
410 > A
410 > A
411 > EOF
411 > EOF
412
412
413
413
414 $ hg rebase -s B --collapse --dest F
414 $ hg rebase -s B --collapse --dest F
415 rebasing 1:112478962961 "B" (B)
415 rebasing 1:112478962961 "B" (B)
416 rebasing 3:26805aba1e60 "C" (C)
416 rebasing 3:26805aba1e60 "C" (C)
417 rebasing 4:be0ef73c17ad "D" (D)
417 rebasing 4:be0ef73c17ad "D" (D)
418 rebasing 5:02c4367d6973 "E" (E tip)
418 rebasing 5:02c4367d6973 "E" (E tip)
419 saved backup bundle to $TESTTMP/internal-merge/.hg/strip-backup/112478962961-1dfb057b-rebase.hg
419 saved backup bundle to $TESTTMP/internal-merge/.hg/strip-backup/112478962961-1dfb057b-rebase.hg
420
420
421 $ hg tglog
421 $ hg tglog
422 o 2: c0512a1797b0 'Collapsed revision
422 o 2: c0512a1797b0 'Collapsed revision
423 | * B
423 | * B
424 | * C
424 | * C
425 | * D
425 | * D
426 | * E'
426 | * E'
427 o 1: 8908a377a434 'F'
427 o 1: 8908a377a434 'F'
428 |
428 |
429 o 0: 426bada5c675 'A'
429 o 0: 426bada5c675 'A'
430
430
431 $ hg manifest --rev tip
431 $ hg manifest --rev tip
432 A
432 A
433 B
433 B
434 C
434 C
435 D
435 D
436 F
436 F
437 $ cd ..
437 $ cd ..
438
438
439 Interactions between collapse and keepbranches
439 Interactions between collapse and keepbranches
440 $ hg init e
440 $ hg init e
441 $ cd e
441 $ cd e
442 $ echo 'a' > a
442 $ echo 'a' > a
443 $ hg ci -Am 'A'
443 $ hg ci -Am 'A'
444 adding a
444 adding a
445
445
446 $ hg branch 'one'
446 $ hg branch 'one'
447 marked working directory as branch one
447 marked working directory as branch one
448 (branches are permanent and global, did you want a bookmark?)
448 (branches are permanent and global, did you want a bookmark?)
449 $ echo 'b' > b
449 $ echo 'b' > b
450 $ hg ci -Am 'B'
450 $ hg ci -Am 'B'
451 adding b
451 adding b
452
452
453 $ hg branch 'two'
453 $ hg branch 'two'
454 marked working directory as branch two
454 marked working directory as branch two
455 $ echo 'c' > c
455 $ echo 'c' > c
456 $ hg ci -Am 'C'
456 $ hg ci -Am 'C'
457 adding c
457 adding c
458
458
459 $ hg up -q 0
459 $ hg up -q 0
460 $ echo 'd' > d
460 $ echo 'd' > d
461 $ hg ci -Am 'D'
461 $ hg ci -Am 'D'
462 adding d
462 adding d
463
463
464 $ hg tglog
464 $ hg tglog
465 @ 3: 41acb9dca9eb 'D'
465 @ 3: 41acb9dca9eb 'D'
466 |
466 |
467 | o 2: 8ac4a08debf1 'C' two
467 | o 2: 8ac4a08debf1 'C' two
468 | |
468 | |
469 | o 1: 1ba175478953 'B' one
469 | o 1: 1ba175478953 'B' one
470 |/
470 |/
471 o 0: 1994f17a630e 'A'
471 o 0: 1994f17a630e 'A'
472
472
473 $ hg rebase --keepbranches --collapse -s 1 -d 3
473 $ hg rebase --keepbranches --collapse -s 1 -d 3
474 abort: cannot collapse multiple named branches
474 abort: cannot collapse multiple named branches
475 [255]
475 [255]
476
476
477 $ repeatchange() {
477 $ repeatchange() {
478 > hg checkout $1
478 > hg checkout $1
479 > hg cp d z
479 > hg cp d z
480 > echo blah >> z
480 > echo blah >> z
481 > hg commit -Am "$2" --user "$3"
481 > hg commit -Am "$2" --user "$3"
482 > }
482 > }
483 $ repeatchange 3 "E" "user1"
483 $ repeatchange 3 "E" "user1"
484 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
484 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 $ repeatchange 3 "E" "user2"
485 $ repeatchange 3 "E" "user2"
486 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
486 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
487 created new head
487 created new head
488 $ hg tglog
488 $ hg tglog
489 @ 5: fbfb97b1089a 'E'
489 @ 5: fbfb97b1089a 'E'
490 |
490 |
491 | o 4: f338eb3c2c7c 'E'
491 | o 4: f338eb3c2c7c 'E'
492 |/
492 |/
493 o 3: 41acb9dca9eb 'D'
493 o 3: 41acb9dca9eb 'D'
494 |
494 |
495 | o 2: 8ac4a08debf1 'C' two
495 | o 2: 8ac4a08debf1 'C' two
496 | |
496 | |
497 | o 1: 1ba175478953 'B' one
497 | o 1: 1ba175478953 'B' one
498 |/
498 |/
499 o 0: 1994f17a630e 'A'
499 o 0: 1994f17a630e 'A'
500
500
501 $ hg rebase -s 5 -d 4
501 $ hg rebase -s 5 -d 4
502 rebasing 5:fbfb97b1089a "E" (tip)
502 rebasing 5:fbfb97b1089a "E" (tip)
503 note: rebase of 5:fbfb97b1089a created no changes to commit
503 note: rebase of 5:fbfb97b1089a created no changes to commit
504 saved backup bundle to $TESTTMP/e/.hg/strip-backup/fbfb97b1089a-553e1d85-rebase.hg
504 saved backup bundle to $TESTTMP/e/.hg/strip-backup/fbfb97b1089a-553e1d85-rebase.hg
505 $ hg tglog
505 $ hg tglog
506 @ 4: f338eb3c2c7c 'E'
506 @ 4: f338eb3c2c7c 'E'
507 |
507 |
508 o 3: 41acb9dca9eb 'D'
508 o 3: 41acb9dca9eb 'D'
509 |
509 |
510 | o 2: 8ac4a08debf1 'C' two
510 | o 2: 8ac4a08debf1 'C' two
511 | |
511 | |
512 | o 1: 1ba175478953 'B' one
512 | o 1: 1ba175478953 'B' one
513 |/
513 |/
514 o 0: 1994f17a630e 'A'
514 o 0: 1994f17a630e 'A'
515
515
516 $ hg export tip
516 $ hg export tip
517 # HG changeset patch
517 # HG changeset patch
518 # User user1
518 # User user1
519 # Date 0 0
519 # Date 0 0
520 # Thu Jan 01 00:00:00 1970 +0000
520 # Thu Jan 01 00:00:00 1970 +0000
521 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
521 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
522 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
522 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
523 E
523 E
524
524
525 diff -r 41acb9dca9eb -r f338eb3c2c7c z
525 diff -r 41acb9dca9eb -r f338eb3c2c7c z
526 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
526 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
527 +++ b/z Thu Jan 01 00:00:00 1970 +0000
527 +++ b/z Thu Jan 01 00:00:00 1970 +0000
528 @@ -0,0 +1,2 @@
528 @@ -0,0 +1,2 @@
529 +d
529 +d
530 +blah
530 +blah
531
531
532 $ cd ..
532 $ cd ..
533
533
534 Rebase, collapse and copies
534 Rebase, collapse and copies
535
535
536 $ hg init copies
536 $ hg init copies
537 $ cd copies
537 $ cd copies
538 $ hg unbundle "$TESTDIR/bundles/renames.hg"
538 $ hg unbundle "$TESTDIR/bundles/renames.hg"
539 adding changesets
539 adding changesets
540 adding manifests
540 adding manifests
541 adding file changes
541 adding file changes
542 added 4 changesets with 11 changes to 7 files (+1 heads)
542 added 4 changesets with 11 changes to 7 files (+1 heads)
543 new changesets f447d5abf5ea:338e84e2e558
543 new changesets f447d5abf5ea:338e84e2e558
544 (run 'hg heads' to see heads, 'hg merge' to merge)
544 (run 'hg heads' to see heads, 'hg merge' to merge)
545 $ hg up -q tip
545 $ hg up -q tip
546 $ hg tglog
546 $ hg tglog
547 @ 3: 338e84e2e558 'move2'
547 @ 3: 338e84e2e558 'move2'
548 |
548 |
549 o 2: 6e7340ee38c0 'move1'
549 o 2: 6e7340ee38c0 'move1'
550 |
550 |
551 | o 1: 1352765a01d4 'change'
551 | o 1: 1352765a01d4 'change'
552 |/
552 |/
553 o 0: f447d5abf5ea 'add'
553 o 0: f447d5abf5ea 'add'
554
554
555 $ hg rebase --collapse -d 1
555 $ hg rebase --collapse -d 1
556 rebasing 2:6e7340ee38c0 "move1"
556 rebasing 2:6e7340ee38c0 "move1"
557 merging a and d to d
557 merging a and d to d
558 merging b and e to e
558 merging b and e to e
559 merging c and f to f
559 merging c and f to f
560 rebasing 3:338e84e2e558 "move2" (tip)
560 rebasing 3:338e84e2e558 "move2" (tip)
561 merging f and c to c
561 merging f and c to c
562 merging e and g to g
562 merging e and g to g
563 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/6e7340ee38c0-ef8ef003-rebase.hg
563 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/6e7340ee38c0-ef8ef003-rebase.hg
564 $ hg st
564 $ hg st
565 $ hg st --copies --change tip
565 $ hg st --copies --change tip
566 A d
566 A d
567 a
567 a
568 A g
568 A g
569 b
569 b
570 R b
570 R b
571 $ hg up tip -q
571 $ hg up tip -q
572 $ cat c
572 $ cat c
573 c
573 c
574 c
574 c
575 $ cat d
575 $ cat d
576 a
576 a
577 a
577 a
578 $ cat g
578 $ cat g
579 b
579 b
580 b
580 b
581 $ hg log -r . --template "{file_copies}\n"
581 $ hg log -r . --template "{file_copies}\n"
582 d (a)g (b)
582 d (a)g (b)
583
583
584 Test collapsing a middle revision in-place
584 Test collapsing a middle revision in-place
585
585
586 $ hg tglog
586 $ hg tglog
587 @ 2: 64b456429f67 'Collapsed revision
587 @ 2: 64b456429f67 'Collapsed revision
588 | * move1
588 | * move1
589 | * move2'
589 | * move2'
590 o 1: 1352765a01d4 'change'
590 o 1: 1352765a01d4 'change'
591 |
591 |
592 o 0: f447d5abf5ea 'add'
592 o 0: f447d5abf5ea 'add'
593
593
594 $ hg rebase --collapse -r 1 -d 0
594 $ hg rebase --collapse -r 1 -d 0
595 abort: can't remove original changesets with unrebased descendants
595 abort: can't remove original changesets with unrebased descendants
596 (use --keep to keep original changesets)
596 (use --keep to keep original changesets)
597 [255]
597 [255]
598
598
599 Test collapsing in place
599 Test collapsing in place
600
600
601 $ hg rebase --collapse -b . -d 0
601 $ hg rebase --collapse -b . -d 0
602 rebasing 1:1352765a01d4 "change"
602 rebasing 1:1352765a01d4 "change"
603 rebasing 2:64b456429f67 "Collapsed revision" (tip)
603 rebasing 2:64b456429f67 "Collapsed revision" (tip)
604 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/1352765a01d4-45a352ea-rebase.hg
604 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/1352765a01d4-45a352ea-rebase.hg
605 $ hg st --change tip --copies
605 $ hg st --change tip --copies
606 M a
606 M a
607 M c
607 M c
608 A d
608 A d
609 a
609 a
610 A g
610 A g
611 b
611 b
612 R b
612 R b
613 $ hg up tip -q
613 $ hg up tip -q
614 $ cat a
614 $ cat a
615 a
615 a
616 a
616 a
617 $ cat c
617 $ cat c
618 c
618 c
619 c
619 c
620 $ cat d
620 $ cat d
621 a
621 a
622 a
622 a
623 $ cat g
623 $ cat g
624 b
624 b
625 b
625 b
626 $ cd ..
626 $ cd ..
627
627
628
628
629 Test stripping a revision with another child
629 Test stripping a revision with another child
630
630
631 $ hg init f
631 $ hg init f
632 $ cd f
632 $ cd f
633
633
634 $ hg debugdrawdag << 'EOF'
634 $ hg debugdrawdag << 'EOF'
635 > C B
635 > C B
636 > |/
636 > |/
637 > A
637 > A
638 > EOF
638 > EOF
639
639
640 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
640 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
641 2:dc0947a82db884575bb76ea10ac97b08536bfa03 default: C
641 2:dc0947a82db884575bb76ea10ac97b08536bfa03 default: C
642 1:112478962961147124edd43549aedd1a335e44bf default: B
642 1:112478962961147124edd43549aedd1a335e44bf default: B
643
643
644 $ hg strip C
644 $ hg strip C
645 saved backup bundle to $TESTTMP/f/.hg/strip-backup/dc0947a82db8-d21b92a4-backup.hg
645 saved backup bundle to $TESTTMP/f/.hg/strip-backup/dc0947a82db8-d21b92a4-backup.hg
646
646
647 $ hg tglog
647 $ hg tglog
648 o 1: 112478962961 'B'
648 o 1: 112478962961 'B'
649 |
649 |
650 o 0: 426bada5c675 'A'
650 o 0: 426bada5c675 'A'
651
651
652
652
653
653
654 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
654 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
655 1:112478962961147124edd43549aedd1a335e44bf default: B
655 1:112478962961147124edd43549aedd1a335e44bf default: B
656
656
657 $ cd ..
657 $ cd ..
658
658
659 Test collapsing changes that add then remove a file
659 Test collapsing changes that add then remove a file
660
660
661 $ hg init collapseaddremove
661 $ hg init collapseaddremove
662 $ cd collapseaddremove
662 $ cd collapseaddremove
663
663
664 $ touch base
664 $ touch base
665 $ hg commit -Am base
665 $ hg commit -Am base
666 adding base
666 adding base
667 $ touch a
667 $ touch a
668 $ hg commit -Am a
668 $ hg commit -Am a
669 adding a
669 adding a
670 $ hg rm a
670 $ hg rm a
671 $ touch b
671 $ touch b
672 $ hg commit -Am b
672 $ hg commit -Am b
673 adding b
673 adding b
674 $ hg book foo
674 $ hg book foo
675 $ hg rebase -d 0 -r "1::2" --collapse -m collapsed
675 $ hg rebase -d 0 -r "1::2" --collapse -m collapsed
676 rebasing 1:6d8d9f24eec3 "a"
676 rebasing 1:6d8d9f24eec3 "a"
677 rebasing 2:1cc73eca5ecc "b" (foo tip)
677 rebasing 2:1cc73eca5ecc "b" (foo tip)
678 saved backup bundle to $TESTTMP/collapseaddremove/.hg/strip-backup/6d8d9f24eec3-77d3b6e2-rebase.hg
678 saved backup bundle to $TESTTMP/collapseaddremove/.hg/strip-backup/6d8d9f24eec3-77d3b6e2-rebase.hg
679 $ hg log -G --template "{rev}: '{desc}' {bookmarks}"
679 $ hg log -G --template "{rev}: '{desc}' {bookmarks}"
680 @ 1: 'collapsed' foo
680 @ 1: 'collapsed' foo
681 |
681 |
682 o 0: 'base'
682 o 0: 'base'
683
683
684 $ hg manifest --rev tip
684 $ hg manifest --rev tip
685 b
685 b
686 base
686 base
687
687
688 $ cd ..
688 $ cd ..
689
689
690 Test that rebase --collapse will remember message after
690 Test that rebase --collapse will remember message after
691 running into merge conflict and invoking rebase --continue.
691 running into merge conflict and invoking rebase --continue.
692
692
693 $ hg init collapse_remember_message
693 $ hg init collapse_remember_message
694 $ cd collapse_remember_message
694 $ cd collapse_remember_message
695 $ hg debugdrawdag << 'EOF'
695 $ hg debugdrawdag << 'EOF'
696 > C B # B/A = B\n
696 > C B # B/A = B\n
697 > |/ # C/A = C\n
697 > |/ # C/A = C\n
698 > A
698 > A
699 > EOF
699 > EOF
700 $ hg rebase --collapse -m "new message" -b B -d C
700 $ hg rebase --collapse -m "new message" -b B -d C
701 rebasing 1:81e5401e4d37 "B" (B)
701 rebasing 1:81e5401e4d37 "B" (B)
702 merging A
702 merging A
703 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
703 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
704 unresolved conflicts (see hg resolve, then hg rebase --continue)
704 unresolved conflicts (see hg resolve, then hg rebase --continue)
705 [1]
705 [1]
706 $ rm A.orig
706 $ rm A.orig
707 $ hg resolve --mark A
707 $ hg resolve --mark A
708 (no more unresolved files)
708 (no more unresolved files)
709 continue: hg rebase --continue
709 continue: hg rebase --continue
710 $ hg rebase --continue
710 $ hg rebase --continue
711 rebasing 1:81e5401e4d37 "B" (B)
711 rebasing 1:81e5401e4d37 "B" (B)
712 saved backup bundle to $TESTTMP/collapse_remember_message/.hg/strip-backup/81e5401e4d37-96c3dd30-rebase.hg
712 saved backup bundle to $TESTTMP/collapse_remember_message/.hg/strip-backup/81e5401e4d37-96c3dd30-rebase.hg
713 $ hg log
713 $ hg log
714 changeset: 2:17186933e123
714 changeset: 2:17186933e123
715 tag: tip
715 tag: tip
716 user: test
716 user: test
717 date: Thu Jan 01 00:00:00 1970 +0000
717 date: Thu Jan 01 00:00:00 1970 +0000
718 summary: new message
718 summary: new message
719
719
720 changeset: 1:043039e9df84
720 changeset: 1:043039e9df84
721 tag: C
721 tag: C
722 user: test
722 user: test
723 date: Thu Jan 01 00:00:00 1970 +0000
723 date: Thu Jan 01 00:00:00 1970 +0000
724 summary: C
724 summary: C
725
725
726 changeset: 0:426bada5c675
726 changeset: 0:426bada5c675
727 tag: A
727 tag: A
728 user: test
728 user: test
729 date: Thu Jan 01 00:00:00 1970 +0000
729 date: Thu Jan 01 00:00:00 1970 +0000
730 summary: A
730 summary: A
731
731
732 $ cd ..
732 $ cd ..
733
733
734 Test aborted editor on final message
734 Test aborted editor on final message
735
735
736 $ HGMERGE=:merge3
736 $ HGMERGE=:merge3
737 $ export HGMERGE
737 $ export HGMERGE
738 $ hg init aborted-editor
738 $ hg init aborted-editor
739 $ cd aborted-editor
739 $ cd aborted-editor
740 $ hg debugdrawdag << 'EOF'
740 $ hg debugdrawdag << 'EOF'
741 > C # D/A = D\n
741 > C # D/A = D\n
742 > | # C/A = C\n
742 > | # C/A = C\n
743 > B D # B/A = B\n
743 > B D # B/A = B\n
744 > |/ # A/A = A\n
744 > |/ # A/A = A\n
745 > A
745 > A
746 > EOF
746 > EOF
747 $ hg rebase --collapse -t internal:merge3 -s B -d D
747 $ hg rebase --collapse -t internal:merge3 -s B -d D
748 rebasing 1:f899f3910ce7 "B" (B)
748 rebasing 1:f899f3910ce7 "B" (B)
749 merging A
749 merging A
750 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
750 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
751 unresolved conflicts (see hg resolve, then hg rebase --continue)
751 unresolved conflicts (see hg resolve, then hg rebase --continue)
752 [1]
752 [1]
753 $ hg tglog
753 $ hg tglog
754 o 3: 63668d570d21 'C'
754 o 3: 63668d570d21 'C'
755 |
755 |
756 | @ 2: 82b8abf9c185 'D'
756 | @ 2: 82b8abf9c185 'D'
757 | |
757 | |
758 @ | 1: f899f3910ce7 'B'
758 @ | 1: f899f3910ce7 'B'
759 |/
759 |/
760 o 0: 4a2df7238c3b 'A'
760 o 0: 4a2df7238c3b 'A'
761
761
762 $ cat A
762 $ cat A
763 <<<<<<< dest: 82b8abf9c185 D - test: D
763 <<<<<<< dest: 82b8abf9c185 D - test: D
764 D
764 D
765 ||||||| base
765 ||||||| base
766 A
766 A
767 =======
767 =======
768 B
768 B
769 >>>>>>> source: f899f3910ce7 B - test: B
769 >>>>>>> source: f899f3910ce7 B - test: B
770 $ echo BC > A
770 $ echo BC > A
771 $ hg resolve -m
771 $ hg resolve -m
772 (no more unresolved files)
772 (no more unresolved files)
773 continue: hg rebase --continue
773 continue: hg rebase --continue
774 $ hg rebase --continue
774 $ hg rebase --continue
775 rebasing 1:f899f3910ce7 "B" (B)
775 rebasing 1:f899f3910ce7 "B" (B)
776 rebasing 3:63668d570d21 "C" (C tip)
776 rebasing 3:63668d570d21 "C" (C tip)
777 merging A
777 merging A
778 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
778 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
779 unresolved conflicts (see hg resolve, then hg rebase --continue)
779 unresolved conflicts (see hg resolve, then hg rebase --continue)
780 [1]
780 [1]
781 $ hg tglog
781 $ hg tglog
782 @ 3: 63668d570d21 'C'
782 @ 3: 63668d570d21 'C'
783 |
783 |
784 | @ 2: 82b8abf9c185 'D'
784 | @ 2: 82b8abf9c185 'D'
785 | |
785 | |
786 o | 1: f899f3910ce7 'B'
786 o | 1: f899f3910ce7 'B'
787 |/
787 |/
788 o 0: 4a2df7238c3b 'A'
788 o 0: 4a2df7238c3b 'A'
789
789
790 $ cat A
790 $ cat A
791 <<<<<<< dest: 82b8abf9c185 D - test: D
791 <<<<<<< dest: 82b8abf9c185 D - test: D
792 BC
792 BC
793 ||||||| base
793 ||||||| base
794 B
794 B
795 =======
795 =======
796 C
796 C
797 >>>>>>> source: 63668d570d21 C tip - test: C
797 >>>>>>> source: 63668d570d21 C tip - test: C
798 $ echo BD > A
798 $ echo BD > A
799 $ hg resolve -m
799 $ hg resolve -m
800 (no more unresolved files)
800 (no more unresolved files)
801 continue: hg rebase --continue
801 continue: hg rebase --continue
802 $ HGEDITOR=false hg rebase --continue --config ui.interactive=1
802 $ HGEDITOR=false hg rebase --continue --config ui.interactive=1
803 already rebased 1:f899f3910ce7 "B" (B) as 82b8abf9c185
803 already rebased 1:f899f3910ce7 "B" (B) as 82b8abf9c185
804 rebasing 3:63668d570d21 "C" (C tip)
804 rebasing 3:63668d570d21 "C" (C tip)
805 abort: edit failed: false exited with status 1
805 abort: edit failed: false exited with status 1
806 [255]
806 [255]
807 $ hg tglog
807 $ hg tglog
808 o 3: 63668d570d21 'C'
808 o 3: 63668d570d21 'C'
809 |
809 |
810 | @ 2: 82b8abf9c185 'D'
810 | @ 2: 82b8abf9c185 'D'
811 | |
811 | |
812 o | 1: f899f3910ce7 'B'
812 o | 1: f899f3910ce7 'B'
813 |/
813 |/
814 o 0: 4a2df7238c3b 'A'
814 o 0: 4a2df7238c3b 'A'
815
815
816 BROKEN: should not result in a conflict
817 $ hg rebase --continue
816 $ hg rebase --continue
818 already rebased 1:f899f3910ce7 "B" (B) as 82b8abf9c185
817 already rebased 1:f899f3910ce7 "B" (B) as 82b8abf9c185
819 rebasing 3:63668d570d21 "C" (C tip)
818 already rebased 3:63668d570d21 "C" (C tip) as 82b8abf9c185
820 merging A
819 saved backup bundle to $TESTTMP/aborted-editor/.hg/strip-backup/f899f3910ce7-7cab5e15-rebase.hg
821 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
822 unresolved conflicts (see hg resolve, then hg rebase --continue)
823 [1]
824 $ cat A
825 <<<<<<< dest: 82b8abf9c185 D - test: D
826 BD
827 ||||||| base
828 B
829 =======
830 C
831 >>>>>>> source: 63668d570d21 C tip - test: C
@@ -1,436 +1,436
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [format]
2 > [format]
3 > usegeneraldelta=yes
3 > usegeneraldelta=yes
4 > [extensions]
4 > [extensions]
5 > rebase=
5 > rebase=
6 > drawdag=$TESTDIR/drawdag.py
6 > drawdag=$TESTDIR/drawdag.py
7 >
7 >
8 > [phases]
8 > [phases]
9 > publish=False
9 > publish=False
10 >
10 >
11 > [alias]
11 > [alias]
12 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
12 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
13 > EOF
13 > EOF
14
14
15 $ hg init a
15 $ hg init a
16 $ cd a
16 $ cd a
17 $ echo c1 >common
17 $ echo c1 >common
18 $ hg add common
18 $ hg add common
19 $ hg ci -m C1
19 $ hg ci -m C1
20
20
21 $ echo c2 >>common
21 $ echo c2 >>common
22 $ hg ci -m C2
22 $ hg ci -m C2
23
23
24 $ echo c3 >>common
24 $ echo c3 >>common
25 $ hg ci -m C3
25 $ hg ci -m C3
26
26
27 $ hg up -q -C 1
27 $ hg up -q -C 1
28
28
29 $ echo l1 >>extra
29 $ echo l1 >>extra
30 $ hg add extra
30 $ hg add extra
31 $ hg ci -m L1
31 $ hg ci -m L1
32 created new head
32 created new head
33
33
34 $ sed -e 's/c2/l2/' common > common.new
34 $ sed -e 's/c2/l2/' common > common.new
35 $ mv common.new common
35 $ mv common.new common
36 $ hg ci -m L2
36 $ hg ci -m L2
37
37
38 $ echo l3 >> extra2
38 $ echo l3 >> extra2
39 $ hg add extra2
39 $ hg add extra2
40 $ hg ci -m L3
40 $ hg ci -m L3
41 $ hg bookmark mybook
41 $ hg bookmark mybook
42
42
43 $ hg phase --force --secret 4
43 $ hg phase --force --secret 4
44
44
45 $ hg tglog
45 $ hg tglog
46 @ 5:secret 'L3' mybook
46 @ 5:secret 'L3' mybook
47 |
47 |
48 o 4:secret 'L2'
48 o 4:secret 'L2'
49 |
49 |
50 o 3:draft 'L1'
50 o 3:draft 'L1'
51 |
51 |
52 | o 2:draft 'C3'
52 | o 2:draft 'C3'
53 |/
53 |/
54 o 1:draft 'C2'
54 o 1:draft 'C2'
55 |
55 |
56 o 0:draft 'C1'
56 o 0:draft 'C1'
57
57
58 Try to call --continue:
58 Try to call --continue:
59
59
60 $ hg rebase --continue
60 $ hg rebase --continue
61 abort: no rebase in progress
61 abort: no rebase in progress
62 [255]
62 [255]
63
63
64 Conflicting rebase:
64 Conflicting rebase:
65
65
66 $ hg rebase -s 3 -d 2
66 $ hg rebase -s 3 -d 2
67 rebasing 3:3163e20567cc "L1"
67 rebasing 3:3163e20567cc "L1"
68 rebasing 4:46f0b057b5c0 "L2"
68 rebasing 4:46f0b057b5c0 "L2"
69 merging common
69 merging common
70 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
70 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
71 unresolved conflicts (see hg resolve, then hg rebase --continue)
71 unresolved conflicts (see hg resolve, then hg rebase --continue)
72 [1]
72 [1]
73
73
74 $ hg status --config commands.status.verbose=1
74 $ hg status --config commands.status.verbose=1
75 M common
75 M common
76 ? common.orig
76 ? common.orig
77 # The repository is in an unfinished *rebase* state.
77 # The repository is in an unfinished *rebase* state.
78
78
79 # Unresolved merge conflicts:
79 # Unresolved merge conflicts:
80 #
80 #
81 # common
81 # common
82 #
82 #
83 # To mark files as resolved: hg resolve --mark FILE
83 # To mark files as resolved: hg resolve --mark FILE
84
84
85 # To continue: hg rebase --continue
85 # To continue: hg rebase --continue
86 # To abort: hg rebase --abort
86 # To abort: hg rebase --abort
87
87
88
88
89 Try to continue without solving the conflict:
89 Try to continue without solving the conflict:
90
90
91 $ hg rebase --continue
91 $ hg rebase --continue
92 abort: unresolved merge conflicts (see 'hg help resolve')
92 abort: unresolved merge conflicts (see 'hg help resolve')
93 [255]
93 [255]
94
94
95 Conclude rebase:
95 Conclude rebase:
96
96
97 $ echo 'resolved merge' >common
97 $ echo 'resolved merge' >common
98 $ hg resolve -m common
98 $ hg resolve -m common
99 (no more unresolved files)
99 (no more unresolved files)
100 continue: hg rebase --continue
100 continue: hg rebase --continue
101 $ hg rebase --continue
101 $ hg rebase --continue
102 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
102 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
103 rebasing 4:46f0b057b5c0 "L2"
103 rebasing 4:46f0b057b5c0 "L2"
104 rebasing 5:8029388f38dc "L3" (mybook)
104 rebasing 5:8029388f38dc "L3" (mybook)
105 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-rebase.hg
105 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-rebase.hg
106
106
107 $ hg tglog
107 $ hg tglog
108 @ 5:secret 'L3' mybook
108 @ 5:secret 'L3' mybook
109 |
109 |
110 o 4:secret 'L2'
110 o 4:secret 'L2'
111 |
111 |
112 o 3:draft 'L1'
112 o 3:draft 'L1'
113 |
113 |
114 o 2:draft 'C3'
114 o 2:draft 'C3'
115 |
115 |
116 o 1:draft 'C2'
116 o 1:draft 'C2'
117 |
117 |
118 o 0:draft 'C1'
118 o 0:draft 'C1'
119
119
120 Check correctness:
120 Check correctness:
121
121
122 $ hg cat -r 0 common
122 $ hg cat -r 0 common
123 c1
123 c1
124
124
125 $ hg cat -r 1 common
125 $ hg cat -r 1 common
126 c1
126 c1
127 c2
127 c2
128
128
129 $ hg cat -r 2 common
129 $ hg cat -r 2 common
130 c1
130 c1
131 c2
131 c2
132 c3
132 c3
133
133
134 $ hg cat -r 3 common
134 $ hg cat -r 3 common
135 c1
135 c1
136 c2
136 c2
137 c3
137 c3
138
138
139 $ hg cat -r 4 common
139 $ hg cat -r 4 common
140 resolved merge
140 resolved merge
141
141
142 $ hg cat -r 5 common
142 $ hg cat -r 5 common
143 resolved merge
143 resolved merge
144
144
145 Bookmark stays active after --continue
145 Bookmark stays active after --continue
146 $ hg bookmarks
146 $ hg bookmarks
147 * mybook 5:d67b21408fc0
147 * mybook 5:d67b21408fc0
148
148
149 $ cd ..
149 $ cd ..
150
150
151 Check that the right ancestors is used while rebasing a merge (issue4041)
151 Check that the right ancestors is used while rebasing a merge (issue4041)
152
152
153 $ hg clone "$TESTDIR/bundles/issue4041.hg" issue4041
153 $ hg clone "$TESTDIR/bundles/issue4041.hg" issue4041
154 requesting all changes
154 requesting all changes
155 adding changesets
155 adding changesets
156 adding manifests
156 adding manifests
157 adding file changes
157 adding file changes
158 added 11 changesets with 8 changes to 3 files (+1 heads)
158 added 11 changesets with 8 changes to 3 files (+1 heads)
159 new changesets 24797d4f68de:2f2496ddf49d
159 new changesets 24797d4f68de:2f2496ddf49d
160 updating to branch default
160 updating to branch default
161 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
161 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 $ cd issue4041
162 $ cd issue4041
163 $ hg log -G
163 $ hg log -G
164 o changeset: 10:2f2496ddf49d
164 o changeset: 10:2f2496ddf49d
165 |\ branch: f1
165 |\ branch: f1
166 | | tag: tip
166 | | tag: tip
167 | | parent: 7:4c9fbe56a16f
167 | | parent: 7:4c9fbe56a16f
168 | | parent: 9:e31216eec445
168 | | parent: 9:e31216eec445
169 | | user: szhang
169 | | user: szhang
170 | | date: Thu Sep 05 12:59:39 2013 -0400
170 | | date: Thu Sep 05 12:59:39 2013 -0400
171 | | summary: merge
171 | | summary: merge
172 | |
172 | |
173 | o changeset: 9:e31216eec445
173 | o changeset: 9:e31216eec445
174 | | branch: f1
174 | | branch: f1
175 | | user: szhang
175 | | user: szhang
176 | | date: Thu Sep 05 12:59:10 2013 -0400
176 | | date: Thu Sep 05 12:59:10 2013 -0400
177 | | summary: more changes to f1
177 | | summary: more changes to f1
178 | |
178 | |
179 | o changeset: 8:8e4e2c1a07ae
179 | o changeset: 8:8e4e2c1a07ae
180 | |\ branch: f1
180 | |\ branch: f1
181 | | | parent: 2:4bc80088dc6b
181 | | | parent: 2:4bc80088dc6b
182 | | | parent: 6:400110238667
182 | | | parent: 6:400110238667
183 | | | user: szhang
183 | | | user: szhang
184 | | | date: Thu Sep 05 12:57:59 2013 -0400
184 | | | date: Thu Sep 05 12:57:59 2013 -0400
185 | | | summary: bad merge
185 | | | summary: bad merge
186 | | |
186 | | |
187 o | | changeset: 7:4c9fbe56a16f
187 o | | changeset: 7:4c9fbe56a16f
188 |/ / branch: f1
188 |/ / branch: f1
189 | | parent: 2:4bc80088dc6b
189 | | parent: 2:4bc80088dc6b
190 | | user: szhang
190 | | user: szhang
191 | | date: Thu Sep 05 12:54:00 2013 -0400
191 | | date: Thu Sep 05 12:54:00 2013 -0400
192 | | summary: changed f1
192 | | summary: changed f1
193 | |
193 | |
194 | o changeset: 6:400110238667
194 | o changeset: 6:400110238667
195 | | branch: f2
195 | | branch: f2
196 | | parent: 4:12e8ec6bb010
196 | | parent: 4:12e8ec6bb010
197 | | user: szhang
197 | | user: szhang
198 | | date: Tue Sep 03 13:58:02 2013 -0400
198 | | date: Tue Sep 03 13:58:02 2013 -0400
199 | | summary: changed f2 on f2
199 | | summary: changed f2 on f2
200 | |
200 | |
201 | | @ changeset: 5:d79e2059b5c0
201 | | @ changeset: 5:d79e2059b5c0
202 | | | parent: 3:8a951942e016
202 | | | parent: 3:8a951942e016
203 | | | user: szhang
203 | | | user: szhang
204 | | | date: Tue Sep 03 13:57:39 2013 -0400
204 | | | date: Tue Sep 03 13:57:39 2013 -0400
205 | | | summary: changed f2 on default
205 | | | summary: changed f2 on default
206 | | |
206 | | |
207 | o | changeset: 4:12e8ec6bb010
207 | o | changeset: 4:12e8ec6bb010
208 | |/ branch: f2
208 | |/ branch: f2
209 | | user: szhang
209 | | user: szhang
210 | | date: Tue Sep 03 13:57:18 2013 -0400
210 | | date: Tue Sep 03 13:57:18 2013 -0400
211 | | summary: created f2 branch
211 | | summary: created f2 branch
212 | |
212 | |
213 | o changeset: 3:8a951942e016
213 | o changeset: 3:8a951942e016
214 | | parent: 0:24797d4f68de
214 | | parent: 0:24797d4f68de
215 | | user: szhang
215 | | user: szhang
216 | | date: Tue Sep 03 13:57:11 2013 -0400
216 | | date: Tue Sep 03 13:57:11 2013 -0400
217 | | summary: added f2.txt
217 | | summary: added f2.txt
218 | |
218 | |
219 o | changeset: 2:4bc80088dc6b
219 o | changeset: 2:4bc80088dc6b
220 | | branch: f1
220 | | branch: f1
221 | | user: szhang
221 | | user: szhang
222 | | date: Tue Sep 03 13:56:20 2013 -0400
222 | | date: Tue Sep 03 13:56:20 2013 -0400
223 | | summary: added f1.txt
223 | | summary: added f1.txt
224 | |
224 | |
225 o | changeset: 1:ef53c9e6b608
225 o | changeset: 1:ef53c9e6b608
226 |/ branch: f1
226 |/ branch: f1
227 | user: szhang
227 | user: szhang
228 | date: Tue Sep 03 13:55:26 2013 -0400
228 | date: Tue Sep 03 13:55:26 2013 -0400
229 | summary: created f1 branch
229 | summary: created f1 branch
230 |
230 |
231 o changeset: 0:24797d4f68de
231 o changeset: 0:24797d4f68de
232 user: szhang
232 user: szhang
233 date: Tue Sep 03 13:55:08 2013 -0400
233 date: Tue Sep 03 13:55:08 2013 -0400
234 summary: added default.txt
234 summary: added default.txt
235
235
236 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
236 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
237 rebase onto 4bc80088dc6b starting from e31216eec445
237 rebase onto 4bc80088dc6b starting from e31216eec445
238 rebasing on disk
238 rebasing on disk
239 rebase status stored
239 rebase status stored
240 rebasing 9:e31216eec445 "more changes to f1"
240 rebasing 9:e31216eec445 "more changes to f1"
241 future parents are 2 and -1
241 future parents are 2 and -1
242 rebase status stored
243 update to 2:4bc80088dc6b
242 update to 2:4bc80088dc6b
244 resolving manifests
243 resolving manifests
245 branchmerge: False, force: True, partial: False
244 branchmerge: False, force: True, partial: False
246 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
245 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
247 f2.txt: other deleted -> r
246 f2.txt: other deleted -> r
248 removing f2.txt
247 removing f2.txt
249 f1.txt: remote created -> g
248 f1.txt: remote created -> g
250 getting f1.txt
249 getting f1.txt
251 merge against 9:e31216eec445
250 merge against 9:e31216eec445
252 detach base 8:8e4e2c1a07ae
251 detach base 8:8e4e2c1a07ae
253 searching for copies back to rev 3
252 searching for copies back to rev 3
254 unmatched files in other (from topological common ancestor):
253 unmatched files in other (from topological common ancestor):
255 f2.txt
254 f2.txt
256 resolving manifests
255 resolving manifests
257 branchmerge: True, force: True, partial: False
256 branchmerge: True, force: True, partial: False
258 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
257 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
259 f1.txt: remote is newer -> g
258 f1.txt: remote is newer -> g
260 getting f1.txt
259 getting f1.txt
261 committing files:
260 committing files:
262 f1.txt
261 f1.txt
263 committing manifest
262 committing manifest
264 committing changelog
263 committing changelog
265 updating the branch cache
264 updating the branch cache
266 rebased as 19c888675e13
265 rebased as 19c888675e13
266 rebase status stored
267 rebasing 10:2f2496ddf49d "merge" (tip)
267 rebasing 10:2f2496ddf49d "merge" (tip)
268 future parents are 11 and 7
268 future parents are 11 and 7
269 rebase status stored
270 already in destination
269 already in destination
271 merge against 10:2f2496ddf49d
270 merge against 10:2f2496ddf49d
272 detach base 9:e31216eec445
271 detach base 9:e31216eec445
273 searching for copies back to rev 3
272 searching for copies back to rev 3
274 unmatched files in other (from topological common ancestor):
273 unmatched files in other (from topological common ancestor):
275 f2.txt
274 f2.txt
276 resolving manifests
275 resolving manifests
277 branchmerge: True, force: True, partial: False
276 branchmerge: True, force: True, partial: False
278 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
277 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
279 f1.txt: remote is newer -> g
278 f1.txt: remote is newer -> g
280 getting f1.txt
279 getting f1.txt
281 committing files:
280 committing files:
282 f1.txt
281 f1.txt
283 committing manifest
282 committing manifest
284 committing changelog
283 committing changelog
285 updating the branch cache
284 updating the branch cache
286 rebased as 2a7f09cac94c
285 rebased as 2a7f09cac94c
286 rebase status stored
287 rebase merging completed
287 rebase merging completed
288 update back to initial working directory parent
288 update back to initial working directory parent
289 resolving manifests
289 resolving manifests
290 branchmerge: False, force: False, partial: False
290 branchmerge: False, force: False, partial: False
291 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
291 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
292 f1.txt: other deleted -> r
292 f1.txt: other deleted -> r
293 removing f1.txt
293 removing f1.txt
294 f2.txt: remote created -> g
294 f2.txt: remote created -> g
295 getting f2.txt
295 getting f2.txt
296 2 changesets found
296 2 changesets found
297 list of changesets:
297 list of changesets:
298 e31216eec445e44352c5f01588856059466a24c9
298 e31216eec445e44352c5f01588856059466a24c9
299 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
299 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
300 bundle2-output-bundle: "HG20", (1 params) 3 parts total
300 bundle2-output-bundle: "HG20", (1 params) 3 parts total
301 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
301 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
302 bundle2-output-part: "cache:rev-branch-cache" streamed payload
302 bundle2-output-part: "cache:rev-branch-cache" streamed payload
303 bundle2-output-part: "phase-heads" 24 bytes payload
303 bundle2-output-part: "phase-heads" 24 bytes payload
304 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-rebase.hg
304 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-rebase.hg
305 3 changesets found
305 3 changesets found
306 list of changesets:
306 list of changesets:
307 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
307 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
308 19c888675e133ab5dff84516926a65672eaf04d9
308 19c888675e133ab5dff84516926a65672eaf04d9
309 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
309 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
310 bundle2-output-bundle: "HG20", 3 parts total
310 bundle2-output-bundle: "HG20", 3 parts total
311 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
311 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
312 bundle2-output-part: "cache:rev-branch-cache" streamed payload
312 bundle2-output-part: "cache:rev-branch-cache" streamed payload
313 bundle2-output-part: "phase-heads" 24 bytes payload
313 bundle2-output-part: "phase-heads" 24 bytes payload
314 adding branch
314 adding branch
315 bundle2-input-bundle: with-transaction
315 bundle2-input-bundle: with-transaction
316 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
316 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
317 adding changesets
317 adding changesets
318 add changeset 4c9fbe56a16f
318 add changeset 4c9fbe56a16f
319 add changeset 19c888675e13
319 add changeset 19c888675e13
320 add changeset 2a7f09cac94c
320 add changeset 2a7f09cac94c
321 adding manifests
321 adding manifests
322 adding file changes
322 adding file changes
323 adding f1.txt revisions
323 adding f1.txt revisions
324 added 2 changesets with 2 changes to 1 files
324 added 2 changesets with 2 changes to 1 files
325 bundle2-input-part: total payload size 1686
325 bundle2-input-part: total payload size 1686
326 bundle2-input-part: "cache:rev-branch-cache" supported
326 bundle2-input-part: "cache:rev-branch-cache" supported
327 bundle2-input-part: total payload size 74
327 bundle2-input-part: total payload size 74
328 truncating cache/rbc-revs-v1 to 56
328 truncating cache/rbc-revs-v1 to 56
329 bundle2-input-part: "phase-heads" supported
329 bundle2-input-part: "phase-heads" supported
330 bundle2-input-part: total payload size 24
330 bundle2-input-part: total payload size 24
331 bundle2-input-bundle: 2 parts total
331 bundle2-input-bundle: 2 parts total
332 updating the branch cache
332 updating the branch cache
333 invalid branchheads cache (served): tip differs
333 invalid branchheads cache (served): tip differs
334 rebase completed
334 rebase completed
335
335
336 Test minimization of merge conflicts
336 Test minimization of merge conflicts
337 $ hg up -q null
337 $ hg up -q null
338 $ echo a > a
338 $ echo a > a
339 $ hg add a
339 $ hg add a
340 $ hg commit -q -m 'a'
340 $ hg commit -q -m 'a'
341 $ echo b >> a
341 $ echo b >> a
342 $ hg commit -q -m 'ab'
342 $ hg commit -q -m 'ab'
343 $ hg bookmark ab
343 $ hg bookmark ab
344 $ hg up -q '.^'
344 $ hg up -q '.^'
345 $ echo b >> a
345 $ echo b >> a
346 $ echo c >> a
346 $ echo c >> a
347 $ hg commit -q -m 'abc'
347 $ hg commit -q -m 'abc'
348 $ hg rebase -s 7bc217434fc1 -d ab --keep
348 $ hg rebase -s 7bc217434fc1 -d ab --keep
349 rebasing 13:7bc217434fc1 "abc" (tip)
349 rebasing 13:7bc217434fc1 "abc" (tip)
350 merging a
350 merging a
351 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
351 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
352 unresolved conflicts (see hg resolve, then hg rebase --continue)
352 unresolved conflicts (see hg resolve, then hg rebase --continue)
353 [1]
353 [1]
354 $ hg diff
354 $ hg diff
355 diff -r 328e4ab1f7cc a
355 diff -r 328e4ab1f7cc a
356 --- a/a Thu Jan 01 00:00:00 1970 +0000
356 --- a/a Thu Jan 01 00:00:00 1970 +0000
357 +++ b/a * (glob)
357 +++ b/a * (glob)
358 @@ -1,2 +1,6 @@
358 @@ -1,2 +1,6 @@
359 a
359 a
360 b
360 b
361 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
361 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
362 +=======
362 +=======
363 +c
363 +c
364 +>>>>>>> source: 7bc217434fc1 - test: abc
364 +>>>>>>> source: 7bc217434fc1 - test: abc
365 $ hg rebase --abort
365 $ hg rebase --abort
366 rebase aborted
366 rebase aborted
367 $ hg up -q -C 7bc217434fc1
367 $ hg up -q -C 7bc217434fc1
368 $ hg rebase -s . -d ab --keep -t internal:merge3
368 $ hg rebase -s . -d ab --keep -t internal:merge3
369 rebasing 13:7bc217434fc1 "abc" (tip)
369 rebasing 13:7bc217434fc1 "abc" (tip)
370 merging a
370 merging a
371 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
371 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
372 unresolved conflicts (see hg resolve, then hg rebase --continue)
372 unresolved conflicts (see hg resolve, then hg rebase --continue)
373 [1]
373 [1]
374 $ hg diff
374 $ hg diff
375 diff -r 328e4ab1f7cc a
375 diff -r 328e4ab1f7cc a
376 --- a/a Thu Jan 01 00:00:00 1970 +0000
376 --- a/a Thu Jan 01 00:00:00 1970 +0000
377 +++ b/a * (glob)
377 +++ b/a * (glob)
378 @@ -1,2 +1,8 @@
378 @@ -1,2 +1,8 @@
379 a
379 a
380 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
380 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
381 b
381 b
382 +||||||| base
382 +||||||| base
383 +=======
383 +=======
384 +b
384 +b
385 +c
385 +c
386 +>>>>>>> source: 7bc217434fc1 - test: abc
386 +>>>>>>> source: 7bc217434fc1 - test: abc
387
387
388 Test rebase with obsstore turned on and off (issue5606)
388 Test rebase with obsstore turned on and off (issue5606)
389
389
390 $ cd $TESTTMP
390 $ cd $TESTTMP
391 $ hg init b
391 $ hg init b
392 $ cd b
392 $ cd b
393 $ hg debugdrawdag <<'EOS'
393 $ hg debugdrawdag <<'EOS'
394 > D
394 > D
395 > |
395 > |
396 > C
396 > C
397 > |
397 > |
398 > B E
398 > B E
399 > |/
399 > |/
400 > A
400 > A
401 > EOS
401 > EOS
402
402
403 $ hg update E -q
403 $ hg update E -q
404 $ echo 3 > B
404 $ echo 3 > B
405 $ hg commit --amend -m E -A B -q
405 $ hg commit --amend -m E -A B -q
406 $ hg rebase -r B+D -d . --config experimental.evolution=true
406 $ hg rebase -r B+D -d . --config experimental.evolution=true
407 rebasing 1:112478962961 "B" (B)
407 rebasing 1:112478962961 "B" (B)
408 merging B
408 merging B
409 warning: conflicts while merging B! (edit, then use 'hg resolve --mark')
409 warning: conflicts while merging B! (edit, then use 'hg resolve --mark')
410 unresolved conflicts (see hg resolve, then hg rebase --continue)
410 unresolved conflicts (see hg resolve, then hg rebase --continue)
411 [1]
411 [1]
412
412
413 $ echo 4 > B
413 $ echo 4 > B
414 $ hg resolve -m
414 $ hg resolve -m
415 (no more unresolved files)
415 (no more unresolved files)
416 continue: hg rebase --continue
416 continue: hg rebase --continue
417 $ hg rebase --continue --config experimental.evolution=none
417 $ hg rebase --continue --config experimental.evolution=none
418 rebasing 1:112478962961 "B" (B)
418 rebasing 1:112478962961 "B" (B)
419 rebasing 3:f585351a92f8 "D" (D)
419 rebasing 3:f585351a92f8 "D" (D)
420 warning: orphaned descendants detected, not stripping 112478962961
420 warning: orphaned descendants detected, not stripping 112478962961
421 saved backup bundle to $TESTTMP/b/.hg/strip-backup/f585351a92f8-e536a9e4-rebase.hg
421 saved backup bundle to $TESTTMP/b/.hg/strip-backup/f585351a92f8-e536a9e4-rebase.hg
422
422
423 $ rm .hg/localtags
423 $ rm .hg/localtags
424 $ hg tglog
424 $ hg tglog
425 o 5:draft 'D'
425 o 5:draft 'D'
426 |
426 |
427 o 4:draft 'B'
427 o 4:draft 'B'
428 |
428 |
429 @ 3:draft 'E'
429 @ 3:draft 'E'
430 |
430 |
431 | o 2:draft 'C'
431 | o 2:draft 'C'
432 | |
432 | |
433 | o 1:draft 'B'
433 | o 1:draft 'B'
434 |/
434 |/
435 o 0:draft 'A'
435 o 0:draft 'A'
436
436
General Comments 0
You need to be logged in to leave comments. Login now