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