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