rewind: add a message about obsolete changeset
Not the best output, but useful to have the data. We can improve that later.
from __future__ import absolute_import
import collections
import hashlib
from mercurial import (
error,
obsolete,
obsutil,
scmutil,
)
from mercurial.i18n import _
from . import (
exthelper,
rewriteutil,
compat,
)
eh = exthelper.exthelper()
# flag in obsolescence markers to link to identical version
identicalflag = 4
@eh.command(
'^rewind',
[('', 'to', [], _("rewind to these revision")),
('', 'as-divergence', None, _("preserve current latest successors")),
],
_(''))
def rewind(ui, repo, **opts):
"""rewind stacks of changeset to a previous content
This command can be used to restore stacks of changesets to an obsolete
state, creating identical identical copies.
The latest successors the obsolete changesets will be superseed by these
new copies. This behavior can be disabled using `--as-divergence`, the
current latest successors won't be affected and content-divergence will
appears between them and the restored version of the obsolete changesets.
"""
unfi = repo.unfiltered()
if not opts.get('to'):
raise error.Abort('no revision to rewind to')
rewinded = scmutil.revrange(repo, opts.get('to'))
successorsmap = collections.defaultdict(set)
rewindmap = {}
sscache = {}
with repo.wlock(), repo.lock():
if not opts['as_divergence']:
for rev in rewinded:
ctx = unfi[rev]
ssets = obsutil.successorssets(repo, ctx.node(), sscache)
if 1 < len(ssets):
msg = _('rewind confused by divergence on %s') % ctx
hint = _('solve divergence first or use "--as-divergence"')
raise error.Abort(msg, hint=hint)
if ssets and ssets[0]:
for succ in ssets[0]:
successorsmap[succ].add(ctx.node())
# Check that we can rewind these changesets
with repo.transaction('rewind'):
for rev in rewinded:
ctx = unfi[rev]
rewindmap[ctx.node()] = _revive_revision(unfi, rev)
relationships = []
cl = unfi.changelog
for (source, dest) in sorted(successorsmap.items()):
newdest = [rewindmap[d] for d in sorted(dest, key=cl.rev)]
rel = (unfi[source], tuple(unfi[d] for d in newdest))
relationships.append(rel)
obsolete.createmarkers(unfi, relationships, operation='rewind')
repo.ui.status(_('rewinded to %d changesets\n') % len(rewinded))
if relationships:
repo.ui.status(_('(%d changesets obsoleted)\n') % len(relationships))
def _revive_revision(unfi, rev):
"""rewind a single revision rev.
"""
ctx = unfi[rev]
extra = ctx.extra().copy()
# rewind hash should be unique over multiple rewind.
user = unfi.ui.config('devel', 'user.obsmarker')
if not user:
user = unfi.ui.username()
date = unfi.ui.configdate('devel', 'default-date')
if date is None:
date = compat.makedate()
noise = "%s\0%s\0%d\0%d" % (ctx.node(), user, date[0], date[1])
extra['__rewind-hash__'] = hashlib.sha256(noise).hexdigest()
p1 = ctx.p1().node()
p2 = ctx.p2().node()
extradict = {'extra': extra}
new, unusedvariable = rewriteutil.rewrite(unfi, ctx, [], ctx,
[p1, p2],
commitopts=extradict)
obsolete.createmarkers(unfi, [(ctx, (unfi[new],))],
flag=identicalflag, operation='rewind')
return new