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