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