diff -r 369c2d5eeea3 -r 7095dbc266e3 mercurial/match.py --- 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 ('' % + return ('' % + (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 ('' % (self.patternspat, self.includepat)) class exactmatcher(basematcher):