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