Patchwork [5,of,9,sparse] sparse: move matchers into core

login
register
mail settings
Submitter Gregory Szorc
Date July 6, 2017, 9:54 p.m.
Message ID <fa855679d5ef4af9530f.1499378061@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/22058/
State Accepted
Headers show

Comments

Gregory Szorc - July 6, 2017, 9:54 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1499370935 25200
#      Thu Jul 06 12:55:35 2017 -0700
# Node ID fa855679d5ef4af9530f9500d1b08a74202aa35a
# Parent  b9d7f9e9fd096f795314ad9ccaea85139df47997
sparse: move matchers into core

The sparse extension contains some matcher types that are
generic and can exist in core.

As part of the move, the classes now inherit from basematcher.
always(), files(), and isexact() have been dropped because
they match the default implementations in basematcher.

Note that this hashing of matchers is somewhat hacky. It is an
optimization that results in better performance. We can and
should think about formalizing the API across all matchers. This
is why matchers are initially being added to sparse.py instead
of match.py. Once they are stable, we can move them to join their
siblings in match.py.
via Mercurial-devel - July 6, 2017, 10:38 p.m.
On Thu, Jul 6, 2017 at 2:54 PM, Gregory Szorc <gregory.szorc@gmail.com> wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1499370935 25200
> #      Thu Jul 06 12:55:35 2017 -0700
> # Node ID fa855679d5ef4af9530f9500d1b08a74202aa35a
> # Parent  b9d7f9e9fd096f795314ad9ccaea85139df47997
> sparse: move matchers into core
>
> The sparse extension contains some matcher types that are
> generic and can exist in core.
>
> As part of the move, the classes now inherit from basematcher.
> always(), files(), and isexact() have been dropped because
> they match the default implementations in basematcher.
>
> Note that this hashing of matchers is somewhat hacky. It is an
> optimization that results in better performance. We can and
> should think about formalizing the API across all matchers. This
> is why matchers are initially being added to sparse.py instead
> of match.py. Once they are stable, we can move them to join their
> siblings in match.py.

The matchers in match.py now override __repr__ and fsmonitor uses that
result of that. Can we do that for these matchers too?

Patch

diff --git a/hgext/sparse.py b/hgext/sparse.py
--- a/hgext/sparse.py
+++ b/hgext/sparse.py
@@ -75,7 +75,6 @@  certain files::
 from __future__ import absolute_import
 
 import collections
-import hashlib
 import os
 
 from mercurial.i18n import _
@@ -120,7 +119,7 @@  def extsetup(ui):
     try:
         fsmonitor = extensions.find('fsmonitor')
         def _hashignore(orig, ignore):
-            return _hashmatcher(ignore)
+            return sparse.hashmatcher(ignore)
         extensions.wrapfunction(fsmonitor, '_hashignore', _hashignore)
     except KeyError:
         pass
@@ -361,8 +360,8 @@  def _setupdirstate(ui):
 
             sparsematch = repo.sparsematch()
             if self.sparsematch != sparsematch or self.origignore != origignore:
-                self.func = unionmatcher([origignore,
-                                          negatematcher(sparsematch)])
+                self.func = sparse.unionmatcher([
+                    origignore, sparse.negatematcher(sparsematch)])
                 self.sparsematch = sparsematch
                 self.origignore = origignore
             return self.func
@@ -457,7 +456,8 @@  def _wraprepo(ui, repo):
                             include=includes, exclude=excludes,
                             default='relpath')
                         if subdirs:
-                            matcher = forceincludematcher(matcher, subdirs)
+                            matcher = sparse.forceincludematcher(matcher,
+                                                                 subdirs)
                         matchers.append(matcher)
                 except IOError:
                     pass
@@ -468,11 +468,11 @@  def _wraprepo(ui, repo):
             elif len(matchers) == 1:
                 result = matchers[0]
             else:
-                result = unionmatcher(matchers)
+                result = sparse.unionmatcher(matchers)
 
             if kwargs.get('includetemp', True):
                 tempincludes = sparse.readtemporaryincludes(self)
-                result = forceincludematcher(result, tempincludes)
+                result = sparse.forceincludematcher(result, tempincludes)
 
             self._sparsematchercache[key] = result
 
@@ -870,100 +870,3 @@  def _verbose_output(ui, opts, profilecou
                          dropped)
             fm.condwrite(ui.verbose, 'files_conflicting',
                          'Files conflicting: %d\n', lookup)
