Commit 39c8585a authored by Boris Feld's avatar Boris Feld
Browse files

WIP on handling stacks

parent 174d966e1eaa
from __future__ import absolute_import
import random
from mercurial import (
error,
hg,
lock as lockmod,
obsolete,
rewriteutil,
scmutil,
)
from mercurial.i18n import _
from . import (
exthelper,
rewriteutil,
)
eh = exthelper.exthelper()
@eh.command(
'^rewind',
[('r', 'rev', [], _("revision to rewind")),
],
_('[-r] [REV]'))
def rewind(ui, repo, *rev, **kwargs):
"""rewind let you restore a topological head closest predecessor. It main
use-case is cancel the latest history rewriting operation, for example
amend. It cannot handle rebased stacks yet.
"""
unfilteredrepo = repo.unfiltered()
currentwdirparent = repo['.']
revs = scmutil.revrange(repo, kwargs.get('rev', []))
# Check that we can rewind these changesets
rewriteutil.precheck(repo, revs, action="rewind")
# Check that we are not rebasing non-heads for the moment
ndrevs = list(repo.revs('descendants(%ld)', revs))
if len(ndrevs) != len(revs):
raise error.Abort('rewind only support rewinding heads')
# Warning if we will leave bookmarks behind
notbookmarked = list(repo.revs('%ld - bookmark()', revs))
if len(notbookmarked) != len(revs):
for rev in revs:
ctx = repo[rev]
if ctx.bookmarks():
for bookmark in ctx.bookmarks():
ui.warn("Bookmark %r will be left behind\n" % bookmark)
revisionstorewind = _getrewindrevisions(revs, unfilteredrepo)
newrevs = {}
with repo.wlock(), repo.lock(), repo.transaction('rewind'):
for base, revision in revisionstorewind:
newrev = _rewindrevision(base, revision, unfilteredrepo)
newrevs[base] = newrev
# Finally update to the new changeset if we rewinded the current wdir
# parent
if currentwdirparent in newrevs:
hg.updaterepo(repo, newrevs[currentwdirparent], False)
def _getrewindrevisions(revs, repo):
"""returns the list of revisions ordered in a way that each revision
parents is either stable or is present before in the returned list
"""
# We only support a single base revision for the moment
if len(revs) != 1:
raise error.Abort('rewind only support rewinding a single head')
base = list(revs)[0]
base = repo[base]
# Get the predecessor
predecessors = repo.obsstore.predecessors
preds = predecessors.get(base.node(), ())
if len(preds) == 0:
# Changeset has no predecessor, abort
raise error.Abort('revision %s has no predecessor' % base)
elif len(preds) > 1:
raise error.Abort('revision %s has too many predecessors' % base)
# Only support simple evolution flow for the moment
assert len(preds) == 1
# Take the predecessor as the start
nodes = [list(preds)[0][0]]
seen = set()
rewindrevisions = []
while nodes:
node = nodes[-1]
ctx = repo[node]
stable = True
# Check the parents
p1 = ctx.p1()
if p1 and p1.obsolete():
stable = False
nodes.append(p1)
p2 = ctx.p2()
if p2 and p2.obsolete():
stable = False
nodes.append(p2)
if stable is False:
continue
# If both parents are stable or in the list, add it to the plan
rewindrevisions.append(ctx)
nodes.pop()
return [(base, single_predecessor)]
def _rewindrevision(base, ctx, repo):
"""rewind a single revision named ctx. Base is the revision to mark as
obsolete
"""
extra = ctx.extra().copy()
extra['__rewind-base__'] = base.node()
p1 = ctx.p1().node()
p1ctx = repo[p1]
p2 = ctx.p2().node()
p2ctx = repo[p2]
# Check that we are not creating orphans
if p1ctx.obsolete() or p1ctx.isunstable() or p2ctx.obsolete() or p2ctx.isunstable():
raise error.Abort("rewind don't works for rebased changesets yet")
extradict = {'extra': extra}
new, unusedvariable = rewriteutil.rewrite(repo, ctx, [], ctx,
[p1, p2],
commitopts=extradict)
# And create a obsmarker between the new node and the base
obsolete.createmarkers(repo, [(base, (repo[new],))])
return new
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment