add a 'inhibit' extension
This extension will be used to reduce the scope of evolve in order to make some
of its features available to the mass earlier.
See extension documentation for details.
"""Reduce the changesets evolution feature scope for early and noob friendly UI
The full scale changeset evolution have some massive bleeding edge and it is
very easy for people not very intimate with the concept to end up in intricate
situation. In order to get some of the benefit sooner, this extension is
disabling some of the less polished aspect of evolution. It should gradually
get thinner and thinner as changeset evolution will get more polished. This
extension is only recommended for large scale organisations. Individual user
should probably stick on using Evolution in its current state, understand its
concept and provide feedback
The first feature provided by this extension is the ability to "inhibit"
obsolescence markers. Obsolete revision can be cheaply brought back to life
that way. However as the inhibitor are not fitting in an append only model,
this is incompatible with sharing mutable history.
"""
from mercurial import localrepo
from mercurial import obsolete
from mercurial import extensions
from mercurial import cmdutil
from mercurial import scmutil
cmdtable = {}
command = cmdutil.command(cmdtable)
def reposetup(ui, repo):
class obsinhibitedrepo(repo.__class__):
@localrepo.storecache('obsinhibit')
def _obsinhibit(self):
# XXX we should make sure it is invalidated by transaction failure
obsinhibit = set()
raw = self.sopener.tryread('obsinhibit')
for i in xrange(0, len(raw), 20):
obsinhibit.add(raw[i:i+20])
return obsinhibit
repo.__class__ = obsinhibitedrepo
# obsolescence inhibitor
########################
def _schedulewrite(tr, obsinhibit):
"""Make sure on disk content will be updated on transaction commit"""
def writer(fp):
"""Serialize the inhibited list to disk.
"""
raw = ''.join(obsinhibit)
fp.write(raw)
tr.addfilegenerator('obsinhibit', ('obsinhibit',), writer)
tr.hookargs['obs_inbihited'] = '1'
def _inhibitmarkers(repo, nodes):
"""add marker inhibitor for all obsolete revision under <nodes>
Content of <nodes> and all mutable ancestors are considered. Marker for
obsolete revision only are created.
"""
newinhibit = repo.set('::%ln and obsolete()', nodes)
if newinhibit:
tr = repo.transaction('obsinhibit')
try:
repo._obsinhibit.update(c.node() for c in newinhibit)
_schedulewrite(tr, repo._obsinhibit)
repo.invalidatevolatilesets()
tr.close()
finally:
tr.release()
def _deinhibitmarkers(repo, nodes):
"""lift obsolescence inhibition on a set of nodes
This will be triggered when inhibited nodes received new obsolescence
markers. Otherwise the new obsolescence markers would also be inhibited.
"""
deinhibited = repo._obsinhibit & set(nodes)
if deinhibited:
tr = repo.transaction('obsinhibit')
try:
repo._obsinhibit -= deinhibited
_schedulewrite(tr, repo._obsinhibit)
repo.invalidatevolatilesets()
tr.close()
finally:
tr.release()
def _createmarkers(orig, repo, relations, flag=0, date=None, metadata=None):
"""wrap markers create to make sure we de-inhibit target nodes"""
# wrapping transactio to unify the one in each function
tr = repo.transaction('add-obsolescence-marker')
try:
orig(repo, relations, flag, date, metadata)
precs = (r[0].node() for r in relations)
_deinhibitmarkers(repo, precs)
tr.close()
finally:
tr.release()
def extsetup(ui):
# lets wrap the computation of the obsolete set
# We apply inhibition there
obsfunc = obsolete.cachefuncs['obsolete']
def _computeobsoleteset(repo):
"""remove any inhibited nodes from the obsolete set
This will trickle down to other part of mercurial (hidden, log, etc)"""
obs = obsfunc(repo)
getrev = repo.changelog.nodemap.get
for n in repo._obsinhibit:
obs.discard(getrev(n))
return obs
obsolete.cachefuncs['obsolete'] = _computeobsoleteset
# drop divergence computation since it is incompatible with "light revive"
obsolete.cachefuncs['divergent'] = lambda repo: set()
# drop bumped computation since it is incompatible with "light revive"
obsolete.cachefuncs['bumped'] = lambda repo: set()
# wrap create marker to make it able to lift the inhibition
extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers)
@command('debugobsinhibit', [], '')
def cmddebugobsinhibit(ui, repo, *revs):
"""inhibit obsolescence markers effect on a set of revs"""
nodes = (repo[r].node() for r in scmutil.revrange(repo, revs))
_inhibitmarkers(repo, nodes)