inhibit: create direct access extension
authorLaurent Charignon <lcharignon@fb.com>
Thu, 14 May 2015 11:23:40 -0700
changeset 1339 0e2eb196923a
parent 1338 77cbf9121e8a
child 1341 e8e3dbddc198
inhibit: create direct access extension Since we want to use direct access without necessarily using inhibit, this patch separates both extensions. Inhibit depends on direct access and we add a test to check that it complains if that is not the case.
hgext/directaccess.py
hgext/inhibit.py
tests/test-inhibit.t
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/directaccess.py	Thu May 14 11:23:40 2015 -0700
@@ -0,0 +1,117 @@
+""" This extension provides direct access
+It is the ability to refer and access hidden sha in commands provided that you 
+know their value.
+For example hg log -r xxx where xxx is a commit has should work whether xxx is
+hidden or not as we assume that the user knows what he is doing when referring
+to xxx.
+"""
+from mercurial import extensions
+from mercurial import cmdutil
+from mercurial import repoview
+from mercurial import branchmap
+from mercurial import revset
+from mercurial import error
+from mercurial import commands
+from mercurial.i18n import _
+
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
+# List of commands where no warning is shown for direct access
+directaccesslevel = [
+    # warning or not, extension (None if core), command name
+    (False, None, 'update'), 
+    (False, None, 'export'),
+    (True,  'rebase', 'rebase'),
+    (False,  'evolve', 'prune'),
+]
+
+def reposetup(ui, repo):
+    repo._explicitaccess = set()
+
+def _computehidden(repo):
+    hidden = repoview.computehidden(repo)
+    cl = repo.changelog
+    dynamic = hidden & repo._explicitaccess
+    if dynamic:
+        blocked = cl.ancestors(dynamic, inclusive=True)
+        hidden = frozenset(r for r in hidden if r not in blocked)
+    return hidden
+
+def setupdirectaccess():
+    """ Add two new filtername that behave like visible to provide direct access
+    and direct access with warning. Wraps the commands to setup direct access """
+    repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden})
+    repoview.filtertable.update({'visible-directaccess-warn': _computehidden})
+    branchmap.subsettable['visible-directaccess-nowarn'] = 'visible'
+    branchmap.subsettable['visible-directaccess-warn'] = 'visible'
+
+    for warn, ext, cmd in directaccesslevel:
+        cmdtable = extensions.find(ext).cmdtable if ext else commands.table
+        wrapper = wrapwithwarning if warn else wrapwithoutwarning
+        try:
+            extensions.wrapcommand(cmdtable, cmd, wrapper)
+        except error.UnknownCommand:
+            pass
+
+def wrapwithoutwarning(orig, ui, repo, *args, **kwargs):
+    if repo and repo.filtername == 'visible':
+        repo = repo.filtered("visible-directaccess-nowarn")
+    return orig(ui, repo, *args, **kwargs)
+
+def wrapwithwarning(orig, ui, repo, *args, **kwargs):
+    if repo and repo.filtername == 'visible':
+        repo = repo.filtered("visible-directaccess-warn")
+    return orig(ui, repo, *args, **kwargs)
+
+def extsetup(ui):
+    extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook)
+    setupdirectaccess()
+
+def gethashsymbols(tree):
+    # Returns the list of symbols of the tree that look like hashes
+    # for example for the revset 3::abe3ff it will return ('abe3ff')
+    if not tree:
+        return []
+
+    if len(tree) == 2 and tree[0] == "symbol":
+        try:
+            int(tree[1])
+            return []
+        except ValueError as e:
+            return [tree[1]]
+    elif len(tree) == 3:
+        return gethashsymbols(tree[1]) + gethashsymbols(tree[2])
+    else:
+        return []
+
+def _posttreebuilthook(orig, tree, repo):
+    # This is use to enabled direct hash access
+    # We extract the symbols that look like hashes and add them to the
+    # explicitaccess set
+    orig(tree, repo)
+    filternm = ""
+    if repo is not None:
+        filternm = repo.filtername
+    if filternm is not None and filternm.startswith('visible-directaccess'):
+        prelength = len(repo._explicitaccess)
+        accessbefore = set(repo._explicitaccess)
+        repo.symbols = gethashsymbols(tree)
+        cl = repo.unfiltered().changelog
+        for node in repo.symbols:
+            try:
+                node = cl._partialmatch(node)
+            except error.LookupError:
+                node = None
+            if node is not None:
+                rev = cl.rev(node)
+                if rev not in repo.changelog:
+                    repo._explicitaccess.add(rev)
+        if prelength != len(repo._explicitaccess):
+            if repo.filtername != 'visible-directaccess-nowarn':
+                unhiddencommits = repo._explicitaccess - accessbefore
+                repo.ui.warn( _("Warning: accessing hidden changesets %s " 
+                                "for write operation\n") % 
+                                (",".join([str(repo.unfiltered()[l]) 
+                                    for l in unhiddencommits])))
+            repo.invalidatevolatilesets()
--- a/hgext/inhibit.py	Thu May 14 15:59:06 2015 -0700
+++ b/hgext/inhibit.py	Thu May 14 11:23:40 2015 -0700
@@ -1,52 +1,33 @@
-"""Reduce the changesets evolution feature scope for early and noob friendly UI
+"""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
+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
+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.
-
-The second feature is called direct access. It is the ability to refer and
-access hidden sha in commands provided that you know their value.
-For example hg log -r XXX where XXX is a commit has should work whether XXX is
-hidden or not as we assume that the user knows what he is doing when referring
-to XXX.
+This extension provides 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 error
 from mercurial import scmutil
