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.
--- 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