-
-class forceincludematcher(object):
-    """A matcher that returns true for any of the forced includes before testing
-    against the actual matcher."""
-    def __init__(self, matcher, includes):
-        self._matcher = matcher
-        self._includes = includes
-
-    def __call__(self, value):
-        return value in self._includes or self._matcher(value)
-
-    def always(self):
-        return False
-
-    def files(self):
-        return []
-
-    def isexact(self):
-        return False
-
-    def anypats(self):
-        return True
-
-    def prefix(self):
-        return False
-
-    def hash(self):
-        sha1 = hashlib.sha1()
-        sha1.update(_hashmatcher(self._matcher))
-        for include in sorted(self._includes):
-            sha1.update(include + '\0')
-        return sha1.hexdigest()
-
-class unionmatcher(object):
-    """A matcher that is the union of several matchers."""
-    def __init__(self, matchers):
-        self._matchers = matchers
-
-    def __call__(self, value):
-        for match in self._matchers:
-            if match(value):
-                return True
-        return False
-
-    def always(self):
-        return False
-
-    def files(self):
-        return []
-
-    def isexact(self):
-        return False
-
-    def anypats(self):
-        return True
-
-    def prefix(self):
-        return False
-
-    def hash(self):
-        sha1 = hashlib.sha1()
-        for m in self._matchers:
-            sha1.update(_hashmatcher(m))
-        return sha1.hexdigest()
-
-class negatematcher(object):
-    def __init__(self, matcher):
-        self._matcher = matcher
-
-    def __call__(self, value):
-        return not self._matcher(value)
-
-    def always(self):
-        return False
-
-    def files(self):
-        return []
-
-    def isexact(self):
-        return False
-
-    def anypats(self):
-        return True
-
-    def hash(self):
-        sha1 = hashlib.sha1()
-        sha1.update('negate')
-        sha1.update(_hashmatcher(self._matcher))
-        return sha1.hexdigest()
-
-def _hashmatcher(matcher):
-    if util.safehasattr(matcher, 'hash'):
-        return matcher.hash()
-
-    sha1 = hashlib.sha1()
-    sha1.update(repr(matcher))
-    return sha1.hexdigest()
diff --git a/mercurial/sparse.py b/mercurial/sparse.py
--- a/mercurial/sparse.py
+++ b/mercurial/sparse.py
@@ -13,6 +13,8 @@  from .i18n import _
 from .node import nullid
 from . import (
     error,
+    match as matchmod,
+    util,
 )
 
 # Whether sparse features are enabled. This variable is intended to be
@@ -193,3 +195,73 @@  def addtemporaryincludes(repo, additiona
     for i in additional:
         includes.add(i)
     writetemporaryincludes(repo, includes)
+
+class forceincludematcher(matchmod.basematcher):
+    """A matcher that returns true for any of the forced includes before testing
+    against the actual matcher."""
+    def __init__(self, matcher, includes):
+        self._matcher = matcher
+        self._includes = includes
+
+    def __call__(self, value):
+        return value in self._includes or self._matcher(value)
+
+    def anypats(self):
+        return True
+
+    def prefix(self):
+        return False
+
+    def hash(self):
+        sha1 = hashlib.sha1()
+        sha1.update(hashmatcher(self._matcher))
+        for include in sorted(self._includes):
+            sha1.update(include + '\0')
+        return sha1.hexdigest()
+
+class unionmatcher(matchmod.basematcher):
+    """A matcher that is the union of several matchers."""
+    def __init__(self, matchers):
+        self._matchers = matchers
+
+    def __call__(self, value):
+        for match in self._matchers:
+            if match(value):
+                return True
+        return False
+
+    def anypats(self):
+        return True
+
+    def prefix(self):
+        return False
+
+    def hash(self):
+        sha1 = hashlib.sha1()
+        for m in self._matchers:
+            sha1.update(hashmatcher(m))
+        return sha1.hexdigest()
+
+class negatematcher(matchmod.basematcher):
+    def __init__(self, matcher):
+        self._matcher = matcher
+
+    def __call__(self, value):
+        return not self._matcher(value)
+
+    def anypats(self):
+        return True
+
+    def hash(self):
+        sha1 = hashlib.sha1()
+        sha1.update('negate')
+        sha1.update(hashmatcher(self._matcher))
+        return sha1.hexdigest()
+
+def hashmatcher(matcher):
+    if util.safehasattr(matcher, 'hash'):
+        return matcher.hash()
+
+    sha1 = hashlib.sha1()
+    sha1.update(repr(matcher))
+    return sha1.hexdigest()