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