# HG changeset patch # User Pierre-Yves David # Date 2024-02-27 13:04:29 # Node ID ec640dc9cebd5ce9dddc783c79a20162205ffd68 # Parent c7e81615b5c4adb400eb9eb33babab282fbacafd 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. diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -4205,15 +4205,24 @@ def perfbranchmap(ui, repo, *filternames # 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 @@ def perfbranchmap(ui, repo, *filternames 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 @@ def perfbranchmapload(ui, repo, filter=b 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 diff --git a/mercurial/branchmap.py b/mercurial/branchmap.py --- a/mercurial/branchmap.py +++ b/mercurial/branchmap.py @@ -100,7 +100,7 @@ class BranchMapCache: 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 @@ class BranchMapCache: 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 @@ class BranchMapCache: 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 @@ class _BaseBranchCache: 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: - - [optional filtered repo hex hash] - - - ... - - 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_INHERITED = 2 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 @@ class branchcache(_BaseBranchCache): 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 @@ class branchcache(_BaseBranchCache): 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: + + [optional filtered repo hex hash] + + + ... + + 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"""