##// END OF EJS Templates
continue: added support for rebase...
Taapas Agrawal -
r42832:35ebdbb3 default
parent child Browse files
Show More
@@ -1,1959 +1,1971 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 nullrev,
24 nullrev,
25 short,
25 short,
26 )
26 )
27 from mercurial import (
27 from mercurial import (
28 bookmarks,
28 bookmarks,
29 cmdutil,
29 cmdutil,
30 commands,
30 commands,
31 copies,
31 copies,
32 destutil,
32 destutil,
33 dirstateguard,
33 dirstateguard,
34 error,
34 error,
35 extensions,
35 extensions,
36 hg,
36 hg,
37 merge as mergemod,
37 merge as mergemod,
38 mergeutil,
38 mergeutil,
39 obsolete,
39 obsolete,
40 obsutil,
40 obsutil,
41 patch,
41 patch,
42 phases,
42 phases,
43 pycompat,
43 pycompat,
44 registrar,
44 registrar,
45 repair,
45 repair,
46 revset,
46 revset,
47 revsetlang,
47 revsetlang,
48 scmutil,
48 scmutil,
49 smartset,
49 smartset,
50 state as statemod,
50 state as statemod,
51 util,
51 util,
52 )
52 )
53
53
54 # The following constants are used throughout the rebase module. The ordering of
54 # The following constants are used throughout the rebase module. The ordering of
55 # their values must be maintained.
55 # their values must be maintained.
56
56
57 # Indicates that a revision needs to be rebased
57 # Indicates that a revision needs to be rebased
58 revtodo = -1
58 revtodo = -1
59 revtodostr = '-1'
59 revtodostr = '-1'
60
60
61 # legacy revstates no longer needed in current code
61 # legacy revstates no longer needed in current code
62 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
62 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
63 legacystates = {'-2', '-3', '-4', '-5'}
63 legacystates = {'-2', '-3', '-4', '-5'}
64
64
65 cmdtable = {}
65 cmdtable = {}
66 command = registrar.command(cmdtable)
66 command = registrar.command(cmdtable)
67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
67 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
69 # be specifying the version(s) of Mercurial they are tested with, or
69 # be specifying the version(s) of Mercurial they are tested with, or
70 # leave the attribute unspecified.
70 # leave the attribute unspecified.
71 testedwith = 'ships-with-hg-core'
71 testedwith = 'ships-with-hg-core'
72
72
73 def _nothingtorebase():
73 def _nothingtorebase():
74 return 1
74 return 1
75
75
76 def _savegraft(ctx, extra):
76 def _savegraft(ctx, extra):
77 s = ctx.extra().get('source', None)
77 s = ctx.extra().get('source', None)
78 if s is not None:
78 if s is not None:
79 extra['source'] = s
79 extra['source'] = s
80 s = ctx.extra().get('intermediate-source', None)
80 s = ctx.extra().get('intermediate-source', None)
81 if s is not None:
81 if s is not None:
82 extra['intermediate-source'] = s
82 extra['intermediate-source'] = s
83
83
84 def _savebranch(ctx, extra):
84 def _savebranch(ctx, extra):
85 extra['branch'] = ctx.branch()
85 extra['branch'] = ctx.branch()
86
86
87 def _destrebase(repo, sourceset, destspace=None):
87 def _destrebase(repo, sourceset, destspace=None):
88 """small wrapper around destmerge to pass the right extra args
88 """small wrapper around destmerge to pass the right extra args
89
89
90 Please wrap destutil.destmerge instead."""
90 Please wrap destutil.destmerge instead."""
91 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
91 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
92 onheadcheck=False, destspace=destspace)
92 onheadcheck=False, destspace=destspace)
93
93
94 revsetpredicate = registrar.revsetpredicate()
94 revsetpredicate = registrar.revsetpredicate()
95
95
96 @revsetpredicate('_destrebase')
96 @revsetpredicate('_destrebase')
97 def _revsetdestrebase(repo, subset, x):
97 def _revsetdestrebase(repo, subset, x):
98 # ``_rebasedefaultdest()``
98 # ``_rebasedefaultdest()``
99
99
100 # default destination for rebase.
100 # default destination for rebase.
101 # # XXX: Currently private because I expect the signature to change.
101 # # XXX: Currently private because I expect the signature to change.
102 # # XXX: - bailing out in case of ambiguity vs returning all data.
102 # # XXX: - bailing out in case of ambiguity vs returning all data.
103 # i18n: "_rebasedefaultdest" is a keyword
103 # i18n: "_rebasedefaultdest" is a keyword
104 sourceset = None
104 sourceset = None
105 if x is not None:
105 if x is not None:
106 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
106 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
107 return subset & smartset.baseset([_destrebase(repo, sourceset)])
107 return subset & smartset.baseset([_destrebase(repo, sourceset)])
108
108
109 @revsetpredicate('_destautoorphanrebase')
109 @revsetpredicate('_destautoorphanrebase')
110 def _revsetdestautoorphanrebase(repo, subset, x):
110 def _revsetdestautoorphanrebase(repo, subset, x):
111 # ``_destautoorphanrebase()``
111 # ``_destautoorphanrebase()``
112
112
113 # automatic rebase destination for a single orphan revision.
113 # automatic rebase destination for a single orphan revision.
114 unfi = repo.unfiltered()
114 unfi = repo.unfiltered()
115 obsoleted = unfi.revs('obsolete()')
115 obsoleted = unfi.revs('obsolete()')
116
116
117 src = revset.getset(repo, subset, x).first()
117 src = revset.getset(repo, subset, x).first()
118
118
119 # Empty src or already obsoleted - Do not return a destination
119 # Empty src or already obsoleted - Do not return a destination
120 if not src or src in obsoleted:
120 if not src or src in obsoleted:
121 return smartset.baseset()
121 return smartset.baseset()
122 dests = destutil.orphanpossibledestination(repo, src)
122 dests = destutil.orphanpossibledestination(repo, src)
123 if len(dests) > 1:
123 if len(dests) > 1:
124 raise error.Abort(
124 raise error.Abort(
125 _("ambiguous automatic rebase: %r could end up on any of %r") % (
125 _("ambiguous automatic rebase: %r could end up on any of %r") % (
126 src, dests))
126 src, dests))
127 # We have zero or one destination, so we can just return here.
127 # We have zero or one destination, so we can just return here.
128 return smartset.baseset(dests)
128 return smartset.baseset(dests)
129
129
130 def _ctxdesc(ctx):
130 def _ctxdesc(ctx):
131 """short description for a context"""
131 """short description for a context"""
132 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
132 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
133 ctx.description().split('\n', 1)[0])
133 ctx.description().split('\n', 1)[0])
134 repo = ctx.repo()
134 repo = ctx.repo()
135 names = []
135 names = []
136 for nsname, ns in repo.names.iteritems():
136 for nsname, ns in repo.names.iteritems():
137 if nsname == 'branches':
137 if nsname == 'branches':
138 continue
138 continue
139 names.extend(ns.names(repo, ctx.node()))
139 names.extend(ns.names(repo, ctx.node()))
140 if names:
140 if names:
141 desc += ' (%s)' % ' '.join(names)
141 desc += ' (%s)' % ' '.join(names)
142 return desc
142 return desc
143
143
144 class rebaseruntime(object):
144 class rebaseruntime(object):
145 """This class is a container for rebase runtime state"""
145 """This class is a container for rebase runtime state"""
146 def __init__(self, repo, ui, inmemory=False, opts=None):
146 def __init__(self, repo, ui, inmemory=False, opts=None):
147 if opts is None:
147 if opts is None:
148 opts = {}
148 opts = {}
149
149
150 # prepared: whether we have rebasestate prepared or not. Currently it
150 # prepared: whether we have rebasestate prepared or not. Currently it
151 # decides whether "self.repo" is unfiltered or not.
151 # decides whether "self.repo" is unfiltered or not.
152 # The rebasestate has explicit hash to hash instructions not depending
152 # The rebasestate has explicit hash to hash instructions not depending
153 # on visibility. If rebasestate exists (in-memory or on-disk), use
153 # on visibility. If rebasestate exists (in-memory or on-disk), use
154 # unfiltered repo to avoid visibility issues.
154 # unfiltered repo to avoid visibility issues.
155 # Before knowing rebasestate (i.e. when starting a new rebase (not
155 # Before knowing rebasestate (i.e. when starting a new rebase (not
156 # --continue or --abort)), the original repo should be used so
156 # --continue or --abort)), the original repo should be used so
157 # visibility-dependent revsets are correct.
157 # visibility-dependent revsets are correct.
158 self.prepared = False
158 self.prepared = False
159 self._repo = repo
159 self._repo = repo
160
160
161 self.ui = ui
161 self.ui = ui
162 self.opts = opts
162 self.opts = opts
163 self.originalwd = None
163 self.originalwd = None
164 self.external = nullrev
164 self.external = nullrev
165 # Mapping between the old revision id and either what is the new rebased
165 # Mapping between the old revision id and either what is the new rebased
166 # revision or what needs to be done with the old revision. The state
166 # revision or what needs to be done with the old revision. The state
167 # dict will be what contains most of the rebase progress state.
167 # dict will be what contains most of the rebase progress state.
168 self.state = {}
168 self.state = {}
169 self.activebookmark = None
169 self.activebookmark = None
170 self.destmap = {}
170 self.destmap = {}
171 self.skipped = set()
171 self.skipped = set()
172
172
173 self.collapsef = opts.get('collapse', False)
173 self.collapsef = opts.get('collapse', False)
174 self.collapsemsg = cmdutil.logmessage(ui, opts)
174 self.collapsemsg = cmdutil.logmessage(ui, opts)
175 self.date = opts.get('date', None)
175 self.date = opts.get('date', None)
176
176
177 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
177 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
178 self.extrafns = [_savegraft]
178 self.extrafns = [_savegraft]
179 if e:
179 if e:
180 self.extrafns = [e]
180 self.extrafns = [e]
181
181
182 self.backupf = ui.configbool('rewrite', 'backup-bundle')
182 self.backupf = ui.configbool('rewrite', 'backup-bundle')
183 self.keepf = opts.get('keep', False)
183 self.keepf = opts.get('keep', False)
184 self.keepbranchesf = opts.get('keepbranches', False)
184 self.keepbranchesf = opts.get('keepbranches', False)
185 self.obsoletenotrebased = {}
185 self.obsoletenotrebased = {}
186 self.obsoletewithoutsuccessorindestination = set()
186 self.obsoletewithoutsuccessorindestination = set()
187 self.inmemory = inmemory
187 self.inmemory = inmemory
188 self.stateobj = statemod.cmdstate(repo, 'rebasestate')
188 self.stateobj = statemod.cmdstate(repo, 'rebasestate')
189
189
190 @property
190 @property
191 def repo(self):
191 def repo(self):
192 if self.prepared:
192 if self.prepared:
193 return self._repo.unfiltered()
193 return self._repo.unfiltered()
194 else:
194 else:
195 return self._repo
195 return self._repo
196
196
197 def storestatus(self, tr=None):
197 def storestatus(self, tr=None):
198 """Store the current status to allow recovery"""
198 """Store the current status to allow recovery"""
199 if tr:
199 if tr:
200 tr.addfilegenerator('rebasestate', ('rebasestate',),
200 tr.addfilegenerator('rebasestate', ('rebasestate',),
201 self._writestatus, location='plain')
201 self._writestatus, location='plain')
202 else:
202 else:
203 with self.repo.vfs("rebasestate", "w") as f:
203 with self.repo.vfs("rebasestate", "w") as f:
204 self._writestatus(f)
204 self._writestatus(f)
205
205
206 def _writestatus(self, f):
206 def _writestatus(self, f):
207 repo = self.repo
207 repo = self.repo
208 assert repo.filtername is None
208 assert repo.filtername is None
209 f.write(repo[self.originalwd].hex() + '\n')
209 f.write(repo[self.originalwd].hex() + '\n')
210 # was "dest". we now write dest per src root below.
210 # was "dest". we now write dest per src root below.
211 f.write('\n')
211 f.write('\n')
212 f.write(repo[self.external].hex() + '\n')
212 f.write(repo[self.external].hex() + '\n')
213 f.write('%d\n' % int(self.collapsef))
213 f.write('%d\n' % int(self.collapsef))
214 f.write('%d\n' % int(self.keepf))
214 f.write('%d\n' % int(self.keepf))
215 f.write('%d\n' % int(self.keepbranchesf))
215 f.write('%d\n' % int(self.keepbranchesf))
216 f.write('%s\n' % (self.activebookmark or ''))
216 f.write('%s\n' % (self.activebookmark or ''))
217 destmap = self.destmap
217 destmap = self.destmap
218 for d, v in self.state.iteritems():
218 for d, v in self.state.iteritems():
219 oldrev = repo[d].hex()
219 oldrev = repo[d].hex()
220 if v >= 0:
220 if v >= 0:
221 newrev = repo[v].hex()
221 newrev = repo[v].hex()
222 else:
222 else:
223 newrev = "%d" % v
223 newrev = "%d" % v
224 destnode = repo[destmap[d]].hex()
224 destnode = repo[destmap[d]].hex()
225 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
225 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
226 repo.ui.debug('rebase status stored\n')
226 repo.ui.debug('rebase status stored\n')
227
227
228 def restorestatus(self):
228 def restorestatus(self):
229 """Restore a previously stored status"""
229 """Restore a previously stored status"""
230 if not self.stateobj.exists():
230 if not self.stateobj.exists():
231 cmdutil.wrongtooltocontinue(self.repo, _('rebase'))
231 cmdutil.wrongtooltocontinue(self.repo, _('rebase'))
232
232
233 data = self._read()
233 data = self._read()
234 self.repo.ui.debug('rebase status resumed\n')
234 self.repo.ui.debug('rebase status resumed\n')
235
235
236 self.originalwd = data['originalwd']
236 self.originalwd = data['originalwd']
237 self.destmap = data['destmap']
237 self.destmap = data['destmap']
238 self.state = data['state']
238 self.state = data['state']
239 self.skipped = data['skipped']
239 self.skipped = data['skipped']
240 self.collapsef = data['collapse']
240 self.collapsef = data['collapse']
241 self.keepf = data['keep']
241 self.keepf = data['keep']
242 self.keepbranchesf = data['keepbranches']
242 self.keepbranchesf = data['keepbranches']
243 self.external = data['external']
243 self.external = data['external']
244 self.activebookmark = data['activebookmark']
244 self.activebookmark = data['activebookmark']
245
245
246 def _read(self):
246 def _read(self):
247 self.prepared = True
247 self.prepared = True
248 repo = self.repo
248 repo = self.repo
249 assert repo.filtername is None
249 assert repo.filtername is None
250 data = {'keepbranches': None, 'collapse': None, 'activebookmark': None,
250 data = {'keepbranches': None, 'collapse': None, 'activebookmark': None,
251 'external': nullrev, 'keep': None, 'originalwd': None}
251 'external': nullrev, 'keep': None, 'originalwd': None}
252 legacydest = None
252 legacydest = None
253 state = {}
253 state = {}
254 destmap = {}
254 destmap = {}
255
255
256 if True:
256 if True:
257 f = repo.vfs("rebasestate")
257 f = repo.vfs("rebasestate")
258 for i, l in enumerate(f.read().splitlines()):
258 for i, l in enumerate(f.read().splitlines()):
259 if i == 0:
259 if i == 0:
260 data['originalwd'] = repo[l].rev()
260 data['originalwd'] = repo[l].rev()
261 elif i == 1:
261 elif i == 1:
262 # this line should be empty in newer version. but legacy
262 # this line should be empty in newer version. but legacy
263 # clients may still use it
263 # clients may still use it
264 if l:
264 if l:
265 legacydest = repo[l].rev()
265 legacydest = repo[l].rev()
266 elif i == 2:
266 elif i == 2:
267 data['external'] = repo[l].rev()
267 data['external'] = repo[l].rev()
268 elif i == 3:
268 elif i == 3:
269 data['collapse'] = bool(int(l))
269 data['collapse'] = bool(int(l))
270 elif i == 4:
270 elif i == 4:
271 data['keep'] = bool(int(l))
271 data['keep'] = bool(int(l))
272 elif i == 5:
272 elif i == 5:
273 data['keepbranches'] = bool(int(l))
273 data['keepbranches'] = bool(int(l))
274 elif i == 6 and not (len(l) == 81 and ':' in l):
274 elif i == 6 and not (len(l) == 81 and ':' in l):
275 # line 6 is a recent addition, so for backwards
275 # line 6 is a recent addition, so for backwards
276 # compatibility check that the line doesn't look like the
276 # compatibility check that the line doesn't look like the
277 # oldrev:newrev lines
277 # oldrev:newrev lines
278 data['activebookmark'] = l
278 data['activebookmark'] = l
279 else:
279 else:
280 args = l.split(':')
280 args = l.split(':')
281 oldrev = repo[args[0]].rev()
281 oldrev = repo[args[0]].rev()
282 newrev = args[1]
282 newrev = args[1]
283 if newrev in legacystates:
283 if newrev in legacystates:
284 continue
284 continue
285 if len(args) > 2:
285 if len(args) > 2:
286 destrev = repo[args[2]].rev()
286 destrev = repo[args[2]].rev()
287 else:
287 else:
288 destrev = legacydest
288 destrev = legacydest
289 destmap[oldrev] = destrev
289 destmap[oldrev] = destrev
290 if newrev == revtodostr:
290 if newrev == revtodostr:
291 state[oldrev] = revtodo
291 state[oldrev] = revtodo
292 # Legacy compat special case
292 # Legacy compat special case
293 else:
293 else:
294 state[oldrev] = repo[newrev].rev()
294 state[oldrev] = repo[newrev].rev()
295
295
296 if data['keepbranches'] is None:
296 if data['keepbranches'] is None:
297 raise error.Abort(_('.hg/rebasestate is incomplete'))
297 raise error.Abort(_('.hg/rebasestate is incomplete'))
298
298
299 data['destmap'] = destmap
299 data['destmap'] = destmap
300 data['state'] = state
300 data['state'] = state
301 skipped = set()
301 skipped = set()
302 # recompute the set of skipped revs
302 # recompute the set of skipped revs
303 if not data['collapse']:
303 if not data['collapse']:
304 seen = set(destmap.values())
304 seen = set(destmap.values())
305 for old, new in sorted(state.items()):
305 for old, new in sorted(state.items()):
306 if new != revtodo and new in seen:
306 if new != revtodo and new in seen:
307 skipped.add(old)
307 skipped.add(old)
308 seen.add(new)
308 seen.add(new)
309 data['skipped'] = skipped
309 data['skipped'] = skipped
310 repo.ui.debug('computed skipped revs: %s\n' %
310 repo.ui.debug('computed skipped revs: %s\n' %
311 (' '.join('%d' % r for r in sorted(skipped)) or ''))
311 (' '.join('%d' % r for r in sorted(skipped)) or ''))
312
312
313 return data
313 return data
314
314
315 def _handleskippingobsolete(self, obsoleterevs, destmap):
315 def _handleskippingobsolete(self, obsoleterevs, destmap):
316 """Compute structures necessary for skipping obsolete revisions
316 """Compute structures necessary for skipping obsolete revisions
317
317
318 obsoleterevs: iterable of all obsolete revisions in rebaseset
318 obsoleterevs: iterable of all obsolete revisions in rebaseset
319 destmap: {srcrev: destrev} destination revisions
319 destmap: {srcrev: destrev} destination revisions
320 """
320 """
321 self.obsoletenotrebased = {}
321 self.obsoletenotrebased = {}
322 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
322 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
323 return
323 return
324 obsoleteset = set(obsoleterevs)
324 obsoleteset = set(obsoleterevs)
325 (self.obsoletenotrebased,
325 (self.obsoletenotrebased,
326 self.obsoletewithoutsuccessorindestination,
326 self.obsoletewithoutsuccessorindestination,
327 obsoleteextinctsuccessors) = _computeobsoletenotrebased(
327 obsoleteextinctsuccessors) = _computeobsoletenotrebased(
328 self.repo, obsoleteset, destmap)
328 self.repo, obsoleteset, destmap)
329 skippedset = set(self.obsoletenotrebased)
329 skippedset = set(self.obsoletenotrebased)
330 skippedset.update(self.obsoletewithoutsuccessorindestination)
330 skippedset.update(self.obsoletewithoutsuccessorindestination)
331 skippedset.update(obsoleteextinctsuccessors)
331 skippedset.update(obsoleteextinctsuccessors)
332 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
332 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
333
333
334 def _prepareabortorcontinue(self, isabort, backup=True, suppwarns=False):
334 def _prepareabortorcontinue(self, isabort, backup=True, suppwarns=False):
335 try:
335 try:
336 self.restorestatus()
336 self.restorestatus()
337 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
337 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
338 except error.RepoLookupError:
338 except error.RepoLookupError:
339 if isabort:
339 if isabort:
340 clearstatus(self.repo)
340 clearstatus(self.repo)
341 clearcollapsemsg(self.repo)
341 clearcollapsemsg(self.repo)
342 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
342 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
343 ' only broken state is cleared)\n'))
343 ' only broken state is cleared)\n'))
344 return 0
344 return 0
345 else:
345 else:
346 msg = _('cannot continue inconsistent rebase')
346 msg = _('cannot continue inconsistent rebase')
347 hint = _('use "hg rebase --abort" to clear broken state')
347 hint = _('use "hg rebase --abort" to clear broken state')
348 raise error.Abort(msg, hint=hint)
348 raise error.Abort(msg, hint=hint)
349
349
350 if isabort:
350 if isabort:
351 backup = backup and self.backupf
351 backup = backup and self.backupf
352 return self._abort(backup=backup, suppwarns=suppwarns)
352 return self._abort(backup=backup, suppwarns=suppwarns)
353
353
354 def _preparenewrebase(self, destmap):
354 def _preparenewrebase(self, destmap):
355 if not destmap:
355 if not destmap:
356 return _nothingtorebase()
356 return _nothingtorebase()
357
357
358 rebaseset = destmap.keys()
358 rebaseset = destmap.keys()
359 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
359 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
360 if (not (self.keepf or allowunstable)
360 if (not (self.keepf or allowunstable)
361 and self.repo.revs('first(children(%ld) - %ld)',
361 and self.repo.revs('first(children(%ld) - %ld)',
362 rebaseset, rebaseset)):
362 rebaseset, rebaseset)):
363 raise error.Abort(
363 raise error.Abort(
364 _("can't remove original changesets with"
364 _("can't remove original changesets with"
365 " unrebased descendants"),
365 " unrebased descendants"),
366 hint=_('use --keep to keep original changesets'))
366 hint=_('use --keep to keep original changesets'))
367
367
368 result = buildstate(self.repo, destmap, self.collapsef)
368 result = buildstate(self.repo, destmap, self.collapsef)
369
369
370 if not result:
370 if not result:
371 # Empty state built, nothing to rebase
371 # Empty state built, nothing to rebase
372 self.ui.status(_('nothing to rebase\n'))
372 self.ui.status(_('nothing to rebase\n'))
373 return _nothingtorebase()
373 return _nothingtorebase()
374
374
375 for root in self.repo.set('roots(%ld)', rebaseset):
375 for root in self.repo.set('roots(%ld)', rebaseset):
376 if not self.keepf and not root.mutable():
376 if not self.keepf and not root.mutable():
377 raise error.Abort(_("can't rebase public changeset %s")
377 raise error.Abort(_("can't rebase public changeset %s")
378 % root,
378 % root,
379 hint=_("see 'hg help phases' for details"))
379 hint=_("see 'hg help phases' for details"))
380
380
381 (self.originalwd, self.destmap, self.state) = result
381 (self.originalwd, self.destmap, self.state) = result
382 if self.collapsef:
382 if self.collapsef:
383 dests = set(self.destmap.values())
383 dests = set(self.destmap.values())
384 if len(dests) != 1:
384 if len(dests) != 1:
385 raise error.Abort(
385 raise error.Abort(
386 _('--collapse does not work with multiple destinations'))
386 _('--collapse does not work with multiple destinations'))
387 destrev = next(iter(dests))
387 destrev = next(iter(dests))
388 destancestors = self.repo.changelog.ancestors([destrev],
388 destancestors = self.repo.changelog.ancestors([destrev],
389 inclusive=True)
389 inclusive=True)
390 self.external = externalparent(self.repo, self.state, destancestors)
390 self.external = externalparent(self.repo, self.state, destancestors)
391
391
392 for destrev in sorted(set(destmap.values())):
392 for destrev in sorted(set(destmap.values())):
393 dest = self.repo[destrev]
393 dest = self.repo[destrev]
394 if dest.closesbranch() and not self.keepbranchesf:
394 if dest.closesbranch() and not self.keepbranchesf:
395 self.ui.status(_('reopening closed branch head %s\n') % dest)
395 self.ui.status(_('reopening closed branch head %s\n') % dest)
396
396
397 self.prepared = True
397 self.prepared = True
398
398
399 def _assignworkingcopy(self):
399 def _assignworkingcopy(self):
400 if self.inmemory:
400 if self.inmemory:
401 from mercurial.context import overlayworkingctx
401 from mercurial.context import overlayworkingctx
402 self.wctx = overlayworkingctx(self.repo)
402 self.wctx = overlayworkingctx(self.repo)
403 self.repo.ui.debug("rebasing in-memory\n")
403 self.repo.ui.debug("rebasing in-memory\n")
404 else:
404 else:
405 self.wctx = self.repo[None]
405 self.wctx = self.repo[None]
406 self.repo.ui.debug("rebasing on disk\n")
406 self.repo.ui.debug("rebasing on disk\n")
407 self.repo.ui.log("rebase",
407 self.repo.ui.log("rebase",
408 "using in-memory rebase: %r\n", self.inmemory,
408 "using in-memory rebase: %r\n", self.inmemory,
409 rebase_imm_used=self.inmemory)
409 rebase_imm_used=self.inmemory)
410
410
411 def _performrebase(self, tr):
411 def _performrebase(self, tr):
412 self._assignworkingcopy()
412 self._assignworkingcopy()
413 repo, ui = self.repo, self.ui
413 repo, ui = self.repo, self.ui
414 if self.keepbranchesf:
414 if self.keepbranchesf:
415 # insert _savebranch at the start of extrafns so if
415 # insert _savebranch at the start of extrafns so if
416 # there's a user-provided extrafn it can clobber branch if
416 # there's a user-provided extrafn it can clobber branch if
417 # desired
417 # desired
418 self.extrafns.insert(0, _savebranch)
418 self.extrafns.insert(0, _savebranch)
419 if self.collapsef:
419 if self.collapsef:
420 branches = set()
420 branches = set()
421 for rev in self.state:
421 for rev in self.state:
422 branches.add(repo[rev].branch())
422 branches.add(repo[rev].branch())
423 if len(branches) > 1:
423 if len(branches) > 1:
424 raise error.Abort(_('cannot collapse multiple named '
424 raise error.Abort(_('cannot collapse multiple named '
425 'branches'))
425 'branches'))
426
426
427 # Calculate self.obsoletenotrebased
427 # Calculate self.obsoletenotrebased
428 obsrevs = _filterobsoleterevs(self.repo, self.state)
428 obsrevs = _filterobsoleterevs(self.repo, self.state)
429 self._handleskippingobsolete(obsrevs, self.destmap)
429 self._handleskippingobsolete(obsrevs, self.destmap)
430
430
431 # Keep track of the active bookmarks in order to reset them later
431 # Keep track of the active bookmarks in order to reset them later
432 self.activebookmark = self.activebookmark or repo._activebookmark
432 self.activebookmark = self.activebookmark or repo._activebookmark
433 if self.activebookmark:
433 if self.activebookmark:
434 bookmarks.deactivate(repo)
434 bookmarks.deactivate(repo)
435
435
436 # Store the state before we begin so users can run 'hg rebase --abort'
436 # Store the state before we begin so users can run 'hg rebase --abort'
437 # if we fail before the transaction closes.
437 # if we fail before the transaction closes.
438 self.storestatus()
438 self.storestatus()
439 if tr:
439 if tr:
440 # When using single transaction, store state when transaction
440 # When using single transaction, store state when transaction
441 # commits.
441 # commits.
442 self.storestatus(tr)
442 self.storestatus(tr)
443
443
444 cands = [k for k, v in self.state.iteritems() if v == revtodo]
444 cands = [k for k, v in self.state.iteritems() if v == revtodo]
445 p = repo.ui.makeprogress(_("rebasing"), unit=_('changesets'),
445 p = repo.ui.makeprogress(_("rebasing"), unit=_('changesets'),
446 total=len(cands))
446 total=len(cands))
447 def progress(ctx):
447 def progress(ctx):
448 p.increment(item=("%d:%s" % (ctx.rev(), ctx)))
448 p.increment(item=("%d:%s" % (ctx.rev(), ctx)))
449 allowdivergence = self.ui.configbool(
449 allowdivergence = self.ui.configbool(
450 'experimental', 'evolution.allowdivergence')
450 'experimental', 'evolution.allowdivergence')
451 for subset in sortsource(self.destmap):
451 for subset in sortsource(self.destmap):
452 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
452 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
453 if not allowdivergence:
453 if not allowdivergence:
454 sortedrevs -= self.repo.revs(
454 sortedrevs -= self.repo.revs(
455 'descendants(%ld) and not %ld',
455 'descendants(%ld) and not %ld',
456 self.obsoletewithoutsuccessorindestination,
456 self.obsoletewithoutsuccessorindestination,
457 self.obsoletewithoutsuccessorindestination,
457 self.obsoletewithoutsuccessorindestination,
458 )
458 )
459 for rev in sortedrevs:
459 for rev in sortedrevs:
460 self._rebasenode(tr, rev, allowdivergence, progress)
460 self._rebasenode(tr, rev, allowdivergence, progress)
461 p.complete()
461 p.complete()
462 ui.note(_('rebase merging completed\n'))
462 ui.note(_('rebase merging completed\n'))
463
463
464 def _concludenode(self, rev, p1, p2, editor, commitmsg=None):
464 def _concludenode(self, rev, p1, p2, editor, commitmsg=None):
465 '''Commit the wd changes with parents p1 and p2.
465 '''Commit the wd changes with parents p1 and p2.
466
466
467 Reuse commit info from rev but also store useful information in extra.
467 Reuse commit info from rev but also store useful information in extra.
468 Return node of committed revision.'''
468 Return node of committed revision.'''
469 repo = self.repo
469 repo = self.repo
470 ctx = repo[rev]
470 ctx = repo[rev]
471 if commitmsg is None:
471 if commitmsg is None:
472 commitmsg = ctx.description()
472 commitmsg = ctx.description()
473 date = self.date
473 date = self.date
474 if date is None:
474 if date is None:
475 date = ctx.date()
475 date = ctx.date()
476 extra = {'rebase_source': ctx.hex()}
476 extra = {'rebase_source': ctx.hex()}
477 for c in self.extrafns:
477 for c in self.extrafns:
478 c(ctx, extra)
478 c(ctx, extra)
479 keepbranch = self.keepbranchesf and repo[p1].branch() != ctx.branch()
479 keepbranch = self.keepbranchesf and repo[p1].branch() != ctx.branch()
480 destphase = max(ctx.phase(), phases.draft)
480 destphase = max(ctx.phase(), phases.draft)
481 overrides = {('phases', 'new-commit'): destphase}
481 overrides = {('phases', 'new-commit'): destphase}
482 if keepbranch:
482 if keepbranch:
483 overrides[('ui', 'allowemptycommit')] = True
483 overrides[('ui', 'allowemptycommit')] = True
484 with repo.ui.configoverride(overrides, 'rebase'):
484 with repo.ui.configoverride(overrides, 'rebase'):
485 if self.inmemory:
485 if self.inmemory:
486 newnode = commitmemorynode(repo, p1, p2,
486 newnode = commitmemorynode(repo, p1, p2,
487 wctx=self.wctx,
487 wctx=self.wctx,
488 extra=extra,
488 extra=extra,
489 commitmsg=commitmsg,
489 commitmsg=commitmsg,
490 editor=editor,
490 editor=editor,
491 user=ctx.user(),
491 user=ctx.user(),
492 date=date)
492 date=date)
493 mergemod.mergestate.clean(repo)
493 mergemod.mergestate.clean(repo)
494 else:
494 else:
495 newnode = commitnode(repo, p1, p2,
495 newnode = commitnode(repo, p1, p2,
496 extra=extra,
496 extra=extra,
497 commitmsg=commitmsg,
497 commitmsg=commitmsg,
498 editor=editor,
498 editor=editor,
499 user=ctx.user(),
499 user=ctx.user(),
500 date=date)
500 date=date)
501
501
502 if newnode is None:
502 if newnode is None:
503 # If it ended up being a no-op commit, then the normal
503 # If it ended up being a no-op commit, then the normal
504 # merge state clean-up path doesn't happen, so do it
504 # merge state clean-up path doesn't happen, so do it
505 # here. Fix issue5494
505 # here. Fix issue5494
506 mergemod.mergestate.clean(repo)
506 mergemod.mergestate.clean(repo)
507 return newnode
507 return newnode
508
508
509 def _rebasenode(self, tr, rev, allowdivergence, progressfn):
509 def _rebasenode(self, tr, rev, allowdivergence, progressfn):
510 repo, ui, opts = self.repo, self.ui, self.opts
510 repo, ui, opts = self.repo, self.ui, self.opts
511 dest = self.destmap[rev]
511 dest = self.destmap[rev]
512 ctx = repo[rev]
512 ctx = repo[rev]
513 desc = _ctxdesc(ctx)
513 desc = _ctxdesc(ctx)
514 if self.state[rev] == rev:
514 if self.state[rev] == rev:
515 ui.status(_('already rebased %s\n') % desc)
515 ui.status(_('already rebased %s\n') % desc)
516 elif (not allowdivergence
516 elif (not allowdivergence
517 and rev in self.obsoletewithoutsuccessorindestination):
517 and rev in self.obsoletewithoutsuccessorindestination):
518 msg = _('note: not rebasing %s and its descendants as '
518 msg = _('note: not rebasing %s and its descendants as '
519 'this would cause divergence\n') % desc
519 'this would cause divergence\n') % desc
520 repo.ui.status(msg)
520 repo.ui.status(msg)
521 self.skipped.add(rev)
521 self.skipped.add(rev)
522 elif rev in self.obsoletenotrebased:
522 elif rev in self.obsoletenotrebased:
523 succ = self.obsoletenotrebased[rev]
523 succ = self.obsoletenotrebased[rev]
524 if succ is None:
524 if succ is None:
525 msg = _('note: not rebasing %s, it has no '
525 msg = _('note: not rebasing %s, it has no '
526 'successor\n') % desc
526 'successor\n') % desc
527 else:
527 else:
528 succdesc = _ctxdesc(repo[succ])
528 succdesc = _ctxdesc(repo[succ])
529 msg = (_('note: not rebasing %s, already in '
529 msg = (_('note: not rebasing %s, already in '
530 'destination as %s\n') % (desc, succdesc))
530 'destination as %s\n') % (desc, succdesc))
531 repo.ui.status(msg)
531 repo.ui.status(msg)
532 # Make clearrebased aware state[rev] is not a true successor
532 # Make clearrebased aware state[rev] is not a true successor
533 self.skipped.add(rev)
533 self.skipped.add(rev)
534 # Record rev as moved to its desired destination in self.state.
534 # Record rev as moved to its desired destination in self.state.
535 # This helps bookmark and working parent movement.
535 # This helps bookmark and working parent movement.
536 dest = max(adjustdest(repo, rev, self.destmap, self.state,
536 dest = max(adjustdest(repo, rev, self.destmap, self.state,
537 self.skipped))
537 self.skipped))
538 self.state[rev] = dest
538 self.state[rev] = dest
539 elif self.state[rev] == revtodo:
539 elif self.state[rev] == revtodo:
540 ui.status(_('rebasing %s\n') % desc)
540 ui.status(_('rebasing %s\n') % desc)
541 progressfn(ctx)
541 progressfn(ctx)
542 p1, p2, base = defineparents(repo, rev, self.destmap,
542 p1, p2, base = defineparents(repo, rev, self.destmap,
543 self.state, self.skipped,
543 self.state, self.skipped,
544 self.obsoletenotrebased)
544 self.obsoletenotrebased)
545 if not self.inmemory and len(repo[None].parents()) == 2:
545 if not self.inmemory and len(repo[None].parents()) == 2:
546 repo.ui.debug('resuming interrupted rebase\n')
546 repo.ui.debug('resuming interrupted rebase\n')
547 else:
547 else:
548 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
548 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
549 with ui.configoverride(overrides, 'rebase'):
549 with ui.configoverride(overrides, 'rebase'):
550 stats = rebasenode(repo, rev, p1, base, self.collapsef,
550 stats = rebasenode(repo, rev, p1, base, self.collapsef,
551 dest, wctx=self.wctx)
551 dest, wctx=self.wctx)
552 if stats.unresolvedcount > 0:
552 if stats.unresolvedcount > 0:
553 if self.inmemory:
553 if self.inmemory:
554 raise error.InMemoryMergeConflictsError()
554 raise error.InMemoryMergeConflictsError()
555 else:
555 else:
556 raise error.InterventionRequired(
556 raise error.InterventionRequired(
557 _('unresolved conflicts (see hg '
557 _('unresolved conflicts (see hg '
558 'resolve, then hg rebase --continue)'))
558 'resolve, then hg rebase --continue)'))
559 if not self.collapsef:
559 if not self.collapsef:
560 merging = p2 != nullrev
560 merging = p2 != nullrev
561 editform = cmdutil.mergeeditform(merging, 'rebase')
561 editform = cmdutil.mergeeditform(merging, 'rebase')
562 editor = cmdutil.getcommiteditor(editform=editform,
562 editor = cmdutil.getcommiteditor(editform=editform,
563 **pycompat.strkwargs(opts))
563 **pycompat.strkwargs(opts))
564 newnode = self._concludenode(rev, p1, p2, editor)
564 newnode = self._concludenode(rev, p1, p2, editor)
565 else:
565 else:
566 # Skip commit if we are collapsing
566 # Skip commit if we are collapsing
567 if self.inmemory:
567 if self.inmemory:
568 self.wctx.setbase(repo[p1])
568 self.wctx.setbase(repo[p1])
569 else:
569 else:
570 repo.setparents(repo[p1].node())
570 repo.setparents(repo[p1].node())
571 newnode = None
571 newnode = None
572 # Update the state
572 # Update the state
573 if newnode is not None:
573 if newnode is not None:
574 self.state[rev] = repo[newnode].rev()
574 self.state[rev] = repo[newnode].rev()
575 ui.debug('rebased as %s\n' % short(newnode))
575 ui.debug('rebased as %s\n' % short(newnode))
576 else:
576 else:
577 if not self.collapsef:
577 if not self.collapsef:
578 ui.warn(_('note: not rebasing %s, its destination already '
578 ui.warn(_('note: not rebasing %s, its destination already '
579 'has all its changes\n') % desc)
579 'has all its changes\n') % desc)
580 self.skipped.add(rev)
580 self.skipped.add(rev)
581 self.state[rev] = p1
581 self.state[rev] = p1
582 ui.debug('next revision set to %d\n' % p1)
582 ui.debug('next revision set to %d\n' % p1)
583 else:
583 else:
584 ui.status(_('already rebased %s as %s\n') %
584 ui.status(_('already rebased %s as %s\n') %
585 (desc, repo[self.state[rev]]))
585 (desc, repo[self.state[rev]]))
586 if not tr:
586 if not tr:
587 # When not using single transaction, store state after each
587 # When not using single transaction, store state after each
588 # commit is completely done. On InterventionRequired, we thus
588 # commit is completely done. On InterventionRequired, we thus
589 # won't store the status. Instead, we'll hit the "len(parents) == 2"
589 # won't store the status. Instead, we'll hit the "len(parents) == 2"
590 # case and realize that the commit was in progress.
590 # case and realize that the commit was in progress.
591 self.storestatus()
591 self.storestatus()
592
592
593 def _finishrebase(self):
593 def _finishrebase(self):
594 repo, ui, opts = self.repo, self.ui, self.opts
594 repo, ui, opts = self.repo, self.ui, self.opts
595 fm = ui.formatter('rebase', opts)
595 fm = ui.formatter('rebase', opts)
596 fm.startitem()
596 fm.startitem()
597 if self.collapsef:
597 if self.collapsef:
598 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
598 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
599 self.state, self.skipped,
599 self.state, self.skipped,
600 self.obsoletenotrebased)
600 self.obsoletenotrebased)
601 editopt = opts.get('edit')
601 editopt = opts.get('edit')
602 editform = 'rebase.collapse'
602 editform = 'rebase.collapse'
603 if self.collapsemsg:
603 if self.collapsemsg:
604 commitmsg = self.collapsemsg
604 commitmsg = self.collapsemsg
605 else:
605 else:
606 commitmsg = 'Collapsed revision'
606 commitmsg = 'Collapsed revision'
607 for rebased in sorted(self.state):
607 for rebased in sorted(self.state):
608 if rebased not in self.skipped:
608 if rebased not in self.skipped:
609 commitmsg += '\n* %s' % repo[rebased].description()
609 commitmsg += '\n* %s' % repo[rebased].description()
610 editopt = True
610 editopt = True
611 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
611 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
612 revtoreuse = max(self.state)
612 revtoreuse = max(self.state)
613
613
614 newnode = self._concludenode(revtoreuse, p1, self.external,
614 newnode = self._concludenode(revtoreuse, p1, self.external,
615 editor, commitmsg=commitmsg)
615 editor, commitmsg=commitmsg)
616
616
617 if newnode is not None:
617 if newnode is not None:
618 newrev = repo[newnode].rev()
618 newrev = repo[newnode].rev()
619 for oldrev in self.state:
619 for oldrev in self.state:
620 self.state[oldrev] = newrev
620 self.state[oldrev] = newrev
621
621
622 if 'qtip' in repo.tags():
622 if 'qtip' in repo.tags():
623 updatemq(repo, self.state, self.skipped,
623 updatemq(repo, self.state, self.skipped,
624 **pycompat.strkwargs(opts))
624 **pycompat.strkwargs(opts))
625
625
626 # restore original working directory
626 # restore original working directory
627 # (we do this before stripping)
627 # (we do this before stripping)
628 newwd = self.state.get(self.originalwd, self.originalwd)
628 newwd = self.state.get(self.originalwd, self.originalwd)
629 if newwd < 0:
629 if newwd < 0:
630 # original directory is a parent of rebase set root or ignored
630 # original directory is a parent of rebase set root or ignored
631 newwd = self.originalwd
631 newwd = self.originalwd
632 if newwd not in [c.rev() for c in repo[None].parents()]:
632 if newwd not in [c.rev() for c in repo[None].parents()]:
633 ui.note(_("update back to initial working directory parent\n"))
633 ui.note(_("update back to initial working directory parent\n"))
634 hg.updaterepo(repo, newwd, overwrite=False)
634 hg.updaterepo(repo, newwd, overwrite=False)
635
635
636 collapsedas = None
636 collapsedas = None
637 if self.collapsef and not self.keepf:
637 if self.collapsef and not self.keepf:
638 collapsedas = newnode
638 collapsedas = newnode
639 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
639 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
640 collapsedas, self.keepf, fm=fm, backup=self.backupf)
640 collapsedas, self.keepf, fm=fm, backup=self.backupf)
641
641
642 clearstatus(repo)
642 clearstatus(repo)
643 clearcollapsemsg(repo)
643 clearcollapsemsg(repo)
644
644
645 ui.note(_("rebase completed\n"))
645 ui.note(_("rebase completed\n"))
646 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
646 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
647 if self.skipped:
647 if self.skipped:
648 skippedlen = len(self.skipped)
648 skippedlen = len(self.skipped)
649 ui.note(_("%d revisions have been skipped\n") % skippedlen)
649 ui.note(_("%d revisions have been skipped\n") % skippedlen)
650 fm.end()
650 fm.end()
651
651
652 if (self.activebookmark and self.activebookmark in repo._bookmarks and
652 if (self.activebookmark and self.activebookmark in repo._bookmarks and
653 repo['.'].node() == repo._bookmarks[self.activebookmark]):
653 repo['.'].node() == repo._bookmarks[self.activebookmark]):
654 bookmarks.activate(repo, self.activebookmark)
654 bookmarks.activate(repo, self.activebookmark)
655
655
656 def _abort(self, backup=True, suppwarns=False):
656 def _abort(self, backup=True, suppwarns=False):
657 '''Restore the repository to its original state.'''
657 '''Restore the repository to its original state.'''
658
658
659 repo = self.repo
659 repo = self.repo
660 try:
660 try:
661 # If the first commits in the rebased set get skipped during the
661 # If the first commits in the rebased set get skipped during the
662 # rebase, their values within the state mapping will be the dest
662 # rebase, their values within the state mapping will be the dest
663 # rev id. The rebased list must must not contain the dest rev
663 # rev id. The rebased list must must not contain the dest rev
664 # (issue4896)
664 # (issue4896)
665 rebased = [s for r, s in self.state.items()
665 rebased = [s for r, s in self.state.items()
666 if s >= 0 and s != r and s != self.destmap[r]]
666 if s >= 0 and s != r and s != self.destmap[r]]
667 immutable = [d for d in rebased if not repo[d].mutable()]
667 immutable = [d for d in rebased if not repo[d].mutable()]
668 cleanup = True
668 cleanup = True
669 if immutable:
669 if immutable:
670 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
670 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
671 % ', '.join(bytes(repo[r]) for r in immutable),
671 % ', '.join(bytes(repo[r]) for r in immutable),
672 hint=_("see 'hg help phases' for details"))
672 hint=_("see 'hg help phases' for details"))
673 cleanup = False
673 cleanup = False
674
674
675 descendants = set()
675 descendants = set()
676 if rebased:
676 if rebased:
677 descendants = set(repo.changelog.descendants(rebased))
677 descendants = set(repo.changelog.descendants(rebased))
678 if descendants - set(rebased):
678 if descendants - set(rebased):
679 repo.ui.warn(_("warning: new changesets detected on "
679 repo.ui.warn(_("warning: new changesets detected on "
680 "destination branch, can't strip\n"))
680 "destination branch, can't strip\n"))
681 cleanup = False
681 cleanup = False
682
682
683 if cleanup:
683 if cleanup:
684 shouldupdate = False
684 shouldupdate = False
685 if rebased:
685 if rebased:
686 strippoints = [
686 strippoints = [
687 c.node() for c in repo.set('roots(%ld)', rebased)]
687 c.node() for c in repo.set('roots(%ld)', rebased)]
688
688
689 updateifonnodes = set(rebased)
689 updateifonnodes = set(rebased)
690 updateifonnodes.update(self.destmap.values())
690 updateifonnodes.update(self.destmap.values())
691 updateifonnodes.add(self.originalwd)
691 updateifonnodes.add(self.originalwd)
692 shouldupdate = repo['.'].rev() in updateifonnodes
692 shouldupdate = repo['.'].rev() in updateifonnodes
693
693
694 # Update away from the rebase if necessary
694 # Update away from the rebase if necessary
695 if shouldupdate or needupdate(repo, self.state):
695 if shouldupdate or needupdate(repo, self.state):
696 mergemod.update(repo, self.originalwd, branchmerge=False,
696 mergemod.update(repo, self.originalwd, branchmerge=False,
697 force=True)
697 force=True)
698
698
699 # Strip from the first rebased revision
699 # Strip from the first rebased revision
700 if rebased:
700 if rebased:
701 repair.strip(repo.ui, repo, strippoints, backup=backup)
701 repair.strip(repo.ui, repo, strippoints, backup=backup)
702
702
703 if self.activebookmark and self.activebookmark in repo._bookmarks:
703 if self.activebookmark and self.activebookmark in repo._bookmarks:
704 bookmarks.activate(repo, self.activebookmark)
704 bookmarks.activate(repo, self.activebookmark)
705
705
706 finally:
706 finally:
707 clearstatus(repo)
707 clearstatus(repo)
708 clearcollapsemsg(repo)
708 clearcollapsemsg(repo)
709 if not suppwarns:
709 if not suppwarns:
710 repo.ui.warn(_('rebase aborted\n'))
710 repo.ui.warn(_('rebase aborted\n'))
711 return 0
711 return 0
712
712
713 @command('rebase',
713 @command('rebase',
714 [('s', 'source', '',
714 [('s', 'source', '',
715 _('rebase the specified changeset and descendants'), _('REV')),
715 _('rebase the specified changeset and descendants'), _('REV')),
716 ('b', 'base', '',
716 ('b', 'base', '',
717 _('rebase everything from branching point of specified changeset'),
717 _('rebase everything from branching point of specified changeset'),
718 _('REV')),
718 _('REV')),
719 ('r', 'rev', [],
719 ('r', 'rev', [],
720 _('rebase these revisions'),
720 _('rebase these revisions'),
721 _('REV')),
721 _('REV')),
722 ('d', 'dest', '',
722 ('d', 'dest', '',
723 _('rebase onto the specified changeset'), _('REV')),
723 _('rebase onto the specified changeset'), _('REV')),
724 ('', 'collapse', False, _('collapse the rebased changesets')),
724 ('', 'collapse', False, _('collapse the rebased changesets')),
725 ('m', 'message', '',
725 ('m', 'message', '',
726 _('use text as collapse commit message'), _('TEXT')),
726 _('use text as collapse commit message'), _('TEXT')),
727 ('e', 'edit', False, _('invoke editor on commit messages')),
727 ('e', 'edit', False, _('invoke editor on commit messages')),
728 ('l', 'logfile', '',
728 ('l', 'logfile', '',
729 _('read collapse commit message from file'), _('FILE')),
729 _('read collapse commit message from file'), _('FILE')),
730 ('k', 'keep', False, _('keep original changesets')),
730 ('k', 'keep', False, _('keep original changesets')),
731 ('', 'keepbranches', False, _('keep original branch names')),
731 ('', 'keepbranches', False, _('keep original branch names')),
732 ('D', 'detach', False, _('(DEPRECATED)')),
732 ('D', 'detach', False, _('(DEPRECATED)')),
733 ('i', 'interactive', False, _('(DEPRECATED)')),
733 ('i', 'interactive', False, _('(DEPRECATED)')),
734 ('t', 'tool', '', _('specify merge tool')),
734 ('t', 'tool', '', _('specify merge tool')),
735 ('', 'stop', False, _('stop interrupted rebase')),
735 ('', 'stop', False, _('stop interrupted rebase')),
736 ('c', 'continue', False, _('continue an interrupted rebase')),
736 ('c', 'continue', False, _('continue an interrupted rebase')),
737 ('a', 'abort', False, _('abort an interrupted rebase')),
737 ('a', 'abort', False, _('abort an interrupted rebase')),
738 ('', 'auto-orphans', '', _('automatically rebase orphan revisions '
738 ('', 'auto-orphans', '', _('automatically rebase orphan revisions '
739 'in the specified revset (EXPERIMENTAL)')),
739 'in the specified revset (EXPERIMENTAL)')),
740 ] + cmdutil.dryrunopts + cmdutil.formatteropts + cmdutil.confirmopts,
740 ] + cmdutil.dryrunopts + cmdutil.formatteropts + cmdutil.confirmopts,
741 _('[-s REV | -b REV] [-d REV] [OPTION]'),
741 _('[-s REV | -b REV] [-d REV] [OPTION]'),
742 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
742 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
743 def rebase(ui, repo, **opts):
743 def rebase(ui, repo, **opts):
744 """move changeset (and descendants) to a different branch
744 """move changeset (and descendants) to a different branch
745
745
746 Rebase uses repeated merging to graft changesets from one part of
746 Rebase uses repeated merging to graft changesets from one part of
747 history (the source) onto another (the destination). This can be
747 history (the source) onto another (the destination). This can be
748 useful for linearizing *local* changes relative to a master
748 useful for linearizing *local* changes relative to a master
749 development tree.
749 development tree.
750
750
751 Published commits cannot be rebased (see :hg:`help phases`).
751 Published commits cannot be rebased (see :hg:`help phases`).
752 To copy commits, see :hg:`help graft`.
752 To copy commits, see :hg:`help graft`.
753
753
754 If you don't specify a destination changeset (``-d/--dest``), rebase
754 If you don't specify a destination changeset (``-d/--dest``), rebase
755 will use the same logic as :hg:`merge` to pick a destination. if
755 will use the same logic as :hg:`merge` to pick a destination. if
756 the current branch contains exactly one other head, the other head
756 the current branch contains exactly one other head, the other head
757 is merged with by default. Otherwise, an explicit revision with
757 is merged with by default. Otherwise, an explicit revision with
758 which to merge with must be provided. (destination changeset is not
758 which to merge with must be provided. (destination changeset is not
759 modified by rebasing, but new changesets are added as its
759 modified by rebasing, but new changesets are added as its
760 descendants.)
760 descendants.)
761
761
762 Here are the ways to select changesets:
762 Here are the ways to select changesets:
763
763
764 1. Explicitly select them using ``--rev``.
764 1. Explicitly select them using ``--rev``.
765
765
766 2. Use ``--source`` to select a root changeset and include all of its
766 2. Use ``--source`` to select a root changeset and include all of its
767 descendants.
767 descendants.
768
768
769 3. Use ``--base`` to select a changeset; rebase will find ancestors
769 3. Use ``--base`` to select a changeset; rebase will find ancestors
770 and their descendants which are not also ancestors of the destination.
770 and their descendants which are not also ancestors of the destination.
771
771
772 4. If you do not specify any of ``--rev``, ``--source``, or ``--base``,
772 4. If you do not specify any of ``--rev``, ``--source``, or ``--base``,
773 rebase will use ``--base .`` as above.
773 rebase will use ``--base .`` as above.
774
774
775 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
775 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
776 can be used in ``--dest``. Destination would be calculated per source
776 can be used in ``--dest``. Destination would be calculated per source
777 revision with ``SRC`` substituted by that single source revision and
777 revision with ``SRC`` substituted by that single source revision and
778 ``ALLSRC`` substituted by all source revisions.
778 ``ALLSRC`` substituted by all source revisions.
779
779
780 Rebase will destroy original changesets unless you use ``--keep``.
780 Rebase will destroy original changesets unless you use ``--keep``.
781 It will also move your bookmarks (even if you do).
781 It will also move your bookmarks (even if you do).
782
782
783 Some changesets may be dropped if they do not contribute changes
783 Some changesets may be dropped if they do not contribute changes
784 (e.g. merges from the destination branch).
784 (e.g. merges from the destination branch).
785
785
786 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
786 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
787 a named branch with two heads. You will need to explicitly specify source
787 a named branch with two heads. You will need to explicitly specify source
788 and/or destination.
788 and/or destination.
789
789
790 If you need to use a tool to automate merge/conflict decisions, you
790 If you need to use a tool to automate merge/conflict decisions, you
791 can specify one with ``--tool``, see :hg:`help merge-tools`.
791 can specify one with ``--tool``, see :hg:`help merge-tools`.
792 As a caveat: the tool will not be used to mediate when a file was
792 As a caveat: the tool will not be used to mediate when a file was
793 deleted, there is no hook presently available for this.
793 deleted, there is no hook presently available for this.
794
794
795 If a rebase is interrupted to manually resolve a conflict, it can be
795 If a rebase is interrupted to manually resolve a conflict, it can be
796 continued with --continue/-c, aborted with --abort/-a, or stopped with
796 continued with --continue/-c, aborted with --abort/-a, or stopped with
797 --stop.
797 --stop.
798
798
799 .. container:: verbose
799 .. container:: verbose
800
800
801 Examples:
801 Examples:
802
802
803 - move "local changes" (current commit back to branching point)
803 - move "local changes" (current commit back to branching point)
804 to the current branch tip after a pull::
804 to the current branch tip after a pull::
805
805
806 hg rebase
806 hg rebase
807
807
808 - move a single changeset to the stable branch::
808 - move a single changeset to the stable branch::
809
809
810 hg rebase -r 5f493448 -d stable
810 hg rebase -r 5f493448 -d stable
811
811
812 - splice a commit and all its descendants onto another part of history::
812 - splice a commit and all its descendants onto another part of history::
813
813
814 hg rebase --source c0c3 --dest 4cf9
814 hg rebase --source c0c3 --dest 4cf9
815
815
816 - rebase everything on a branch marked by a bookmark onto the
816 - rebase everything on a branch marked by a bookmark onto the
817 default branch::
817 default branch::
818
818
819 hg rebase --base myfeature --dest default
819 hg rebase --base myfeature --dest default
820
820
821 - collapse a sequence of changes into a single commit::
821 - collapse a sequence of changes into a single commit::
822
822
823 hg rebase --collapse -r 1520:1525 -d .
823 hg rebase --collapse -r 1520:1525 -d .
824
824
825 - move a named branch while preserving its name::
825 - move a named branch while preserving its name::
826
826
827 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
827 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
828
828
829 - stabilize orphaned changesets so history looks linear::
829 - stabilize orphaned changesets so history looks linear::
830
830
831 hg rebase -r 'orphan()-obsolete()'\
831 hg rebase -r 'orphan()-obsolete()'\
832 -d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
832 -d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
833 max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
833 max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
834
834
835 Configuration Options:
835 Configuration Options:
836
836
837 You can make rebase require a destination if you set the following config
837 You can make rebase require a destination if you set the following config
838 option::
838 option::
839
839
840 [commands]
840 [commands]
841 rebase.requiredest = True
841 rebase.requiredest = True
842
842
843 By default, rebase will close the transaction after each commit. For
843 By default, rebase will close the transaction after each commit. For
844 performance purposes, you can configure rebase to use a single transaction
844 performance purposes, you can configure rebase to use a single transaction
845 across the entire rebase. WARNING: This setting introduces a significant
845 across the entire rebase. WARNING: This setting introduces a significant
846 risk of losing the work you've done in a rebase if the rebase aborts
846 risk of losing the work you've done in a rebase if the rebase aborts
847 unexpectedly::
847 unexpectedly::
848
848
849 [rebase]
849 [rebase]
850 singletransaction = True
850 singletransaction = True
851
851
852 By default, rebase writes to the working copy, but you can configure it to
852 By default, rebase writes to the working copy, but you can configure it to
853 run in-memory for better performance. When the rebase is not moving the
853 run in-memory for better performance. When the rebase is not moving the
854 parent(s) of the working copy (AKA the "currently checked out changesets"),
854 parent(s) of the working copy (AKA the "currently checked out changesets"),
855 this may also allow it to run even if the working copy is dirty::
855 this may also allow it to run even if the working copy is dirty::
856
856
857 [rebase]
857 [rebase]
858 experimental.inmemory = True
858 experimental.inmemory = True
859
859
860 Return Values:
860 Return Values:
861
861
862 Returns 0 on success, 1 if nothing to rebase or there are
862 Returns 0 on success, 1 if nothing to rebase or there are
863 unresolved conflicts.
863 unresolved conflicts.
864
864
865 """
865 """
866 opts = pycompat.byteskwargs(opts)
866 opts = pycompat.byteskwargs(opts)
867 inmemory = ui.configbool('rebase', 'experimental.inmemory')
867 inmemory = ui.configbool('rebase', 'experimental.inmemory')
868 dryrun = opts.get('dry_run')
868 dryrun = opts.get('dry_run')
869 confirm = opts.get('confirm')
869 confirm = opts.get('confirm')
870 selactions = [k for k in ['abort', 'stop', 'continue'] if opts.get(k)]
870 selactions = [k for k in ['abort', 'stop', 'continue'] if opts.get(k)]
871 if len(selactions) > 1:
871 if len(selactions) > 1:
872 raise error.Abort(_('cannot use --%s with --%s')
872 raise error.Abort(_('cannot use --%s with --%s')
873 % tuple(selactions[:2]))
873 % tuple(selactions[:2]))
874 action = selactions[0] if selactions else None
874 action = selactions[0] if selactions else None
875 if dryrun and action:
875 if dryrun and action:
876 raise error.Abort(_('cannot specify both --dry-run and --%s') % action)
876 raise error.Abort(_('cannot specify both --dry-run and --%s') % action)
877 if confirm and action:
877 if confirm and action:
878 raise error.Abort(_('cannot specify both --confirm and --%s') % action)
878 raise error.Abort(_('cannot specify both --confirm and --%s') % action)
879 if dryrun and confirm:
879 if dryrun and confirm:
880 raise error.Abort(_('cannot specify both --confirm and --dry-run'))
880 raise error.Abort(_('cannot specify both --confirm and --dry-run'))
881
881
882 if action or repo.currenttransaction() is not None:
882 if action or repo.currenttransaction() is not None:
883 # in-memory rebase is not compatible with resuming rebases.
883 # in-memory rebase is not compatible with resuming rebases.
884 # (Or if it is run within a transaction, since the restart logic can
884 # (Or if it is run within a transaction, since the restart logic can
885 # fail the entire transaction.)
885 # fail the entire transaction.)
886 inmemory = False
886 inmemory = False
887
887
888 if opts.get('auto_orphans'):
888 if opts.get('auto_orphans'):
889 for key in opts:
889 for key in opts:
890 if key != 'auto_orphans' and opts.get(key):
890 if key != 'auto_orphans' and opts.get(key):
891 raise error.Abort(_('--auto-orphans is incompatible with %s') %
891 raise error.Abort(_('--auto-orphans is incompatible with %s') %
892 ('--' + key))
892 ('--' + key))
893 userrevs = list(repo.revs(opts.get('auto_orphans')))
893 userrevs = list(repo.revs(opts.get('auto_orphans')))
894 opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)]
894 opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)]
895 opts['dest'] = '_destautoorphanrebase(SRC)'
895 opts['dest'] = '_destautoorphanrebase(SRC)'
896
896
897 if dryrun or confirm:
897 if dryrun or confirm:
898 return _dryrunrebase(ui, repo, action, opts)
898 return _dryrunrebase(ui, repo, action, opts)
899 elif action == 'stop':
899 elif action == 'stop':
900 rbsrt = rebaseruntime(repo, ui)
900 rbsrt = rebaseruntime(repo, ui)
901 with repo.wlock(), repo.lock():
901 with repo.wlock(), repo.lock():
902 rbsrt.restorestatus()
902 rbsrt.restorestatus()
903 if rbsrt.collapsef:
903 if rbsrt.collapsef:
904 raise error.Abort(_("cannot stop in --collapse session"))
904 raise error.Abort(_("cannot stop in --collapse session"))
905 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
905 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
906 if not (rbsrt.keepf or allowunstable):
906 if not (rbsrt.keepf or allowunstable):
907 raise error.Abort(_("cannot remove original changesets with"
907 raise error.Abort(_("cannot remove original changesets with"
908 " unrebased descendants"),
908 " unrebased descendants"),
909 hint=_('either enable obsmarkers to allow unstable '
909 hint=_('either enable obsmarkers to allow unstable '
910 'revisions or use --keep to keep original '
910 'revisions or use --keep to keep original '
911 'changesets'))
911 'changesets'))
912 if needupdate(repo, rbsrt.state):
912 if needupdate(repo, rbsrt.state):
913 # update to the current working revision
913 # update to the current working revision
914 # to clear interrupted merge
914 # to clear interrupted merge
915 hg.updaterepo(repo, rbsrt.originalwd, overwrite=True)
915 hg.updaterepo(repo, rbsrt.originalwd, overwrite=True)
916 rbsrt._finishrebase()
916 rbsrt._finishrebase()
917 return 0
917 return 0
918 elif inmemory:
918 elif inmemory:
919 try:
919 try:
920 # in-memory merge doesn't support conflicts, so if we hit any, abort
920 # in-memory merge doesn't support conflicts, so if we hit any, abort
921 # and re-run as an on-disk merge.
921 # and re-run as an on-disk merge.
922 overrides = {('rebase', 'singletransaction'): True}
922 overrides = {('rebase', 'singletransaction'): True}
923 with ui.configoverride(overrides, 'rebase'):
923 with ui.configoverride(overrides, 'rebase'):
924 return _dorebase(ui, repo, action, opts, inmemory=inmemory)
924 return _dorebase(ui, repo, action, opts, inmemory=inmemory)
925 except error.InMemoryMergeConflictsError:
925 except error.InMemoryMergeConflictsError:
926 ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
926 ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
927 ' merge\n'))
927 ' merge\n'))
928 # TODO: Make in-memory merge not use the on-disk merge state, so
928 # TODO: Make in-memory merge not use the on-disk merge state, so
929 # we don't have to clean it here
929 # we don't have to clean it here
930 mergemod.mergestate.clean(repo)
930 mergemod.mergestate.clean(repo)
931 clearstatus(repo)
931 clearstatus(repo)
932 clearcollapsemsg(repo)
932 clearcollapsemsg(repo)
933 return _dorebase(ui, repo, action, opts, inmemory=False)
933 return _dorebase(ui, repo, action, opts, inmemory=False)
934 else:
934 else:
935 return _dorebase(ui, repo, action, opts)
935 return _dorebase(ui, repo, action, opts)
936
936
937 def _dryrunrebase(ui, repo, action, opts):
937 def _dryrunrebase(ui, repo, action, opts):
938 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
938 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts)
939 confirm = opts.get('confirm')
939 confirm = opts.get('confirm')
940 if confirm:
940 if confirm:
941 ui.status(_('starting in-memory rebase\n'))
941 ui.status(_('starting in-memory rebase\n'))
942 else:
942 else:
943 ui.status(_('starting dry-run rebase; repository will not be '
943 ui.status(_('starting dry-run rebase; repository will not be '
944 'changed\n'))
944 'changed\n'))
945 with repo.wlock(), repo.lock():
945 with repo.wlock(), repo.lock():
946 needsabort = True
946 needsabort = True
947 try:
947 try:
948 overrides = {('rebase', 'singletransaction'): True}
948 overrides = {('rebase', 'singletransaction'): True}
949 with ui.configoverride(overrides, 'rebase'):
949 with ui.configoverride(overrides, 'rebase'):
950 _origrebase(ui, repo, action, opts, rbsrt, inmemory=True,
950 _origrebase(ui, repo, action, opts, rbsrt, inmemory=True,
951 leaveunfinished=True)
951 leaveunfinished=True)
952 except error.InMemoryMergeConflictsError:
952 except error.InMemoryMergeConflictsError:
953 ui.status(_('hit a merge conflict\n'))
953 ui.status(_('hit a merge conflict\n'))
954 return 1
954 return 1
955 except error.Abort:
955 except error.Abort:
956 needsabort = False
956 needsabort = False
957 raise
957 raise
958 else:
958 else:
959 if confirm:
959 if confirm:
960 ui.status(_('rebase completed successfully\n'))
960 ui.status(_('rebase completed successfully\n'))
961 if not ui.promptchoice(_(b'apply changes (yn)?'
961 if not ui.promptchoice(_(b'apply changes (yn)?'
962 b'$$ &Yes $$ &No')):
962 b'$$ &Yes $$ &No')):
963 # finish unfinished rebase
963 # finish unfinished rebase
964 rbsrt._finishrebase()
964 rbsrt._finishrebase()
965 else:
965 else:
966 rbsrt._prepareabortorcontinue(isabort=True, backup=False,
966 rbsrt._prepareabortorcontinue(isabort=True, backup=False,
967 suppwarns=True)
967 suppwarns=True)
968 needsabort = False
968 needsabort = False
969 else:
969 else:
970 ui.status(_('dry-run rebase completed successfully; run without'
970 ui.status(_('dry-run rebase completed successfully; run without'
971 ' -n/--dry-run to perform this rebase\n'))
971 ' -n/--dry-run to perform this rebase\n'))
972 return 0
972 return 0
973 finally:
973 finally:
974 if needsabort:
974 if needsabort:
975 # no need to store backup in case of dryrun
975 # no need to store backup in case of dryrun
976 rbsrt._prepareabortorcontinue(isabort=True, backup=False,
976 rbsrt._prepareabortorcontinue(isabort=True, backup=False,
977 suppwarns=True)
977 suppwarns=True)
978
978
979 def _dorebase(ui, repo, action, opts, inmemory=False):
979 def _dorebase(ui, repo, action, opts, inmemory=False):
980 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
980 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
981 return _origrebase(ui, repo, action, opts, rbsrt, inmemory=inmemory)
981 return _origrebase(ui, repo, action, opts, rbsrt, inmemory=inmemory)
982
982
983 def _origrebase(ui, repo, action, opts, rbsrt, inmemory=False,
983 def _origrebase(ui, repo, action, opts, rbsrt, inmemory=False,
984 leaveunfinished=False):
984 leaveunfinished=False):
985 assert action != 'stop'
985 assert action != 'stop'
986 with repo.wlock(), repo.lock():
986 with repo.wlock(), repo.lock():
987 # Validate input and define rebasing points
987 # Validate input and define rebasing points
988 destf = opts.get('dest', None)
988 destf = opts.get('dest', None)
989 srcf = opts.get('source', None)
989 srcf = opts.get('source', None)
990 basef = opts.get('base', None)
990 basef = opts.get('base', None)
991 revf = opts.get('rev', [])
991 revf = opts.get('rev', [])
992 # search default destination in this space
992 # search default destination in this space
993 # used in the 'hg pull --rebase' case, see issue 5214.
993 # used in the 'hg pull --rebase' case, see issue 5214.
994 destspace = opts.get('_destspace')
994 destspace = opts.get('_destspace')
995 if opts.get('interactive'):
995 if opts.get('interactive'):
996 try:
996 try:
997 if extensions.find('histedit'):
997 if extensions.find('histedit'):
998 enablehistedit = ''
998 enablehistedit = ''
999 except KeyError:
999 except KeyError:
1000 enablehistedit = " --config extensions.histedit="
1000 enablehistedit = " --config extensions.histedit="
1001 help = "hg%s help -e histedit" % enablehistedit
1001 help = "hg%s help -e histedit" % enablehistedit
1002 msg = _("interactive history editing is supported by the "
1002 msg = _("interactive history editing is supported by the "
1003 "'histedit' extension (see \"%s\")") % help
1003 "'histedit' extension (see \"%s\")") % help
1004 raise error.Abort(msg)
1004 raise error.Abort(msg)
1005
1005
1006 if rbsrt.collapsemsg and not rbsrt.collapsef:
1006 if rbsrt.collapsemsg and not rbsrt.collapsef:
1007 raise error.Abort(
1007 raise error.Abort(
1008 _('message can only be specified with collapse'))
1008 _('message can only be specified with collapse'))
1009
1009
1010 if action:
1010 if action:
1011 if rbsrt.collapsef:
1011 if rbsrt.collapsef:
1012 raise error.Abort(
1012 raise error.Abort(
1013 _('cannot use collapse with continue or abort'))
1013 _('cannot use collapse with continue or abort'))
1014 if srcf or basef or destf:
1014 if srcf or basef or destf:
1015 raise error.Abort(
1015 raise error.Abort(
1016 _('abort and continue do not allow specifying revisions'))
1016 _('abort and continue do not allow specifying revisions'))
1017 if action == 'abort' and opts.get('tool', False):
1017 if action == 'abort' and opts.get('tool', False):
1018 ui.warn(_('tool option will be ignored\n'))
1018 ui.warn(_('tool option will be ignored\n'))
1019 if action == 'continue':
1019 if action == 'continue':
1020 ms = mergemod.mergestate.read(repo)
1020 ms = mergemod.mergestate.read(repo)
1021 mergeutil.checkunresolved(ms)
1021 mergeutil.checkunresolved(ms)
1022
1022
1023 retcode = rbsrt._prepareabortorcontinue(isabort=(action == 'abort'))
1023 retcode = rbsrt._prepareabortorcontinue(isabort=(action == 'abort'))
1024 if retcode is not None:
1024 if retcode is not None:
1025 return retcode
1025 return retcode
1026 else:
1026 else:
1027 destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef,
1027 destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef,
1028 revf, destspace=destspace)
1028 revf, destspace=destspace)
1029 retcode = rbsrt._preparenewrebase(destmap)
1029 retcode = rbsrt._preparenewrebase(destmap)
1030 if retcode is not None:
1030 if retcode is not None:
1031 return retcode
1031 return retcode
1032 storecollapsemsg(repo, rbsrt.collapsemsg)
1032 storecollapsemsg(repo, rbsrt.collapsemsg)
1033
1033
1034 tr = None
1034 tr = None
1035
1035
1036 singletr = ui.configbool('rebase', 'singletransaction')
1036 singletr = ui.configbool('rebase', 'singletransaction')
1037 if singletr:
1037 if singletr:
1038 tr = repo.transaction('rebase')
1038 tr = repo.transaction('rebase')
1039
1039
1040 # If `rebase.singletransaction` is enabled, wrap the entire operation in
1040 # If `rebase.singletransaction` is enabled, wrap the entire operation in
1041 # one transaction here. Otherwise, transactions are obtained when
1041 # one transaction here. Otherwise, transactions are obtained when
1042 # committing each node, which is slower but allows partial success.
1042 # committing each node, which is slower but allows partial success.
1043 with util.acceptintervention(tr):
1043 with util.acceptintervention(tr):
1044 # Same logic for the dirstate guard, except we don't create one when
1044 # Same logic for the dirstate guard, except we don't create one when
1045 # rebasing in-memory (it's not needed).
1045 # rebasing in-memory (it's not needed).
1046 dsguard = None
1046 dsguard = None
1047 if singletr and not inmemory:
1047 if singletr and not inmemory:
1048 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1048 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1049 with util.acceptintervention(dsguard):
1049 with util.acceptintervention(dsguard):
1050 rbsrt._performrebase(tr)
1050 rbsrt._performrebase(tr)
1051 if not leaveunfinished:
1051 if not leaveunfinished:
1052 rbsrt._finishrebase()
1052 rbsrt._finishrebase()
1053
1053
1054 def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None,
1054 def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None,
1055 revf=None, destspace=None):
1055 revf=None, destspace=None):
1056 """use revisions argument to define destmap {srcrev: destrev}"""
1056 """use revisions argument to define destmap {srcrev: destrev}"""
1057 if revf is None:
1057 if revf is None:
1058 revf = []
1058 revf = []
1059
1059
1060 # destspace is here to work around issues with `hg pull --rebase` see
1060 # destspace is here to work around issues with `hg pull --rebase` see
1061 # issue5214 for details
1061 # issue5214 for details
1062 if srcf and basef:
1062 if srcf and basef:
1063 raise error.Abort(_('cannot specify both a source and a base'))
1063 raise error.Abort(_('cannot specify both a source and a base'))
1064 if revf and basef:
1064 if revf and basef:
1065 raise error.Abort(_('cannot specify both a revision and a base'))
1065 raise error.Abort(_('cannot specify both a revision and a base'))
1066 if revf and srcf:
1066 if revf and srcf:
1067 raise error.Abort(_('cannot specify both a revision and a source'))
1067 raise error.Abort(_('cannot specify both a revision and a source'))
1068
1068
1069 if not inmemory:
1069 if not inmemory:
1070 cmdutil.checkunfinished(repo)
1070 cmdutil.checkunfinished(repo)
1071 cmdutil.bailifchanged(repo)
1071 cmdutil.bailifchanged(repo)
1072
1072
1073 if ui.configbool('commands', 'rebase.requiredest') and not destf:
1073 if ui.configbool('commands', 'rebase.requiredest') and not destf:
1074 raise error.Abort(_('you must specify a destination'),
1074 raise error.Abort(_('you must specify a destination'),
1075 hint=_('use: hg rebase -d REV'))
1075 hint=_('use: hg rebase -d REV'))
1076
1076
1077 dest = None
1077 dest = None
1078
1078
1079 if revf:
1079 if revf:
1080 rebaseset = scmutil.revrange(repo, revf)
1080 rebaseset = scmutil.revrange(repo, revf)
1081 if not rebaseset:
1081 if not rebaseset:
1082 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
1082 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
1083 return None
1083 return None
1084 elif srcf:
1084 elif srcf:
1085 src = scmutil.revrange(repo, [srcf])
1085 src = scmutil.revrange(repo, [srcf])
1086 if not src:
1086 if not src:
1087 ui.status(_('empty "source" revision set - nothing to rebase\n'))
1087 ui.status(_('empty "source" revision set - nothing to rebase\n'))
1088 return None
1088 return None
1089 rebaseset = repo.revs('(%ld)::', src)
1089 rebaseset = repo.revs('(%ld)::', src)
1090 assert rebaseset
1090 assert rebaseset
1091 else:
1091 else:
1092 base = scmutil.revrange(repo, [basef or '.'])
1092 base = scmutil.revrange(repo, [basef or '.'])
1093 if not base:
1093 if not base:
1094 ui.status(_('empty "base" revision set - '
1094 ui.status(_('empty "base" revision set - '
1095 "can't compute rebase set\n"))
1095 "can't compute rebase set\n"))
1096 return None
1096 return None
1097 if destf:
1097 if destf:
1098 # --base does not support multiple destinations
1098 # --base does not support multiple destinations
1099 dest = scmutil.revsingle(repo, destf)
1099 dest = scmutil.revsingle(repo, destf)
1100 else:
1100 else:
1101 dest = repo[_destrebase(repo, base, destspace=destspace)]
1101 dest = repo[_destrebase(repo, base, destspace=destspace)]
1102 destf = bytes(dest)
1102 destf = bytes(dest)
1103
1103
1104 roots = [] # selected children of branching points
1104 roots = [] # selected children of branching points
1105 bpbase = {} # {branchingpoint: [origbase]}
1105 bpbase = {} # {branchingpoint: [origbase]}
1106 for b in base: # group bases by branching points
1106 for b in base: # group bases by branching points
1107 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
1107 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
1108 bpbase[bp] = bpbase.get(bp, []) + [b]
1108 bpbase[bp] = bpbase.get(bp, []) + [b]
1109 if None in bpbase:
1109 if None in bpbase:
1110 # emulate the old behavior, showing "nothing to rebase" (a better
1110 # emulate the old behavior, showing "nothing to rebase" (a better
1111 # behavior may be abort with "cannot find branching point" error)
1111 # behavior may be abort with "cannot find branching point" error)
1112 bpbase.clear()
1112 bpbase.clear()
1113 for bp, bs in bpbase.iteritems(): # calculate roots
1113 for bp, bs in bpbase.iteritems(): # calculate roots
1114 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
1114 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
1115
1115
1116 rebaseset = repo.revs('%ld::', roots)
1116 rebaseset = repo.revs('%ld::', roots)
1117
1117
1118 if not rebaseset:
1118 if not rebaseset:
1119 # transform to list because smartsets are not comparable to
1119 # transform to list because smartsets are not comparable to
1120 # lists. This should be improved to honor laziness of
1120 # lists. This should be improved to honor laziness of
1121 # smartset.
1121 # smartset.
1122 if list(base) == [dest.rev()]:
1122 if list(base) == [dest.rev()]:
1123 if basef:
1123 if basef:
1124 ui.status(_('nothing to rebase - %s is both "base"'
1124 ui.status(_('nothing to rebase - %s is both "base"'
1125 ' and destination\n') % dest)
1125 ' and destination\n') % dest)
1126 else:
1126 else:
1127 ui.status(_('nothing to rebase - working directory '
1127 ui.status(_('nothing to rebase - working directory '
1128 'parent is also destination\n'))
1128 'parent is also destination\n'))
1129 elif not repo.revs('%ld - ::%d', base, dest.rev()):
1129 elif not repo.revs('%ld - ::%d', base, dest.rev()):
1130 if basef:
1130 if basef:
1131 ui.status(_('nothing to rebase - "base" %s is '
1131 ui.status(_('nothing to rebase - "base" %s is '
1132 'already an ancestor of destination '
1132 'already an ancestor of destination '
1133 '%s\n') %
1133 '%s\n') %
1134 ('+'.join(bytes(repo[r]) for r in base),
1134 ('+'.join(bytes(repo[r]) for r in base),
1135 dest))
1135 dest))
1136 else:
1136 else:
1137 ui.status(_('nothing to rebase - working '
1137 ui.status(_('nothing to rebase - working '
1138 'directory parent is already an '
1138 'directory parent is already an '
1139 'ancestor of destination %s\n') % dest)
1139 'ancestor of destination %s\n') % dest)
1140 else: # can it happen?
1140 else: # can it happen?
1141 ui.status(_('nothing to rebase from %s to %s\n') %
1141 ui.status(_('nothing to rebase from %s to %s\n') %
1142 ('+'.join(bytes(repo[r]) for r in base), dest))
1142 ('+'.join(bytes(repo[r]) for r in base), dest))
1143 return None
1143 return None
1144
1144
1145 rebasingwcp = repo['.'].rev() in rebaseset
1145 rebasingwcp = repo['.'].rev() in rebaseset
1146 ui.log("rebase", "rebasing working copy parent: %r\n", rebasingwcp,
1146 ui.log("rebase", "rebasing working copy parent: %r\n", rebasingwcp,
1147 rebase_rebasing_wcp=rebasingwcp)
1147 rebase_rebasing_wcp=rebasingwcp)
1148 if inmemory and rebasingwcp:
1148 if inmemory and rebasingwcp:
1149 # Check these since we did not before.
1149 # Check these since we did not before.
1150 cmdutil.checkunfinished(repo)
1150 cmdutil.checkunfinished(repo)
1151 cmdutil.bailifchanged(repo)
1151 cmdutil.bailifchanged(repo)
1152
1152
1153 if not destf:
1153 if not destf:
1154 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
1154 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
1155 destf = bytes(dest)
1155 destf = bytes(dest)
1156
1156
1157 allsrc = revsetlang.formatspec('%ld', rebaseset)
1157 allsrc = revsetlang.formatspec('%ld', rebaseset)
1158 alias = {'ALLSRC': allsrc}
1158 alias = {'ALLSRC': allsrc}
1159
1159
1160 if dest is None:
1160 if dest is None:
1161 try:
1161 try:
1162 # fast path: try to resolve dest without SRC alias
1162 # fast path: try to resolve dest without SRC alias
1163 dest = scmutil.revsingle(repo, destf, localalias=alias)
1163 dest = scmutil.revsingle(repo, destf, localalias=alias)
1164 except error.RepoLookupError:
1164 except error.RepoLookupError:
1165 # multi-dest path: resolve dest for each SRC separately
1165 # multi-dest path: resolve dest for each SRC separately
1166 destmap = {}
1166 destmap = {}
1167 for r in rebaseset:
1167 for r in rebaseset:
1168 alias['SRC'] = revsetlang.formatspec('%d', r)
1168 alias['SRC'] = revsetlang.formatspec('%d', r)
1169 # use repo.anyrevs instead of scmutil.revsingle because we
1169 # use repo.anyrevs instead of scmutil.revsingle because we
1170 # don't want to abort if destset is empty.
1170 # don't want to abort if destset is empty.
1171 destset = repo.anyrevs([destf], user=True, localalias=alias)
1171 destset = repo.anyrevs([destf], user=True, localalias=alias)
1172 size = len(destset)
1172 size = len(destset)
1173 if size == 1:
1173 if size == 1:
1174 destmap[r] = destset.first()
1174 destmap[r] = destset.first()
1175 elif size == 0:
1175 elif size == 0:
1176 ui.note(_('skipping %s - empty destination\n') % repo[r])
1176 ui.note(_('skipping %s - empty destination\n') % repo[r])
1177 else:
1177 else:
1178 raise error.Abort(_('rebase destination for %s is not '
1178 raise error.Abort(_('rebase destination for %s is not '
1179 'unique') % repo[r])
1179 'unique') % repo[r])
1180
1180
1181 if dest is not None:
1181 if dest is not None:
1182 # single-dest case: assign dest to each rev in rebaseset
1182 # single-dest case: assign dest to each rev in rebaseset
1183 destrev = dest.rev()
1183 destrev = dest.rev()
1184 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1184 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1185
1185
1186 if not destmap:
1186 if not destmap:
1187 ui.status(_('nothing to rebase - empty destination\n'))
1187 ui.status(_('nothing to rebase - empty destination\n'))
1188 return None
1188 return None
1189
1189
1190 return destmap
1190 return destmap
1191
1191
1192 def externalparent(repo, state, destancestors):
1192 def externalparent(repo, state, destancestors):
1193 """Return the revision that should be used as the second parent
1193 """Return the revision that should be used as the second parent
1194 when the revisions in state is collapsed on top of destancestors.
1194 when the revisions in state is collapsed on top of destancestors.
1195 Abort if there is more than one parent.
1195 Abort if there is more than one parent.
1196 """
1196 """
1197 parents = set()
1197 parents = set()
1198 source = min(state)
1198 source = min(state)
1199 for rev in state:
1199 for rev in state:
1200 if rev == source:
1200 if rev == source:
1201 continue
1201 continue
1202 for p in repo[rev].parents():
1202 for p in repo[rev].parents():
1203 if (p.rev() not in state
1203 if (p.rev() not in state
1204 and p.rev() not in destancestors):
1204 and p.rev() not in destancestors):
1205 parents.add(p.rev())
1205 parents.add(p.rev())
1206 if not parents:
1206 if not parents:
1207 return nullrev
1207 return nullrev
1208 if len(parents) == 1:
1208 if len(parents) == 1:
1209 return parents.pop()
1209 return parents.pop()
1210 raise error.Abort(_('unable to collapse on top of %d, there is more '
1210 raise error.Abort(_('unable to collapse on top of %d, there is more '
1211 'than one external parent: %s') %
1211 'than one external parent: %s') %
1212 (max(destancestors),
1212 (max(destancestors),
1213 ', '.join("%d" % p for p in sorted(parents))))
1213 ', '.join("%d" % p for p in sorted(parents))))
1214
1214
1215 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg):
1215 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg):
1216 '''Commit the memory changes with parents p1 and p2.
1216 '''Commit the memory changes with parents p1 and p2.
1217 Return node of committed revision.'''
1217 Return node of committed revision.'''
1218 # Replicates the empty check in ``repo.commit``.
1218 # Replicates the empty check in ``repo.commit``.
1219 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
1219 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
1220 return None
1220 return None
1221
1221
1222 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1222 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1223 # ``branch`` (used when passing ``--keepbranches``).
1223 # ``branch`` (used when passing ``--keepbranches``).
1224 branch = repo[p1].branch()
1224 branch = repo[p1].branch()
1225 if 'branch' in extra:
1225 if 'branch' in extra:
1226 branch = extra['branch']
1226 branch = extra['branch']
1227
1227
1228 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
1228 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
1229 extra=extra, user=user, branch=branch, editor=editor)
1229 extra=extra, user=user, branch=branch, editor=editor)
1230 commitres = repo.commitctx(memctx)
1230 commitres = repo.commitctx(memctx)
1231 wctx.clean() # Might be reused
1231 wctx.clean() # Might be reused
1232 return commitres
1232 return commitres
1233
1233
1234 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg):
1234 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg):
1235 '''Commit the wd changes with parents p1 and p2.
1235 '''Commit the wd changes with parents p1 and p2.
1236 Return node of committed revision.'''
1236 Return node of committed revision.'''
1237 dsguard = util.nullcontextmanager()
1237 dsguard = util.nullcontextmanager()
1238 if not repo.ui.configbool('rebase', 'singletransaction'):
1238 if not repo.ui.configbool('rebase', 'singletransaction'):
1239 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1239 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1240 with dsguard:
1240 with dsguard:
1241 repo.setparents(repo[p1].node(), repo[p2].node())
1241 repo.setparents(repo[p1].node(), repo[p2].node())
1242
1242
1243 # Commit might fail if unresolved files exist
1243 # Commit might fail if unresolved files exist
1244 newnode = repo.commit(text=commitmsg, user=user, date=date,
1244 newnode = repo.commit(text=commitmsg, user=user, date=date,
1245 extra=extra, editor=editor)
1245 extra=extra, editor=editor)
1246
1246
1247 repo.dirstate.setbranch(repo[newnode].branch())
1247 repo.dirstate.setbranch(repo[newnode].branch())
1248 return newnode
1248 return newnode
1249
1249
1250 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1250 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1251 'Rebase a single revision rev on top of p1 using base as merge ancestor'
1251 'Rebase a single revision rev on top of p1 using base as merge ancestor'
1252 # Merge phase
1252 # Merge phase
1253 # Update to destination and merge it with local
1253 # Update to destination and merge it with local
1254 if wctx.isinmemory():
1254 if wctx.isinmemory():
1255 wctx.setbase(repo[p1])
1255 wctx.setbase(repo[p1])
1256 else:
1256 else:
1257 if repo['.'].rev() != p1:
1257 if repo['.'].rev() != p1:
1258 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
1258 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
1259 mergemod.update(repo, p1, branchmerge=False, force=True)
1259 mergemod.update(repo, p1, branchmerge=False, force=True)
1260 else:
1260 else:
1261 repo.ui.debug(" already in destination\n")
1261 repo.ui.debug(" already in destination\n")
1262 # This is, alas, necessary to invalidate workingctx's manifest cache,
1262 # This is, alas, necessary to invalidate workingctx's manifest cache,
1263 # as well as other data we litter on it in other places.
1263 # as well as other data we litter on it in other places.
1264 wctx = repo[None]
1264 wctx = repo[None]
1265 repo.dirstate.write(repo.currenttransaction())
1265 repo.dirstate.write(repo.currenttransaction())
1266 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
1266 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
1267 if base is not None:
1267 if base is not None:
1268 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
1268 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
1269 # When collapsing in-place, the parent is the common ancestor, we
1269 # When collapsing in-place, the parent is the common ancestor, we
1270 # have to allow merging with it.
1270 # have to allow merging with it.
1271 stats = mergemod.update(repo, rev, branchmerge=True, force=True,
1271 stats = mergemod.update(repo, rev, branchmerge=True, force=True,
1272 ancestor=base, mergeancestor=collapse,
1272 ancestor=base, mergeancestor=collapse,
1273 labels=['dest', 'source'], wc=wctx)
1273 labels=['dest', 'source'], wc=wctx)
1274 if collapse:
1274 if collapse:
1275 copies.duplicatecopies(repo, wctx, rev, dest)
1275 copies.duplicatecopies(repo, wctx, rev, dest)
1276 else:
1276 else:
1277 # If we're not using --collapse, we need to
1277 # If we're not using --collapse, we need to
1278 # duplicate copies between the revision we're
1278 # duplicate copies between the revision we're
1279 # rebasing and its first parent, but *not*
1279 # rebasing and its first parent, but *not*
1280 # duplicate any copies that have already been
1280 # duplicate any copies that have already been
1281 # performed in the destination.
1281 # performed in the destination.
1282 p1rev = repo[rev].p1().rev()
1282 p1rev = repo[rev].p1().rev()
1283 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1283 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1284 return stats
1284 return stats
1285
1285
1286 def adjustdest(repo, rev, destmap, state, skipped):
1286 def adjustdest(repo, rev, destmap, state, skipped):
1287 r"""adjust rebase destination given the current rebase state
1287 r"""adjust rebase destination given the current rebase state
1288
1288
1289 rev is what is being rebased. Return a list of two revs, which are the
1289 rev is what is being rebased. Return a list of two revs, which are the
1290 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1290 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1291 nullrev, return dest without adjustment for it.
1291 nullrev, return dest without adjustment for it.
1292
1292
1293 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1293 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1294 to B1, and E's destination will be adjusted from F to B1.
1294 to B1, and E's destination will be adjusted from F to B1.
1295
1295
1296 B1 <- written during rebasing B
1296 B1 <- written during rebasing B
1297 |
1297 |
1298 F <- original destination of B, E
1298 F <- original destination of B, E
1299 |
1299 |
1300 | E <- rev, which is being rebased
1300 | E <- rev, which is being rebased
1301 | |
1301 | |
1302 | D <- prev, one parent of rev being checked
1302 | D <- prev, one parent of rev being checked
1303 | |
1303 | |
1304 | x <- skipped, ex. no successor or successor in (::dest)
1304 | x <- skipped, ex. no successor or successor in (::dest)
1305 | |
1305 | |
1306 | C <- rebased as C', different destination
1306 | C <- rebased as C', different destination
1307 | |
1307 | |
1308 | B <- rebased as B1 C'
1308 | B <- rebased as B1 C'
1309 |/ |
1309 |/ |
1310 A G <- destination of C, different
1310 A G <- destination of C, different
1311
1311
1312 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1312 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1313 first move C to C1, G to G1, and when it's checking H, the adjusted
1313 first move C to C1, G to G1, and when it's checking H, the adjusted
1314 destinations will be [C1, G1].
1314 destinations will be [C1, G1].
1315
1315
1316 H C1 G1
1316 H C1 G1
1317 /| | /
1317 /| | /
1318 F G |/
1318 F G |/
1319 K | | -> K
1319 K | | -> K
1320 | C D |
1320 | C D |
1321 | |/ |
1321 | |/ |
1322 | B | ...
1322 | B | ...
1323 |/ |/
1323 |/ |/
1324 A A
1324 A A
1325
1325
1326 Besides, adjust dest according to existing rebase information. For example,
1326 Besides, adjust dest according to existing rebase information. For example,
1327
1327
1328 B C D B needs to be rebased on top of C, C needs to be rebased on top
1328 B C D B needs to be rebased on top of C, C needs to be rebased on top
1329 \|/ of D. We will rebase C first.
1329 \|/ of D. We will rebase C first.
1330 A
1330 A
1331
1331
1332 C' After rebasing C, when considering B's destination, use C'
1332 C' After rebasing C, when considering B's destination, use C'
1333 | instead of the original C.
1333 | instead of the original C.
1334 B D
1334 B D
1335 \ /
1335 \ /
1336 A
1336 A
1337 """
1337 """
1338 # pick already rebased revs with same dest from state as interesting source
1338 # pick already rebased revs with same dest from state as interesting source
1339 dest = destmap[rev]
1339 dest = destmap[rev]
1340 source = [s for s, d in state.items()
1340 source = [s for s, d in state.items()
1341 if d > 0 and destmap[s] == dest and s not in skipped]
1341 if d > 0 and destmap[s] == dest and s not in skipped]
1342
1342
1343 result = []
1343 result = []
1344 for prev in repo.changelog.parentrevs(rev):
1344 for prev in repo.changelog.parentrevs(rev):
1345 adjusted = dest
1345 adjusted = dest
1346 if prev != nullrev:
1346 if prev != nullrev:
1347 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1347 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1348 if candidate is not None:
1348 if candidate is not None:
1349 adjusted = state[candidate]
1349 adjusted = state[candidate]
1350 if adjusted == dest and dest in state:
1350 if adjusted == dest and dest in state:
1351 adjusted = state[dest]
1351 adjusted = state[dest]
1352 if adjusted == revtodo:
1352 if adjusted == revtodo:
1353 # sortsource should produce an order that makes this impossible
1353 # sortsource should produce an order that makes this impossible
1354 raise error.ProgrammingError(
1354 raise error.ProgrammingError(
1355 'rev %d should be rebased already at this time' % dest)
1355 'rev %d should be rebased already at this time' % dest)
1356 result.append(adjusted)
1356 result.append(adjusted)
1357 return result
1357 return result
1358
1358
1359 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1359 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1360 """
1360 """
1361 Abort if rebase will create divergence or rebase is noop because of markers
1361 Abort if rebase will create divergence or rebase is noop because of markers
1362
1362
1363 `rebaseobsrevs`: set of obsolete revision in source
1363 `rebaseobsrevs`: set of obsolete revision in source
1364 `rebaseobsskipped`: set of revisions from source skipped because they have
1364 `rebaseobsskipped`: set of revisions from source skipped because they have
1365 successors in destination or no non-obsolete successor.
1365 successors in destination or no non-obsolete successor.
1366 """
1366 """
1367 # Obsolete node with successors not in dest leads to divergence
1367 # Obsolete node with successors not in dest leads to divergence
1368 divergenceok = ui.configbool('experimental',
1368 divergenceok = ui.configbool('experimental',
1369 'evolution.allowdivergence')
1369 'evolution.allowdivergence')
1370 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1370 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1371
1371
1372 if divergencebasecandidates and not divergenceok:
1372 if divergencebasecandidates and not divergenceok:
1373 divhashes = (bytes(repo[r])
1373 divhashes = (bytes(repo[r])
1374 for r in divergencebasecandidates)
1374 for r in divergencebasecandidates)
1375 msg = _("this rebase will cause "
1375 msg = _("this rebase will cause "
1376 "divergences from: %s")
1376 "divergences from: %s")
1377 h = _("to force the rebase please set "
1377 h = _("to force the rebase please set "
1378 "experimental.evolution.allowdivergence=True")
1378 "experimental.evolution.allowdivergence=True")
1379 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1379 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1380
1380
1381 def successorrevs(unfi, rev):
1381 def successorrevs(unfi, rev):
1382 """yield revision numbers for successors of rev"""
1382 """yield revision numbers for successors of rev"""
1383 assert unfi.filtername is None
1383 assert unfi.filtername is None
1384 nodemap = unfi.changelog.nodemap
1384 nodemap = unfi.changelog.nodemap
1385 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1385 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1386 if s in nodemap:
1386 if s in nodemap:
1387 yield nodemap[s]
1387 yield nodemap[s]
1388
1388
1389 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1389 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1390 """Return new parents and optionally a merge base for rev being rebased
1390 """Return new parents and optionally a merge base for rev being rebased
1391
1391
1392 The destination specified by "dest" cannot always be used directly because
1392 The destination specified by "dest" cannot always be used directly because
1393 previously rebase result could affect destination. For example,
1393 previously rebase result could affect destination. For example,
1394
1394
1395 D E rebase -r C+D+E -d B
1395 D E rebase -r C+D+E -d B
1396 |/ C will be rebased to C'
1396 |/ C will be rebased to C'
1397 B C D's new destination will be C' instead of B
1397 B C D's new destination will be C' instead of B
1398 |/ E's new destination will be C' instead of B
1398 |/ E's new destination will be C' instead of B
1399 A
1399 A
1400
1400
1401 The new parents of a merge is slightly more complicated. See the comment
1401 The new parents of a merge is slightly more complicated. See the comment
1402 block below.
1402 block below.
1403 """
1403 """
1404 # use unfiltered changelog since successorrevs may return filtered nodes
1404 # use unfiltered changelog since successorrevs may return filtered nodes
1405 assert repo.filtername is None
1405 assert repo.filtername is None
1406 cl = repo.changelog
1406 cl = repo.changelog
1407 isancestor = cl.isancestorrev
1407 isancestor = cl.isancestorrev
1408
1408
1409 dest = destmap[rev]
1409 dest = destmap[rev]
1410 oldps = repo.changelog.parentrevs(rev) # old parents
1410 oldps = repo.changelog.parentrevs(rev) # old parents
1411 newps = [nullrev, nullrev] # new parents
1411 newps = [nullrev, nullrev] # new parents
1412 dests = adjustdest(repo, rev, destmap, state, skipped)
1412 dests = adjustdest(repo, rev, destmap, state, skipped)
1413 bases = list(oldps) # merge base candidates, initially just old parents
1413 bases = list(oldps) # merge base candidates, initially just old parents
1414
1414
1415 if all(r == nullrev for r in oldps[1:]):
1415 if all(r == nullrev for r in oldps[1:]):
1416 # For non-merge changeset, just move p to adjusted dest as requested.
1416 # For non-merge changeset, just move p to adjusted dest as requested.
1417 newps[0] = dests[0]
1417 newps[0] = dests[0]
1418 else:
1418 else:
1419 # For merge changeset, if we move p to dests[i] unconditionally, both
1419 # For merge changeset, if we move p to dests[i] unconditionally, both
1420 # parents may change and the end result looks like "the merge loses a
1420 # parents may change and the end result looks like "the merge loses a
1421 # parent", which is a surprise. This is a limit because "--dest" only
1421 # parent", which is a surprise. This is a limit because "--dest" only
1422 # accepts one dest per src.
1422 # accepts one dest per src.
1423 #
1423 #
1424 # Therefore, only move p with reasonable conditions (in this order):
1424 # Therefore, only move p with reasonable conditions (in this order):
1425 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1425 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1426 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1426 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1427 #
1427 #
1428 # Comparing with adjustdest, the logic here does some additional work:
1428 # Comparing with adjustdest, the logic here does some additional work:
1429 # 1. decide which parents will not be moved towards dest
1429 # 1. decide which parents will not be moved towards dest
1430 # 2. if the above decision is "no", should a parent still be moved
1430 # 2. if the above decision is "no", should a parent still be moved
1431 # because it was rebased?
1431 # because it was rebased?
1432 #
1432 #
1433 # For example:
1433 # For example:
1434 #
1434 #
1435 # C # "rebase -r C -d D" is an error since none of the parents
1435 # C # "rebase -r C -d D" is an error since none of the parents
1436 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1436 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1437 # A B D # B (using rule "2."), since B will be rebased.
1437 # A B D # B (using rule "2."), since B will be rebased.
1438 #
1438 #
1439 # The loop tries to be not rely on the fact that a Mercurial node has
1439 # The loop tries to be not rely on the fact that a Mercurial node has
1440 # at most 2 parents.
1440 # at most 2 parents.
1441 for i, p in enumerate(oldps):
1441 for i, p in enumerate(oldps):
1442 np = p # new parent
1442 np = p # new parent
1443 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1443 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1444 np = dests[i]
1444 np = dests[i]
1445 elif p in state and state[p] > 0:
1445 elif p in state and state[p] > 0:
1446 np = state[p]
1446 np = state[p]
1447
1447
1448 # "bases" only record "special" merge bases that cannot be
1448 # "bases" only record "special" merge bases that cannot be
1449 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1449 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1450 # For example:
1450 # For example:
1451 #
1451 #
1452 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1452 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1453 # | C # is B', but merge base for C is B, instead of
1453 # | C # is B', but merge base for C is B, instead of
1454 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1454 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1455 # | B # "state" edges are merged (so there will be an edge from
1455 # | B # "state" edges are merged (so there will be an edge from
1456 # |/ # B to B'), the merge base is still ancestor(C, B') in
1456 # |/ # B to B'), the merge base is still ancestor(C, B') in
1457 # A # the merged graph.
1457 # A # the merged graph.
1458 #
1458 #
1459 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1459 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1460 # which uses "virtual null merge" to explain this situation.
1460 # which uses "virtual null merge" to explain this situation.
1461 if isancestor(p, np):
1461 if isancestor(p, np):
1462 bases[i] = nullrev
1462 bases[i] = nullrev
1463
1463
1464 # If one parent becomes an ancestor of the other, drop the ancestor
1464 # If one parent becomes an ancestor of the other, drop the ancestor
1465 for j, x in enumerate(newps[:i]):
1465 for j, x in enumerate(newps[:i]):
1466 if x == nullrev:
1466 if x == nullrev:
1467 continue
1467 continue
1468 if isancestor(np, x): # CASE-1
1468 if isancestor(np, x): # CASE-1
1469 np = nullrev
1469 np = nullrev
1470 elif isancestor(x, np): # CASE-2
1470 elif isancestor(x, np): # CASE-2
1471 newps[j] = np
1471 newps[j] = np
1472 np = nullrev
1472 np = nullrev
1473 # New parents forming an ancestor relationship does not
1473 # New parents forming an ancestor relationship does not
1474 # mean the old parents have a similar relationship. Do not
1474 # mean the old parents have a similar relationship. Do not
1475 # set bases[x] to nullrev.
1475 # set bases[x] to nullrev.
1476 bases[j], bases[i] = bases[i], bases[j]
1476 bases[j], bases[i] = bases[i], bases[j]
1477
1477
1478 newps[i] = np
1478 newps[i] = np
1479
1479
1480 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1480 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1481 # base. If only p2 changes, merging using unchanged p1 as merge base is
1481 # base. If only p2 changes, merging using unchanged p1 as merge base is
1482 # suboptimal. Therefore swap parents to make the merge sane.
1482 # suboptimal. Therefore swap parents to make the merge sane.
1483 if newps[1] != nullrev and oldps[0] == newps[0]:
1483 if newps[1] != nullrev and oldps[0] == newps[0]:
1484 assert len(newps) == 2 and len(oldps) == 2
1484 assert len(newps) == 2 and len(oldps) == 2
1485 newps.reverse()
1485 newps.reverse()
1486 bases.reverse()
1486 bases.reverse()
1487
1487
1488 # No parent change might be an error because we fail to make rev a
1488 # No parent change might be an error because we fail to make rev a
1489 # descendent of requested dest. This can happen, for example:
1489 # descendent of requested dest. This can happen, for example:
1490 #
1490 #
1491 # C # rebase -r C -d D
1491 # C # rebase -r C -d D
1492 # /| # None of A and B will be changed to D and rebase fails.
1492 # /| # None of A and B will be changed to D and rebase fails.
1493 # A B D
1493 # A B D
1494 if set(newps) == set(oldps) and dest not in newps:
1494 if set(newps) == set(oldps) and dest not in newps:
1495 raise error.Abort(_('cannot rebase %d:%s without '
1495 raise error.Abort(_('cannot rebase %d:%s without '
1496 'moving at least one of its parents')
1496 'moving at least one of its parents')
1497 % (rev, repo[rev]))
1497 % (rev, repo[rev]))
1498
1498
1499 # Source should not be ancestor of dest. The check here guarantees it's
1499 # Source should not be ancestor of dest. The check here guarantees it's
1500 # impossible. With multi-dest, the initial check does not cover complex
1500 # impossible. With multi-dest, the initial check does not cover complex
1501 # cases since we don't have abstractions to dry-run rebase cheaply.
1501 # cases since we don't have abstractions to dry-run rebase cheaply.
1502 if any(p != nullrev and isancestor(rev, p) for p in newps):
1502 if any(p != nullrev and isancestor(rev, p) for p in newps):
1503 raise error.Abort(_('source is ancestor of destination'))
1503 raise error.Abort(_('source is ancestor of destination'))
1504
1504
1505 # "rebasenode" updates to new p1, use the corresponding merge base.
1505 # "rebasenode" updates to new p1, use the corresponding merge base.
1506 if bases[0] != nullrev:
1506 if bases[0] != nullrev:
1507 base = bases[0]
1507 base = bases[0]
1508 else:
1508 else:
1509 base = None
1509 base = None
1510
1510
1511 # Check if the merge will contain unwanted changes. That may happen if
1511 # Check if the merge will contain unwanted changes. That may happen if
1512 # there are multiple special (non-changelog ancestor) merge bases, which
1512 # there are multiple special (non-changelog ancestor) merge bases, which
1513 # cannot be handled well by the 3-way merge algorithm. For example:
1513 # cannot be handled well by the 3-way merge algorithm. For example:
1514 #
1514 #
1515 # F
1515 # F
1516 # /|
1516 # /|
1517 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1517 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1518 # | | # as merge base, the difference between D and F will include
1518 # | | # as merge base, the difference between D and F will include
1519 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1519 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1520 # |/ # chosen, the rebased F will contain B.
1520 # |/ # chosen, the rebased F will contain B.
1521 # A Z
1521 # A Z
1522 #
1522 #
1523 # But our merge base candidates (D and E in above case) could still be
1523 # But our merge base candidates (D and E in above case) could still be
1524 # better than the default (ancestor(F, Z) == null). Therefore still
1524 # better than the default (ancestor(F, Z) == null). Therefore still
1525 # pick one (so choose p1 above).
1525 # pick one (so choose p1 above).
1526 if sum(1 for b in bases if b != nullrev) > 1:
1526 if sum(1 for b in bases if b != nullrev) > 1:
1527 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1527 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1528 for i, base in enumerate(bases):
1528 for i, base in enumerate(bases):
1529 if base == nullrev:
1529 if base == nullrev:
1530 continue
1530 continue
1531 # Revisions in the side (not chosen as merge base) branch that
1531 # Revisions in the side (not chosen as merge base) branch that
1532 # might contain "surprising" contents
1532 # might contain "surprising" contents
1533 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1533 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1534 bases, base, base, dest))
1534 bases, base, base, dest))
1535
1535
1536 # If those revisions are covered by rebaseset, the result is good.
1536 # If those revisions are covered by rebaseset, the result is good.
1537 # A merge in rebaseset would be considered to cover its ancestors.
1537 # A merge in rebaseset would be considered to cover its ancestors.
1538 if siderevs:
1538 if siderevs:
1539 rebaseset = [r for r, d in state.items()
1539 rebaseset = [r for r, d in state.items()
1540 if d > 0 and r not in obsskipped]
1540 if d > 0 and r not in obsskipped]
1541 merges = [r for r in rebaseset
1541 merges = [r for r in rebaseset
1542 if cl.parentrevs(r)[1] != nullrev]
1542 if cl.parentrevs(r)[1] != nullrev]
1543 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1543 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1544 siderevs, merges, rebaseset))
1544 siderevs, merges, rebaseset))
1545
1545
1546 # Choose a merge base that has a minimal number of unwanted revs.
1546 # Choose a merge base that has a minimal number of unwanted revs.
1547 l, i = min((len(revs), i)
1547 l, i = min((len(revs), i)
1548 for i, revs in enumerate(unwanted) if revs is not None)
1548 for i, revs in enumerate(unwanted) if revs is not None)
1549 base = bases[i]
1549 base = bases[i]
1550
1550
1551 # newps[0] should match merge base if possible. Currently, if newps[i]
1551 # newps[0] should match merge base if possible. Currently, if newps[i]
1552 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1552 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1553 # the other's ancestor. In that case, it's fine to not swap newps here.
1553 # the other's ancestor. In that case, it's fine to not swap newps here.
1554 # (see CASE-1 and CASE-2 above)
1554 # (see CASE-1 and CASE-2 above)
1555 if i != 0 and newps[i] != nullrev:
1555 if i != 0 and newps[i] != nullrev:
1556 newps[0], newps[i] = newps[i], newps[0]
1556 newps[0], newps[i] = newps[i], newps[0]
1557
1557
1558 # The merge will include unwanted revisions. Abort now. Revisit this if
1558 # The merge will include unwanted revisions. Abort now. Revisit this if
1559 # we have a more advanced merge algorithm that handles multiple bases.
1559 # we have a more advanced merge algorithm that handles multiple bases.
1560 if l > 0:
1560 if l > 0:
1561 unwanteddesc = _(' or ').join(
1561 unwanteddesc = _(' or ').join(
1562 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1562 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1563 for revs in unwanted if revs is not None))
1563 for revs in unwanted if revs is not None))
1564 raise error.Abort(
1564 raise error.Abort(
1565 _('rebasing %d:%s will include unwanted changes from %s')
1565 _('rebasing %d:%s will include unwanted changes from %s')
1566 % (rev, repo[rev], unwanteddesc))
1566 % (rev, repo[rev], unwanteddesc))
1567
1567
1568 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1568 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1569
1569
1570 return newps[0], newps[1], base
1570 return newps[0], newps[1], base
1571
1571
1572 def isagitpatch(repo, patchname):
1572 def isagitpatch(repo, patchname):
1573 'Return true if the given patch is in git format'
1573 'Return true if the given patch is in git format'
1574 mqpatch = os.path.join(repo.mq.path, patchname)
1574 mqpatch = os.path.join(repo.mq.path, patchname)
1575 for line in patch.linereader(open(mqpatch, 'rb')):
1575 for line in patch.linereader(open(mqpatch, 'rb')):
1576 if line.startswith('diff --git'):
1576 if line.startswith('diff --git'):
1577 return True
1577 return True
1578 return False
1578 return False
1579
1579
1580 def updatemq(repo, state, skipped, **opts):
1580 def updatemq(repo, state, skipped, **opts):
1581 'Update rebased mq patches - finalize and then import them'
1581 'Update rebased mq patches - finalize and then import them'
1582 mqrebase = {}
1582 mqrebase = {}
1583 mq = repo.mq
1583 mq = repo.mq
1584 original_series = mq.fullseries[:]
1584 original_series = mq.fullseries[:]
1585 skippedpatches = set()
1585 skippedpatches = set()
1586
1586
1587 for p in mq.applied:
1587 for p in mq.applied:
1588 rev = repo[p.node].rev()
1588 rev = repo[p.node].rev()
1589 if rev in state:
1589 if rev in state:
1590 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1590 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1591 (rev, p.name))
1591 (rev, p.name))
1592 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1592 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1593 else:
1593 else:
1594 # Applied but not rebased, not sure this should happen
1594 # Applied but not rebased, not sure this should happen
1595 skippedpatches.add(p.name)
1595 skippedpatches.add(p.name)
1596
1596
1597 if mqrebase:
1597 if mqrebase:
1598 mq.finish(repo, mqrebase.keys())
1598 mq.finish(repo, mqrebase.keys())
1599
1599
1600 # We must start import from the newest revision
1600 # We must start import from the newest revision
1601 for rev in sorted(mqrebase, reverse=True):
1601 for rev in sorted(mqrebase, reverse=True):
1602 if rev not in skipped:
1602 if rev not in skipped:
1603 name, isgit = mqrebase[rev]
1603 name, isgit = mqrebase[rev]
1604 repo.ui.note(_('updating mq patch %s to %d:%s\n') %
1604 repo.ui.note(_('updating mq patch %s to %d:%s\n') %
1605 (name, state[rev], repo[state[rev]]))
1605 (name, state[rev], repo[state[rev]]))
1606 mq.qimport(repo, (), patchname=name, git=isgit,
1606 mq.qimport(repo, (), patchname=name, git=isgit,
1607 rev=["%d" % state[rev]])
1607 rev=["%d" % state[rev]])
1608 else:
1608 else:
1609 # Rebased and skipped
1609 # Rebased and skipped
1610 skippedpatches.add(mqrebase[rev][0])
1610 skippedpatches.add(mqrebase[rev][0])
1611
1611
1612 # Patches were either applied and rebased and imported in
1612 # Patches were either applied and rebased and imported in
1613 # order, applied and removed or unapplied. Discard the removed
1613 # order, applied and removed or unapplied. Discard the removed
1614 # ones while preserving the original series order and guards.
1614 # ones while preserving the original series order and guards.
1615 newseries = [s for s in original_series
1615 newseries = [s for s in original_series
1616 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1616 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1617 mq.fullseries[:] = newseries
1617 mq.fullseries[:] = newseries
1618 mq.seriesdirty = True
1618 mq.seriesdirty = True
1619 mq.savedirty()
1619 mq.savedirty()
1620
1620
1621 def storecollapsemsg(repo, collapsemsg):
1621 def storecollapsemsg(repo, collapsemsg):
1622 'Store the collapse message to allow recovery'
1622 'Store the collapse message to allow recovery'
1623 collapsemsg = collapsemsg or ''
1623 collapsemsg = collapsemsg or ''
1624 f = repo.vfs("last-message.txt", "w")
1624 f = repo.vfs("last-message.txt", "w")
1625 f.write("%s\n" % collapsemsg)
1625 f.write("%s\n" % collapsemsg)
1626 f.close()
1626 f.close()
1627
1627
1628 def clearcollapsemsg(repo):
1628 def clearcollapsemsg(repo):
1629 'Remove collapse message file'
1629 'Remove collapse message file'
1630 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1630 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1631
1631
1632 def restorecollapsemsg(repo, isabort):
1632 def restorecollapsemsg(repo, isabort):
1633 'Restore previously stored collapse message'
1633 'Restore previously stored collapse message'
1634 try:
1634 try:
1635 f = repo.vfs("last-message.txt")
1635 f = repo.vfs("last-message.txt")
1636 collapsemsg = f.readline().strip()
1636 collapsemsg = f.readline().strip()
1637 f.close()
1637 f.close()
1638 except IOError as err:
1638 except IOError as err:
1639 if err.errno != errno.ENOENT:
1639 if err.errno != errno.ENOENT:
1640 raise
1640 raise
1641 if isabort:
1641 if isabort:
1642 # Oh well, just abort like normal
1642 # Oh well, just abort like normal
1643 collapsemsg = ''
1643 collapsemsg = ''
1644 else:
1644 else:
1645 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1645 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1646 return collapsemsg
1646 return collapsemsg
1647
1647
1648 def clearstatus(repo):
1648 def clearstatus(repo):
1649 'Remove the status files'
1649 'Remove the status files'
1650 # Make sure the active transaction won't write the state file
1650 # Make sure the active transaction won't write the state file
1651 tr = repo.currenttransaction()
1651 tr = repo.currenttransaction()
1652 if tr:
1652 if tr:
1653 tr.removefilegenerator('rebasestate')
1653 tr.removefilegenerator('rebasestate')
1654 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1654 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1655
1655
1656 def needupdate(repo, state):
1656 def needupdate(repo, state):
1657 '''check whether we should `update --clean` away from a merge, or if
1657 '''check whether we should `update --clean` away from a merge, or if
1658 somehow the working dir got forcibly updated, e.g. by older hg'''
1658 somehow the working dir got forcibly updated, e.g. by older hg'''
1659 parents = [p.rev() for p in repo[None].parents()]
1659 parents = [p.rev() for p in repo[None].parents()]
1660
1660
1661 # Are we in a merge state at all?
1661 # Are we in a merge state at all?
1662 if len(parents) < 2:
1662 if len(parents) < 2:
1663 return False
1663 return False
1664
1664
1665 # We should be standing on the first as-of-yet unrebased commit.
1665 # We should be standing on the first as-of-yet unrebased commit.
1666 firstunrebased = min([old for old, new in state.iteritems()
1666 firstunrebased = min([old for old, new in state.iteritems()
1667 if new == nullrev])
1667 if new == nullrev])
1668 if firstunrebased in parents:
1668 if firstunrebased in parents:
1669 return True
1669 return True
1670
1670
1671 return False
1671 return False
1672
1672
1673 def sortsource(destmap):
1673 def sortsource(destmap):
1674 """yield source revisions in an order that we only rebase things once
1674 """yield source revisions in an order that we only rebase things once
1675
1675
1676 If source and destination overlaps, we should filter out revisions
1676 If source and destination overlaps, we should filter out revisions
1677 depending on other revisions which hasn't been rebased yet.
1677 depending on other revisions which hasn't been rebased yet.
1678
1678
1679 Yield a sorted list of revisions each time.
1679 Yield a sorted list of revisions each time.
1680
1680
1681 For example, when rebasing A to B, B to C. This function yields [B], then
1681 For example, when rebasing A to B, B to C. This function yields [B], then
1682 [A], indicating B needs to be rebased first.
1682 [A], indicating B needs to be rebased first.
1683
1683
1684 Raise if there is a cycle so the rebase is impossible.
1684 Raise if there is a cycle so the rebase is impossible.
1685 """
1685 """
1686 srcset = set(destmap)
1686 srcset = set(destmap)
1687 while srcset:
1687 while srcset:
1688 srclist = sorted(srcset)
1688 srclist = sorted(srcset)
1689 result = []
1689 result = []
1690 for r in srclist:
1690 for r in srclist:
1691 if destmap[r] not in srcset:
1691 if destmap[r] not in srcset:
1692 result.append(r)
1692 result.append(r)
1693 if not result:
1693 if not result:
1694 raise error.Abort(_('source and destination form a cycle'))
1694 raise error.Abort(_('source and destination form a cycle'))
1695 srcset -= set(result)
1695 srcset -= set(result)
1696 yield result
1696 yield result
1697
1697
1698 def buildstate(repo, destmap, collapse):
1698 def buildstate(repo, destmap, collapse):
1699 '''Define which revisions are going to be rebased and where
1699 '''Define which revisions are going to be rebased and where
1700
1700
1701 repo: repo
1701 repo: repo
1702 destmap: {srcrev: destrev}
1702 destmap: {srcrev: destrev}
1703 '''
1703 '''
1704 rebaseset = destmap.keys()
1704 rebaseset = destmap.keys()
1705 originalwd = repo['.'].rev()
1705 originalwd = repo['.'].rev()
1706
1706
1707 # This check isn't strictly necessary, since mq detects commits over an
1707 # This check isn't strictly necessary, since mq detects commits over an
1708 # applied patch. But it prevents messing up the working directory when
1708 # applied patch. But it prevents messing up the working directory when
1709 # a partially completed rebase is blocked by mq.
1709 # a partially completed rebase is blocked by mq.
1710 if 'qtip' in repo.tags():
1710 if 'qtip' in repo.tags():
1711 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1711 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1712 if set(destmap.values()) & mqapplied:
1712 if set(destmap.values()) & mqapplied:
1713 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1713 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1714
1714
1715 # Get "cycle" error early by exhausting the generator.
1715 # Get "cycle" error early by exhausting the generator.
1716 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1716 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1717 if not sortedsrc:
1717 if not sortedsrc:
1718 raise error.Abort(_('no matching revisions'))
1718 raise error.Abort(_('no matching revisions'))
1719
1719
1720 # Only check the first batch of revisions to rebase not depending on other
1720 # Only check the first batch of revisions to rebase not depending on other
1721 # rebaseset. This means "source is ancestor of destination" for the second
1721 # rebaseset. This means "source is ancestor of destination" for the second
1722 # (and following) batches of revisions are not checked here. We rely on
1722 # (and following) batches of revisions are not checked here. We rely on
1723 # "defineparents" to do that check.
1723 # "defineparents" to do that check.
1724 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1724 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1725 if not roots:
1725 if not roots:
1726 raise error.Abort(_('no matching revisions'))
1726 raise error.Abort(_('no matching revisions'))
1727 def revof(r):
1727 def revof(r):
1728 return r.rev()
1728 return r.rev()
1729 roots = sorted(roots, key=revof)
1729 roots = sorted(roots, key=revof)
1730 state = dict.fromkeys(rebaseset, revtodo)
1730 state = dict.fromkeys(rebaseset, revtodo)
1731 emptyrebase = (len(sortedsrc) == 1)
1731 emptyrebase = (len(sortedsrc) == 1)
1732 for root in roots:
1732 for root in roots:
1733 dest = repo[destmap[root.rev()]]
1733 dest = repo[destmap[root.rev()]]
1734 commonbase = root.ancestor(dest)
1734 commonbase = root.ancestor(dest)
1735 if commonbase == root:
1735 if commonbase == root:
1736 raise error.Abort(_('source is ancestor of destination'))
1736 raise error.Abort(_('source is ancestor of destination'))
1737 if commonbase == dest:
1737 if commonbase == dest:
1738 wctx = repo[None]
1738 wctx = repo[None]
1739 if dest == wctx.p1():
1739 if dest == wctx.p1():
1740 # when rebasing to '.', it will use the current wd branch name
1740 # when rebasing to '.', it will use the current wd branch name
1741 samebranch = root.branch() == wctx.branch()
1741 samebranch = root.branch() == wctx.branch()
1742 else:
1742 else:
1743 samebranch = root.branch() == dest.branch()
1743 samebranch = root.branch() == dest.branch()
1744 if not collapse and samebranch and dest in root.parents():
1744 if not collapse and samebranch and dest in root.parents():
1745 # mark the revision as done by setting its new revision
1745 # mark the revision as done by setting its new revision
1746 # equal to its old (current) revisions
1746 # equal to its old (current) revisions
1747 state[root.rev()] = root.rev()
1747 state[root.rev()] = root.rev()
1748 repo.ui.debug('source is a child of destination\n')
1748 repo.ui.debug('source is a child of destination\n')
1749 continue
1749 continue
1750
1750
1751 emptyrebase = False
1751 emptyrebase = False
1752 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1752 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1753 if emptyrebase:
1753 if emptyrebase:
1754 return None
1754 return None
1755 for rev in sorted(state):
1755 for rev in sorted(state):
1756 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1756 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1757 # if all parents of this revision are done, then so is this revision
1757 # if all parents of this revision are done, then so is this revision
1758 if parents and all((state.get(p) == p for p in parents)):
1758 if parents and all((state.get(p) == p for p in parents)):
1759 state[rev] = rev
1759 state[rev] = rev
1760 return originalwd, destmap, state
1760 return originalwd, destmap, state
1761
1761
1762 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1762 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1763 keepf=False, fm=None, backup=True):
1763 keepf=False, fm=None, backup=True):
1764 """dispose of rebased revision at the end of the rebase
1764 """dispose of rebased revision at the end of the rebase
1765
1765
1766 If `collapsedas` is not None, the rebase was a collapse whose result if the
1766 If `collapsedas` is not None, the rebase was a collapse whose result if the
1767 `collapsedas` node.
1767 `collapsedas` node.
1768
1768
1769 If `keepf` is not True, the rebase has --keep set and no nodes should be
1769 If `keepf` is not True, the rebase has --keep set and no nodes should be
1770 removed (but bookmarks still need to be moved).
1770 removed (but bookmarks still need to be moved).
1771
1771
1772 If `backup` is False, no backup will be stored when stripping rebased
1772 If `backup` is False, no backup will be stored when stripping rebased
1773 revisions.
1773 revisions.
1774 """
1774 """
1775 tonode = repo.changelog.node
1775 tonode = repo.changelog.node
1776 replacements = {}
1776 replacements = {}
1777 moves = {}
1777 moves = {}
1778 stripcleanup = not obsolete.isenabled(repo, obsolete.createmarkersopt)
1778 stripcleanup = not obsolete.isenabled(repo, obsolete.createmarkersopt)
1779
1779
1780 collapsednodes = []
1780 collapsednodes = []
1781 for rev, newrev in sorted(state.items()):
1781 for rev, newrev in sorted(state.items()):
1782 if newrev >= 0 and newrev != rev:
1782 if newrev >= 0 and newrev != rev:
1783 oldnode = tonode(rev)
1783 oldnode = tonode(rev)
1784 newnode = collapsedas or tonode(newrev)
1784 newnode = collapsedas or tonode(newrev)
1785 moves[oldnode] = newnode
1785 moves[oldnode] = newnode
1786 if not keepf:
1786 if not keepf:
1787 succs = None
1787 succs = None
1788 if rev in skipped:
1788 if rev in skipped:
1789 if stripcleanup or not repo[rev].obsolete():
1789 if stripcleanup or not repo[rev].obsolete():
1790 succs = ()
1790 succs = ()
1791 elif collapsedas:
1791 elif collapsedas:
1792 collapsednodes.append(oldnode)
1792 collapsednodes.append(oldnode)
1793 else:
1793 else:
1794 succs = (newnode,)
1794 succs = (newnode,)
1795 if succs is not None:
1795 if succs is not None:
1796 replacements[(oldnode,)] = succs
1796 replacements[(oldnode,)] = succs
1797 if collapsednodes:
1797 if collapsednodes:
1798 replacements[tuple(collapsednodes)] = (collapsedas,)
1798 replacements[tuple(collapsednodes)] = (collapsedas,)
1799 scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup)
1799 scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup)
1800 if fm:
1800 if fm:
1801 hf = fm.hexfunc
1801 hf = fm.hexfunc
1802 fl = fm.formatlist
1802 fl = fm.formatlist
1803 fd = fm.formatdict
1803 fd = fm.formatdict
1804 changes = {}
1804 changes = {}
1805 for oldns, newn in replacements.iteritems():
1805 for oldns, newn in replacements.iteritems():
1806 for oldn in oldns:
1806 for oldn in oldns:
1807 changes[hf(oldn)] = fl([hf(n) for n in newn], name='node')
1807 changes[hf(oldn)] = fl([hf(n) for n in newn], name='node')
1808 nodechanges = fd(changes, key="oldnode", value="newnodes")
1808 nodechanges = fd(changes, key="oldnode", value="newnodes")
1809 fm.data(nodechanges=nodechanges)
1809 fm.data(nodechanges=nodechanges)
1810
1810
1811 def pullrebase(orig, ui, repo, *args, **opts):
1811 def pullrebase(orig, ui, repo, *args, **opts):
1812 'Call rebase after pull if the latter has been invoked with --rebase'
1812 'Call rebase after pull if the latter has been invoked with --rebase'
1813 if opts.get(r'rebase'):
1813 if opts.get(r'rebase'):
1814 if ui.configbool('commands', 'rebase.requiredest'):
1814 if ui.configbool('commands', 'rebase.requiredest'):
1815 msg = _('rebase destination required by configuration')
1815 msg = _('rebase destination required by configuration')
1816 hint = _('use hg pull followed by hg rebase -d DEST')
1816 hint = _('use hg pull followed by hg rebase -d DEST')
1817 raise error.Abort(msg, hint=hint)
1817 raise error.Abort(msg, hint=hint)
1818
1818
1819 with repo.wlock(), repo.lock():
1819 with repo.wlock(), repo.lock():
1820 if opts.get(r'update'):
1820 if opts.get(r'update'):
1821 del opts[r'update']
1821 del opts[r'update']
1822 ui.debug('--update and --rebase are not compatible, ignoring '
1822 ui.debug('--update and --rebase are not compatible, ignoring '
1823 'the update flag\n')
1823 'the update flag\n')
1824
1824
1825 cmdutil.checkunfinished(repo, skipmerge=True)
1825 cmdutil.checkunfinished(repo, skipmerge=True)
1826 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1826 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1827 'please commit or shelve your changes first'))
1827 'please commit or shelve your changes first'))
1828
1828
1829 revsprepull = len(repo)
1829 revsprepull = len(repo)
1830 origpostincoming = commands.postincoming
1830 origpostincoming = commands.postincoming
1831 def _dummy(*args, **kwargs):
1831 def _dummy(*args, **kwargs):
1832 pass
1832 pass
1833 commands.postincoming = _dummy
1833 commands.postincoming = _dummy
1834 try:
1834 try:
1835 ret = orig(ui, repo, *args, **opts)
1835 ret = orig(ui, repo, *args, **opts)
1836 finally:
1836 finally:
1837 commands.postincoming = origpostincoming
1837 commands.postincoming = origpostincoming
1838 revspostpull = len(repo)
1838 revspostpull = len(repo)
1839 if revspostpull > revsprepull:
1839 if revspostpull > revsprepull:
1840 # --rev option from pull conflict with rebase own --rev
1840 # --rev option from pull conflict with rebase own --rev
1841 # dropping it
1841 # dropping it
1842 if r'rev' in opts:
1842 if r'rev' in opts:
1843 del opts[r'rev']
1843 del opts[r'rev']
1844 # positional argument from pull conflicts with rebase's own
1844 # positional argument from pull conflicts with rebase's own
1845 # --source.
1845 # --source.
1846 if r'source' in opts:
1846 if r'source' in opts:
1847 del opts[r'source']
1847 del opts[r'source']
1848 # revsprepull is the len of the repo, not revnum of tip.
1848 # revsprepull is the len of the repo, not revnum of tip.
1849 destspace = list(repo.changelog.revs(start=revsprepull))
1849 destspace = list(repo.changelog.revs(start=revsprepull))
1850 opts[r'_destspace'] = destspace
1850 opts[r'_destspace'] = destspace
1851 try:
1851 try:
1852 rebase(ui, repo, **opts)
1852 rebase(ui, repo, **opts)
1853 except error.NoMergeDestAbort:
1853 except error.NoMergeDestAbort:
1854 # we can maybe update instead
1854 # we can maybe update instead
1855 rev, _a, _b = destutil.destupdate(repo)
1855 rev, _a, _b = destutil.destupdate(repo)
1856 if rev == repo['.'].rev():
1856 if rev == repo['.'].rev():
1857 ui.status(_('nothing to rebase\n'))
1857 ui.status(_('nothing to rebase\n'))
1858 else:
1858 else:
1859 ui.status(_('nothing to rebase - updating instead\n'))
1859 ui.status(_('nothing to rebase - updating instead\n'))
1860 # not passing argument to get the bare update behavior
1860 # not passing argument to get the bare update behavior
1861 # with warning and trumpets
1861 # with warning and trumpets
1862 commands.update(ui, repo)
1862 commands.update(ui, repo)
1863 else:
1863 else:
1864 if opts.get(r'tool'):
1864 if opts.get(r'tool'):
1865 raise error.Abort(_('--tool can only be used with --rebase'))
1865 raise error.Abort(_('--tool can only be used with --rebase'))
1866 ret = orig(ui, repo, *args, **opts)
1866 ret = orig(ui, repo, *args, **opts)
1867
1867
1868 return ret
1868 return ret
1869
1869
1870 def _filterobsoleterevs(repo, revs):
1870 def _filterobsoleterevs(repo, revs):
1871 """returns a set of the obsolete revisions in revs"""
1871 """returns a set of the obsolete revisions in revs"""
1872 return set(r for r in revs if repo[r].obsolete())
1872 return set(r for r in revs if repo[r].obsolete())
1873
1873
1874 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1874 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1875 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
1875 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
1876
1876
1877 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
1877 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
1878 obsolete nodes to be rebased given in `rebaseobsrevs`.
1878 obsolete nodes to be rebased given in `rebaseobsrevs`.
1879
1879
1880 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
1880 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
1881 without a successor in destination.
1881 without a successor in destination.
1882
1882
1883 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
1883 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
1884 obsolete successors.
1884 obsolete successors.
1885 """
1885 """
1886 obsoletenotrebased = {}
1886 obsoletenotrebased = {}
1887 obsoletewithoutsuccessorindestination = set()
1887 obsoletewithoutsuccessorindestination = set()
1888 obsoleteextinctsuccessors = set()
1888 obsoleteextinctsuccessors = set()
1889
1889
1890 assert repo.filtername is None
1890 assert repo.filtername is None
1891 cl = repo.changelog
1891 cl = repo.changelog
1892 nodemap = cl.nodemap
1892 nodemap = cl.nodemap
1893 extinctrevs = set(repo.revs('extinct()'))
1893 extinctrevs = set(repo.revs('extinct()'))
1894 for srcrev in rebaseobsrevs:
1894 for srcrev in rebaseobsrevs:
1895 srcnode = cl.node(srcrev)
1895 srcnode = cl.node(srcrev)
1896 # XXX: more advanced APIs are required to handle split correctly
1896 # XXX: more advanced APIs are required to handle split correctly
1897 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1897 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1898 # obsutil.allsuccessors includes node itself
1898 # obsutil.allsuccessors includes node itself
1899 successors.remove(srcnode)
1899 successors.remove(srcnode)
1900 succrevs = {nodemap[s] for s in successors if s in nodemap}
1900 succrevs = {nodemap[s] for s in successors if s in nodemap}
1901 if succrevs.issubset(extinctrevs):
1901 if succrevs.issubset(extinctrevs):
1902 # all successors are extinct
1902 # all successors are extinct
1903 obsoleteextinctsuccessors.add(srcrev)
1903 obsoleteextinctsuccessors.add(srcrev)
1904 if not successors:
1904 if not successors:
1905 # no successor
1905 # no successor
1906 obsoletenotrebased[srcrev] = None
1906 obsoletenotrebased[srcrev] = None
1907 else:
1907 else:
1908 dstrev = destmap[srcrev]
1908 dstrev = destmap[srcrev]
1909 for succrev in succrevs:
1909 for succrev in succrevs:
1910 if cl.isancestorrev(succrev, dstrev):
1910 if cl.isancestorrev(succrev, dstrev):
1911 obsoletenotrebased[srcrev] = succrev
1911 obsoletenotrebased[srcrev] = succrev
1912 break
1912 break
1913 else:
1913 else:
1914 # If 'srcrev' has a successor in rebase set but none in
1914 # If 'srcrev' has a successor in rebase set but none in
1915 # destination (which would be catched above), we shall skip it
1915 # destination (which would be catched above), we shall skip it
1916 # and its descendants to avoid divergence.
1916 # and its descendants to avoid divergence.
1917 if srcrev in extinctrevs or any(s in destmap for s in succrevs):
1917 if srcrev in extinctrevs or any(s in destmap for s in succrevs):
1918 obsoletewithoutsuccessorindestination.add(srcrev)
1918 obsoletewithoutsuccessorindestination.add(srcrev)
1919
1919
1920 return (
1920 return (
1921 obsoletenotrebased,
1921 obsoletenotrebased,
1922 obsoletewithoutsuccessorindestination,
1922 obsoletewithoutsuccessorindestination,
1923 obsoleteextinctsuccessors,
1923 obsoleteextinctsuccessors,
1924 )
1924 )
1925
1925
1926 def abortrebase(ui, repo):
1926 def abortrebase(ui, repo):
1927 with repo.wlock(), repo.lock():
1927 with repo.wlock(), repo.lock():
1928 rbsrt = rebaseruntime(repo, ui)
1928 rbsrt = rebaseruntime(repo, ui)
1929 rbsrt._prepareabortorcontinue(isabort=True)
1929 rbsrt._prepareabortorcontinue(isabort=True)
1930
1930
1931 def continuerebase(ui, repo):
1932 with repo.wlock(), repo.lock():
1933 rbsrt = rebaseruntime(repo, ui)
1934 ms = mergemod.mergestate.read(repo)
1935 mergeutil.checkunresolved(ms)
1936 retcode = rbsrt._prepareabortorcontinue(isabort=False)
1937 if retcode is not None:
1938 return retcode
1939 rbsrt._performrebase(None)
1940 rbsrt._finishrebase()
1941
1931 def summaryhook(ui, repo):
1942 def summaryhook(ui, repo):
1932 if not repo.vfs.exists('rebasestate'):
1943 if not repo.vfs.exists('rebasestate'):
1933 return
1944 return
1934 try:
1945 try:
1935 rbsrt = rebaseruntime(repo, ui, {})
1946 rbsrt = rebaseruntime(repo, ui, {})
1936 rbsrt.restorestatus()
1947 rbsrt.restorestatus()
1937 state = rbsrt.state
1948 state = rbsrt.state
1938 except error.RepoLookupError:
1949 except error.RepoLookupError:
1939 # i18n: column positioning for "hg summary"
1950 # i18n: column positioning for "hg summary"
1940 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1951 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1941 ui.write(msg)
1952 ui.write(msg)
1942 return
1953 return
1943 numrebased = len([i for i in state.itervalues() if i >= 0])
1954 numrebased = len([i for i in state.itervalues() if i >= 0])
1944 # i18n: column positioning for "hg summary"
1955 # i18n: column positioning for "hg summary"
1945 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1956 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1946 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1957 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1947 ui.label(_('%d remaining'), 'rebase.remaining') %
1958 ui.label(_('%d remaining'), 'rebase.remaining') %
1948 (len(state) - numrebased)))
1959 (len(state) - numrebased)))
1949
1960
1950 def uisetup(ui):
1961 def uisetup(ui):
1951 #Replace pull with a decorator to provide --rebase option
1962 #Replace pull with a decorator to provide --rebase option
1952 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1963 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1953 entry[1].append(('', 'rebase', None,
1964 entry[1].append(('', 'rebase', None,
1954 _("rebase working directory to branch head")))
1965 _("rebase working directory to branch head")))
1955 entry[1].append(('t', 'tool', '',
1966 entry[1].append(('t', 'tool', '',
1956 _("specify merge tool for rebase")))
1967 _("specify merge tool for rebase")))
1957 cmdutil.summaryhooks.add('rebase', summaryhook)
1968 cmdutil.summaryhooks.add('rebase', summaryhook)
1958 statemod.addunfinished('rebase', fname='rebasestate', stopflag=True,
1969 statemod.addunfinished('rebase', fname='rebasestate', stopflag=True,
1959 continueflag=True, abortfunc=abortrebase)
1970 continueflag=True, abortfunc=abortrebase,
1971 continuefunc=continuerebase)
@@ -1,92 +1,99 b''
1 #testcases continuecommand continueflag
2 #if continueflag
3 $ cat >> $HGRCPATH <<EOF
4 > [alias]
5 > continue = rebase --continue
6 > EOF
7 #endif
1
8
2 $ . "$TESTDIR/narrow-library.sh"
9 $ . "$TESTDIR/narrow-library.sh"
3
10
4 create full repo
11 create full repo
5
12
6 $ hg init master
13 $ hg init master
7 $ cd master
14 $ cd master
8
15
9 $ mkdir inside
16 $ mkdir inside
10 $ echo inside1 > inside/f1
17 $ echo inside1 > inside/f1
11 $ echo inside2 > inside/f2
18 $ echo inside2 > inside/f2
12 $ mkdir outside
19 $ mkdir outside
13 $ echo outside1 > outside/f1
20 $ echo outside1 > outside/f1
14 $ echo outside2 > outside/f2
21 $ echo outside2 > outside/f2
15 $ hg ci -Aqm 'initial'
22 $ hg ci -Aqm 'initial'
16
23
17 $ echo modified > inside/f1
24 $ echo modified > inside/f1
18 $ hg ci -qm 'modify inside/f1'
25 $ hg ci -qm 'modify inside/f1'
19
26
20 $ hg update -q 0
27 $ hg update -q 0
21 $ echo modified2 > inside/f2
28 $ echo modified2 > inside/f2
22 $ hg ci -qm 'modify inside/f2'
29 $ hg ci -qm 'modify inside/f2'
23
30
24 $ hg update -q 0
31 $ hg update -q 0
25 $ echo modified > outside/f1
32 $ echo modified > outside/f1
26 $ hg ci -qm 'modify outside/f1'
33 $ hg ci -qm 'modify outside/f1'
27
34
28 $ hg update -q 0
35 $ hg update -q 0
29 $ echo modified2 > outside/f1
36 $ echo modified2 > outside/f1
30 $ hg ci -qm 'conflicting outside/f1'
37 $ hg ci -qm 'conflicting outside/f1'
31
38
32 $ cd ..
39 $ cd ..
33
40
34 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
41 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
35 requesting all changes
42 requesting all changes
36 adding changesets
43 adding changesets
37 adding manifests
44 adding manifests
38 adding file changes
45 adding file changes
39 added 5 changesets with 4 changes to 2 files (+3 heads)
46 added 5 changesets with 4 changes to 2 files (+3 heads)
40 new changesets *:* (glob)
47 new changesets *:* (glob)
41 updating to branch default
48 updating to branch default
42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 $ cd narrow
50 $ cd narrow
44 $ cat >> $HGRCPATH <<EOF
51 $ cat >> $HGRCPATH <<EOF
45 > [extensions]
52 > [extensions]
46 > rebase=
53 > rebase=
47 > EOF
54 > EOF
48
55
49 $ hg update -q 0
56 $ hg update -q 0
50
57
51 Can rebase onto commit where no files outside narrow spec are involved
58 Can rebase onto commit where no files outside narrow spec are involved
52
59
53 $ hg update -q 0
60 $ hg update -q 0
54 $ echo modified > inside/f2
61 $ echo modified > inside/f2
55 $ hg ci -qm 'modify inside/f2'
62 $ hg ci -qm 'modify inside/f2'
56 $ hg rebase -d 'desc("modify inside/f1")'
63 $ hg rebase -d 'desc("modify inside/f1")'
57 rebasing 5:c2f36d04e05d "modify inside/f2" (tip)
64 rebasing 5:c2f36d04e05d "modify inside/f2" (tip)
58 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
65 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
59
66
60 Can rebase onto conflicting changes inside narrow spec
67 Can rebase onto conflicting changes inside narrow spec
61
68
62 $ hg update -q 0
69 $ hg update -q 0
63 $ echo conflicting > inside/f1
70 $ echo conflicting > inside/f1
64 $ hg ci -qm 'conflicting inside/f1'
71 $ hg ci -qm 'conflicting inside/f1'
65 $ hg rebase -d 'desc("modify inside/f1")' 2>&1 | egrep -v '(warning:|incomplete!)'
72 $ hg rebase -d 'desc("modify inside/f1")' 2>&1 | egrep -v '(warning:|incomplete!)'
66 rebasing 6:cdce97fbf653 "conflicting inside/f1" (tip)
73 rebasing 6:cdce97fbf653 "conflicting inside/f1" (tip)
67 merging inside/f1
74 merging inside/f1
68 unresolved conflicts (see hg resolve, then hg rebase --continue)
75 unresolved conflicts (see hg resolve, then hg rebase --continue)
69 $ echo modified3 > inside/f1
76 $ echo modified3 > inside/f1
70 $ hg resolve -m 2>&1 | grep -v continue:
77 $ hg resolve -m 2>&1 | grep -v continue:
71 (no more unresolved files)
78 (no more unresolved files)
72 $ hg rebase --continue
79 $ hg continue
73 rebasing 6:cdce97fbf653 "conflicting inside/f1" (tip)
80 rebasing 6:cdce97fbf653 "conflicting inside/f1" (tip)
74 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
81 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
75
82
76 Can rebase onto non-conflicting changes outside narrow spec
83 Can rebase onto non-conflicting changes outside narrow spec
77
84
78 $ hg update -q 0
85 $ hg update -q 0
79 $ echo modified > inside/f2
86 $ echo modified > inside/f2
80 $ hg ci -qm 'modify inside/f2'
87 $ hg ci -qm 'modify inside/f2'
81 $ hg rebase -d 'desc("modify outside/f1")'
88 $ hg rebase -d 'desc("modify outside/f1")'
82 rebasing 7:c2f36d04e05d "modify inside/f2" (tip)
89 rebasing 7:c2f36d04e05d "modify inside/f2" (tip)
83 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
90 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-rebase.hg (glob)
84
91
85 Rebase interrupts on conflicting changes outside narrow spec
92 Rebase interrupts on conflicting changes outside narrow spec
86
93
87 $ hg update -q 'desc("conflicting outside/f1")'
94 $ hg update -q 'desc("conflicting outside/f1")'
88 $ hg phase -f -d .
95 $ hg phase -f -d .
89 $ hg rebase -d 'desc("modify outside/f1")'
96 $ hg rebase -d 'desc("modify outside/f1")'
90 rebasing 4:707c035aadb6 "conflicting outside/f1"
97 rebasing 4:707c035aadb6 "conflicting outside/f1"
91 abort: conflict in file 'outside/f1' is outside narrow clone
98 abort: conflict in file 'outside/f1' is outside narrow clone
92 [255]
99 [255]
@@ -1,517 +1,525 b''
1 #testcases abortcommand abortflag
1 #testcases abortcommand abortflag
2 #testcases continuecommand continueflag
2
3
3 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
5 > [extensions]
5 > rebase=
6 > rebase=
6 >
7 >
7 > [phases]
8 > [phases]
8 > publish=False
9 > publish=False
9 >
10 >
10 > [alias]
11 > [alias]
11 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
12 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
12 > EOF
13 > EOF
13
14
14 #if abortflag
15 #if abortflag
15 $ cat >> $HGRCPATH <<EOF
16 $ cat >> $HGRCPATH <<EOF
16 > [alias]
17 > [alias]
17 > abort = rebase --abort
18 > abort = rebase --abort
18 > EOF
19 > EOF
19 #endif
20 #endif
20
21
22 #if continueflag
23 $ cat >> $HGRCPATH <<EOF
24 > [alias]
25 > continue = rebase --continue
26 > EOF
27 #endif
28
21 $ hg init a
29 $ hg init a
22 $ cd a
30 $ cd a
23
31
24 $ touch .hg/rebasestate
32 $ touch .hg/rebasestate
25 $ hg sum
33 $ hg sum
26 parent: -1:000000000000 tip (empty repository)
34 parent: -1:000000000000 tip (empty repository)
27 branch: default
35 branch: default
28 commit: (clean)
36 commit: (clean)
29 update: (current)
37 update: (current)
30 abort: .hg/rebasestate is incomplete
38 abort: .hg/rebasestate is incomplete
31 [255]
39 [255]
32 $ rm .hg/rebasestate
40 $ rm .hg/rebasestate
33
41
34 $ echo c1 > common
42 $ echo c1 > common
35 $ hg add common
43 $ hg add common
36 $ hg ci -m C1
44 $ hg ci -m C1
37
45
38 $ echo c2 >> common
46 $ echo c2 >> common
39 $ hg ci -m C2
47 $ hg ci -m C2
40
48
41 $ echo c3 >> common
49 $ echo c3 >> common
42 $ hg ci -m C3
50 $ hg ci -m C3
43
51
44 $ hg up -q -C 1
52 $ hg up -q -C 1
45
53
46 $ echo l1 >> extra
54 $ echo l1 >> extra
47 $ hg add extra
55 $ hg add extra
48 $ hg ci -m L1
56 $ hg ci -m L1
49 created new head
57 created new head
50
58
51 $ sed -e 's/c2/l2/' common > common.new
59 $ sed -e 's/c2/l2/' common > common.new
52 $ mv common.new common
60 $ mv common.new common
53 $ hg ci -m L2
61 $ hg ci -m L2
54
62
55 $ hg phase --force --secret 2
63 $ hg phase --force --secret 2
56
64
57 $ hg tglog
65 $ hg tglog
58 @ 4:draft 'L2'
66 @ 4:draft 'L2'
59 |
67 |
60 o 3:draft 'L1'
68 o 3:draft 'L1'
61 |
69 |
62 | o 2:secret 'C3'
70 | o 2:secret 'C3'
63 |/
71 |/
64 o 1:draft 'C2'
72 o 1:draft 'C2'
65 |
73 |
66 o 0:draft 'C1'
74 o 0:draft 'C1'
67
75
68
76
69 Conflicting rebase:
77 Conflicting rebase:
70
78
71 $ hg rebase -s 3 -d 2
79 $ hg rebase -s 3 -d 2
72 rebasing 3:3163e20567cc "L1"
80 rebasing 3:3163e20567cc "L1"
73 rebasing 4:46f0b057b5c0 "L2" (tip)
81 rebasing 4:46f0b057b5c0 "L2" (tip)
74 merging common
82 merging common
75 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
83 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
76 unresolved conflicts (see hg resolve, then hg rebase --continue)
84 unresolved conflicts (see hg resolve, then hg rebase --continue)
77 [1]
85 [1]
78
86
79 Insert unsupported advisory merge record:
87 Insert unsupported advisory merge record:
80
88
81 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
89 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
82 $ hg debugmergestate
90 $ hg debugmergestate
83 * version 2 records
91 * version 2 records
84 local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
92 local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
85 other: 46f0b057b5c061d276b91491c22151f78698abd2
93 other: 46f0b057b5c061d276b91491c22151f78698abd2
86 labels:
94 labels:
87 local: dest
95 local: dest
88 other: source
96 other: source
89 unrecognized entry: x advisory record
97 unrecognized entry: x advisory record
90 file extras: common (ancestorlinknode = 3163e20567cc93074fbb7a53c8b93312e59dbf2c)
98 file extras: common (ancestorlinknode = 3163e20567cc93074fbb7a53c8b93312e59dbf2c)
91 file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
99 file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
92 local path: common (flags "")
100 local path: common (flags "")
93 ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
101 ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
94 other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
102 other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
95 $ hg resolve -l
103 $ hg resolve -l
96 U common
104 U common
97
105
98 Insert unsupported mandatory merge record:
106 Insert unsupported mandatory merge record:
99
107
100 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
108 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
101 $ hg debugmergestate
109 $ hg debugmergestate
102 * version 2 records
110 * version 2 records
103 local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
111 local: 3e046f2ecedb793b97ed32108086edd1a162f8bc
104 other: 46f0b057b5c061d276b91491c22151f78698abd2
112 other: 46f0b057b5c061d276b91491c22151f78698abd2
105 labels:
113 labels:
106 local: dest
114 local: dest
107 other: source
115 other: source
108 file extras: common (ancestorlinknode = 3163e20567cc93074fbb7a53c8b93312e59dbf2c)
116 file extras: common (ancestorlinknode = 3163e20567cc93074fbb7a53c8b93312e59dbf2c)
109 file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
117 file: common (record type "F", state "u", hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1)
110 local path: common (flags "")
118 local path: common (flags "")
111 ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
119 ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
112 other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
120 other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
113 unrecognized entry: X mandatory record
121 unrecognized entry: X mandatory record
114 $ hg resolve -l
122 $ hg resolve -l
115 abort: unsupported merge state records: X
123 abort: unsupported merge state records: X
116 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
124 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
117 [255]
125 [255]
118 $ hg resolve -ma
126 $ hg resolve -ma
119 abort: unsupported merge state records: X
127 abort: unsupported merge state records: X
120 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
128 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
121 [255]
129 [255]
122
130
123 Abort (should clear out unsupported merge state):
131 Abort (should clear out unsupported merge state):
124
132
125 #if abortcommand
133 #if abortcommand
126 when in dry-run mode
134 when in dry-run mode
127 $ hg abort --dry-run
135 $ hg abort --dry-run
128 rebase in progress, will be aborted
136 rebase in progress, will be aborted
129 #endif
137 #endif
130
138
131 $ hg abort
139 $ hg abort
132 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3e046f2ecedb-6beef7d5-backup.hg
140 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3e046f2ecedb-6beef7d5-backup.hg
133 rebase aborted
141 rebase aborted
134 $ hg debugmergestate
142 $ hg debugmergestate
135 no merge state found
143 no merge state found
136
144
137 $ hg tglog
145 $ hg tglog
138 @ 4:draft 'L2'
146 @ 4:draft 'L2'
139 |
147 |
140 o 3:draft 'L1'
148 o 3:draft 'L1'
141 |
149 |
142 | o 2:secret 'C3'
150 | o 2:secret 'C3'
143 |/
151 |/
144 o 1:draft 'C2'
152 o 1:draft 'C2'
145 |
153 |
146 o 0:draft 'C1'
154 o 0:draft 'C1'
147
155
148 Test safety for inconsistent rebase state, which may be created (and
156 Test safety for inconsistent rebase state, which may be created (and
149 forgotten) by Mercurial earlier than 2.7. This emulates Mercurial
157 forgotten) by Mercurial earlier than 2.7. This emulates Mercurial
150 earlier than 2.7 by renaming ".hg/rebasestate" temporarily.
158 earlier than 2.7 by renaming ".hg/rebasestate" temporarily.
151
159
152 $ hg rebase -s 3 -d 2
160 $ hg rebase -s 3 -d 2
153 rebasing 3:3163e20567cc "L1"
161 rebasing 3:3163e20567cc "L1"
154 rebasing 4:46f0b057b5c0 "L2" (tip)
162 rebasing 4:46f0b057b5c0 "L2" (tip)
155 merging common
163 merging common
156 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
164 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
157 unresolved conflicts (see hg resolve, then hg rebase --continue)
165 unresolved conflicts (see hg resolve, then hg rebase --continue)
158 [1]
166 [1]
159
167
160 $ mv .hg/rebasestate .hg/rebasestate.back
168 $ mv .hg/rebasestate .hg/rebasestate.back
161 $ hg update --quiet --clean 2
169 $ hg update --quiet --clean 2
162 $ hg --config extensions.mq= strip --quiet "destination()"
170 $ hg --config extensions.mq= strip --quiet "destination()"
163 $ mv .hg/rebasestate.back .hg/rebasestate
171 $ mv .hg/rebasestate.back .hg/rebasestate
164
172
165 $ hg rebase --continue
173 $ hg continue
166 abort: cannot continue inconsistent rebase
174 abort: cannot continue inconsistent rebase
167 (use "hg rebase --abort" to clear broken state)
175 (use "hg rebase --abort" to clear broken state)
168 [255]
176 [255]
169 $ hg summary | grep '^rebase: '
177 $ hg summary | grep '^rebase: '
170 rebase: (use "hg rebase --abort" to clear broken state)
178 rebase: (use "hg rebase --abort" to clear broken state)
171 $ hg abort
179 $ hg abort
172 rebase aborted (no revision is removed, only broken state is cleared)
180 rebase aborted (no revision is removed, only broken state is cleared)
173
181
174 $ cd ..
182 $ cd ..
175
183
176
184
177 Construct new repo:
185 Construct new repo:
178
186
179 $ hg init b
187 $ hg init b
180 $ cd b
188 $ cd b
181
189
182 $ echo a > a
190 $ echo a > a
183 $ hg ci -Am A
191 $ hg ci -Am A
184 adding a
192 adding a
185
193
186 $ echo b > b
194 $ echo b > b
187 $ hg ci -Am B
195 $ hg ci -Am B
188 adding b
196 adding b
189
197
190 $ echo c > c
198 $ echo c > c
191 $ hg ci -Am C
199 $ hg ci -Am C
192 adding c
200 adding c
193
201
194 $ hg up -q 0
202 $ hg up -q 0
195
203
196 $ echo b > b
204 $ echo b > b
197 $ hg ci -Am 'B bis'
205 $ hg ci -Am 'B bis'
198 adding b
206 adding b
199 created new head
207 created new head
200
208
201 $ echo c1 > c
209 $ echo c1 > c
202 $ hg ci -Am C1
210 $ hg ci -Am C1
203 adding c
211 adding c
204
212
205 $ hg phase --force --secret 1
213 $ hg phase --force --secret 1
206 $ hg phase --public 1
214 $ hg phase --public 1
207
215
208 Rebase and abort without generating new changesets:
216 Rebase and abort without generating new changesets:
209
217
210 $ hg tglog
218 $ hg tglog
211 @ 4:draft 'C1'
219 @ 4:draft 'C1'
212 |
220 |
213 o 3:draft 'B bis'
221 o 3:draft 'B bis'
214 |
222 |
215 | o 2:secret 'C'
223 | o 2:secret 'C'
216 | |
224 | |
217 | o 1:public 'B'
225 | o 1:public 'B'
218 |/
226 |/
219 o 0:public 'A'
227 o 0:public 'A'
220
228
221 $ hg rebase -b 4 -d 2
229 $ hg rebase -b 4 -d 2
222 rebasing 3:a6484957d6b9 "B bis"
230 rebasing 3:a6484957d6b9 "B bis"
223 note: not rebasing 3:a6484957d6b9 "B bis", its destination already has all its changes
231 note: not rebasing 3:a6484957d6b9 "B bis", its destination already has all its changes
224 rebasing 4:145842775fec "C1" (tip)
232 rebasing 4:145842775fec "C1" (tip)
225 merging c
233 merging c
226 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
234 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
227 unresolved conflicts (see hg resolve, then hg rebase --continue)
235 unresolved conflicts (see hg resolve, then hg rebase --continue)
228 [1]
236 [1]
229
237
230 $ hg tglog
238 $ hg tglog
231 @ 4:draft 'C1'
239 @ 4:draft 'C1'
232 |
240 |
233 o 3:draft 'B bis'
241 o 3:draft 'B bis'
234 |
242 |
235 | @ 2:secret 'C'
243 | @ 2:secret 'C'
236 | |
244 | |
237 | o 1:public 'B'
245 | o 1:public 'B'
238 |/
246 |/
239 o 0:public 'A'
247 o 0:public 'A'
240
248
241 $ hg rebase -a
249 $ hg rebase -a
242 rebase aborted
250 rebase aborted
243
251
244 $ hg tglog
252 $ hg tglog
245 @ 4:draft 'C1'
253 @ 4:draft 'C1'
246 |
254 |
247 o 3:draft 'B bis'
255 o 3:draft 'B bis'
248 |
256 |
249 | o 2:secret 'C'
257 | o 2:secret 'C'
250 | |
258 | |
251 | o 1:public 'B'
259 | o 1:public 'B'
252 |/
260 |/
253 o 0:public 'A'
261 o 0:public 'A'
254
262
255
263
256 $ cd ..
264 $ cd ..
257
265
258 rebase abort should not leave working copy in a merge state if tip-1 is public
266 rebase abort should not leave working copy in a merge state if tip-1 is public
259 (issue4082)
267 (issue4082)
260
268
261 $ hg init abortpublic
269 $ hg init abortpublic
262 $ cd abortpublic
270 $ cd abortpublic
263 $ echo a > a && hg ci -Aqm a
271 $ echo a > a && hg ci -Aqm a
264 $ hg book master
272 $ hg book master
265 $ hg book foo
273 $ hg book foo
266 $ echo b > b && hg ci -Aqm b
274 $ echo b > b && hg ci -Aqm b
267 $ hg up -q master
275 $ hg up -q master
268 $ echo c > c && hg ci -Aqm c
276 $ echo c > c && hg ci -Aqm c
269 $ hg phase -p -r .
277 $ hg phase -p -r .
270 $ hg up -q foo
278 $ hg up -q foo
271 $ echo C > c && hg ci -Aqm C
279 $ echo C > c && hg ci -Aqm C
272 $ hg log -G --template "{rev} {desc} {bookmarks}"
280 $ hg log -G --template "{rev} {desc} {bookmarks}"
273 @ 3 C foo
281 @ 3 C foo
274 |
282 |
275 | o 2 c master
283 | o 2 c master
276 | |
284 | |
277 o | 1 b
285 o | 1 b
278 |/
286 |/
279 o 0 a
287 o 0 a
280
288
281
289
282 $ hg rebase -d master -r foo
290 $ hg rebase -d master -r foo
283 rebasing 3:6c0f977a22d8 "C" (foo tip)
291 rebasing 3:6c0f977a22d8 "C" (foo tip)
284 merging c
292 merging c
285 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
293 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
286 unresolved conflicts (see hg resolve, then hg rebase --continue)
294 unresolved conflicts (see hg resolve, then hg rebase --continue)
287 [1]
295 [1]
288 $ hg abort
296 $ hg abort
289 rebase aborted
297 rebase aborted
290 $ hg log -G --template "{rev} {desc} {bookmarks}"
298 $ hg log -G --template "{rev} {desc} {bookmarks}"
291 @ 3 C foo
299 @ 3 C foo
292 |
300 |
293 | o 2 c master
301 | o 2 c master
294 | |
302 | |
295 o | 1 b
303 o | 1 b
296 |/
304 |/
297 o 0 a
305 o 0 a
298
306
299 $ cd ..
307 $ cd ..
300
308
301 Make sure we don't clobber changes in the working directory when the
309 Make sure we don't clobber changes in the working directory when the
302 user has somehow managed to update to a different revision (issue4009)
310 user has somehow managed to update to a different revision (issue4009)
303
311
304 $ hg init noupdate
312 $ hg init noupdate
305 $ cd noupdate
313 $ cd noupdate
306 $ hg book @
314 $ hg book @
307 $ echo original > a
315 $ echo original > a
308 $ hg add a
316 $ hg add a
309 $ hg commit -m a
317 $ hg commit -m a
310 $ echo x > b
318 $ echo x > b
311 $ hg add b
319 $ hg add b
312 $ hg commit -m b1
320 $ hg commit -m b1
313 $ hg up 0
321 $ hg up 0
314 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
322 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
315 (leaving bookmark @)
323 (leaving bookmark @)
316 $ hg book foo
324 $ hg book foo
317 $ echo y > b
325 $ echo y > b
318 $ hg add b
326 $ hg add b
319 $ hg commit -m b2
327 $ hg commit -m b2
320 created new head
328 created new head
321
329
322 $ hg rebase -d @ -b foo --tool=internal:fail
330 $ hg rebase -d @ -b foo --tool=internal:fail
323 rebasing 2:070cf4580bb5 "b2" (foo tip)
331 rebasing 2:070cf4580bb5 "b2" (foo tip)
324 unresolved conflicts (see hg resolve, then hg rebase --continue)
332 unresolved conflicts (see hg resolve, then hg rebase --continue)
325 [1]
333 [1]
326
334
327 $ mv .hg/rebasestate ./ # so we're allowed to hg up like in mercurial <2.6.3
335 $ mv .hg/rebasestate ./ # so we're allowed to hg up like in mercurial <2.6.3
328 $ hg up -C 0 # user does other stuff in the repo
336 $ hg up -C 0 # user does other stuff in the repo
329 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
337 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
330
338
331 $ mv rebasestate .hg/ # user upgrades to 2.7
339 $ mv rebasestate .hg/ # user upgrades to 2.7
332
340
333 $ echo new > a
341 $ echo new > a
334 $ hg up 1 # user gets an error saying to run hg rebase --abort
342 $ hg up 1 # user gets an error saying to run hg rebase --abort
335 abort: rebase in progress
343 abort: rebase in progress
336 (use 'hg rebase --continue' or 'hg rebase --abort')
344 (use 'hg rebase --continue' or 'hg rebase --abort')
337 [255]
345 [255]
338
346
339 $ cat a
347 $ cat a
340 new
348 new
341 $ hg abort
349 $ hg abort
342 rebase aborted
350 rebase aborted
343 $ cat a
351 $ cat a
344 new
352 new
345
353
346 $ cd ..
354 $ cd ..
347
355
348 test aborting an interrupted series (issue5084)
356 test aborting an interrupted series (issue5084)
349 $ hg init interrupted
357 $ hg init interrupted
350 $ cd interrupted
358 $ cd interrupted
351 $ touch base
359 $ touch base
352 $ hg add base
360 $ hg add base
353 $ hg commit -m base
361 $ hg commit -m base
354 $ touch a
362 $ touch a
355 $ hg add a
363 $ hg add a
356 $ hg commit -m a
364 $ hg commit -m a
357 $ echo 1 > a
365 $ echo 1 > a
358 $ hg commit -m 1
366 $ hg commit -m 1
359 $ touch b
367 $ touch b
360 $ hg add b
368 $ hg add b
361 $ hg commit -m b
369 $ hg commit -m b
362 $ echo 2 >> a
370 $ echo 2 >> a
363 $ hg commit -m c
371 $ hg commit -m c
364 $ touch d
372 $ touch d
365 $ hg add d
373 $ hg add d
366 $ hg commit -m d
374 $ hg commit -m d
367 $ hg co -q 1
375 $ hg co -q 1
368 $ hg rm a
376 $ hg rm a
369 $ hg commit -m no-a
377 $ hg commit -m no-a
370 created new head
378 created new head
371 $ hg co 0
379 $ hg co 0
372 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
380 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
373 $ hg log -G --template "{rev} {desc} {bookmarks}"
381 $ hg log -G --template "{rev} {desc} {bookmarks}"
374 o 6 no-a
382 o 6 no-a
375 |
383 |
376 | o 5 d
384 | o 5 d
377 | |
385 | |
378 | o 4 c
386 | o 4 c
379 | |
387 | |
380 | o 3 b
388 | o 3 b
381 | |
389 | |
382 | o 2 1
390 | o 2 1
383 |/
391 |/
384 o 1 a
392 o 1 a
385 |
393 |
386 @ 0 base
394 @ 0 base
387
395
388 $ hg --config extensions.n=$TESTDIR/failfilemerge.py rebase -s 3 -d tip
396 $ hg --config extensions.n=$TESTDIR/failfilemerge.py rebase -s 3 -d tip
389 rebasing 3:3a71550954f1 "b"
397 rebasing 3:3a71550954f1 "b"
390 rebasing 4:e80b69427d80 "c"
398 rebasing 4:e80b69427d80 "c"
391 abort: ^C
399 abort: ^C
392 [255]
400 [255]
393
401
394 New operations are blocked with the correct state message
402 New operations are blocked with the correct state message
395
403
396 $ find .hg -name '*state' -prune | sort
404 $ find .hg -name '*state' -prune | sort
397 .hg/dirstate
405 .hg/dirstate
398 .hg/merge/state
406 .hg/merge/state
399 .hg/rebasestate
407 .hg/rebasestate
400 .hg/undo.backup.dirstate
408 .hg/undo.backup.dirstate
401 .hg/undo.dirstate
409 .hg/undo.dirstate
402 .hg/updatestate
410 .hg/updatestate
403
411
404 $ hg rebase -s 3 -d tip
412 $ hg rebase -s 3 -d tip
405 abort: rebase in progress
413 abort: rebase in progress
406 (use 'hg rebase --continue' or 'hg rebase --abort')
414 (use 'hg rebase --continue' or 'hg rebase --abort')
407 [255]
415 [255]
408 $ hg up .
416 $ hg up .
409 abort: rebase in progress
417 abort: rebase in progress
410 (use 'hg rebase --continue' or 'hg rebase --abort')
418 (use 'hg rebase --continue' or 'hg rebase --abort')
411 [255]
419 [255]
412 $ hg up -C .
420 $ hg up -C .
413 abort: rebase in progress
421 abort: rebase in progress
414 (use 'hg rebase --continue' or 'hg rebase --abort')
422 (use 'hg rebase --continue' or 'hg rebase --abort')
415 [255]
423 [255]
416
424
417 $ hg graft 3
425 $ hg graft 3
418 abort: rebase in progress
426 abort: rebase in progress
419 (use 'hg rebase --continue' or 'hg rebase --abort')
427 (use 'hg rebase --continue' or 'hg rebase --abort')
420 [255]
428 [255]
421
429
422 $ hg abort
430 $ hg abort
423 saved backup bundle to $TESTTMP/interrupted/.hg/strip-backup/3d8812cf300d-93041a90-backup.hg
431 saved backup bundle to $TESTTMP/interrupted/.hg/strip-backup/3d8812cf300d-93041a90-backup.hg
424 rebase aborted
432 rebase aborted
425 $ hg log -G --template "{rev} {desc} {bookmarks}"
433 $ hg log -G --template "{rev} {desc} {bookmarks}"
426 o 6 no-a
434 o 6 no-a
427 |
435 |
428 | o 5 d
436 | o 5 d
429 | |
437 | |
430 | o 4 c
438 | o 4 c
431 | |
439 | |
432 | o 3 b
440 | o 3 b
433 | |
441 | |
434 | o 2 1
442 | o 2 1
435 |/
443 |/
436 o 1 a
444 o 1 a
437 |
445 |
438 @ 0 base
446 @ 0 base
439
447
440 $ hg summary
448 $ hg summary
441 parent: 0:df4f53cec30a
449 parent: 0:df4f53cec30a
442 base
450 base
443 branch: default
451 branch: default
444 commit: (clean)
452 commit: (clean)
445 update: 6 new changesets (update)
453 update: 6 new changesets (update)
446 phases: 7 draft
454 phases: 7 draft
447
455
448 $ cd ..
456 $ cd ..
449 On the other hand, make sure we *do* clobber changes whenever we
457 On the other hand, make sure we *do* clobber changes whenever we
450 haven't somehow managed to update the repo to a different revision
458 haven't somehow managed to update the repo to a different revision
451 during a rebase (issue4661)
459 during a rebase (issue4661)
452
460
453 $ hg ini yesupdate
461 $ hg ini yesupdate
454 $ cd yesupdate
462 $ cd yesupdate
455 $ echo "initial data" > foo.txt
463 $ echo "initial data" > foo.txt
456 $ hg add
464 $ hg add
457 adding foo.txt
465 adding foo.txt
458 $ hg ci -m "initial checkin"
466 $ hg ci -m "initial checkin"
459 $ echo "change 1" > foo.txt
467 $ echo "change 1" > foo.txt
460 $ hg ci -m "change 1"
468 $ hg ci -m "change 1"
461 $ hg up 0
469 $ hg up 0
462 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
470 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
463 $ echo "conflicting change 1" > foo.txt
471 $ echo "conflicting change 1" > foo.txt
464 $ hg ci -m "conflicting 1"
472 $ hg ci -m "conflicting 1"
465 created new head
473 created new head
466 $ echo "conflicting change 2" > foo.txt
474 $ echo "conflicting change 2" > foo.txt
467 $ hg ci -m "conflicting 2"
475 $ hg ci -m "conflicting 2"
468
476
469 $ hg rebase -d 1 --tool 'internal:fail'
477 $ hg rebase -d 1 --tool 'internal:fail'
470 rebasing 2:e4ea5cdc9789 "conflicting 1"
478 rebasing 2:e4ea5cdc9789 "conflicting 1"
471 unresolved conflicts (see hg resolve, then hg rebase --continue)
479 unresolved conflicts (see hg resolve, then hg rebase --continue)
472 [1]
480 [1]
473 $ hg abort
481 $ hg abort
474 rebase aborted
482 rebase aborted
475 $ hg summary
483 $ hg summary
476 parent: 3:b16646383533 tip
484 parent: 3:b16646383533 tip
477 conflicting 2
485 conflicting 2
478 branch: default
486 branch: default
479 commit: (clean)
487 commit: (clean)
480 update: 1 new changesets, 2 branch heads (merge)
488 update: 1 new changesets, 2 branch heads (merge)
481 phases: 4 draft
489 phases: 4 draft
482 $ cd ..
490 $ cd ..
483
491
484 test aborting a rebase succeeds after rebasing with skipped commits onto a
492 test aborting a rebase succeeds after rebasing with skipped commits onto a
485 public changeset (issue4896)
493 public changeset (issue4896)
486
494
487 $ hg init succeedonpublic
495 $ hg init succeedonpublic
488 $ cd succeedonpublic
496 $ cd succeedonpublic
489 $ echo 'content' > root
497 $ echo 'content' > root
490 $ hg commit -A -m 'root' -q
498 $ hg commit -A -m 'root' -q
491
499
492 set up public branch
500 set up public branch
493 $ echo 'content' > disappear
501 $ echo 'content' > disappear
494 $ hg commit -A -m 'disappear public' -q
502 $ hg commit -A -m 'disappear public' -q
495 commit will cause merge conflict on rebase
503 commit will cause merge conflict on rebase
496 $ echo '' > root
504 $ echo '' > root
497 $ hg commit -m 'remove content public' -q
505 $ hg commit -m 'remove content public' -q
498 $ hg phase --public
506 $ hg phase --public
499
507
500 setup the draft branch that will be rebased onto public commit
508 setup the draft branch that will be rebased onto public commit
501 $ hg up -r 0 -q
509 $ hg up -r 0 -q
502 $ echo 'content' > disappear
510 $ echo 'content' > disappear
503 commit will disappear
511 commit will disappear
504 $ hg commit -A -m 'disappear draft' -q
512 $ hg commit -A -m 'disappear draft' -q
505 $ echo 'addedcontADDEDentadded' > root
513 $ echo 'addedcontADDEDentadded' > root
506 commit will cause merge conflict on rebase
514 commit will cause merge conflict on rebase
507 $ hg commit -m 'add content draft' -q
515 $ hg commit -m 'add content draft' -q
508
516
509 $ hg rebase -d 'public()' --tool :merge -q
517 $ hg rebase -d 'public()' --tool :merge -q
510 note: not rebasing 3:0682fd3dabf5 "disappear draft", its destination already has all its changes
518 note: not rebasing 3:0682fd3dabf5 "disappear draft", its destination already has all its changes
511 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
519 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
512 unresolved conflicts (see hg resolve, then hg rebase --continue)
520 unresolved conflicts (see hg resolve, then hg rebase --continue)
513 [1]
521 [1]
514 $ hg abort
522 $ hg abort
515 rebase aborted
523 rebase aborted
516 $ cd ..
524 $ cd ..
517
525
@@ -1,76 +1,89 b''
1 #testcases continuecommand continueflag
1 Test rebase --continue with rebasestate written by legacy client
2 Test rebase --continue with rebasestate written by legacy client
2
3
3 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
5 > [extensions]
5 > rebase=
6 > rebase=
6 > drawdag=$TESTDIR/drawdag.py
7 > drawdag=$TESTDIR/drawdag.py
7 > EOF
8 > EOF
8
9
10 #if continueflag
11 $ cat >> $HGRCPATH <<EOF
12 > [alias]
13 > continue = rebase --continue
14 > EOF
15 #endif
16
9 $ hg init
17 $ hg init
10 $ hg debugdrawdag <<'EOF'
18 $ hg debugdrawdag <<'EOF'
11 > D H
19 > D H
12 > | |
20 > | |
13 > C G
21 > C G
14 > | |
22 > | |
15 > B F
23 > B F
16 > | |
24 > | |
17 > Z A E
25 > Z A E
18 > \|/
26 > \|/
19 > R
27 > R
20 > EOF
28 > EOF
21
29
22 rebasestate generated by a legacy client running "hg rebase -r B+D+E+G+H -d Z"
30 rebasestate generated by a legacy client running "hg rebase -r B+D+E+G+H -d Z"
23
31
24 $ touch .hg/last-message.txt
32 $ touch .hg/last-message.txt
25 $ cat > .hg/rebasestate <<EOF
33 $ cat > .hg/rebasestate <<EOF
26 > 0000000000000000000000000000000000000000
34 > 0000000000000000000000000000000000000000
27 > f424eb6a8c01c4a0c0fba9f863f79b3eb5b4b69f
35 > f424eb6a8c01c4a0c0fba9f863f79b3eb5b4b69f
28 > 0000000000000000000000000000000000000000
36 > 0000000000000000000000000000000000000000
29 > 0
37 > 0
30 > 0
38 > 0
31 > 0
39 > 0
32 >
40 >
33 > 21a6c45028857f500f56ae84fbf40689c429305b:-2
41 > 21a6c45028857f500f56ae84fbf40689c429305b:-2
34 > de008c61a447fcfd93f808ef527d933a84048ce7:0000000000000000000000000000000000000000
42 > de008c61a447fcfd93f808ef527d933a84048ce7:0000000000000000000000000000000000000000
35 > c1e6b162678d07d0b204e5c8267d51b4e03b633c:0000000000000000000000000000000000000000
43 > c1e6b162678d07d0b204e5c8267d51b4e03b633c:0000000000000000000000000000000000000000
36 > aeba276fcb7df8e10153a07ee728d5540693f5aa:-3
44 > aeba276fcb7df8e10153a07ee728d5540693f5aa:-3
37 > bd5548558fcf354d37613005737a143871bf3723:-3
45 > bd5548558fcf354d37613005737a143871bf3723:-3
38 > d2fa1c02b2401b0e32867f26cce50818a4bd796a:0000000000000000000000000000000000000000
46 > d2fa1c02b2401b0e32867f26cce50818a4bd796a:0000000000000000000000000000000000000000
39 > 6f7a236de6852570cd54649ab62b1012bb78abc8:0000000000000000000000000000000000000000
47 > 6f7a236de6852570cd54649ab62b1012bb78abc8:0000000000000000000000000000000000000000
40 > 6582e6951a9c48c236f746f186378e36f59f4928:0000000000000000000000000000000000000000
48 > 6582e6951a9c48c236f746f186378e36f59f4928:0000000000000000000000000000000000000000
41 > EOF
49 > EOF
42
50
43 $ hg rebase --continue
51 #if continuecommand
52 $ hg continue --dry-run
53 rebase in progress, will be resumed
54 #endif
55
56 $ hg continue
44 rebasing 4:c1e6b162678d "B" (B)
57 rebasing 4:c1e6b162678d "B" (B)
45 rebasing 8:6f7a236de685 "D" (D)
58 rebasing 8:6f7a236de685 "D" (D)
46 rebasing 2:de008c61a447 "E" (E)
59 rebasing 2:de008c61a447 "E" (E)
47 rebasing 7:d2fa1c02b240 "G" (G)
60 rebasing 7:d2fa1c02b240 "G" (G)
48 rebasing 9:6582e6951a9c "H" (H tip)
61 rebasing 9:6582e6951a9c "H" (H tip)
49 warning: orphaned descendants detected, not stripping c1e6b162678d, de008c61a447
62 warning: orphaned descendants detected, not stripping c1e6b162678d, de008c61a447
50 saved backup bundle to $TESTTMP/.hg/strip-backup/6f7a236de685-9880a3dc-rebase.hg
63 saved backup bundle to $TESTTMP/.hg/strip-backup/6f7a236de685-9880a3dc-rebase.hg
51
64
52 $ hg log -G -T '{rev}:{node|short} {desc}\n'
65 $ hg log -G -T '{rev}:{node|short} {desc}\n'
53 o 11:721b8da0a708 H
66 o 11:721b8da0a708 H
54 |
67 |
55 o 10:9d65695ec3c2 G
68 o 10:9d65695ec3c2 G
56 |
69 |
57 o 9:21c8397a5d68 E
70 o 9:21c8397a5d68 E
58 |
71 |
59 | o 8:fc52970345e8 D
72 | o 8:fc52970345e8 D
60 | |
73 | |
61 | o 7:eac96551b107 B
74 | o 7:eac96551b107 B
62 |/
75 |/
63 | o 6:bd5548558fcf C
76 | o 6:bd5548558fcf C
64 | |
77 | |
65 | | o 5:aeba276fcb7d F
78 | | o 5:aeba276fcb7d F
66 | | |
79 | | |
67 | o | 4:c1e6b162678d B
80 | o | 4:c1e6b162678d B
68 | | |
81 | | |
69 o | | 3:f424eb6a8c01 Z
82 o | | 3:f424eb6a8c01 Z
70 | | |
83 | | |
71 +---o 2:de008c61a447 E
84 +---o 2:de008c61a447 E
72 | |
85 | |
73 | o 1:21a6c4502885 A
86 | o 1:21a6c4502885 A
74 |/
87 |/
75 o 0:b41ce7760717 R
88 o 0:b41ce7760717 R
76
89
@@ -1,189 +1,196 b''
1 #testcases continuecommand continueflag
1 This emulates the effects of an hg pull --rebase in which the remote repo
2 This emulates the effects of an hg pull --rebase in which the remote repo
2 already has one local mq patch
3 already has one local mq patch
3
4
4 $ cat >> $HGRCPATH <<EOF
5 $ cat >> $HGRCPATH <<EOF
5 > [extensions]
6 > [extensions]
6 > rebase=
7 > rebase=
7 > mq=
8 > mq=
8 >
9 >
9 > [phases]
10 > [phases]
10 > publish=False
11 > publish=False
11 >
12 >
12 > [alias]
13 > [alias]
13 > tglog = log -G --template "{rev}: {node|short} '{desc}' tags: {tags}\n"
14 > tglog = log -G --template "{rev}: {node|short} '{desc}' tags: {tags}\n"
14 > EOF
15 > EOF
15
16
17 #if continueflag
18 $ cat >> $HGRCPATH <<EOF
19 > [alias]
20 > continue = rebase --continue
21 > EOF
22 #endif
16
23
17 $ hg init a
24 $ hg init a
18 $ cd a
25 $ cd a
19 $ hg qinit -c
26 $ hg qinit -c
20
27
21 $ echo c1 > c1
28 $ echo c1 > c1
22 $ hg add c1
29 $ hg add c1
23 $ hg ci -m C1
30 $ hg ci -m C1
24
31
25 $ echo r1 > r1
32 $ echo r1 > r1
26 $ hg add r1
33 $ hg add r1
27 $ hg ci -m R1
34 $ hg ci -m R1
28
35
29 $ hg up -q 0
36 $ hg up -q 0
30
37
31 $ hg qnew p0.patch -d '1 0'
38 $ hg qnew p0.patch -d '1 0'
32 $ echo p0 > p0
39 $ echo p0 > p0
33 $ hg add p0
40 $ hg add p0
34 $ hg qref -m P0
41 $ hg qref -m P0
35
42
36 $ hg qnew p1.patch -d '2 0'
43 $ hg qnew p1.patch -d '2 0'
37 $ echo p1 > p1
44 $ echo p1 > p1
38 $ hg add p1
45 $ hg add p1
39 $ hg qref -m P1
46 $ hg qref -m P1
40
47
41 $ hg export qtip > p1.patch
48 $ hg export qtip > p1.patch
42
49
43 $ hg up -q -C 1
50 $ hg up -q -C 1
44
51
45 $ hg import p1.patch
52 $ hg import p1.patch
46 applying p1.patch
53 applying p1.patch
47
54
48 $ rm p1.patch
55 $ rm p1.patch
49
56
50 $ hg up -q -C qtip
57 $ hg up -q -C qtip
51
58
52 $ hg rebase -v
59 $ hg rebase -v
53 rebasing 2:13a46ce44f60 "P0" (p0.patch qbase)
60 rebasing 2:13a46ce44f60 "P0" (p0.patch qbase)
54 resolving manifests
61 resolving manifests
55 removing p0
62 removing p0
56 getting r1
63 getting r1
57 resolving manifests
64 resolving manifests
58 getting p0
65 getting p0
59 committing files:
66 committing files:
60 p0
67 p0
61 committing manifest
68 committing manifest
62 committing changelog
69 committing changelog
63 rebasing 3:148775c71080 "P1" (p1.patch qtip)
70 rebasing 3:148775c71080 "P1" (p1.patch qtip)
64 resolving manifests
71 resolving manifests
65 note: not rebasing 3:148775c71080 "P1" (p1.patch qtip), its destination already has all its changes
72 note: not rebasing 3:148775c71080 "P1" (p1.patch qtip), its destination already has all its changes
66 rebase merging completed
73 rebase merging completed
67 updating mq patch p0.patch to 5:9ecc820b1737
74 updating mq patch p0.patch to 5:9ecc820b1737
68 $TESTTMP/a/.hg/patches/p0.patch
75 $TESTTMP/a/.hg/patches/p0.patch
69 2 changesets found
76 2 changesets found
70 uncompressed size of bundle content:
77 uncompressed size of bundle content:
71 348 (changelog)
78 348 (changelog)
72 324 (manifests)
79 324 (manifests)
73 129 p0
80 129 p0
74 129 p1
81 129 p1
75 saved backup bundle to $TESTTMP/a/.hg/strip-backup/13a46ce44f60-5da6ecfb-rebase.hg
82 saved backup bundle to $TESTTMP/a/.hg/strip-backup/13a46ce44f60-5da6ecfb-rebase.hg
76 2 changesets found
83 2 changesets found
77 uncompressed size of bundle content:
84 uncompressed size of bundle content:
78 403 (changelog)
85 403 (changelog)
79 324 (manifests)
86 324 (manifests)
80 129 p0
87 129 p0
81 129 p1
88 129 p1
82 adding branch
89 adding branch
83 adding changesets
90 adding changesets
84 adding manifests
91 adding manifests
85 adding file changes
92 adding file changes
86 added 2 changesets with 2 changes to 2 files
93 added 2 changesets with 2 changes to 2 files
87 rebase completed
94 rebase completed
88 1 revisions have been skipped
95 1 revisions have been skipped
89
96
90 $ hg tglog
97 $ hg tglog
91 @ 3: 9ecc820b1737 'P0' tags: p0.patch qbase qtip tip
98 @ 3: 9ecc820b1737 'P0' tags: p0.patch qbase qtip tip
92 |
99 |
93 o 2: 869d8b134a27 'P1' tags: qparent
100 o 2: 869d8b134a27 'P1' tags: qparent
94 |
101 |
95 o 1: da108f2755df 'R1' tags:
102 o 1: da108f2755df 'R1' tags:
96 |
103 |
97 o 0: cd320d50b341 'C1' tags:
104 o 0: cd320d50b341 'C1' tags:
98
105
99 $ cd ..
106 $ cd ..
100
107
101
108
102 $ hg init b
109 $ hg init b
103 $ cd b
110 $ cd b
104 $ hg qinit -c
111 $ hg qinit -c
105
112
106 $ for i in r0 r1 r2 r3 r4 r5 r6;
113 $ for i in r0 r1 r2 r3 r4 r5 r6;
107 > do
114 > do
108 > echo $i > $i
115 > echo $i > $i
109 > hg ci -Am $i
116 > hg ci -Am $i
110 > done
117 > done
111 adding r0
118 adding r0
112 adding r1
119 adding r1
113 adding r2
120 adding r2
114 adding r3
121 adding r3
115 adding r4
122 adding r4
116 adding r5
123 adding r5
117 adding r6
124 adding r6
118
125
119 $ hg qimport -r 1:tip
126 $ hg qimport -r 1:tip
120
127
121 $ hg up -q 0
128 $ hg up -q 0
122
129
123 $ for i in r1 r3 r7 r8;
130 $ for i in r1 r3 r7 r8;
124 > do
131 > do
125 > echo $i > $i
132 > echo $i > $i
126 > hg ci -Am branch2-$i
133 > hg ci -Am branch2-$i
127 > done
134 > done
128 adding r1
135 adding r1
129 created new head
136 created new head
130 adding r3
137 adding r3
131 adding r7
138 adding r7
132 adding r8
139 adding r8
133
140
134 $ echo somethingelse > r4
141 $ echo somethingelse > r4
135 $ hg ci -Am branch2-r4
142 $ hg ci -Am branch2-r4
136 adding r4
143 adding r4
137
144
138 $ echo r6 > r6
145 $ echo r6 > r6
139 $ hg ci -Am branch2-r6
146 $ hg ci -Am branch2-r6
140 adding r6
147 adding r6
141
148
142 $ hg up -q qtip
149 $ hg up -q qtip
143
150
144 $ HGMERGE=internal:fail hg rebase
151 $ HGMERGE=internal:fail hg rebase
145 rebasing 1:b4bffa6e4776 "r1" (qbase r1)
152 rebasing 1:b4bffa6e4776 "r1" (qbase r1)
146 note: not rebasing 1:b4bffa6e4776 "r1" (qbase r1), its destination already has all its changes
153 note: not rebasing 1:b4bffa6e4776 "r1" (qbase r1), its destination already has all its changes
147 rebasing 2:c0fd129beb01 "r2" (r2)
154 rebasing 2:c0fd129beb01 "r2" (r2)
148 rebasing 3:6ff5b8feed8e "r3" (r3)
155 rebasing 3:6ff5b8feed8e "r3" (r3)
149 note: not rebasing 3:6ff5b8feed8e "r3" (r3), its destination already has all its changes
156 note: not rebasing 3:6ff5b8feed8e "r3" (r3), its destination already has all its changes
150 rebasing 4:094320fec554 "r4" (r4)
157 rebasing 4:094320fec554 "r4" (r4)
151 unresolved conflicts (see hg resolve, then hg rebase --continue)
158 unresolved conflicts (see hg resolve, then hg rebase --continue)
152 [1]
159 [1]
153
160
154 $ HGMERGE=internal:local hg resolve --all
161 $ HGMERGE=internal:local hg resolve --all
155 (no more unresolved files)
162 (no more unresolved files)
156 continue: hg rebase --continue
163 continue: hg rebase --continue
157
164
158 $ hg rebase --continue
165 $ hg continue
159 already rebased 1:b4bffa6e4776 "r1" (qbase r1) as 057f55ff8f44
166 already rebased 1:b4bffa6e4776 "r1" (qbase r1) as 057f55ff8f44
160 already rebased 2:c0fd129beb01 "r2" (r2) as 1660ab13ce9a
167 already rebased 2:c0fd129beb01 "r2" (r2) as 1660ab13ce9a
161 already rebased 3:6ff5b8feed8e "r3" (r3) as 1660ab13ce9a
168 already rebased 3:6ff5b8feed8e "r3" (r3) as 1660ab13ce9a
162 rebasing 4:094320fec554 "r4" (r4)
169 rebasing 4:094320fec554 "r4" (r4)
163 note: not rebasing 4:094320fec554 "r4" (r4), its destination already has all its changes
170 note: not rebasing 4:094320fec554 "r4" (r4), its destination already has all its changes
164 rebasing 5:681a378595ba "r5" (r5)
171 rebasing 5:681a378595ba "r5" (r5)
165 rebasing 6:512a1f24768b "r6" (qtip r6)
172 rebasing 6:512a1f24768b "r6" (qtip r6)
166 note: not rebasing 6:512a1f24768b "r6" (qtip r6), its destination already has all its changes
173 note: not rebasing 6:512a1f24768b "r6" (qtip r6), its destination already has all its changes
167 saved backup bundle to $TESTTMP/b/.hg/strip-backup/b4bffa6e4776-b9bfb84d-rebase.hg
174 saved backup bundle to $TESTTMP/b/.hg/strip-backup/b4bffa6e4776-b9bfb84d-rebase.hg
168
175
169 $ hg tglog
176 $ hg tglog
170 @ 8: 0b9735ce8f0a 'r5' tags: qtip r5 tip
177 @ 8: 0b9735ce8f0a 'r5' tags: qtip r5 tip
171 |
178 |
172 o 7: 1660ab13ce9a 'r2' tags: qbase r2
179 o 7: 1660ab13ce9a 'r2' tags: qbase r2
173 |
180 |
174 o 6: 057f55ff8f44 'branch2-r6' tags: qparent
181 o 6: 057f55ff8f44 'branch2-r6' tags: qparent
175 |
182 |
176 o 5: 1d7287f8deb1 'branch2-r4' tags:
183 o 5: 1d7287f8deb1 'branch2-r4' tags:
177 |
184 |
178 o 4: 3c10b9db2bd5 'branch2-r8' tags:
185 o 4: 3c10b9db2bd5 'branch2-r8' tags:
179 |
186 |
180 o 3: b684023158dc 'branch2-r7' tags:
187 o 3: b684023158dc 'branch2-r7' tags:
181 |
188 |
182 o 2: d817754b1251 'branch2-r3' tags:
189 o 2: d817754b1251 'branch2-r3' tags:
183 |
190 |
184 o 1: 0621a206f8a4 'branch2-r1' tags:
191 o 1: 0621a206f8a4 'branch2-r1' tags:
185 |
192 |
186 o 0: 222799e2f90b 'r0' tags:
193 o 0: 222799e2f90b 'r0' tags:
187
194
188
195
189 $ cd ..
196 $ cd ..
@@ -1,196 +1,204 b''
1 #testcases continuecommand continueflag
1 Rebasing using a single transaction
2 Rebasing using a single transaction
2
3
3 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
5 > [extensions]
5 > rebase=
6 > rebase=
6 > drawdag=$TESTDIR/drawdag.py
7 > drawdag=$TESTDIR/drawdag.py
7 >
8 >
8 > [rebase]
9 > [rebase]
9 > singletransaction=True
10 > singletransaction=True
10 >
11 >
11 > [phases]
12 > [phases]
12 > publish=False
13 > publish=False
13 >
14 >
14 > [alias]
15 > [alias]
15 > tglog = log -G --template "{rev}: {desc}"
16 > tglog = log -G --template "{rev}: {desc}"
16 > EOF
17 > EOF
17
18
19 #if continueflag
20 $ cat >> $HGRCPATH <<EOF
21 > [alias]
22 > continue = rebase --continue
23 > EOF
24 #endif
25
18 Check that a simple rebase works
26 Check that a simple rebase works
19
27
20 $ hg init simple && cd simple
28 $ hg init simple && cd simple
21 $ hg debugdrawdag <<'EOF'
29 $ hg debugdrawdag <<'EOF'
22 > Z
30 > Z
23 > |
31 > |
24 > | D
32 > | D
25 > | |
33 > | |
26 > | C
34 > | C
27 > | |
35 > | |
28 > Y B
36 > Y B
29 > |/
37 > |/
30 > A
38 > A
31 > EOF
39 > EOF
32 - We should only see one status stored message. It comes from the start.
40 - We should only see one status stored message. It comes from the start.
33 $ hg rebase --debug -b D -d Z | grep 'status stored'
41 $ hg rebase --debug -b D -d Z | grep 'status stored'
34 rebase status stored
42 rebase status stored
35 $ hg tglog
43 $ hg tglog
36 o 5: D
44 o 5: D
37 |
45 |
38 o 4: C
46 o 4: C
39 |
47 |
40 o 3: B
48 o 3: B
41 |
49 |
42 o 2: Z
50 o 2: Z
43 |
51 |
44 o 1: Y
52 o 1: Y
45 |
53 |
46 o 0: A
54 o 0: A
47
55
48 $ cd ..
56 $ cd ..
49
57
50 Check that --collapse works
58 Check that --collapse works
51
59
52 $ hg init collapse && cd collapse
60 $ hg init collapse && cd collapse
53 $ hg debugdrawdag <<'EOF'
61 $ hg debugdrawdag <<'EOF'
54 > Z
62 > Z
55 > |
63 > |
56 > | D
64 > | D
57 > | |
65 > | |
58 > | C
66 > | C
59 > | |
67 > | |
60 > Y B
68 > Y B
61 > |/
69 > |/
62 > A
70 > A
63 > EOF
71 > EOF
64 - We should only see two status stored messages. One from the start, one from
72 - We should only see two status stored messages. One from the start, one from
65 - cmdutil.commitforceeditor() which forces tr.writepending()
73 - cmdutil.commitforceeditor() which forces tr.writepending()
66 $ hg rebase --collapse --debug -b D -d Z | grep 'status stored'
74 $ hg rebase --collapse --debug -b D -d Z | grep 'status stored'
67 rebase status stored
75 rebase status stored
68 rebase status stored
76 rebase status stored
69 $ hg tglog
77 $ hg tglog
70 o 3: Collapsed revision
78 o 3: Collapsed revision
71 | * B
79 | * B
72 | * C
80 | * C
73 | * D
81 | * D
74 o 2: Z
82 o 2: Z
75 |
83 |
76 o 1: Y
84 o 1: Y
77 |
85 |
78 o 0: A
86 o 0: A
79
87
80 $ cd ..
88 $ cd ..
81
89
82 With --collapse, check that conflicts can be resolved and rebase can then be
90 With --collapse, check that conflicts can be resolved and rebase can then be
83 continued
91 continued
84
92
85 $ hg init collapse-conflict && cd collapse-conflict
93 $ hg init collapse-conflict && cd collapse-conflict
86 $ hg debugdrawdag <<'EOF'
94 $ hg debugdrawdag <<'EOF'
87 > Z # Z/conflict=Z
95 > Z # Z/conflict=Z
88 > |
96 > |
89 > | D
97 > | D
90 > | |
98 > | |
91 > | C # C/conflict=C
99 > | C # C/conflict=C
92 > | |
100 > | |
93 > Y B
101 > Y B
94 > |/
102 > |/
95 > A
103 > A
96 > EOF
104 > EOF
97 $ hg rebase --collapse -b D -d Z
105 $ hg rebase --collapse -b D -d Z
98 rebasing 1:112478962961 "B" (B)
106 rebasing 1:112478962961 "B" (B)
99 rebasing 3:c26739dbe603 "C" (C)
107 rebasing 3:c26739dbe603 "C" (C)
100 merging conflict
108 merging conflict
101 warning: conflicts while merging conflict! (edit, then use 'hg resolve --mark')
109 warning: conflicts while merging conflict! (edit, then use 'hg resolve --mark')
102 unresolved conflicts (see hg resolve, then hg rebase --continue)
110 unresolved conflicts (see hg resolve, then hg rebase --continue)
103 [1]
111 [1]
104 $ hg tglog
112 $ hg tglog
105 o 5: D
113 o 5: D
106 |
114 |
107 | @ 4: Z
115 | @ 4: Z
108 | |
116 | |
109 @ | 3: C
117 @ | 3: C
110 | |
118 | |
111 | o 2: Y
119 | o 2: Y
112 | |
120 | |
113 o | 1: B
121 o | 1: B
114 |/
122 |/
115 o 0: A
123 o 0: A
116
124
117 $ hg st
125 $ hg st
118 M C
126 M C
119 M conflict
127 M conflict
120 A B
128 A B
121 ? conflict.orig
129 ? conflict.orig
122 $ echo resolved > conflict
130 $ echo resolved > conflict
123 $ hg resolve -m
131 $ hg resolve -m
124 (no more unresolved files)
132 (no more unresolved files)
125 continue: hg rebase --continue
133 continue: hg rebase --continue
126 $ hg rebase --continue
134 $ hg continue
127 already rebased 1:112478962961 "B" (B) as 79bc8f4973ce
135 already rebased 1:112478962961 "B" (B) as 79bc8f4973ce
128 rebasing 3:c26739dbe603 "C" (C)
136 rebasing 3:c26739dbe603 "C" (C)
129 rebasing 5:d24bb333861c "D" (D tip)
137 rebasing 5:d24bb333861c "D" (D tip)
130 saved backup bundle to $TESTTMP/collapse-conflict/.hg/strip-backup/112478962961-b5b34645-rebase.hg
138 saved backup bundle to $TESTTMP/collapse-conflict/.hg/strip-backup/112478962961-b5b34645-rebase.hg
131 $ hg tglog
139 $ hg tglog
132 o 3: Collapsed revision
140 o 3: Collapsed revision
133 | * B
141 | * B
134 | * C
142 | * C
135 | * D
143 | * D
136 o 2: Z
144 o 2: Z
137 |
145 |
138 o 1: Y
146 o 1: Y
139 |
147 |
140 o 0: A
148 o 0: A
141
149
142 $ cd ..
150 $ cd ..
143
151
144 With --collapse, check that the commit message editing can be canceled and
152 With --collapse, check that the commit message editing can be canceled and
145 rebase can then be continued
153 rebase can then be continued
146
154
147 $ hg init collapse-cancel-editor && cd collapse-cancel-editor
155 $ hg init collapse-cancel-editor && cd collapse-cancel-editor
148 $ hg debugdrawdag <<'EOF'
156 $ hg debugdrawdag <<'EOF'
149 > Z
157 > Z
150 > |
158 > |
151 > | D
159 > | D
152 > | |
160 > | |
153 > | C
161 > | C
154 > | |
162 > | |
155 > Y B
163 > Y B
156 > |/
164 > |/
157 > A
165 > A
158 > EOF
166 > EOF
159 $ HGEDITOR=false hg --config ui.interactive=1 rebase --collapse -b D -d Z
167 $ HGEDITOR=false hg --config ui.interactive=1 rebase --collapse -b D -d Z
160 rebasing 1:112478962961 "B" (B)
168 rebasing 1:112478962961 "B" (B)
161 rebasing 3:26805aba1e60 "C" (C)
169 rebasing 3:26805aba1e60 "C" (C)
162 rebasing 5:f585351a92f8 "D" (D tip)
170 rebasing 5:f585351a92f8 "D" (D tip)
163 transaction abort!
171 transaction abort!
164 rollback completed
172 rollback completed
165 abort: edit failed: false exited with status 1
173 abort: edit failed: false exited with status 1
166 [255]
174 [255]
167 $ hg tglog
175 $ hg tglog
168 o 5: D
176 o 5: D
169 |
177 |
170 | o 4: Z
178 | o 4: Z
171 | |
179 | |
172 o | 3: C
180 o | 3: C
173 | |
181 | |
174 | o 2: Y
182 | o 2: Y
175 | |
183 | |
176 o | 1: B
184 o | 1: B
177 |/
185 |/
178 o 0: A
186 o 0: A
179
187
180 $ hg rebase --continue
188 $ hg continue
181 rebasing 1:112478962961 "B" (B)
189 rebasing 1:112478962961 "B" (B)
182 rebasing 3:26805aba1e60 "C" (C)
190 rebasing 3:26805aba1e60 "C" (C)
183 rebasing 5:f585351a92f8 "D" (D tip)
191 rebasing 5:f585351a92f8 "D" (D tip)
184 saved backup bundle to $TESTTMP/collapse-cancel-editor/.hg/strip-backup/112478962961-cb2a9b47-rebase.hg
192 saved backup bundle to $TESTTMP/collapse-cancel-editor/.hg/strip-backup/112478962961-cb2a9b47-rebase.hg
185 $ hg tglog
193 $ hg tglog
186 o 3: Collapsed revision
194 o 3: Collapsed revision
187 | * B
195 | * B
188 | * C
196 | * C
189 | * D
197 | * D
190 o 2: Z
198 o 2: Z
191 |
199 |
192 o 1: Y
200 o 1: Y
193 |
201 |
194 o 0: A
202 o 0: A
195
203
196 $ cd ..
204 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now