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