changeset 51781:a891347058e7

manifest: introduce a `read_delta_parents` method This new method have a clearer semantic and can be used by code that need this semantic. This should avoid bugs, allow for more targeted optimisation, and provide a clearer API. Users will be updated in subsequent changesets. This is also part of the wider effort to clarify and fix this API. one more method coming.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 01 Aug 2024 13:10:09 +0200
parents 852bd109dd55
children 4704c4c79f21
files mercurial/interfaces/repository.py mercurial/manifest.py
diffstat 2 files changed, 104 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/interfaces/repository.py	Thu Aug 01 12:14:40 2024 +0200
+++ b/mercurial/interfaces/repository.py	Thu Aug 01 13:10:09 2024 +0200
@@ -1210,6 +1210,20 @@
         The returned object conforms to the ``imanifestdict`` interface.
         """
 
+    def read_delta_parents(*, shallow=False, exact=True):
+        """return a diff from this revision against both parents.
+
+        If `exact` is False, this might return a superset of the diff, containing
+        files that are actually present as is in one of the parents.
+
+        If `shallow` is True, this will read the delta for this directory,
+        without recursively reading subdirectory manifests. Instead, any
+        subdirectory entry will be reported as it appears in the manifest, i.e.
+        the subdirectory will be reported among files and distinguished only by
+        its 't' flag. This only apply if the underlying manifest support it.
+
+        The returned object conforms to the ``imanifestdict`` interface."""
+
     def readfast(shallow=False):
         """Calls either ``read()`` or ``readdelta()``.
 
--- a/mercurial/manifest.py	Thu Aug 01 12:14:40 2024 +0200
+++ b/mercurial/manifest.py	Thu Aug 01 13:10:09 2024 +0200
@@ -2283,6 +2283,40 @@
             )
         return (None, self.read())
 
+    def read_delta_parents(
+        self,
+        *,
+        shallow: bool = False,
+        exact: bool = True,
+    ) -> ManifestDict:
+        """see `interface.imanifestrevisionbase` documentations"""
+        store = self._storage()
+        r = store.rev(self._node)
+        deltaparent = store.deltaparent(r)
+        parents = [p for p in store.parentrevs(r) if p is not nullrev]
+        if not exact and deltaparent in parents:
+            d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
+            return manifestdict(store.nodeconstants.nodelen, d)
+        elif not exact or len(parents) == 0:
+            return self.read()
+        elif len(parents) == 1:
+            p = parents[0]
+            d = mdiff.patchtext(store.revdiff(p, r))
+            return manifestdict(store.nodeconstants.nodelen, d)
+        else:
+            p1, p2 = parents
+            d1 = mdiff.patchtext(store.revdiff(p1, r))
+            d2 = mdiff.patchtext(store.revdiff(p2, r))
+            d1 = manifestdict(store.nodeconstants.nodelen, d1)
+            d2 = manifestdict(store.nodeconstants.nodelen, d2)
+            md = manifestdict(store.nodeconstants.nodelen)
+            for f, new_node, new_flag in d1.iterentries():
+                if f not in d2:
+                    continue
+                if new_node is not None:
+                    md.set(f, new_node, new_flag)
+            return md
+
     def find(self, key: bytes) -> Tuple[bytes, bytes]:
         return self.read().find(key)
 
@@ -2486,6 +2520,62 @@
                     md.setflag(f, fl1)
         return md
 
+    def read_delta_parents(
+        self,
+        *,
+        shallow: bool = False,
+        exact: bool = True,
+    ) -> AnyManifestDict:
+        """see `interface.imanifestrevisionbase` documentations"""
+        store = self._storage()
+        r = store.rev(self._node)
+        parents = [p for p in store.parentrevs(r) if p is not nullrev]
+        if not exact:
+            return self.read_any_fast_delta(parents, shallow=shallow)[1]
+        elif len(parents) == 0:
+            if shallow:
+                d = store.revision(self._node)
+                return manifestdict(store.nodeconstants.nodelen, d)
+            else:
+                return self.read()
+        elif len(parents) == 1:
+            p = parents[0]
+            if shallow:
+                d = mdiff.patchtext(store.revdiff(p, r))
+                return manifestdict(store.nodeconstants.nodelen, d)
+            else:
+                return self._read_storage_slow_delta(base=p)
+        else:
+            p1, p2 = parents
+            if shallow:
+                d1 = mdiff.patchtext(store.revdiff(p1, r))
+                d2 = mdiff.patchtext(store.revdiff(p2, r))
+                d1 = manifestdict(store.nodeconstants.nodelen, d1)
+                d2 = manifestdict(store.nodeconstants.nodelen, d2)
+                md = manifestdict(store.nodeconstants.nodelen)
+                for f, new_node, new_flag in d1.iterentries():
+                    if f not in d2:
+                        continue
+                    if new_node is not None:
+                        md.set(f, new_node, new_flag)
+                return md
+            else:
+                m1 = self._manifestlog.get(self._dir, store.node(p1)).read()
+                m2 = self._manifestlog.get(self._dir, store.node(p2)).read()
+                mc = self.read()
+                d1 = m1.diff(mc)
+                d2 = m2.diff(mc)
+                md = treemanifest(
+                    self._manifestlog.nodeconstants,
+                    dir=self._dir,
+                )
+                for f, new_node, new_flag in d1.iterentries():
+                    if f not in d2:
+                        continue
+                    if new_node is not None:
+                        md.set(f, new_node, new_flag)
+                return md
+
     def readfast(self, shallow=False) -> AnyManifestDict:
         """Calls either readdelta or read, based on which would be less work.
         readdelta is called if the delta is against the p1, and therefore can be