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