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