60 self._per_filter = {} |
60 self._per_filter = {} |
61 |
61 |
62 def __getitem__(self, repo): |
62 def __getitem__(self, repo): |
63 self.updatecache(repo) |
63 self.updatecache(repo) |
64 bcache = self._per_filter[repo.filtername] |
64 bcache = self._per_filter[repo.filtername] |
|
65 bcache._ensure_populated(repo) |
65 assert bcache._filtername == repo.filtername, ( |
66 assert bcache._filtername == repo.filtername, ( |
66 bcache._filtername, |
67 bcache._filtername, |
67 repo.filtername, |
68 repo.filtername, |
68 ) |
69 ) |
69 return bcache |
70 return bcache |
482 self._hasnode = repo.changelog.hasnode |
483 self._hasnode = repo.changelog.hasnode |
483 |
484 |
484 def _compute_key_hashes(self, repo) -> Tuple[bytes]: |
485 def _compute_key_hashes(self, repo) -> Tuple[bytes]: |
485 raise NotImplementedError |
486 raise NotImplementedError |
486 |
487 |
|
488 def _ensure_populated(self, repo): |
|
489 """make sure any lazily loaded values are fully populated""" |
|
490 |
487 def validfor(self, repo): |
491 def validfor(self, repo): |
488 """check that cache contents are valid for (a subset of) this repo |
492 """check that cache contents are valid for (a subset of) this repo |
489 |
493 |
490 - False when the order of changesets changed or if we detect a strip. |
494 - False when the order of changesets changed or if we detect a strip. |
491 - True when cache is up-to-date for the current repo or its subset.""" |
495 - True when cache is up-to-date for the current repo or its subset.""" |
859 """ |
863 """ |
860 |
864 |
861 _base_filename = b"branch3" |
865 _base_filename = b"branch3" |
862 _default_key_hashes = (None, None) |
866 _default_key_hashes = (None, None) |
863 |
867 |
864 def _get_topo_heads(self, repo) -> List[int]: |
868 def __init__(self, *args, pure_topo_branch=None, **kwargs): |
|
869 super().__init__(*args, **kwargs) |
|
870 self._pure_topo_branch = pure_topo_branch |
|
871 self._needs_populate = self._pure_topo_branch is not None |
|
872 |
|
873 def inherit_for(self, repo): |
|
874 new = super().inherit_for(repo) |
|
875 new._pure_topo_branch = self._pure_topo_branch |
|
876 new._needs_populate = self._needs_populate |
|
877 return new |
|
878 |
|
879 def _get_topo_heads(self, repo): |
865 """returns the topological head of a repoview content up to self.tiprev""" |
880 """returns the topological head of a repoview content up to self.tiprev""" |
866 cl = repo.changelog |
881 cl = repo.changelog |
867 if self.tiprev == nullrev: |
882 if self.tiprev == nullrev: |
868 return [] |
883 return [] |
869 elif self.tiprev == cl.tiprev(): |
884 elif self.tiprev == cl.tiprev(): |
881 if self.key_hashes: |
896 if self.key_hashes: |
882 if self.key_hashes[0] is not None: |
897 if self.key_hashes[0] is not None: |
883 cache_keys[b"filtered-hash"] = hex(self.key_hashes[0]) |
898 cache_keys[b"filtered-hash"] = hex(self.key_hashes[0]) |
884 if self.key_hashes[1] is not None: |
899 if self.key_hashes[1] is not None: |
885 cache_keys[b"obsolete-hash"] = hex(self.key_hashes[1]) |
900 cache_keys[b"obsolete-hash"] = hex(self.key_hashes[1]) |
|
901 if self._pure_topo_branch is not None: |
|
902 cache_keys[b"topo-mode"] = b"pure" |
886 pieces = (b"%s=%s" % i for i in sorted(cache_keys.items())) |
903 pieces = (b"%s=%s" % i for i in sorted(cache_keys.items())) |
887 fp.write(b" ".join(pieces) + b'\n') |
904 fp.write(b" ".join(pieces) + b'\n') |
|
905 if self._pure_topo_branch is not None: |
|
906 label = encoding.fromlocal(self._pure_topo_branch) |
|
907 fp.write(label + b'\n') |
888 |
908 |
889 def _write_heads(self, repo, fp) -> int: |
909 def _write_heads(self, repo, fp) -> int: |
890 """write list of heads to a file |
910 """write list of heads to a file |
891 |
911 |
892 Return the number of heads written.""" |
912 Return the number of heads written.""" |
893 nodecount = 0 |
913 nodecount = 0 |
894 topo_heads = set(self._get_topo_heads(repo)) |
914 topo_heads = None |
|
915 if self._pure_topo_branch is None: |
|
916 topo_heads = set(self._get_topo_heads(repo)) |
895 to_rev = repo.changelog.index.rev |
917 to_rev = repo.changelog.index.rev |
896 for label, nodes in sorted(self._entries.items()): |
918 for label, nodes in sorted(self._entries.items()): |
|
919 if label == self._pure_topo_branch: |
|
920 # not need to write anything the header took care of that |
|
921 continue |
897 label = encoding.fromlocal(label) |
922 label = encoding.fromlocal(label) |
898 for node in nodes: |
923 for node in nodes: |
899 rev = to_rev(node) |
924 if topo_heads is not None: |
900 if rev in topo_heads: |
925 rev = to_rev(node) |
901 continue |
926 if rev in topo_heads: |
|
927 continue |
902 if node in self._closednodes: |
928 if node in self._closednodes: |
903 state = b'c' |
929 state = b'c' |
904 else: |
930 else: |
905 state = b'o' |
931 state = b'o' |
906 nodecount += 1 |
932 nodecount += 1 |
914 cache_keys = dict(p.split(b'=', 1) for p in pieces) |
940 cache_keys = dict(p.split(b'=', 1) for p in pieces) |
915 |
941 |
916 args = {} |
942 args = {} |
917 filtered_hash = None |
943 filtered_hash = None |
918 obsolete_hash = None |
944 obsolete_hash = None |
|
945 has_pure_topo_heads = False |
919 for k, v in cache_keys.items(): |
946 for k, v in cache_keys.items(): |
920 if k == b"tip-rev": |
947 if k == b"tip-rev": |
921 args["tiprev"] = int(v) |
948 args["tiprev"] = int(v) |
922 elif k == b"tip-node": |
949 elif k == b"tip-node": |
923 args["tipnode"] = bin(v) |
950 args["tipnode"] = bin(v) |
924 elif k == b"filtered-hash": |
951 elif k == b"filtered-hash": |
925 filtered_hash = bin(v) |
952 filtered_hash = bin(v) |
926 elif k == b"obsolete-hash": |
953 elif k == b"obsolete-hash": |
927 obsolete_hash = bin(v) |
954 obsolete_hash = bin(v) |
|
955 elif k == b"topo-mode": |
|
956 if v == b"pure": |
|
957 has_pure_topo_heads = True |
|
958 else: |
|
959 msg = b"unknown topo-mode: %r" % v |
|
960 raise ValueError(msg) |
928 else: |
961 else: |
929 msg = b"unknown cache key: %r" % k |
962 msg = b"unknown cache key: %r" % k |
930 raise ValueError(msg) |
963 raise ValueError(msg) |
931 args["key_hashes"] = (filtered_hash, obsolete_hash) |
964 args["key_hashes"] = (filtered_hash, obsolete_hash) |
|
965 if has_pure_topo_heads: |
|
966 pure_line = next(lineiter).rstrip(b'\n') |
|
967 args["pure_topo_branch"] = encoding.tolocal(pure_line) |
932 return args |
968 return args |
933 |
969 |
934 def _load_heads(self, repo, lineiter): |
970 def _load_heads(self, repo, lineiter): |
935 """fully loads the branchcache by reading from the file using the line |
971 """fully loads the branchcache by reading from the file using the line |
936 iterator passed""" |
972 iterator passed""" |
937 super()._load_heads(repo, lineiter) |
973 super()._load_heads(repo, lineiter) |
|
974 if self._pure_topo_branch is not None: |
|
975 # no need to read the repository heads, we know their value already. |
|
976 return |
938 cl = repo.changelog |
977 cl = repo.changelog |
939 getbranchinfo = repo.revbranchcache().branchinfo |
978 getbranchinfo = repo.revbranchcache().branchinfo |
940 obsrevs = obsolete.getrevs(repo, b'obsolete') |
979 obsrevs = obsolete.getrevs(repo, b'obsolete') |
941 to_node = cl.node |
980 to_node = cl.node |
942 touched_branch = set() |
981 touched_branch = set() |
957 """return the cache key hashes that match this repoview state""" |
996 """return the cache key hashes that match this repoview state""" |
958 return scmutil.filtered_and_obsolete_hash( |
997 return scmutil.filtered_and_obsolete_hash( |
959 repo, |
998 repo, |
960 self.tiprev, |
999 self.tiprev, |
961 ) |
1000 ) |
|
1001 |
|
1002 def _process_new( |
|
1003 self, |
|
1004 repo, |
|
1005 newbranches, |
|
1006 new_closed, |
|
1007 obs_ignored, |
|
1008 max_rev, |
|
1009 ) -> None: |
|
1010 if ( |
|
1011 # note: the check about `obs_ignored` is too strict as the |
|
1012 # obsolete revision could be non-topological, but lets keep |
|
1013 # things simple for now |
|
1014 # |
|
1015 # The same apply to `new_closed` if the closed changeset are |
|
1016 # not a head, we don't care that it is closed, but lets keep |
|
1017 # things simple here too. |
|
1018 not (obs_ignored or new_closed) |
|
1019 and ( |
|
1020 not newbranches |
|
1021 or ( |
|
1022 len(newbranches) == 1 |
|
1023 and ( |
|
1024 self.tiprev == nullrev |
|
1025 or self._pure_topo_branch in newbranches |
|
1026 ) |
|
1027 ) |
|
1028 ) |
|
1029 ): |
|
1030 if newbranches: |
|
1031 assert len(newbranches) == 1 |
|
1032 self._pure_topo_branch = list(newbranches.keys())[0] |
|
1033 self._needs_populate = True |
|
1034 self._entries.pop(self._pure_topo_branch, None) |
|
1035 return |
|
1036 |
|
1037 self._ensure_populated(repo) |
|
1038 self._pure_topo_branch = None |
|
1039 super()._process_new( |
|
1040 repo, |
|
1041 newbranches, |
|
1042 new_closed, |
|
1043 obs_ignored, |
|
1044 max_rev, |
|
1045 ) |
|
1046 |
|
1047 def _ensure_populated(self, repo): |
|
1048 """make sure any lazily loaded values are fully populated""" |
|
1049 if self._needs_populate: |
|
1050 assert self._pure_topo_branch is not None |
|
1051 cl = repo.changelog |
|
1052 to_node = cl.node |
|
1053 topo_heads = self._get_topo_heads(repo) |
|
1054 heads = [to_node(r) for r in topo_heads] |
|
1055 self._entries[self._pure_topo_branch] = heads |
|
1056 self._needs_populate = False |
962 |
1057 |
963 |
1058 |
964 class remotebranchcache(_BaseBranchCache): |
1059 class remotebranchcache(_BaseBranchCache): |
965 """Branchmap info for a remote connection, should not write locally""" |
1060 """Branchmap info for a remote connection, should not write locally""" |
966 |
1061 |