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