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