diff mercurial/match.py @ 22513:ca709785caf2

match: simplify brittle predicate construction In match.__init__(), we create the matchfn predicate by and-ing together the individual predicates for includes, excludes (negated) and patterns. Instead of the current set of nested if/else blocks, we can simplify by adding the predicates to a list and defining the overall predicate in a generic way based on the components. We can still optimize it for the 0-length and 1-length cases. This way, there is no combinatorial explosion to deal with if new component predicates are added, and there is less risk of getting the overall predicate wrong.
author Martin von Zweigbergk <martinvonz@gmail.com>
date Fri, 19 Sep 2014 13:49:58 -0700
parents d516b6de3821
children bbb2f8b0459e
line wrap: on
line diff
--- a/mercurial/match.py	Tue Sep 23 14:45:23 2014 -0700
+++ b/mercurial/match.py	Fri Sep 19 13:49:58 2014 -0700
@@ -66,47 +66,39 @@
         self._ctx = ctx
         self._always = False
 
+        matchfns = []
         if include:
             kindpats = _normalize(include, 'glob', root, cwd, auditor)
             self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
+            matchfns.append(im)
         if exclude:
             kindpats = _normalize(exclude, 'glob', root, cwd, auditor)
             self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
+            matchfns.append(lambda f: not em(f))
         if exact:
             if isinstance(patterns, list):
                 self._files = patterns
             else:
                 self._files = list(patterns)
-            pm = self.exact
+            matchfns.append(self.exact)
         elif patterns:
             kindpats = _normalize(patterns, default, root, cwd, auditor)
             self._files = _roots(kindpats)
             self._anypats = self._anypats or _anypats(kindpats)
             self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
+            matchfns.append(pm)
 
-        if patterns or exact:
-            if include:
-                if exclude:
-                    m = lambda f: im(f) and not em(f) and pm(f)
-                else:
-                    m = lambda f: im(f) and pm(f)
-            else:
-                if exclude:
-                    m = lambda f: not em(f) and pm(f)
-                else:
-                    m = pm
+        if not matchfns:
+            m = util.always
+            self._always = True
+        elif len(matchfns) == 1:
+            m = matchfns[0]
         else:
-            if include:
-                if exclude:
-                    m = lambda f: im(f) and not em(f)
-                else:
-                    m = im
-            else:
-                if exclude:
-                    m = lambda f: not em(f)
-                else:
-                    m = lambda f: True
-                    self._always = True
+            def m(f):
+                for matchfn in matchfns:
+                    if not matchfn(f):
+                        return False
+                return True
 
         self.matchfn = m
         self._fmap = set(self._files)