comparison mercurial/match.py @ 33379:7ddb2aa2b7af

match: express anypats(), not prefix(), in terms of the others When I added prefix() in 9789b4a7c595 (match: introduce boolean prefix() method, 2014-10-28), we already had always(), isexact(), and anypats(), so it made sense to write it in terms of them (a prefix matcher is one that isn't any of the other types). It's only now that I realize that it's much more natural to define prefix() explicitly (it's one that uses path: patterns, roughly speaking) and let anypats() be defined in terms of the others. Remember that these methods are all used for determining which fast paths are possible. anypats() simply means that no fast paths are possible (it could be called complex() instead). Further evidence is that rootfilesin:some/dir does not have any patterns, but it's still considered to be an anypats() matcher. That's because anypats() really just means that it's not a prefix() matcher (and not always() and not isexact()). This patch thus changes prefix() to return False by default and anypats() to return True only if the other three are False. Having anypats() be True by default also seems like a good thing, because it means forgetting to override it will lead only to performance bugs, not correctness bugs. Since the base class's implementation changes, we're also forced to update the subclasses. That change exposed and fixed a bug in the differencematcher: for example when both its two input matchers were prefix matchers, we would say that the result was also a prefix matcher, which is incorrect, because e.g "path:dir - path:dir/foo" no longer matches everything under "dir" (which is what prefix() means).
author Martin von Zweigbergk <martinvonz@google.com>
date Sun, 09 Jul 2017 17:02:09 -0700
parents adf95bfb423a
children 892d255ec2a1
comparison
equal deleted inserted replaced
33378:adf95bfb423a 33379:7ddb2aa2b7af
305 This function's behavior is undefined if it has returned False for 305 This function's behavior is undefined if it has returned False for
306 one of the dir's parent directories. 306 one of the dir's parent directories.
307 ''' 307 '''
308 return False 308 return False
309 309
310 def always(self):
311 '''Matcher will match everything and .files() will be empty --
312 optimization might be possible.'''
313 return False
314
315 def isexact(self):
316 '''Matcher will match exactly the list of files in .files() --
317 optimization might be possible.'''
318 return False
319
320 def prefix(self):
321 '''Matcher will match the paths in .files() recursively --
322 optimization might be possible.'''
323 return False
324
310 def anypats(self): 325 def anypats(self):
311 '''Matcher uses patterns or include/exclude.''' 326 '''None of .always(), .isexact(), and .prefix() is true --
312 return False 327 optimizations will be difficult.'''
313 328 return not self.always() and not self.isexact() and not self.prefix()
314 def always(self):
315 '''Matcher will match everything and .files() will be empty
316 - optimization might be possible and necessary.'''
317 return False
318
319 def isexact(self):
320 return False
321
322 def prefix(self):
323 return not self.always() and not self.isexact() and not self.anypats()
324 329
325 class alwaysmatcher(basematcher): 330 class alwaysmatcher(basematcher):
326 '''Matches everything.''' 331 '''Matches everything.'''
327 332
328 def __init__(self, root, cwd, badfn=None, relativeuipath=False): 333 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
383 dir in self._fileset or 388 dir in self._fileset or
384 dir in self._dirs or 389 dir in self._dirs or
385 any(parentdir in self._fileset 390 any(parentdir in self._fileset
386 for parentdir in util.finddirs(dir))) 391 for parentdir in util.finddirs(dir)))
387 392
388 def anypats(self): 393 def prefix(self):
389 return self._anypats 394 return not self._anypats
390 395
391 def __repr__(self): 396 def __repr__(self):
392 return ('<patternmatcher patterns=%r>' % self._pats) 397 return ('<patternmatcher patterns=%r>' % self._pats)
393 398
394 class includematcher(basematcher): 399 class includematcher(basematcher):
413 return ('.' in self._roots or 418 return ('.' in self._roots or
414 dir in self._roots or 419 dir in self._roots or
415 dir in self._dirs or 420 dir in self._dirs or
416 any(parentdir in self._roots 421 any(parentdir in self._roots
417 for parentdir in util.finddirs(dir))) 422 for parentdir in util.finddirs(dir)))
418
419 def anypats(self):
420 return True
421 423
422 def __repr__(self): 424 def __repr__(self):
423 return ('<includematcher includes=%r>' % self._pats) 425 return ('<includematcher includes=%r>' % self._pats)
424 426
425 class exactmatcher(basematcher): 427 class exactmatcher(basematcher):
494 return False 496 return False
495 return bool(self._m1.visitdir(dir)) 497 return bool(self._m1.visitdir(dir))
496 498
497 def isexact(self): 499 def isexact(self):
498 return self._m1.isexact() 500 return self._m1.isexact()
499
500 def anypats(self):
501 return self._m1.anypats() or self._m2.anypats()
502 501
503 def __repr__(self): 502 def __repr__(self):
504 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2)) 503 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
505 504
506 def intersectmatchers(m1, m2): 505 def intersectmatchers(m1, m2):
563 def always(self): 562 def always(self):
564 return self._m1.always() and self._m2.always() 563 return self._m1.always() and self._m2.always()
565 564
566 def isexact(self): 565 def isexact(self):
567 return self._m1.isexact() or self._m2.isexact() 566 return self._m1.isexact() or self._m2.isexact()
568
569 def anypats(self):
570 return self._m1.anypats() or self._m2.anypats()
571 567
572 def __repr__(self): 568 def __repr__(self):
573 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2)) 569 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
574 570
575 class subdirmatcher(basematcher): 571 class subdirmatcher(basematcher):
643 return self._matcher.visitdir(dir) 639 return self._matcher.visitdir(dir)
644 640
645 def always(self): 641 def always(self):
646 return self._always 642 return self._always
647 643
648 def anypats(self): 644 def prefix(self):
649 return self._matcher.anypats() 645 return self._matcher.prefix() and not self._always
650 646
651 def __repr__(self): 647 def __repr__(self):
652 return ('<subdirmatcher path=%r, matcher=%r>' % 648 return ('<subdirmatcher path=%r, matcher=%r>' %
653 (self._path, self._matcher)) 649 (self._path, self._matcher))
654 650
660 self._includes = includes 656 self._includes = includes
661 657
662 def __call__(self, value): 658 def __call__(self, value):
663 return value in self._includes or self._matcher(value) 659 return value in self._includes or self._matcher(value)
664 660
665 def anypats(self):
666 return True
667
668 def prefix(self):
669 return False
670
671 def __repr__(self): 661 def __repr__(self):
672 return ('<forceincludematcher matcher=%r, includes=%r>' % 662 return ('<forceincludematcher matcher=%r, includes=%r>' %
673 (self._matcher, sorted(self._includes))) 663 (self._matcher, sorted(self._includes)))
674 664
675 class unionmatcher(basematcher): 665 class unionmatcher(basematcher):
681 for match in self._matchers: 671 for match in self._matchers:
682 if match(value): 672 if match(value):
683 return True 673 return True
684 return False 674 return False
685 675
686 def anypats(self):
687 return True
688
689 def prefix(self):
690 return False
691
692 def __repr__(self): 676 def __repr__(self):
693 return ('<unionmatcher matchers=%r>' % self._matchers) 677 return ('<unionmatcher matchers=%r>' % self._matchers)
694 678
695 class negatematcher(basematcher): 679 class negatematcher(basematcher):
696 def __init__(self, matcher): 680 def __init__(self, matcher):
697 self._matcher = matcher 681 self._matcher = matcher
698 682
699 def __call__(self, value): 683 def __call__(self, value):
700 return not self._matcher(value) 684 return not self._matcher(value)
701
702 def anypats(self):
703 return True
704 685
705 def __repr__(self): 686 def __repr__(self):
706 return ('<negatematcher matcher=%r>' % self._matcher) 687 return ('<negatematcher matcher=%r>' % self._matcher)
707 688
708 def patkind(pattern, default=None): 689 def patkind(pattern, default=None):