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