Mercurial > hg
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): |