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