changeset 32185:6dea1701f170

match: make subinclude construction lazy The matcher subinclude functionality allows us to have .hgignore files that include subdirectory hgignore files. Today it parses the entire repo at once, even if we only need to test a file in one subdirectory. This patch makes the subinclude tree creation lazy, which speeds up matcher creation significantly in large repos with very large trees of ignore patterns.
author Durham Goode <durham@fb.com>
date Wed, 03 May 2017 10:30:57 -0700
parents 35a69efbf190
children 435a3842ca3a
files mercurial/match.py
diffstat 1 files changed, 13 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/match.py	Wed May 03 09:09:44 2017 -0700
+++ b/mercurial/match.py	Wed May 03 10:30:57 2017 -0700
@@ -52,7 +52,7 @@
     return fset, other
 
 def _expandsubinclude(kindpats, root):
-    '''Returns the list of subinclude matchers and the kindpats without the
+    '''Returns the list of subinclude matcher args and the kindpats without the
     subincludes in it.'''
     relmatchers = []
     other = []
@@ -64,12 +64,12 @@
             path = pathutil.join(sourceroot, pat)
 
             newroot = pathutil.dirname(path)
-            relmatcher = match(newroot, '', [], ['include:%s' % path])
+            matcherargs = (newroot, '', [], ['include:%s' % path])
 
             prefix = pathutil.canonpath(root, root, newroot)
             if prefix:
                 prefix += '/'
-            relmatchers.append((prefix, relmatcher))
+            relmatchers.append((prefix, matcherargs))
         else:
             other.append((kind, pat, source))
 
@@ -584,10 +584,17 @@
 
     subincludes, kindpats = _expandsubinclude(kindpats, root)
     if subincludes:
+        submatchers = {}
         def matchsubinclude(f):
-            for prefix, mf in subincludes:
-                if f.startswith(prefix) and mf(f[len(prefix):]):
-                    return True
+            for prefix, matcherargs in subincludes:
+                if f.startswith(prefix):
+                    mf = submatchers.get(prefix)
+                    if mf is None:
+                        mf = match(*matcherargs)
+                        submatchers[prefix] = mf
+
+                    if mf(f[len(prefix):]):
+                        return True
             return False
         matchfuncs.append(matchsubinclude)