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