--- a/mercurial/match.py Mon Feb 13 15:39:29 2017 -0800
+++ b/mercurial/match.py Mon Feb 13 17:03:14 2017 -0800
@@ -125,9 +125,12 @@
self._always = False
self._pathrestricted = bool(include or exclude or patterns)
self._warn = warn
+
+ # roots are directories which are recursively included/excluded.
self._includeroots = set()
+ self._excluderoots = set()
+ # dirs are directories which are non-recursively included.
self._includedirs = set(['.'])
- self._excluderoots = set()
if badfn is not None:
self.bad = badfn
@@ -137,14 +140,20 @@
kindpats = self._normalize(include, 'glob', root, cwd, auditor)
self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
listsubrepos, root)
- self._includeroots.update(_roots(kindpats))
- self._includedirs.update(util.dirs(self._includeroots))
+ roots, dirs = _rootsanddirs(kindpats)
+ self._includeroots.update(roots)
+ self._includedirs.update(dirs)
matchfns.append(im)
if exclude:
kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
listsubrepos, root)
if not _anypats(kindpats):
+ # Only consider recursive excludes as such - if a non-recursive
+ # exclude is used, we must still recurse into the excluded
+ # directory, at least to find subdirectories. In such a case,
+ # the regex still won't match the non-recursively-excluded
+ # files.
self._excluderoots.update(_roots(kindpats))
matchfns.append(lambda f: not em(f))
if exact:
@@ -241,7 +250,7 @@
return 'all'
if dir in self._excluderoots:
return False
- if (self._includeroots and
+ if ((self._includeroots or self._includedirs != set(['.'])) and
'.' not in self._includeroots and
dir not in self._includeroots and
dir not in self._includedirs and
@@ -422,7 +431,9 @@
# m.exact(file) must be based off of the actual user input, otherwise
# inexact case matches are treated as exact, and not noted without -v.
if self._files:
- self._fileroots = set(_roots(self._kp))
+ roots, dirs = _rootsanddirs(self._kp)
+ self._fileroots = set(roots)
+ self._fileroots.update(dirs)
def _normalize(self, patterns, default, root, cwd, auditor):
self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
@@ -621,19 +632,16 @@
raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
raise error.Abort(_("invalid pattern"))
-def _roots(kindpats):
- '''return roots and exact explicitly listed files from patterns
+def _patternrootsanddirs(kindpats):
+ '''Returns roots and directories corresponding to each pattern.
- >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
- ['g', 'g', '.']
- >>> _roots([('rootfilesin', 'g', ''), ('rootfilesin', '', '')])
- ['g', '.']
- >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
- ['r', 'p/p', '.']
- >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
- ['.', '.', '.']
+ This calculates the roots and directories exactly matching the patterns and
+ returns a tuple of (roots, dirs) for each. It does not return other
+ directories which may also need to be considered, like the parent
+ directories.
'''
r = []
+ d = []
for kind, pat, source in kindpats:
if kind == 'glob': # find the non-glob prefix
root = []
@@ -642,11 +650,48 @@
break
root.append(p)
r.append('/'.join(root) or '.')
- elif kind in ('relpath', 'path', 'rootfilesin'):
+ elif kind in ('relpath', 'path'):
r.append(pat or '.')
+ elif kind in ('rootfilesin',):
+ d.append(pat or '.')
else: # relglob, re, relre
r.append('.')
- return r
+ return r, d
+
+def _roots(kindpats):
+ '''Returns root directories to match recursively from the given patterns.'''
+ roots, dirs = _patternrootsanddirs(kindpats)
+ return roots
+
+def _rootsanddirs(kindpats):
+ '''Returns roots and exact directories from patterns.
+
+ roots are directories to match recursively, whereas exact directories should
+ be matched non-recursively. The returned (roots, dirs) tuple will also
+ include directories that need to be implicitly considered as either, such as
+ parent directories.
+
+ >>> _rootsanddirs(\
+ [('glob', 'g/h/*', ''), ('glob', 'g/h', ''), ('glob', 'g*', '')])
+ (['g/h', 'g/h', '.'], ['g'])
+ >>> _rootsanddirs(\
+ [('rootfilesin', 'g/h', ''), ('rootfilesin', '', '')])
+ ([], ['g/h', '.', 'g'])
+ >>> _rootsanddirs(\
+ [('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
+ (['r', 'p/p', '.'], ['p'])
+ >>> _rootsanddirs(\
+ [('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
+ (['.', '.', '.'], [])
+ '''
+ r, d = _patternrootsanddirs(kindpats)
+
+ # Append the parents as non-recursive/exact directories, since they must be
+ # scanned to get to either the roots or the other exact directories.
+ d.extend(util.dirs(d))
+ d.extend(util.dirs(r))
+
+ return r, d
def _explicitfiles(kindpats):
'''Returns the potential explicit filenames from the patterns.