##// END OF EJS Templates
rebase: choose merge base without unwanted revisions...
Jun Wu -
r33863:3160876c default
parent child Browse files
Show More
@@ -1,1530 +1,1550 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 https://mercurial-scm.org/wiki/RebaseExtension
14 https://mercurial-scm.org/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from __future__ import absolute_import
17 from __future__ import absolute_import
18
18
19 import errno
19 import errno
20 import os
20 import os
21
21
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 from mercurial.node import (
23 from mercurial.node import (
24 hex,
24 hex,
25 nullid,
25 nullid,
26 nullrev,
26 nullrev,
27 short,
27 short,
28 )
28 )
29 from mercurial import (
29 from mercurial import (
30 bookmarks,
30 bookmarks,
31 cmdutil,
31 cmdutil,
32 commands,
32 commands,
33 copies,
33 copies,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 error,
36 error,
37 extensions,
37 extensions,
38 hg,
38 hg,
39 lock,
39 lock,
40 merge as mergemod,
40 merge as mergemod,
41 mergeutil,
41 mergeutil,
42 obsolete,
42 obsolete,
43 obsutil,
43 obsutil,
44 patch,
44 patch,
45 phases,
45 phases,
46 registrar,
46 registrar,
47 repair,
47 repair,
48 repoview,
48 repoview,
49 revset,
49 revset,
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 templateopts = cmdutil.templateopts
56 templateopts = cmdutil.templateopts
57
57
58 # The following constants are used throughout the rebase module. The ordering of
58 # The following constants are used throughout the rebase module. The ordering of
59 # their values must be maintained.
59 # their values must be maintained.
60
60
61 # Indicates that a revision needs to be rebased
61 # Indicates that a revision needs to be rebased
62 revtodo = -1
62 revtodo = -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 = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
128 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
129 if names:
129 if names:
130 desc += ' (%s)' % ' '.join(names)
130 desc += ' (%s)' % ' '.join(names)
131 return desc
131 return desc
132
132
133 class rebaseruntime(object):
133 class rebaseruntime(object):
134 """This class is a container for rebase runtime state"""
134 """This class is a container for rebase runtime state"""
135 def __init__(self, repo, ui, opts=None):
135 def __init__(self, repo, ui, opts=None):
136 if opts is None:
136 if opts is None:
137 opts = {}
137 opts = {}
138
138
139 self.repo = repo
139 self.repo = repo
140 self.ui = ui
140 self.ui = ui
141 self.opts = opts
141 self.opts = opts
142 self.originalwd = None
142 self.originalwd = None
143 self.external = nullrev
143 self.external = nullrev
144 # Mapping between the old revision id and either what is the new rebased
144 # Mapping between the old revision id and either what is the new rebased
145 # revision or what needs to be done with the old revision. The state
145 # revision or what needs to be done with the old revision. The state
146 # dict will be what contains most of the rebase progress state.
146 # dict will be what contains most of the rebase progress state.
147 self.state = {}
147 self.state = {}
148 self.activebookmark = None
148 self.activebookmark = None
149 self.dest = None
149 self.dest = None
150 self.skipped = set()
150 self.skipped = set()
151
151
152 self.collapsef = opts.get('collapse', False)
152 self.collapsef = opts.get('collapse', False)
153 self.collapsemsg = cmdutil.logmessage(ui, opts)
153 self.collapsemsg = cmdutil.logmessage(ui, opts)
154 self.date = opts.get('date', None)
154 self.date = opts.get('date', None)
155
155
156 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
156 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
157 self.extrafns = [_savegraft]
157 self.extrafns = [_savegraft]
158 if e:
158 if e:
159 self.extrafns = [e]
159 self.extrafns = [e]
160
160
161 self.keepf = opts.get('keep', False)
161 self.keepf = opts.get('keep', False)
162 self.keepbranchesf = opts.get('keepbranches', False)
162 self.keepbranchesf = opts.get('keepbranches', False)
163 # keepopen is not meant for use on the command line, but by
163 # keepopen is not meant for use on the command line, but by
164 # other extensions
164 # other extensions
165 self.keepopen = opts.get('keepopen', False)
165 self.keepopen = opts.get('keepopen', False)
166 self.obsoletenotrebased = {}
166 self.obsoletenotrebased = {}
167
167
168 def storestatus(self, tr=None):
168 def storestatus(self, tr=None):
169 """Store the current status to allow recovery"""
169 """Store the current status to allow recovery"""
170 if tr:
170 if tr:
171 tr.addfilegenerator('rebasestate', ('rebasestate',),
171 tr.addfilegenerator('rebasestate', ('rebasestate',),
172 self._writestatus, location='plain')
172 self._writestatus, location='plain')
173 else:
173 else:
174 with self.repo.vfs("rebasestate", "w") as f:
174 with self.repo.vfs("rebasestate", "w") as f:
175 self._writestatus(f)
175 self._writestatus(f)
176
176
177 def _writestatus(self, f):
177 def _writestatus(self, f):
178 repo = self.repo.unfiltered()
178 repo = self.repo.unfiltered()
179 f.write(repo[self.originalwd].hex() + '\n')
179 f.write(repo[self.originalwd].hex() + '\n')
180 f.write(repo[self.dest].hex() + '\n')
180 f.write(repo[self.dest].hex() + '\n')
181 f.write(repo[self.external].hex() + '\n')
181 f.write(repo[self.external].hex() + '\n')
182 f.write('%d\n' % int(self.collapsef))
182 f.write('%d\n' % int(self.collapsef))
183 f.write('%d\n' % int(self.keepf))
183 f.write('%d\n' % int(self.keepf))
184 f.write('%d\n' % int(self.keepbranchesf))
184 f.write('%d\n' % int(self.keepbranchesf))
185 f.write('%s\n' % (self.activebookmark or ''))
185 f.write('%s\n' % (self.activebookmark or ''))
186 for d, v in self.state.iteritems():
186 for d, v in self.state.iteritems():
187 oldrev = repo[d].hex()
187 oldrev = repo[d].hex()
188 if v >= 0:
188 if v >= 0:
189 newrev = repo[v].hex()
189 newrev = repo[v].hex()
190 elif v == revtodo:
190 elif v == revtodo:
191 # To maintain format compatibility, we have to use nullid.
191 # To maintain format compatibility, we have to use nullid.
192 # Please do remove this special case when upgrading the format.
192 # Please do remove this special case when upgrading the format.
193 newrev = hex(nullid)
193 newrev = hex(nullid)
194 else:
194 else:
195 newrev = v
195 newrev = v
196 f.write("%s:%s\n" % (oldrev, newrev))
196 f.write("%s:%s\n" % (oldrev, newrev))
197 repo.ui.debug('rebase status stored\n')
197 repo.ui.debug('rebase status stored\n')
198
198
199 def restorestatus(self):
199 def restorestatus(self):
200 """Restore a previously stored status"""
200 """Restore a previously stored status"""
201 repo = self.repo
201 repo = self.repo
202 keepbranches = None
202 keepbranches = None
203 dest = None
203 dest = None
204 collapse = False
204 collapse = False
205 external = nullrev
205 external = nullrev
206 activebookmark = None
206 activebookmark = None
207 state = {}
207 state = {}
208
208
209 try:
209 try:
210 f = repo.vfs("rebasestate")
210 f = repo.vfs("rebasestate")
211 for i, l in enumerate(f.read().splitlines()):
211 for i, l in enumerate(f.read().splitlines()):
212 if i == 0:
212 if i == 0:
213 originalwd = repo[l].rev()
213 originalwd = repo[l].rev()
214 elif i == 1:
214 elif i == 1:
215 dest = repo[l].rev()
215 dest = repo[l].rev()
216 elif i == 2:
216 elif i == 2:
217 external = repo[l].rev()
217 external = repo[l].rev()
218 elif i == 3:
218 elif i == 3:
219 collapse = bool(int(l))
219 collapse = bool(int(l))
220 elif i == 4:
220 elif i == 4:
221 keep = bool(int(l))
221 keep = bool(int(l))
222 elif i == 5:
222 elif i == 5:
223 keepbranches = bool(int(l))
223 keepbranches = bool(int(l))
224 elif i == 6 and not (len(l) == 81 and ':' in l):
224 elif i == 6 and not (len(l) == 81 and ':' in l):
225 # line 6 is a recent addition, so for backwards
225 # line 6 is a recent addition, so for backwards
226 # compatibility check that the line doesn't look like the
226 # compatibility check that the line doesn't look like the
227 # oldrev:newrev lines
227 # oldrev:newrev lines
228 activebookmark = l
228 activebookmark = l
229 else:
229 else:
230 oldrev, newrev = l.split(':')
230 oldrev, newrev = l.split(':')
231 if newrev in legacystates:
231 if newrev in legacystates:
232 continue
232 continue
233 elif newrev == nullid:
233 elif newrev == nullid:
234 state[repo[oldrev].rev()] = revtodo
234 state[repo[oldrev].rev()] = revtodo
235 # Legacy compat special case
235 # Legacy compat special case
236 else:
236 else:
237 state[repo[oldrev].rev()] = repo[newrev].rev()
237 state[repo[oldrev].rev()] = repo[newrev].rev()
238
238
239 except IOError as err:
239 except IOError as err:
240 if err.errno != errno.ENOENT:
240 if err.errno != errno.ENOENT:
241 raise
241 raise
242 cmdutil.wrongtooltocontinue(repo, _('rebase'))
242 cmdutil.wrongtooltocontinue(repo, _('rebase'))
243
243
244 if keepbranches is None:
244 if keepbranches is None:
245 raise error.Abort(_('.hg/rebasestate is incomplete'))
245 raise error.Abort(_('.hg/rebasestate is incomplete'))
246
246
247 skipped = set()
247 skipped = set()
248 # recompute the set of skipped revs
248 # recompute the set of skipped revs
249 if not collapse:
249 if not collapse:
250 seen = {dest}
250 seen = {dest}
251 for old, new in sorted(state.items()):
251 for old, new in sorted(state.items()):
252 if new != revtodo and new in seen:
252 if new != revtodo and new in seen:
253 skipped.add(old)
253 skipped.add(old)
254 seen.add(new)
254 seen.add(new)
255 repo.ui.debug('computed skipped revs: %s\n' %
255 repo.ui.debug('computed skipped revs: %s\n' %
256 (' '.join(str(r) for r in sorted(skipped)) or None))
256 (' '.join(str(r) for r in sorted(skipped)) or None))
257 repo.ui.debug('rebase status resumed\n')
257 repo.ui.debug('rebase status resumed\n')
258 _setrebasesetvisibility(repo, set(state.keys()) | {originalwd})
258 _setrebasesetvisibility(repo, set(state.keys()) | {originalwd})
259
259
260 self.originalwd = originalwd
260 self.originalwd = originalwd
261 self.dest = dest
261 self.dest = dest
262 self.state = state
262 self.state = state
263 self.skipped = skipped
263 self.skipped = skipped
264 self.collapsef = collapse
264 self.collapsef = collapse
265 self.keepf = keep
265 self.keepf = keep
266 self.keepbranchesf = keepbranches
266 self.keepbranchesf = keepbranches
267 self.external = external
267 self.external = external
268 self.activebookmark = activebookmark
268 self.activebookmark = activebookmark
269
269
270 def _handleskippingobsolete(self, rebaserevs, obsoleterevs, dest):
270 def _handleskippingobsolete(self, rebaserevs, obsoleterevs, dest):
271 """Compute structures necessary for skipping obsolete revisions
271 """Compute structures necessary for skipping obsolete revisions
272
272
273 rebaserevs: iterable of all revisions that are to be rebased
273 rebaserevs: iterable of all revisions that are to be rebased
274 obsoleterevs: iterable of all obsolete revisions in rebaseset
274 obsoleterevs: iterable of all obsolete revisions in rebaseset
275 dest: a destination revision for the rebase operation
275 dest: a destination revision for the rebase operation
276 """
276 """
277 self.obsoletenotrebased = {}
277 self.obsoletenotrebased = {}
278 if not self.ui.configbool('experimental', 'rebaseskipobsolete',
278 if not self.ui.configbool('experimental', 'rebaseskipobsolete',
279 default=True):
279 default=True):
280 return
280 return
281 obsoleteset = set(obsoleterevs)
281 obsoleteset = set(obsoleterevs)
282 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
282 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
283 obsoleteset, dest)
283 obsoleteset, dest)
284 skippedset = set(self.obsoletenotrebased)
284 skippedset = set(self.obsoletenotrebased)
285 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
285 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
286
286
287 def _prepareabortorcontinue(self, isabort):
287 def _prepareabortorcontinue(self, isabort):
288 try:
288 try:
289 self.restorestatus()
289 self.restorestatus()
290 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
290 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
291 except error.RepoLookupError:
291 except error.RepoLookupError:
292 if isabort:
292 if isabort:
293 clearstatus(self.repo)
293 clearstatus(self.repo)
294 clearcollapsemsg(self.repo)
294 clearcollapsemsg(self.repo)
295 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
295 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
296 ' only broken state is cleared)\n'))
296 ' only broken state is cleared)\n'))
297 return 0
297 return 0
298 else:
298 else:
299 msg = _('cannot continue inconsistent rebase')
299 msg = _('cannot continue inconsistent rebase')
300 hint = _('use "hg rebase --abort" to clear broken state')
300 hint = _('use "hg rebase --abort" to clear broken state')
301 raise error.Abort(msg, hint=hint)
301 raise error.Abort(msg, hint=hint)
302 if isabort:
302 if isabort:
303 return abort(self.repo, self.originalwd, self.dest,
303 return abort(self.repo, self.originalwd, self.dest,
304 self.state, activebookmark=self.activebookmark)
304 self.state, activebookmark=self.activebookmark)
305
305
306 def _preparenewrebase(self, dest, rebaseset):
306 def _preparenewrebase(self, dest, rebaseset):
307 if dest is None:
307 if dest is None:
308 return _nothingtorebase()
308 return _nothingtorebase()
309
309
310 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
310 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
311 if (not (self.keepf or allowunstable)
311 if (not (self.keepf or allowunstable)
312 and self.repo.revs('first(children(%ld) - %ld)',
312 and self.repo.revs('first(children(%ld) - %ld)',
313 rebaseset, rebaseset)):
313 rebaseset, rebaseset)):
314 raise error.Abort(
314 raise error.Abort(
315 _("can't remove original changesets with"
315 _("can't remove original changesets with"
316 " unrebased descendants"),
316 " unrebased descendants"),
317 hint=_('use --keep to keep original changesets'))
317 hint=_('use --keep to keep original changesets'))
318
318
319 obsrevs = _filterobsoleterevs(self.repo, set(rebaseset))
319 obsrevs = _filterobsoleterevs(self.repo, set(rebaseset))
320 self._handleskippingobsolete(rebaseset, obsrevs, dest.rev())
320 self._handleskippingobsolete(rebaseset, obsrevs, dest.rev())
321
321
322 result = buildstate(self.repo, dest, rebaseset, self.collapsef,
322 result = buildstate(self.repo, dest, rebaseset, self.collapsef,
323 self.obsoletenotrebased)
323 self.obsoletenotrebased)
324
324
325 if not result:
325 if not result:
326 # Empty state built, nothing to rebase
326 # Empty state built, nothing to rebase
327 self.ui.status(_('nothing to rebase\n'))
327 self.ui.status(_('nothing to rebase\n'))
328 return _nothingtorebase()
328 return _nothingtorebase()
329
329
330 for root in self.repo.set('roots(%ld)', rebaseset):
330 for root in self.repo.set('roots(%ld)', rebaseset):
331 if not self.keepf and not root.mutable():
331 if not self.keepf and not root.mutable():
332 raise error.Abort(_("can't rebase public changeset %s")
332 raise error.Abort(_("can't rebase public changeset %s")
333 % root,
333 % root,
334 hint=_("see 'hg help phases' for details"))
334 hint=_("see 'hg help phases' for details"))
335
335
336 (self.originalwd, self.dest, self.state) = result
336 (self.originalwd, self.dest, self.state) = result
337 if self.collapsef:
337 if self.collapsef:
338 destancestors = self.repo.changelog.ancestors([self.dest],
338 destancestors = self.repo.changelog.ancestors([self.dest],
339 inclusive=True)
339 inclusive=True)
340 self.external = externalparent(self.repo, self.state, destancestors)
340 self.external = externalparent(self.repo, self.state, destancestors)
341
341
342 if dest.closesbranch() and not self.keepbranchesf:
342 if dest.closesbranch() and not self.keepbranchesf:
343 self.ui.status(_('reopening closed branch head %s\n') % dest)
343 self.ui.status(_('reopening closed branch head %s\n') % dest)
344
344
345 def _performrebase(self, tr):
345 def _performrebase(self, tr):
346 repo, ui, opts = self.repo, self.ui, self.opts
346 repo, ui, opts = self.repo, self.ui, self.opts
347 if self.keepbranchesf:
347 if self.keepbranchesf:
348 # insert _savebranch at the start of extrafns so if
348 # insert _savebranch at the start of extrafns so if
349 # there's a user-provided extrafn it can clobber branch if
349 # there's a user-provided extrafn it can clobber branch if
350 # desired
350 # desired
351 self.extrafns.insert(0, _savebranch)
351 self.extrafns.insert(0, _savebranch)
352 if self.collapsef:
352 if self.collapsef:
353 branches = set()
353 branches = set()
354 for rev in self.state:
354 for rev in self.state:
355 branches.add(repo[rev].branch())
355 branches.add(repo[rev].branch())
356 if len(branches) > 1:
356 if len(branches) > 1:
357 raise error.Abort(_('cannot collapse multiple named '
357 raise error.Abort(_('cannot collapse multiple named '
358 'branches'))
358 'branches'))
359
359
360 # Keep track of the active bookmarks in order to reset them later
360 # Keep track of the active bookmarks in order to reset them later
361 self.activebookmark = self.activebookmark or repo._activebookmark
361 self.activebookmark = self.activebookmark or repo._activebookmark
362 if self.activebookmark:
362 if self.activebookmark:
363 bookmarks.deactivate(repo)
363 bookmarks.deactivate(repo)
364
364
365 # Store the state before we begin so users can run 'hg rebase --abort'
365 # Store the state before we begin so users can run 'hg rebase --abort'
366 # if we fail before the transaction closes.
366 # if we fail before the transaction closes.
367 self.storestatus()
367 self.storestatus()
368
368
369 sortedrevs = repo.revs('sort(%ld, -topo)', self.state)
369 sortedrevs = repo.revs('sort(%ld, -topo)', self.state)
370 cands = [k for k, v in self.state.iteritems() if v == revtodo]
370 cands = [k for k, v in self.state.iteritems() if v == revtodo]
371 total = len(cands)
371 total = len(cands)
372 pos = 0
372 pos = 0
373 for rev in sortedrevs:
373 for rev in sortedrevs:
374 ctx = repo[rev]
374 ctx = repo[rev]
375 desc = _ctxdesc(ctx)
375 desc = _ctxdesc(ctx)
376 if self.state[rev] == rev:
376 if self.state[rev] == rev:
377 ui.status(_('already rebased %s\n') % desc)
377 ui.status(_('already rebased %s\n') % desc)
378 elif self.state[rev] == revtodo:
378 elif self.state[rev] == revtodo:
379 pos += 1
379 pos += 1
380 ui.status(_('rebasing %s\n') % desc)
380 ui.status(_('rebasing %s\n') % desc)
381 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
381 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
382 _('changesets'), total)
382 _('changesets'), total)
383 p1, p2, base = defineparents(repo, rev, self.dest, self.state)
383 p1, p2, base = defineparents(repo, rev, self.dest, self.state)
384 self.storestatus(tr=tr)
384 self.storestatus(tr=tr)
385 storecollapsemsg(repo, self.collapsemsg)
385 storecollapsemsg(repo, self.collapsemsg)
386 if len(repo[None].parents()) == 2:
386 if len(repo[None].parents()) == 2:
387 repo.ui.debug('resuming interrupted rebase\n')
387 repo.ui.debug('resuming interrupted rebase\n')
388 else:
388 else:
389 try:
389 try:
390 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
390 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
391 'rebase')
391 'rebase')
392 stats = rebasenode(repo, rev, p1, base, self.state,
392 stats = rebasenode(repo, rev, p1, base, self.state,
393 self.collapsef, self.dest)
393 self.collapsef, self.dest)
394 if stats and stats[3] > 0:
394 if stats and stats[3] > 0:
395 raise error.InterventionRequired(
395 raise error.InterventionRequired(
396 _('unresolved conflicts (see hg '
396 _('unresolved conflicts (see hg '
397 'resolve, then hg rebase --continue)'))
397 'resolve, then hg rebase --continue)'))
398 finally:
398 finally:
399 ui.setconfig('ui', 'forcemerge', '', 'rebase')
399 ui.setconfig('ui', 'forcemerge', '', 'rebase')
400 if not self.collapsef:
400 if not self.collapsef:
401 merging = p2 != nullrev
401 merging = p2 != nullrev
402 editform = cmdutil.mergeeditform(merging, 'rebase')
402 editform = cmdutil.mergeeditform(merging, 'rebase')
403 editor = cmdutil.getcommiteditor(editform=editform, **opts)
403 editor = cmdutil.getcommiteditor(editform=editform, **opts)
404 newnode = concludenode(repo, rev, p1, p2,
404 newnode = concludenode(repo, rev, p1, p2,
405 extrafn=_makeextrafn(self.extrafns),
405 extrafn=_makeextrafn(self.extrafns),
406 editor=editor,
406 editor=editor,
407 keepbranches=self.keepbranchesf,
407 keepbranches=self.keepbranchesf,
408 date=self.date)
408 date=self.date)
409 if newnode is None:
409 if newnode is None:
410 # If it ended up being a no-op commit, then the normal
410 # If it ended up being a no-op commit, then the normal
411 # merge state clean-up path doesn't happen, so do it
411 # merge state clean-up path doesn't happen, so do it
412 # here. Fix issue5494
412 # here. Fix issue5494
413 mergemod.mergestate.clean(repo)
413 mergemod.mergestate.clean(repo)
414 else:
414 else:
415 # Skip commit if we are collapsing
415 # Skip commit if we are collapsing
416 repo.setparents(repo[p1].node())
416 repo.setparents(repo[p1].node())
417 newnode = None
417 newnode = None
418 # Update the state
418 # Update the state
419 if newnode is not None:
419 if newnode is not None:
420 self.state[rev] = repo[newnode].rev()
420 self.state[rev] = repo[newnode].rev()
421 ui.debug('rebased as %s\n' % short(newnode))
421 ui.debug('rebased as %s\n' % short(newnode))
422 else:
422 else:
423 if not self.collapsef:
423 if not self.collapsef:
424 ui.warn(_('note: rebase of %d:%s created no changes '
424 ui.warn(_('note: rebase of %d:%s created no changes '
425 'to commit\n') % (rev, ctx))
425 'to commit\n') % (rev, ctx))
426 self.skipped.add(rev)
426 self.skipped.add(rev)
427 self.state[rev] = p1
427 self.state[rev] = p1
428 ui.debug('next revision set to %s\n' % p1)
428 ui.debug('next revision set to %s\n' % p1)
429 else:
429 else:
430 ui.status(_('already rebased %s as %s\n') %
430 ui.status(_('already rebased %s as %s\n') %
431 (desc, repo[self.state[rev]]))
431 (desc, repo[self.state[rev]]))
432
432
433 ui.progress(_('rebasing'), None)
433 ui.progress(_('rebasing'), None)
434 ui.note(_('rebase merging completed\n'))
434 ui.note(_('rebase merging completed\n'))
435
435
436 def _finishrebase(self):
436 def _finishrebase(self):
437 repo, ui, opts = self.repo, self.ui, self.opts
437 repo, ui, opts = self.repo, self.ui, self.opts
438 if self.collapsef and not self.keepopen:
438 if self.collapsef and not self.keepopen:
439 p1, p2, _base = defineparents(repo, min(self.state),
439 p1, p2, _base = defineparents(repo, min(self.state),
440 self.dest, self.state)
440 self.dest, self.state)
441 editopt = opts.get('edit')
441 editopt = opts.get('edit')
442 editform = 'rebase.collapse'
442 editform = 'rebase.collapse'
443 if self.collapsemsg:
443 if self.collapsemsg:
444 commitmsg = self.collapsemsg
444 commitmsg = self.collapsemsg
445 else:
445 else:
446 commitmsg = 'Collapsed revision'
446 commitmsg = 'Collapsed revision'
447 for rebased in sorted(self.state):
447 for rebased in sorted(self.state):
448 if rebased not in self.skipped:
448 if rebased not in self.skipped:
449 commitmsg += '\n* %s' % repo[rebased].description()
449 commitmsg += '\n* %s' % repo[rebased].description()
450 editopt = True
450 editopt = True
451 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
451 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
452 revtoreuse = max(self.state)
452 revtoreuse = max(self.state)
453
453
454 dsguard = None
454 dsguard = None
455 if ui.configbool('rebase', 'singletransaction'):
455 if ui.configbool('rebase', 'singletransaction'):
456 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
456 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
457 with util.acceptintervention(dsguard):
457 with util.acceptintervention(dsguard):
458 newnode = concludenode(repo, revtoreuse, p1, self.external,
458 newnode = concludenode(repo, revtoreuse, p1, self.external,
459 commitmsg=commitmsg,
459 commitmsg=commitmsg,
460 extrafn=_makeextrafn(self.extrafns),
460 extrafn=_makeextrafn(self.extrafns),
461 editor=editor,
461 editor=editor,
462 keepbranches=self.keepbranchesf,
462 keepbranches=self.keepbranchesf,
463 date=self.date)
463 date=self.date)
464 if newnode is None:
464 if newnode is None:
465 newrev = self.dest
465 newrev = self.dest
466 else:
466 else:
467 newrev = repo[newnode].rev()
467 newrev = repo[newnode].rev()
468 for oldrev in self.state.iterkeys():
468 for oldrev in self.state.iterkeys():
469 self.state[oldrev] = newrev
469 self.state[oldrev] = newrev
470
470
471 if 'qtip' in repo.tags():
471 if 'qtip' in repo.tags():
472 updatemq(repo, self.state, self.skipped, **opts)
472 updatemq(repo, self.state, self.skipped, **opts)
473
473
474 # restore original working directory
474 # restore original working directory
475 # (we do this before stripping)
475 # (we do this before stripping)
476 newwd = self.state.get(self.originalwd, self.originalwd)
476 newwd = self.state.get(self.originalwd, self.originalwd)
477 if newwd < 0:
477 if newwd < 0:
478 # original directory is a parent of rebase set root or ignored
478 # original directory is a parent of rebase set root or ignored
479 newwd = self.originalwd
479 newwd = self.originalwd
480 if newwd not in [c.rev() for c in repo[None].parents()]:
480 if newwd not in [c.rev() for c in repo[None].parents()]:
481 ui.note(_("update back to initial working directory parent\n"))
481 ui.note(_("update back to initial working directory parent\n"))
482 hg.updaterepo(repo, newwd, False)
482 hg.updaterepo(repo, newwd, False)
483
483
484 if not self.keepf:
484 if not self.keepf:
485 collapsedas = None
485 collapsedas = None
486 if self.collapsef:
486 if self.collapsef:
487 collapsedas = newnode
487 collapsedas = newnode
488 clearrebased(ui, repo, self.dest, self.state, self.skipped,
488 clearrebased(ui, repo, self.dest, self.state, self.skipped,
489 collapsedas)
489 collapsedas)
490
490
491 clearstatus(repo)
491 clearstatus(repo)
492 clearcollapsemsg(repo)
492 clearcollapsemsg(repo)
493
493
494 ui.note(_("rebase completed\n"))
494 ui.note(_("rebase completed\n"))
495 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
495 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
496 if self.skipped:
496 if self.skipped:
497 skippedlen = len(self.skipped)
497 skippedlen = len(self.skipped)
498 ui.note(_("%d revisions have been skipped\n") % skippedlen)
498 ui.note(_("%d revisions have been skipped\n") % skippedlen)
499
499
500 if (self.activebookmark and self.activebookmark in repo._bookmarks and
500 if (self.activebookmark and self.activebookmark in repo._bookmarks and
501 repo['.'].node() == repo._bookmarks[self.activebookmark]):
501 repo['.'].node() == repo._bookmarks[self.activebookmark]):
502 bookmarks.activate(repo, self.activebookmark)
502 bookmarks.activate(repo, self.activebookmark)
503
503
504 @command('rebase',
504 @command('rebase',
505 [('s', 'source', '',
505 [('s', 'source', '',
506 _('rebase the specified changeset and descendants'), _('REV')),
506 _('rebase the specified changeset and descendants'), _('REV')),
507 ('b', 'base', '',
507 ('b', 'base', '',
508 _('rebase everything from branching point of specified changeset'),
508 _('rebase everything from branching point of specified changeset'),
509 _('REV')),
509 _('REV')),
510 ('r', 'rev', [],
510 ('r', 'rev', [],
511 _('rebase these revisions'),
511 _('rebase these revisions'),
512 _('REV')),
512 _('REV')),
513 ('d', 'dest', '',
513 ('d', 'dest', '',
514 _('rebase onto the specified changeset'), _('REV')),
514 _('rebase onto the specified changeset'), _('REV')),
515 ('', 'collapse', False, _('collapse the rebased changesets')),
515 ('', 'collapse', False, _('collapse the rebased changesets')),
516 ('m', 'message', '',
516 ('m', 'message', '',
517 _('use text as collapse commit message'), _('TEXT')),
517 _('use text as collapse commit message'), _('TEXT')),
518 ('e', 'edit', False, _('invoke editor on commit messages')),
518 ('e', 'edit', False, _('invoke editor on commit messages')),
519 ('l', 'logfile', '',
519 ('l', 'logfile', '',
520 _('read collapse commit message from file'), _('FILE')),
520 _('read collapse commit message from file'), _('FILE')),
521 ('k', 'keep', False, _('keep original changesets')),
521 ('k', 'keep', False, _('keep original changesets')),
522 ('', 'keepbranches', False, _('keep original branch names')),
522 ('', 'keepbranches', False, _('keep original branch names')),
523 ('D', 'detach', False, _('(DEPRECATED)')),
523 ('D', 'detach', False, _('(DEPRECATED)')),
524 ('i', 'interactive', False, _('(DEPRECATED)')),
524 ('i', 'interactive', False, _('(DEPRECATED)')),
525 ('t', 'tool', '', _('specify merge tool')),
525 ('t', 'tool', '', _('specify merge tool')),
526 ('c', 'continue', False, _('continue an interrupted rebase')),
526 ('c', 'continue', False, _('continue an interrupted rebase')),
527 ('a', 'abort', False, _('abort an interrupted rebase'))] +
527 ('a', 'abort', False, _('abort an interrupted rebase'))] +
528 templateopts,
528 templateopts,
529 _('[-s REV | -b REV] [-d REV] [OPTION]'))
529 _('[-s REV | -b REV] [-d REV] [OPTION]'))
530 def rebase(ui, repo, **opts):
530 def rebase(ui, repo, **opts):
531 """move changeset (and descendants) to a different branch
531 """move changeset (and descendants) to a different branch
532
532
533 Rebase uses repeated merging to graft changesets from one part of
533 Rebase uses repeated merging to graft changesets from one part of
534 history (the source) onto another (the destination). This can be
534 history (the source) onto another (the destination). This can be
535 useful for linearizing *local* changes relative to a master
535 useful for linearizing *local* changes relative to a master
536 development tree.
536 development tree.
537
537
538 Published commits cannot be rebased (see :hg:`help phases`).
538 Published commits cannot be rebased (see :hg:`help phases`).
539 To copy commits, see :hg:`help graft`.
539 To copy commits, see :hg:`help graft`.
540
540
541 If you don't specify a destination changeset (``-d/--dest``), rebase
541 If you don't specify a destination changeset (``-d/--dest``), rebase
542 will use the same logic as :hg:`merge` to pick a destination. if
542 will use the same logic as :hg:`merge` to pick a destination. if
543 the current branch contains exactly one other head, the other head
543 the current branch contains exactly one other head, the other head
544 is merged with by default. Otherwise, an explicit revision with
544 is merged with by default. Otherwise, an explicit revision with
545 which to merge with must be provided. (destination changeset is not
545 which to merge with must be provided. (destination changeset is not
546 modified by rebasing, but new changesets are added as its
546 modified by rebasing, but new changesets are added as its
547 descendants.)
547 descendants.)
548
548
549 Here are the ways to select changesets:
549 Here are the ways to select changesets:
550
550
551 1. Explicitly select them using ``--rev``.
551 1. Explicitly select them using ``--rev``.
552
552
553 2. Use ``--source`` to select a root changeset and include all of its
553 2. Use ``--source`` to select a root changeset and include all of its
554 descendants.
554 descendants.
555
555
556 3. Use ``--base`` to select a changeset; rebase will find ancestors
556 3. Use ``--base`` to select a changeset; rebase will find ancestors
557 and their descendants which are not also ancestors of the destination.
557 and their descendants which are not also ancestors of the destination.
558
558
559 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
559 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
560 rebase will use ``--base .`` as above.
560 rebase will use ``--base .`` as above.
561
561
562 Rebase will destroy original changesets unless you use ``--keep``.
562 Rebase will destroy original changesets unless you use ``--keep``.
563 It will also move your bookmarks (even if you do).
563 It will also move your bookmarks (even if you do).
564
564
565 Some changesets may be dropped if they do not contribute changes
565 Some changesets may be dropped if they do not contribute changes
566 (e.g. merges from the destination branch).
566 (e.g. merges from the destination branch).
567
567
568 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
568 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
569 a named branch with two heads. You will need to explicitly specify source
569 a named branch with two heads. You will need to explicitly specify source
570 and/or destination.
570 and/or destination.
571
571
572 If you need to use a tool to automate merge/conflict decisions, you
572 If you need to use a tool to automate merge/conflict decisions, you
573 can specify one with ``--tool``, see :hg:`help merge-tools`.
573 can specify one with ``--tool``, see :hg:`help merge-tools`.
574 As a caveat: the tool will not be used to mediate when a file was
574 As a caveat: the tool will not be used to mediate when a file was
575 deleted, there is no hook presently available for this.
575 deleted, there is no hook presently available for this.
576
576
577 If a rebase is interrupted to manually resolve a conflict, it can be
577 If a rebase is interrupted to manually resolve a conflict, it can be
578 continued with --continue/-c or aborted with --abort/-a.
578 continued with --continue/-c or aborted with --abort/-a.
579
579
580 .. container:: verbose
580 .. container:: verbose
581
581
582 Examples:
582 Examples:
583
583
584 - move "local changes" (current commit back to branching point)
584 - move "local changes" (current commit back to branching point)
585 to the current branch tip after a pull::
585 to the current branch tip after a pull::
586
586
587 hg rebase
587 hg rebase
588
588
589 - move a single changeset to the stable branch::
589 - move a single changeset to the stable branch::
590
590
591 hg rebase -r 5f493448 -d stable
591 hg rebase -r 5f493448 -d stable
592
592
593 - splice a commit and all its descendants onto another part of history::
593 - splice a commit and all its descendants onto another part of history::
594
594
595 hg rebase --source c0c3 --dest 4cf9
595 hg rebase --source c0c3 --dest 4cf9
596
596
597 - rebase everything on a branch marked by a bookmark onto the
597 - rebase everything on a branch marked by a bookmark onto the
598 default branch::
598 default branch::
599
599
600 hg rebase --base myfeature --dest default
600 hg rebase --base myfeature --dest default
601
601
602 - collapse a sequence of changes into a single commit::
602 - collapse a sequence of changes into a single commit::
603
603
604 hg rebase --collapse -r 1520:1525 -d .
604 hg rebase --collapse -r 1520:1525 -d .
605
605
606 - move a named branch while preserving its name::
606 - move a named branch while preserving its name::
607
607
608 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
608 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
609
609
610 Configuration Options:
610 Configuration Options:
611
611
612 You can make rebase require a destination if you set the following config
612 You can make rebase require a destination if you set the following config
613 option::
613 option::
614
614
615 [commands]
615 [commands]
616 rebase.requiredest = True
616 rebase.requiredest = True
617
617
618 By default, rebase will close the transaction after each commit. For
618 By default, rebase will close the transaction after each commit. For
619 performance purposes, you can configure rebase to use a single transaction
619 performance purposes, you can configure rebase to use a single transaction
620 across the entire rebase. WARNING: This setting introduces a significant
620 across the entire rebase. WARNING: This setting introduces a significant
621 risk of losing the work you've done in a rebase if the rebase aborts
621 risk of losing the work you've done in a rebase if the rebase aborts
622 unexpectedly::
622 unexpectedly::
623
623
624 [rebase]
624 [rebase]
625 singletransaction = True
625 singletransaction = True
626
626
627 Return Values:
627 Return Values:
628
628
629 Returns 0 on success, 1 if nothing to rebase or there are
629 Returns 0 on success, 1 if nothing to rebase or there are
630 unresolved conflicts.
630 unresolved conflicts.
631
631
632 """
632 """
633 rbsrt = rebaseruntime(repo, ui, opts)
633 rbsrt = rebaseruntime(repo, ui, opts)
634
634
635 with repo.wlock(), repo.lock():
635 with repo.wlock(), repo.lock():
636 # Validate input and define rebasing points
636 # Validate input and define rebasing points
637 destf = opts.get('dest', None)
637 destf = opts.get('dest', None)
638 srcf = opts.get('source', None)
638 srcf = opts.get('source', None)
639 basef = opts.get('base', None)
639 basef = opts.get('base', None)
640 revf = opts.get('rev', [])
640 revf = opts.get('rev', [])
641 # search default destination in this space
641 # search default destination in this space
642 # used in the 'hg pull --rebase' case, see issue 5214.
642 # used in the 'hg pull --rebase' case, see issue 5214.
643 destspace = opts.get('_destspace')
643 destspace = opts.get('_destspace')
644 contf = opts.get('continue')
644 contf = opts.get('continue')
645 abortf = opts.get('abort')
645 abortf = opts.get('abort')
646 if opts.get('interactive'):
646 if opts.get('interactive'):
647 try:
647 try:
648 if extensions.find('histedit'):
648 if extensions.find('histedit'):
649 enablehistedit = ''
649 enablehistedit = ''
650 except KeyError:
650 except KeyError:
651 enablehistedit = " --config extensions.histedit="
651 enablehistedit = " --config extensions.histedit="
652 help = "hg%s help -e histedit" % enablehistedit
652 help = "hg%s help -e histedit" % enablehistedit
653 msg = _("interactive history editing is supported by the "
653 msg = _("interactive history editing is supported by the "
654 "'histedit' extension (see \"%s\")") % help
654 "'histedit' extension (see \"%s\")") % help
655 raise error.Abort(msg)
655 raise error.Abort(msg)
656
656
657 if rbsrt.collapsemsg and not rbsrt.collapsef:
657 if rbsrt.collapsemsg and not rbsrt.collapsef:
658 raise error.Abort(
658 raise error.Abort(
659 _('message can only be specified with collapse'))
659 _('message can only be specified with collapse'))
660
660
661 if contf or abortf:
661 if contf or abortf:
662 if contf and abortf:
662 if contf and abortf:
663 raise error.Abort(_('cannot use both abort and continue'))
663 raise error.Abort(_('cannot use both abort and continue'))
664 if rbsrt.collapsef:
664 if rbsrt.collapsef:
665 raise error.Abort(
665 raise error.Abort(
666 _('cannot use collapse with continue or abort'))
666 _('cannot use collapse with continue or abort'))
667 if srcf or basef or destf:
667 if srcf or basef or destf:
668 raise error.Abort(
668 raise error.Abort(
669 _('abort and continue do not allow specifying revisions'))
669 _('abort and continue do not allow specifying revisions'))
670 if abortf and opts.get('tool', False):
670 if abortf and opts.get('tool', False):
671 ui.warn(_('tool option will be ignored\n'))
671 ui.warn(_('tool option will be ignored\n'))
672 if contf:
672 if contf:
673 ms = mergemod.mergestate.read(repo)
673 ms = mergemod.mergestate.read(repo)
674 mergeutil.checkunresolved(ms)
674 mergeutil.checkunresolved(ms)
675
675
676 retcode = rbsrt._prepareabortorcontinue(abortf)
676 retcode = rbsrt._prepareabortorcontinue(abortf)
677 if retcode is not None:
677 if retcode is not None:
678 return retcode
678 return retcode
679 else:
679 else:
680 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
680 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
681 destspace=destspace)
681 destspace=destspace)
682 retcode = rbsrt._preparenewrebase(dest, rebaseset)
682 retcode = rbsrt._preparenewrebase(dest, rebaseset)
683 if retcode is not None:
683 if retcode is not None:
684 return retcode
684 return retcode
685
685
686 tr = None
686 tr = None
687 dsguard = None
687 dsguard = None
688
688
689 singletr = ui.configbool('rebase', 'singletransaction')
689 singletr = ui.configbool('rebase', 'singletransaction')
690 if singletr:
690 if singletr:
691 tr = repo.transaction('rebase')
691 tr = repo.transaction('rebase')
692 with util.acceptintervention(tr):
692 with util.acceptintervention(tr):
693 if singletr:
693 if singletr:
694 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
694 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
695 with util.acceptintervention(dsguard):
695 with util.acceptintervention(dsguard):
696 rbsrt._performrebase(tr)
696 rbsrt._performrebase(tr)
697
697
698 rbsrt._finishrebase()
698 rbsrt._finishrebase()
699
699
700 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=None,
700 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=None,
701 destspace=None):
701 destspace=None):
702 """use revisions argument to define destination and rebase set
702 """use revisions argument to define destination and rebase set
703 """
703 """
704 if revf is None:
704 if revf is None:
705 revf = []
705 revf = []
706
706
707 # destspace is here to work around issues with `hg pull --rebase` see
707 # destspace is here to work around issues with `hg pull --rebase` see
708 # issue5214 for details
708 # issue5214 for details
709 if srcf and basef:
709 if srcf and basef:
710 raise error.Abort(_('cannot specify both a source and a base'))
710 raise error.Abort(_('cannot specify both a source and a base'))
711 if revf and basef:
711 if revf and basef:
712 raise error.Abort(_('cannot specify both a revision and a base'))
712 raise error.Abort(_('cannot specify both a revision and a base'))
713 if revf and srcf:
713 if revf and srcf:
714 raise error.Abort(_('cannot specify both a revision and a source'))
714 raise error.Abort(_('cannot specify both a revision and a source'))
715
715
716 cmdutil.checkunfinished(repo)
716 cmdutil.checkunfinished(repo)
717 cmdutil.bailifchanged(repo)
717 cmdutil.bailifchanged(repo)
718
718
719 if ui.configbool('commands', 'rebase.requiredest') and not destf:
719 if ui.configbool('commands', 'rebase.requiredest') and not destf:
720 raise error.Abort(_('you must specify a destination'),
720 raise error.Abort(_('you must specify a destination'),
721 hint=_('use: hg rebase -d REV'))
721 hint=_('use: hg rebase -d REV'))
722
722
723 if destf:
723 if destf:
724 dest = scmutil.revsingle(repo, destf)
724 dest = scmutil.revsingle(repo, destf)
725
725
726 if revf:
726 if revf:
727 rebaseset = scmutil.revrange(repo, revf)
727 rebaseset = scmutil.revrange(repo, revf)
728 if not rebaseset:
728 if not rebaseset:
729 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
729 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
730 return None, None
730 return None, None
731 elif srcf:
731 elif srcf:
732 src = scmutil.revrange(repo, [srcf])
732 src = scmutil.revrange(repo, [srcf])
733 if not src:
733 if not src:
734 ui.status(_('empty "source" revision set - nothing to rebase\n'))
734 ui.status(_('empty "source" revision set - nothing to rebase\n'))
735 return None, None
735 return None, None
736 rebaseset = repo.revs('(%ld)::', src)
736 rebaseset = repo.revs('(%ld)::', src)
737 assert rebaseset
737 assert rebaseset
738 else:
738 else:
739 base = scmutil.revrange(repo, [basef or '.'])
739 base = scmutil.revrange(repo, [basef or '.'])
740 if not base:
740 if not base:
741 ui.status(_('empty "base" revision set - '
741 ui.status(_('empty "base" revision set - '
742 "can't compute rebase set\n"))
742 "can't compute rebase set\n"))
743 return None, None
743 return None, None
744 if not destf:
744 if not destf:
745 dest = repo[_destrebase(repo, base, destspace=destspace)]
745 dest = repo[_destrebase(repo, base, destspace=destspace)]
746 destf = str(dest)
746 destf = str(dest)
747
747
748 roots = [] # selected children of branching points
748 roots = [] # selected children of branching points
749 bpbase = {} # {branchingpoint: [origbase]}
749 bpbase = {} # {branchingpoint: [origbase]}
750 for b in base: # group bases by branching points
750 for b in base: # group bases by branching points
751 bp = repo.revs('ancestor(%d, %d)', b, dest).first()
751 bp = repo.revs('ancestor(%d, %d)', b, dest).first()
752 bpbase[bp] = bpbase.get(bp, []) + [b]
752 bpbase[bp] = bpbase.get(bp, []) + [b]
753 if None in bpbase:
753 if None in bpbase:
754 # emulate the old behavior, showing "nothing to rebase" (a better
754 # emulate the old behavior, showing "nothing to rebase" (a better
755 # behavior may be abort with "cannot find branching point" error)
755 # behavior may be abort with "cannot find branching point" error)
756 bpbase.clear()
756 bpbase.clear()
757 for bp, bs in bpbase.iteritems(): # calculate roots
757 for bp, bs in bpbase.iteritems(): # calculate roots
758 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
758 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
759
759
760 rebaseset = repo.revs('%ld::', roots)
760 rebaseset = repo.revs('%ld::', roots)
761
761
762 if not rebaseset:
762 if not rebaseset:
763 # transform to list because smartsets are not comparable to
763 # transform to list because smartsets are not comparable to
764 # lists. This should be improved to honor laziness of
764 # lists. This should be improved to honor laziness of
765 # smartset.
765 # smartset.
766 if list(base) == [dest.rev()]:
766 if list(base) == [dest.rev()]:
767 if basef:
767 if basef:
768 ui.status(_('nothing to rebase - %s is both "base"'
768 ui.status(_('nothing to rebase - %s is both "base"'
769 ' and destination\n') % dest)
769 ' and destination\n') % dest)
770 else:
770 else:
771 ui.status(_('nothing to rebase - working directory '
771 ui.status(_('nothing to rebase - working directory '
772 'parent is also destination\n'))
772 'parent is also destination\n'))
773 elif not repo.revs('%ld - ::%d', base, dest):
773 elif not repo.revs('%ld - ::%d', base, dest):
774 if basef:
774 if basef:
775 ui.status(_('nothing to rebase - "base" %s is '
775 ui.status(_('nothing to rebase - "base" %s is '
776 'already an ancestor of destination '
776 'already an ancestor of destination '
777 '%s\n') %
777 '%s\n') %
778 ('+'.join(str(repo[r]) for r in base),
778 ('+'.join(str(repo[r]) for r in base),
779 dest))
779 dest))
780 else:
780 else:
781 ui.status(_('nothing to rebase - working '
781 ui.status(_('nothing to rebase - working '
782 'directory parent is already an '
782 'directory parent is already an '
783 'ancestor of destination %s\n') % dest)
783 'ancestor of destination %s\n') % dest)
784 else: # can it happen?
784 else: # can it happen?
785 ui.status(_('nothing to rebase from %s to %s\n') %
785 ui.status(_('nothing to rebase from %s to %s\n') %
786 ('+'.join(str(repo[r]) for r in base), dest))
786 ('+'.join(str(repo[r]) for r in base), dest))
787 return None, None
787 return None, None
788
788
789 if not destf:
789 if not destf:
790 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
790 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
791 destf = str(dest)
791 destf = str(dest)
792
792
793 return dest, rebaseset
793 return dest, rebaseset
794
794
795 def externalparent(repo, state, destancestors):
795 def externalparent(repo, state, destancestors):
796 """Return the revision that should be used as the second parent
796 """Return the revision that should be used as the second parent
797 when the revisions in state is collapsed on top of destancestors.
797 when the revisions in state is collapsed on top of destancestors.
798 Abort if there is more than one parent.
798 Abort if there is more than one parent.
799 """
799 """
800 parents = set()
800 parents = set()
801 source = min(state)
801 source = min(state)
802 for rev in state:
802 for rev in state:
803 if rev == source:
803 if rev == source:
804 continue
804 continue
805 for p in repo[rev].parents():
805 for p in repo[rev].parents():
806 if (p.rev() not in state
806 if (p.rev() not in state
807 and p.rev() not in destancestors):
807 and p.rev() not in destancestors):
808 parents.add(p.rev())
808 parents.add(p.rev())
809 if not parents:
809 if not parents:
810 return nullrev
810 return nullrev
811 if len(parents) == 1:
811 if len(parents) == 1:
812 return parents.pop()
812 return parents.pop()
813 raise error.Abort(_('unable to collapse on top of %s, there is more '
813 raise error.Abort(_('unable to collapse on top of %s, there is more '
814 'than one external parent: %s') %
814 'than one external parent: %s') %
815 (max(destancestors),
815 (max(destancestors),
816 ', '.join(str(p) for p in sorted(parents))))
816 ', '.join(str(p) for p in sorted(parents))))
817
817
818 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
818 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
819 keepbranches=False, date=None):
819 keepbranches=False, date=None):
820 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
820 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
821 but also store useful information in extra.
821 but also store useful information in extra.
822 Return node of committed revision.'''
822 Return node of committed revision.'''
823 dsguard = util.nullcontextmanager()
823 dsguard = util.nullcontextmanager()
824 if not repo.ui.configbool('rebase', 'singletransaction'):
824 if not repo.ui.configbool('rebase', 'singletransaction'):
825 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
825 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
826 with dsguard:
826 with dsguard:
827 repo.setparents(repo[p1].node(), repo[p2].node())
827 repo.setparents(repo[p1].node(), repo[p2].node())
828 ctx = repo[rev]
828 ctx = repo[rev]
829 if commitmsg is None:
829 if commitmsg is None:
830 commitmsg = ctx.description()
830 commitmsg = ctx.description()
831 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
831 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
832 extra = {'rebase_source': ctx.hex()}
832 extra = {'rebase_source': ctx.hex()}
833 if extrafn:
833 if extrafn:
834 extrafn(ctx, extra)
834 extrafn(ctx, extra)
835
835
836 destphase = max(ctx.phase(), phases.draft)
836 destphase = max(ctx.phase(), phases.draft)
837 overrides = {('phases', 'new-commit'): destphase}
837 overrides = {('phases', 'new-commit'): destphase}
838 with repo.ui.configoverride(overrides, 'rebase'):
838 with repo.ui.configoverride(overrides, 'rebase'):
839 if keepbranch:
839 if keepbranch:
840 repo.ui.setconfig('ui', 'allowemptycommit', True)
840 repo.ui.setconfig('ui', 'allowemptycommit', True)
841 # Commit might fail if unresolved files exist
841 # Commit might fail if unresolved files exist
842 if date is None:
842 if date is None:
843 date = ctx.date()
843 date = ctx.date()
844 newnode = repo.commit(text=commitmsg, user=ctx.user(),
844 newnode = repo.commit(text=commitmsg, user=ctx.user(),
845 date=date, extra=extra, editor=editor)
845 date=date, extra=extra, editor=editor)
846
846
847 repo.dirstate.setbranch(repo[newnode].branch())
847 repo.dirstate.setbranch(repo[newnode].branch())
848 return newnode
848 return newnode
849
849
850 def rebasenode(repo, rev, p1, base, state, collapse, dest):
850 def rebasenode(repo, rev, p1, base, state, collapse, dest):
851 'Rebase a single revision rev on top of p1 using base as merge ancestor'
851 'Rebase a single revision rev on top of p1 using base as merge ancestor'
852 # Merge phase
852 # Merge phase
853 # Update to destination and merge it with local
853 # Update to destination and merge it with local
854 if repo['.'].rev() != p1:
854 if repo['.'].rev() != p1:
855 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
855 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
856 mergemod.update(repo, p1, False, True)
856 mergemod.update(repo, p1, False, True)
857 else:
857 else:
858 repo.ui.debug(" already in destination\n")
858 repo.ui.debug(" already in destination\n")
859 repo.dirstate.write(repo.currenttransaction())
859 repo.dirstate.write(repo.currenttransaction())
860 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
860 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
861 if base is not None:
861 if base is not None:
862 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
862 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
863 # When collapsing in-place, the parent is the common ancestor, we
863 # When collapsing in-place, the parent is the common ancestor, we
864 # have to allow merging with it.
864 # have to allow merging with it.
865 stats = mergemod.update(repo, rev, True, True, base, collapse,
865 stats = mergemod.update(repo, rev, True, True, base, collapse,
866 labels=['dest', 'source'])
866 labels=['dest', 'source'])
867 if collapse:
867 if collapse:
868 copies.duplicatecopies(repo, rev, dest)
868 copies.duplicatecopies(repo, rev, dest)
869 else:
869 else:
870 # If we're not using --collapse, we need to
870 # If we're not using --collapse, we need to
871 # duplicate copies between the revision we're
871 # duplicate copies between the revision we're
872 # rebasing and its first parent, but *not*
872 # rebasing and its first parent, but *not*
873 # duplicate any copies that have already been
873 # duplicate any copies that have already been
874 # performed in the destination.
874 # performed in the destination.
875 p1rev = repo[rev].p1().rev()
875 p1rev = repo[rev].p1().rev()
876 copies.duplicatecopies(repo, rev, p1rev, skiprev=dest)
876 copies.duplicatecopies(repo, rev, p1rev, skiprev=dest)
877 return stats
877 return stats
878
878
879 def adjustdest(repo, rev, dest, state):
879 def adjustdest(repo, rev, dest, state):
880 """adjust rebase destination given the current rebase state
880 """adjust rebase destination given the current rebase state
881
881
882 rev is what is being rebased. Return a list of two revs, which are the
882 rev is what is being rebased. Return a list of two revs, which are the
883 adjusted destinations for rev's p1 and p2, respectively. If a parent is
883 adjusted destinations for rev's p1 and p2, respectively. If a parent is
884 nullrev, return dest without adjustment for it.
884 nullrev, return dest without adjustment for it.
885
885
886 For example, when doing rebase -r B+E -d F, rebase will first move B to B1,
886 For example, when doing rebase -r B+E -d F, rebase will first move B to B1,
887 and E's destination will be adjusted from F to B1.
887 and E's destination will be adjusted from F to B1.
888
888
889 B1 <- written during rebasing B
889 B1 <- written during rebasing B
890 |
890 |
891 F <- original destination of B, E
891 F <- original destination of B, E
892 |
892 |
893 | E <- rev, which is being rebased
893 | E <- rev, which is being rebased
894 | |
894 | |
895 | D <- prev, one parent of rev being checked
895 | D <- prev, one parent of rev being checked
896 | |
896 | |
897 | x <- skipped, ex. no successor or successor in (::dest)
897 | x <- skipped, ex. no successor or successor in (::dest)
898 | |
898 | |
899 | C
899 | C
900 | |
900 | |
901 | B <- rebased as B1
901 | B <- rebased as B1
902 |/
902 |/
903 A
903 A
904
904
905 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
905 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
906 first move C to C1, G to G1, and when it's checking H, the adjusted
906 first move C to C1, G to G1, and when it's checking H, the adjusted
907 destinations will be [C1, G1].
907 destinations will be [C1, G1].
908
908
909 H C1 G1
909 H C1 G1
910 /| | /
910 /| | /
911 F G |/
911 F G |/
912 K | | -> K
912 K | | -> K
913 | C D |
913 | C D |
914 | |/ |
914 | |/ |
915 | B | ...
915 | B | ...
916 |/ |/
916 |/ |/
917 A A
917 A A
918 """
918 """
919 # pick already rebased revs from state
919 # pick already rebased revs from state
920 source = [s for s, d in state.items() if d > 0]
920 source = [s for s, d in state.items() if d > 0]
921
921
922 result = []
922 result = []
923 for prev in repo.changelog.parentrevs(rev):
923 for prev in repo.changelog.parentrevs(rev):
924 adjusted = dest
924 adjusted = dest
925 if prev != nullrev:
925 if prev != nullrev:
926 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
926 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
927 if candidate is not None:
927 if candidate is not None:
928 adjusted = state[candidate]
928 adjusted = state[candidate]
929 result.append(adjusted)
929 result.append(adjusted)
930 return result
930 return result
931
931
932 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
932 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
933 """
933 """
934 Abort if rebase will create divergence or rebase is noop because of markers
934 Abort if rebase will create divergence or rebase is noop because of markers
935
935
936 `rebaseobsrevs`: set of obsolete revision in source
936 `rebaseobsrevs`: set of obsolete revision in source
937 `rebaseobsskipped`: set of revisions from source skipped because they have
937 `rebaseobsskipped`: set of revisions from source skipped because they have
938 successors in destination
938 successors in destination
939 """
939 """
940 # Obsolete node with successors not in dest leads to divergence
940 # Obsolete node with successors not in dest leads to divergence
941 divergenceok = ui.configbool('experimental',
941 divergenceok = ui.configbool('experimental',
942 'allowdivergence')
942 'allowdivergence')
943 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
943 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
944
944
945 if divergencebasecandidates and not divergenceok:
945 if divergencebasecandidates and not divergenceok:
946 divhashes = (str(repo[r])
946 divhashes = (str(repo[r])
947 for r in divergencebasecandidates)
947 for r in divergencebasecandidates)
948 msg = _("this rebase will cause "
948 msg = _("this rebase will cause "
949 "divergences from: %s")
949 "divergences from: %s")
950 h = _("to force the rebase please set "
950 h = _("to force the rebase please set "
951 "experimental.allowdivergence=True")
951 "experimental.allowdivergence=True")
952 raise error.Abort(msg % (",".join(divhashes),), hint=h)
952 raise error.Abort(msg % (",".join(divhashes),), hint=h)
953
953
954 def successorrevs(repo, rev):
954 def successorrevs(repo, rev):
955 """yield revision numbers for successors of rev"""
955 """yield revision numbers for successors of rev"""
956 unfi = repo.unfiltered()
956 unfi = repo.unfiltered()
957 nodemap = unfi.changelog.nodemap
957 nodemap = unfi.changelog.nodemap
958 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
958 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
959 if s in nodemap:
959 if s in nodemap:
960 yield nodemap[s]
960 yield nodemap[s]
961
961
962 def defineparents(repo, rev, dest, state):
962 def defineparents(repo, rev, dest, state):
963 """Return new parents and optionally a merge base for rev being rebased
963 """Return new parents and optionally a merge base for rev being rebased
964
964
965 The destination specified by "dest" cannot always be used directly because
965 The destination specified by "dest" cannot always be used directly because
966 previously rebase result could affect destination. For example,
966 previously rebase result could affect destination. For example,
967
967
968 D E rebase -r C+D+E -d B
968 D E rebase -r C+D+E -d B
969 |/ C will be rebased to C'
969 |/ C will be rebased to C'
970 B C D's new destination will be C' instead of B
970 B C D's new destination will be C' instead of B
971 |/ E's new destination will be C' instead of B
971 |/ E's new destination will be C' instead of B
972 A
972 A
973
973
974 The new parents of a merge is slightly more complicated. See the comment
974 The new parents of a merge is slightly more complicated. See the comment
975 block below.
975 block below.
976 """
976 """
977 cl = repo.changelog
977 cl = repo.changelog
978 def isancestor(a, b):
978 def isancestor(a, b):
979 # take revision numbers instead of nodes
979 # take revision numbers instead of nodes
980 if a == b:
980 if a == b:
981 return True
981 return True
982 elif a > b:
982 elif a > b:
983 return False
983 return False
984 return cl.isancestor(cl.node(a), cl.node(b))
984 return cl.isancestor(cl.node(a), cl.node(b))
985
985
986 oldps = repo.changelog.parentrevs(rev) # old parents
986 oldps = repo.changelog.parentrevs(rev) # old parents
987 newps = [nullrev, nullrev] # new parents
987 newps = [nullrev, nullrev] # new parents
988 dests = adjustdest(repo, rev, dest, state) # adjusted destinations
988 dests = adjustdest(repo, rev, dest, state) # adjusted destinations
989 bases = list(oldps) # merge base candidates, initially just old parents
989 bases = list(oldps) # merge base candidates, initially just old parents
990
990
991 if all(r == nullrev for r in oldps[1:]):
991 if all(r == nullrev for r in oldps[1:]):
992 # For non-merge changeset, just move p to adjusted dest as requested.
992 # For non-merge changeset, just move p to adjusted dest as requested.
993 newps[0] = dests[0]
993 newps[0] = dests[0]
994 else:
994 else:
995 # For merge changeset, if we move p to dests[i] unconditionally, both
995 # For merge changeset, if we move p to dests[i] unconditionally, both
996 # parents may change and the end result looks like "the merge loses a
996 # parents may change and the end result looks like "the merge loses a
997 # parent", which is a surprise. This is a limit because "--dest" only
997 # parent", which is a surprise. This is a limit because "--dest" only
998 # accepts one dest per src.
998 # accepts one dest per src.
999 #
999 #
1000 # Therefore, only move p with reasonable conditions (in this order):
1000 # Therefore, only move p with reasonable conditions (in this order):
1001 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1001 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1002 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1002 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1003 #
1003 #
1004 # Comparing with adjustdest, the logic here does some additional work:
1004 # Comparing with adjustdest, the logic here does some additional work:
1005 # 1. decide which parents will not be moved towards dest
1005 # 1. decide which parents will not be moved towards dest
1006 # 2. if the above decision is "no", should a parent still be moved
1006 # 2. if the above decision is "no", should a parent still be moved
1007 # because it was rebased?
1007 # because it was rebased?
1008 #
1008 #
1009 # For example:
1009 # For example:
1010 #
1010 #
1011 # C # "rebase -r C -d D" is an error since none of the parents
1011 # C # "rebase -r C -d D" is an error since none of the parents
1012 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1012 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1013 # A B D # B (using rule "2."), since B will be rebased.
1013 # A B D # B (using rule "2."), since B will be rebased.
1014 #
1014 #
1015 # The loop tries to be not rely on the fact that a Mercurial node has
1015 # The loop tries to be not rely on the fact that a Mercurial node has
1016 # at most 2 parents.
1016 # at most 2 parents.
1017 for i, p in enumerate(oldps):
1017 for i, p in enumerate(oldps):
1018 np = p # new parent
1018 np = p # new parent
1019 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1019 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1020 np = dests[i]
1020 np = dests[i]
1021 elif p in state and state[p] > 0:
1021 elif p in state and state[p] > 0:
1022 np = state[p]
1022 np = state[p]
1023
1023
1024 # "bases" only record "special" merge bases that cannot be
1024 # "bases" only record "special" merge bases that cannot be
1025 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1025 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1026 # For example:
1026 # For example:
1027 #
1027 #
1028 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1028 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1029 # | C # is B', but merge base for C is B, instead of
1029 # | C # is B', but merge base for C is B, instead of
1030 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1030 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1031 # | B # "state" edges are merged (so there will be an edge from
1031 # | B # "state" edges are merged (so there will be an edge from
1032 # |/ # B to B'), the merge base is still ancestor(C, B') in
1032 # |/ # B to B'), the merge base is still ancestor(C, B') in
1033 # A # the merged graph.
1033 # A # the merged graph.
1034 #
1034 #
1035 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1035 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1036 # which uses "virtual null merge" to explain this situation.
1036 # which uses "virtual null merge" to explain this situation.
1037 if isancestor(p, np):
1037 if isancestor(p, np):
1038 bases[i] = nullrev
1038 bases[i] = nullrev
1039
1039
1040 # If one parent becomes an ancestor of the other, drop the ancestor
1040 # If one parent becomes an ancestor of the other, drop the ancestor
1041 for j, x in enumerate(newps[:i]):
1041 for j, x in enumerate(newps[:i]):
1042 if x == nullrev:
1042 if x == nullrev:
1043 continue
1043 continue
1044 if isancestor(np, x):
1044 if isancestor(np, x): # CASE-1
1045 np = nullrev
1045 np = nullrev
1046 elif isancestor(x, np):
1046 elif isancestor(x, np): # CASE-2
1047 newps[j] = np
1047 newps[j] = np
1048 np = nullrev
1048 np = nullrev
1049 # New parents forming an ancestor relationship does not
1050 # mean the old parents have a similar relationship. Do not
1051 # set bases[x] to nullrev.
1049 bases[j], bases[i] = bases[i], bases[j]
1052 bases[j], bases[i] = bases[i], bases[j]
1050
1053
1051 newps[i] = np
1054 newps[i] = np
1052
1055
1053 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1056 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1054 # base. If only p2 changes, merging using unchanged p1 as merge base is
1057 # base. If only p2 changes, merging using unchanged p1 as merge base is
1055 # suboptimal. Therefore swap parents to make the merge sane.
1058 # suboptimal. Therefore swap parents to make the merge sane.
1056 if newps[1] != nullrev and oldps[0] == newps[0]:
1059 if newps[1] != nullrev and oldps[0] == newps[0]:
1057 assert len(newps) == 2 and len(oldps) == 2
1060 assert len(newps) == 2 and len(oldps) == 2
1058 newps.reverse()
1061 newps.reverse()
1059 bases.reverse()
1062 bases.reverse()
1060
1063
1061 # No parent change might be an error because we fail to make rev a
1064 # No parent change might be an error because we fail to make rev a
1062 # descendent of requested dest. This can happen, for example:
1065 # descendent of requested dest. This can happen, for example:
1063 #
1066 #
1064 # C # rebase -r C -d D
1067 # C # rebase -r C -d D
1065 # /| # None of A and B will be changed to D and rebase fails.
1068 # /| # None of A and B will be changed to D and rebase fails.
1066 # A B D
1069 # A B D
1067 if set(newps) == set(oldps) and dest not in newps:
1070 if set(newps) == set(oldps) and dest not in newps:
1068 raise error.Abort(_('cannot rebase %d:%s without '
1071 raise error.Abort(_('cannot rebase %d:%s without '
1069 'moving at least one of its parents')
1072 'moving at least one of its parents')
1070 % (rev, repo[rev]))
1073 % (rev, repo[rev]))
1071
1074
1072 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1073
1074 # "rebasenode" updates to new p1, use the corresponding merge base.
1075 # "rebasenode" updates to new p1, use the corresponding merge base.
1075 if bases[0] != nullrev:
1076 if bases[0] != nullrev:
1076 base = bases[0]
1077 base = bases[0]
1077 else:
1078 else:
1078 base = None
1079 base = None
1079
1080
1080 # Check if the merge will contain unwanted changes. That may happen if
1081 # Check if the merge will contain unwanted changes. That may happen if
1081 # there are multiple special (non-changelog ancestor) merge bases, which
1082 # there are multiple special (non-changelog ancestor) merge bases, which
1082 # cannot be handled well by the 3-way merge algorithm. For example:
1083 # cannot be handled well by the 3-way merge algorithm. For example:
1083 #
1084 #
1084 # F
1085 # F
1085 # /|
1086 # /|
1086 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1087 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1087 # | | # as merge base, the difference between D and F will include
1088 # | | # as merge base, the difference between D and F will include
1088 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1089 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1089 # |/ # chosen, the rebased F will contain B.
1090 # |/ # chosen, the rebased F will contain B.
1090 # A Z
1091 # A Z
1091 #
1092 #
1092 # But our merge base candidates (D and E in above case) could still be
1093 # But our merge base candidates (D and E in above case) could still be
1093 # better than the default (ancestor(F, Z) == null). Therefore still
1094 # better than the default (ancestor(F, Z) == null). Therefore still
1094 # pick one (so choose p1 above).
1095 # pick one (so choose p1 above).
1095 if sum(1 for b in bases if b != nullrev) > 1:
1096 if sum(1 for b in bases if b != nullrev) > 1:
1096 assert base is not None
1097 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1098 for i, base in enumerate(bases):
1099 if base == nullrev:
1100 continue
1101 # Revisions in the side (not chosen as merge base) branch that
1102 # might contain "surprising" contents
1103 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1104 bases, base, base, dest))
1097
1105
1098 # Revisions in the side (not chosen as merge base) branch that might
1106 # If those revisions are covered by rebaseset, the result is good.
1099 # contain "surprising" contents
1107 # A merge in rebaseset would be considered to cover its ancestors.
1100 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1108 if siderevs:
1101 bases, base, base, dest))
1109 rebaseset = [r for r, d in state.items() if d > 0]
1110 merges = [r for r in rebaseset
1111 if cl.parentrevs(r)[1] != nullrev]
1112 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1113 siderevs, merges, rebaseset))
1102
1114
1103 # If those revisions are covered by rebaseset, the result is good.
1115 # Choose a merge base that has a minimal number of unwanted revs.
1104 # A merge in rebaseset would be considered to cover its ancestors.
1116 l, i = min((len(revs), i)
1105 if siderevs:
1117 for i, revs in enumerate(unwanted) if revs is not None)
1106 rebaseset = [r for r, d in state.items() if d > 0]
1118 base = bases[i]
1107 merges = [r for r in rebaseset if cl.parentrevs(r)[1] != nullrev]
1119
1108 unwantedrevs = list(repo.revs('%ld - (::%ld) - %ld',
1120 # newps[0] should match merge base if possible. Currently, if newps[i]
1109 siderevs, merges, rebaseset))
1121 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1122 # the other's ancestor. In that case, it's fine to not swap newps here.
1123 # (see CASE-1 and CASE-2 above)
1124 if i != 0 and newps[i] != nullrev:
1125 newps[0], newps[i] = newps[i], newps[0]
1110
1126
1111 # For revs not covered, it is worth a warning.
1127 # The merge will include unwanted revisions. Abort now. Revisit this if
1112 if unwantedrevs:
1128 # we have a more advanced merge algorithm that handles multiple bases.
1113 repo.ui.warn(
1129 if l > 0:
1114 _('warning: rebasing %d:%s may include unwanted changes '
1130 unwanteddesc = _(' or ').join(
1115 'from %s\n')
1131 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1116 % (rev, repo[rev], ', '.join('%d:%s' % (r, repo[r])
1132 for revs in unwanted if revs is not None))
1117 for r in unwantedrevs)))
1133 raise error.Abort(
1134 _('rebasing %d:%s will include unwanted changes from %s')
1135 % (rev, repo[rev], unwanteddesc))
1136
1137 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1118
1138
1119 return newps[0], newps[1], base
1139 return newps[0], newps[1], base
1120
1140
1121 def isagitpatch(repo, patchname):
1141 def isagitpatch(repo, patchname):
1122 'Return true if the given patch is in git format'
1142 'Return true if the given patch is in git format'
1123 mqpatch = os.path.join(repo.mq.path, patchname)
1143 mqpatch = os.path.join(repo.mq.path, patchname)
1124 for line in patch.linereader(file(mqpatch, 'rb')):
1144 for line in patch.linereader(file(mqpatch, 'rb')):
1125 if line.startswith('diff --git'):
1145 if line.startswith('diff --git'):
1126 return True
1146 return True
1127 return False
1147 return False
1128
1148
1129 def updatemq(repo, state, skipped, **opts):
1149 def updatemq(repo, state, skipped, **opts):
1130 'Update rebased mq patches - finalize and then import them'
1150 'Update rebased mq patches - finalize and then import them'
1131 mqrebase = {}
1151 mqrebase = {}
1132 mq = repo.mq
1152 mq = repo.mq
1133 original_series = mq.fullseries[:]
1153 original_series = mq.fullseries[:]
1134 skippedpatches = set()
1154 skippedpatches = set()
1135
1155
1136 for p in mq.applied:
1156 for p in mq.applied:
1137 rev = repo[p.node].rev()
1157 rev = repo[p.node].rev()
1138 if rev in state:
1158 if rev in state:
1139 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1159 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1140 (rev, p.name))
1160 (rev, p.name))
1141 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1161 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1142 else:
1162 else:
1143 # Applied but not rebased, not sure this should happen
1163 # Applied but not rebased, not sure this should happen
1144 skippedpatches.add(p.name)
1164 skippedpatches.add(p.name)
1145
1165
1146 if mqrebase:
1166 if mqrebase:
1147 mq.finish(repo, mqrebase.keys())
1167 mq.finish(repo, mqrebase.keys())
1148
1168
1149 # We must start import from the newest revision
1169 # We must start import from the newest revision
1150 for rev in sorted(mqrebase, reverse=True):
1170 for rev in sorted(mqrebase, reverse=True):
1151 if rev not in skipped:
1171 if rev not in skipped:
1152 name, isgit = mqrebase[rev]
1172 name, isgit = mqrebase[rev]
1153 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1173 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1154 (name, state[rev], repo[state[rev]]))
1174 (name, state[rev], repo[state[rev]]))
1155 mq.qimport(repo, (), patchname=name, git=isgit,
1175 mq.qimport(repo, (), patchname=name, git=isgit,
1156 rev=[str(state[rev])])
1176 rev=[str(state[rev])])
1157 else:
1177 else:
1158 # Rebased and skipped
1178 # Rebased and skipped
1159 skippedpatches.add(mqrebase[rev][0])
1179 skippedpatches.add(mqrebase[rev][0])
1160
1180
1161 # Patches were either applied and rebased and imported in
1181 # Patches were either applied and rebased and imported in
1162 # order, applied and removed or unapplied. Discard the removed
1182 # order, applied and removed or unapplied. Discard the removed
1163 # ones while preserving the original series order and guards.
1183 # ones while preserving the original series order and guards.
1164 newseries = [s for s in original_series
1184 newseries = [s for s in original_series
1165 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1185 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1166 mq.fullseries[:] = newseries
1186 mq.fullseries[:] = newseries
1167 mq.seriesdirty = True
1187 mq.seriesdirty = True
1168 mq.savedirty()
1188 mq.savedirty()
1169
1189
1170 def storecollapsemsg(repo, collapsemsg):
1190 def storecollapsemsg(repo, collapsemsg):
1171 'Store the collapse message to allow recovery'
1191 'Store the collapse message to allow recovery'
1172 collapsemsg = collapsemsg or ''
1192 collapsemsg = collapsemsg or ''
1173 f = repo.vfs("last-message.txt", "w")
1193 f = repo.vfs("last-message.txt", "w")
1174 f.write("%s\n" % collapsemsg)
1194 f.write("%s\n" % collapsemsg)
1175 f.close()
1195 f.close()
1176
1196
1177 def clearcollapsemsg(repo):
1197 def clearcollapsemsg(repo):
1178 'Remove collapse message file'
1198 'Remove collapse message file'
1179 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1199 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1180
1200
1181 def restorecollapsemsg(repo, isabort):
1201 def restorecollapsemsg(repo, isabort):
1182 'Restore previously stored collapse message'
1202 'Restore previously stored collapse message'
1183 try:
1203 try:
1184 f = repo.vfs("last-message.txt")
1204 f = repo.vfs("last-message.txt")
1185 collapsemsg = f.readline().strip()
1205 collapsemsg = f.readline().strip()
1186 f.close()
1206 f.close()
1187 except IOError as err:
1207 except IOError as err:
1188 if err.errno != errno.ENOENT:
1208 if err.errno != errno.ENOENT:
1189 raise
1209 raise
1190 if isabort:
1210 if isabort:
1191 # Oh well, just abort like normal
1211 # Oh well, just abort like normal
1192 collapsemsg = ''
1212 collapsemsg = ''
1193 else:
1213 else:
1194 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1214 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1195 return collapsemsg
1215 return collapsemsg
1196
1216
1197 def clearstatus(repo):
1217 def clearstatus(repo):
1198 'Remove the status files'
1218 'Remove the status files'
1199 _clearrebasesetvisibiliy(repo)
1219 _clearrebasesetvisibiliy(repo)
1200 # Make sure the active transaction won't write the state file
1220 # Make sure the active transaction won't write the state file
1201 tr = repo.currenttransaction()
1221 tr = repo.currenttransaction()
1202 if tr:
1222 if tr:
1203 tr.removefilegenerator('rebasestate')
1223 tr.removefilegenerator('rebasestate')
1204 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1224 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1205
1225
1206 def needupdate(repo, state):
1226 def needupdate(repo, state):
1207 '''check whether we should `update --clean` away from a merge, or if
1227 '''check whether we should `update --clean` away from a merge, or if
1208 somehow the working dir got forcibly updated, e.g. by older hg'''
1228 somehow the working dir got forcibly updated, e.g. by older hg'''
1209 parents = [p.rev() for p in repo[None].parents()]
1229 parents = [p.rev() for p in repo[None].parents()]
1210
1230
1211 # Are we in a merge state at all?
1231 # Are we in a merge state at all?
1212 if len(parents) < 2:
1232 if len(parents) < 2:
1213 return False
1233 return False
1214
1234
1215 # We should be standing on the first as-of-yet unrebased commit.
1235 # We should be standing on the first as-of-yet unrebased commit.
1216 firstunrebased = min([old for old, new in state.iteritems()
1236 firstunrebased = min([old for old, new in state.iteritems()
1217 if new == nullrev])
1237 if new == nullrev])
1218 if firstunrebased in parents:
1238 if firstunrebased in parents:
1219 return True
1239 return True
1220
1240
1221 return False
1241 return False
1222
1242
1223 def abort(repo, originalwd, dest, state, activebookmark=None):
1243 def abort(repo, originalwd, dest, state, activebookmark=None):
1224 '''Restore the repository to its original state. Additional args:
1244 '''Restore the repository to its original state. Additional args:
1225
1245
1226 activebookmark: the name of the bookmark that should be active after the
1246 activebookmark: the name of the bookmark that should be active after the
1227 restore'''
1247 restore'''
1228
1248
1229 try:
1249 try:
1230 # If the first commits in the rebased set get skipped during the rebase,
1250 # If the first commits in the rebased set get skipped during the rebase,
1231 # their values within the state mapping will be the dest rev id. The
1251 # their values within the state mapping will be the dest rev id. The
1232 # dstates list must must not contain the dest rev (issue4896)
1252 # dstates list must must not contain the dest rev (issue4896)
1233 dstates = [s for s in state.values() if s >= 0 and s != dest]
1253 dstates = [s for s in state.values() if s >= 0 and s != dest]
1234 immutable = [d for d in dstates if not repo[d].mutable()]
1254 immutable = [d for d in dstates if not repo[d].mutable()]
1235 cleanup = True
1255 cleanup = True
1236 if immutable:
1256 if immutable:
1237 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1257 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1238 % ', '.join(str(repo[r]) for r in immutable),
1258 % ', '.join(str(repo[r]) for r in immutable),
1239 hint=_("see 'hg help phases' for details"))
1259 hint=_("see 'hg help phases' for details"))
1240 cleanup = False
1260 cleanup = False
1241
1261
1242 descendants = set()
1262 descendants = set()
1243 if dstates:
1263 if dstates:
1244 descendants = set(repo.changelog.descendants(dstates))
1264 descendants = set(repo.changelog.descendants(dstates))
1245 if descendants - set(dstates):
1265 if descendants - set(dstates):
1246 repo.ui.warn(_("warning: new changesets detected on destination "
1266 repo.ui.warn(_("warning: new changesets detected on destination "
1247 "branch, can't strip\n"))
1267 "branch, can't strip\n"))
1248 cleanup = False
1268 cleanup = False
1249
1269
1250 if cleanup:
1270 if cleanup:
1251 shouldupdate = False
1271 shouldupdate = False
1252 rebased = filter(lambda x: x >= 0 and x != dest, state.values())
1272 rebased = filter(lambda x: x >= 0 and x != dest, state.values())
1253 if rebased:
1273 if rebased:
1254 strippoints = [
1274 strippoints = [
1255 c.node() for c in repo.set('roots(%ld)', rebased)]
1275 c.node() for c in repo.set('roots(%ld)', rebased)]
1256
1276
1257 updateifonnodes = set(rebased)
1277 updateifonnodes = set(rebased)
1258 updateifonnodes.add(dest)
1278 updateifonnodes.add(dest)
1259 updateifonnodes.add(originalwd)
1279 updateifonnodes.add(originalwd)
1260 shouldupdate = repo['.'].rev() in updateifonnodes
1280 shouldupdate = repo['.'].rev() in updateifonnodes
1261
1281
1262 # Update away from the rebase if necessary
1282 # Update away from the rebase if necessary
1263 if shouldupdate or needupdate(repo, state):
1283 if shouldupdate or needupdate(repo, state):
1264 mergemod.update(repo, originalwd, False, True)
1284 mergemod.update(repo, originalwd, False, True)
1265
1285
1266 # Strip from the first rebased revision
1286 # Strip from the first rebased revision
1267 if rebased:
1287 if rebased:
1268 # no backup of rebased cset versions needed
1288 # no backup of rebased cset versions needed
1269 repair.strip(repo.ui, repo, strippoints)
1289 repair.strip(repo.ui, repo, strippoints)
1270
1290
1271 if activebookmark and activebookmark in repo._bookmarks:
1291 if activebookmark and activebookmark in repo._bookmarks:
1272 bookmarks.activate(repo, activebookmark)
1292 bookmarks.activate(repo, activebookmark)
1273
1293
1274 finally:
1294 finally:
1275 clearstatus(repo)
1295 clearstatus(repo)
1276 clearcollapsemsg(repo)
1296 clearcollapsemsg(repo)
1277 repo.ui.warn(_('rebase aborted\n'))
1297 repo.ui.warn(_('rebase aborted\n'))
1278 return 0
1298 return 0
1279
1299
1280 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1300 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1281 '''Define which revisions are going to be rebased and where
1301 '''Define which revisions are going to be rebased and where
1282
1302
1283 repo: repo
1303 repo: repo
1284 dest: context
1304 dest: context
1285 rebaseset: set of rev
1305 rebaseset: set of rev
1286 '''
1306 '''
1287 originalwd = repo['.'].rev()
1307 originalwd = repo['.'].rev()
1288 _setrebasesetvisibility(repo, set(rebaseset) | {originalwd})
1308 _setrebasesetvisibility(repo, set(rebaseset) | {originalwd})
1289
1309
1290 # This check isn't strictly necessary, since mq detects commits over an
1310 # This check isn't strictly necessary, since mq detects commits over an
1291 # applied patch. But it prevents messing up the working directory when
1311 # applied patch. But it prevents messing up the working directory when
1292 # a partially completed rebase is blocked by mq.
1312 # a partially completed rebase is blocked by mq.
1293 if 'qtip' in repo.tags() and (dest.node() in
1313 if 'qtip' in repo.tags() and (dest.node() in
1294 [s.node for s in repo.mq.applied]):
1314 [s.node for s in repo.mq.applied]):
1295 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1315 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1296
1316
1297 roots = list(repo.set('roots(%ld)', rebaseset))
1317 roots = list(repo.set('roots(%ld)', rebaseset))
1298 if not roots:
1318 if not roots:
1299 raise error.Abort(_('no matching revisions'))
1319 raise error.Abort(_('no matching revisions'))
1300 roots.sort()
1320 roots.sort()
1301 state = dict.fromkeys(rebaseset, revtodo)
1321 state = dict.fromkeys(rebaseset, revtodo)
1302 emptyrebase = True
1322 emptyrebase = True
1303 for root in roots:
1323 for root in roots:
1304 commonbase = root.ancestor(dest)
1324 commonbase = root.ancestor(dest)
1305 if commonbase == root:
1325 if commonbase == root:
1306 raise error.Abort(_('source is ancestor of destination'))
1326 raise error.Abort(_('source is ancestor of destination'))
1307 if commonbase == dest:
1327 if commonbase == dest:
1308 wctx = repo[None]
1328 wctx = repo[None]
1309 if dest == wctx.p1():
1329 if dest == wctx.p1():
1310 # when rebasing to '.', it will use the current wd branch name
1330 # when rebasing to '.', it will use the current wd branch name
1311 samebranch = root.branch() == wctx.branch()
1331 samebranch = root.branch() == wctx.branch()
1312 else:
1332 else:
1313 samebranch = root.branch() == dest.branch()
1333 samebranch = root.branch() == dest.branch()
1314 if not collapse and samebranch and dest in root.parents():
1334 if not collapse and samebranch and dest in root.parents():
1315 # mark the revision as done by setting its new revision
1335 # mark the revision as done by setting its new revision
1316 # equal to its old (current) revisions
1336 # equal to its old (current) revisions
1317 state[root.rev()] = root.rev()
1337 state[root.rev()] = root.rev()
1318 repo.ui.debug('source is a child of destination\n')
1338 repo.ui.debug('source is a child of destination\n')
1319 continue
1339 continue
1320
1340
1321 emptyrebase = False
1341 emptyrebase = False
1322 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1342 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1323 if emptyrebase:
1343 if emptyrebase:
1324 return None
1344 return None
1325 for rev in sorted(state):
1345 for rev in sorted(state):
1326 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1346 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1327 # if all parents of this revision are done, then so is this revision
1347 # if all parents of this revision are done, then so is this revision
1328 if parents and all((state.get(p) == p for p in parents)):
1348 if parents and all((state.get(p) == p for p in parents)):
1329 state[rev] = rev
1349 state[rev] = rev
1330 unfi = repo.unfiltered()
1350 unfi = repo.unfiltered()
1331 for r in obsoletenotrebased:
1351 for r in obsoletenotrebased:
1332 desc = _ctxdesc(unfi[r])
1352 desc = _ctxdesc(unfi[r])
1333 succ = obsoletenotrebased[r]
1353 succ = obsoletenotrebased[r]
1334 if succ is None:
1354 if succ is None:
1335 msg = _('note: not rebasing %s, it has no successor\n') % desc
1355 msg = _('note: not rebasing %s, it has no successor\n') % desc
1336 del state[r]
1356 del state[r]
1337 else:
1357 else:
1338 destctx = unfi[succ]
1358 destctx = unfi[succ]
1339 destdesc = '%d:%s "%s"' % (destctx.rev(), destctx,
1359 destdesc = '%d:%s "%s"' % (destctx.rev(), destctx,
1340 destctx.description().split('\n', 1)[0])
1360 destctx.description().split('\n', 1)[0])
1341 msg = (_('note: not rebasing %s, already in destination as %s\n')
1361 msg = (_('note: not rebasing %s, already in destination as %s\n')
1342 % (desc, destdesc))
1362 % (desc, destdesc))
1343 del state[r]
1363 del state[r]
1344 repo.ui.status(msg)
1364 repo.ui.status(msg)
1345 return originalwd, dest.rev(), state
1365 return originalwd, dest.rev(), state
1346
1366
1347 def clearrebased(ui, repo, dest, state, skipped, collapsedas=None):
1367 def clearrebased(ui, repo, dest, state, skipped, collapsedas=None):
1348 """dispose of rebased revision at the end of the rebase
1368 """dispose of rebased revision at the end of the rebase
1349
1369
1350 If `collapsedas` is not None, the rebase was a collapse whose result if the
1370 If `collapsedas` is not None, the rebase was a collapse whose result if the
1351 `collapsedas` node."""
1371 `collapsedas` node."""
1352 tonode = repo.changelog.node
1372 tonode = repo.changelog.node
1353 # Move bookmark of skipped nodes to destination. This cannot be handled
1373 # Move bookmark of skipped nodes to destination. This cannot be handled
1354 # by scmutil.cleanupnodes since it will treat rev as removed (no successor)
1374 # by scmutil.cleanupnodes since it will treat rev as removed (no successor)
1355 # and move bookmark backwards.
1375 # and move bookmark backwards.
1356 bmchanges = [(name, tonode(max(adjustdest(repo, rev, dest, state))))
1376 bmchanges = [(name, tonode(max(adjustdest(repo, rev, dest, state))))
1357 for rev in skipped
1377 for rev in skipped
1358 for name in repo.nodebookmarks(tonode(rev))]
1378 for name in repo.nodebookmarks(tonode(rev))]
1359 if bmchanges:
1379 if bmchanges:
1360 with repo.transaction('rebase') as tr:
1380 with repo.transaction('rebase') as tr:
1361 repo._bookmarks.applychanges(repo, tr, bmchanges)
1381 repo._bookmarks.applychanges(repo, tr, bmchanges)
1362 mapping = {}
1382 mapping = {}
1363 for rev, newrev in sorted(state.items()):
1383 for rev, newrev in sorted(state.items()):
1364 if newrev >= 0 and newrev != rev:
1384 if newrev >= 0 and newrev != rev:
1365 if rev in skipped:
1385 if rev in skipped:
1366 succs = ()
1386 succs = ()
1367 elif collapsedas is not None:
1387 elif collapsedas is not None:
1368 succs = (collapsedas,)
1388 succs = (collapsedas,)
1369 else:
1389 else:
1370 succs = (tonode(newrev),)
1390 succs = (tonode(newrev),)
1371 mapping[tonode(rev)] = succs
1391 mapping[tonode(rev)] = succs
1372 scmutil.cleanupnodes(repo, mapping, 'rebase')
1392 scmutil.cleanupnodes(repo, mapping, 'rebase')
1373
1393
1374 def pullrebase(orig, ui, repo, *args, **opts):
1394 def pullrebase(orig, ui, repo, *args, **opts):
1375 'Call rebase after pull if the latter has been invoked with --rebase'
1395 'Call rebase after pull if the latter has been invoked with --rebase'
1376 ret = None
1396 ret = None
1377 if opts.get('rebase'):
1397 if opts.get('rebase'):
1378 if ui.configbool('commands', 'rebase.requiredest'):
1398 if ui.configbool('commands', 'rebase.requiredest'):
1379 msg = _('rebase destination required by configuration')
1399 msg = _('rebase destination required by configuration')
1380 hint = _('use hg pull followed by hg rebase -d DEST')
1400 hint = _('use hg pull followed by hg rebase -d DEST')
1381 raise error.Abort(msg, hint=hint)
1401 raise error.Abort(msg, hint=hint)
1382
1402
1383 with repo.wlock(), repo.lock():
1403 with repo.wlock(), repo.lock():
1384 if opts.get('update'):
1404 if opts.get('update'):
1385 del opts['update']
1405 del opts['update']
1386 ui.debug('--update and --rebase are not compatible, ignoring '
1406 ui.debug('--update and --rebase are not compatible, ignoring '
1387 'the update flag\n')
1407 'the update flag\n')
1388
1408
1389 cmdutil.checkunfinished(repo)
1409 cmdutil.checkunfinished(repo)
1390 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1410 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1391 'please commit or shelve your changes first'))
1411 'please commit or shelve your changes first'))
1392
1412
1393 revsprepull = len(repo)
1413 revsprepull = len(repo)
1394 origpostincoming = commands.postincoming
1414 origpostincoming = commands.postincoming
1395 def _dummy(*args, **kwargs):
1415 def _dummy(*args, **kwargs):
1396 pass
1416 pass
1397 commands.postincoming = _dummy
1417 commands.postincoming = _dummy
1398 try:
1418 try:
1399 ret = orig(ui, repo, *args, **opts)
1419 ret = orig(ui, repo, *args, **opts)
1400 finally:
1420 finally:
1401 commands.postincoming = origpostincoming
1421 commands.postincoming = origpostincoming
1402 revspostpull = len(repo)
1422 revspostpull = len(repo)
1403 if revspostpull > revsprepull:
1423 if revspostpull > revsprepull:
1404 # --rev option from pull conflict with rebase own --rev
1424 # --rev option from pull conflict with rebase own --rev
1405 # dropping it
1425 # dropping it
1406 if 'rev' in opts:
1426 if 'rev' in opts:
1407 del opts['rev']
1427 del opts['rev']
1408 # positional argument from pull conflicts with rebase's own
1428 # positional argument from pull conflicts with rebase's own
1409 # --source.
1429 # --source.
1410 if 'source' in opts:
1430 if 'source' in opts:
1411 del opts['source']
1431 del opts['source']
1412 # revsprepull is the len of the repo, not revnum of tip.
1432 # revsprepull is the len of the repo, not revnum of tip.
1413 destspace = list(repo.changelog.revs(start=revsprepull))
1433 destspace = list(repo.changelog.revs(start=revsprepull))
1414 opts['_destspace'] = destspace
1434 opts['_destspace'] = destspace
1415 try:
1435 try:
1416 rebase(ui, repo, **opts)
1436 rebase(ui, repo, **opts)
1417 except error.NoMergeDestAbort:
1437 except error.NoMergeDestAbort:
1418 # we can maybe update instead
1438 # we can maybe update instead
1419 rev, _a, _b = destutil.destupdate(repo)
1439 rev, _a, _b = destutil.destupdate(repo)
1420 if rev == repo['.'].rev():
1440 if rev == repo['.'].rev():
1421 ui.status(_('nothing to rebase\n'))
1441 ui.status(_('nothing to rebase\n'))
1422 else:
1442 else:
1423 ui.status(_('nothing to rebase - updating instead\n'))
1443 ui.status(_('nothing to rebase - updating instead\n'))
1424 # not passing argument to get the bare update behavior
1444 # not passing argument to get the bare update behavior
1425 # with warning and trumpets
1445 # with warning and trumpets
1426 commands.update(ui, repo)
1446 commands.update(ui, repo)
1427 else:
1447 else:
1428 if opts.get('tool'):
1448 if opts.get('tool'):
1429 raise error.Abort(_('--tool can only be used with --rebase'))
1449 raise error.Abort(_('--tool can only be used with --rebase'))
1430 ret = orig(ui, repo, *args, **opts)
1450 ret = orig(ui, repo, *args, **opts)
1431
1451
1432 return ret
1452 return ret
1433
1453
1434 def _setrebasesetvisibility(repo, revs):
1454 def _setrebasesetvisibility(repo, revs):
1435 """store the currently rebased set on the repo object
1455 """store the currently rebased set on the repo object
1436
1456
1437 This is used by another function to prevent rebased revision to because
1457 This is used by another function to prevent rebased revision to because
1438 hidden (see issue4504)"""
1458 hidden (see issue4504)"""
1439 repo = repo.unfiltered()
1459 repo = repo.unfiltered()
1440 repo._rebaseset = revs
1460 repo._rebaseset = revs
1441 # invalidate cache if visibility changes
1461 # invalidate cache if visibility changes
1442 hiddens = repo.filteredrevcache.get('visible', set())
1462 hiddens = repo.filteredrevcache.get('visible', set())
1443 if revs & hiddens:
1463 if revs & hiddens:
1444 repo.invalidatevolatilesets()
1464 repo.invalidatevolatilesets()
1445
1465
1446 def _clearrebasesetvisibiliy(repo):
1466 def _clearrebasesetvisibiliy(repo):
1447 """remove rebaseset data from the repo"""
1467 """remove rebaseset data from the repo"""
1448 repo = repo.unfiltered()
1468 repo = repo.unfiltered()
1449 if '_rebaseset' in vars(repo):
1469 if '_rebaseset' in vars(repo):
1450 del repo._rebaseset
1470 del repo._rebaseset
1451
1471
1452 def _rebasedvisible(orig, repo):
1472 def _rebasedvisible(orig, repo):
1453 """ensure rebased revs stay visible (see issue4504)"""
1473 """ensure rebased revs stay visible (see issue4504)"""
1454 blockers = orig(repo)
1474 blockers = orig(repo)
1455 blockers.update(getattr(repo, '_rebaseset', ()))
1475 blockers.update(getattr(repo, '_rebaseset', ()))
1456 return blockers
1476 return blockers
1457
1477
1458 def _filterobsoleterevs(repo, revs):
1478 def _filterobsoleterevs(repo, revs):
1459 """returns a set of the obsolete revisions in revs"""
1479 """returns a set of the obsolete revisions in revs"""
1460 return set(r for r in revs if repo[r].obsolete())
1480 return set(r for r in revs if repo[r].obsolete())
1461
1481
1462 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1482 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1463 """return a mapping obsolete => successor for all obsolete nodes to be
1483 """return a mapping obsolete => successor for all obsolete nodes to be
1464 rebased that have a successors in the destination
1484 rebased that have a successors in the destination
1465
1485
1466 obsolete => None entries in the mapping indicate nodes with no successor"""
1486 obsolete => None entries in the mapping indicate nodes with no successor"""
1467 obsoletenotrebased = {}
1487 obsoletenotrebased = {}
1468
1488
1469 # Build a mapping successor => obsolete nodes for the obsolete
1489 # Build a mapping successor => obsolete nodes for the obsolete
1470 # nodes to be rebased
1490 # nodes to be rebased
1471 allsuccessors = {}
1491 allsuccessors = {}
1472 cl = repo.changelog
1492 cl = repo.changelog
1473 for r in rebaseobsrevs:
1493 for r in rebaseobsrevs:
1474 node = cl.node(r)
1494 node = cl.node(r)
1475 for s in obsutil.allsuccessors(repo.obsstore, [node]):
1495 for s in obsutil.allsuccessors(repo.obsstore, [node]):
1476 try:
1496 try:
1477 allsuccessors[cl.rev(s)] = cl.rev(node)
1497 allsuccessors[cl.rev(s)] = cl.rev(node)
1478 except LookupError:
1498 except LookupError:
1479 pass
1499 pass
1480
1500
1481 if allsuccessors:
1501 if allsuccessors:
1482 # Look for successors of obsolete nodes to be rebased among
1502 # Look for successors of obsolete nodes to be rebased among
1483 # the ancestors of dest
1503 # the ancestors of dest
1484 ancs = cl.ancestors([dest],
1504 ancs = cl.ancestors([dest],
1485 stoprev=min(allsuccessors),
1505 stoprev=min(allsuccessors),
1486 inclusive=True)
1506 inclusive=True)
1487 for s in allsuccessors:
1507 for s in allsuccessors:
1488 if s in ancs:
1508 if s in ancs:
1489 obsoletenotrebased[allsuccessors[s]] = s
1509 obsoletenotrebased[allsuccessors[s]] = s
1490 elif (s == allsuccessors[s] and
1510 elif (s == allsuccessors[s] and
1491 allsuccessors.values().count(s) == 1):
1511 allsuccessors.values().count(s) == 1):
1492 # plain prune
1512 # plain prune
1493 obsoletenotrebased[s] = None
1513 obsoletenotrebased[s] = None
1494
1514
1495 return obsoletenotrebased
1515 return obsoletenotrebased
1496
1516
1497 def summaryhook(ui, repo):
1517 def summaryhook(ui, repo):
1498 if not repo.vfs.exists('rebasestate'):
1518 if not repo.vfs.exists('rebasestate'):
1499 return
1519 return
1500 try:
1520 try:
1501 rbsrt = rebaseruntime(repo, ui, {})
1521 rbsrt = rebaseruntime(repo, ui, {})
1502 rbsrt.restorestatus()
1522 rbsrt.restorestatus()
1503 state = rbsrt.state
1523 state = rbsrt.state
1504 except error.RepoLookupError:
1524 except error.RepoLookupError:
1505 # i18n: column positioning for "hg summary"
1525 # i18n: column positioning for "hg summary"
1506 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1526 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1507 ui.write(msg)
1527 ui.write(msg)
1508 return
1528 return
1509 numrebased = len([i for i in state.itervalues() if i >= 0])
1529 numrebased = len([i for i in state.itervalues() if i >= 0])
1510 # i18n: column positioning for "hg summary"
1530 # i18n: column positioning for "hg summary"
1511 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1531 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1512 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1532 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1513 ui.label(_('%d remaining'), 'rebase.remaining') %
1533 ui.label(_('%d remaining'), 'rebase.remaining') %
1514 (len(state) - numrebased)))
1534 (len(state) - numrebased)))
1515
1535
1516 def uisetup(ui):
1536 def uisetup(ui):
1517 #Replace pull with a decorator to provide --rebase option
1537 #Replace pull with a decorator to provide --rebase option
1518 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1538 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1519 entry[1].append(('', 'rebase', None,
1539 entry[1].append(('', 'rebase', None,
1520 _("rebase working directory to branch head")))
1540 _("rebase working directory to branch head")))
1521 entry[1].append(('t', 'tool', '',
1541 entry[1].append(('t', 'tool', '',
1522 _("specify merge tool for rebase")))
1542 _("specify merge tool for rebase")))
1523 cmdutil.summaryhooks.add('rebase', summaryhook)
1543 cmdutil.summaryhooks.add('rebase', summaryhook)
1524 cmdutil.unfinishedstates.append(
1544 cmdutil.unfinishedstates.append(
1525 ['rebasestate', False, False, _('rebase in progress'),
1545 ['rebasestate', False, False, _('rebase in progress'),
1526 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1546 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1527 cmdutil.afterresolvedstates.append(
1547 cmdutil.afterresolvedstates.append(
1528 ['rebasestate', _('hg rebase --continue')])
1548 ['rebasestate', _('hg rebase --continue')])
1529 # ensure rebased rev are not hidden
1549 # ensure rebased rev are not hidden
1530 extensions.wrapfunction(repoview, 'pinnedrevs', _rebasedvisible)
1550 extensions.wrapfunction(repoview, 'pinnedrevs', _rebasedvisible)
@@ -1,434 +1,426 b''
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 > [alias]
7 > [alias]
8 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
8 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
9 > EOF
9 > EOF
10
10
11 $ hg init repo
11 $ hg init repo
12 $ cd repo
12 $ cd repo
13
13
14 $ echo A > a
14 $ echo A > a
15 $ echo >> a
15 $ echo >> a
16 $ hg ci -Am A
16 $ hg ci -Am A
17 adding a
17 adding a
18
18
19 $ echo B > a
19 $ echo B > a
20 $ echo >> a
20 $ echo >> a
21 $ hg ci -m B
21 $ hg ci -m B
22
22
23 $ echo C > a
23 $ echo C > a
24 $ echo >> a
24 $ echo >> a
25 $ hg ci -m C
25 $ hg ci -m C
26
26
27 $ hg up -q -C 0
27 $ hg up -q -C 0
28
28
29 $ echo D >> a
29 $ echo D >> a
30 $ hg ci -Am AD
30 $ hg ci -Am AD
31 created new head
31 created new head
32
32
33 $ hg tglog
33 $ hg tglog
34 @ 3: 'AD'
34 @ 3: 'AD'
35 |
35 |
36 | o 2: 'C'
36 | o 2: 'C'
37 | |
37 | |
38 | o 1: 'B'
38 | o 1: 'B'
39 |/
39 |/
40 o 0: 'A'
40 o 0: 'A'
41
41
42 $ hg rebase -s 1 -d 3
42 $ hg rebase -s 1 -d 3
43 rebasing 1:0f4f7cb4f549 "B"
43 rebasing 1:0f4f7cb4f549 "B"
44 merging a
44 merging a
45 rebasing 2:30ae917c0e4f "C"
45 rebasing 2:30ae917c0e4f "C"
46 merging a
46 merging a
47 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/0f4f7cb4f549-82b3b163-rebase.hg (glob)
47 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/0f4f7cb4f549-82b3b163-rebase.hg (glob)
48
48
49 $ hg tglog
49 $ hg tglog
50 o 3: 'C'
50 o 3: 'C'
51 |
51 |
52 o 2: 'B'
52 o 2: 'B'
53 |
53 |
54 @ 1: 'AD'
54 @ 1: 'AD'
55 |
55 |
56 o 0: 'A'
56 o 0: 'A'
57
57
58
58
59 $ cd ..
59 $ cd ..
60
60
61
61
62 Test rebasing of merges with ancestors of the rebase destination - a situation
62 Test rebasing of merges with ancestors of the rebase destination - a situation
63 that often happens when trying to recover from repeated merging with a mainline
63 that often happens when trying to recover from repeated merging with a mainline
64 branch.
64 branch.
65
65
66 The test case creates a dev branch that contains a couple of merges from the
66 The test case creates a dev branch that contains a couple of merges from the
67 default branch. When rebasing to the default branch, these merges would be
67 default branch. When rebasing to the default branch, these merges would be
68 merges with ancestors on the same branch. The merges _could_ contain some
68 merges with ancestors on the same branch. The merges _could_ contain some
69 interesting conflict resolutions or additional changes in the merge commit, but
69 interesting conflict resolutions or additional changes in the merge commit, but
70 that is mixed up with the actual merge stuff and there is in general no way to
70 that is mixed up with the actual merge stuff and there is in general no way to
71 separate them.
71 separate them.
72
72
73 Note: The dev branch contains _no_ changes to f-default. It might be unclear
73 Note: The dev branch contains _no_ changes to f-default. It might be unclear
74 how rebasing of ancestor merges should be handled, but the current behavior
74 how rebasing of ancestor merges should be handled, but the current behavior
75 with spurious prompts for conflicts in files that didn't change seems very
75 with spurious prompts for conflicts in files that didn't change seems very
76 wrong.
76 wrong.
77
77
78 $ hg init ancestor-merge
78 $ hg init ancestor-merge
79 $ cd ancestor-merge
79 $ cd ancestor-merge
80
80
81 $ touch f-default
81 $ touch f-default
82 $ hg ci -Aqm 'default: create f-default'
82 $ hg ci -Aqm 'default: create f-default'
83
83
84 $ hg branch -q dev
84 $ hg branch -q dev
85 $ hg ci -qm 'dev: create branch'
85 $ hg ci -qm 'dev: create branch'
86
86
87 $ echo stuff > f-dev
87 $ echo stuff > f-dev
88 $ hg ci -Aqm 'dev: f-dev stuff'
88 $ hg ci -Aqm 'dev: f-dev stuff'
89
89
90 $ hg up -q default
90 $ hg up -q default
91 $ echo stuff > f-default
91 $ echo stuff > f-default
92 $ hg ci -m 'default: f-default stuff'
92 $ hg ci -m 'default: f-default stuff'
93
93
94 $ hg up -q dev
94 $ hg up -q dev
95 $ hg merge -q default
95 $ hg merge -q default
96 $ hg ci -m 'dev: merge default'
96 $ hg ci -m 'dev: merge default'
97
97
98 $ hg up -q default
98 $ hg up -q default
99 $ hg rm f-default
99 $ hg rm f-default
100 $ hg ci -m 'default: remove f-default'
100 $ hg ci -m 'default: remove f-default'
101
101
102 $ hg up -q dev
102 $ hg up -q dev
103 $ hg merge -q default
103 $ hg merge -q default
104 $ hg ci -m 'dev: merge default'
104 $ hg ci -m 'dev: merge default'
105
105
106 $ hg up -q default
106 $ hg up -q default
107 $ echo stuff > f-other
107 $ echo stuff > f-other
108 $ hg ci -Aqm 'default: f-other stuff'
108 $ hg ci -Aqm 'default: f-other stuff'
109
109
110 $ hg tglog
110 $ hg tglog
111 @ 7: 'default: f-other stuff'
111 @ 7: 'default: f-other stuff'
112 |
112 |
113 | o 6: 'dev: merge default' dev
113 | o 6: 'dev: merge default' dev
114 |/|
114 |/|
115 o | 5: 'default: remove f-default'
115 o | 5: 'default: remove f-default'
116 | |
116 | |
117 | o 4: 'dev: merge default' dev
117 | o 4: 'dev: merge default' dev
118 |/|
118 |/|
119 o | 3: 'default: f-default stuff'
119 o | 3: 'default: f-default stuff'
120 | |
120 | |
121 | o 2: 'dev: f-dev stuff' dev
121 | o 2: 'dev: f-dev stuff' dev
122 | |
122 | |
123 | o 1: 'dev: create branch' dev
123 | o 1: 'dev: create branch' dev
124 |/
124 |/
125 o 0: 'default: create f-default'
125 o 0: 'default: create f-default'
126
126
127 $ hg clone -qU . ../ancestor-merge-2
127 $ hg clone -qU . ../ancestor-merge-2
128
128
129 Full rebase all the way back from branching point:
129 Full rebase all the way back from branching point:
130
130
131 $ hg rebase -r 'only(dev,default)' -d default --config ui.interactive=True << EOF
131 $ hg rebase -r 'only(dev,default)' -d default --config ui.interactive=True << EOF
132 > c
132 > c
133 > EOF
133 > EOF
134 rebasing 1:1d1a643d390e "dev: create branch"
134 rebasing 1:1d1a643d390e "dev: create branch"
135 note: rebase of 1:1d1a643d390e created no changes to commit
135 note: rebase of 1:1d1a643d390e created no changes to commit
136 rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
136 rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
137 rebasing 4:4b019212aaf6 "dev: merge default"
137 rebasing 4:4b019212aaf6 "dev: merge default"
138 other [source] changed f-default which local [dest] deleted
138 other [source] changed f-default which local [dest] deleted
139 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
139 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
140 rebasing 6:9455ee510502 "dev: merge default"
140 rebasing 6:9455ee510502 "dev: merge default"
141 saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-43e9e04b-rebase.hg (glob)
141 saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-43e9e04b-rebase.hg (glob)
142 $ hg tglog
142 $ hg tglog
143 o 6: 'dev: merge default'
143 o 6: 'dev: merge default'
144 |
144 |
145 o 5: 'dev: merge default'
145 o 5: 'dev: merge default'
146 |
146 |
147 o 4: 'dev: f-dev stuff'
147 o 4: 'dev: f-dev stuff'
148 |
148 |
149 @ 3: 'default: f-other stuff'
149 @ 3: 'default: f-other stuff'
150 |
150 |
151 o 2: 'default: remove f-default'
151 o 2: 'default: remove f-default'
152 |
152 |
153 o 1: 'default: f-default stuff'
153 o 1: 'default: f-default stuff'
154 |
154 |
155 o 0: 'default: create f-default'
155 o 0: 'default: create f-default'
156
156
157 Grafty cherry picking rebasing:
157 Grafty cherry picking rebasing:
158
158
159 $ cd ../ancestor-merge-2
159 $ cd ../ancestor-merge-2
160
160
161 $ hg phase -fdr0:
161 $ hg phase -fdr0:
162 $ hg rebase -r 'children(only(dev,default))' -d default --config ui.interactive=True << EOF
162 $ hg rebase -r 'children(only(dev,default))' -d default --config ui.interactive=True << EOF
163 > c
163 > c
164 > EOF
164 > EOF
165 rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
165 rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
166 rebasing 4:4b019212aaf6 "dev: merge default"
166 rebasing 4:4b019212aaf6 "dev: merge default"
167 other [source] changed f-default which local [dest] deleted
167 other [source] changed f-default which local [dest] deleted
168 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
168 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
169 rebasing 6:9455ee510502 "dev: merge default"
169 rebasing 6:9455ee510502 "dev: merge default"
170 saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-62d0b222-rebase.hg (glob)
170 saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-62d0b222-rebase.hg (glob)
171 $ hg tglog
171 $ hg tglog
172 o 7: 'dev: merge default'
172 o 7: 'dev: merge default'
173 |
173 |
174 o 6: 'dev: merge default'
174 o 6: 'dev: merge default'
175 |
175 |
176 o 5: 'dev: f-dev stuff'
176 o 5: 'dev: f-dev stuff'
177 |
177 |
178 o 4: 'default: f-other stuff'
178 o 4: 'default: f-other stuff'
179 |
179 |
180 o 3: 'default: remove f-default'
180 o 3: 'default: remove f-default'
181 |
181 |
182 o 2: 'default: f-default stuff'
182 o 2: 'default: f-default stuff'
183 |
183 |
184 | o 1: 'dev: create branch' dev
184 | o 1: 'dev: create branch' dev
185 |/
185 |/
186 o 0: 'default: create f-default'
186 o 0: 'default: create f-default'
187
187
188 $ cd ..
188 $ cd ..
189
189
190
190
191 Test order of parents of rebased merged with un-rebased changes as p1.
191 Test order of parents of rebased merged with un-rebased changes as p1.
192
192
193 $ hg init parentorder
193 $ hg init parentorder
194 $ cd parentorder
194 $ cd parentorder
195 $ touch f
195 $ touch f
196 $ hg ci -Aqm common
196 $ hg ci -Aqm common
197 $ touch change
197 $ touch change
198 $ hg ci -Aqm change
198 $ hg ci -Aqm change
199 $ touch target
199 $ touch target
200 $ hg ci -Aqm target
200 $ hg ci -Aqm target
201 $ hg up -qr 0
201 $ hg up -qr 0
202 $ touch outside
202 $ touch outside
203 $ hg ci -Aqm outside
203 $ hg ci -Aqm outside
204 $ hg merge -qr 1
204 $ hg merge -qr 1
205 $ hg ci -m 'merge p1 3=outside p2 1=ancestor'
205 $ hg ci -m 'merge p1 3=outside p2 1=ancestor'
206 $ hg par
206 $ hg par
207 changeset: 4:6990226659be
207 changeset: 4:6990226659be
208 tag: tip
208 tag: tip
209 parent: 3:f59da8fc0fcf
209 parent: 3:f59da8fc0fcf
210 parent: 1:dd40c13f7a6f
210 parent: 1:dd40c13f7a6f
211 user: test
211 user: test
212 date: Thu Jan 01 00:00:00 1970 +0000
212 date: Thu Jan 01 00:00:00 1970 +0000
213 summary: merge p1 3=outside p2 1=ancestor
213 summary: merge p1 3=outside p2 1=ancestor
214
214
215 $ hg up -qr 1
215 $ hg up -qr 1
216 $ hg merge -qr 3
216 $ hg merge -qr 3
217 $ hg ci -qm 'merge p1 1=ancestor p2 3=outside'
217 $ hg ci -qm 'merge p1 1=ancestor p2 3=outside'
218 $ hg par
218 $ hg par
219 changeset: 5:a57575f79074
219 changeset: 5:a57575f79074
220 tag: tip
220 tag: tip
221 parent: 1:dd40c13f7a6f
221 parent: 1:dd40c13f7a6f
222 parent: 3:f59da8fc0fcf
222 parent: 3:f59da8fc0fcf
223 user: test
223 user: test
224 date: Thu Jan 01 00:00:00 1970 +0000
224 date: Thu Jan 01 00:00:00 1970 +0000
225 summary: merge p1 1=ancestor p2 3=outside
225 summary: merge p1 1=ancestor p2 3=outside
226
226
227 $ hg tglog
227 $ hg tglog
228 @ 5: 'merge p1 1=ancestor p2 3=outside'
228 @ 5: 'merge p1 1=ancestor p2 3=outside'
229 |\
229 |\
230 +---o 4: 'merge p1 3=outside p2 1=ancestor'
230 +---o 4: 'merge p1 3=outside p2 1=ancestor'
231 | |/
231 | |/
232 | o 3: 'outside'
232 | o 3: 'outside'
233 | |
233 | |
234 +---o 2: 'target'
234 +---o 2: 'target'
235 | |
235 | |
236 o | 1: 'change'
236 o | 1: 'change'
237 |/
237 |/
238 o 0: 'common'
238 o 0: 'common'
239
239
240 $ hg rebase -r 4 -d 2
240 $ hg rebase -r 4 -d 2
241 rebasing 4:6990226659be "merge p1 3=outside p2 1=ancestor"
241 rebasing 4:6990226659be "merge p1 3=outside p2 1=ancestor"
242 saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/6990226659be-4d67a0d3-rebase.hg (glob)
242 saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/6990226659be-4d67a0d3-rebase.hg (glob)
243 $ hg tip
243 $ hg tip
244 changeset: 5:cca50676b1c5
244 changeset: 5:cca50676b1c5
245 tag: tip
245 tag: tip
246 parent: 2:a60552eb93fb
246 parent: 2:a60552eb93fb
247 parent: 3:f59da8fc0fcf
247 parent: 3:f59da8fc0fcf
248 user: test
248 user: test
249 date: Thu Jan 01 00:00:00 1970 +0000
249 date: Thu Jan 01 00:00:00 1970 +0000
250 summary: merge p1 3=outside p2 1=ancestor
250 summary: merge p1 3=outside p2 1=ancestor
251
251
252 $ hg rebase -r 4 -d 2
252 $ hg rebase -r 4 -d 2
253 rebasing 4:a57575f79074 "merge p1 1=ancestor p2 3=outside"
253 rebasing 4:a57575f79074 "merge p1 1=ancestor p2 3=outside"
254 saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/a57575f79074-385426e5-rebase.hg (glob)
254 saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/a57575f79074-385426e5-rebase.hg (glob)
255 $ hg tip
255 $ hg tip
256 changeset: 5:f9daf77ffe76
256 changeset: 5:f9daf77ffe76
257 tag: tip
257 tag: tip
258 parent: 2:a60552eb93fb
258 parent: 2:a60552eb93fb
259 parent: 3:f59da8fc0fcf
259 parent: 3:f59da8fc0fcf
260 user: test
260 user: test
261 date: Thu Jan 01 00:00:00 1970 +0000
261 date: Thu Jan 01 00:00:00 1970 +0000
262 summary: merge p1 1=ancestor p2 3=outside
262 summary: merge p1 1=ancestor p2 3=outside
263
263
264 $ hg tglog
264 $ hg tglog
265 @ 5: 'merge p1 1=ancestor p2 3=outside'
265 @ 5: 'merge p1 1=ancestor p2 3=outside'
266 |\
266 |\
267 +---o 4: 'merge p1 3=outside p2 1=ancestor'
267 +---o 4: 'merge p1 3=outside p2 1=ancestor'
268 | |/
268 | |/
269 | o 3: 'outside'
269 | o 3: 'outside'
270 | |
270 | |
271 o | 2: 'target'
271 o | 2: 'target'
272 | |
272 | |
273 o | 1: 'change'
273 o | 1: 'change'
274 |/
274 |/
275 o 0: 'common'
275 o 0: 'common'
276
276
277 rebase of merge of ancestors
277 rebase of merge of ancestors
278
278
279 $ hg up -qr 2
279 $ hg up -qr 2
280 $ hg merge -qr 3
280 $ hg merge -qr 3
281 $ echo 'other change while merging future "rebase ancestors"' > other
281 $ echo 'other change while merging future "rebase ancestors"' > other
282 $ hg ci -Aqm 'merge rebase ancestors'
282 $ hg ci -Aqm 'merge rebase ancestors'
283 $ hg rebase -d 5 -v
283 $ hg rebase -d 5 -v
284 rebasing 6:4c5f12f25ebe "merge rebase ancestors" (tip)
284 rebasing 6:4c5f12f25ebe "merge rebase ancestors" (tip)
285 resolving manifests
285 resolving manifests
286 removing other
286 removing other
287 note: merging f9daf77ffe76+ and 4c5f12f25ebe using bids from ancestors a60552eb93fb and f59da8fc0fcf
287 note: merging f9daf77ffe76+ and 4c5f12f25ebe using bids from ancestors a60552eb93fb and f59da8fc0fcf
288
288
289 calculating bids for ancestor a60552eb93fb
289 calculating bids for ancestor a60552eb93fb
290 resolving manifests
290 resolving manifests
291
291
292 calculating bids for ancestor f59da8fc0fcf
292 calculating bids for ancestor f59da8fc0fcf
293 resolving manifests
293 resolving manifests
294
294
295 auction for merging merge bids
295 auction for merging merge bids
296 other: consensus for g
296 other: consensus for g
297 end of auction
297 end of auction
298
298
299 getting other
299 getting other
300 committing files:
300 committing files:
301 other
301 other
302 committing manifest
302 committing manifest
303 committing changelog
303 committing changelog
304 rebase merging completed
304 rebase merging completed
305 1 changesets found
305 1 changesets found
306 uncompressed size of bundle content:
306 uncompressed size of bundle content:
307 199 (changelog)
307 199 (changelog)
308 216 (manifests)
308 216 (manifests)
309 182 other
309 182 other
310 saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/4c5f12f25ebe-f46990e5-rebase.hg (glob)
310 saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/4c5f12f25ebe-f46990e5-rebase.hg (glob)
311 1 changesets found
311 1 changesets found
312 uncompressed size of bundle content:
312 uncompressed size of bundle content:
313 254 (changelog)
313 254 (changelog)
314 167 (manifests)
314 167 (manifests)
315 182 other
315 182 other
316 adding branch
316 adding branch
317 adding changesets
317 adding changesets
318 adding manifests
318 adding manifests
319 adding file changes
319 adding file changes
320 added 1 changesets with 1 changes to 1 files
320 added 1 changesets with 1 changes to 1 files
321 rebase completed
321 rebase completed
322 $ hg tglog
322 $ hg tglog
323 @ 6: 'merge rebase ancestors'
323 @ 6: 'merge rebase ancestors'
324 |
324 |
325 o 5: 'merge p1 1=ancestor p2 3=outside'
325 o 5: 'merge p1 1=ancestor p2 3=outside'
326 |\
326 |\
327 +---o 4: 'merge p1 3=outside p2 1=ancestor'
327 +---o 4: 'merge p1 3=outside p2 1=ancestor'
328 | |/
328 | |/
329 | o 3: 'outside'
329 | o 3: 'outside'
330 | |
330 | |
331 o | 2: 'target'
331 o | 2: 'target'
332 | |
332 | |
333 o | 1: 'change'
333 o | 1: 'change'
334 |/
334 |/
335 o 0: 'common'
335 o 0: 'common'
336
336
337 Due to the limitation of 3-way merge algorithm (1 merge base), rebasing a merge
337 Due to the limitation of 3-way merge algorithm (1 merge base), rebasing a merge
338 may include unwanted content:
338 may include unwanted content:
339
339
340 $ hg init $TESTTMP/dual-merge-base1
340 $ hg init $TESTTMP/dual-merge-base1
341 $ cd $TESTTMP/dual-merge-base1
341 $ cd $TESTTMP/dual-merge-base1
342 $ hg debugdrawdag <<'EOS'
342 $ hg debugdrawdag <<'EOS'
343 > F
343 > F
344 > /|
344 > /|
345 > D E
345 > D E
346 > | |
346 > | |
347 > B C
347 > B C
348 > |/
348 > |/
349 > A Z
349 > A Z
350 > |/
350 > |/
351 > R
351 > R
352 > EOS
352 > EOS
353 $ hg rebase -r D+E+F -d Z
353 $ hg rebase -r D+E+F -d Z
354 rebasing 5:5f2c926dfecf "D" (D)
354 rebasing 5:5f2c926dfecf "D" (D)
355 rebasing 6:b296604d9846 "E" (E)
355 rebasing 6:b296604d9846 "E" (E)
356 rebasing 7:caa9781e507d "F" (F tip)
356 rebasing 7:caa9781e507d "F" (F tip)
357 warning: rebasing 7:caa9781e507d may include unwanted changes from 4:d6003a550c2c
357 abort: rebasing 7:caa9781e507d will include unwanted changes from 4:d6003a550c2c or 3:c1e6b162678d
358 saved backup bundle to $TESTTMP/dual-merge-base1/.hg/strip-backup/b296604d9846-0516f6d2-rebase.hg (glob)
358 [255]
359 $ hg log -r 4 -T '{files}\n'
360 C
361 $ hg manifest -r 'desc(F)'
362 C
363 D
364 E
365 R
366 Z
367
359
368 The warning does not get printed if there is no unwanted change detected:
360 The warning does not get printed if there is no unwanted change detected:
369
361
370 $ hg init $TESTTMP/dual-merge-base2
362 $ hg init $TESTTMP/dual-merge-base2
371 $ cd $TESTTMP/dual-merge-base2
363 $ cd $TESTTMP/dual-merge-base2
372 $ hg debugdrawdag <<'EOS'
364 $ hg debugdrawdag <<'EOS'
373 > D
365 > D
374 > /|
366 > /|
375 > B C
367 > B C
376 > |/
368 > |/
377 > A Z
369 > A Z
378 > |/
370 > |/
379 > R
371 > R
380 > EOS
372 > EOS
381 $ hg rebase -r B+C+D -d Z
373 $ hg rebase -r B+C+D -d Z
382 rebasing 3:c1e6b162678d "B" (B)
374 rebasing 3:c1e6b162678d "B" (B)
383 rebasing 4:d6003a550c2c "C" (C)
375 rebasing 4:d6003a550c2c "C" (C)
384 rebasing 5:c8f78076273e "D" (D tip)
376 rebasing 5:c8f78076273e "D" (D tip)
385 saved backup bundle to $TESTTMP/dual-merge-base2/.hg/strip-backup/d6003a550c2c-6f1424b6-rebase.hg (glob)
377 saved backup bundle to $TESTTMP/dual-merge-base2/.hg/strip-backup/d6003a550c2c-6f1424b6-rebase.hg (glob)
386 $ hg manifest -r 'desc(D)'
378 $ hg manifest -r 'desc(D)'
387 B
379 B
388 C
380 C
389 R
381 R
390 Z
382 Z
391
383
392 The merge base could be different from old p1 (changed parent becomes new p1):
384 The merge base could be different from old p1 (changed parent becomes new p1):
393
385
394 $ hg init $TESTTMP/chosen-merge-base1
386 $ hg init $TESTTMP/chosen-merge-base1
395 $ cd $TESTTMP/chosen-merge-base1
387 $ cd $TESTTMP/chosen-merge-base1
396 $ hg debugdrawdag <<'EOS'
388 $ hg debugdrawdag <<'EOS'
397 > F
389 > F
398 > /|
390 > /|
399 > D E
391 > D E
400 > | |
392 > | |
401 > B C Z
393 > B C Z
402 > EOS
394 > EOS
403 $ hg rebase -r D+F -d Z
395 $ hg rebase -r D+F -d Z
404 rebasing 3:004dc1679908 "D" (D)
396 rebasing 3:004dc1679908 "D" (D)
405 rebasing 5:4be4cbf6f206 "F" (F tip)
397 rebasing 5:4be4cbf6f206 "F" (F tip)
406 saved backup bundle to $TESTTMP/chosen-merge-base1/.hg/strip-backup/004dc1679908-06a66a3c-rebase.hg (glob)
398 saved backup bundle to $TESTTMP/chosen-merge-base1/.hg/strip-backup/004dc1679908-06a66a3c-rebase.hg (glob)
407 $ hg manifest -r 'desc(F)'
399 $ hg manifest -r 'desc(F)'
408 C
400 C
409 D
401 D
410 E
402 E
411 Z
403 Z
412 $ hg log -r `hg log -r 'desc(F)' -T '{p1node}'` -T '{desc}\n'
404 $ hg log -r `hg log -r 'desc(F)' -T '{p1node}'` -T '{desc}\n'
413 D
405 D
414
406
415 $ hg init $TESTTMP/chosen-merge-base2
407 $ hg init $TESTTMP/chosen-merge-base2
416 $ cd $TESTTMP/chosen-merge-base2
408 $ cd $TESTTMP/chosen-merge-base2
417 $ hg debugdrawdag <<'EOS'
409 $ hg debugdrawdag <<'EOS'
418 > F
410 > F
419 > /|
411 > /|
420 > D E
412 > D E
421 > | |
413 > | |
422 > B C Z
414 > B C Z
423 > EOS
415 > EOS
424 $ hg rebase -r E+F -d Z
416 $ hg rebase -r E+F -d Z
425 rebasing 4:974e4943c210 "E" (E)
417 rebasing 4:974e4943c210 "E" (E)
426 rebasing 5:4be4cbf6f206 "F" (F tip)
418 rebasing 5:4be4cbf6f206 "F" (F tip)
427 saved backup bundle to $TESTTMP/chosen-merge-base2/.hg/strip-backup/974e4943c210-b2874da5-rebase.hg (glob)
419 saved backup bundle to $TESTTMP/chosen-merge-base2/.hg/strip-backup/974e4943c210-b2874da5-rebase.hg (glob)
428 $ hg manifest -r 'desc(F)'
420 $ hg manifest -r 'desc(F)'
429 B
421 B
430 D
422 D
431 E
423 E
432 Z
424 Z
433 $ hg log -r `hg log -r 'desc(F)' -T '{p1node}'` -T '{desc}\n'
425 $ hg log -r `hg log -r 'desc(F)' -T '{p1node}'` -T '{desc}\n'
434 E
426 E
@@ -1,1164 +1,1242 b''
1 ==========================
1 ==========================
2 Test rebase with obsolete
2 Test rebase with obsolete
3 ==========================
3 ==========================
4
4
5 Enable obsolete
5 Enable obsolete
6
6
7 $ cat >> $HGRCPATH << EOF
7 $ cat >> $HGRCPATH << EOF
8 > [ui]
8 > [ui]
9 > logtemplate= {rev}:{node|short} {desc|firstline}
9 > logtemplate= {rev}:{node|short} {desc|firstline}
10 > [experimental]
10 > [experimental]
11 > stabilization=createmarkers,allowunstable
11 > stabilization=createmarkers,allowunstable
12 > [phases]
12 > [phases]
13 > publish=False
13 > publish=False
14 > [extensions]
14 > [extensions]
15 > rebase=
15 > rebase=
16 > drawdag=$TESTDIR/drawdag.py
16 > drawdag=$TESTDIR/drawdag.py
17 > EOF
17 > EOF
18
18
19 Setup rebase canonical repo
19 Setup rebase canonical repo
20
20
21 $ hg init base
21 $ hg init base
22 $ cd base
22 $ cd base
23 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
23 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
24 adding changesets
24 adding changesets
25 adding manifests
25 adding manifests
26 adding file changes
26 adding file changes
27 added 8 changesets with 7 changes to 7 files (+2 heads)
27 added 8 changesets with 7 changes to 7 files (+2 heads)
28 (run 'hg heads' to see heads, 'hg merge' to merge)
28 (run 'hg heads' to see heads, 'hg merge' to merge)
29 $ hg up tip
29 $ hg up tip
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 $ hg log -G
31 $ hg log -G
32 @ 7:02de42196ebe H
32 @ 7:02de42196ebe H
33 |
33 |
34 | o 6:eea13746799a G
34 | o 6:eea13746799a G
35 |/|
35 |/|
36 o | 5:24b6387c8c8c F
36 o | 5:24b6387c8c8c F
37 | |
37 | |
38 | o 4:9520eea781bc E
38 | o 4:9520eea781bc E
39 |/
39 |/
40 | o 3:32af7686d403 D
40 | o 3:32af7686d403 D
41 | |
41 | |
42 | o 2:5fddd98957c8 C
42 | o 2:5fddd98957c8 C
43 | |
43 | |
44 | o 1:42ccdea3bb16 B
44 | o 1:42ccdea3bb16 B
45 |/
45 |/
46 o 0:cd010b8cd998 A
46 o 0:cd010b8cd998 A
47
47
48 $ cd ..
48 $ cd ..
49
49
50 simple rebase
50 simple rebase
51 ---------------------------------
51 ---------------------------------
52
52
53 $ hg clone base simple
53 $ hg clone base simple
54 updating to branch default
54 updating to branch default
55 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 $ cd simple
56 $ cd simple
57 $ hg up 32af7686d403
57 $ hg up 32af7686d403
58 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
58 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
59 $ hg rebase -d eea13746799a
59 $ hg rebase -d eea13746799a
60 rebasing 1:42ccdea3bb16 "B"
60 rebasing 1:42ccdea3bb16 "B"
61 rebasing 2:5fddd98957c8 "C"
61 rebasing 2:5fddd98957c8 "C"
62 rebasing 3:32af7686d403 "D"
62 rebasing 3:32af7686d403 "D"
63 $ hg log -G
63 $ hg log -G
64 @ 10:8eeb3c33ad33 D
64 @ 10:8eeb3c33ad33 D
65 |
65 |
66 o 9:2327fea05063 C
66 o 9:2327fea05063 C
67 |
67 |
68 o 8:e4e5be0395b2 B
68 o 8:e4e5be0395b2 B
69 |
69 |
70 | o 7:02de42196ebe H
70 | o 7:02de42196ebe H
71 | |
71 | |
72 o | 6:eea13746799a G
72 o | 6:eea13746799a G
73 |\|
73 |\|
74 | o 5:24b6387c8c8c F
74 | o 5:24b6387c8c8c F
75 | |
75 | |
76 o | 4:9520eea781bc E
76 o | 4:9520eea781bc E
77 |/
77 |/
78 o 0:cd010b8cd998 A
78 o 0:cd010b8cd998 A
79
79
80 $ hg log --hidden -G
80 $ hg log --hidden -G
81 @ 10:8eeb3c33ad33 D
81 @ 10:8eeb3c33ad33 D
82 |
82 |
83 o 9:2327fea05063 C
83 o 9:2327fea05063 C
84 |
84 |
85 o 8:e4e5be0395b2 B
85 o 8:e4e5be0395b2 B
86 |
86 |
87 | o 7:02de42196ebe H
87 | o 7:02de42196ebe H
88 | |
88 | |
89 o | 6:eea13746799a G
89 o | 6:eea13746799a G
90 |\|
90 |\|
91 | o 5:24b6387c8c8c F
91 | o 5:24b6387c8c8c F
92 | |
92 | |
93 o | 4:9520eea781bc E
93 o | 4:9520eea781bc E
94 |/
94 |/
95 | x 3:32af7686d403 D
95 | x 3:32af7686d403 D
96 | |
96 | |
97 | x 2:5fddd98957c8 C
97 | x 2:5fddd98957c8 C
98 | |
98 | |
99 | x 1:42ccdea3bb16 B
99 | x 1:42ccdea3bb16 B
100 |/
100 |/
101 o 0:cd010b8cd998 A
101 o 0:cd010b8cd998 A
102
102
103 $ hg debugobsolete
103 $ hg debugobsolete
104 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (*) {'user': 'test'} (glob)
104 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (*) {'user': 'test'} (glob)
105 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (*) {'user': 'test'} (glob)
105 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (*) {'user': 'test'} (glob)
106 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (*) {'user': 'test'} (glob)
106 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (*) {'user': 'test'} (glob)
107
107
108
108
109 $ cd ..
109 $ cd ..
110
110
111 empty changeset
111 empty changeset
112 ---------------------------------
112 ---------------------------------
113
113
114 $ hg clone base empty
114 $ hg clone base empty
115 updating to branch default
115 updating to branch default
116 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 $ cd empty
117 $ cd empty
118 $ hg up eea13746799a
118 $ hg up eea13746799a
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
120
120
121 We make a copy of both the first changeset in the rebased and some other in the
121 We make a copy of both the first changeset in the rebased and some other in the
122 set.
122 set.
123
123
124 $ hg graft 42ccdea3bb16 32af7686d403
124 $ hg graft 42ccdea3bb16 32af7686d403
125 grafting 1:42ccdea3bb16 "B"
125 grafting 1:42ccdea3bb16 "B"
126 grafting 3:32af7686d403 "D"
126 grafting 3:32af7686d403 "D"
127 $ hg rebase -s 42ccdea3bb16 -d .
127 $ hg rebase -s 42ccdea3bb16 -d .
128 rebasing 1:42ccdea3bb16 "B"
128 rebasing 1:42ccdea3bb16 "B"
129 note: rebase of 1:42ccdea3bb16 created no changes to commit
129 note: rebase of 1:42ccdea3bb16 created no changes to commit
130 rebasing 2:5fddd98957c8 "C"
130 rebasing 2:5fddd98957c8 "C"
131 rebasing 3:32af7686d403 "D"
131 rebasing 3:32af7686d403 "D"
132 note: rebase of 3:32af7686d403 created no changes to commit
132 note: rebase of 3:32af7686d403 created no changes to commit
133 $ hg log -G
133 $ hg log -G
134 o 10:5ae4c968c6ac C
134 o 10:5ae4c968c6ac C
135 |
135 |
136 @ 9:08483444fef9 D
136 @ 9:08483444fef9 D
137 |
137 |
138 o 8:8877864f1edb B
138 o 8:8877864f1edb B
139 |
139 |
140 | o 7:02de42196ebe H
140 | o 7:02de42196ebe H
141 | |
141 | |
142 o | 6:eea13746799a G
142 o | 6:eea13746799a G
143 |\|
143 |\|
144 | o 5:24b6387c8c8c F
144 | o 5:24b6387c8c8c F
145 | |
145 | |
146 o | 4:9520eea781bc E
146 o | 4:9520eea781bc E
147 |/
147 |/
148 o 0:cd010b8cd998 A
148 o 0:cd010b8cd998 A
149
149
150 $ hg log --hidden -G
150 $ hg log --hidden -G
151 o 10:5ae4c968c6ac C
151 o 10:5ae4c968c6ac C
152 |
152 |
153 @ 9:08483444fef9 D
153 @ 9:08483444fef9 D
154 |
154 |
155 o 8:8877864f1edb B
155 o 8:8877864f1edb B
156 |
156 |
157 | o 7:02de42196ebe H
157 | o 7:02de42196ebe H
158 | |
158 | |
159 o | 6:eea13746799a G
159 o | 6:eea13746799a G
160 |\|
160 |\|
161 | o 5:24b6387c8c8c F
161 | o 5:24b6387c8c8c F
162 | |
162 | |
163 o | 4:9520eea781bc E
163 o | 4:9520eea781bc E
164 |/
164 |/
165 | x 3:32af7686d403 D
165 | x 3:32af7686d403 D
166 | |
166 | |
167 | x 2:5fddd98957c8 C
167 | x 2:5fddd98957c8 C
168 | |
168 | |
169 | x 1:42ccdea3bb16 B
169 | x 1:42ccdea3bb16 B
170 |/
170 |/
171 o 0:cd010b8cd998 A
171 o 0:cd010b8cd998 A
172
172
173 $ hg debugobsolete
173 $ hg debugobsolete
174 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
174 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
175 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
175 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
176 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
176 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
177
177
178
178
179 More complex case where part of the rebase set were already rebased
179 More complex case where part of the rebase set were already rebased
180
180
181 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
181 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
182 rebasing 9:08483444fef9 "D"
182 rebasing 9:08483444fef9 "D"
183 $ hg debugobsolete
183 $ hg debugobsolete
184 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
184 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
185 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
185 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
186 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
186 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
187 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
187 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
188 $ hg log -G
188 $ hg log -G
189 @ 11:4596109a6a43 D
189 @ 11:4596109a6a43 D
190 |
190 |
191 | o 10:5ae4c968c6ac C
191 | o 10:5ae4c968c6ac C
192 | |
192 | |
193 | x 9:08483444fef9 D
193 | x 9:08483444fef9 D
194 | |
194 | |
195 | o 8:8877864f1edb B
195 | o 8:8877864f1edb B
196 | |
196 | |
197 o | 7:02de42196ebe H
197 o | 7:02de42196ebe H
198 | |
198 | |
199 | o 6:eea13746799a G
199 | o 6:eea13746799a G
200 |/|
200 |/|
201 o | 5:24b6387c8c8c F
201 o | 5:24b6387c8c8c F
202 | |
202 | |
203 | o 4:9520eea781bc E
203 | o 4:9520eea781bc E
204 |/
204 |/
205 o 0:cd010b8cd998 A
205 o 0:cd010b8cd998 A
206
206
207 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
207 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
208 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D"
208 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 "D"
209 rebasing 8:8877864f1edb "B"
209 rebasing 8:8877864f1edb "B"
210 rebasing 10:5ae4c968c6ac "C"
210 rebasing 10:5ae4c968c6ac "C"
211 $ hg debugobsolete
211 $ hg debugobsolete
212 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
212 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (*) {'user': 'test'} (glob)
213 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
213 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (*) {'user': 'test'} (glob)
214 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
214 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (*) {'user': 'test'} (glob)
215 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
215 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (*) {'user': 'test'} (glob)
216 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob)
216 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (*) {'user': 'test'} (glob)
217 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob)
217 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (*) {'user': 'test'} (glob)
218 $ hg log --rev 'contentdivergent()'
218 $ hg log --rev 'contentdivergent()'
219 $ hg log -G
219 $ hg log -G
220 o 13:98f6af4ee953 C
220 o 13:98f6af4ee953 C
221 |
221 |
222 o 12:462a34d07e59 B
222 o 12:462a34d07e59 B
223 |
223 |
224 @ 11:4596109a6a43 D
224 @ 11:4596109a6a43 D
225 |
225 |
226 o 7:02de42196ebe H
226 o 7:02de42196ebe H
227 |
227 |
228 | o 6:eea13746799a G
228 | o 6:eea13746799a G
229 |/|
229 |/|
230 o | 5:24b6387c8c8c F
230 o | 5:24b6387c8c8c F
231 | |
231 | |
232 | o 4:9520eea781bc E
232 | o 4:9520eea781bc E
233 |/
233 |/
234 o 0:cd010b8cd998 A
234 o 0:cd010b8cd998 A
235
235
236 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
236 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
237 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
237 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
238 phase: draft
238 phase: draft
239 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
239 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
240 parent: -1:0000000000000000000000000000000000000000
240 parent: -1:0000000000000000000000000000000000000000
241 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
241 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
242 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
242 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
243 date: Sat Apr 30 15:24:48 2011 +0200
243 date: Sat Apr 30 15:24:48 2011 +0200
244 files+: D
244 files+: D
245 extra: branch=default
245 extra: branch=default
246 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
246 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
247 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
247 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
248 description:
248 description:
249 D
249 D
250
250
251
251
252 $ hg up -qr 'desc(G)'
252 $ hg up -qr 'desc(G)'
253 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
253 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
254 grafting 11:4596109a6a43 "D"
254 grafting 11:4596109a6a43 "D"
255 $ hg up -qr 'desc(E)'
255 $ hg up -qr 'desc(E)'
256 $ hg rebase -s tip -d .
256 $ hg rebase -s tip -d .
257 rebasing 14:9e36056a46e3 "D" (tip)
257 rebasing 14:9e36056a46e3 "D" (tip)
258 $ hg log --style default --debug -r tip
258 $ hg log --style default --debug -r tip
259 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
259 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
260 tag: tip
260 tag: tip
261 phase: draft
261 phase: draft
262 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
262 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
263 parent: -1:0000000000000000000000000000000000000000
263 parent: -1:0000000000000000000000000000000000000000
264 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
264 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
265 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
265 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
266 date: Sat Apr 30 15:24:48 2011 +0200
266 date: Sat Apr 30 15:24:48 2011 +0200
267 files+: D
267 files+: D
268 extra: branch=default
268 extra: branch=default
269 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
269 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
270 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
270 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
271 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
271 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
272 description:
272 description:
273 D
273 D
274
274
275
275
276 Start rebase from a commit that is obsolete but not hidden only because it's
276 Start rebase from a commit that is obsolete but not hidden only because it's
277 a working copy parent. We should be moved back to the starting commit as usual
277 a working copy parent. We should be moved back to the starting commit as usual
278 even though it is hidden (until we're moved there).
278 even though it is hidden (until we're moved there).
279
279
280 $ hg --hidden up -qr 'first(hidden())'
280 $ hg --hidden up -qr 'first(hidden())'
281 $ hg rebase --rev 13 --dest 15
281 $ hg rebase --rev 13 --dest 15
282 rebasing 13:98f6af4ee953 "C"
282 rebasing 13:98f6af4ee953 "C"
283 $ hg log -G
283 $ hg log -G
284 o 16:294a2b93eb4d C
284 o 16:294a2b93eb4d C
285 |
285 |
286 o 15:627d46148090 D
286 o 15:627d46148090 D
287 |
287 |
288 | o 12:462a34d07e59 B
288 | o 12:462a34d07e59 B
289 | |
289 | |
290 | o 11:4596109a6a43 D
290 | o 11:4596109a6a43 D
291 | |
291 | |
292 | o 7:02de42196ebe H
292 | o 7:02de42196ebe H
293 | |
293 | |
294 +---o 6:eea13746799a G
294 +---o 6:eea13746799a G
295 | |/
295 | |/
296 | o 5:24b6387c8c8c F
296 | o 5:24b6387c8c8c F
297 | |
297 | |
298 o | 4:9520eea781bc E
298 o | 4:9520eea781bc E
299 |/
299 |/
300 | @ 1:42ccdea3bb16 B
300 | @ 1:42ccdea3bb16 B
301 |/
301 |/
302 o 0:cd010b8cd998 A
302 o 0:cd010b8cd998 A
303
303
304
304
305 $ cd ..
305 $ cd ..
306
306
307 collapse rebase
307 collapse rebase
308 ---------------------------------
308 ---------------------------------
309
309
310 $ hg clone base collapse
310 $ hg clone base collapse
311 updating to branch default
311 updating to branch default
312 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 $ cd collapse
313 $ cd collapse
314 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
314 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
315 rebasing 1:42ccdea3bb16 "B"
315 rebasing 1:42ccdea3bb16 "B"
316 rebasing 2:5fddd98957c8 "C"
316 rebasing 2:5fddd98957c8 "C"
317 rebasing 3:32af7686d403 "D"
317 rebasing 3:32af7686d403 "D"
318 $ hg log -G
318 $ hg log -G
319 o 8:4dc2197e807b Collapsed revision
319 o 8:4dc2197e807b Collapsed revision
320 |
320 |
321 | @ 7:02de42196ebe H
321 | @ 7:02de42196ebe H
322 | |
322 | |
323 o | 6:eea13746799a G
323 o | 6:eea13746799a G
324 |\|
324 |\|
325 | o 5:24b6387c8c8c F
325 | o 5:24b6387c8c8c F
326 | |
326 | |
327 o | 4:9520eea781bc E
327 o | 4:9520eea781bc E
328 |/
328 |/
329 o 0:cd010b8cd998 A
329 o 0:cd010b8cd998 A
330
330
331 $ hg log --hidden -G
331 $ hg log --hidden -G
332 o 8:4dc2197e807b Collapsed revision
332 o 8:4dc2197e807b Collapsed revision
333 |
333 |
334 | @ 7:02de42196ebe H
334 | @ 7:02de42196ebe H
335 | |
335 | |
336 o | 6:eea13746799a G
336 o | 6:eea13746799a G
337 |\|
337 |\|
338 | o 5:24b6387c8c8c F
338 | o 5:24b6387c8c8c F
339 | |
339 | |
340 o | 4:9520eea781bc E
340 o | 4:9520eea781bc E
341 |/
341 |/
342 | x 3:32af7686d403 D
342 | x 3:32af7686d403 D
343 | |
343 | |
344 | x 2:5fddd98957c8 C
344 | x 2:5fddd98957c8 C
345 | |
345 | |
346 | x 1:42ccdea3bb16 B
346 | x 1:42ccdea3bb16 B
347 |/
347 |/
348 o 0:cd010b8cd998 A
348 o 0:cd010b8cd998 A
349
349
350 $ hg id --debug -r tip
350 $ hg id --debug -r tip
351 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
351 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
352 $ hg debugobsolete
352 $ hg debugobsolete
353 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
353 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
354 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
354 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
355 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
355 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (*) {'user': 'test'} (glob)
356
356
357 $ cd ..
357 $ cd ..
358
358
359 Rebase set has hidden descendants
359 Rebase set has hidden descendants
360 ---------------------------------
360 ---------------------------------
361
361
362 We rebase a changeset which has a hidden changeset. The hidden changeset must
362 We rebase a changeset which has a hidden changeset. The hidden changeset must
363 not be rebased.
363 not be rebased.
364
364
365 $ hg clone base hidden
365 $ hg clone base hidden
366 updating to branch default
366 updating to branch default
367 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 $ cd hidden
368 $ cd hidden
369 $ hg rebase -s 5fddd98957c8 -d eea13746799a
369 $ hg rebase -s 5fddd98957c8 -d eea13746799a
370 rebasing 2:5fddd98957c8 "C"
370 rebasing 2:5fddd98957c8 "C"
371 rebasing 3:32af7686d403 "D"
371 rebasing 3:32af7686d403 "D"
372 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
372 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
373 rebasing 1:42ccdea3bb16 "B"
373 rebasing 1:42ccdea3bb16 "B"
374 $ hg log -G
374 $ hg log -G
375 o 10:7c6027df6a99 B
375 o 10:7c6027df6a99 B
376 |
376 |
377 | o 9:cf44d2f5a9f4 D
377 | o 9:cf44d2f5a9f4 D
378 | |
378 | |
379 | o 8:e273c5e7d2d2 C
379 | o 8:e273c5e7d2d2 C
380 | |
380 | |
381 @ | 7:02de42196ebe H
381 @ | 7:02de42196ebe H
382 | |
382 | |
383 | o 6:eea13746799a G
383 | o 6:eea13746799a G
384 |/|
384 |/|
385 o | 5:24b6387c8c8c F
385 o | 5:24b6387c8c8c F
386 | |
386 | |
387 | o 4:9520eea781bc E
387 | o 4:9520eea781bc E
388 |/
388 |/
389 o 0:cd010b8cd998 A
389 o 0:cd010b8cd998 A
390
390
391 $ hg log --hidden -G
391 $ hg log --hidden -G
392 o 10:7c6027df6a99 B
392 o 10:7c6027df6a99 B
393 |
393 |
394 | o 9:cf44d2f5a9f4 D
394 | o 9:cf44d2f5a9f4 D
395 | |
395 | |
396 | o 8:e273c5e7d2d2 C
396 | o 8:e273c5e7d2d2 C
397 | |
397 | |
398 @ | 7:02de42196ebe H
398 @ | 7:02de42196ebe H
399 | |
399 | |
400 | o 6:eea13746799a G
400 | o 6:eea13746799a G
401 |/|
401 |/|
402 o | 5:24b6387c8c8c F
402 o | 5:24b6387c8c8c F
403 | |
403 | |
404 | o 4:9520eea781bc E
404 | o 4:9520eea781bc E
405 |/
405 |/
406 | x 3:32af7686d403 D
406 | x 3:32af7686d403 D
407 | |
407 | |
408 | x 2:5fddd98957c8 C
408 | x 2:5fddd98957c8 C
409 | |
409 | |
410 | x 1:42ccdea3bb16 B
410 | x 1:42ccdea3bb16 B
411 |/
411 |/
412 o 0:cd010b8cd998 A
412 o 0:cd010b8cd998 A
413
413
414 $ hg debugobsolete
414 $ hg debugobsolete
415 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (*) {'user': 'test'} (glob)
415 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (*) {'user': 'test'} (glob)
416 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (*) {'user': 'test'} (glob)
416 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (*) {'user': 'test'} (glob)
417 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (*) {'user': 'test'} (glob)
417 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (*) {'user': 'test'} (glob)
418
418
419 Test that rewriting leaving instability behind is allowed
419 Test that rewriting leaving instability behind is allowed
420 ---------------------------------------------------------------------
420 ---------------------------------------------------------------------
421
421
422 $ hg log -r 'children(8)'
422 $ hg log -r 'children(8)'
423 9:cf44d2f5a9f4 D (no-eol)
423 9:cf44d2f5a9f4 D (no-eol)
424 $ hg rebase -r 8
424 $ hg rebase -r 8
425 rebasing 8:e273c5e7d2d2 "C"
425 rebasing 8:e273c5e7d2d2 "C"
426 $ hg log -G
426 $ hg log -G
427 o 11:0d8f238b634c C
427 o 11:0d8f238b634c C
428 |
428 |
429 o 10:7c6027df6a99 B
429 o 10:7c6027df6a99 B
430 |
430 |
431 | o 9:cf44d2f5a9f4 D
431 | o 9:cf44d2f5a9f4 D
432 | |
432 | |
433 | x 8:e273c5e7d2d2 C
433 | x 8:e273c5e7d2d2 C
434 | |
434 | |
435 @ | 7:02de42196ebe H
435 @ | 7:02de42196ebe H
436 | |
436 | |
437 | o 6:eea13746799a G
437 | o 6:eea13746799a G
438 |/|
438 |/|
439 o | 5:24b6387c8c8c F
439 o | 5:24b6387c8c8c F
440 | |
440 | |
441 | o 4:9520eea781bc E
441 | o 4:9520eea781bc E
442 |/
442 |/
443 o 0:cd010b8cd998 A
443 o 0:cd010b8cd998 A
444
444
445
445
446
446
447 Test multiple root handling
447 Test multiple root handling
448 ------------------------------------
448 ------------------------------------
449
449
450 $ hg rebase --dest 4 --rev '7+11+9'
450 $ hg rebase --dest 4 --rev '7+11+9'
451 rebasing 9:cf44d2f5a9f4 "D"
451 rebasing 9:cf44d2f5a9f4 "D"
452 rebasing 7:02de42196ebe "H"
452 rebasing 7:02de42196ebe "H"
453 rebasing 11:0d8f238b634c "C" (tip)
453 rebasing 11:0d8f238b634c "C" (tip)
454 $ hg log -G
454 $ hg log -G
455 o 14:1e8370e38cca C
455 o 14:1e8370e38cca C
456 |
456 |
457 @ 13:bfe264faf697 H
457 @ 13:bfe264faf697 H
458 |
458 |
459 | o 12:102b4c1d889b D
459 | o 12:102b4c1d889b D
460 |/
460 |/
461 | o 10:7c6027df6a99 B
461 | o 10:7c6027df6a99 B
462 | |
462 | |
463 | x 7:02de42196ebe H
463 | x 7:02de42196ebe H
464 | |
464 | |
465 +---o 6:eea13746799a G
465 +---o 6:eea13746799a G
466 | |/
466 | |/
467 | o 5:24b6387c8c8c F
467 | o 5:24b6387c8c8c F
468 | |
468 | |
469 o | 4:9520eea781bc E
469 o | 4:9520eea781bc E
470 |/
470 |/
471 o 0:cd010b8cd998 A
471 o 0:cd010b8cd998 A
472
472
473 $ cd ..
473 $ cd ..
474
474
475 Detach both parents
475 Detach both parents
476
476
477 $ hg init double-detach
477 $ hg init double-detach
478 $ cd double-detach
478 $ cd double-detach
479
479
480 $ hg debugdrawdag <<EOF
480 $ hg debugdrawdag <<EOF
481 > F
481 > F
482 > /|
482 > /|
483 > C E
483 > C E
484 > | |
484 > | |
485 > B D G
485 > B D G
486 > \|/
486 > \|/
487 > A
487 > A
488 > EOF
488 > EOF
489
489
490 $ hg rebase -d G -r 'B + D + F'
490 $ hg rebase -d G -r 'B + D + F'
491 rebasing 1:112478962961 "B" (B)
491 rebasing 1:112478962961 "B" (B)
492 rebasing 2:b18e25de2cf5 "D" (D)
492 rebasing 2:b18e25de2cf5 "D" (D)
493 rebasing 6:f15c3adaf214 "F" (F tip)
493 rebasing 6:f15c3adaf214 "F" (F tip)
494 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
494 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
495 [255]
495 [255]
496
496
497 $ cd ..
497 $ cd ..
498
498
499 test on rebase dropping a merge
499 test on rebase dropping a merge
500
500
501 (setup)
501 (setup)
502
502
503 $ hg init dropmerge
503 $ hg init dropmerge
504 $ cd dropmerge
504 $ cd dropmerge
505 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
505 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
506 adding changesets
506 adding changesets
507 adding manifests
507 adding manifests
508 adding file changes
508 adding file changes
509 added 8 changesets with 7 changes to 7 files (+2 heads)
509 added 8 changesets with 7 changes to 7 files (+2 heads)
510 (run 'hg heads' to see heads, 'hg merge' to merge)
510 (run 'hg heads' to see heads, 'hg merge' to merge)
511 $ hg up 3
511 $ hg up 3
512 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
512 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 $ hg merge 7
513 $ hg merge 7
514 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
515 (branch merge, don't forget to commit)
515 (branch merge, don't forget to commit)
516 $ hg ci -m 'M'
516 $ hg ci -m 'M'
517 $ echo I > I
517 $ echo I > I
518 $ hg add I
518 $ hg add I
519 $ hg ci -m I
519 $ hg ci -m I
520 $ hg log -G
520 $ hg log -G
521 @ 9:4bde274eefcf I
521 @ 9:4bde274eefcf I
522 |
522 |
523 o 8:53a6a128b2b7 M
523 o 8:53a6a128b2b7 M
524 |\
524 |\
525 | o 7:02de42196ebe H
525 | o 7:02de42196ebe H
526 | |
526 | |
527 | | o 6:eea13746799a G
527 | | o 6:eea13746799a G
528 | |/|
528 | |/|
529 | o | 5:24b6387c8c8c F
529 | o | 5:24b6387c8c8c F
530 | | |
530 | | |
531 | | o 4:9520eea781bc E
531 | | o 4:9520eea781bc E
532 | |/
532 | |/
533 o | 3:32af7686d403 D
533 o | 3:32af7686d403 D
534 | |
534 | |
535 o | 2:5fddd98957c8 C
535 o | 2:5fddd98957c8 C
536 | |
536 | |
537 o | 1:42ccdea3bb16 B
537 o | 1:42ccdea3bb16 B
538 |/
538 |/
539 o 0:cd010b8cd998 A
539 o 0:cd010b8cd998 A
540
540
541 (actual test)
541 (actual test)
542
542
543 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
543 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
544 rebasing 3:32af7686d403 "D"
544 rebasing 3:32af7686d403 "D"
545 rebasing 7:02de42196ebe "H"
545 rebasing 7:02de42196ebe "H"
546 rebasing 9:4bde274eefcf "I" (tip)
546 rebasing 9:4bde274eefcf "I" (tip)
547 $ hg log -G
547 $ hg log -G
548 @ 12:acd174b7ab39 I
548 @ 12:acd174b7ab39 I
549 |
549 |
550 o 11:6c11a6218c97 H
550 o 11:6c11a6218c97 H
551 |
551 |
552 | o 10:b5313c85b22e D
552 | o 10:b5313c85b22e D
553 |/
553 |/
554 | o 8:53a6a128b2b7 M
554 | o 8:53a6a128b2b7 M
555 | |\
555 | |\
556 | | x 7:02de42196ebe H
556 | | x 7:02de42196ebe H
557 | | |
557 | | |
558 o---+ 6:eea13746799a G
558 o---+ 6:eea13746799a G
559 | | |
559 | | |
560 | | o 5:24b6387c8c8c F
560 | | o 5:24b6387c8c8c F
561 | | |
561 | | |
562 o---+ 4:9520eea781bc E
562 o---+ 4:9520eea781bc E
563 / /
563 / /
564 x | 3:32af7686d403 D
564 x | 3:32af7686d403 D
565 | |
565 | |
566 o | 2:5fddd98957c8 C
566 o | 2:5fddd98957c8 C
567 | |
567 | |
568 o | 1:42ccdea3bb16 B
568 o | 1:42ccdea3bb16 B
569 |/
569 |/
570 o 0:cd010b8cd998 A
570 o 0:cd010b8cd998 A
571
571
572
572
573 Test hidden changesets in the rebase set (issue4504)
573 Test hidden changesets in the rebase set (issue4504)
574
574
575 $ hg up --hidden 9
575 $ hg up --hidden 9
576 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
576 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
577 $ echo J > J
577 $ echo J > J
578 $ hg add J
578 $ hg add J
579 $ hg commit -m J
579 $ hg commit -m J
580 $ hg debugobsolete `hg log --rev . -T '{node}'`
580 $ hg debugobsolete `hg log --rev . -T '{node}'`
581 obsoleted 1 changesets
581 obsoleted 1 changesets
582
582
583 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
583 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
584 rebasing 9:4bde274eefcf "I"
584 rebasing 9:4bde274eefcf "I"
585 rebasing 13:06edfc82198f "J" (tip)
585 rebasing 13:06edfc82198f "J" (tip)
586 $ hg log -G
586 $ hg log -G
587 @ 15:5ae8a643467b J
587 @ 15:5ae8a643467b J
588 |
588 |
589 o 14:9ad579b4a5de I
589 o 14:9ad579b4a5de I
590 |
590 |
591 | o 12:acd174b7ab39 I
591 | o 12:acd174b7ab39 I
592 | |
592 | |
593 | o 11:6c11a6218c97 H
593 | o 11:6c11a6218c97 H
594 | |
594 | |
595 o | 10:b5313c85b22e D
595 o | 10:b5313c85b22e D
596 |/
596 |/
597 | o 8:53a6a128b2b7 M
597 | o 8:53a6a128b2b7 M
598 | |\
598 | |\
599 | | x 7:02de42196ebe H
599 | | x 7:02de42196ebe H
600 | | |
600 | | |
601 o---+ 6:eea13746799a G
601 o---+ 6:eea13746799a G
602 | | |
602 | | |
603 | | o 5:24b6387c8c8c F
603 | | o 5:24b6387c8c8c F
604 | | |
604 | | |
605 o---+ 4:9520eea781bc E
605 o---+ 4:9520eea781bc E
606 / /
606 / /
607 x | 3:32af7686d403 D
607 x | 3:32af7686d403 D
608 | |
608 | |
609 o | 2:5fddd98957c8 C
609 o | 2:5fddd98957c8 C
610 | |
610 | |
611 o | 1:42ccdea3bb16 B
611 o | 1:42ccdea3bb16 B
612 |/
612 |/
613 o 0:cd010b8cd998 A
613 o 0:cd010b8cd998 A
614
614
615 $ hg up 14 -C
615 $ hg up 14 -C
616 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
616 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
617 $ echo "K" > K
617 $ echo "K" > K
618 $ hg add K
618 $ hg add K
619 $ hg commit --amend -m "K"
619 $ hg commit --amend -m "K"
620 $ echo "L" > L
620 $ echo "L" > L
621 $ hg add L
621 $ hg add L
622 $ hg commit -m "L"
622 $ hg commit -m "L"
623 $ hg up '.^'
623 $ hg up '.^'
624 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
624 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
625 $ echo "M" > M
625 $ echo "M" > M
626 $ hg add M
626 $ hg add M
627 $ hg commit --amend -m "M"
627 $ hg commit --amend -m "M"
628 $ hg log -G
628 $ hg log -G
629 @ 20:bfaedf8eb73b M
629 @ 20:bfaedf8eb73b M
630 |
630 |
631 | o 18:97219452e4bd L
631 | o 18:97219452e4bd L
632 | |
632 | |
633 | x 17:fc37a630c901 K
633 | x 17:fc37a630c901 K
634 |/
634 |/
635 | o 15:5ae8a643467b J
635 | o 15:5ae8a643467b J
636 | |
636 | |
637 | x 14:9ad579b4a5de I
637 | x 14:9ad579b4a5de I
638 |/
638 |/
639 | o 12:acd174b7ab39 I
639 | o 12:acd174b7ab39 I
640 | |
640 | |
641 | o 11:6c11a6218c97 H
641 | o 11:6c11a6218c97 H
642 | |
642 | |
643 o | 10:b5313c85b22e D
643 o | 10:b5313c85b22e D
644 |/
644 |/
645 | o 8:53a6a128b2b7 M
645 | o 8:53a6a128b2b7 M
646 | |\
646 | |\
647 | | x 7:02de42196ebe H
647 | | x 7:02de42196ebe H
648 | | |
648 | | |
649 o---+ 6:eea13746799a G
649 o---+ 6:eea13746799a G
650 | | |
650 | | |
651 | | o 5:24b6387c8c8c F
651 | | o 5:24b6387c8c8c F
652 | | |
652 | | |
653 o---+ 4:9520eea781bc E
653 o---+ 4:9520eea781bc E
654 / /
654 / /
655 x | 3:32af7686d403 D
655 x | 3:32af7686d403 D
656 | |
656 | |
657 o | 2:5fddd98957c8 C
657 o | 2:5fddd98957c8 C
658 | |
658 | |
659 o | 1:42ccdea3bb16 B
659 o | 1:42ccdea3bb16 B
660 |/
660 |/
661 o 0:cd010b8cd998 A
661 o 0:cd010b8cd998 A
662
662
663 $ hg rebase -s 14 -d 18 --config experimental.rebaseskipobsolete=True
663 $ hg rebase -s 14 -d 18 --config experimental.rebaseskipobsolete=True
664 note: not rebasing 14:9ad579b4a5de "I", already in destination as 17:fc37a630c901 "K"
664 note: not rebasing 14:9ad579b4a5de "I", already in destination as 17:fc37a630c901 "K"
665 rebasing 15:5ae8a643467b "J"
665 rebasing 15:5ae8a643467b "J"
666
666
667 $ cd ..
667 $ cd ..
668
668
669 Skip obsolete changeset even with multiple hops
669 Skip obsolete changeset even with multiple hops
670 -----------------------------------------------
670 -----------------------------------------------
671
671
672 setup
672 setup
673
673
674 $ hg init obsskip
674 $ hg init obsskip
675 $ cd obsskip
675 $ cd obsskip
676 $ cat << EOF >> .hg/hgrc
676 $ cat << EOF >> .hg/hgrc
677 > [experimental]
677 > [experimental]
678 > rebaseskipobsolete = True
678 > rebaseskipobsolete = True
679 > [extensions]
679 > [extensions]
680 > strip =
680 > strip =
681 > EOF
681 > EOF
682 $ echo A > A
682 $ echo A > A
683 $ hg add A
683 $ hg add A
684 $ hg commit -m A
684 $ hg commit -m A
685 $ echo B > B
685 $ echo B > B
686 $ hg add B
686 $ hg add B
687 $ hg commit -m B0
687 $ hg commit -m B0
688 $ hg commit --amend -m B1
688 $ hg commit --amend -m B1
689 $ hg commit --amend -m B2
689 $ hg commit --amend -m B2
690 $ hg up --hidden 'desc(B0)'
690 $ hg up --hidden 'desc(B0)'
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
691 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
692 $ echo C > C
692 $ echo C > C
693 $ hg add C
693 $ hg add C
694 $ hg commit -m C
694 $ hg commit -m C
695
695
696 Rebase finds its way in a chain of marker
696 Rebase finds its way in a chain of marker
697
697
698 $ hg rebase -d 'desc(B2)'
698 $ hg rebase -d 'desc(B2)'
699 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
699 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
700 rebasing 4:212cb178bcbb "C" (tip)
700 rebasing 4:212cb178bcbb "C" (tip)
701
701
702 Even when the chain include missing node
702 Even when the chain include missing node
703
703
704 $ hg up --hidden 'desc(B0)'
704 $ hg up --hidden 'desc(B0)'
705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
706 $ echo D > D
706 $ echo D > D
707 $ hg add D
707 $ hg add D
708 $ hg commit -m D
708 $ hg commit -m D
709 $ hg --hidden strip -r 'desc(B1)'
709 $ hg --hidden strip -r 'desc(B1)'
710 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg (glob)
710 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg (glob)
711
711
712 $ hg rebase -d 'desc(B2)'
712 $ hg rebase -d 'desc(B2)'
713 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
713 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
714 rebasing 5:1a79b7535141 "D" (tip)
714 rebasing 5:1a79b7535141 "D" (tip)
715 $ hg up 4
715 $ hg up 4
716 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
716 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
717 $ echo "O" > O
717 $ echo "O" > O
718 $ hg add O
718 $ hg add O
719 $ hg commit -m O
719 $ hg commit -m O
720 $ echo "P" > P
720 $ echo "P" > P
721 $ hg add P
721 $ hg add P
722 $ hg commit -m P
722 $ hg commit -m P
723 $ hg log -G
723 $ hg log -G
724 @ 8:8d47583e023f P
724 @ 8:8d47583e023f P
725 |
725 |
726 o 7:360bbaa7d3ce O
726 o 7:360bbaa7d3ce O
727 |
727 |
728 | o 6:9c48361117de D
728 | o 6:9c48361117de D
729 | |
729 | |
730 o | 4:ff2c4d47b71d C
730 o | 4:ff2c4d47b71d C
731 |/
731 |/
732 o 2:261e70097290 B2
732 o 2:261e70097290 B2
733 |
733 |
734 o 0:4a2df7238c3b A
734 o 0:4a2df7238c3b A
735
735
736 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.stabilization=all
736 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.stabilization=all
737 obsoleted 1 changesets
737 obsoleted 1 changesets
738 $ hg rebase -d 6 -r "4::"
738 $ hg rebase -d 6 -r "4::"
739 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
739 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
740 rebasing 4:ff2c4d47b71d "C"
740 rebasing 4:ff2c4d47b71d "C"
741 rebasing 8:8d47583e023f "P" (tip)
741 rebasing 8:8d47583e023f "P" (tip)
742
742
743 If all the changeset to be rebased are obsolete and present in the destination, we
743 If all the changeset to be rebased are obsolete and present in the destination, we
744 should display a friendly error message
744 should display a friendly error message
745
745
746 $ hg log -G
746 $ hg log -G
747 @ 10:121d9e3bc4c6 P
747 @ 10:121d9e3bc4c6 P
748 |
748 |
749 o 9:4be60e099a77 C
749 o 9:4be60e099a77 C
750 |
750 |
751 o 6:9c48361117de D
751 o 6:9c48361117de D
752 |
752 |
753 o 2:261e70097290 B2
753 o 2:261e70097290 B2
754 |
754 |
755 o 0:4a2df7238c3b A
755 o 0:4a2df7238c3b A
756
756
757
757
758 $ hg up 9
758 $ hg up 9
759 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
759 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
760 $ echo "non-relevant change" > nonrelevant
760 $ echo "non-relevant change" > nonrelevant
761 $ hg add nonrelevant
761 $ hg add nonrelevant
762 $ hg commit -m nonrelevant
762 $ hg commit -m nonrelevant
763 created new head
763 created new head
764 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.stabilization=all
764 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.stabilization=all
765 obsoleted 1 changesets
765 obsoleted 1 changesets
766 $ hg rebase -r . -d 10
766 $ hg rebase -r . -d 10
767 note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor
767 note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor
768
768
769 If a rebase is going to create divergence, it should abort
769 If a rebase is going to create divergence, it should abort
770
770
771 $ hg log -G
771 $ hg log -G
772 @ 11:f44da1f4954c nonrelevant
772 @ 11:f44da1f4954c nonrelevant
773 |
773 |
774 | o 10:121d9e3bc4c6 P
774 | o 10:121d9e3bc4c6 P
775 |/
775 |/
776 o 9:4be60e099a77 C
776 o 9:4be60e099a77 C
777 |
777 |
778 o 6:9c48361117de D
778 o 6:9c48361117de D
779 |
779 |
780 o 2:261e70097290 B2
780 o 2:261e70097290 B2
781 |
781 |
782 o 0:4a2df7238c3b A
782 o 0:4a2df7238c3b A
783
783
784
784
785 $ hg up 9
785 $ hg up 9
786 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
786 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
787 $ echo "john" > doe
787 $ echo "john" > doe
788 $ hg add doe
788 $ hg add doe
789 $ hg commit -m "john doe"
789 $ hg commit -m "john doe"
790 created new head
790 created new head
791 $ hg up 10
791 $ hg up 10
792 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
792 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
793 $ echo "foo" > bar
793 $ echo "foo" > bar
794 $ hg add bar
794 $ hg add bar
795 $ hg commit --amend -m "10'"
795 $ hg commit --amend -m "10'"
796 $ hg up 10 --hidden
796 $ hg up 10 --hidden
797 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
797 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
798 $ echo "bar" > foo
798 $ echo "bar" > foo
799 $ hg add foo
799 $ hg add foo
800 $ hg commit -m "bar foo"
800 $ hg commit -m "bar foo"
801 $ hg log -G
801 $ hg log -G
802 @ 15:73568ab6879d bar foo
802 @ 15:73568ab6879d bar foo
803 |
803 |
804 | o 14:77d874d096a2 10'
804 | o 14:77d874d096a2 10'
805 | |
805 | |
806 | | o 12:3eb461388009 john doe
806 | | o 12:3eb461388009 john doe
807 | |/
807 | |/
808 x | 10:121d9e3bc4c6 P
808 x | 10:121d9e3bc4c6 P
809 |/
809 |/
810 o 9:4be60e099a77 C
810 o 9:4be60e099a77 C
811 |
811 |
812 o 6:9c48361117de D
812 o 6:9c48361117de D
813 |
813 |
814 o 2:261e70097290 B2
814 o 2:261e70097290 B2
815 |
815 |
816 o 0:4a2df7238c3b A
816 o 0:4a2df7238c3b A
817
817
818 $ hg summary
818 $ hg summary
819 parent: 15:73568ab6879d tip (orphan)
819 parent: 15:73568ab6879d tip (orphan)
820 bar foo
820 bar foo
821 branch: default
821 branch: default
822 commit: (clean)
822 commit: (clean)
823 update: 2 new changesets, 3 branch heads (merge)
823 update: 2 new changesets, 3 branch heads (merge)
824 phases: 8 draft
824 phases: 8 draft
825 orphan: 1 changesets
825 orphan: 1 changesets
826 $ hg rebase -s 10 -d 12
826 $ hg rebase -s 10 -d 12
827 abort: this rebase will cause divergences from: 121d9e3bc4c6
827 abort: this rebase will cause divergences from: 121d9e3bc4c6
828 (to force the rebase please set experimental.allowdivergence=True)
828 (to force the rebase please set experimental.allowdivergence=True)
829 [255]
829 [255]
830 $ hg log -G
830 $ hg log -G
831 @ 15:73568ab6879d bar foo
831 @ 15:73568ab6879d bar foo
832 |
832 |
833 | o 14:77d874d096a2 10'
833 | o 14:77d874d096a2 10'
834 | |
834 | |
835 | | o 12:3eb461388009 john doe
835 | | o 12:3eb461388009 john doe
836 | |/
836 | |/
837 x | 10:121d9e3bc4c6 P
837 x | 10:121d9e3bc4c6 P
838 |/
838 |/
839 o 9:4be60e099a77 C
839 o 9:4be60e099a77 C
840 |
840 |
841 o 6:9c48361117de D
841 o 6:9c48361117de D
842 |
842 |
843 o 2:261e70097290 B2
843 o 2:261e70097290 B2
844 |
844 |
845 o 0:4a2df7238c3b A
845 o 0:4a2df7238c3b A
846
846
847 With experimental.allowdivergence=True, rebase can create divergence
847 With experimental.allowdivergence=True, rebase can create divergence
848
848
849 $ hg rebase -s 10 -d 12 --config experimental.allowdivergence=True
849 $ hg rebase -s 10 -d 12 --config experimental.allowdivergence=True
850 rebasing 10:121d9e3bc4c6 "P"
850 rebasing 10:121d9e3bc4c6 "P"
851 rebasing 15:73568ab6879d "bar foo" (tip)
851 rebasing 15:73568ab6879d "bar foo" (tip)
852 $ hg summary
852 $ hg summary
853 parent: 17:61bd55f69bc4 tip
853 parent: 17:61bd55f69bc4 tip
854 bar foo
854 bar foo
855 branch: default
855 branch: default
856 commit: (clean)
856 commit: (clean)
857 update: 1 new changesets, 2 branch heads (merge)
857 update: 1 new changesets, 2 branch heads (merge)
858 phases: 8 draft
858 phases: 8 draft
859 content-divergent: 2 changesets
859 content-divergent: 2 changesets
860
860
861 rebase --continue + skipped rev because their successors are in destination
861 rebase --continue + skipped rev because their successors are in destination
862 we make a change in trunk and work on conflicting changes to make rebase abort.
862 we make a change in trunk and work on conflicting changes to make rebase abort.
863
863
864 $ hg log -G -r 17::
864 $ hg log -G -r 17::
865 @ 17:61bd55f69bc4 bar foo
865 @ 17:61bd55f69bc4 bar foo
866 |
866 |
867 ~
867 ~
868
868
869 Create the two changes in trunk
869 Create the two changes in trunk
870 $ printf "a" > willconflict
870 $ printf "a" > willconflict
871 $ hg add willconflict
871 $ hg add willconflict
872 $ hg commit -m "willconflict first version"
872 $ hg commit -m "willconflict first version"
873
873
874 $ printf "dummy" > C
874 $ printf "dummy" > C
875 $ hg commit -m "dummy change successor"
875 $ hg commit -m "dummy change successor"
876
876
877 Create the changes that we will rebase
877 Create the changes that we will rebase
878 $ hg update -C 17 -q
878 $ hg update -C 17 -q
879 $ printf "b" > willconflict
879 $ printf "b" > willconflict
880 $ hg add willconflict
880 $ hg add willconflict
881 $ hg commit -m "willconflict second version"
881 $ hg commit -m "willconflict second version"
882 created new head
882 created new head
883 $ printf "dummy" > K
883 $ printf "dummy" > K
884 $ hg add K
884 $ hg add K
885 $ hg commit -m "dummy change"
885 $ hg commit -m "dummy change"
886 $ printf "dummy" > L
886 $ printf "dummy" > L
887 $ hg add L
887 $ hg add L
888 $ hg commit -m "dummy change"
888 $ hg commit -m "dummy change"
889 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 19 -T '{node}'` --config experimental.stabilization=all
889 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 19 -T '{node}'` --config experimental.stabilization=all
890 obsoleted 1 changesets
890 obsoleted 1 changesets
891
891
892 $ hg log -G -r 17::
892 $ hg log -G -r 17::
893 @ 22:7bdc8a87673d dummy change
893 @ 22:7bdc8a87673d dummy change
894 |
894 |
895 x 21:8b31da3c4919 dummy change
895 x 21:8b31da3c4919 dummy change
896 |
896 |
897 o 20:b82fb57ea638 willconflict second version
897 o 20:b82fb57ea638 willconflict second version
898 |
898 |
899 | o 19:601db7a18f51 dummy change successor
899 | o 19:601db7a18f51 dummy change successor
900 | |
900 | |
901 | o 18:357ddf1602d5 willconflict first version
901 | o 18:357ddf1602d5 willconflict first version
902 |/
902 |/
903 o 17:61bd55f69bc4 bar foo
903 o 17:61bd55f69bc4 bar foo
904 |
904 |
905 ~
905 ~
906 $ hg rebase -r ".^^ + .^ + ." -d 19
906 $ hg rebase -r ".^^ + .^ + ." -d 19
907 note: not rebasing 21:8b31da3c4919 "dummy change", already in destination as 19:601db7a18f51 "dummy change successor"
907 note: not rebasing 21:8b31da3c4919 "dummy change", already in destination as 19:601db7a18f51 "dummy change successor"
908 rebasing 20:b82fb57ea638 "willconflict second version"
908 rebasing 20:b82fb57ea638 "willconflict second version"
909 merging willconflict
909 merging willconflict
910 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
910 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
911 unresolved conflicts (see hg resolve, then hg rebase --continue)
911 unresolved conflicts (see hg resolve, then hg rebase --continue)
912 [1]
912 [1]
913
913
914 $ hg resolve --mark willconflict
914 $ hg resolve --mark willconflict
915 (no more unresolved files)
915 (no more unresolved files)
916 continue: hg rebase --continue
916 continue: hg rebase --continue
917 $ hg rebase --continue
917 $ hg rebase --continue
918 rebasing 20:b82fb57ea638 "willconflict second version"
918 rebasing 20:b82fb57ea638 "willconflict second version"
919 rebasing 22:7bdc8a87673d "dummy change" (tip)
919 rebasing 22:7bdc8a87673d "dummy change" (tip)
920 $ cd ..
920 $ cd ..
921
921
922 Rebase merge where successor of one parent is equal to destination (issue5198)
922 Rebase merge where successor of one parent is equal to destination (issue5198)
923
923
924 $ hg init p1-succ-is-dest
924 $ hg init p1-succ-is-dest
925 $ cd p1-succ-is-dest
925 $ cd p1-succ-is-dest
926
926
927 $ hg debugdrawdag <<EOF
927 $ hg debugdrawdag <<EOF
928 > F
928 > F
929 > /|
929 > /|
930 > E D B # replace: D -> B
930 > E D B # replace: D -> B
931 > \|/
931 > \|/
932 > A
932 > A
933 > EOF
933 > EOF
934
934
935 $ hg rebase -d B -s D
935 $ hg rebase -d B -s D
936 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
936 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
937 rebasing 4:66f1a38021c9 "F" (F tip)
937 rebasing 4:66f1a38021c9 "F" (F tip)
938 $ hg log -G
938 $ hg log -G
939 o 5:50e9d60b99c6 F
939 o 5:50e9d60b99c6 F
940 |\
940 |\
941 | | x 4:66f1a38021c9 F
941 | | x 4:66f1a38021c9 F
942 | |/|
942 | |/|
943 | o | 3:7fb047a69f22 E
943 | o | 3:7fb047a69f22 E
944 | | |
944 | | |
945 | | x 2:b18e25de2cf5 D
945 | | x 2:b18e25de2cf5 D
946 | |/
946 | |/
947 o | 1:112478962961 B
947 o | 1:112478962961 B
948 |/
948 |/
949 o 0:426bada5c675 A
949 o 0:426bada5c675 A
950
950
951 $ cd ..
951 $ cd ..
952
952
953 Rebase merge where successor of other parent is equal to destination
953 Rebase merge where successor of other parent is equal to destination
954
954
955 $ hg init p2-succ-is-dest
955 $ hg init p2-succ-is-dest
956 $ cd p2-succ-is-dest
956 $ cd p2-succ-is-dest
957
957
958 $ hg debugdrawdag <<EOF
958 $ hg debugdrawdag <<EOF
959 > F
959 > F
960 > /|
960 > /|
961 > E D B # replace: E -> B
961 > E D B # replace: E -> B
962 > \|/
962 > \|/
963 > A
963 > A
964 > EOF
964 > EOF
965
965
966 $ hg rebase -d B -s E
966 $ hg rebase -d B -s E
967 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
967 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
968 rebasing 4:66f1a38021c9 "F" (F tip)
968 rebasing 4:66f1a38021c9 "F" (F tip)
969 $ hg log -G
969 $ hg log -G
970 o 5:aae1787dacee F
970 o 5:aae1787dacee F
971 |\
971 |\
972 | | x 4:66f1a38021c9 F
972 | | x 4:66f1a38021c9 F
973 | |/|
973 | |/|
974 | | x 3:7fb047a69f22 E
974 | | x 3:7fb047a69f22 E
975 | | |
975 | | |
976 | o | 2:b18e25de2cf5 D
976 | o | 2:b18e25de2cf5 D
977 | |/
977 | |/
978 o / 1:112478962961 B
978 o / 1:112478962961 B
979 |/
979 |/
980 o 0:426bada5c675 A
980 o 0:426bada5c675 A
981
981
982 $ cd ..
982 $ cd ..
983
983
984 Rebase merge where successor of one parent is ancestor of destination
984 Rebase merge where successor of one parent is ancestor of destination
985
985
986 $ hg init p1-succ-in-dest
986 $ hg init p1-succ-in-dest
987 $ cd p1-succ-in-dest
987 $ cd p1-succ-in-dest
988
988
989 $ hg debugdrawdag <<EOF
989 $ hg debugdrawdag <<EOF
990 > F C
990 > F C
991 > /| |
991 > /| |
992 > E D B # replace: D -> B
992 > E D B # replace: D -> B
993 > \|/
993 > \|/
994 > A
994 > A
995 > EOF
995 > EOF
996
996
997 $ hg rebase -d C -s D
997 $ hg rebase -d C -s D
998 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
998 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
999 rebasing 5:66f1a38021c9 "F" (F tip)
999 rebasing 5:66f1a38021c9 "F" (F tip)
1000
1000
1001 $ hg log -G
1001 $ hg log -G
1002 o 6:0913febf6439 F
1002 o 6:0913febf6439 F
1003 |\
1003 |\
1004 +---x 5:66f1a38021c9 F
1004 +---x 5:66f1a38021c9 F
1005 | | |
1005 | | |
1006 | o | 4:26805aba1e60 C
1006 | o | 4:26805aba1e60 C
1007 | | |
1007 | | |
1008 o | | 3:7fb047a69f22 E
1008 o | | 3:7fb047a69f22 E
1009 | | |
1009 | | |
1010 +---x 2:b18e25de2cf5 D
1010 +---x 2:b18e25de2cf5 D
1011 | |
1011 | |
1012 | o 1:112478962961 B
1012 | o 1:112478962961 B
1013 |/
1013 |/
1014 o 0:426bada5c675 A
1014 o 0:426bada5c675 A
1015
1015
1016 $ cd ..
1016 $ cd ..
1017
1017
1018 Rebase merge where successor of other parent is ancestor of destination
1018 Rebase merge where successor of other parent is ancestor of destination
1019
1019
1020 $ hg init p2-succ-in-dest
1020 $ hg init p2-succ-in-dest
1021 $ cd p2-succ-in-dest
1021 $ cd p2-succ-in-dest
1022
1022
1023 $ hg debugdrawdag <<EOF
1023 $ hg debugdrawdag <<EOF
1024 > F C
1024 > F C
1025 > /| |
1025 > /| |
1026 > E D B # replace: E -> B
1026 > E D B # replace: E -> B
1027 > \|/
1027 > \|/
1028 > A
1028 > A
1029 > EOF
1029 > EOF
1030
1030
1031 $ hg rebase -d C -s E
1031 $ hg rebase -d C -s E
1032 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
1032 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
1033 rebasing 5:66f1a38021c9 "F" (F tip)
1033 rebasing 5:66f1a38021c9 "F" (F tip)
1034 $ hg log -G
1034 $ hg log -G
1035 o 6:c6ab0cc6d220 F
1035 o 6:c6ab0cc6d220 F
1036 |\
1036 |\
1037 +---x 5:66f1a38021c9 F
1037 +---x 5:66f1a38021c9 F
1038 | | |
1038 | | |
1039 | o | 4:26805aba1e60 C
1039 | o | 4:26805aba1e60 C
1040 | | |
1040 | | |
1041 | | x 3:7fb047a69f22 E
1041 | | x 3:7fb047a69f22 E
1042 | | |
1042 | | |
1043 o---+ 2:b18e25de2cf5 D
1043 o---+ 2:b18e25de2cf5 D
1044 / /
1044 / /
1045 o / 1:112478962961 B
1045 o / 1:112478962961 B
1046 |/
1046 |/
1047 o 0:426bada5c675 A
1047 o 0:426bada5c675 A
1048
1048
1049 $ cd ..
1049 $ cd ..
1050
1050
1051 Rebase merge where successor of one parent is ancestor of destination
1051 Rebase merge where successor of one parent is ancestor of destination
1052
1052
1053 $ hg init p1-succ-in-dest-b
1053 $ hg init p1-succ-in-dest-b
1054 $ cd p1-succ-in-dest-b
1054 $ cd p1-succ-in-dest-b
1055
1055
1056 $ hg debugdrawdag <<EOF
1056 $ hg debugdrawdag <<EOF
1057 > F C
1057 > F C
1058 > /| |
1058 > /| |
1059 > E D B # replace: E -> B
1059 > E D B # replace: E -> B
1060 > \|/
1060 > \|/
1061 > A
1061 > A
1062 > EOF
1062 > EOF
1063
1063
1064 $ hg rebase -d C -b F
1064 $ hg rebase -d C -b F
1065 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
1065 note: not rebasing 3:7fb047a69f22 "E" (E), already in destination as 1:112478962961 "B"
1066 rebasing 2:b18e25de2cf5 "D" (D)
1066 rebasing 2:b18e25de2cf5 "D" (D)
1067 rebasing 5:66f1a38021c9 "F" (F tip)
1067 rebasing 5:66f1a38021c9 "F" (F tip)
1068 warning: rebasing 5:66f1a38021c9 may include unwanted changes from 3:7fb047a69f22
1068 note: rebase of 5:66f1a38021c9 created no changes to commit
1069 $ hg log -G
1069 $ hg log -G
1070 o 7:9ed45af61fa0 F
1071 |
1072 o 6:8f47515dda15 D
1070 o 6:8f47515dda15 D
1073 |
1071 |
1074 | x 5:66f1a38021c9 F
1072 | x 5:66f1a38021c9 F
1075 | |\
1073 | |\
1076 o | | 4:26805aba1e60 C
1074 o | | 4:26805aba1e60 C
1077 | | |
1075 | | |
1078 | | x 3:7fb047a69f22 E
1076 | | x 3:7fb047a69f22 E
1079 | | |
1077 | | |
1080 | x | 2:b18e25de2cf5 D
1078 | x | 2:b18e25de2cf5 D
1081 | |/
1079 | |/
1082 o / 1:112478962961 B
1080 o / 1:112478962961 B
1083 |/
1081 |/
1084 o 0:426bada5c675 A
1082 o 0:426bada5c675 A
1085
1083
1086 $ cd ..
1084 $ cd ..
1087
1085
1088 Rebase merge where successor of other parent is ancestor of destination
1086 Rebase merge where successor of other parent is ancestor of destination
1089
1087
1090 $ hg init p2-succ-in-dest-b
1088 $ hg init p2-succ-in-dest-b
1091 $ cd p2-succ-in-dest-b
1089 $ cd p2-succ-in-dest-b
1092
1090
1093 $ hg debugdrawdag <<EOF
1091 $ hg debugdrawdag <<EOF
1094 > F C
1092 > F C
1095 > /| |
1093 > /| |
1096 > E D B # replace: D -> B
1094 > E D B # replace: D -> B
1097 > \|/
1095 > \|/
1098 > A
1096 > A
1099 > EOF
1097 > EOF
1100
1098
1101 $ hg rebase -d C -b F
1099 $ hg rebase -d C -b F
1102 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
1100 note: not rebasing 2:b18e25de2cf5 "D" (D), already in destination as 1:112478962961 "B"
1103 rebasing 3:7fb047a69f22 "E" (E)
1101 rebasing 3:7fb047a69f22 "E" (E)
1104 rebasing 5:66f1a38021c9 "F" (F tip)
1102 rebasing 5:66f1a38021c9 "F" (F tip)
1105 warning: rebasing 5:66f1a38021c9 may include unwanted changes from 2:b18e25de2cf5
1103 note: rebase of 5:66f1a38021c9 created no changes to commit
1106
1104
1107 $ hg log -G
1105 $ hg log -G
1108 o 7:502540f44880 F
1109 |
1110 o 6:533690786a86 E
1106 o 6:533690786a86 E
1111 |
1107 |
1112 | x 5:66f1a38021c9 F
1108 | x 5:66f1a38021c9 F
1113 | |\
1109 | |\
1114 o | | 4:26805aba1e60 C
1110 o | | 4:26805aba1e60 C
1115 | | |
1111 | | |
1116 | | x 3:7fb047a69f22 E
1112 | | x 3:7fb047a69f22 E
1117 | | |
1113 | | |
1118 | x | 2:b18e25de2cf5 D
1114 | x | 2:b18e25de2cf5 D
1119 | |/
1115 | |/
1120 o / 1:112478962961 B
1116 o / 1:112478962961 B
1121 |/
1117 |/
1122 o 0:426bada5c675 A
1118 o 0:426bada5c675 A
1123
1119
1124 $ cd ..
1120 $ cd ..
1125
1121
1122 Rebase merge where both parents have successors in destination
1123
1124 $ hg init p12-succ-in-dest
1125 $ cd p12-succ-in-dest
1126 $ hg debugdrawdag <<'EOS'
1127 > E F
1128 > /| /| # replace: A -> C
1129 > A B C D # replace: B -> D
1130 > | |
1131 > X Y
1132 > EOS
1133 $ hg rebase -r A+B+E -d F
1134 note: not rebasing 4:a3d17304151f "A" (A), already in destination as 0:96cc3511f894 "C"
1135 note: not rebasing 5:b23a2cc00842 "B" (B), already in destination as 1:058c1e1fb10a "D"
1136 rebasing 7:dac5d11c5a7d "E" (E tip)
1137 abort: rebasing 7:dac5d11c5a7d will include unwanted changes from 3:59c792af609c, 5:b23a2cc00842 or 2:ba2b7fa7166d, 4:a3d17304151f
1138 [255]
1139 $ cd ..
1140
1141 Rebase a non-clean merge. One parent has successor in destination, the other
1142 parent moves as requested.
1143
1144 $ hg init p1-succ-p2-move
1145 $ cd p1-succ-p2-move
1146 $ hg debugdrawdag <<'EOS'
1147 > D Z
1148 > /| | # replace: A -> C
1149 > A B C # D/D = D
1150 > EOS
1151 $ hg rebase -r A+B+D -d Z
1152 note: not rebasing 0:426bada5c675 "A" (A), already in destination as 2:96cc3511f894 "C"
1153 rebasing 1:fc2b737bb2e5 "B" (B)
1154 rebasing 3:b8ed089c80ad "D" (D)
1155
1156 $ rm .hg/localtags
1157 $ hg log -G
1158 o 6:e4f78693cc88 D
1159 |
1160 o 5:76840d832e98 B
1161 |
1162 o 4:50e41c1f3950 Z
1163 |
1164 o 2:96cc3511f894 C
1165
1166 $ hg files -r tip
1167 B
1168 C
1169 D
1170 Z
1171
1172 $ cd ..
1173
1174 $ hg init p1-move-p2-succ
1175 $ cd p1-move-p2-succ
1176 $ hg debugdrawdag <<'EOS'
1177 > D Z
1178 > /| | # replace: B -> C
1179 > A B C # D/D = D
1180 > EOS
1181 $ hg rebase -r B+A+D -d Z
1182 note: not rebasing 1:fc2b737bb2e5 "B" (B), already in destination as 2:96cc3511f894 "C"
1183 rebasing 0:426bada5c675 "A" (A)
1184 rebasing 3:b8ed089c80ad "D" (D)
1185
1186 $ rm .hg/localtags
1187 $ hg log -G
1188 o 6:1b355ed94d82 D
1189 |
1190 o 5:a81a74d764a6 A
1191 |
1192 o 4:50e41c1f3950 Z
1193 |
1194 o 2:96cc3511f894 C
1195
1196 $ hg files -r tip
1197 A
1198 C
1199 D
1200 Z
1201
1202 $ cd ..
1203
1126 Test that bookmark is moved and working dir is updated when all changesets have
1204 Test that bookmark is moved and working dir is updated when all changesets have
1127 equivalents in destination
1205 equivalents in destination
1128 $ hg init rbsrepo && cd rbsrepo
1206 $ hg init rbsrepo && cd rbsrepo
1129 $ echo "[experimental]" > .hg/hgrc
1207 $ echo "[experimental]" > .hg/hgrc
1130 $ echo "stabilization=all" >> .hg/hgrc
1208 $ echo "stabilization=all" >> .hg/hgrc
1131 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1209 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1132 $ echo root > root && hg ci -Am root
1210 $ echo root > root && hg ci -Am root
1133 adding root
1211 adding root
1134 $ echo a > a && hg ci -Am a
1212 $ echo a > a && hg ci -Am a
1135 adding a
1213 adding a
1136 $ hg up 0
1214 $ hg up 0
1137 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1215 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1138 $ echo b > b && hg ci -Am b
1216 $ echo b > b && hg ci -Am b
1139 adding b
1217 adding b
1140 created new head
1218 created new head
1141 $ hg rebase -r 2 -d 1
1219 $ hg rebase -r 2 -d 1
1142 rebasing 2:1e9a3c00cbe9 "b" (tip)
1220 rebasing 2:1e9a3c00cbe9 "b" (tip)
1143 $ hg log -r . # working dir is at rev 3 (successor of 2)
1221 $ hg log -r . # working dir is at rev 3 (successor of 2)
1144 3:be1832deae9a b (no-eol)
1222 3:be1832deae9a b (no-eol)
1145 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1223 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1146 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1224 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1147 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1225 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1148 2:1e9a3c00cbe9 b (no-eol)
1226 2:1e9a3c00cbe9 b (no-eol)
1149 $ hg rebase -r 2 -d 3 --config experimental.stabilization.track-operation=1
1227 $ hg rebase -r 2 -d 3 --config experimental.stabilization.track-operation=1
1150 note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b"
1228 note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b"
1151 Check that working directory was not updated to rev 3 because rev 2 was skipped
1229 Check that working directory was not updated to rev 3 because rev 2 was skipped
1152 during the rebase operation
1230 during the rebase operation
1153 $ hg log -r .
1231 $ hg log -r .
1154 2:1e9a3c00cbe9 b (no-eol)
1232 2:1e9a3c00cbe9 b (no-eol)
1155
1233
1156 Check that bookmark was not moved to rev 3 if rev 2 was skipped during the
1234 Check that bookmark was not moved to rev 3 if rev 2 was skipped during the
1157 rebase operation. This makes sense because if rev 2 has a successor, the
1235 rebase operation. This makes sense because if rev 2 has a successor, the
1158 operation generating that successor (ex. rebase) should be responsible for
1236 operation generating that successor (ex. rebase) should be responsible for
1159 moving bookmarks. If the bookmark is on a precursor, like rev 2, that means the
1237 moving bookmarks. If the bookmark is on a precursor, like rev 2, that means the
1160 user manually moved it back. In that case we should not move it again.
1238 user manually moved it back. In that case we should not move it again.
1161 $ hg bookmarks
1239 $ hg bookmarks
1162 mybook 2:1e9a3c00cbe9
1240 mybook 2:1e9a3c00cbe9
1163 $ hg debugobsolete --rev tip
1241 $ hg debugobsolete --rev tip
1164 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (*) {'user': 'test'} (glob)
1242 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (*) {'user': 'test'} (glob)
General Comments 0
You need to be logged in to leave comments. Login now