481 def __repr__(self): |
481 def __repr__(self): |
482 s = (stringutil.buildrepr(self._predrepr) |
482 s = (stringutil.buildrepr(self._predrepr) |
483 or pycompat.byterepr(self.matchfn)) |
483 or pycompat.byterepr(self.matchfn)) |
484 return '<predicatenmatcher pred=%s>' % s |
484 return '<predicatenmatcher pred=%s>' % s |
485 |
485 |
|
486 def normalizerootdir(dir, funcname): |
|
487 if dir == '.': |
|
488 util.nouideprecwarn("match.%s() no longer accepts " |
|
489 "'.', use '' instead." % funcname, '5.1') |
|
490 return '' |
|
491 return dir |
|
492 |
|
493 |
486 class patternmatcher(basematcher): |
494 class patternmatcher(basematcher): |
487 """Matches a set of (kind, pat, source) against a 'root' directory. |
495 """Matches a set of (kind, pat, source) against a 'root' directory. |
488 |
496 |
489 >>> kindpats = [ |
497 >>> kindpats = [ |
490 ... (b're', br'.*\.c$', b''), |
498 ... (b're', br'.*\.c$', b''), |
523 self._prefix = _prefix(kindpats) |
531 self._prefix = _prefix(kindpats) |
524 self._pats, self.matchfn = _buildmatch(kindpats, '$', root) |
532 self._pats, self.matchfn = _buildmatch(kindpats, '$', root) |
525 |
533 |
526 @propertycache |
534 @propertycache |
527 def _dirs(self): |
535 def _dirs(self): |
528 return set(util.dirs(self._fileset)) | {'.'} |
536 return set(util.dirs(self._fileset)) | {''} |
529 |
537 |
530 def visitdir(self, dir): |
538 def visitdir(self, dir): |
|
539 dir = normalizerootdir(dir, 'visitdir') |
531 if self._prefix and dir in self._fileset: |
540 if self._prefix and dir in self._fileset: |
532 return 'all' |
541 return 'all' |
533 return ('.' in self._fileset or |
542 return ('' in self._fileset or |
534 dir in self._fileset or |
543 dir in self._fileset or |
535 dir in self._dirs or |
544 dir in self._dirs or |
536 any(parentdir in self._fileset |
545 any(parentdir in self._fileset |
537 for parentdir in util.finddirs(dir))) |
546 for parentdir in util.finddirs(dir))) |
538 |
547 |
578 # yields (dirname, basename) tuples, walking back to the root. This is |
587 # yields (dirname, basename) tuples, walking back to the root. This is |
579 # very similar to util.finddirs, except: |
588 # very similar to util.finddirs, except: |
580 # - produces a (dirname, basename) tuple, not just 'dirname' |
589 # - produces a (dirname, basename) tuple, not just 'dirname' |
581 # - includes root dir |
590 # - includes root dir |
582 # Unlike manifest._splittopdir, this does not suffix `dirname` with a |
591 # Unlike manifest._splittopdir, this does not suffix `dirname` with a |
583 # slash, and produces '.' for the root instead of ''. |
592 # slash. |
584 oldpos = len(path) |
593 oldpos = len(path) |
585 pos = path.rfind('/') |
594 pos = path.rfind('/') |
586 while pos != -1: |
595 while pos != -1: |
587 yield path[:pos], path[pos + 1:oldpos] |
596 yield path[:pos], path[pos + 1:oldpos] |
588 oldpos = pos |
597 oldpos = pos |
589 pos = path.rfind('/', 0, pos) |
598 pos = path.rfind('/', 0, pos) |
590 yield '.', path[:oldpos] |
599 yield '', path[:oldpos] |
591 |
600 |
592 def get(self, path): |
601 def get(self, path): |
593 return self._dirs.get(path, set()) |
602 return self._dirs.get(path, set()) |
594 |
603 |
595 class includematcher(basematcher): |
604 class includematcher(basematcher): |
607 # parents are directories which are non-recursively included because |
616 # parents are directories which are non-recursively included because |
608 # they are needed to get to items in _dirs or _roots. |
617 # they are needed to get to items in _dirs or _roots. |
609 self._parents = set(parents) |
618 self._parents = set(parents) |
610 |
619 |
611 def visitdir(self, dir): |
620 def visitdir(self, dir): |
|
621 dir = normalizerootdir(dir, 'visitdir') |
612 if self._prefix and dir in self._roots: |
622 if self._prefix and dir in self._roots: |
613 return 'all' |
623 return 'all' |
614 return ('.' in self._roots or |
624 return ('' in self._roots or |
615 dir in self._roots or |
625 dir in self._roots or |
616 dir in self._dirs or |
626 dir in self._dirs or |
617 dir in self._parents or |
627 dir in self._parents or |
618 any(parentdir in self._roots |
628 any(parentdir in self._roots |
619 for parentdir in util.finddirs(dir))) |
629 for parentdir in util.finddirs(dir))) |
681 |
691 |
682 matchfn = basematcher.exact |
692 matchfn = basematcher.exact |
683 |
693 |
684 @propertycache |
694 @propertycache |
685 def _dirs(self): |
695 def _dirs(self): |
686 return set(util.dirs(self._fileset)) | {'.'} |
696 return set(util.dirs(self._fileset)) | {''} |
687 |
697 |
688 def visitdir(self, dir): |
698 def visitdir(self, dir): |
|
699 dir = normalizerootdir(dir, 'visitdir') |
689 return dir in self._dirs |
700 return dir in self._dirs |
690 |
701 |
691 def visitchildrenset(self, dir): |
702 def visitchildrenset(self, dir): |
|
703 dir = normalizerootdir(dir, 'visitchildrenset') |
|
704 |
692 if not self._fileset or dir not in self._dirs: |
705 if not self._fileset or dir not in self._dirs: |
693 return set() |
706 return set() |
694 |
707 |
695 candidates = self._fileset | self._dirs - {'.'} |
708 candidates = self._fileset | self._dirs - {''} |
696 if dir != '.': |
709 if dir != '': |
697 d = dir + '/' |
710 d = dir + '/' |
698 candidates = set(c[len(d):] for c in candidates if |
711 candidates = set(c[len(d):] for c in candidates if |
699 c.startswith(d)) |
712 c.startswith(d)) |
700 # self._dirs includes all of the directories, recursively, so if |
713 # self._dirs includes all of the directories, recursively, so if |
701 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo', |
714 # we're attempting to match foo/bar/baz.txt, it'll have '', 'foo', |
702 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a |
715 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a |
703 # '/' in it, indicating a it's for a subdir-of-a-subdir; the |
716 # '/' in it, indicating a it's for a subdir-of-a-subdir; the |
704 # immediate subdir will be in there without a slash. |
717 # immediate subdir will be in there without a slash. |
705 ret = {c for c in candidates if '/' not in c} |
718 ret = {c for c in candidates if '/' not in c} |
706 # We really do not expect ret to be empty, since that would imply that |
719 # We really do not expect ret to be empty, since that would imply that |
770 # subdirectory. |
783 # subdirectory. |
771 return 'this' |
784 return 'this' |
772 # Possible values for m1: set(...), set() |
785 # Possible values for m1: set(...), set() |
773 # Possible values for m2: 'this', set(...) |
786 # Possible values for m2: 'this', set(...) |
774 # We ignore m2's set results. They're possibly incorrect: |
787 # We ignore m2's set results. They're possibly incorrect: |
775 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'): |
788 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset(''): |
776 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd |
789 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd |
777 # return set(), which is *not* correct, we still need to visit 'dir'! |
790 # return set(), which is *not* correct, we still need to visit 'dir'! |
778 return m1_set |
791 return m1_set |
779 |
792 |
780 def isexact(self): |
793 def isexact(self): |
916 # from the inputs. Instead, we override matchfn() and visitdir() to |
929 # from the inputs. Instead, we override matchfn() and visitdir() to |
917 # call the original matcher with the subdirectory path prepended. |
930 # call the original matcher with the subdirectory path prepended. |
918 return self._matcher.matchfn(self._path + "/" + f) |
931 return self._matcher.matchfn(self._path + "/" + f) |
919 |
932 |
920 def visitdir(self, dir): |
933 def visitdir(self, dir): |
921 if dir == '.': |
934 dir = normalizerootdir(dir, 'visitdir') |
|
935 if dir == '': |
922 dir = self._path |
936 dir = self._path |
923 else: |
937 else: |
924 dir = self._path + "/" + dir |
938 dir = self._path + "/" + dir |
925 return self._matcher.visitdir(dir) |
939 return self._matcher.visitdir(dir) |
926 |
940 |
927 def visitchildrenset(self, dir): |
941 def visitchildrenset(self, dir): |
928 if dir == '.': |
942 dir = normalizerootdir(dir, 'visitchildrenset') |
|
943 if dir == '': |
929 dir = self._path |
944 dir = self._path |
930 else: |
945 else: |
931 dir = self._path + "/" + dir |
946 dir = self._path + "/" + dir |
932 return self._matcher.visitchildrenset(dir) |
947 return self._matcher.visitchildrenset(dir) |
933 |
948 |
992 return False |
1007 return False |
993 return self._matcher.matchfn(f[len(self._pathprefix):]) |
1008 return self._matcher.matchfn(f[len(self._pathprefix):]) |
994 |
1009 |
995 @propertycache |
1010 @propertycache |
996 def _pathdirs(self): |
1011 def _pathdirs(self): |
997 return set(util.finddirs(self._path)) | {'.'} |
1012 return set(util.finddirs(self._path)) | {''} |
998 |
1013 |
999 def visitdir(self, dir): |
1014 def visitdir(self, dir): |
1000 if dir == self._path: |
1015 if dir == self._path: |
1001 return self._matcher.visitdir('.') |
1016 return self._matcher.visitdir('') |
1002 if dir.startswith(self._pathprefix): |
1017 if dir.startswith(self._pathprefix): |
1003 return self._matcher.visitdir(dir[len(self._pathprefix):]) |
1018 return self._matcher.visitdir(dir[len(self._pathprefix):]) |
1004 return dir in self._pathdirs |
1019 return dir in self._pathdirs |
1005 |
1020 |
1006 def visitchildrenset(self, dir): |
1021 def visitchildrenset(self, dir): |
1007 if dir == self._path: |
1022 if dir == self._path: |
1008 return self._matcher.visitchildrenset('.') |
1023 return self._matcher.visitchildrenset('') |
1009 if dir.startswith(self._pathprefix): |
1024 if dir.startswith(self._pathprefix): |
1010 return self._matcher.visitchildrenset(dir[len(self._pathprefix):]) |
1025 return self._matcher.visitchildrenset(dir[len(self._pathprefix):]) |
1011 if dir in self._pathdirs: |
1026 if dir in self._pathdirs: |
1012 return 'this' |
1027 return 'this' |
1013 return set() |
1028 return set() |
1339 root = [] |
1354 root = [] |
1340 for p in pat.split('/'): |
1355 for p in pat.split('/'): |
1341 if '[' in p or '{' in p or '*' in p or '?' in p: |
1356 if '[' in p or '{' in p or '*' in p or '?' in p: |
1342 break |
1357 break |
1343 root.append(p) |
1358 root.append(p) |
1344 r.append('/'.join(root) or '.') |
1359 r.append('/'.join(root)) |
1345 elif kind in ('relpath', 'path'): |
1360 elif kind in ('relpath', 'path'): |
1346 r.append(pat or '.') |
1361 if pat == '.': |
|
1362 pat = '' |
|
1363 r.append(pat) |
1347 elif kind in ('rootfilesin',): |
1364 elif kind in ('rootfilesin',): |
1348 d.append(pat or '.') |
1365 if pat == '.': |
|
1366 pat = '' |
|
1367 d.append(pat) |
1349 else: # relglob, re, relre |
1368 else: # relglob, re, relre |
1350 r.append('.') |
1369 r.append('') |
1351 return r, d |
1370 return r, d |
1352 |
1371 |
1353 def _roots(kindpats): |
1372 def _roots(kindpats): |
1354 '''Returns root directories to match recursively from the given patterns.''' |
1373 '''Returns root directories to match recursively from the given patterns.''' |
1355 roots, dirs = _patternrootsanddirs(kindpats) |
1374 roots, dirs = _patternrootsanddirs(kindpats) |
1365 Returns a tuple of (roots, dirs, parents). |
1384 Returns a tuple of (roots, dirs, parents). |
1366 |
1385 |
1367 >>> _rootsdirsandparents( |
1386 >>> _rootsdirsandparents( |
1368 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''), |
1387 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''), |
1369 ... (b'glob', b'g*', b'')]) |
1388 ... (b'glob', b'g*', b'')]) |
1370 (['g/h', 'g/h', '.'], [], ['g', '.']) |
1389 (['g/h', 'g/h', ''], [], ['g', '']) |
1371 >>> _rootsdirsandparents( |
1390 >>> _rootsdirsandparents( |
1372 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')]) |
1391 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')]) |
1373 ([], ['g/h', '.'], ['g', '.']) |
1392 ([], ['g/h', ''], ['g', '']) |
1374 >>> _rootsdirsandparents( |
1393 >>> _rootsdirsandparents( |
1375 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''), |
1394 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''), |
1376 ... (b'path', b'', b'')]) |
1395 ... (b'path', b'', b'')]) |
1377 (['r', 'p/p', '.'], [], ['p', '.']) |
1396 (['r', 'p/p', ''], [], ['p', '']) |
1378 >>> _rootsdirsandparents( |
1397 >>> _rootsdirsandparents( |
1379 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''), |
1398 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''), |
1380 ... (b'relre', b'rr', b'')]) |
1399 ... (b'relre', b'rr', b'')]) |
1381 (['.', '.', '.'], [], ['.']) |
1400 (['', '', ''], [], ['']) |
1382 ''' |
1401 ''' |
1383 r, d = _patternrootsanddirs(kindpats) |
1402 r, d = _patternrootsanddirs(kindpats) |
1384 |
1403 |
1385 p = [] |
1404 p = [] |
1386 # Append the parents as non-recursive/exact directories, since they must be |
1405 # Append the parents as non-recursive/exact directories, since they must be |
1387 # scanned to get to either the roots or the other exact directories. |
1406 # scanned to get to either the roots or the other exact directories. |
1388 p.extend(util.dirs(d)) |
1407 p.extend(util.dirs(d)) |
1389 p.extend(util.dirs(r)) |
1408 p.extend(util.dirs(r)) |
1390 # util.dirs() does not include the root directory, so add it manually |
1409 # util.dirs() does not include the root directory, so add it manually |
1391 p.append('.') |
1410 p.append('') |
1392 |
1411 |
1393 # FIXME: all uses of this function convert these to sets, do so before |
1412 # FIXME: all uses of this function convert these to sets, do so before |
1394 # returning. |
1413 # returning. |
1395 # FIXME: all uses of this function do not need anything in 'roots' and |
1414 # FIXME: all uses of this function do not need anything in 'roots' and |
1396 # 'dirs' to also be in 'parents', consider removing them before returning. |
1415 # 'dirs' to also be in 'parents', consider removing them before returning. |