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.
--- 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)