-from mercurial import repoview
-from mercurial import branchmap
-from mercurial import revset
-from mercurial import error
 from mercurial import commands
 from mercurial import lock as lockmod
 from mercurial import bookmarks
-from mercurial import lock as lockmod
 from mercurial.i18n import _
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
-# List of commands where no warning is shown for direct access
-directaccesslevel = [
-    # warning or not, extension (None if core), command name
-    (False, None, 'update'), 
-    (False, None, 'export'),
-    (True,  'rebase', 'rebase'),
-    (False,  'evolve', 'prune'),
-]
-
 def reposetup(ui, repo):
 
     class obsinhibitedrepo(repo.__class__):
@@ -66,36 +47,10 @@
             return newnode
 
     repo.__class__ = obsinhibitedrepo
-    repo._explicitaccess = set()
-    if not ui.configbool('inhibit', 'onlydirectaccess', False):
-        # Wrapping this to inhibit obsolete revs resulting from a transaction
-        extensions.wrapfunction(localrepo.localrepository,
-                                'transaction', transactioncallback)
-
-def _computehidden(repo):
-    hidden = repoview.computehidden(repo)
-    cl = repo.changelog
-    dynamic = hidden & repo._explicitaccess
-    if dynamic:
-        blocked = cl.ancestors(dynamic, inclusive=True)
-        hidden = frozenset(r for r in hidden if r not in blocked)
-    return hidden
+    # Wrapping this to inhibit obsolete revs resulting from a transaction
+    extensions.wrapfunction(localrepo.localrepository,
+                            'transaction', transactioncallback)
 
