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