796 return set(pathutil.dirs(self._fileset)) |
797 return set(pathutil.dirs(self._fileset)) |
797 |
798 |
798 def visitdir(self, dir): |
799 def visitdir(self, dir): |
799 return dir in self._dirs |
800 return dir in self._dirs |
800 |
801 |
|
802 @propertycache |
|
803 def _visitchildrenset_candidates(self): |
|
804 """A memoized set of candidates for visitchildrenset.""" |
|
805 return self._fileset | self._dirs - {b''} |
|
806 |
|
807 @propertycache |
|
808 def _sorted_visitchildrenset_candidates(self): |
|
809 """A memoized sorted list of candidates for visitchildrenset.""" |
|
810 return sorted(self._visitchildrenset_candidates) |
|
811 |
801 def visitchildrenset(self, dir): |
812 def visitchildrenset(self, dir): |
802 if not self._fileset or dir not in self._dirs: |
813 if not self._fileset or dir not in self._dirs: |
803 return set() |
814 return set() |
804 |
815 |
805 candidates = self._fileset | self._dirs - {b''} |
816 if dir == b'': |
806 if dir != b'': |
817 candidates = self._visitchildrenset_candidates |
|
818 else: |
|
819 candidates = self._sorted_visitchildrenset_candidates |
807 d = dir + b'/' |
820 d = dir + b'/' |
808 candidates = {c[len(d) :] for c in candidates if c.startswith(d)} |
821 # Use bisect to find the first element potentially starting with d |
|
822 # (i.e. >= d). This should always find at least one element (we'll |
|
823 # assert later if this is not the case). |
|
824 first = bisect.bisect_left(candidates, d) |
|
825 # We need a representation of the first element that is > d that |
|
826 # does not start with d, so since we added a `/` on the end of dir, |
|
827 # we'll add whatever comes after slash (we could probably assume |
|
828 # that `0` is after `/`, but let's not) to the end of dir instead. |
|
829 dnext = dir + encoding.strtolocal(chr(ord(b'/') + 1)) |
|
830 # Use bisect to find the first element >= d_next |
|
831 last = bisect.bisect_left(candidates, dnext, lo=first) |
|
832 dlen = len(d) |
|
833 candidates = {c[dlen:] for c in candidates[first:last]} |
809 # self._dirs includes all of the directories, recursively, so if |
834 # self._dirs includes all of the directories, recursively, so if |
810 # we're attempting to match foo/bar/baz.txt, it'll have '', 'foo', |
835 # we're attempting to match foo/bar/baz.txt, it'll have '', 'foo', |
811 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a |
836 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a |
812 # '/' in it, indicating a it's for a subdir-of-a-subdir; the |
837 # '/' in it, indicating a it's for a subdir-of-a-subdir; the |
813 # immediate subdir will be in there without a slash. |
838 # immediate subdir will be in there without a slash. |