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