diff --git a/mercurial/interfaces/repository.py b/mercurial/interfaces/repository.py --- a/mercurial/interfaces/repository.py +++ b/mercurial/interfaces/repository.py @@ -1224,6 +1224,21 @@ class imanifestrevisionstored(imanifestr 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 @@ class imanifestlog(interfaceutil.Interfa """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. diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -2090,6 +2090,10 @@ class manifestlog: """ 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 @@ class ManifestCtx: 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 @@ class TreeManifestCtx: 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