comparison mercurial/branchmap.py @ 51529:4141d12de073

branchcache: store filtered hash and obsolete hash independently for V3 This will avoid the bug covered in tests/test-branches-obsolete.t when we stop storing all heads explicitly in V3.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 06 Mar 2024 11:39:44 +0100
parents fa9e3976a5a0
children f85f23f1479b
comparison
equal deleted inserted replaced
51528:88b0e07dd2cd 51529:4141d12de073
811 The first line is used to check if the cache is still valid. It is a series 811 The first line is used to check if the cache is still valid. It is a series
812 of key value pair. The following key are recognized: 812 of key value pair. The following key are recognized:
813 813
814 - tip-rev: the rev-num of the tip-most revision seen by this cache 814 - tip-rev: the rev-num of the tip-most revision seen by this cache
815 - tip-node: the node-id of the tip-most revision sen by this cache 815 - tip-node: the node-id of the tip-most revision sen by this cache
816 - filtered-hash: the hash of all filtered and obsolete revisions (before 816 - filtered-hash: the hash of all filtered revisions (before tip-rev)
817 ignored by this cache.
818 - obsolete-hash: the hash of all non-filtered obsolete revisions (before
817 tip-rev) ignored by this cache. 819 tip-rev) ignored by this cache.
818 820
819 The tip-rev is used to know how far behind the value in the file are 821 The tip-rev is used to know how far behind the value in the file are
820 compared to the current repository state. 822 compared to the current repository state.
821 823
822 The tip-node and filtered-hash are used to detect if this cache can be used 824 The tip-node, filtered-hash and obsolete-hash are used to detect if this
823 for this repository state at all. 825 cache can be used for this repository state at all.
824 826
825 The open/closed state is represented by a single letter 'o' or 'c'. 827 The open/closed state is represented by a single letter 'o' or 'c'.
826 This field can be used to avoid changelog reads when determining if a 828 This field can be used to avoid changelog reads when determining if a
827 branch head closes a branch or not. 829 branch head closes a branch or not.
828 """ 830 """
829 831
830 _base_filename = b"branch3" 832 _base_filename = b"branch3"
833 _default_key_hashes = (None, None)
831 834
832 def _write_header(self, fp) -> None: 835 def _write_header(self, fp) -> None:
833 cache_keys = { 836 cache_keys = {
834 b"tip-node": hex(self.tipnode), 837 b"tip-node": hex(self.tipnode),
835 b"tip-rev": b'%d' % self.tiprev, 838 b"tip-rev": b'%d' % self.tiprev,
836 } 839 }
837 if self.key_hashes: 840 if self.key_hashes:
838 cache_keys[b"filtered-hash"] = hex(self.key_hashes[0]) 841 if self.key_hashes[0] is not None:
842 cache_keys[b"filtered-hash"] = hex(self.key_hashes[0])
843 if self.key_hashes[1] is not None:
844 cache_keys[b"obsolete-hash"] = hex(self.key_hashes[1])
839 pieces = (b"%s=%s" % i for i in sorted(cache_keys.items())) 845 pieces = (b"%s=%s" % i for i in sorted(cache_keys.items()))
840 fp.write(b" ".join(pieces) + b'\n') 846 fp.write(b" ".join(pieces) + b'\n')
841 847
842 @classmethod 848 @classmethod
843 def _load_header(cls, repo, lineiter): 849 def _load_header(cls, repo, lineiter):
844 header_line = next(lineiter) 850 header_line = next(lineiter)
845 pieces = header_line.rstrip(b'\n').split(b" ") 851 pieces = header_line.rstrip(b'\n').split(b" ")
846 cache_keys = dict(p.split(b'=', 1) for p in pieces) 852 cache_keys = dict(p.split(b'=', 1) for p in pieces)
847 853
848 args = {} 854 args = {}
855 filtered_hash = None
856 obsolete_hash = None
849 for k, v in cache_keys.items(): 857 for k, v in cache_keys.items():
850 if k == b"tip-rev": 858 if k == b"tip-rev":
851 args["tiprev"] = int(v) 859 args["tiprev"] = int(v)
852 elif k == b"tip-node": 860 elif k == b"tip-node":
853 args["tipnode"] = bin(v) 861 args["tipnode"] = bin(v)
854 elif k == b"filtered-hash": 862 elif k == b"filtered-hash":
855 args["key_hashes"] = (bin(v),) 863 filtered_hash = bin(v)
864 elif k == b"obsolete-hash":
865 obsolete_hash = bin(v)
856 else: 866 else:
857 msg = b"unknown cache key: %r" % k 867 msg = b"unknown cache key: %r" % k
858 raise ValueError(msg) 868 raise ValueError(msg)
869 args["key_hashes"] = (filtered_hash, obsolete_hash)
859 return args 870 return args
860 871
861 def _compute_key_hashes(self, repo) -> Tuple[bytes]: 872 def _compute_key_hashes(self, repo) -> Tuple[bytes]:
862 """return the cache key hashes that match this repoview state""" 873 """return the cache key hashes that match this repoview state"""
863 filtered_hash = scmutil.combined_filtered_and_obsolete_hash( 874 return scmutil.filtered_and_obsolete_hash(
864 repo, 875 repo,
865 self.tiprev, 876 self.tiprev,
866 needobsolete=True,
867 ) 877 )
868 if filtered_hash is None:
869 return cast(Tuple[bytes], ())
870 else:
871 return (filtered_hash,)
872 878
873 879
874 class remotebranchcache(_BaseBranchCache): 880 class remotebranchcache(_BaseBranchCache):
875 """Branchmap info for a remote connection, should not write locally""" 881 """Branchmap info for a remote connection, should not write locally"""
876 882