changeset 6789:cd8e1a697124 stable

topic: compatibility for branchmap.BranchCacheV2
author Anton Shestakov <av6@dwimlabs.net>
date Wed, 12 Jun 2024 19:27:31 +0400
parents 0674b56d3526
children 38a845d432e9
files hgext3rd/topic/topicmap.py
diffstat 1 files changed, 107 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/hgext3rd/topic/topicmap.py	Mon Mar 18 14:01:40 2024 -0300
+++ b/hgext3rd/topic/topicmap.py	Wed Jun 12 19:27:31 2024 +0400
@@ -121,26 +121,36 @@
     return ret
 
 def _wrapbmcache(ui):
-    class topiccache(_topiccache, branchmap.branchcache):
-        pass
-    branchmap.branchcache = topiccache
+    if util.safehasattr(branchmap, 'BranchCacheV2'):
+        class TopicCache(_TopicCacheV2, branchmap.BranchCacheV2):
+            pass
+        branchmap.BranchCacheV2 = TopicCache
 
-    try:
-        # Mercurial 5.0
-        class remotetopiccache(_topiccache, branchmap.remotebranchcache):
+        class remotetopiccache(_TopicCacheV2, branchmap.remotebranchcache):
             pass
         branchmap.remotebranchcache = remotetopiccache
+    else:
+        # hg <= 6.7 (ec640dc9cebd)
+        class topiccache(_topiccache, branchmap.branchcache):
+            pass
+        branchmap.branchcache = topiccache
 
-        def _wrapupdatebmcachemethod(orig, self, repo):
-            # pass in the bound method as the original
-            return _wrapupdatebmcache(orig.__get__(self), repo)
-        extensions.wrapfunction(branchmap.BranchMapCache, 'updatecache', _wrapupdatebmcachemethod)
-    except AttributeError:
-        # hg <= 4.9 (3461814417f3)
-        extensions.wrapfunction(branchmap, 'updatecache', _wrapupdatebmcache)
-        # branchcache in hg <= 4.9 doesn't have load method, instead there's a
-        # module-level function to read on-disk cache and return a branchcache
-        extensions.wrapfunction(branchmap, 'read', _wrapbmread)
+        try:
+            # Mercurial 5.0
+            class remotetopiccache(_topiccache, branchmap.remotebranchcache):
+                pass
+            branchmap.remotebranchcache = remotetopiccache
+
+            def _wrapupdatebmcachemethod(orig, self, repo):
+                # pass in the bound method as the original
+                return _wrapupdatebmcache(orig.__get__(self), repo)
+            extensions.wrapfunction(branchmap.BranchMapCache, 'updatecache', _wrapupdatebmcachemethod)
+        except AttributeError:
+            # hg <= 4.9 (3461814417f3)
+            extensions.wrapfunction(branchmap, 'updatecache', _wrapupdatebmcache)
+            # branchcache in hg <= 4.9 doesn't have load method, instead there's a
+            # module-level function to read on-disk cache and return a branchcache
+            extensions.wrapfunction(branchmap, 'read', _wrapbmread)
 
 def _wrapupdatebmcache(orig, repo):
     previous = getattr(repo, '_autobranchmaptopic', False)
@@ -239,6 +249,87 @@
         super(_topiccache, self).update(repo, revgen)
         self.phaseshash = _phaseshash(repo, self.tiprev)
 
+class _TopicCacheV2(object): # combine me with branchmap.BranchCacheV2
+
+    def __init__(self, *args, **kwargs):
+        super(_TopicCacheV2, self).__init__(*args, **kwargs)
+        self.phaseshash = None
+
+    def _load_heads(self, repo, lineiter):
+        """call BranchCacheV2._load_heads(), and then transform branch names to
+        be in the new "//" format
+        """
+        assert isinstance(self, branchmap.BranchCacheV2)  # help pytype
+        super(_TopicCacheV2, self)._load_heads(repo, lineiter)
+
+        for branch in tuple(self._entries):
+            formatted = common.formatfqbn(branch=branch)
+            if branch != formatted:
+                self._entries[formatted] = self._entries.pop(branch)
+
+    def validfor(self, repo):
+        """Is the cache content valid regarding a repo
+
+        - False when cached tipnode is unknown or if we detect a strip.
+        - True when cache is up to date or a subset of current repo."""
+        assert isinstance(self, (branchmap.BranchCacheV2, branchmap.remotebranchcache))  # help pytype
+        valid = super(_TopicCacheV2, self).validfor(repo)
+        if not valid:
+            return False
+        elif not istopicfilter(repo.filtername) or self.phaseshash is None:
+            # phasehash at None means this is a branchmap
+            # come from non topic thing
+            return True
+        else:
+            try:
+                valid = self.phaseshash == _phaseshash(repo, self.tiprev)
+                return valid
+            except IndexError:
+                return False
+
+    def write(self, repo):
+        """write cache to disk if it's not topic-only, but first transform
+        cache keys from branches in "//" format into bare branch names
+        """
+        # we expect mutable set to be small enough to be that computing it all
+        # the time will be fast enough
+        if not istopicfilter(repo.filtername):
+            entries = self._entries.copy()
+
+            for formatted in tuple(entries):
+                branch, tns, topic = common.parsefqbn(formatted)
+                if branch != formatted:
+                    entries[branch] = entries.pop(formatted)
+
+            oldentries = self._entries
+            try:
+                self._entries = entries
+                super(_TopicCacheV2, self).write(repo)
+            finally:
+                self._entries = oldentries
+
+    def update(self, repo, revgen):
+        """Given a branchhead cache, self, that may have extra nodes or be
+        missing heads, and a generator of nodes that are strictly a superset of
+        heads missing, this function updates self to be correct.
+        """
+        assert isinstance(self, (branchmap.BranchCacheV2, branchmap.remotebranchcache))  # help pytype
+        if not istopicfilter(repo.filtername):
+            return super(_TopicCacheV2, self).update(repo, revgen)
+
+        # See topic.discovery._headssummary(), where repo.unfiltered gets
+        # overridden to return .filtered('unfiltered-topic'). revbranchcache
+        # only can be created for unfiltered repo (filtername is None), so we
+        # do that here, and this revbranchcache will be cached inside repo.
+        # When we get rid of *-topic filters, then this workaround can be
+        # removed too.
+        repo.unfiltered().revbranchcache()
+
+        super(_TopicCacheV2, self).update(repo, revgen)
+        if util.safehasattr(self, 'tiprev'):
+            # remotebranchcache doesn't have self.tiprev
+            self.phaseshash = _phaseshash(repo, self.tiprev)
+
 def _wrapbmread(orig, repo):
     """call branchmap.read(), and then transform branch names to be in the
     new "//" format