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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/inhibit.py Fri Mar 06 21:25:44 2015 -0800
@@ -0,0 +1,125 @@
+"""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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-inhibit.t Fri Mar 06 21:25:44 2015 -0800
@@ -0,0 +1,129 @@
+ $ cat >> $HGRCPATH <<EOF
+ > [ui]
+ > logtemplate = {rev}:{node|short} {desc}\n
+ > [extensions]
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+ $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH
+ $ mkcommit() {
+ > echo "$1" > "$1"
+ > hg add "$1"
+ > hg ci -m "add $1"
+ > }
+
+ $ hg init inhibit
+ $ cd inhibit
+ $ mkcommit cA
+ $ mkcommit cB
+ $ mkcommit cC
+ $ mkcommit cD
+ $ hg up 'desc(cA)'
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit cE
+ created new head
+ $ mkcommit cG
+ $ mkcommit cH
+ $ mkcommit cJ
+ $ hg log -G
+ @ 7:18214586bf78 add cJ
+ |
+ o 6:cf5c4f4554ce add cH
+ |
+ o 5:5419eb264a33 add cG
+ |
+ o 4:98065434e5c6 add cE
+ |
+ | o 3:2db36d8066ff add cD
+ | |
+ | o 2:7df62a38b9bf add cC
+ | |
+ | o 1:02bcbc3f6e56 add cB
+ |/
+ o 0:54ccbc537fc2 add cA
+
+
+plain prune
+
+ $ hg prune 1::
+ 3 changesets pruned
+ $ hg log -G
+ @ 7:18214586bf78 add cJ
+ |
+ o 6:cf5c4f4554ce add cH
+ |
+ o 5:5419eb264a33 add cG
+ |
+ o 4:98065434e5c6 add cE
+ |
+ o 0:54ccbc537fc2 add cA
+
+ $ hg debugobsinhibit --hidden 1::
+ $ hg log -G
+ @ 7:18214586bf78 add cJ
+ |
+ o 6:cf5c4f4554ce add cH
+ |
+ o 5:5419eb264a33 add cG
+ |
+ o 4:98065434e5c6 add cE
+ |
+ | o 3:2db36d8066ff add cD
+ | |
+ | o 2:7df62a38b9bf add cC
+ | |
+ | o 1:02bcbc3f6e56 add cB
+ |/
+ o 0:54ccbc537fc2 add cA
+
+ $ hg prune --hidden 1::
+ 3 changesets pruned
+ $ hg log -G
+ @ 7:18214586bf78 add cJ
+ |
+ o 6:cf5c4f4554ce add cH
+ |
+ o 5:5419eb264a33 add cG
+ |
+ o 4:98065434e5c6 add cE
+ |
+ o 0:54ccbc537fc2 add cA
+
+
+after amend
+
+ $ echo babar > cJ
+ $ hg amend
+ $ hg log -G
+ @ 9:55c73a90e4b4 add cJ
+ |
+ o 6:cf5c4f4554ce add cH
+ |
+ o 5:5419eb264a33 add cG
+ |
+ o 4:98065434e5c6 add cE
+ |
+ o 0:54ccbc537fc2 add cA
+
+ $ hg debugobsinhibit --hidden 18214586bf78
+ $ hg log -G
+ @ 9:55c73a90e4b4 add cJ
+ |
+ | o 7:18214586bf78 add cJ
+ |/
+ o 6:cf5c4f4554ce add cH
+ |
+ o 5:5419eb264a33 add cG
+ |
+ o 4:98065434e5c6 add cE
+ |
+ o 0:54ccbc537fc2 add cA
+
+
+and no divergence
+
+ $ hg summary
+ parent: 9:55c73a90e4b4 tip
+ add cJ
+ branch: default
+ commit: (clean)
+ update: 1 new changesets, 2 branch heads (merge)