manifest: add a read_delta_new_entries method
This new method have a well defined semantic and can be adjusted by narrow as it
needs.
--- a/mercurial/interfaces/repository.py Thu Aug 01 13:15:54 2024 +0200
+++ b/mercurial/interfaces/repository.py Tue Aug 06 02:12:08 2024 +0200
@@ -1224,6 +1224,21 @@
The returned object conforms to the ``imanifestdict`` interface."""
+ def read_delta_new_entries(*, shallow=False):
+ """Return a manifest containing just the entries that might be new to
+ the repository.
+
+ This is often equivalent to a diff against both parents, but without
+ garantee. For performance reason, It might contains more files in some cases.
+
+ 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()``.
@@ -1477,6 +1492,10 @@
"""nodeconstants used by the current repository."""
)
+ narrowed = interfaceutil.Attribute(
+ """True, is the manifest is narrowed by a matcher"""
+ )
+
def __getitem__(node):
"""Obtain a manifest instance for a given binary node.
--- a/mercurial/manifest.py Thu Aug 01 13:15:54 2024 +0200
+++ b/mercurial/manifest.py Tue Aug 06 02:12:08 2024 +0200
@@ -2090,6 +2090,10 @@
"""
return self.get(b'', node)
+ @property
+ def narrowed(self):
+ return not (self._narrowmatch is None or self._narrowmatch.always())
+
def get(
self, tree: bytes, node: bytes, verify: bool = True
) -> AnyManifestCtx:
@@ -2317,6 +2321,20 @@
md.set(f, new_node, new_flag)
return md
+ def read_delta_new_entries(self, *, shallow=False) -> ManifestDict:
+ """see `interface.imanifestrevisionbase` documentations"""
+ # If we are using narrow, returning a delta against an arbitrary
+ # changeset might return file outside the narrowspec. This can create
+ # issue when running validation server side with strict security as
+ # push from low priviledge usage might be seen as adding new revision
+ # for files they cannot touch. So we are strict if narrow is involved.
+ if self._manifestlog.narrowed:
+ return self.read_delta_parents(shallow=shallow, exact=True)
+ store = self._storage()
+ r = store.rev(self._node)
+ d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
+ return manifestdict(store.nodeconstants.nodelen, d)
+
def find(self, key: bytes) -> Tuple[bytes, bytes]:
return self.read().find(key)
@@ -2576,6 +2594,23 @@
md.set(f, new_node, new_flag)
return md
+ def read_delta_new_entries(
+ self, *, shallow: bool = False
+ ) -> AnyManifestDict:
+ """see `interface.imanifestrevisionbase` documentations"""
+ # If we are using narrow, returning a delta against an arbitrary
+ # changeset might return file outside the narrowspec. This can create
+ # issue when running validation server side with strict security as
+ # push from low priviledge usage might be seen as adding new revision
+ # for files they cannot touch. So we are strict if narrow is involved.
+ if self._manifestlog.narrowed:
+ return self.read_delta_parents(shallow=shallow, exact=True)
+ # delegate to existing another existing method for simplicity
+ store = self._storage()
+ r = store.rev(self._node)
+ bases = (store.deltaparent(r),)
+ return self.read_any_fast_delta(bases, shallow=shallow)[1]
+
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