mercurial/branchmap.py
changeset 51527 fa9e3976a5a0
parent 51526 a03fa40afd01
child 51529 4141d12de073
equal deleted inserted replaced
51526:a03fa40afd01 51527:fa9e3976a5a0
    23     Optional,
    23     Optional,
    24     Set,
    24     Set,
    25     TYPE_CHECKING,
    25     TYPE_CHECKING,
    26     Tuple,
    26     Tuple,
    27     Union,
    27     Union,
       
    28     cast,
    28 )
    29 )
    29 
    30 
    30 from . import (
    31 from . import (
    31     encoding,
    32     encoding,
    32     error,
    33     error,
   407 
   408 
   408 class _LocalBranchCache(_BaseBranchCache):
   409 class _LocalBranchCache(_BaseBranchCache):
   409     """base class of branch-map info for a local repo or repoview"""
   410     """base class of branch-map info for a local repo or repoview"""
   410 
   411 
   411     _base_filename = None
   412     _base_filename = None
       
   413     _default_key_hashes: Tuple[bytes] = cast(Tuple[bytes], ())
   412 
   414 
   413     def __init__(
   415     def __init__(
   414         self,
   416         self,
   415         repo: "localrepo.localrepository",
   417         repo: "localrepo.localrepository",
   416         entries: Union[
   418         entries: Union[
   417             Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
   419             Dict[bytes, List[bytes]], Iterable[Tuple[bytes, List[bytes]]]
   418         ] = (),
   420         ] = (),
   419         tipnode: Optional[bytes] = None,
   421         tipnode: Optional[bytes] = None,
   420         tiprev: Optional[int] = nullrev,
   422         tiprev: Optional[int] = nullrev,
   421         filteredhash: Optional[bytes] = None,
   423         key_hashes: Optional[Tuple[bytes]] = None,
   422         closednodes: Optional[Set[bytes]] = None,
   424         closednodes: Optional[Set[bytes]] = None,
   423         hasnode: Optional[Callable[[bytes], bool]] = None,
   425         hasnode: Optional[Callable[[bytes], bool]] = None,
   424         verify_node: bool = False,
   426         verify_node: bool = False,
   425         inherited: bool = False,
   427         inherited: bool = False,
   426     ) -> None:
   428     ) -> None:
   431         if tipnode is None:
   433         if tipnode is None:
   432             self.tipnode = repo.nullid
   434             self.tipnode = repo.nullid
   433         else:
   435         else:
   434             self.tipnode = tipnode
   436             self.tipnode = tipnode
   435         self.tiprev = tiprev
   437         self.tiprev = tiprev
   436         self.filteredhash = filteredhash
   438         if key_hashes is None:
       
   439             self.key_hashes = self._default_key_hashes
       
   440         else:
       
   441             self.key_hashes = key_hashes
   437         self._state = STATE_CLEAN
   442         self._state = STATE_CLEAN
   438         if inherited:
   443         if inherited:
   439             self._state = STATE_INHERITED
   444             self._state = STATE_INHERITED
   440 
   445 
   441         super().__init__(repo=repo, entries=entries, closed_nodes=closednodes)
   446         super().__init__(repo=repo, entries=entries, closed_nodes=closednodes)
   448         # branches for which nodes are verified
   453         # branches for which nodes are verified
   449         self._verifiedbranches = set()
   454         self._verifiedbranches = set()
   450         self._hasnode = None
   455         self._hasnode = None
   451         if self._verify_node:
   456         if self._verify_node:
   452             self._hasnode = repo.changelog.hasnode
   457             self._hasnode = repo.changelog.hasnode
       
   458 
       
   459     def _compute_key_hashes(self, repo) -> Tuple[bytes]:
       
   460         raise NotImplementedError
   453 
   461 
   454     def validfor(self, repo):
   462     def validfor(self, repo):
   455         """check that cache contents are valid for (a subset of) this repo
   463         """check that cache contents are valid for (a subset of) this repo
   456 
   464 
   457         - False when the order of changesets changed or if we detect a strip.
   465         - False when the order of changesets changed or if we detect a strip.
   464             return False
   472             return False
   465         if self.tipnode != node:
   473         if self.tipnode != node:
   466             # tiprev doesn't correspond to tipnode: repo was stripped, or this
   474             # tiprev doesn't correspond to tipnode: repo was stripped, or this
   467             # repo has a different order of changesets
   475             # repo has a different order of changesets
   468             return False
   476             return False
   469         tiphash = scmutil.combined_filtered_and_obsolete_hash(
   477         repo_key_hashes = self._compute_key_hashes(repo)
   470             repo,
       
   471             self.tiprev,
       
   472             needobsolete=True,
       
   473         )
       
   474         # hashes don't match if this repo view has a different set of filtered
   478         # hashes don't match if this repo view has a different set of filtered
   475         # revisions (e.g. due to phase changes) or obsolete revisions (e.g.
   479         # revisions (e.g. due to phase changes) or obsolete revisions (e.g.
   476         # history was rewritten)
   480         # history was rewritten)
   477         return self.filteredhash == tiphash
   481         return self.key_hashes == repo_key_hashes
   478 
   482 
   479     @classmethod
   483     @classmethod
   480     def fromfile(cls, repo):
   484     def fromfile(cls, repo):
   481         f = None
   485         f = None
   482         try:
   486         try:
   511 
   515 
   512         return bcache
   516         return bcache
   513 
   517 
   514     @classmethod
   518     @classmethod
   515     def _load_header(cls, repo, lineiter) -> "dict[str, Any]":
   519     def _load_header(cls, repo, lineiter) -> "dict[str, Any]":
   516         """parse the head of a branchmap file
   520         raise NotImplementedError
   517 
       
   518         return parameters to pass to a newly created class instance.
       
   519         """
       
   520         cachekey = next(lineiter).rstrip(b'\n').split(b" ", 2)
       
   521         last, lrev = cachekey[:2]
       
   522         last, lrev = bin(last), int(lrev)
       
   523         filteredhash = None
       
   524         if len(cachekey) > 2:
       
   525             filteredhash = bin(cachekey[2])
       
   526         return {
       
   527             "tipnode": last,
       
   528             "tiprev": lrev,
       
   529             "filteredhash": filteredhash,
       
   530         }
       
   531 
   521 
   532     def _load_heads(self, repo, lineiter):
   522     def _load_heads(self, repo, lineiter):
   533         """fully loads the branchcache by reading from the file using the line
   523         """fully loads the branchcache by reading from the file using the line
   534         iterator passed"""
   524         iterator passed"""
   535         for line in lineiter:
   525         for line in lineiter:
   563             # always replaced, so no need to deepcopy until the above remains
   553             # always replaced, so no need to deepcopy until the above remains
   564             # true.
   554             # true.
   565             entries=self._entries,
   555             entries=self._entries,
   566             tipnode=self.tipnode,
   556             tipnode=self.tipnode,
   567             tiprev=self.tiprev,
   557             tiprev=self.tiprev,
   568             filteredhash=self.filteredhash,
   558             key_hashes=self.key_hashes,
   569             closednodes=set(self._closednodes),
   559             closednodes=set(self._closednodes),
   570             verify_node=self._verify_node,
   560             verify_node=self._verify_node,
   571             inherited=True,
   561             inherited=True,
   572         )
   562         )
   573         # also copy information about the current verification state
   563         # also copy information about the current verification state
   619                 b"couldn't write branch cache: %s\n"
   609                 b"couldn't write branch cache: %s\n"
   620                 % stringutil.forcebytestr(inst)
   610                 % stringutil.forcebytestr(inst)
   621             )
   611             )
   622 
   612 
   623     def _write_header(self, fp) -> None:
   613     def _write_header(self, fp) -> None:
   624         """write the branch cache header to a file"""
   614         raise NotImplementedError
   625         cachekey = [hex(self.tipnode), b'%d' % self.tiprev]
       
   626         if self.filteredhash is not None:
       
   627             cachekey.append(hex(self.filteredhash))
       
   628         fp.write(b" ".join(cachekey) + b'\n')
       
   629 
   615 
   630     def _write_heads(self, fp) -> int:
   616     def _write_heads(self, fp) -> int:
   631         """write list of heads to a file
   617         """write list of heads to a file
   632 
   618 
   633         Return the number of heads written."""
   619         Return the number of heads written."""
   712             # invalid for the repo.
   698             # invalid for the repo.
   713             #
   699             #
   714             # However. we've just updated the cache and we assume it's valid,
   700             # However. we've just updated the cache and we assume it's valid,
   715             # so let's make the cache key valid as well by recomputing it from
   701             # so let's make the cache key valid as well by recomputing it from
   716             # the cached data
   702             # the cached data
       
   703             self.key_hashes = self._compute_key_hashes(repo)
   717             self.filteredhash = scmutil.combined_filtered_and_obsolete_hash(
   704             self.filteredhash = scmutil.combined_filtered_and_obsolete_hash(
   718                 repo,
   705                 repo,
   719                 self.tiprev,
   706                 self.tiprev,
   720                 needobsolete=True,
       
   721             )
   707             )
   722 
   708 
   723         self._state = STATE_DIRTY
   709         self._state = STATE_DIRTY
   724         tr = repo.currenttransaction()
   710         tr = repo.currenttransaction()
   725         if getattr(tr, 'finalized', True):
   711         if getattr(tr, 'finalized', True):
   770     branch head closes a branch or not.
   756     branch head closes a branch or not.
   771     """
   757     """
   772 
   758 
   773     _base_filename = b"branch2"
   759     _base_filename = b"branch2"
   774 
   760 
       
   761     @classmethod
       
   762     def _load_header(cls, repo, lineiter) -> "dict[str, Any]":
       
   763         """parse the head of a branchmap file
       
   764 
       
   765         return parameters to pass to a newly created class instance.
       
   766         """
       
   767         cachekey = next(lineiter).rstrip(b'\n').split(b" ", 2)
       
   768         last, lrev = cachekey[:2]
       
   769         last, lrev = bin(last), int(lrev)
       
   770         filteredhash = ()
       
   771         if len(cachekey) > 2:
       
   772             filteredhash = (bin(cachekey[2]),)
       
   773         return {
       
   774             "tipnode": last,
       
   775             "tiprev": lrev,
       
   776             "key_hashes": filteredhash,
       
   777         }
       
   778 
       
   779     def _write_header(self, fp) -> None:
       
   780         """write the branch cache header to a file"""
       
   781         cachekey = [hex(self.tipnode), b'%d' % self.tiprev]
       
   782         if self.key_hashes:
       
   783             cachekey.append(hex(self.key_hashes[0]))
       
   784         fp.write(b" ".join(cachekey) + b'\n')
       
   785 
       
   786     def _compute_key_hashes(self, repo) -> Tuple[bytes]:
       
   787         """return the cache key hashes that match this repoview state"""
       
   788         filtered_hash = scmutil.combined_filtered_and_obsolete_hash(
       
   789             repo,
       
   790             self.tiprev,
       
   791             needobsolete=True,
       
   792         )
       
   793         keys: Tuple[bytes] = cast(Tuple[bytes], ())
       
   794         if filtered_hash is not None:
       
   795             keys: Tuple[bytes] = (filtered_hash,)
       
   796         return keys
       
   797 
   775 
   798 
   776 class BranchCacheV3(_LocalBranchCache):
   799 class BranchCacheV3(_LocalBranchCache):
   777     """a branch cache using version 3 of the format on disk
   800     """a branch cache using version 3 of the format on disk
   778 
   801 
   779     This version is still EXPERIMENTAL and the format is subject to changes.
   802     This version is still EXPERIMENTAL and the format is subject to changes.
   809     def _write_header(self, fp) -> None:
   832     def _write_header(self, fp) -> None:
   810         cache_keys = {
   833         cache_keys = {
   811             b"tip-node": hex(self.tipnode),
   834             b"tip-node": hex(self.tipnode),
   812             b"tip-rev": b'%d' % self.tiprev,
   835             b"tip-rev": b'%d' % self.tiprev,
   813         }
   836         }
   814         if self.filteredhash is not None:
   837         if self.key_hashes:
   815             cache_keys[b"filtered-hash"] = hex(self.filteredhash)
   838             cache_keys[b"filtered-hash"] = hex(self.key_hashes[0])
   816         pieces = (b"%s=%s" % i for i in sorted(cache_keys.items()))
   839         pieces = (b"%s=%s" % i for i in sorted(cache_keys.items()))
   817         fp.write(b" ".join(pieces) + b'\n')
   840         fp.write(b" ".join(pieces) + b'\n')
   818 
   841 
   819     @classmethod
   842     @classmethod
   820     def _load_header(cls, repo, lineiter):
   843     def _load_header(cls, repo, lineiter):
   827             if k == b"tip-rev":
   850             if k == b"tip-rev":
   828                 args["tiprev"] = int(v)
   851                 args["tiprev"] = int(v)
   829             elif k == b"tip-node":
   852             elif k == b"tip-node":
   830                 args["tipnode"] = bin(v)
   853                 args["tipnode"] = bin(v)
   831             elif k == b"filtered-hash":
   854             elif k == b"filtered-hash":
   832                 args["filteredhash"] = bin(v)
   855                 args["key_hashes"] = (bin(v),)
   833             else:
   856             else:
   834                 msg = b"unknown cache key: %r" % k
   857                 msg = b"unknown cache key: %r" % k
   835                 raise ValueError(msg)
   858                 raise ValueError(msg)
   836         return args
   859         return args
       
   860 
       
   861     def _compute_key_hashes(self, repo) -> Tuple[bytes]:
       
   862         """return the cache key hashes that match this repoview state"""
       
   863         filtered_hash = scmutil.combined_filtered_and_obsolete_hash(
       
   864             repo,
       
   865             self.tiprev,
       
   866             needobsolete=True,
       
   867         )
       
   868         if filtered_hash is None:
       
   869             return cast(Tuple[bytes], ())
       
   870         else:
       
   871             return (filtered_hash,)
   837 
   872 
   838 
   873 
   839 class remotebranchcache(_BaseBranchCache):
   874 class remotebranchcache(_BaseBranchCache):
   840     """Branchmap info for a remote connection, should not write locally"""
   875     """Branchmap info for a remote connection, should not write locally"""
   841 
   876