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