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