branchcache: use an explicit class for the v2 version
authorPierre-Yves David <pierre-yves.david@octobus.net>
Tue, 27 Feb 2024 14:04:29 +0100
changeset 51561 ec640dc9cebd
parent 51560 c7e81615b5c4
child 51562 fe8347b984f3
branchcache: use an explicit class for the v2 version This prepare the introduction of an experimental v3 format version. In the process, we move the description of the format in that new class.
contrib/perf.py
mercurial/branchmap.py
--- a/contrib/perf.py	Tue Feb 27 15:33:21 2024 +0100
+++ b/contrib/perf.py	Tue Feb 27 14:04:29 2024 +0100
@@ -4205,15 +4205,24 @@
         # add unfiltered
         allfilters.append(None)
 
-    if util.safehasattr(branchmap.branchcache, 'fromfile'):
+    old_branch_cache_from_file = None
+    branchcacheread = None
+    if util.safehasattr(branchmap, 'branch_cache_from_file'):
+        old_branch_cache_from_file = branchmap.branch_cache_from_file
+        branchmap.branch_cache_from_file = lambda *args: None
+    elif util.safehasattr(branchmap.branchcache, 'fromfile'):
         branchcacheread = safeattrsetter(branchmap.branchcache, b'fromfile')
         branchcacheread.set(classmethod(lambda *args: None))
     else:
         # older versions
         branchcacheread = safeattrsetter(branchmap, b'read')
         branchcacheread.set(lambda *args: None)
-    branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
-    branchcachewrite.set(lambda *args: None)
+    if util.safehasattr(branchmap, '_LocalBranchCache'):
+        branchcachewrite = safeattrsetter(branchmap._LocalBranchCache, b'write')
+        branchcachewrite.set(lambda *args: None)
+    else:
+        branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
+        branchcachewrite.set(lambda *args: None)
     try:
         for name in allfilters:
             printname = name
@@ -4221,7 +4230,10 @@
                 printname = b'unfiltered'
             timer(getbranchmap(name), title=printname)
     finally:
-        branchcacheread.restore()
+        if old_branch_cache_from_file is not None:
+            branchmap.branch_cache_from_file = old_branch_cache_from_file
+        if branchcacheread is not None:
+            branchcacheread.restore()
         branchcachewrite.restore()
     fm.end()
 
@@ -4381,10 +4393,10 @@
 
     repo.branchmap()  # make sure we have a relevant, up to date branchmap
 
-    try:
-        fromfile = branchmap.branchcache.fromfile
-    except AttributeError:
-        # older versions
+    fromfile = getattr(branchmap, 'branch_cache_from_file', None)
+    if fromfile is None:
+        fromfile = getattr(branchmap.branchcache, 'fromfile', None)
+    if fromfile is None:
         fromfile = branchmap.read
 
     currentfilter = filter
--- a/mercurial/branchmap.py	Tue Feb 27 15:33:21 2024 +0100
+++ b/mercurial/branchmap.py	Tue Feb 27 14:04:29 2024 +0100
@@ -100,7 +100,7 @@
         bcache = self._per_filter.get(filtername)
         if bcache is None or not bcache.validfor(repo):
             # cache object missing or cache object stale? Read from disk
-            bcache = branchcache.fromfile(repo)
+            bcache = branch_cache_from_file(repo)
 
         revs = []
         if bcache is None:
@@ -116,7 +116,7 @@
                 revs.extend(r for r in extrarevs if r <= bcache.tiprev)
             else:
                 # nothing to fall back on, start empty.
-                bcache = branchcache(repo)
+                bcache = new_branch_cache(repo)
 
         revs.extend(cl.revs(start=bcache.tiprev + 1))
         if revs:
@@ -147,7 +147,7 @@
 
         if rbheads:
             rtiprev = max((int(clrev(node)) for node in rbheads))
-            cache = branchcache(
+            cache = new_branch_cache(
                 repo,
                 remotebranchmap,
                 repo[rtiprev].node(),
@@ -199,21 +199,6 @@
 
     This cache is used to avoid costly computations to determine all the
     branch heads of a repo.
-
-    The cache is serialized on disk in the following format:
-
-    <tip hex node> <tip rev number> [optional filtered repo hex hash]
-    <branch head hex node> <open/closed state> <branch name>
-    <branch head hex node> <open/closed state> <branch name>
-    ...
-
-    The first line is used to check if the cache is still valid. If the
-    branch cache is for a filtered repo view, an optional third hash is
-    included that hashes the hashes of all filtered and obsolete revisions.
-
-    The open/closed state is represented by a single letter 'o' or 'c'.
-    This field can be used to avoid changelog reads when determining if a
-    branch head closes a branch or not.
     """
 
     def __init__(
@@ -420,10 +405,10 @@
 STATE_DIRTY = 3
 
 
-class branchcache(_BaseBranchCache):
-    """Branchmap info for a local repo or repoview"""
+class _LocalBranchCache(_BaseBranchCache):
+    """base class of branch-map info for a local repo or repoview"""
 
-    _base_filename = b"branch2"
+    _base_filename = None
 
     def __init__(
         self,
@@ -560,6 +545,7 @@
     def _filename(cls, repo):
         """name of a branchcache file for a given repo or repoview"""
         filename = cls._base_filename
+        assert filename is not None
         if repo.filtername:
             filename = b'%s-%s' % (filename, repo.filtername)
         return filename
@@ -741,6 +727,38 @@
             self.write(repo)
 
 
+def branch_cache_from_file(repo) -> Optional[_LocalBranchCache]:
+    """Build a branch cache from on-disk data if possible"""
+    return BranchCacheV2.fromfile(repo)
+
+
+def new_branch_cache(repo, *args, **kwargs):
+    """Build a new branch cache from argument"""
+    return BranchCacheV2(repo, *args, **kwargs)
+
+
+class BranchCacheV2(_LocalBranchCache):
+    """a branch cache using version 2 of the format on disk
+
+    The cache is serialized on disk in the following format:
+
+    <tip hex node> <tip rev number> [optional filtered repo hex hash]
+    <branch head hex node> <open/closed state> <branch name>
+    <branch head hex node> <open/closed state> <branch name>
+    ...
+
+    The first line is used to check if the cache is still valid. If the
+    branch cache is for a filtered repo view, an optional third hash is
+    included that hashes the hashes of all filtered and obsolete revisions.
+
+    The open/closed state is represented by a single letter 'o' or 'c'.
+    This field can be used to avoid changelog reads when determining if a
+    branch head closes a branch or not.
+    """
+
+    _base_filename = b"branch2"
+
+
 class remotebranchcache(_BaseBranchCache):
     """Branchmap info for a remote connection, should not write locally"""