-def setupdirectaccess():
-    """ Add two new filtername that behave like visible to provide direct access
-    and direct access with warning. Wraps the commands to setup direct access """
-    repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden})
-    repoview.filtertable.update({'visible-directaccess-warn': _computehidden})
-    branchmap.subsettable['visible-directaccess-nowarn'] = 'visible'
-    branchmap.subsettable['visible-directaccess-warn'] = 'visible'
-
-    for warn, ext, cmd in directaccesslevel:
-        cmdtable = extensions.find(ext).cmdtable if ext else commands.table
-        wrapper = wrapwithwarning if warn else wrapwithoutwarning
-        try:
-            extensions.wrapcommand(cmdtable, cmd, wrapper)
-        except error.UnknownCommand:
-            pass
 def _update(orig, ui, repo, *args, **kwargs):
     """
     When moving to a commit we want to inhibit any obsolete commit affecting
@@ -211,7 +166,6 @@
     finally:
         tr.release()
 
-
 def transactioncallback(orig, repo, *args, **kwargs):
     """ Wrap localrepo.transaction to inhibit new obsolete changes """
     def inhibitposttransaction(transaction):
@@ -226,16 +180,6 @@
     transaction.addpostclose('inhibitposttransaction', inhibitposttransaction)
     return transaction
 
-def wrapwithoutwarning(orig, ui, repo, *args, **kwargs):
-    if repo and repo.filtername == 'visible':
-        repo = repo.filtered("visible-directaccess-nowarn")
-    return orig(ui, repo, *args, **kwargs)
-
-def wrapwithwarning(orig, ui, repo, *args, **kwargs):
-    if repo and repo.filtername == 'visible':
-        repo = repo.filtered("visible-directaccess-warn")
-    return orig(ui, repo, *args, **kwargs)
-
 def extsetup(ui):
     # lets wrap the computation of the obsolete set
     # We apply inhibition there
@@ -249,77 +193,29 @@
         for n in repo._obsinhibit:
             obs.discard(getrev(n))
         return obs
+    try:
+        extensions.find('directaccess')
+    except KeyError:
+        errormsg = _('Cannot use inhibit without the direct access extension')
+        raise error.Abort(errormsg)
     obsolete.cachefuncs['obsolete'] = _computeobsoleteset
     # wrap create marker to make it able to lift the inhibition
     extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers)
-    extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook)
-    setupdirectaccess()
-    if not ui.configbool('inhibit', 'onlydirectaccess', False):
-        # 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 update to make sure that no obsolete commit is visible after an
-        # update
-        extensions.wrapcommand(commands.table, 'update', _update)
-        # There are two ways to save bookmark changes during a transation, we
-        # wrap both to add inhibition markers.
-        extensions.wrapfunction(bookmarks.bmstore, 'recordchange', _bookmarkchanged)
-        extensions.wrapfunction(bookmarks.bmstore, 'write', _bookmarkchanged)
-        # Add bookmark -D option
-        entry = extensions.wrapcommand(commands.table, 'bookmark', _bookmark)
-        entry[1].append(('D','prune',None,
-                        _('delete the bookmark and prune the commits underneath')))
-
-
-
-def gethashsymbols(tree):
-    # Returns the list of symbols of the tree that look like hashes
-    # for example for the revset 3::abe3ff it will return ('abe3ff')
-    if not tree:
-        return []
-
-    if len(tree) == 2 and tree[0] == "symbol":
-        try:
-            int(tree[1])
-            return []
-        except ValueError as e:
-            return [tree[1]]
-    elif len(tree) == 3:
-        return gethashsymbols(tree[1]) + gethashsymbols(tree[2])
-    else:
-        return []
-
-def _posttreebuilthook(orig, tree, repo):
-    # This is use to enabled direct hash access
-    # We extract the symbols that look like hashes and add them to the
-    # explicitaccess set
-    orig(tree, repo)
-    filternm = ""
-    if repo is not None:
-        filternm = repo.filtername
-    if filternm is not None and filternm.startswith('visible-directaccess'):
-        prelength = len(repo._explicitaccess)
-        accessbefore = set(repo._explicitaccess)
-        repo.symbols = gethashsymbols(tree)
-        cl = repo.unfiltered().changelog
-        for node in repo.symbols:
-            try:
-                node = cl._partialmatch(node)
-            except error.LookupError:
-                node = None
-            if node is not None:
-                rev = cl.rev(node)
-                if rev not in repo.changelog:
-                    repo._explicitaccess.add(rev)
-        if prelength != len(repo._explicitaccess):
-            if repo.filtername != 'visible-directaccess-nowarn':
-                unhiddencommits = repo._explicitaccess - accessbefore
-                repo.ui.warn( _("Warning: accessing hidden changesets %s " 
-                                "for write operation\n") % 
-                                (",".join([str(repo.unfiltered()[l]) 
-                                    for l in unhiddencommits])))
-            repo.invalidatevolatilesets()
+    # 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 update to make sure that no obsolete commit is visible after an
+    # update
+    extensions.wrapcommand(commands.table, 'update', _update)
+    # There are two ways to save bookmark changes during a transation, we
+    # wrap both to add inhibition markers.
+    extensions.wrapfunction(bookmarks.bmstore, 'recordchange', _bookmarkchanged)
+    extensions.wrapfunction(bookmarks.bmstore, 'write', _bookmarkchanged)
+    # Add bookmark -D option
+    entry = extensions.wrapcommand(commands.table, 'bookmark', _bookmark)
+    entry[1].append(('D','prune',None,
+                    _('delete the bookmark and prune the commits underneath')))
 
 @command('debugobsinhibit', [], '')
 def cmddebugobsinhibit(ui, repo, *revs):
--- a/tests/test-inhibit.t	Thu May 14 15:59:06 2015 -0700
+++ b/tests/test-inhibit.t	Thu May 14 11:23:40 2015 -0700
@@ -9,6 +9,7 @@
   > strip=
   > EOF
   $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+  $ echo "directaccess=$(echo $(dirname $TESTDIR))/hgext/directaccess.py" >> $HGRCPATH
   $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH
   $ mkcommit() {
   >    echo "$1" > "$1"
@@ -562,17 +563,17 @@
   added 2 changesets with 1 changes to 2 files (+1 heads)
   (run 'hg heads' to see heads, 'hg merge' to merge)
 
-Only allow direct access and check that evolve works like before
+ Only allow direct access and check that evolve works like before
   $ cat >> $HGRCPATH <<EOF
-  > [inhibit]
-  > onlydirectaccess = True
+  > [extensions]
+  > inhibit=!
   > EOF
 
   $ hg up 15
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory parent is obsolete!
   $ echo "CM" > cM
   $ hg amend
-  1 new unstable changesets
   $ hg log -G
   @  21:721c3c279519 add cM
   |
@@ -582,10 +583,8 @@
   |/
   o  14:d66ccb8c5871 add cL
   |
-  | o  9:55c73a90e4b4 add cJ
-  | |
-  o |  7:18214586bf78 add cJ
-  |/
+  o  7:18214586bf78 add cJ
+  |
   o  6:cf5c4f4554ce add cH
   |
   o  5:5419eb264a33 add cG
@@ -594,3 +593,15 @@
   |
   o  0:54ccbc537fc2 add cA
   
+
+Inhibit should not work without directaccess
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > directaccess=!
+  > EOF
+  $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH
+
+  $ hg up 15
+  abort: Cannot use inhibit without the direct access extension
+  [255]
+