diff mercurial/match.py @ 41282:4fab8a7d2d72

match: support rooted globs in hgignore In a .hgignore, "glob:foo" always means "**/foo". This cannot be avoided because there is no syntax like "^" in regexes to say you don't want the implied "**/" (of course one can use regexes, but glob syntax is nice). When you have a long list of fairly specific globs like path/to/some/thing, this has two consequences: 1. unintended files may be ignored (not too common though) 2. matching performance can suffer significantly Here is vanilla hg status timing on a private repository: Using syntax:glob everywhere real 0m2.199s user 0m1.545s sys 0m0.619s When rooting the appropriate globs real 0m1.434s user 0m0.847s sys 0m0.565s (tangentially, none of this shows up in --profile's output. It seems that C code doesn't play well with profiling) The code already supports this but there is no syntax to make use of it, so it seems reasonable to create such syntax. I create a new hgignore syntax "rootglob". Differential Revision: https://phab.mercurial-scm.org/D5493
author Valentin Gatien-Baron <vgatien-baron@janestreet.com>
date Thu, 03 Jan 2019 19:02:46 -0500
parents 074c72a38423
children b7a0efb3c370
line wrap: on
line diff
--- a/mercurial/match.py	Wed Nov 07 15:45:09 2018 -0800
+++ b/mercurial/match.py	Thu Jan 03 19:02:46 2019 -0500
@@ -25,6 +25,7 @@
 )
 
 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
+                   'rootglob',
                    'listfile', 'listfile0', 'set', 'include', 'subinclude',
                    'rootfilesin')
 cwdrelativepatternkinds = ('relpath', 'glob')
@@ -221,7 +222,7 @@
     for kind, pat in [_patsplit(p, default) for p in patterns]:
         if kind in cwdrelativepatternkinds:
             pat = pathutil.canonpath(root, cwd, pat, auditor)
-        elif kind in ('relglob', 'path', 'rootfilesin'):
+        elif kind in ('relglob', 'path', 'rootfilesin', 'rootglob'):
             pat = util.normpath(pat)
         elif kind in ('listfile', 'listfile0'):
             try:
@@ -1137,7 +1138,7 @@
         if pat.startswith('^'):
             return pat
         return '.*' + pat
-    if kind == 'glob':
+    if kind in ('glob', 'rootglob'):
         return _globre(pat) + globsuffix
     raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
 
@@ -1252,7 +1253,7 @@
     r = []
     d = []
     for kind, pat, source in kindpats:
-        if kind == 'glob': # find the non-glob prefix
+        if kind in ('glob', 'rootglob'): # find the non-glob prefix
             root = []
             for p in pat.split('/'):
                 if '[' in p or '{' in p or '*' in p or '?' in p:
@@ -1351,6 +1352,7 @@
     syntax: glob   # defaults following lines to non-rooted globs
     re:pattern     # non-rooted regular expression
     glob:pattern   # non-rooted glob
+    rootglob:pat   # rooted glob (same root as ^ in regexps)
     pattern        # pattern of the current default type
 
     if sourceinfo is set, returns a list of tuples:
@@ -1361,6 +1363,7 @@
         're': 'relre:',
         'regexp': 'relre:',
         'glob': 'relglob:',
+        'rootglob': 'rootglob:',
         'include': 'include',
         'subinclude': 'subinclude',
     }