##// END OF EJS Templates
match: add visitchildrenset complement to visitdir...
spectral -
r38990:081cc9a9 default
parent child Browse files
Show More
@@ -1,1137 +1,1319 b''
1 # match.py - filename matching
1 # match.py - filename matching
2 #
2 #
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import copy
10 import copy
11 import itertools
11 import os
12 import os
12 import re
13 import re
13
14
14 from .i18n import _
15 from .i18n import _
15 from . import (
16 from . import (
16 encoding,
17 encoding,
17 error,
18 error,
18 pathutil,
19 pathutil,
19 pycompat,
20 pycompat,
20 util,
21 util,
21 )
22 )
22 from .utils import (
23 from .utils import (
23 stringutil,
24 stringutil,
24 )
25 )
25
26
26 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
27 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
27 'listfile', 'listfile0', 'set', 'include', 'subinclude',
28 'listfile', 'listfile0', 'set', 'include', 'subinclude',
28 'rootfilesin')
29 'rootfilesin')
29 cwdrelativepatternkinds = ('relpath', 'glob')
30 cwdrelativepatternkinds = ('relpath', 'glob')
30
31
31 propertycache = util.propertycache
32 propertycache = util.propertycache
32
33
33 def _rematcher(regex):
34 def _rematcher(regex):
34 '''compile the regexp with the best available regexp engine and return a
35 '''compile the regexp with the best available regexp engine and return a
35 matcher function'''
36 matcher function'''
36 m = util.re.compile(regex)
37 m = util.re.compile(regex)
37 try:
38 try:
38 # slightly faster, provided by facebook's re2 bindings
39 # slightly faster, provided by facebook's re2 bindings
39 return m.test_match
40 return m.test_match
40 except AttributeError:
41 except AttributeError:
41 return m.match
42 return m.match
42
43
43 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
44 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
44 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
45 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
45 matchers = []
46 matchers = []
46 other = []
47 other = []
47
48
48 for kind, pat, source in kindpats:
49 for kind, pat, source in kindpats:
49 if kind == 'set':
50 if kind == 'set':
50 if not ctx:
51 if not ctx:
51 raise error.ProgrammingError("fileset expression with no "
52 raise error.ProgrammingError("fileset expression with no "
52 "context")
53 "context")
53 matchers.append(ctx.matchfileset(pat, badfn=badfn))
54 matchers.append(ctx.matchfileset(pat, badfn=badfn))
54
55
55 if listsubrepos:
56 if listsubrepos:
56 for subpath in ctx.substate:
57 for subpath in ctx.substate:
57 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
58 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
58 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
59 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
59 matchers.append(pm)
60 matchers.append(pm)
60
61
61 continue
62 continue
62 other.append((kind, pat, source))
63 other.append((kind, pat, source))
63 return matchers, other
64 return matchers, other
64
65
65 def _expandsubinclude(kindpats, root):
66 def _expandsubinclude(kindpats, root):
66 '''Returns the list of subinclude matcher args and the kindpats without the
67 '''Returns the list of subinclude matcher args and the kindpats without the
67 subincludes in it.'''
68 subincludes in it.'''
68 relmatchers = []
69 relmatchers = []
69 other = []
70 other = []
70
71
71 for kind, pat, source in kindpats:
72 for kind, pat, source in kindpats:
72 if kind == 'subinclude':
73 if kind == 'subinclude':
73 sourceroot = pathutil.dirname(util.normpath(source))
74 sourceroot = pathutil.dirname(util.normpath(source))
74 pat = util.pconvert(pat)
75 pat = util.pconvert(pat)
75 path = pathutil.join(sourceroot, pat)
76 path = pathutil.join(sourceroot, pat)
76
77
77 newroot = pathutil.dirname(path)
78 newroot = pathutil.dirname(path)
78 matcherargs = (newroot, '', [], ['include:%s' % path])
79 matcherargs = (newroot, '', [], ['include:%s' % path])
79
80
80 prefix = pathutil.canonpath(root, root, newroot)
81 prefix = pathutil.canonpath(root, root, newroot)
81 if prefix:
82 if prefix:
82 prefix += '/'
83 prefix += '/'
83 relmatchers.append((prefix, matcherargs))
84 relmatchers.append((prefix, matcherargs))
84 else:
85 else:
85 other.append((kind, pat, source))
86 other.append((kind, pat, source))
86
87
87 return relmatchers, other
88 return relmatchers, other
88
89
89 def _kindpatsalwaysmatch(kindpats):
90 def _kindpatsalwaysmatch(kindpats):
90 """"Checks whether the kindspats match everything, as e.g.
91 """"Checks whether the kindspats match everything, as e.g.
91 'relpath:.' does.
92 'relpath:.' does.
92 """
93 """
93 for kind, pat, source in kindpats:
94 for kind, pat, source in kindpats:
94 if pat != '' or kind not in ['relpath', 'glob']:
95 if pat != '' or kind not in ['relpath', 'glob']:
95 return False
96 return False
96 return True
97 return True
97
98
98 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
99 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
99 listsubrepos=False, badfn=None):
100 listsubrepos=False, badfn=None):
100 matchers = []
101 matchers = []
101 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
102 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
102 listsubrepos=listsubrepos, badfn=badfn)
103 listsubrepos=listsubrepos, badfn=badfn)
103 if kindpats:
104 if kindpats:
104 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
105 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
105 badfn=badfn)
106 badfn=badfn)
106 matchers.append(m)
107 matchers.append(m)
107 if fms:
108 if fms:
108 matchers.extend(fms)
109 matchers.extend(fms)
109 if not matchers:
110 if not matchers:
110 return nevermatcher(root, cwd, badfn=badfn)
111 return nevermatcher(root, cwd, badfn=badfn)
111 if len(matchers) == 1:
112 if len(matchers) == 1:
112 return matchers[0]
113 return matchers[0]
113 return unionmatcher(matchers)
114 return unionmatcher(matchers)
114
115
115 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
116 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
116 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
117 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
117 badfn=None, icasefs=False):
118 badfn=None, icasefs=False):
118 """build an object to match a set of file patterns
119 """build an object to match a set of file patterns
119
120
120 arguments:
121 arguments:
121 root - the canonical root of the tree you're matching against
122 root - the canonical root of the tree you're matching against
122 cwd - the current working directory, if relevant
123 cwd - the current working directory, if relevant
123 patterns - patterns to find
124 patterns - patterns to find
124 include - patterns to include (unless they are excluded)
125 include - patterns to include (unless they are excluded)
125 exclude - patterns to exclude (even if they are included)
126 exclude - patterns to exclude (even if they are included)
126 default - if a pattern in patterns has no explicit type, assume this one
127 default - if a pattern in patterns has no explicit type, assume this one
127 exact - patterns are actually filenames (include/exclude still apply)
128 exact - patterns are actually filenames (include/exclude still apply)
128 warn - optional function used for printing warnings
129 warn - optional function used for printing warnings
129 badfn - optional bad() callback for this matcher instead of the default
130 badfn - optional bad() callback for this matcher instead of the default
130 icasefs - make a matcher for wdir on case insensitive filesystems, which
131 icasefs - make a matcher for wdir on case insensitive filesystems, which
131 normalizes the given patterns to the case in the filesystem
132 normalizes the given patterns to the case in the filesystem
132
133
133 a pattern is one of:
134 a pattern is one of:
134 'glob:<glob>' - a glob relative to cwd
135 'glob:<glob>' - a glob relative to cwd
135 're:<regexp>' - a regular expression
136 're:<regexp>' - a regular expression
136 'path:<path>' - a path relative to repository root, which is matched
137 'path:<path>' - a path relative to repository root, which is matched
137 recursively
138 recursively
138 'rootfilesin:<path>' - a path relative to repository root, which is
139 'rootfilesin:<path>' - a path relative to repository root, which is
139 matched non-recursively (will not match subdirectories)
140 matched non-recursively (will not match subdirectories)
140 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
141 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
141 'relpath:<path>' - a path relative to cwd
142 'relpath:<path>' - a path relative to cwd
142 'relre:<regexp>' - a regexp that needn't match the start of a name
143 'relre:<regexp>' - a regexp that needn't match the start of a name
143 'set:<fileset>' - a fileset expression
144 'set:<fileset>' - a fileset expression
144 'include:<path>' - a file of patterns to read and include
145 'include:<path>' - a file of patterns to read and include
145 'subinclude:<path>' - a file of patterns to match against files under
146 'subinclude:<path>' - a file of patterns to match against files under
146 the same directory
147 the same directory
147 '<something>' - a pattern of the specified default type
148 '<something>' - a pattern of the specified default type
148 """
149 """
149 normalize = _donormalize
150 normalize = _donormalize
150 if icasefs:
151 if icasefs:
151 if exact:
152 if exact:
152 raise error.ProgrammingError("a case-insensitive exact matcher "
153 raise error.ProgrammingError("a case-insensitive exact matcher "
153 "doesn't make sense")
154 "doesn't make sense")
154 dirstate = ctx.repo().dirstate
155 dirstate = ctx.repo().dirstate
155 dsnormalize = dirstate.normalize
156 dsnormalize = dirstate.normalize
156
157
157 def normalize(patterns, default, root, cwd, auditor, warn):
158 def normalize(patterns, default, root, cwd, auditor, warn):
158 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
159 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
159 kindpats = []
160 kindpats = []
160 for kind, pats, source in kp:
161 for kind, pats, source in kp:
161 if kind not in ('re', 'relre'): # regex can't be normalized
162 if kind not in ('re', 'relre'): # regex can't be normalized
162 p = pats
163 p = pats
163 pats = dsnormalize(pats)
164 pats = dsnormalize(pats)
164
165
165 # Preserve the original to handle a case only rename.
166 # Preserve the original to handle a case only rename.
166 if p != pats and p in dirstate:
167 if p != pats and p in dirstate:
167 kindpats.append((kind, p, source))
168 kindpats.append((kind, p, source))
168
169
169 kindpats.append((kind, pats, source))
170 kindpats.append((kind, pats, source))
170 return kindpats
171 return kindpats
171
172
172 if exact:
173 if exact:
173 m = exactmatcher(root, cwd, patterns, badfn)
174 m = exactmatcher(root, cwd, patterns, badfn)
174 elif patterns:
175 elif patterns:
175 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
176 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
176 if _kindpatsalwaysmatch(kindpats):
177 if _kindpatsalwaysmatch(kindpats):
177 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
178 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
178 else:
179 else:
179 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
180 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
180 ctx=ctx, listsubrepos=listsubrepos,
181 ctx=ctx, listsubrepos=listsubrepos,
181 badfn=badfn)
182 badfn=badfn)
182 else:
183 else:
183 # It's a little strange that no patterns means to match everything.
184 # It's a little strange that no patterns means to match everything.
184 # Consider changing this to match nothing (probably using nevermatcher).
185 # Consider changing this to match nothing (probably using nevermatcher).
185 m = alwaysmatcher(root, cwd, badfn)
186 m = alwaysmatcher(root, cwd, badfn)
186
187
187 if include:
188 if include:
188 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
189 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
189 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
190 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
190 listsubrepos=listsubrepos, badfn=None)
191 listsubrepos=listsubrepos, badfn=None)
191 m = intersectmatchers(m, im)
192 m = intersectmatchers(m, im)
192 if exclude:
193 if exclude:
193 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
194 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
194 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
195 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
195 listsubrepos=listsubrepos, badfn=None)
196 listsubrepos=listsubrepos, badfn=None)
196 m = differencematcher(m, em)
197 m = differencematcher(m, em)
197 return m
198 return m
198
199
199 def exact(root, cwd, files, badfn=None):
200 def exact(root, cwd, files, badfn=None):
200 return exactmatcher(root, cwd, files, badfn=badfn)
201 return exactmatcher(root, cwd, files, badfn=badfn)
201
202
202 def always(root, cwd):
203 def always(root, cwd):
203 return alwaysmatcher(root, cwd)
204 return alwaysmatcher(root, cwd)
204
205
205 def never(root, cwd):
206 def never(root, cwd):
206 return nevermatcher(root, cwd)
207 return nevermatcher(root, cwd)
207
208
208 def badmatch(match, badfn):
209 def badmatch(match, badfn):
209 """Make a copy of the given matcher, replacing its bad method with the given
210 """Make a copy of the given matcher, replacing its bad method with the given
210 one.
211 one.
211 """
212 """
212 m = copy.copy(match)
213 m = copy.copy(match)
213 m.bad = badfn
214 m.bad = badfn
214 return m
215 return m
215
216
216 def _donormalize(patterns, default, root, cwd, auditor, warn):
217 def _donormalize(patterns, default, root, cwd, auditor, warn):
217 '''Convert 'kind:pat' from the patterns list to tuples with kind and
218 '''Convert 'kind:pat' from the patterns list to tuples with kind and
218 normalized and rooted patterns and with listfiles expanded.'''
219 normalized and rooted patterns and with listfiles expanded.'''
219 kindpats = []
220 kindpats = []
220 for kind, pat in [_patsplit(p, default) for p in patterns]:
221 for kind, pat in [_patsplit(p, default) for p in patterns]:
221 if kind in cwdrelativepatternkinds:
222 if kind in cwdrelativepatternkinds:
222 pat = pathutil.canonpath(root, cwd, pat, auditor)
223 pat = pathutil.canonpath(root, cwd, pat, auditor)
223 elif kind in ('relglob', 'path', 'rootfilesin'):
224 elif kind in ('relglob', 'path', 'rootfilesin'):
224 pat = util.normpath(pat)
225 pat = util.normpath(pat)
225 elif kind in ('listfile', 'listfile0'):
226 elif kind in ('listfile', 'listfile0'):
226 try:
227 try:
227 files = util.readfile(pat)
228 files = util.readfile(pat)
228 if kind == 'listfile0':
229 if kind == 'listfile0':
229 files = files.split('\0')
230 files = files.split('\0')
230 else:
231 else:
231 files = files.splitlines()
232 files = files.splitlines()
232 files = [f for f in files if f]
233 files = [f for f in files if f]
233 except EnvironmentError:
234 except EnvironmentError:
234 raise error.Abort(_("unable to read file list (%s)") % pat)
235 raise error.Abort(_("unable to read file list (%s)") % pat)
235 for k, p, source in _donormalize(files, default, root, cwd,
236 for k, p, source in _donormalize(files, default, root, cwd,
236 auditor, warn):
237 auditor, warn):
237 kindpats.append((k, p, pat))
238 kindpats.append((k, p, pat))
238 continue
239 continue
239 elif kind == 'include':
240 elif kind == 'include':
240 try:
241 try:
241 fullpath = os.path.join(root, util.localpath(pat))
242 fullpath = os.path.join(root, util.localpath(pat))
242 includepats = readpatternfile(fullpath, warn)
243 includepats = readpatternfile(fullpath, warn)
243 for k, p, source in _donormalize(includepats, default,
244 for k, p, source in _donormalize(includepats, default,
244 root, cwd, auditor, warn):
245 root, cwd, auditor, warn):
245 kindpats.append((k, p, source or pat))
246 kindpats.append((k, p, source or pat))
246 except error.Abort as inst:
247 except error.Abort as inst:
247 raise error.Abort('%s: %s' % (pat, inst[0]))
248 raise error.Abort('%s: %s' % (pat, inst[0]))
248 except IOError as inst:
249 except IOError as inst:
249 if warn:
250 if warn:
250 warn(_("skipping unreadable pattern file '%s': %s\n") %
251 warn(_("skipping unreadable pattern file '%s': %s\n") %
251 (pat, stringutil.forcebytestr(inst.strerror)))
252 (pat, stringutil.forcebytestr(inst.strerror)))
252 continue
253 continue
253 # else: re or relre - which cannot be normalized
254 # else: re or relre - which cannot be normalized
254 kindpats.append((kind, pat, ''))
255 kindpats.append((kind, pat, ''))
255 return kindpats
256 return kindpats
256
257
257 class basematcher(object):
258 class basematcher(object):
258
259
259 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
260 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
260 self._root = root
261 self._root = root
261 self._cwd = cwd
262 self._cwd = cwd
262 if badfn is not None:
263 if badfn is not None:
263 self.bad = badfn
264 self.bad = badfn
264 self._relativeuipath = relativeuipath
265 self._relativeuipath = relativeuipath
265
266
266 def __call__(self, fn):
267 def __call__(self, fn):
267 return self.matchfn(fn)
268 return self.matchfn(fn)
268 def __iter__(self):
269 def __iter__(self):
269 for f in self._files:
270 for f in self._files:
270 yield f
271 yield f
271 # Callbacks related to how the matcher is used by dirstate.walk.
272 # Callbacks related to how the matcher is used by dirstate.walk.
272 # Subscribers to these events must monkeypatch the matcher object.
273 # Subscribers to these events must monkeypatch the matcher object.
273 def bad(self, f, msg):
274 def bad(self, f, msg):
274 '''Callback from dirstate.walk for each explicit file that can't be
275 '''Callback from dirstate.walk for each explicit file that can't be
275 found/accessed, with an error message.'''
276 found/accessed, with an error message.'''
276
277
277 # If an explicitdir is set, it will be called when an explicitly listed
278 # If an explicitdir is set, it will be called when an explicitly listed
278 # directory is visited.
279 # directory is visited.
279 explicitdir = None
280 explicitdir = None
280
281
281 # If an traversedir is set, it will be called when a directory discovered
282 # If an traversedir is set, it will be called when a directory discovered
282 # by recursive traversal is visited.
283 # by recursive traversal is visited.
283 traversedir = None
284 traversedir = None
284
285
285 def abs(self, f):
286 def abs(self, f):
286 '''Convert a repo path back to path that is relative to the root of the
287 '''Convert a repo path back to path that is relative to the root of the
287 matcher.'''
288 matcher.'''
288 return f
289 return f
289
290
290 def rel(self, f):
291 def rel(self, f):
291 '''Convert repo path back to path that is relative to cwd of matcher.'''
292 '''Convert repo path back to path that is relative to cwd of matcher.'''
292 return util.pathto(self._root, self._cwd, f)
293 return util.pathto(self._root, self._cwd, f)
293
294
294 def uipath(self, f):
295 def uipath(self, f):
295 '''Convert repo path to a display path. If patterns or -I/-X were used
296 '''Convert repo path to a display path. If patterns or -I/-X were used
296 to create this matcher, the display path will be relative to cwd.
297 to create this matcher, the display path will be relative to cwd.
297 Otherwise it is relative to the root of the repo.'''
298 Otherwise it is relative to the root of the repo.'''
298 return (self._relativeuipath and self.rel(f)) or self.abs(f)
299 return (self._relativeuipath and self.rel(f)) or self.abs(f)
299
300
300 @propertycache
301 @propertycache
301 def _files(self):
302 def _files(self):
302 return []
303 return []
303
304
304 def files(self):
305 def files(self):
305 '''Explicitly listed files or patterns or roots:
306 '''Explicitly listed files or patterns or roots:
306 if no patterns or .always(): empty list,
307 if no patterns or .always(): empty list,
307 if exact: list exact files,
308 if exact: list exact files,
308 if not .anypats(): list all files and dirs,
309 if not .anypats(): list all files and dirs,
309 else: optimal roots'''
310 else: optimal roots'''
310 return self._files
311 return self._files
311
312
312 @propertycache
313 @propertycache
313 def _fileset(self):
314 def _fileset(self):
314 return set(self._files)
315 return set(self._files)
315
316
316 def exact(self, f):
317 def exact(self, f):
317 '''Returns True if f is in .files().'''
318 '''Returns True if f is in .files().'''
318 return f in self._fileset
319 return f in self._fileset
319
320
320 def matchfn(self, f):
321 def matchfn(self, f):
321 return False
322 return False
322
323
323 def visitdir(self, dir):
324 def visitdir(self, dir):
324 '''Decides whether a directory should be visited based on whether it
325 '''Decides whether a directory should be visited based on whether it
325 has potential matches in it or one of its subdirectories. This is
326 has potential matches in it or one of its subdirectories. This is
326 based on the match's primary, included, and excluded patterns.
327 based on the match's primary, included, and excluded patterns.
327
328
328 Returns the string 'all' if the given directory and all subdirectories
329 Returns the string 'all' if the given directory and all subdirectories
329 should be visited. Otherwise returns True or False indicating whether
330 should be visited. Otherwise returns True or False indicating whether
330 the given directory should be visited.
331 the given directory should be visited.
331 '''
332 '''
332 return True
333 return True
333
334
335 def visitchildrenset(self, dir):
336 '''Decides whether a directory should be visited based on whether it
337 has potential matches in it or one of its subdirectories, and
338 potentially lists which subdirectories of that directory should be
339 visited. This is based on the match's primary, included, and excluded
340 patterns.
341
342 This function is very similar to 'visitdir', and the following mapping
343 can be applied:
344
345 visitdir | visitchildrenlist
346 ----------+-------------------
347 False | set()
348 'all' | 'all'
349 True | 'this' OR non-empty set of subdirs to visit
350
351 Example:
352 Assume matchers ['path:foo/bar', 'rootfilesin:qux'], we would return
353 the following values (assuming the implementation of visitchildrenset
354 is capable of recognizing this; some implementations are not).
355
356 '.' -> {'foo', 'qux'}
357 'baz' -> set()
358 'foo' -> {'bar'}
359 # Ideally this would be 'all', but since the prefix nature of matchers
360 # is applied to the entire matcher, we have to downgrade to this
361 # 'this' due to the non-prefix 'rootfilesin'-kind matcher.
362 'foo/bar' -> 'this'
363 'qux' -> 'this'
364 '''
365 return 'this'
366
334 def always(self):
367 def always(self):
335 '''Matcher will match everything and .files() will be empty --
368 '''Matcher will match everything and .files() will be empty --
336 optimization might be possible.'''
369 optimization might be possible.'''
337 return False
370 return False
338
371
339 def isexact(self):
372 def isexact(self):
340 '''Matcher will match exactly the list of files in .files() --
373 '''Matcher will match exactly the list of files in .files() --
341 optimization might be possible.'''
374 optimization might be possible.'''
342 return False
375 return False
343
376
344 def prefix(self):
377 def prefix(self):
345 '''Matcher will match the paths in .files() recursively --
378 '''Matcher will match the paths in .files() recursively --
346 optimization might be possible.'''
379 optimization might be possible.'''
347 return False
380 return False
348
381
349 def anypats(self):
382 def anypats(self):
350 '''None of .always(), .isexact(), and .prefix() is true --
383 '''None of .always(), .isexact(), and .prefix() is true --
351 optimizations will be difficult.'''
384 optimizations will be difficult.'''
352 return not self.always() and not self.isexact() and not self.prefix()
385 return not self.always() and not self.isexact() and not self.prefix()
353
386
354 class alwaysmatcher(basematcher):
387 class alwaysmatcher(basematcher):
355 '''Matches everything.'''
388 '''Matches everything.'''
356
389
357 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
390 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
358 super(alwaysmatcher, self).__init__(root, cwd, badfn,
391 super(alwaysmatcher, self).__init__(root, cwd, badfn,
359 relativeuipath=relativeuipath)
392 relativeuipath=relativeuipath)
360
393
361 def always(self):
394 def always(self):
362 return True
395 return True
363
396
364 def matchfn(self, f):
397 def matchfn(self, f):
365 return True
398 return True
366
399
367 def visitdir(self, dir):
400 def visitdir(self, dir):
368 return 'all'
401 return 'all'
369
402
403 def visitchildrenset(self, dir):
404 return 'all'
405
370 def __repr__(self):
406 def __repr__(self):
371 return r'<alwaysmatcher>'
407 return r'<alwaysmatcher>'
372
408
373 class nevermatcher(basematcher):
409 class nevermatcher(basematcher):
374 '''Matches nothing.'''
410 '''Matches nothing.'''
375
411
376 def __init__(self, root, cwd, badfn=None):
412 def __init__(self, root, cwd, badfn=None):
377 super(nevermatcher, self).__init__(root, cwd, badfn)
413 super(nevermatcher, self).__init__(root, cwd, badfn)
378
414
379 # It's a little weird to say that the nevermatcher is an exact matcher
415 # It's a little weird to say that the nevermatcher is an exact matcher
380 # or a prefix matcher, but it seems to make sense to let callers take
416 # or a prefix matcher, but it seems to make sense to let callers take
381 # fast paths based on either. There will be no exact matches, nor any
417 # fast paths based on either. There will be no exact matches, nor any
382 # prefixes (files() returns []), so fast paths iterating over them should
418 # prefixes (files() returns []), so fast paths iterating over them should
383 # be efficient (and correct).
419 # be efficient (and correct).
384 def isexact(self):
420 def isexact(self):
385 return True
421 return True
386
422
387 def prefix(self):
423 def prefix(self):
388 return True
424 return True
389
425
390 def visitdir(self, dir):
426 def visitdir(self, dir):
391 return False
427 return False
392
428
429 def visitchildrenset(self, dir):
430 return set()
431
393 def __repr__(self):
432 def __repr__(self):
394 return r'<nevermatcher>'
433 return r'<nevermatcher>'
395
434
396 class predicatematcher(basematcher):
435 class predicatematcher(basematcher):
397 """A matcher adapter for a simple boolean function"""
436 """A matcher adapter for a simple boolean function"""
398
437
399 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
438 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
400 super(predicatematcher, self).__init__(root, cwd, badfn)
439 super(predicatematcher, self).__init__(root, cwd, badfn)
401 self.matchfn = predfn
440 self.matchfn = predfn
402 self._predrepr = predrepr
441 self._predrepr = predrepr
403
442
404 @encoding.strmethod
443 @encoding.strmethod
405 def __repr__(self):
444 def __repr__(self):
406 s = (stringutil.buildrepr(self._predrepr)
445 s = (stringutil.buildrepr(self._predrepr)
407 or pycompat.byterepr(self.matchfn))
446 or pycompat.byterepr(self.matchfn))
408 return '<predicatenmatcher pred=%s>' % s
447 return '<predicatenmatcher pred=%s>' % s
409
448
410 class patternmatcher(basematcher):
449 class patternmatcher(basematcher):
411
450
412 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
451 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
413 super(patternmatcher, self).__init__(root, cwd, badfn)
452 super(patternmatcher, self).__init__(root, cwd, badfn)
414
453
415 self._files = _explicitfiles(kindpats)
454 self._files = _explicitfiles(kindpats)
416 self._prefix = _prefix(kindpats)
455 self._prefix = _prefix(kindpats)
417 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
456 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
418 root)
457 root)
419
458
420 @propertycache
459 @propertycache
421 def _dirs(self):
460 def _dirs(self):
422 return set(util.dirs(self._fileset)) | {'.'}
461 return set(util.dirs(self._fileset)) | {'.'}
423
462
424 def visitdir(self, dir):
463 def visitdir(self, dir):
425 if self._prefix and dir in self._fileset:
464 if self._prefix and dir in self._fileset:
426 return 'all'
465 return 'all'
427 return ('.' in self._fileset or
466 return ('.' in self._fileset or
428 dir in self._fileset or
467 dir in self._fileset or
429 dir in self._dirs or
468 dir in self._dirs or
430 any(parentdir in self._fileset
469 any(parentdir in self._fileset
431 for parentdir in util.finddirs(dir)))
470 for parentdir in util.finddirs(dir)))
432
471
472 def visitchildrenset(self, dir):
473 ret = self.visitdir(dir)
474 if ret is True:
475 return 'this'
476 elif not ret:
477 return set()
478 assert ret == 'all'
479 return 'all'
480
433 def prefix(self):
481 def prefix(self):
434 return self._prefix
482 return self._prefix
435
483
436 @encoding.strmethod
484 @encoding.strmethod
437 def __repr__(self):
485 def __repr__(self):
438 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
486 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
439
487
440 class includematcher(basematcher):
488 class includematcher(basematcher):
441
489
442 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
490 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
443 super(includematcher, self).__init__(root, cwd, badfn)
491 super(includematcher, self).__init__(root, cwd, badfn)
444
492
445 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
493 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
446 listsubrepos, root)
494 listsubrepos, root)
447 self._prefix = _prefix(kindpats)
495 self._prefix = _prefix(kindpats)
448 roots, dirs, parents = _rootsdirsandparents(kindpats)
496 roots, dirs, parents = _rootsdirsandparents(kindpats)
449 # roots are directories which are recursively included.
497 # roots are directories which are recursively included.
450 self._roots = set(roots)
498 self._roots = set(roots)
451 # dirs are directories which are non-recursively included.
499 # dirs are directories which are non-recursively included.
452 self._dirs = set(dirs)
500 self._dirs = set(dirs)
453 # parents are directories which are non-recursively included because
501 # parents are directories which are non-recursively included because
454 # they are needed to get to items in _dirs or _roots.
502 # they are needed to get to items in _dirs or _roots.
455 self._parents = set(parents)
503 self._parents = set(parents)
456
504
457 def visitdir(self, dir):
505 def visitdir(self, dir):
458 if self._prefix and dir in self._roots:
506 if self._prefix and dir in self._roots:
459 return 'all'
507 return 'all'
460 return ('.' in self._roots or
508 return ('.' in self._roots or
461 dir in self._roots or
509 dir in self._roots or
462 dir in self._dirs or
510 dir in self._dirs or
463 dir in self._parents or
511 dir in self._parents or
464 any(parentdir in self._roots
512 any(parentdir in self._roots
465 for parentdir in util.finddirs(dir)))
513 for parentdir in util.finddirs(dir)))
466
514
515 def visitchildrenset(self, dir):
516 if self._prefix and dir in self._roots:
517 return 'all'
518 # Note: this does *not* include the 'dir in self._parents' case from
519 # visitdir, that's handled below.
520 if ('.' in self._roots or
521 dir in self._roots or
522 dir in self._dirs or
523 any(parentdir in self._roots
524 for parentdir in util.finddirs(dir))):
525 return 'this'
526
527 ret = set()
528 if dir in self._parents:
529 # We add a '/' on to `dir` so that we don't return items that are
530 # prefixed by `dir` but are actually siblings of `dir`.
531 suffixeddir = dir + '/' if dir != '.' else ''
532 # Look in all _roots, _dirs, and _parents for things that start with
533 # 'suffixeddir'.
534 for d in [q for q in
535 itertools.chain(self._roots, self._dirs, self._parents) if
536 q.startswith(suffixeddir)]:
537 # Don't emit '.' in the response for the root directory
538 if not suffixeddir and d == '.':
539 continue
540
541 # We return the item name without the `suffixeddir` prefix or a
542 # slash suffix
543 d = d[len(suffixeddir):]
544 if '/' in d:
545 # This is a subdirectory-of-a-subdirectory, i.e.
546 # suffixeddir='foo/', d was 'foo/bar/baz' before removing
547 # 'foo/'.
548 d = d[:d.index('/')]
549 ret.add(d)
550 return ret
551
467 @encoding.strmethod
552 @encoding.strmethod
468 def __repr__(self):
553 def __repr__(self):
469 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
554 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
470
555
471 class exactmatcher(basematcher):
556 class exactmatcher(basematcher):
472 '''Matches the input files exactly. They are interpreted as paths, not
557 '''Matches the input files exactly. They are interpreted as paths, not
473 patterns (so no kind-prefixes).
558 patterns (so no kind-prefixes).
474 '''
559 '''
475
560
476 def __init__(self, root, cwd, files, badfn=None):
561 def __init__(self, root, cwd, files, badfn=None):
477 super(exactmatcher, self).__init__(root, cwd, badfn)
562 super(exactmatcher, self).__init__(root, cwd, badfn)
478
563
479 if isinstance(files, list):
564 if isinstance(files, list):
480 self._files = files
565 self._files = files
481 else:
566 else:
482 self._files = list(files)
567 self._files = list(files)
483
568
484 matchfn = basematcher.exact
569 matchfn = basematcher.exact
485
570
486 @propertycache
571 @propertycache
487 def _dirs(self):
572 def _dirs(self):
488 return set(util.dirs(self._fileset)) | {'.'}
573 return set(util.dirs(self._fileset)) | {'.'}
489
574
490 def visitdir(self, dir):
575 def visitdir(self, dir):
491 return dir in self._dirs
576 return dir in self._dirs
492
577
578 def visitchildrenset(self, dir):
579 if dir in self._dirs:
580 candidates = self._dirs - {'.'}
581 if dir != '.':
582 d = dir + '/'
583 candidates = set(c[len(d):] for c in candidates if
584 c.startswith(d))
585 # self._dirs includes all of the directories, recursively, so if
586 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo',
587 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
588 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
589 # immediate subdir will be in there without a slash.
590 ret = set(c for c in candidates if '/' not in c)
591 # We need to emit 'this' for foo/bar, not set(), not {'baz.txt'}.
592 if not ret:
593 return 'this'
594 return ret
595 return set()
596
493 def isexact(self):
597 def isexact(self):
494 return True
598 return True
495
599
496 @encoding.strmethod
600 @encoding.strmethod
497 def __repr__(self):
601 def __repr__(self):
498 return ('<exactmatcher files=%r>' % self._files)
602 return ('<exactmatcher files=%r>' % self._files)
499
603
500 class differencematcher(basematcher):
604 class differencematcher(basematcher):
501 '''Composes two matchers by matching if the first matches and the second
605 '''Composes two matchers by matching if the first matches and the second
502 does not.
606 does not.
503
607
504 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
608 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
505 traversedir) are ignored.
609 traversedir) are ignored.
506 '''
610 '''
507 def __init__(self, m1, m2):
611 def __init__(self, m1, m2):
508 super(differencematcher, self).__init__(m1._root, m1._cwd)
612 super(differencematcher, self).__init__(m1._root, m1._cwd)
509 self._m1 = m1
613 self._m1 = m1
510 self._m2 = m2
614 self._m2 = m2
511 self.bad = m1.bad
615 self.bad = m1.bad
512 self.explicitdir = m1.explicitdir
616 self.explicitdir = m1.explicitdir
513 self.traversedir = m1.traversedir
617 self.traversedir = m1.traversedir
514
618
515 def matchfn(self, f):
619 def matchfn(self, f):
516 return self._m1(f) and not self._m2(f)
620 return self._m1(f) and not self._m2(f)
517
621
518 @propertycache
622 @propertycache
519 def _files(self):
623 def _files(self):
520 if self.isexact():
624 if self.isexact():
521 return [f for f in self._m1.files() if self(f)]
625 return [f for f in self._m1.files() if self(f)]
522 # If m1 is not an exact matcher, we can't easily figure out the set of
626 # If m1 is not an exact matcher, we can't easily figure out the set of
523 # files, because its files() are not always files. For example, if
627 # files, because its files() are not always files. For example, if
524 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
628 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
525 # want to remove "dir" from the set even though it would match m2,
629 # want to remove "dir" from the set even though it would match m2,
526 # because the "dir" in m1 may not be a file.
630 # because the "dir" in m1 may not be a file.
527 return self._m1.files()
631 return self._m1.files()
528
632
529 def visitdir(self, dir):
633 def visitdir(self, dir):
530 if self._m2.visitdir(dir) == 'all':
634 if self._m2.visitdir(dir) == 'all':
531 return False
635 return False
532 return bool(self._m1.visitdir(dir))
636 return bool(self._m1.visitdir(dir))
533
637
638 def visitchildrenset(self, dir):
639 m2_set = self._m2.visitchildrenset(dir)
640 if m2_set == 'all':
641 return set()
642 m1_set = self._m1.visitchildrenset(dir)
643 # Possible values for m1: 'all', 'this', set(...), set()
644 # Possible values for m2: 'this', set(...), set()
645 # If m2 has nothing under here that we care about, return m1, even if
646 # it's 'all'. This is a change in behavior from visitdir, which would
647 # return True, not 'all', for some reason.
648 if not m2_set:
649 return m1_set
650 if m1_set in ['all', 'this']:
651 # Never return 'all' here if m2_set is any kind of non-empty (either
652 # 'this' or set(foo)), since m2 might return set() for a
653 # subdirectory.
654 return 'this'
655 # Possible values for m1: set(...), set()
656 # Possible values for m2: 'this', set(...)
657 # We ignore m2's set results. They're possibly incorrect:
658 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'):
659 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd
660 # return set(), which is *not* correct, we still need to visit 'dir'!
661 return m1_set
662
534 def isexact(self):
663 def isexact(self):
535 return self._m1.isexact()
664 return self._m1.isexact()
536
665
537 @encoding.strmethod
666 @encoding.strmethod
538 def __repr__(self):
667 def __repr__(self):
539 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
668 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
540
669
541 def intersectmatchers(m1, m2):
670 def intersectmatchers(m1, m2):
542 '''Composes two matchers by matching if both of them match.
671 '''Composes two matchers by matching if both of them match.
543
672
544 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
673 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
545 traversedir) are ignored.
674 traversedir) are ignored.
546 '''
675 '''
547 if m1 is None or m2 is None:
676 if m1 is None or m2 is None:
548 return m1 or m2
677 return m1 or m2
549 if m1.always():
678 if m1.always():
550 m = copy.copy(m2)
679 m = copy.copy(m2)
551 # TODO: Consider encapsulating these things in a class so there's only
680 # TODO: Consider encapsulating these things in a class so there's only
552 # one thing to copy from m1.
681 # one thing to copy from m1.
553 m.bad = m1.bad
682 m.bad = m1.bad
554 m.explicitdir = m1.explicitdir
683 m.explicitdir = m1.explicitdir
555 m.traversedir = m1.traversedir
684 m.traversedir = m1.traversedir
556 m.abs = m1.abs
685 m.abs = m1.abs
557 m.rel = m1.rel
686 m.rel = m1.rel
558 m._relativeuipath |= m1._relativeuipath
687 m._relativeuipath |= m1._relativeuipath
559 return m
688 return m
560 if m2.always():
689 if m2.always():
561 m = copy.copy(m1)
690 m = copy.copy(m1)
562 m._relativeuipath |= m2._relativeuipath
691 m._relativeuipath |= m2._relativeuipath
563 return m
692 return m
564 return intersectionmatcher(m1, m2)
693 return intersectionmatcher(m1, m2)
565
694
566 class intersectionmatcher(basematcher):
695 class intersectionmatcher(basematcher):
567 def __init__(self, m1, m2):
696 def __init__(self, m1, m2):
568 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
697 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
569 self._m1 = m1
698 self._m1 = m1
570 self._m2 = m2
699 self._m2 = m2
571 self.bad = m1.bad
700 self.bad = m1.bad
572 self.explicitdir = m1.explicitdir
701 self.explicitdir = m1.explicitdir
573 self.traversedir = m1.traversedir
702 self.traversedir = m1.traversedir
574
703
575 @propertycache
704 @propertycache
576 def _files(self):
705 def _files(self):
577 if self.isexact():
706 if self.isexact():
578 m1, m2 = self._m1, self._m2
707 m1, m2 = self._m1, self._m2
579 if not m1.isexact():
708 if not m1.isexact():
580 m1, m2 = m2, m1
709 m1, m2 = m2, m1
581 return [f for f in m1.files() if m2(f)]
710 return [f for f in m1.files() if m2(f)]
582 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
711 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
583 # the set of files, because their files() are not always files. For
712 # the set of files, because their files() are not always files. For
584 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
713 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
585 # "path:dir2", we don't want to remove "dir2" from the set.
714 # "path:dir2", we don't want to remove "dir2" from the set.
586 return self._m1.files() + self._m2.files()
715 return self._m1.files() + self._m2.files()
587
716
588 def matchfn(self, f):
717 def matchfn(self, f):
589 return self._m1(f) and self._m2(f)
718 return self._m1(f) and self._m2(f)
590
719
591 def visitdir(self, dir):
720 def visitdir(self, dir):
592 visit1 = self._m1.visitdir(dir)
721 visit1 = self._m1.visitdir(dir)
593 if visit1 == 'all':
722 if visit1 == 'all':
594 return self._m2.visitdir(dir)
723 return self._m2.visitdir(dir)
595 # bool() because visit1=True + visit2='all' should not be 'all'
724 # bool() because visit1=True + visit2='all' should not be 'all'
596 return bool(visit1 and self._m2.visitdir(dir))
725 return bool(visit1 and self._m2.visitdir(dir))
597
726
727 def visitchildrenset(self, dir):
728 m1_set = self._m1.visitchildrenset(dir)
729 if not m1_set:
730 return set()
731 m2_set = self._m2.visitchildrenset(dir)
732 if not m2_set:
733 return set()
734
735 if m1_set == 'all':
736 return m2_set
737 elif m2_set == 'all':
738 return m1_set
739
740 if m1_set == 'this' or m2_set == 'this':
741 return 'this'
742
743 assert isinstance(m1_set, set) and isinstance(m2_set, set)
744 return m1_set.intersection(m2_set)
745
598 def always(self):
746 def always(self):
599 return self._m1.always() and self._m2.always()
747 return self._m1.always() and self._m2.always()
600
748
601 def isexact(self):
749 def isexact(self):
602 return self._m1.isexact() or self._m2.isexact()
750 return self._m1.isexact() or self._m2.isexact()
603
751
604 @encoding.strmethod
752 @encoding.strmethod
605 def __repr__(self):
753 def __repr__(self):
606 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
754 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
607
755
608 class subdirmatcher(basematcher):
756 class subdirmatcher(basematcher):
609 """Adapt a matcher to work on a subdirectory only.
757 """Adapt a matcher to work on a subdirectory only.
610
758
611 The paths are remapped to remove/insert the path as needed:
759 The paths are remapped to remove/insert the path as needed:
612
760
613 >>> from . import pycompat
761 >>> from . import pycompat
614 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
762 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
615 >>> m2 = subdirmatcher(b'sub', m1)
763 >>> m2 = subdirmatcher(b'sub', m1)
616 >>> bool(m2(b'a.txt'))
764 >>> bool(m2(b'a.txt'))
617 False
765 False
618 >>> bool(m2(b'b.txt'))
766 >>> bool(m2(b'b.txt'))
619 True
767 True
620 >>> bool(m2.matchfn(b'a.txt'))
768 >>> bool(m2.matchfn(b'a.txt'))
621 False
769 False
622 >>> bool(m2.matchfn(b'b.txt'))
770 >>> bool(m2.matchfn(b'b.txt'))
623 True
771 True
624 >>> m2.files()
772 >>> m2.files()
625 ['b.txt']
773 ['b.txt']
626 >>> m2.exact(b'b.txt')
774 >>> m2.exact(b'b.txt')
627 True
775 True
628 >>> util.pconvert(m2.rel(b'b.txt'))
776 >>> util.pconvert(m2.rel(b'b.txt'))
629 'sub/b.txt'
777 'sub/b.txt'
630 >>> def bad(f, msg):
778 >>> def bad(f, msg):
631 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
779 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
632 >>> m1.bad = bad
780 >>> m1.bad = bad
633 >>> m2.bad(b'x.txt', b'No such file')
781 >>> m2.bad(b'x.txt', b'No such file')
634 sub/x.txt: No such file
782 sub/x.txt: No such file
635 >>> m2.abs(b'c.txt')
783 >>> m2.abs(b'c.txt')
636 'sub/c.txt'
784 'sub/c.txt'
637 """
785 """
638
786
639 def __init__(self, path, matcher):
787 def __init__(self, path, matcher):
640 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
788 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
641 self._path = path
789 self._path = path
642 self._matcher = matcher
790 self._matcher = matcher
643 self._always = matcher.always()
791 self._always = matcher.always()
644
792
645 self._files = [f[len(path) + 1:] for f in matcher._files
793 self._files = [f[len(path) + 1:] for f in matcher._files
646 if f.startswith(path + "/")]
794 if f.startswith(path + "/")]
647
795
648 # If the parent repo had a path to this subrepo and the matcher is
796 # If the parent repo had a path to this subrepo and the matcher is
649 # a prefix matcher, this submatcher always matches.
797 # a prefix matcher, this submatcher always matches.
650 if matcher.prefix():
798 if matcher.prefix():
651 self._always = any(f == path for f in matcher._files)
799 self._always = any(f == path for f in matcher._files)
652
800
653 def bad(self, f, msg):
801 def bad(self, f, msg):
654 self._matcher.bad(self._path + "/" + f, msg)
802 self._matcher.bad(self._path + "/" + f, msg)
655
803
656 def abs(self, f):
804 def abs(self, f):
657 return self._matcher.abs(self._path + "/" + f)
805 return self._matcher.abs(self._path + "/" + f)
658
806
659 def rel(self, f):
807 def rel(self, f):
660 return self._matcher.rel(self._path + "/" + f)
808 return self._matcher.rel(self._path + "/" + f)
661
809
662 def uipath(self, f):
810 def uipath(self, f):
663 return self._matcher.uipath(self._path + "/" + f)
811 return self._matcher.uipath(self._path + "/" + f)
664
812
665 def matchfn(self, f):
813 def matchfn(self, f):
666 # Some information is lost in the superclass's constructor, so we
814 # Some information is lost in the superclass's constructor, so we
667 # can not accurately create the matching function for the subdirectory
815 # can not accurately create the matching function for the subdirectory
668 # from the inputs. Instead, we override matchfn() and visitdir() to
816 # from the inputs. Instead, we override matchfn() and visitdir() to
669 # call the original matcher with the subdirectory path prepended.
817 # call the original matcher with the subdirectory path prepended.
670 return self._matcher.matchfn(self._path + "/" + f)
818 return self._matcher.matchfn(self._path + "/" + f)
671
819
672 def visitdir(self, dir):
820 def visitdir(self, dir):
673 if dir == '.':
821 if dir == '.':
674 dir = self._path
822 dir = self._path
675 else:
823 else:
676 dir = self._path + "/" + dir
824 dir = self._path + "/" + dir
677 return self._matcher.visitdir(dir)
825 return self._matcher.visitdir(dir)
678
826
827 def visitchildrenset(self, dir):
828 if dir == '.':
829 dir = self._path
830 else:
831 dir = self._path + "/" + dir
832 return self._matcher.visitchildrenset(dir)
833
679 def always(self):
834 def always(self):
680 return self._always
835 return self._always
681
836
682 def prefix(self):
837 def prefix(self):
683 return self._matcher.prefix() and not self._always
838 return self._matcher.prefix() and not self._always
684
839
685 @encoding.strmethod
840 @encoding.strmethod
686 def __repr__(self):
841 def __repr__(self):
687 return ('<subdirmatcher path=%r, matcher=%r>' %
842 return ('<subdirmatcher path=%r, matcher=%r>' %
688 (self._path, self._matcher))
843 (self._path, self._matcher))
689
844
690 class prefixdirmatcher(basematcher):
845 class prefixdirmatcher(basematcher):
691 """Adapt a matcher to work on a parent directory.
846 """Adapt a matcher to work on a parent directory.
692
847
693 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
848 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
694 traversedir) are ignored.
849 traversedir) are ignored.
695
850
696 The prefix path should usually be the relative path from the root of
851 The prefix path should usually be the relative path from the root of
697 this matcher to the root of the wrapped matcher.
852 this matcher to the root of the wrapped matcher.
698
853
699 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
854 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
700 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
855 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
701 >>> bool(m2(b'a.txt'),)
856 >>> bool(m2(b'a.txt'),)
702 False
857 False
703 >>> bool(m2(b'd/e/a.txt'))
858 >>> bool(m2(b'd/e/a.txt'))
704 True
859 True
705 >>> bool(m2(b'd/e/b.txt'))
860 >>> bool(m2(b'd/e/b.txt'))
706 False
861 False
707 >>> m2.files()
862 >>> m2.files()
708 ['d/e/a.txt', 'd/e/f/b.txt']
863 ['d/e/a.txt', 'd/e/f/b.txt']
709 >>> m2.exact(b'd/e/a.txt')
864 >>> m2.exact(b'd/e/a.txt')
710 True
865 True
711 >>> m2.visitdir(b'd')
866 >>> m2.visitdir(b'd')
712 True
867 True
713 >>> m2.visitdir(b'd/e')
868 >>> m2.visitdir(b'd/e')
714 True
869 True
715 >>> m2.visitdir(b'd/e/f')
870 >>> m2.visitdir(b'd/e/f')
716 True
871 True
717 >>> m2.visitdir(b'd/e/g')
872 >>> m2.visitdir(b'd/e/g')
718 False
873 False
719 >>> m2.visitdir(b'd/ef')
874 >>> m2.visitdir(b'd/ef')
720 False
875 False
721 """
876 """
722
877
723 def __init__(self, root, cwd, path, matcher, badfn=None):
878 def __init__(self, root, cwd, path, matcher, badfn=None):
724 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
879 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
725 if not path:
880 if not path:
726 raise error.ProgrammingError('prefix path must not be empty')
881 raise error.ProgrammingError('prefix path must not be empty')
727 self._path = path
882 self._path = path
728 self._pathprefix = path + '/'
883 self._pathprefix = path + '/'
729 self._matcher = matcher
884 self._matcher = matcher
730
885
731 @propertycache
886 @propertycache
732 def _files(self):
887 def _files(self):
733 return [self._pathprefix + f for f in self._matcher._files]
888 return [self._pathprefix + f for f in self._matcher._files]
734
889
735 def matchfn(self, f):
890 def matchfn(self, f):
736 if not f.startswith(self._pathprefix):
891 if not f.startswith(self._pathprefix):
737 return False
892 return False
738 return self._matcher.matchfn(f[len(self._pathprefix):])
893 return self._matcher.matchfn(f[len(self._pathprefix):])
739
894
740 @propertycache
895 @propertycache
741 def _pathdirs(self):
896 def _pathdirs(self):
742 return set(util.finddirs(self._path)) | {'.'}
897 return set(util.finddirs(self._path)) | {'.'}
743
898
744 def visitdir(self, dir):
899 def visitdir(self, dir):
745 if dir == self._path:
900 if dir == self._path:
746 return self._matcher.visitdir('.')
901 return self._matcher.visitdir('.')
747 if dir.startswith(self._pathprefix):
902 if dir.startswith(self._pathprefix):
748 return self._matcher.visitdir(dir[len(self._pathprefix):])
903 return self._matcher.visitdir(dir[len(self._pathprefix):])
749 return dir in self._pathdirs
904 return dir in self._pathdirs
750
905
906 def visitchildrenset(self, dir):
907 if dir == self._path:
908 return self._matcher.visitchildrenset('.')
909 if dir.startswith(self._pathprefix):
910 return self._matcher.visitchildrenset(dir[len(self._pathprefix):])
911 if dir in self._pathdirs:
912 return 'this'
913
751 def isexact(self):
914 def isexact(self):
752 return self._matcher.isexact()
915 return self._matcher.isexact()
753
916
754 def prefix(self):
917 def prefix(self):
755 return self._matcher.prefix()
918 return self._matcher.prefix()
756
919
757 @encoding.strmethod
920 @encoding.strmethod
758 def __repr__(self):
921 def __repr__(self):
759 return ('<prefixdirmatcher path=%r, matcher=%r>'
922 return ('<prefixdirmatcher path=%r, matcher=%r>'
760 % (pycompat.bytestr(self._path), self._matcher))
923 % (pycompat.bytestr(self._path), self._matcher))
761
924
762 class unionmatcher(basematcher):
925 class unionmatcher(basematcher):
763 """A matcher that is the union of several matchers.
926 """A matcher that is the union of several matchers.
764
927
765 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
928 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
766 taken from the first matcher.
929 taken from the first matcher.
767 """
930 """
768
931
769 def __init__(self, matchers):
932 def __init__(self, matchers):
770 m1 = matchers[0]
933 m1 = matchers[0]
771 super(unionmatcher, self).__init__(m1._root, m1._cwd)
934 super(unionmatcher, self).__init__(m1._root, m1._cwd)
772 self.explicitdir = m1.explicitdir
935 self.explicitdir = m1.explicitdir
773 self.traversedir = m1.traversedir
936 self.traversedir = m1.traversedir
774 self._matchers = matchers
937 self._matchers = matchers
775
938
776 def matchfn(self, f):
939 def matchfn(self, f):
777 for match in self._matchers:
940 for match in self._matchers:
778 if match(f):
941 if match(f):
779 return True
942 return True
780 return False
943 return False
781
944
782 def visitdir(self, dir):
945 def visitdir(self, dir):
783 r = False
946 r = False
784 for m in self._matchers:
947 for m in self._matchers:
785 v = m.visitdir(dir)
948 v = m.visitdir(dir)
786 if v == 'all':
949 if v == 'all':
787 return v
950 return v
788 r |= v
951 r |= v
789 return r
952 return r
790
953
954 def visitchildrenset(self, dir):
955 r = set()
956 this = False
957 for m in self._matchers:
958 v = m.visitchildrenset(dir)
959 if not v:
960 continue
961 if v == 'all':
962 return v
963 if this or v == 'this':
964 this = True
965 # don't break, we might have an 'all' in here.
966 continue
967 assert isinstance(v, set)
968 r = r.union(v)
969 if this:
970 return 'this'
971 return r
972
791 @encoding.strmethod
973 @encoding.strmethod
792 def __repr__(self):
974 def __repr__(self):
793 return ('<unionmatcher matchers=%r>' % self._matchers)
975 return ('<unionmatcher matchers=%r>' % self._matchers)
794
976
795 def patkind(pattern, default=None):
977 def patkind(pattern, default=None):
796 '''If pattern is 'kind:pat' with a known kind, return kind.'''
978 '''If pattern is 'kind:pat' with a known kind, return kind.'''
797 return _patsplit(pattern, default)[0]
979 return _patsplit(pattern, default)[0]
798
980
799 def _patsplit(pattern, default):
981 def _patsplit(pattern, default):
800 """Split a string into the optional pattern kind prefix and the actual
982 """Split a string into the optional pattern kind prefix and the actual
801 pattern."""
983 pattern."""
802 if ':' in pattern:
984 if ':' in pattern:
803 kind, pat = pattern.split(':', 1)
985 kind, pat = pattern.split(':', 1)
804 if kind in allpatternkinds:
986 if kind in allpatternkinds:
805 return kind, pat
987 return kind, pat
806 return default, pattern
988 return default, pattern
807
989
808 def _globre(pat):
990 def _globre(pat):
809 r'''Convert an extended glob string to a regexp string.
991 r'''Convert an extended glob string to a regexp string.
810
992
811 >>> from . import pycompat
993 >>> from . import pycompat
812 >>> def bprint(s):
994 >>> def bprint(s):
813 ... print(pycompat.sysstr(s))
995 ... print(pycompat.sysstr(s))
814 >>> bprint(_globre(br'?'))
996 >>> bprint(_globre(br'?'))
815 .
997 .
816 >>> bprint(_globre(br'*'))
998 >>> bprint(_globre(br'*'))
817 [^/]*
999 [^/]*
818 >>> bprint(_globre(br'**'))
1000 >>> bprint(_globre(br'**'))
819 .*
1001 .*
820 >>> bprint(_globre(br'**/a'))
1002 >>> bprint(_globre(br'**/a'))
821 (?:.*/)?a
1003 (?:.*/)?a
822 >>> bprint(_globre(br'a/**/b'))
1004 >>> bprint(_globre(br'a/**/b'))
823 a/(?:.*/)?b
1005 a/(?:.*/)?b
824 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
1006 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
825 [a*?!^][\^b][^c]
1007 [a*?!^][\^b][^c]
826 >>> bprint(_globre(br'{a,b}'))
1008 >>> bprint(_globre(br'{a,b}'))
827 (?:a|b)
1009 (?:a|b)
828 >>> bprint(_globre(br'.\*\?'))
1010 >>> bprint(_globre(br'.\*\?'))
829 \.\*\?
1011 \.\*\?
830 '''
1012 '''
831 i, n = 0, len(pat)
1013 i, n = 0, len(pat)
832 res = ''
1014 res = ''
833 group = 0
1015 group = 0
834 escape = util.stringutil.reescape
1016 escape = util.stringutil.reescape
835 def peek():
1017 def peek():
836 return i < n and pat[i:i + 1]
1018 return i < n and pat[i:i + 1]
837 while i < n:
1019 while i < n:
838 c = pat[i:i + 1]
1020 c = pat[i:i + 1]
839 i += 1
1021 i += 1
840 if c not in '*?[{},\\':
1022 if c not in '*?[{},\\':
841 res += escape(c)
1023 res += escape(c)
842 elif c == '*':
1024 elif c == '*':
843 if peek() == '*':
1025 if peek() == '*':
844 i += 1
1026 i += 1
845 if peek() == '/':
1027 if peek() == '/':
846 i += 1
1028 i += 1
847 res += '(?:.*/)?'
1029 res += '(?:.*/)?'
848 else:
1030 else:
849 res += '.*'
1031 res += '.*'
850 else:
1032 else:
851 res += '[^/]*'
1033 res += '[^/]*'
852 elif c == '?':
1034 elif c == '?':
853 res += '.'
1035 res += '.'
854 elif c == '[':
1036 elif c == '[':
855 j = i
1037 j = i
856 if j < n and pat[j:j + 1] in '!]':
1038 if j < n and pat[j:j + 1] in '!]':
857 j += 1
1039 j += 1
858 while j < n and pat[j:j + 1] != ']':
1040 while j < n and pat[j:j + 1] != ']':
859 j += 1
1041 j += 1
860 if j >= n:
1042 if j >= n:
861 res += '\\['
1043 res += '\\['
862 else:
1044 else:
863 stuff = pat[i:j].replace('\\','\\\\')
1045 stuff = pat[i:j].replace('\\','\\\\')
864 i = j + 1
1046 i = j + 1
865 if stuff[0:1] == '!':
1047 if stuff[0:1] == '!':
866 stuff = '^' + stuff[1:]
1048 stuff = '^' + stuff[1:]
867 elif stuff[0:1] == '^':
1049 elif stuff[0:1] == '^':
868 stuff = '\\' + stuff
1050 stuff = '\\' + stuff
869 res = '%s[%s]' % (res, stuff)
1051 res = '%s[%s]' % (res, stuff)
870 elif c == '{':
1052 elif c == '{':
871 group += 1
1053 group += 1
872 res += '(?:'
1054 res += '(?:'
873 elif c == '}' and group:
1055 elif c == '}' and group:
874 res += ')'
1056 res += ')'
875 group -= 1
1057 group -= 1
876 elif c == ',' and group:
1058 elif c == ',' and group:
877 res += '|'
1059 res += '|'
878 elif c == '\\':
1060 elif c == '\\':
879 p = peek()
1061 p = peek()
880 if p:
1062 if p:
881 i += 1
1063 i += 1
882 res += escape(p)
1064 res += escape(p)
883 else:
1065 else:
884 res += escape(c)
1066 res += escape(c)
885 else:
1067 else:
886 res += escape(c)
1068 res += escape(c)
887 return res
1069 return res
888
1070
889 def _regex(kind, pat, globsuffix):
1071 def _regex(kind, pat, globsuffix):
890 '''Convert a (normalized) pattern of any kind into a regular expression.
1072 '''Convert a (normalized) pattern of any kind into a regular expression.
891 globsuffix is appended to the regexp of globs.'''
1073 globsuffix is appended to the regexp of globs.'''
892 if not pat:
1074 if not pat:
893 return ''
1075 return ''
894 if kind == 're':
1076 if kind == 're':
895 return pat
1077 return pat
896 if kind in ('path', 'relpath'):
1078 if kind in ('path', 'relpath'):
897 if pat == '.':
1079 if pat == '.':
898 return ''
1080 return ''
899 return util.stringutil.reescape(pat) + '(?:/|$)'
1081 return util.stringutil.reescape(pat) + '(?:/|$)'
900 if kind == 'rootfilesin':
1082 if kind == 'rootfilesin':
901 if pat == '.':
1083 if pat == '.':
902 escaped = ''
1084 escaped = ''
903 else:
1085 else:
904 # Pattern is a directory name.
1086 # Pattern is a directory name.
905 escaped = util.stringutil.reescape(pat) + '/'
1087 escaped = util.stringutil.reescape(pat) + '/'
906 # Anything after the pattern must be a non-directory.
1088 # Anything after the pattern must be a non-directory.
907 return escaped + '[^/]+$'
1089 return escaped + '[^/]+$'
908 if kind == 'relglob':
1090 if kind == 'relglob':
909 return '(?:|.*/)' + _globre(pat) + globsuffix
1091 return '(?:|.*/)' + _globre(pat) + globsuffix
910 if kind == 'relre':
1092 if kind == 'relre':
911 if pat.startswith('^'):
1093 if pat.startswith('^'):
912 return pat
1094 return pat
913 return '.*' + pat
1095 return '.*' + pat
914 if kind == 'glob':
1096 if kind == 'glob':
915 return _globre(pat) + globsuffix
1097 return _globre(pat) + globsuffix
916 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1098 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
917
1099
918 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
1100 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
919 '''Return regexp string and a matcher function for kindpats.
1101 '''Return regexp string and a matcher function for kindpats.
920 globsuffix is appended to the regexp of globs.'''
1102 globsuffix is appended to the regexp of globs.'''
921 matchfuncs = []
1103 matchfuncs = []
922
1104
923 subincludes, kindpats = _expandsubinclude(kindpats, root)
1105 subincludes, kindpats = _expandsubinclude(kindpats, root)
924 if subincludes:
1106 if subincludes:
925 submatchers = {}
1107 submatchers = {}
926 def matchsubinclude(f):
1108 def matchsubinclude(f):
927 for prefix, matcherargs in subincludes:
1109 for prefix, matcherargs in subincludes:
928 if f.startswith(prefix):
1110 if f.startswith(prefix):
929 mf = submatchers.get(prefix)
1111 mf = submatchers.get(prefix)
930 if mf is None:
1112 if mf is None:
931 mf = match(*matcherargs)
1113 mf = match(*matcherargs)
932 submatchers[prefix] = mf
1114 submatchers[prefix] = mf
933
1115
934 if mf(f[len(prefix):]):
1116 if mf(f[len(prefix):]):
935 return True
1117 return True
936 return False
1118 return False
937 matchfuncs.append(matchsubinclude)
1119 matchfuncs.append(matchsubinclude)
938
1120
939 regex = ''
1121 regex = ''
940 if kindpats:
1122 if kindpats:
941 regex, mf = _buildregexmatch(kindpats, globsuffix)
1123 regex, mf = _buildregexmatch(kindpats, globsuffix)
942 matchfuncs.append(mf)
1124 matchfuncs.append(mf)
943
1125
944 if len(matchfuncs) == 1:
1126 if len(matchfuncs) == 1:
945 return regex, matchfuncs[0]
1127 return regex, matchfuncs[0]
946 else:
1128 else:
947 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1129 return regex, lambda f: any(mf(f) for mf in matchfuncs)
948
1130
949 def _buildregexmatch(kindpats, globsuffix):
1131 def _buildregexmatch(kindpats, globsuffix):
950 """Build a match function from a list of kinds and kindpats,
1132 """Build a match function from a list of kinds and kindpats,
951 return regexp string and a matcher function."""
1133 return regexp string and a matcher function."""
952 try:
1134 try:
953 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
1135 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
954 for (k, p, s) in kindpats])
1136 for (k, p, s) in kindpats])
955 if len(regex) > 20000:
1137 if len(regex) > 20000:
956 raise OverflowError
1138 raise OverflowError
957 return regex, _rematcher(regex)
1139 return regex, _rematcher(regex)
958 except OverflowError:
1140 except OverflowError:
959 # We're using a Python with a tiny regex engine and we
1141 # We're using a Python with a tiny regex engine and we
960 # made it explode, so we'll divide the pattern list in two
1142 # made it explode, so we'll divide the pattern list in two
961 # until it works
1143 # until it works
962 l = len(kindpats)
1144 l = len(kindpats)
963 if l < 2:
1145 if l < 2:
964 raise
1146 raise
965 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
1147 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
966 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
1148 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
967 return regex, lambda s: a(s) or b(s)
1149 return regex, lambda s: a(s) or b(s)
968 except re.error:
1150 except re.error:
969 for k, p, s in kindpats:
1151 for k, p, s in kindpats:
970 try:
1152 try:
971 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
1153 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
972 except re.error:
1154 except re.error:
973 if s:
1155 if s:
974 raise error.Abort(_("%s: invalid pattern (%s): %s") %
1156 raise error.Abort(_("%s: invalid pattern (%s): %s") %
975 (s, k, p))
1157 (s, k, p))
976 else:
1158 else:
977 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1159 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
978 raise error.Abort(_("invalid pattern"))
1160 raise error.Abort(_("invalid pattern"))
979
1161
980 def _patternrootsanddirs(kindpats):
1162 def _patternrootsanddirs(kindpats):
981 '''Returns roots and directories corresponding to each pattern.
1163 '''Returns roots and directories corresponding to each pattern.
982
1164
983 This calculates the roots and directories exactly matching the patterns and
1165 This calculates the roots and directories exactly matching the patterns and
984 returns a tuple of (roots, dirs) for each. It does not return other
1166 returns a tuple of (roots, dirs) for each. It does not return other
985 directories which may also need to be considered, like the parent
1167 directories which may also need to be considered, like the parent
986 directories.
1168 directories.
987 '''
1169 '''
988 r = []
1170 r = []
989 d = []
1171 d = []
990 for kind, pat, source in kindpats:
1172 for kind, pat, source in kindpats:
991 if kind == 'glob': # find the non-glob prefix
1173 if kind == 'glob': # find the non-glob prefix
992 root = []
1174 root = []
993 for p in pat.split('/'):
1175 for p in pat.split('/'):
994 if '[' in p or '{' in p or '*' in p or '?' in p:
1176 if '[' in p or '{' in p or '*' in p or '?' in p:
995 break
1177 break
996 root.append(p)
1178 root.append(p)
997 r.append('/'.join(root) or '.')
1179 r.append('/'.join(root) or '.')
998 elif kind in ('relpath', 'path'):
1180 elif kind in ('relpath', 'path'):
999 r.append(pat or '.')
1181 r.append(pat or '.')
1000 elif kind in ('rootfilesin',):
1182 elif kind in ('rootfilesin',):
1001 d.append(pat or '.')
1183 d.append(pat or '.')
1002 else: # relglob, re, relre
1184 else: # relglob, re, relre
1003 r.append('.')
1185 r.append('.')
1004 return r, d
1186 return r, d
1005
1187
1006 def _roots(kindpats):
1188 def _roots(kindpats):
1007 '''Returns root directories to match recursively from the given patterns.'''
1189 '''Returns root directories to match recursively from the given patterns.'''
1008 roots, dirs = _patternrootsanddirs(kindpats)
1190 roots, dirs = _patternrootsanddirs(kindpats)
1009 return roots
1191 return roots
1010
1192
1011 def _rootsdirsandparents(kindpats):
1193 def _rootsdirsandparents(kindpats):
1012 '''Returns roots and exact directories from patterns.
1194 '''Returns roots and exact directories from patterns.
1013
1195
1014 roots are directories to match recursively, whereas exact directories should
1196 roots are directories to match recursively, whereas exact directories should
1015 be matched non-recursively. The returned (roots, dirs) tuple will also
1197 be matched non-recursively. The returned (roots, dirs) tuple will also
1016 include directories that need to be implicitly considered as either, such as
1198 include directories that need to be implicitly considered as either, such as
1017 parent directories.
1199 parent directories.
1018
1200
1019 >>> _rootsdirsandparents(
1201 >>> _rootsdirsandparents(
1020 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1202 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1021 ... (b'glob', b'g*', b'')])
1203 ... (b'glob', b'g*', b'')])
1022 (['g/h', 'g/h', '.'], [], ['g', '.'])
1204 (['g/h', 'g/h', '.'], [], ['g', '.'])
1023 >>> _rootsdirsandparents(
1205 >>> _rootsdirsandparents(
1024 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1206 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1025 ([], ['g/h', '.'], ['g', '.'])
1207 ([], ['g/h', '.'], ['g', '.'])
1026 >>> _rootsdirsandparents(
1208 >>> _rootsdirsandparents(
1027 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1209 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1028 ... (b'path', b'', b'')])
1210 ... (b'path', b'', b'')])
1029 (['r', 'p/p', '.'], [], ['p', '.'])
1211 (['r', 'p/p', '.'], [], ['p', '.'])
1030 >>> _rootsdirsandparents(
1212 >>> _rootsdirsandparents(
1031 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1213 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1032 ... (b'relre', b'rr', b'')])
1214 ... (b'relre', b'rr', b'')])
1033 (['.', '.', '.'], [], ['.'])
1215 (['.', '.', '.'], [], ['.'])
1034 '''
1216 '''
1035 r, d = _patternrootsanddirs(kindpats)
1217 r, d = _patternrootsanddirs(kindpats)
1036
1218
1037 p = []
1219 p = []
1038 # Append the parents as non-recursive/exact directories, since they must be
1220 # Append the parents as non-recursive/exact directories, since they must be
1039 # scanned to get to either the roots or the other exact directories.
1221 # scanned to get to either the roots or the other exact directories.
1040 p.extend(util.dirs(d))
1222 p.extend(util.dirs(d))
1041 p.extend(util.dirs(r))
1223 p.extend(util.dirs(r))
1042 # util.dirs() does not include the root directory, so add it manually
1224 # util.dirs() does not include the root directory, so add it manually
1043 p.append('.')
1225 p.append('.')
1044
1226
1045 return r, d, p
1227 return r, d, p
1046
1228
1047 def _explicitfiles(kindpats):
1229 def _explicitfiles(kindpats):
1048 '''Returns the potential explicit filenames from the patterns.
1230 '''Returns the potential explicit filenames from the patterns.
1049
1231
1050 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1232 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1051 ['foo/bar']
1233 ['foo/bar']
1052 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1234 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1053 []
1235 []
1054 '''
1236 '''
1055 # Keep only the pattern kinds where one can specify filenames (vs only
1237 # Keep only the pattern kinds where one can specify filenames (vs only
1056 # directory names).
1238 # directory names).
1057 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1239 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1058 return _roots(filable)
1240 return _roots(filable)
1059
1241
1060 def _prefix(kindpats):
1242 def _prefix(kindpats):
1061 '''Whether all the patterns match a prefix (i.e. recursively)'''
1243 '''Whether all the patterns match a prefix (i.e. recursively)'''
1062 for kind, pat, source in kindpats:
1244 for kind, pat, source in kindpats:
1063 if kind not in ('path', 'relpath'):
1245 if kind not in ('path', 'relpath'):
1064 return False
1246 return False
1065 return True
1247 return True
1066
1248
1067 _commentre = None
1249 _commentre = None
1068
1250
1069 def readpatternfile(filepath, warn, sourceinfo=False):
1251 def readpatternfile(filepath, warn, sourceinfo=False):
1070 '''parse a pattern file, returning a list of
1252 '''parse a pattern file, returning a list of
1071 patterns. These patterns should be given to compile()
1253 patterns. These patterns should be given to compile()
1072 to be validated and converted into a match function.
1254 to be validated and converted into a match function.
1073
1255
1074 trailing white space is dropped.
1256 trailing white space is dropped.
1075 the escape character is backslash.
1257 the escape character is backslash.
1076 comments start with #.
1258 comments start with #.
1077 empty lines are skipped.
1259 empty lines are skipped.
1078
1260
1079 lines can be of the following formats:
1261 lines can be of the following formats:
1080
1262
1081 syntax: regexp # defaults following lines to non-rooted regexps
1263 syntax: regexp # defaults following lines to non-rooted regexps
1082 syntax: glob # defaults following lines to non-rooted globs
1264 syntax: glob # defaults following lines to non-rooted globs
1083 re:pattern # non-rooted regular expression
1265 re:pattern # non-rooted regular expression
1084 glob:pattern # non-rooted glob
1266 glob:pattern # non-rooted glob
1085 pattern # pattern of the current default type
1267 pattern # pattern of the current default type
1086
1268
1087 if sourceinfo is set, returns a list of tuples:
1269 if sourceinfo is set, returns a list of tuples:
1088 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1270 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1089 '''
1271 '''
1090
1272
1091 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1273 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1092 'include': 'include', 'subinclude': 'subinclude'}
1274 'include': 'include', 'subinclude': 'subinclude'}
1093 syntax = 'relre:'
1275 syntax = 'relre:'
1094 patterns = []
1276 patterns = []
1095
1277
1096 fp = open(filepath, 'rb')
1278 fp = open(filepath, 'rb')
1097 for lineno, line in enumerate(util.iterfile(fp), start=1):
1279 for lineno, line in enumerate(util.iterfile(fp), start=1):
1098 if "#" in line:
1280 if "#" in line:
1099 global _commentre
1281 global _commentre
1100 if not _commentre:
1282 if not _commentre:
1101 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1283 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1102 # remove comments prefixed by an even number of escapes
1284 # remove comments prefixed by an even number of escapes
1103 m = _commentre.search(line)
1285 m = _commentre.search(line)
1104 if m:
1286 if m:
1105 line = line[:m.end(1)]
1287 line = line[:m.end(1)]
1106 # fixup properly escaped comments that survived the above
1288 # fixup properly escaped comments that survived the above
1107 line = line.replace("\\#", "#")
1289 line = line.replace("\\#", "#")
1108 line = line.rstrip()
1290 line = line.rstrip()
1109 if not line:
1291 if not line:
1110 continue
1292 continue
1111
1293
1112 if line.startswith('syntax:'):
1294 if line.startswith('syntax:'):
1113 s = line[7:].strip()
1295 s = line[7:].strip()
1114 try:
1296 try:
1115 syntax = syntaxes[s]
1297 syntax = syntaxes[s]
1116 except KeyError:
1298 except KeyError:
1117 if warn:
1299 if warn:
1118 warn(_("%s: ignoring invalid syntax '%s'\n") %
1300 warn(_("%s: ignoring invalid syntax '%s'\n") %
1119 (filepath, s))
1301 (filepath, s))
1120 continue
1302 continue
1121
1303
1122 linesyntax = syntax
1304 linesyntax = syntax
1123 for s, rels in syntaxes.iteritems():
1305 for s, rels in syntaxes.iteritems():
1124 if line.startswith(rels):
1306 if line.startswith(rels):
1125 linesyntax = rels
1307 linesyntax = rels
1126 line = line[len(rels):]
1308 line = line[len(rels):]
1127 break
1309 break
1128 elif line.startswith(s+':'):
1310 elif line.startswith(s+':'):
1129 linesyntax = rels
1311 linesyntax = rels
1130 line = line[len(s) + 1:]
1312 line = line[len(s) + 1:]
1131 break
1313 break
1132 if sourceinfo:
1314 if sourceinfo:
1133 patterns.append((linesyntax + line, lineno, line))
1315 patterns.append((linesyntax + line, lineno, line))
1134 else:
1316 else:
1135 patterns.append(linesyntax + line)
1317 patterns.append(linesyntax + line)
1136 fp.close()
1318 fp.close()
1137 return patterns
1319 return patterns
@@ -1,448 +1,817 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import unittest
3 import unittest
4
4
5 import silenttestrunner
5 import silenttestrunner
6
6
7 from mercurial import (
7 from mercurial import (
8 match as matchmod,
8 match as matchmod,
9 util,
9 util,
10 )
10 )
11
11
12 class BaseMatcherTests(unittest.TestCase):
12 class BaseMatcherTests(unittest.TestCase):
13
13
14 def testVisitdir(self):
14 def testVisitdir(self):
15 m = matchmod.basematcher('', '')
15 m = matchmod.basematcher('', '')
16 self.assertTrue(m.visitdir('.'))
16 self.assertTrue(m.visitdir('.'))
17 self.assertTrue(m.visitdir('dir'))
17 self.assertTrue(m.visitdir('dir'))
18
18
19 def testVisitchildrenset(self):
20 m = matchmod.basematcher('', '')
21 self.assertEqual(m.visitchildrenset('.'), 'this')
22 self.assertEqual(m.visitchildrenset('dir'), 'this')
23
19 class AlwaysMatcherTests(unittest.TestCase):
24 class AlwaysMatcherTests(unittest.TestCase):
20
25
21 def testVisitdir(self):
26 def testVisitdir(self):
22 m = matchmod.alwaysmatcher('', '')
27 m = matchmod.alwaysmatcher('', '')
23 self.assertEqual(m.visitdir('.'), 'all')
28 self.assertEqual(m.visitdir('.'), 'all')
24 self.assertEqual(m.visitdir('dir'), 'all')
29 self.assertEqual(m.visitdir('dir'), 'all')
25
30
31 def testVisitchildrenset(self):
32 m = matchmod.alwaysmatcher('', '')
33 self.assertEqual(m.visitchildrenset('.'), 'all')
34 self.assertEqual(m.visitchildrenset('dir'), 'all')
35
26 class NeverMatcherTests(unittest.TestCase):
36 class NeverMatcherTests(unittest.TestCase):
27
37
28 def testVisitdir(self):
38 def testVisitdir(self):
29 m = matchmod.nevermatcher('', '')
39 m = matchmod.nevermatcher('', '')
30 self.assertFalse(m.visitdir('.'))
40 self.assertFalse(m.visitdir('.'))
31 self.assertFalse(m.visitdir('dir'))
41 self.assertFalse(m.visitdir('dir'))
32
42
43 def testVisitchildrenset(self):
44 m = matchmod.nevermatcher('', '')
45 self.assertEqual(m.visitchildrenset('.'), set())
46 self.assertEqual(m.visitchildrenset('dir'), set())
47
33 class PredicateMatcherTests(unittest.TestCase):
48 class PredicateMatcherTests(unittest.TestCase):
34 # predicatematcher does not currently define either of these methods, so
49 # predicatematcher does not currently define either of these methods, so
35 # this is equivalent to BaseMatcherTests.
50 # this is equivalent to BaseMatcherTests.
36
51
37 def testVisitdir(self):
52 def testVisitdir(self):
38 m = matchmod.predicatematcher('', '', lambda *a: False)
53 m = matchmod.predicatematcher('', '', lambda *a: False)
39 self.assertTrue(m.visitdir('.'))
54 self.assertTrue(m.visitdir('.'))
40 self.assertTrue(m.visitdir('dir'))
55 self.assertTrue(m.visitdir('dir'))
41
56
57 def testVisitchildrenset(self):
58 m = matchmod.predicatematcher('', '', lambda *a: False)
59 self.assertEqual(m.visitchildrenset('.'), 'this')
60 self.assertEqual(m.visitchildrenset('dir'), 'this')
61
42 class PatternMatcherTests(unittest.TestCase):
62 class PatternMatcherTests(unittest.TestCase):
43
63
44 def testVisitdirPrefix(self):
64 def testVisitdirPrefix(self):
45 m = matchmod.match('x', '', patterns=['path:dir/subdir'])
65 m = matchmod.match('x', '', patterns=['path:dir/subdir'])
46 assert isinstance(m, matchmod.patternmatcher)
66 assert isinstance(m, matchmod.patternmatcher)
47 self.assertTrue(m.visitdir('.'))
67 self.assertTrue(m.visitdir('.'))
48 self.assertTrue(m.visitdir('dir'))
68 self.assertTrue(m.visitdir('dir'))
49 self.assertEqual(m.visitdir('dir/subdir'), 'all')
69 self.assertEqual(m.visitdir('dir/subdir'), 'all')
50 # OPT: This should probably be 'all' if its parent is?
70 # OPT: This should probably be 'all' if its parent is?
51 self.assertTrue(m.visitdir('dir/subdir/x'))
71 self.assertTrue(m.visitdir('dir/subdir/x'))
52 self.assertFalse(m.visitdir('folder'))
72 self.assertFalse(m.visitdir('folder'))
53
73
74 def testVisitchildrensetPrefix(self):
75 m = matchmod.match('x', '', patterns=['path:dir/subdir'])
76 assert isinstance(m, matchmod.patternmatcher)
77 self.assertEqual(m.visitchildrenset('.'), 'this')
78 self.assertEqual(m.visitchildrenset('dir'), 'this')
79 self.assertEqual(m.visitchildrenset('dir/subdir'), 'all')
80 # OPT: This should probably be 'all' if its parent is?
81 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
82 self.assertEqual(m.visitchildrenset('folder'), set())
83
54 def testVisitdirRootfilesin(self):
84 def testVisitdirRootfilesin(self):
55 m = matchmod.match('x', '', patterns=['rootfilesin:dir/subdir'])
85 m = matchmod.match('x', '', patterns=['rootfilesin:dir/subdir'])
56 assert isinstance(m, matchmod.patternmatcher)
86 assert isinstance(m, matchmod.patternmatcher)
57 self.assertTrue(m.visitdir('.'))
87 self.assertTrue(m.visitdir('.'))
58 self.assertFalse(m.visitdir('dir/subdir/x'))
88 self.assertFalse(m.visitdir('dir/subdir/x'))
59 self.assertFalse(m.visitdir('folder'))
89 self.assertFalse(m.visitdir('folder'))
60 # FIXME: These should probably be True.
90 # FIXME: These should probably be True.
61 self.assertFalse(m.visitdir('dir'))
91 self.assertFalse(m.visitdir('dir'))
62 self.assertFalse(m.visitdir('dir/subdir'))
92 self.assertFalse(m.visitdir('dir/subdir'))
63
93
94 def testVisitchildrensetRootfilesin(self):
95 m = matchmod.match('x', '', patterns=['rootfilesin:dir/subdir'])
96 assert isinstance(m, matchmod.patternmatcher)
97 self.assertEqual(m.visitchildrenset('.'), 'this')
98 self.assertEqual(m.visitchildrenset('dir/subdir/x'), set())
99 self.assertEqual(m.visitchildrenset('folder'), set())
100 self.assertEqual(m.visitchildrenset('dir'), set())
101 self.assertEqual(m.visitchildrenset('dir/subdir'), set())
102
64 def testVisitdirGlob(self):
103 def testVisitdirGlob(self):
65 m = matchmod.match('x', '', patterns=['glob:dir/z*'])
104 m = matchmod.match('x', '', patterns=['glob:dir/z*'])
66 assert isinstance(m, matchmod.patternmatcher)
105 assert isinstance(m, matchmod.patternmatcher)
67 self.assertTrue(m.visitdir('.'))
106 self.assertTrue(m.visitdir('.'))
68 self.assertTrue(m.visitdir('dir'))
107 self.assertTrue(m.visitdir('dir'))
69 self.assertFalse(m.visitdir('folder'))
108 self.assertFalse(m.visitdir('folder'))
70 # OPT: these should probably be False.
109 # OPT: these should probably be False.
71 self.assertTrue(m.visitdir('dir/subdir'))
110 self.assertTrue(m.visitdir('dir/subdir'))
72 self.assertTrue(m.visitdir('dir/subdir/x'))
111 self.assertTrue(m.visitdir('dir/subdir/x'))
73
112
113 def testVisitchildrensetGlob(self):
114 m = matchmod.match('x', '', patterns=['glob:dir/z*'])
115 assert isinstance(m, matchmod.patternmatcher)
116 self.assertEqual(m.visitchildrenset('.'), 'this')
117 self.assertEqual(m.visitchildrenset('folder'), set())
118 self.assertEqual(m.visitchildrenset('dir'), 'this')
119 # OPT: these should probably be set().
120 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
121 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
122
74 class IncludeMatcherTests(unittest.TestCase):
123 class IncludeMatcherTests(unittest.TestCase):
75
124
76 def testVisitdirPrefix(self):
125 def testVisitdirPrefix(self):
77 m = matchmod.match('x', '', include=['path:dir/subdir'])
126 m = matchmod.match('x', '', include=['path:dir/subdir'])
78 assert isinstance(m, matchmod.includematcher)
127 assert isinstance(m, matchmod.includematcher)
79 self.assertTrue(m.visitdir('.'))
128 self.assertTrue(m.visitdir('.'))
80 self.assertTrue(m.visitdir('dir'))
129 self.assertTrue(m.visitdir('dir'))
81 self.assertEqual(m.visitdir('dir/subdir'), 'all')
130 self.assertEqual(m.visitdir('dir/subdir'), 'all')
82 # OPT: This should probably be 'all' if its parent is?
131 # OPT: This should probably be 'all' if its parent is?
83 self.assertTrue(m.visitdir('dir/subdir/x'))
132 self.assertTrue(m.visitdir('dir/subdir/x'))
84 self.assertFalse(m.visitdir('folder'))
133 self.assertFalse(m.visitdir('folder'))
85
134
135 def testVisitchildrensetPrefix(self):
136 m = matchmod.match('x', '', include=['path:dir/subdir'])
137 assert isinstance(m, matchmod.includematcher)
138 self.assertEqual(m.visitchildrenset('.'), {'dir'})
139 self.assertEqual(m.visitchildrenset('dir'), {'subdir'})
140 self.assertEqual(m.visitchildrenset('dir/subdir'), 'all')
141 # OPT: This should probably be 'all' if its parent is?
142 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
143 self.assertEqual(m.visitchildrenset('folder'), set())
144
86 def testVisitdirRootfilesin(self):
145 def testVisitdirRootfilesin(self):
87 m = matchmod.match('x', '', include=['rootfilesin:dir/subdir'])
146 m = matchmod.match('x', '', include=['rootfilesin:dir/subdir'])
88 assert isinstance(m, matchmod.includematcher)
147 assert isinstance(m, matchmod.includematcher)
89 self.assertTrue(m.visitdir('.'))
148 self.assertTrue(m.visitdir('.'))
90 self.assertTrue(m.visitdir('dir'))
149 self.assertTrue(m.visitdir('dir'))
91 self.assertTrue(m.visitdir('dir/subdir'))
150 self.assertTrue(m.visitdir('dir/subdir'))
92 self.assertFalse(m.visitdir('dir/subdir/x'))
151 self.assertFalse(m.visitdir('dir/subdir/x'))
93 self.assertFalse(m.visitdir('folder'))
152 self.assertFalse(m.visitdir('folder'))
94
153
154 def testVisitchildrensetRootfilesin(self):
155 m = matchmod.match('x', '', include=['rootfilesin:dir/subdir'])
156 assert isinstance(m, matchmod.includematcher)
157 self.assertEqual(m.visitchildrenset('.'), {'dir'})
158 self.assertEqual(m.visitchildrenset('dir'), {'subdir'})
159 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
160 self.assertEqual(m.visitchildrenset('dir/subdir/x'), set())
161 self.assertEqual(m.visitchildrenset('folder'), set())
162
95 def testVisitdirGlob(self):
163 def testVisitdirGlob(self):
96 m = matchmod.match('x', '', include=['glob:dir/z*'])
164 m = matchmod.match('x', '', include=['glob:dir/z*'])
97 assert isinstance(m, matchmod.includematcher)
165 assert isinstance(m, matchmod.includematcher)
98 self.assertTrue(m.visitdir('.'))
166 self.assertTrue(m.visitdir('.'))
99 self.assertTrue(m.visitdir('dir'))
167 self.assertTrue(m.visitdir('dir'))
100 self.assertFalse(m.visitdir('folder'))
168 self.assertFalse(m.visitdir('folder'))
101 # OPT: these should probably be False.
169 # OPT: these should probably be False.
102 self.assertTrue(m.visitdir('dir/subdir'))
170 self.assertTrue(m.visitdir('dir/subdir'))
103 self.assertTrue(m.visitdir('dir/subdir/x'))
171 self.assertTrue(m.visitdir('dir/subdir/x'))
104
172
173 def testVisitchildrensetGlob(self):
174 m = matchmod.match('x', '', include=['glob:dir/z*'])
175 assert isinstance(m, matchmod.includematcher)
176 self.assertEqual(m.visitchildrenset('.'), {'dir'})
177 self.assertEqual(m.visitchildrenset('folder'), set())
178 self.assertEqual(m.visitchildrenset('dir'), 'this')
179 # OPT: these should probably be set().
180 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
181 self.assertEqual(m.visitchildrenset('dir/subdir/x'), 'this')
182
105 class ExactMatcherTests(unittest.TestCase):
183 class ExactMatcherTests(unittest.TestCase):
106
184
107 def testVisitdir(self):
185 def testVisitdir(self):
108 m = matchmod.match('x', '', patterns=['dir/subdir/foo.txt'], exact=True)
186 m = matchmod.match('x', '', patterns=['dir/subdir/foo.txt'], exact=True)
109 assert isinstance(m, matchmod.exactmatcher)
187 assert isinstance(m, matchmod.exactmatcher)
110 self.assertTrue(m.visitdir('.'))
188 self.assertTrue(m.visitdir('.'))
111 self.assertTrue(m.visitdir('dir'))
189 self.assertTrue(m.visitdir('dir'))
112 self.assertTrue(m.visitdir('dir/subdir'))
190 self.assertTrue(m.visitdir('dir/subdir'))
113 self.assertFalse(m.visitdir('dir/subdir/foo.txt'))
191 self.assertFalse(m.visitdir('dir/subdir/foo.txt'))
114 self.assertFalse(m.visitdir('dir/foo'))
192 self.assertFalse(m.visitdir('dir/foo'))
115 self.assertFalse(m.visitdir('dir/subdir/x'))
193 self.assertFalse(m.visitdir('dir/subdir/x'))
116 self.assertFalse(m.visitdir('folder'))
194 self.assertFalse(m.visitdir('folder'))
117
195
196 def testVisitchildrenset(self):
197 m = matchmod.match('x', '', patterns=['dir/subdir/foo.txt'], exact=True)
198 assert isinstance(m, matchmod.exactmatcher)
199 self.assertEqual(m.visitchildrenset('.'), {'dir'})
200 self.assertEqual(m.visitchildrenset('dir'), {'subdir'})
201 self.assertEqual(m.visitchildrenset('dir/subdir'), 'this')
202 self.assertEqual(m.visitchildrenset('dir/subdir/x'), set())
203 self.assertEqual(m.visitchildrenset('dir/subdir/foo.txt'), set())
204 self.assertEqual(m.visitchildrenset('folder'), set())
205
118 class DifferenceMatcherTests(unittest.TestCase):
206 class DifferenceMatcherTests(unittest.TestCase):
119
207
120 def testVisitdirM2always(self):
208 def testVisitdirM2always(self):
121 m1 = matchmod.alwaysmatcher('', '')
209 m1 = matchmod.alwaysmatcher('', '')
122 m2 = matchmod.alwaysmatcher('', '')
210 m2 = matchmod.alwaysmatcher('', '')
123 dm = matchmod.differencematcher(m1, m2)
211 dm = matchmod.differencematcher(m1, m2)
124 # dm should be equivalent to a nevermatcher.
212 # dm should be equivalent to a nevermatcher.
125 self.assertFalse(dm.visitdir('.'))
213 self.assertFalse(dm.visitdir('.'))
126 self.assertFalse(dm.visitdir('dir'))
214 self.assertFalse(dm.visitdir('dir'))
127 self.assertFalse(dm.visitdir('dir/subdir'))
215 self.assertFalse(dm.visitdir('dir/subdir'))
128 self.assertFalse(dm.visitdir('dir/subdir/z'))
216 self.assertFalse(dm.visitdir('dir/subdir/z'))
129 self.assertFalse(dm.visitdir('dir/foo'))
217 self.assertFalse(dm.visitdir('dir/foo'))
130 self.assertFalse(dm.visitdir('dir/subdir/x'))
218 self.assertFalse(dm.visitdir('dir/subdir/x'))
131 self.assertFalse(dm.visitdir('folder'))
219 self.assertFalse(dm.visitdir('folder'))
132
220
221 def testVisitchildrensetM2always(self):
222 m1 = matchmod.alwaysmatcher('', '')
223 m2 = matchmod.alwaysmatcher('', '')
224 dm = matchmod.differencematcher(m1, m2)
225 # dm should be equivalent to a nevermatcher.
226 self.assertEqual(dm.visitchildrenset('.'), set())
227 self.assertEqual(dm.visitchildrenset('dir'), set())
228 self.assertEqual(dm.visitchildrenset('dir/subdir'), set())
229 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), set())
230 self.assertEqual(dm.visitchildrenset('dir/foo'), set())
231 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), set())
232 self.assertEqual(dm.visitchildrenset('folder'), set())
233
133 def testVisitdirM2never(self):
234 def testVisitdirM2never(self):
134 m1 = matchmod.alwaysmatcher('', '')
235 m1 = matchmod.alwaysmatcher('', '')
135 m2 = matchmod.nevermatcher('', '')
236 m2 = matchmod.nevermatcher('', '')
136 dm = matchmod.differencematcher(m1, m2)
237 dm = matchmod.differencematcher(m1, m2)
137 # dm should be equivalent to a alwaysmatcher. OPT: if m2 is a
238 # dm should be equivalent to a alwaysmatcher. OPT: if m2 is a
138 # nevermatcher, we could return 'all' for these.
239 # nevermatcher, we could return 'all' for these.
139 #
240 #
140 # We're testing Equal-to-True instead of just 'assertTrue' since
241 # We're testing Equal-to-True instead of just 'assertTrue' since
141 # assertTrue does NOT verify that it's a bool, just that it's truthy.
242 # assertTrue does NOT verify that it's a bool, just that it's truthy.
142 # While we may want to eventually make these return 'all', they should
243 # While we may want to eventually make these return 'all', they should
143 # not currently do so.
244 # not currently do so.
144 self.assertEqual(dm.visitdir('.'), True)
245 self.assertEqual(dm.visitdir('.'), True)
145 self.assertEqual(dm.visitdir('dir'), True)
246 self.assertEqual(dm.visitdir('dir'), True)
146 self.assertEqual(dm.visitdir('dir/subdir'), True)
247 self.assertEqual(dm.visitdir('dir/subdir'), True)
147 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
248 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
148 self.assertEqual(dm.visitdir('dir/foo'), True)
249 self.assertEqual(dm.visitdir('dir/foo'), True)
149 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
250 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
150 self.assertEqual(dm.visitdir('folder'), True)
251 self.assertEqual(dm.visitdir('folder'), True)
151
252
253 def testVisitchildrensetM2never(self):
254 m1 = matchmod.alwaysmatcher('', '')
255 m2 = matchmod.nevermatcher('', '')
256 dm = matchmod.differencematcher(m1, m2)
257 # dm should be equivalent to a alwaysmatcher.
258 self.assertEqual(dm.visitchildrenset('.'), 'all')
259 self.assertEqual(dm.visitchildrenset('dir'), 'all')
260 self.assertEqual(dm.visitchildrenset('dir/subdir'), 'all')
261 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), 'all')
262 self.assertEqual(dm.visitchildrenset('dir/foo'), 'all')
263 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), 'all')
264 self.assertEqual(dm.visitchildrenset('folder'), 'all')
265
152 def testVisitdirM2SubdirPrefix(self):
266 def testVisitdirM2SubdirPrefix(self):
153 m1 = matchmod.alwaysmatcher('', '')
267 m1 = matchmod.alwaysmatcher('', '')
154 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
268 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
155 dm = matchmod.differencematcher(m1, m2)
269 dm = matchmod.differencematcher(m1, m2)
156 self.assertEqual(dm.visitdir('.'), True)
270 self.assertEqual(dm.visitdir('.'), True)
157 self.assertEqual(dm.visitdir('dir'), True)
271 self.assertEqual(dm.visitdir('dir'), True)
158 self.assertFalse(dm.visitdir('dir/subdir'))
272 self.assertFalse(dm.visitdir('dir/subdir'))
159 # OPT: We should probably return False for these; we don't because
273 # OPT: We should probably return False for these; we don't because
160 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
274 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
161 # an 'all' pattern, just True.
275 # an 'all' pattern, just True.
162 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
276 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
163 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
277 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
164 # OPT: We could return 'all' for these.
278 # OPT: We could return 'all' for these.
165 self.assertEqual(dm.visitdir('dir/foo'), True)
279 self.assertEqual(dm.visitdir('dir/foo'), True)
166 self.assertEqual(dm.visitdir('folder'), True)
280 self.assertEqual(dm.visitdir('folder'), True)
167
281
282 def testVisitchildrensetM2SubdirPrefix(self):
283 m1 = matchmod.alwaysmatcher('', '')
284 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
285 dm = matchmod.differencematcher(m1, m2)
286 self.assertEqual(dm.visitchildrenset('.'), 'this')
287 self.assertEqual(dm.visitchildrenset('dir'), 'this')
288 self.assertEqual(dm.visitchildrenset('dir/subdir'), set())
289 self.assertEqual(dm.visitchildrenset('dir/foo'), 'all')
290 self.assertEqual(dm.visitchildrenset('folder'), 'all')
291 # OPT: We should probably return set() for these; we don't because
292 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
293 # an 'all' pattern, just 'this'.
294 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), 'this')
295 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), 'this')
296
168 # We're using includematcher instead of patterns because it behaves slightly
297 # We're using includematcher instead of patterns because it behaves slightly
169 # better (giving narrower results) than patternmatcher.
298 # better (giving narrower results) than patternmatcher.
170 def testVisitdirIncludeIncludfe(self):
299 def testVisitdirIncludeIncludfe(self):
171 m1 = matchmod.match('', '', include=['path:dir/subdir'])
300 m1 = matchmod.match('', '', include=['path:dir/subdir'])
172 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
301 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
173 dm = matchmod.differencematcher(m1, m2)
302 dm = matchmod.differencematcher(m1, m2)
174 self.assertEqual(dm.visitdir('.'), True)
303 self.assertEqual(dm.visitdir('.'), True)
175 self.assertEqual(dm.visitdir('dir'), True)
304 self.assertEqual(dm.visitdir('dir'), True)
176 self.assertEqual(dm.visitdir('dir/subdir'), True)
305 self.assertEqual(dm.visitdir('dir/subdir'), True)
177 self.assertFalse(dm.visitdir('dir/foo'))
306 self.assertFalse(dm.visitdir('dir/foo'))
178 self.assertFalse(dm.visitdir('folder'))
307 self.assertFalse(dm.visitdir('folder'))
179 # OPT: We should probably return False for these; we don't because
308 # OPT: We should probably return False for these; we don't because
180 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
309 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
181 # an 'all' pattern, just True.
310 # an 'all' pattern, just True.
182 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
311 self.assertEqual(dm.visitdir('dir/subdir/z'), True)
183 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
312 self.assertEqual(dm.visitdir('dir/subdir/x'), True)
184
313
314 def testVisitchildrensetIncludeInclude(self):
315 m1 = matchmod.match('', '', include=['path:dir/subdir'])
316 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
317 dm = matchmod.differencematcher(m1, m2)
318 self.assertEqual(dm.visitchildrenset('.'), {'dir'})
319 self.assertEqual(dm.visitchildrenset('dir'), {'subdir'})
320 self.assertEqual(dm.visitchildrenset('dir/subdir'), 'all')
321 self.assertEqual(dm.visitchildrenset('dir/foo'), set())
322 self.assertEqual(dm.visitchildrenset('folder'), set())
323 # OPT: We should probably return set() for these; we don't because
324 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
325 # an 'all' pattern, just 'this'.
326 self.assertEqual(dm.visitchildrenset('dir/subdir/z'), 'this')
327 self.assertEqual(dm.visitchildrenset('dir/subdir/x'), 'this')
328
185 class IntersectionMatcherTests(unittest.TestCase):
329 class IntersectionMatcherTests(unittest.TestCase):
186
330
187 def testVisitdirM2always(self):
331 def testVisitdirM2always(self):
188 m1 = matchmod.alwaysmatcher('', '')
332 m1 = matchmod.alwaysmatcher('', '')
189 m2 = matchmod.alwaysmatcher('', '')
333 m2 = matchmod.alwaysmatcher('', '')
190 im = matchmod.intersectmatchers(m1, m2)
334 im = matchmod.intersectmatchers(m1, m2)
191 # im should be equivalent to a alwaysmatcher.
335 # im should be equivalent to a alwaysmatcher.
192 self.assertEqual(im.visitdir('.'), 'all')
336 self.assertEqual(im.visitdir('.'), 'all')
193 self.assertEqual(im.visitdir('dir'), 'all')
337 self.assertEqual(im.visitdir('dir'), 'all')
194 self.assertEqual(im.visitdir('dir/subdir'), 'all')
338 self.assertEqual(im.visitdir('dir/subdir'), 'all')
195 self.assertEqual(im.visitdir('dir/subdir/z'), 'all')
339 self.assertEqual(im.visitdir('dir/subdir/z'), 'all')
196 self.assertEqual(im.visitdir('dir/foo'), 'all')
340 self.assertEqual(im.visitdir('dir/foo'), 'all')
197 self.assertEqual(im.visitdir('dir/subdir/x'), 'all')
341 self.assertEqual(im.visitdir('dir/subdir/x'), 'all')
198 self.assertEqual(im.visitdir('folder'), 'all')
342 self.assertEqual(im.visitdir('folder'), 'all')
199
343
344 def testVisitchildrensetM2always(self):
345 m1 = matchmod.alwaysmatcher('', '')
346 m2 = matchmod.alwaysmatcher('', '')
347 im = matchmod.intersectmatchers(m1, m2)
348 # im should be equivalent to a alwaysmatcher.
349 self.assertEqual(im.visitchildrenset('.'), 'all')
350 self.assertEqual(im.visitchildrenset('dir'), 'all')
351 self.assertEqual(im.visitchildrenset('dir/subdir'), 'all')
352 self.assertEqual(im.visitchildrenset('dir/subdir/z'), 'all')
353 self.assertEqual(im.visitchildrenset('dir/foo'), 'all')
354 self.assertEqual(im.visitchildrenset('dir/subdir/x'), 'all')
355 self.assertEqual(im.visitchildrenset('folder'), 'all')
356
200 def testVisitdirM2never(self):
357 def testVisitdirM2never(self):
201 m1 = matchmod.alwaysmatcher('', '')
358 m1 = matchmod.alwaysmatcher('', '')
202 m2 = matchmod.nevermatcher('', '')
359 m2 = matchmod.nevermatcher('', '')
203 im = matchmod.intersectmatchers(m1, m2)
360 im = matchmod.intersectmatchers(m1, m2)
204 # im should be equivalent to a nevermatcher.
361 # im should be equivalent to a nevermatcher.
205 self.assertFalse(im.visitdir('.'))
362 self.assertFalse(im.visitdir('.'))
206 self.assertFalse(im.visitdir('dir'))
363 self.assertFalse(im.visitdir('dir'))
207 self.assertFalse(im.visitdir('dir/subdir'))
364 self.assertFalse(im.visitdir('dir/subdir'))
208 self.assertFalse(im.visitdir('dir/subdir/z'))
365 self.assertFalse(im.visitdir('dir/subdir/z'))
209 self.assertFalse(im.visitdir('dir/foo'))
366 self.assertFalse(im.visitdir('dir/foo'))
210 self.assertFalse(im.visitdir('dir/subdir/x'))
367 self.assertFalse(im.visitdir('dir/subdir/x'))
211 self.assertFalse(im.visitdir('folder'))
368 self.assertFalse(im.visitdir('folder'))
212
369
370 def testVisitchildrensetM2never(self):
371 m1 = matchmod.alwaysmatcher('', '')
372 m2 = matchmod.nevermatcher('', '')
373 im = matchmod.intersectmatchers(m1, m2)
374 # im should be equivalent to a nevermqtcher.
375 self.assertEqual(im.visitchildrenset('.'), set())
376 self.assertEqual(im.visitchildrenset('dir'), set())
377 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
378 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
379 self.assertEqual(im.visitchildrenset('dir/foo'), set())
380 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
381 self.assertEqual(im.visitchildrenset('folder'), set())
382
213 def testVisitdirM2SubdirPrefix(self):
383 def testVisitdirM2SubdirPrefix(self):
214 m1 = matchmod.alwaysmatcher('', '')
384 m1 = matchmod.alwaysmatcher('', '')
215 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
385 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
216 im = matchmod.intersectmatchers(m1, m2)
386 im = matchmod.intersectmatchers(m1, m2)
217 self.assertEqual(im.visitdir('.'), True)
387 self.assertEqual(im.visitdir('.'), True)
218 self.assertEqual(im.visitdir('dir'), True)
388 self.assertEqual(im.visitdir('dir'), True)
219 self.assertEqual(im.visitdir('dir/subdir'), 'all')
389 self.assertEqual(im.visitdir('dir/subdir'), 'all')
220 self.assertFalse(im.visitdir('dir/foo'))
390 self.assertFalse(im.visitdir('dir/foo'))
221 self.assertFalse(im.visitdir('folder'))
391 self.assertFalse(im.visitdir('folder'))
222 # OPT: We should probably return 'all' for these; we don't because
392 # OPT: We should probably return 'all' for these; we don't because
223 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
393 # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
224 # an 'all' pattern, just True.
394 # an 'all' pattern, just True.
225 self.assertEqual(im.visitdir('dir/subdir/z'), True)
395 self.assertEqual(im.visitdir('dir/subdir/z'), True)
226 self.assertEqual(im.visitdir('dir/subdir/x'), True)
396 self.assertEqual(im.visitdir('dir/subdir/x'), True)
227
397
398 def testVisitchildrensetM2SubdirPrefix(self):
399 m1 = matchmod.alwaysmatcher('', '')
400 m2 = matchmod.match('', '', include=['path:dir/subdir'])
401 im = matchmod.intersectmatchers(m1, m2)
402 self.assertEqual(im.visitchildrenset('.'), {'dir'})
403 self.assertEqual(im.visitchildrenset('dir'), {'subdir'})
404 self.assertEqual(im.visitchildrenset('dir/subdir'), 'all')
405 self.assertEqual(im.visitchildrenset('dir/foo'), set())
406 self.assertEqual(im.visitchildrenset('folder'), set())
407 # OPT: We should probably return 'all' for these
408 self.assertEqual(im.visitchildrenset('dir/subdir/z'), 'this')
409 self.assertEqual(im.visitchildrenset('dir/subdir/x'), 'this')
410
228 # We're using includematcher instead of patterns because it behaves slightly
411 # We're using includematcher instead of patterns because it behaves slightly
229 # better (giving narrower results) than patternmatcher.
412 # better (giving narrower results) than patternmatcher.
230 def testVisitdirIncludeIncludfe(self):
413 def testVisitdirIncludeIncludfe(self):
231 m1 = matchmod.match('', '', include=['path:dir/subdir'])
414 m1 = matchmod.match('', '', include=['path:dir/subdir'])
232 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
415 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
233 im = matchmod.intersectmatchers(m1, m2)
416 im = matchmod.intersectmatchers(m1, m2)
234 self.assertEqual(im.visitdir('.'), True)
417 self.assertEqual(im.visitdir('.'), True)
235 self.assertEqual(im.visitdir('dir'), True)
418 self.assertEqual(im.visitdir('dir'), True)
236 self.assertFalse(im.visitdir('dir/subdir'))
419 self.assertFalse(im.visitdir('dir/subdir'))
237 self.assertFalse(im.visitdir('dir/foo'))
420 self.assertFalse(im.visitdir('dir/foo'))
238 self.assertFalse(im.visitdir('folder'))
421 self.assertFalse(im.visitdir('folder'))
239 self.assertFalse(im.visitdir('dir/subdir/z'))
422 self.assertFalse(im.visitdir('dir/subdir/z'))
240 self.assertFalse(im.visitdir('dir/subdir/x'))
423 self.assertFalse(im.visitdir('dir/subdir/x'))
241
424
425 def testVisitchildrensetIncludeInclude(self):
426 m1 = matchmod.match('', '', include=['path:dir/subdir'])
427 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
428 im = matchmod.intersectmatchers(m1, m2)
429 self.assertEqual(im.visitchildrenset('.'), {'dir'})
430 self.assertEqual(im.visitchildrenset('dir'), 'this')
431 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
432 self.assertEqual(im.visitchildrenset('dir/foo'), set())
433 self.assertEqual(im.visitchildrenset('folder'), set())
434 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
435 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
436
242 # We're using includematcher instead of patterns because it behaves slightly
437 # We're using includematcher instead of patterns because it behaves slightly
243 # better (giving narrower results) than patternmatcher.
438 # better (giving narrower results) than patternmatcher.
244 def testVisitdirIncludeInclude2(self):
439 def testVisitdirIncludeInclude2(self):
245 m1 = matchmod.match('', '', include=['path:dir/subdir'])
440 m1 = matchmod.match('', '', include=['path:dir/subdir'])
246 m2 = matchmod.match('', '', include=['path:folder'])
441 m2 = matchmod.match('', '', include=['path:folder'])
247 im = matchmod.intersectmatchers(m1, m2)
442 im = matchmod.intersectmatchers(m1, m2)
248 # FIXME: is True correct here?
443 # FIXME: is True correct here?
249 self.assertEqual(im.visitdir('.'), True)
444 self.assertEqual(im.visitdir('.'), True)
250 self.assertFalse(im.visitdir('dir'))
445 self.assertFalse(im.visitdir('dir'))
251 self.assertFalse(im.visitdir('dir/subdir'))
446 self.assertFalse(im.visitdir('dir/subdir'))
252 self.assertFalse(im.visitdir('dir/foo'))
447 self.assertFalse(im.visitdir('dir/foo'))
253 self.assertFalse(im.visitdir('folder'))
448 self.assertFalse(im.visitdir('folder'))
254 self.assertFalse(im.visitdir('dir/subdir/z'))
449 self.assertFalse(im.visitdir('dir/subdir/z'))
255 self.assertFalse(im.visitdir('dir/subdir/x'))
450 self.assertFalse(im.visitdir('dir/subdir/x'))
256
451
452 def testVisitchildrensetIncludeInclude2(self):
453 m1 = matchmod.match('', '', include=['path:dir/subdir'])
454 m2 = matchmod.match('', '', include=['path:folder'])
455 im = matchmod.intersectmatchers(m1, m2)
456 # FIXME: is set() correct here?
457 self.assertEqual(im.visitchildrenset('.'), set())
458 self.assertEqual(im.visitchildrenset('dir'), set())
459 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
460 self.assertEqual(im.visitchildrenset('dir/foo'), set())
461 self.assertEqual(im.visitchildrenset('folder'), set())
462 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
463 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
464
257 # We're using includematcher instead of patterns because it behaves slightly
465 # We're using includematcher instead of patterns because it behaves slightly
258 # better (giving narrower results) than patternmatcher.
466 # better (giving narrower results) than patternmatcher.
259 def testVisitdirIncludeInclude3(self):
467 def testVisitdirIncludeInclude3(self):
260 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
468 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
261 m2 = matchmod.match('', '', include=['path:dir/subdir'])
469 m2 = matchmod.match('', '', include=['path:dir/subdir'])
262 im = matchmod.intersectmatchers(m1, m2)
470 im = matchmod.intersectmatchers(m1, m2)
263 self.assertEqual(im.visitdir('.'), True)
471 self.assertEqual(im.visitdir('.'), True)
264 self.assertEqual(im.visitdir('dir'), True)
472 self.assertEqual(im.visitdir('dir'), True)
265 self.assertEqual(im.visitdir('dir/subdir'), True)
473 self.assertEqual(im.visitdir('dir/subdir'), True)
266 self.assertFalse(im.visitdir('dir/foo'))
474 self.assertFalse(im.visitdir('dir/foo'))
267 self.assertFalse(im.visitdir('folder'))
475 self.assertFalse(im.visitdir('folder'))
268 self.assertFalse(im.visitdir('dir/subdir/z'))
476 self.assertFalse(im.visitdir('dir/subdir/z'))
269 # OPT: this should probably be 'all' not True.
477 # OPT: this should probably be 'all' not True.
270 self.assertEqual(im.visitdir('dir/subdir/x'), True)
478 self.assertEqual(im.visitdir('dir/subdir/x'), True)
271
479
480 def testVisitchildrensetIncludeInclude3(self):
481 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
482 m2 = matchmod.match('', '', include=['path:dir/subdir'])
483 im = matchmod.intersectmatchers(m1, m2)
484 self.assertEqual(im.visitchildrenset('.'), {'dir'})
485 self.assertEqual(im.visitchildrenset('dir'), {'subdir'})
486 self.assertEqual(im.visitchildrenset('dir/subdir'), {'x'})
487 self.assertEqual(im.visitchildrenset('dir/foo'), set())
488 self.assertEqual(im.visitchildrenset('folder'), set())
489 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
490 # OPT: this should probably be 'all' not 'this'.
491 self.assertEqual(im.visitchildrenset('dir/subdir/x'), 'this')
492
272 # We're using includematcher instead of patterns because it behaves slightly
493 # We're using includematcher instead of patterns because it behaves slightly
273 # better (giving narrower results) than patternmatcher.
494 # better (giving narrower results) than patternmatcher.
274 def testVisitdirIncludeInclude4(self):
495 def testVisitdirIncludeInclude4(self):
275 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
496 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
276 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
497 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
277 im = matchmod.intersectmatchers(m1, m2)
498 im = matchmod.intersectmatchers(m1, m2)
278 # OPT: these next three could probably be False as well.
499 # OPT: these next three could probably be False as well.
279 self.assertEqual(im.visitdir('.'), True)
500 self.assertEqual(im.visitdir('.'), True)
280 self.assertEqual(im.visitdir('dir'), True)
501 self.assertEqual(im.visitdir('dir'), True)
281 self.assertEqual(im.visitdir('dir/subdir'), True)
502 self.assertEqual(im.visitdir('dir/subdir'), True)
282 self.assertFalse(im.visitdir('dir/foo'))
503 self.assertFalse(im.visitdir('dir/foo'))
283 self.assertFalse(im.visitdir('folder'))
504 self.assertFalse(im.visitdir('folder'))
284 self.assertFalse(im.visitdir('dir/subdir/z'))
505 self.assertFalse(im.visitdir('dir/subdir/z'))
285 self.assertFalse(im.visitdir('dir/subdir/x'))
506 self.assertFalse(im.visitdir('dir/subdir/x'))
286
507
508 def testVisitchildrensetIncludeInclude4(self):
509 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
510 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
511 im = matchmod.intersectmatchers(m1, m2)
512 # OPT: these next two could probably be set() as well.
513 self.assertEqual(im.visitchildrenset('.'), {'dir'})
514 self.assertEqual(im.visitchildrenset('dir'), {'subdir'})
515 self.assertEqual(im.visitchildrenset('dir/subdir'), set())
516 self.assertEqual(im.visitchildrenset('dir/foo'), set())
517 self.assertEqual(im.visitchildrenset('folder'), set())
518 self.assertEqual(im.visitchildrenset('dir/subdir/z'), set())
519 self.assertEqual(im.visitchildrenset('dir/subdir/x'), set())
520
287 class UnionMatcherTests(unittest.TestCase):
521 class UnionMatcherTests(unittest.TestCase):
288
522
289 def testVisitdirM2always(self):
523 def testVisitdirM2always(self):
290 m1 = matchmod.alwaysmatcher('', '')
524 m1 = matchmod.alwaysmatcher('', '')
291 m2 = matchmod.alwaysmatcher('', '')
525 m2 = matchmod.alwaysmatcher('', '')
292 um = matchmod.unionmatcher([m1, m2])
526 um = matchmod.unionmatcher([m1, m2])
293 # um should be equivalent to a alwaysmatcher.
527 # um should be equivalent to a alwaysmatcher.
294 self.assertEqual(um.visitdir('.'), 'all')
528 self.assertEqual(um.visitdir('.'), 'all')
295 self.assertEqual(um.visitdir('dir'), 'all')
529 self.assertEqual(um.visitdir('dir'), 'all')
296 self.assertEqual(um.visitdir('dir/subdir'), 'all')
530 self.assertEqual(um.visitdir('dir/subdir'), 'all')
297 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
531 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
298 self.assertEqual(um.visitdir('dir/foo'), 'all')
532 self.assertEqual(um.visitdir('dir/foo'), 'all')
299 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
533 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
300 self.assertEqual(um.visitdir('folder'), 'all')
534 self.assertEqual(um.visitdir('folder'), 'all')
301
535
536 def testVisitchildrensetM2always(self):
537 m1 = matchmod.alwaysmatcher('', '')
538 m2 = matchmod.alwaysmatcher('', '')
539 um = matchmod.unionmatcher([m1, m2])
540 # um should be equivalent to a alwaysmatcher.
541 self.assertEqual(um.visitchildrenset('.'), 'all')
542 self.assertEqual(um.visitchildrenset('dir'), 'all')
543 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
544 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
545 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
546 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
547 self.assertEqual(um.visitchildrenset('folder'), 'all')
548
302 def testVisitdirM1never(self):
549 def testVisitdirM1never(self):
303 m1 = matchmod.nevermatcher('', '')
550 m1 = matchmod.nevermatcher('', '')
304 m2 = matchmod.alwaysmatcher('', '')
551 m2 = matchmod.alwaysmatcher('', '')
305 um = matchmod.unionmatcher([m1, m2])
552 um = matchmod.unionmatcher([m1, m2])
306 # um should be equivalent to a alwaysmatcher.
553 # um should be equivalent to a alwaysmatcher.
307 self.assertEqual(um.visitdir('.'), 'all')
554 self.assertEqual(um.visitdir('.'), 'all')
308 self.assertEqual(um.visitdir('dir'), 'all')
555 self.assertEqual(um.visitdir('dir'), 'all')
309 self.assertEqual(um.visitdir('dir/subdir'), 'all')
556 self.assertEqual(um.visitdir('dir/subdir'), 'all')
310 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
557 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
311 self.assertEqual(um.visitdir('dir/foo'), 'all')
558 self.assertEqual(um.visitdir('dir/foo'), 'all')
312 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
559 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
313 self.assertEqual(um.visitdir('folder'), 'all')
560 self.assertEqual(um.visitdir('folder'), 'all')
314
561
562 def testVisitchildrensetM1never(self):
563 m1 = matchmod.nevermatcher('', '')
564 m2 = matchmod.alwaysmatcher('', '')
565 um = matchmod.unionmatcher([m1, m2])
566 # um should be equivalent to a alwaysmatcher.
567 self.assertEqual(um.visitchildrenset('.'), 'all')
568 self.assertEqual(um.visitchildrenset('dir'), 'all')
569 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
570 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
571 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
572 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
573 self.assertEqual(um.visitchildrenset('folder'), 'all')
574
315 def testVisitdirM2never(self):
575 def testVisitdirM2never(self):
316 m1 = matchmod.alwaysmatcher('', '')
576 m1 = matchmod.alwaysmatcher('', '')
317 m2 = matchmod.nevermatcher('', '')
577 m2 = matchmod.nevermatcher('', '')
318 um = matchmod.unionmatcher([m1, m2])
578 um = matchmod.unionmatcher([m1, m2])
319 # um should be equivalent to a alwaysmatcher.
579 # um should be equivalent to a alwaysmatcher.
320 self.assertEqual(um.visitdir('.'), 'all')
580 self.assertEqual(um.visitdir('.'), 'all')
321 self.assertEqual(um.visitdir('dir'), 'all')
581 self.assertEqual(um.visitdir('dir'), 'all')
322 self.assertEqual(um.visitdir('dir/subdir'), 'all')
582 self.assertEqual(um.visitdir('dir/subdir'), 'all')
323 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
583 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
324 self.assertEqual(um.visitdir('dir/foo'), 'all')
584 self.assertEqual(um.visitdir('dir/foo'), 'all')
325 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
585 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
326 self.assertEqual(um.visitdir('folder'), 'all')
586 self.assertEqual(um.visitdir('folder'), 'all')
327
587
588 def testVisitchildrensetM2never(self):
589 m1 = matchmod.alwaysmatcher('', '')
590 m2 = matchmod.nevermatcher('', '')
591 um = matchmod.unionmatcher([m1, m2])
592 # um should be equivalent to a alwaysmatcher.
593 self.assertEqual(um.visitchildrenset('.'), 'all')
594 self.assertEqual(um.visitchildrenset('dir'), 'all')
595 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
596 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
597 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
598 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
599 self.assertEqual(um.visitchildrenset('folder'), 'all')
600
328 def testVisitdirM2SubdirPrefix(self):
601 def testVisitdirM2SubdirPrefix(self):
329 m1 = matchmod.alwaysmatcher('', '')
602 m1 = matchmod.alwaysmatcher('', '')
330 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
603 m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
331 um = matchmod.unionmatcher([m1, m2])
604 um = matchmod.unionmatcher([m1, m2])
332 self.assertEqual(um.visitdir('.'), 'all')
605 self.assertEqual(um.visitdir('.'), 'all')
333 self.assertEqual(um.visitdir('dir'), 'all')
606 self.assertEqual(um.visitdir('dir'), 'all')
334 self.assertEqual(um.visitdir('dir/subdir'), 'all')
607 self.assertEqual(um.visitdir('dir/subdir'), 'all')
335 self.assertEqual(um.visitdir('dir/foo'), 'all')
608 self.assertEqual(um.visitdir('dir/foo'), 'all')
336 self.assertEqual(um.visitdir('folder'), 'all')
609 self.assertEqual(um.visitdir('folder'), 'all')
337 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
610 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
338 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
611 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
339
612
613 def testVisitchildrensetM2SubdirPrefix(self):
614 m1 = matchmod.alwaysmatcher('', '')
615 m2 = matchmod.match('', '', include=['path:dir/subdir'])
616 um = matchmod.unionmatcher([m1, m2])
617 self.assertEqual(um.visitchildrenset('.'), 'all')
618 self.assertEqual(um.visitchildrenset('dir'), 'all')
619 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
620 self.assertEqual(um.visitchildrenset('dir/foo'), 'all')
621 self.assertEqual(um.visitchildrenset('folder'), 'all')
622 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
623 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
624
340 # We're using includematcher instead of patterns because it behaves slightly
625 # We're using includematcher instead of patterns because it behaves slightly
341 # better (giving narrower results) than patternmatcher.
626 # better (giving narrower results) than patternmatcher.
342 def testVisitdirIncludeIncludfe(self):
627 def testVisitdirIncludeIncludfe(self):
343 m1 = matchmod.match('', '', include=['path:dir/subdir'])
628 m1 = matchmod.match('', '', include=['path:dir/subdir'])
344 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
629 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
345 um = matchmod.unionmatcher([m1, m2])
630 um = matchmod.unionmatcher([m1, m2])
346 self.assertEqual(um.visitdir('.'), True)
631 self.assertEqual(um.visitdir('.'), True)
347 self.assertEqual(um.visitdir('dir'), True)
632 self.assertEqual(um.visitdir('dir'), True)
348 self.assertEqual(um.visitdir('dir/subdir'), 'all')
633 self.assertEqual(um.visitdir('dir/subdir'), 'all')
349 self.assertFalse(um.visitdir('dir/foo'))
634 self.assertFalse(um.visitdir('dir/foo'))
350 self.assertFalse(um.visitdir('folder'))
635 self.assertFalse(um.visitdir('folder'))
351 # OPT: These two should probably be 'all' not True.
636 # OPT: These two should probably be 'all' not True.
352 self.assertEqual(um.visitdir('dir/subdir/z'), True)
637 self.assertEqual(um.visitdir('dir/subdir/z'), True)
353 self.assertEqual(um.visitdir('dir/subdir/x'), True)
638 self.assertEqual(um.visitdir('dir/subdir/x'), True)
354
639
640 def testVisitchildrensetIncludeInclude(self):
641 m1 = matchmod.match('', '', include=['path:dir/subdir'])
642 m2 = matchmod.match('', '', include=['rootfilesin:dir'])
643 um = matchmod.unionmatcher([m1, m2])
644 self.assertEqual(um.visitchildrenset('.'), {'dir'})
645 self.assertEqual(um.visitchildrenset('dir'), 'this')
646 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
647 self.assertEqual(um.visitchildrenset('dir/foo'), set())
648 self.assertEqual(um.visitchildrenset('folder'), set())
649 # OPT: These next two could be 'all' instead of 'this'.
650 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'this')
651 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'this')
652
355 # We're using includematcher instead of patterns because it behaves slightly
653 # We're using includematcher instead of patterns because it behaves slightly
356 # better (giving narrower results) than patternmatcher.
654 # better (giving narrower results) than patternmatcher.
357 def testVisitdirIncludeInclude2(self):
655 def testVisitdirIncludeInclude2(self):
358 m1 = matchmod.match('', '', include=['path:dir/subdir'])
656 m1 = matchmod.match('', '', include=['path:dir/subdir'])
359 m2 = matchmod.match('', '', include=['path:folder'])
657 m2 = matchmod.match('', '', include=['path:folder'])
360 um = matchmod.unionmatcher([m1, m2])
658 um = matchmod.unionmatcher([m1, m2])
361 self.assertEqual(um.visitdir('.'), True)
659 self.assertEqual(um.visitdir('.'), True)
362 self.assertEqual(um.visitdir('dir'), True)
660 self.assertEqual(um.visitdir('dir'), True)
363 self.assertEqual(um.visitdir('dir/subdir'), 'all')
661 self.assertEqual(um.visitdir('dir/subdir'), 'all')
364 self.assertFalse(um.visitdir('dir/foo'))
662 self.assertFalse(um.visitdir('dir/foo'))
365 self.assertEqual(um.visitdir('folder'), 'all')
663 self.assertEqual(um.visitdir('folder'), 'all')
366 # OPT: These should probably be 'all' not True.
664 # OPT: These should probably be 'all' not True.
367 self.assertEqual(um.visitdir('dir/subdir/z'), True)
665 self.assertEqual(um.visitdir('dir/subdir/z'), True)
368 self.assertEqual(um.visitdir('dir/subdir/x'), True)
666 self.assertEqual(um.visitdir('dir/subdir/x'), True)
369
667
668 def testVisitchildrensetIncludeInclude2(self):
669 m1 = matchmod.match('', '', include=['path:dir/subdir'])
670 m2 = matchmod.match('', '', include=['path:folder'])
671 um = matchmod.unionmatcher([m1, m2])
672 self.assertEqual(um.visitchildrenset('.'), {'folder', 'dir'})
673 self.assertEqual(um.visitchildrenset('dir'), {'subdir'})
674 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
675 self.assertEqual(um.visitchildrenset('dir/foo'), set())
676 self.assertEqual(um.visitchildrenset('folder'), 'all')
677 # OPT: These next two could be 'all' instead of 'this'.
678 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'this')
679 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'this')
680
370 # We're using includematcher instead of patterns because it behaves slightly
681 # We're using includematcher instead of patterns because it behaves slightly
371 # better (giving narrower results) than patternmatcher.
682 # better (giving narrower results) than patternmatcher.
372 def testVisitdirIncludeInclude3(self):
683 def testVisitdirIncludeInclude3(self):
373 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
684 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
374 m2 = matchmod.match('', '', include=['path:dir/subdir'])
685 m2 = matchmod.match('', '', include=['path:dir/subdir'])
375 um = matchmod.unionmatcher([m1, m2])
686 um = matchmod.unionmatcher([m1, m2])
376 self.assertEqual(um.visitdir('.'), True)
687 self.assertEqual(um.visitdir('.'), True)
377 self.assertEqual(um.visitdir('dir'), True)
688 self.assertEqual(um.visitdir('dir'), True)
378 self.assertEqual(um.visitdir('dir/subdir'), 'all')
689 self.assertEqual(um.visitdir('dir/subdir'), 'all')
379 self.assertFalse(um.visitdir('dir/foo'))
690 self.assertFalse(um.visitdir('dir/foo'))
380 self.assertFalse(um.visitdir('folder'))
691 self.assertFalse(um.visitdir('folder'))
381 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
692 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
382 # OPT: this should probably be 'all' not True.
693 # OPT: this should probably be 'all' not True.
383 self.assertEqual(um.visitdir('dir/subdir/z'), True)
694 self.assertEqual(um.visitdir('dir/subdir/z'), True)
384
695
696 def testVisitchildrensetIncludeInclude3(self):
697 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
698 m2 = matchmod.match('', '', include=['path:dir/subdir'])
699 um = matchmod.unionmatcher([m1, m2])
700 self.assertEqual(um.visitchildrenset('.'), {'dir'})
701 self.assertEqual(um.visitchildrenset('dir'), {'subdir'})
702 self.assertEqual(um.visitchildrenset('dir/subdir'), 'all')
703 self.assertEqual(um.visitchildrenset('dir/foo'), set())
704 self.assertEqual(um.visitchildrenset('folder'), set())
705 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
706 # OPT: this should probably be 'all' not 'this'.
707 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'this')
708
385 # We're using includematcher instead of patterns because it behaves slightly
709 # We're using includematcher instead of patterns because it behaves slightly
386 # better (giving narrower results) than patternmatcher.
710 # better (giving narrower results) than patternmatcher.
387 def testVisitdirIncludeInclude4(self):
711 def testVisitdirIncludeInclude4(self):
388 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
712 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
389 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
713 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
390 um = matchmod.unionmatcher([m1, m2])
714 um = matchmod.unionmatcher([m1, m2])
391 # OPT: these next three could probably be False as well.
715 # OPT: these next three could probably be False as well.
392 self.assertEqual(um.visitdir('.'), True)
716 self.assertEqual(um.visitdir('.'), True)
393 self.assertEqual(um.visitdir('dir'), True)
717 self.assertEqual(um.visitdir('dir'), True)
394 self.assertEqual(um.visitdir('dir/subdir'), True)
718 self.assertEqual(um.visitdir('dir/subdir'), True)
395 self.assertFalse(um.visitdir('dir/foo'))
719 self.assertFalse(um.visitdir('dir/foo'))
396 self.assertFalse(um.visitdir('folder'))
720 self.assertFalse(um.visitdir('folder'))
397 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
721 self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
398 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
722 self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
399
723
724 def testVisitchildrensetIncludeInclude4(self):
725 m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
726 m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
727 um = matchmod.unionmatcher([m1, m2])
728 self.assertEqual(um.visitchildrenset('.'), {'dir'})
729 self.assertEqual(um.visitchildrenset('dir'), {'subdir'})
730 self.assertEqual(um.visitchildrenset('dir/subdir'), {'x', 'z'})
731 self.assertEqual(um.visitchildrenset('dir/foo'), set())
732 self.assertEqual(um.visitchildrenset('folder'), set())
733 self.assertEqual(um.visitchildrenset('dir/subdir/z'), 'all')
734 self.assertEqual(um.visitchildrenset('dir/subdir/x'), 'all')
735
400 class SubdirMatcherTests(unittest.TestCase):
736 class SubdirMatcherTests(unittest.TestCase):
401
737
402 def testVisitdir(self):
738 def testVisitdir(self):
403 m = matchmod.match('', '', include=['path:dir/subdir'])
739 m = matchmod.match('', '', include=['path:dir/subdir'])
404 sm = matchmod.subdirmatcher('dir', m)
740 sm = matchmod.subdirmatcher('dir', m)
405
741
406 self.assertEqual(sm.visitdir('.'), True)
742 self.assertEqual(sm.visitdir('.'), True)
407 self.assertEqual(sm.visitdir('subdir'), 'all')
743 self.assertEqual(sm.visitdir('subdir'), 'all')
408 # OPT: These next two should probably be 'all' not True.
744 # OPT: These next two should probably be 'all' not True.
409 self.assertEqual(sm.visitdir('subdir/x'), True)
745 self.assertEqual(sm.visitdir('subdir/x'), True)
410 self.assertEqual(sm.visitdir('subdir/z'), True)
746 self.assertEqual(sm.visitdir('subdir/z'), True)
411 self.assertFalse(sm.visitdir('foo'))
747 self.assertFalse(sm.visitdir('foo'))
412
748
749 def testVisitchildrenset(self):
750 m = matchmod.match('', '', include=['path:dir/subdir'])
751 sm = matchmod.subdirmatcher('dir', m)
752
753 self.assertEqual(sm.visitchildrenset('.'), {'subdir'})
754 self.assertEqual(sm.visitchildrenset('subdir'), 'all')
755 # OPT: These next two should probably be 'all' not 'this'.
756 self.assertEqual(sm.visitchildrenset('subdir/x'), 'this')
757 self.assertEqual(sm.visitchildrenset('subdir/z'), 'this')
758 self.assertEqual(sm.visitchildrenset('foo'), set())
759
413 class PrefixdirMatcherTests(unittest.TestCase):
760 class PrefixdirMatcherTests(unittest.TestCase):
414
761
415 def testVisitdir(self):
762 def testVisitdir(self):
416 m = matchmod.match(util.localpath('root/d'), 'e/f',
763 m = matchmod.match(util.localpath('root/d'), 'e/f',
417 ['../a.txt', 'b.txt'])
764 ['../a.txt', 'b.txt'])
418 pm = matchmod.prefixdirmatcher('root', 'd/e/f', 'd', m)
765 pm = matchmod.prefixdirmatcher('root', 'd/e/f', 'd', m)
419
766
420 # `m` elides 'd' because it's part of the root, and the rest of the
767 # `m` elides 'd' because it's part of the root, and the rest of the
421 # patterns are relative.
768 # patterns are relative.
422 self.assertEqual(bool(m('a.txt')), False)
769 self.assertEqual(bool(m('a.txt')), False)
423 self.assertEqual(bool(m('b.txt')), False)
770 self.assertEqual(bool(m('b.txt')), False)
424 self.assertEqual(bool(m('e/a.txt')), True)
771 self.assertEqual(bool(m('e/a.txt')), True)
425 self.assertEqual(bool(m('e/b.txt')), False)
772 self.assertEqual(bool(m('e/b.txt')), False)
426 self.assertEqual(bool(m('e/f/b.txt')), True)
773 self.assertEqual(bool(m('e/f/b.txt')), True)
427
774
428 # The prefix matcher re-adds 'd' to the paths, so they need to be
775 # The prefix matcher re-adds 'd' to the paths, so they need to be
429 # specified when using the prefixdirmatcher.
776 # specified when using the prefixdirmatcher.
430 self.assertEqual(bool(pm('a.txt')), False)
777 self.assertEqual(bool(pm('a.txt')), False)
431 self.assertEqual(bool(pm('b.txt')), False)
778 self.assertEqual(bool(pm('b.txt')), False)
432 self.assertEqual(bool(pm('d/e/a.txt')), True)
779 self.assertEqual(bool(pm('d/e/a.txt')), True)
433 self.assertEqual(bool(pm('d/e/b.txt')), False)
780 self.assertEqual(bool(pm('d/e/b.txt')), False)
434 self.assertEqual(bool(pm('d/e/f/b.txt')), True)
781 self.assertEqual(bool(pm('d/e/f/b.txt')), True)
435
782
436 self.assertEqual(m.visitdir('.'), True)
783 self.assertEqual(m.visitdir('.'), True)
437 self.assertEqual(m.visitdir('e'), True)
784 self.assertEqual(m.visitdir('e'), True)
438 self.assertEqual(m.visitdir('e/f'), True)
785 self.assertEqual(m.visitdir('e/f'), True)
439 self.assertEqual(m.visitdir('e/f/g'), False)
786 self.assertEqual(m.visitdir('e/f/g'), False)
440
787
441 self.assertEqual(pm.visitdir('.'), True)
788 self.assertEqual(pm.visitdir('.'), True)
442 self.assertEqual(pm.visitdir('d'), True)
789 self.assertEqual(pm.visitdir('d'), True)
443 self.assertEqual(pm.visitdir('d/e'), True)
790 self.assertEqual(pm.visitdir('d/e'), True)
444 self.assertEqual(pm.visitdir('d/e/f'), True)
791 self.assertEqual(pm.visitdir('d/e/f'), True)
445 self.assertEqual(pm.visitdir('d/e/f/g'), False)
792 self.assertEqual(pm.visitdir('d/e/f/g'), False)
446
793
794 def testVisitchildrenset(self):
795 m = matchmod.match(util.localpath('root/d'), 'e/f',
796 ['../a.txt', 'b.txt'])
797 pm = matchmod.prefixdirmatcher('root', 'd/e/f', 'd', m)
798
799 # OPT: visitchildrenset could possibly return {'e'} and {'f'} for these
800 # next two, respectively; patternmatcher does not have this
801 # optimization.
802 self.assertEqual(m.visitchildrenset('.'), 'this')
803 self.assertEqual(m.visitchildrenset('e'), 'this')
804 self.assertEqual(m.visitchildrenset('e/f'), 'this')
805 self.assertEqual(m.visitchildrenset('e/f/g'), set())
806
807 # OPT: visitchildrenset could possibly return {'d'}, {'e'}, and {'f'}
808 # for these next three, respectively; patternmatcher does not have this
809 # optimization.
810 self.assertEqual(pm.visitchildrenset('.'), 'this')
811 self.assertEqual(pm.visitchildrenset('d'), 'this')
812 self.assertEqual(pm.visitchildrenset('d/e'), 'this')
813 self.assertEqual(pm.visitchildrenset('d/e/f'), 'this')
814 self.assertEqual(pm.visitchildrenset('d/e/f/g'), set())
815
447 if __name__ == '__main__':
816 if __name__ == '__main__':
448 silenttestrunner.main(__name__)
817 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now