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