mercurial/match.py
changeset 32501 7095dbc266e3
parent 32500 369c2d5eeea3
child 32502 3026f19b4b01
--- a/mercurial/match.py	Thu May 18 23:39:39 2017 -0700
+++ b/mercurial/match.py	Fri May 19 22:36:14 2017 -0700
@@ -145,18 +145,18 @@
     if exact:
         m = exactmatcher(root, cwd, patterns, badfn)
     else:
-        m = matcher(root, cwd, normalize, patterns, include=None,
-                    default=default, auditor=auditor, ctx=ctx,
-                    listsubrepos=listsubrepos, warn=warn, badfn=badfn)
+        m = patternmatcher(root, cwd, normalize, patterns, include=None,
+                           default=default, auditor=auditor, ctx=ctx,
+                           listsubrepos=listsubrepos, warn=warn, badfn=badfn)
     if include:
-        im = matcher(root, cwd, normalize, [], include=include, default=default,
-                     auditor=auditor, ctx=ctx, listsubrepos=listsubrepos,
-                     warn=warn, badfn=None)
+        im = includematcher(root, cwd, normalize, [], include=include,
+                            default=default, auditor=auditor, ctx=ctx,
+                            listsubrepos=listsubrepos, warn=warn, badfn=None)
         m = intersectmatchers(m, im)
     if exclude:
-        em = matcher(root, cwd, normalize, [], include=exclude, default=default,
-                     auditor=auditor, ctx=ctx, listsubrepos=listsubrepos,
-                     warn=warn, badfn=None)
+        em = includematcher(root, cwd, normalize, [], include=exclude,
+                            default=default, auditor=auditor, ctx=ctx,
+                            listsubrepos=listsubrepos, warn=warn, badfn=None)
         m = differencematcher(m, em)
     return m
 
@@ -311,13 +311,14 @@
     def prefix(self):
         return not self.always() and not self.isexact() and not self.anypats()
 
-class matcher(basematcher):
+class patternmatcher(basematcher):
 
     def __init__(self, root, cwd, normalize, patterns, include=None,
                  default='glob', auditor=None, ctx=None,
                  listsubrepos=False, warn=None, badfn=None):
-        super(matcher, self).__init__(root, cwd, badfn,
-                                      relativeuipath=bool(include or patterns))
+        super(patternmatcher, self).__init__(root, cwd, badfn,
+                                             relativeuipath=bool(include or
+                                                                 patterns))
         if include is None:
             include = []
 
@@ -397,7 +398,97 @@
         return self._always
 
     def __repr__(self):
-        return ('<matcher patterns=%r, includes=%r>' %
+        return ('<patternmatcher patterns=%r, includes=%r>' %
+                (self.patternspat, self.includepat))
+
+class includematcher(basematcher):
+
+    def __init__(self, root, cwd, normalize, patterns, include=None,
+                 default='glob', auditor=None, ctx=None,
+                 listsubrepos=False, warn=None, badfn=None):
+        super(includematcher, self).__init__(root, cwd, badfn,
+                                             relativeuipath=bool(include or
+                                                                 patterns))
+        if include is None:
+            include = []
+
+        self._anypats = bool(include)
+        self._anyincludepats = False
+        self._always = False
+        self.patternspat = None
+        self.includepat = None
+
+        # roots are directories which are recursively included.
+        self._includeroots = set()
+        # dirs are directories which are non-recursively included.
+        self._includedirs = set()
+
+        matchfns = []
+        if include:
+            kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
+            self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
+                                              listsubrepos, root)
+            self._anyincludepats = _anypats(kindpats)
+            roots, dirs = _rootsanddirs(kindpats)
+            self._includeroots.update(roots)
+            self._includedirs.update(dirs)
+            matchfns.append(im)
+        if patterns:
+            kindpats = normalize(patterns, default, root, cwd, auditor, warn)
+            if not _kindpatsalwaysmatch(kindpats):
+                self._files = _explicitfiles(kindpats)
+                self._anypats = self._anypats or _anypats(kindpats)
+                self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
+                                                   listsubrepos, root)
+                matchfns.append(pm)
+
+        if not matchfns:
+            m = util.always
+            self._always = True
+        elif len(matchfns) == 1:
+            m = matchfns[0]
+        else:
+            def m(f):
+                for matchfn in matchfns:
+                    if not matchfn(f):
+                        return False
+                return True
+
+        self.matchfn = m
+
+    @propertycache
+    def _dirs(self):
+        return set(util.dirs(self._fileset)) | {'.'}
+
+    def visitdir(self, dir):
+        if self.prefix() and dir in self._fileset:
+            return 'all'
+        if self._includeroots or self._includedirs:
+            if (not self._anyincludepats and
+                dir in self._includeroots):
+                # The condition above is essentially self.prefix() for includes
+                return 'all'
+            if ('.' not in self._includeroots and
+                dir not in self._includeroots and
+                dir not in self._includedirs and
+                not any(parent in self._includeroots
+                        for parent in util.finddirs(dir))):
+                return False
+        return (not self._fileset or
+                '.' in self._fileset or
+                dir in self._fileset or
+                dir in self._dirs or
+                any(parentdir in self._fileset
+                    for parentdir in util.finddirs(dir)))
+
+    def anypats(self):
+        return self._anypats
+
+    def always(self):
+        return self._always
+
+    def __repr__(self):
+        return ('<includematcher patterns=%r, includes=%r>' %
                 (self.patternspat, self.includepat))
 
 class exactmatcher(basematcher):