mercurial/store.py
changeset 50471 521fec115dad
parent 50469 53af67c70af0
child 50472 9fdc28e21b68
--- a/mercurial/store.py	Mon May 15 08:56:08 2023 +0200
+++ b/mercurial/store.py	Mon May 15 08:56:23 2023 +0200
@@ -10,9 +10,11 @@
 import os
 import re
 import stat
+from typing import Generator
 
 from .i18n import _
 from .pycompat import getattr
+from .thirdparty import attr
 from .node import hex
 from . import (
     changelog,
@@ -451,6 +453,20 @@
 FILETYPE_OTHER = FILEFLAGS_OTHER
 
 
+@attr.s(slots=True)
+class StoreEntry:
+    """An entry in the store
+
+    This is returned by `store.walk` and represent some data in the store."""
+
+    unencoded_path = attr.ib()
+    is_revlog = attr.ib(default=False)
+    revlog_type = attr.ib(default=None)
+    is_revlog_main = attr.ib(default=None)
+    is_volatile = attr.ib(default=False)
+    file_size = attr.ib(default=None)
+
+
 class basicstore:
     '''base class for local repository stores'''
 
@@ -500,7 +516,9 @@
         rootstore = manifest.manifestrevlog(repo.nodeconstants, self.vfs)
         return manifest.manifestlog(self.vfs, repo, rootstore, storenarrowmatch)
 
-    def datafiles(self, matcher=None, undecodable=None):
+    def datafiles(
+        self, matcher=None, undecodable=None
+    ) -> Generator[StoreEntry, None, None]:
         """Like walk, but excluding the changelog and root manifest.
 
         When [undecodable] is None, revlogs names that can't be
@@ -510,20 +528,35 @@
         files = self._walk(b'data', True) + self._walk(b'meta', True)
         for (t, u, s) in files:
             if t is not None:
-                yield (FILEFLAGS_FILELOG | t, u, s)
+                yield StoreEntry(
+                    unencoded_path=u,
+                    is_revlog=True,
+                    revlog_type=FILEFLAGS_FILELOG,
+                    is_revlog_main=bool(t & FILEFLAGS_REVLOG_MAIN),
+                    is_volatile=bool(t & FILEFLAGS_VOLATILE),
+                    file_size=s,
+                )
 
-    def topfiles(self):
+    def topfiles(self) -> Generator[StoreEntry, None, None]:
         # yield manifest before changelog
         files = reversed(self._walk(b'', False))
         for (t, u, s) in files:
             if u.startswith(b'00changelog'):
-                yield (FILEFLAGS_CHANGELOG | t, u, s)
+                revlog_type = FILEFLAGS_CHANGELOG
             elif u.startswith(b'00manifest'):
-                yield (FILEFLAGS_MANIFESTLOG | t, u, s)
+                revlog_type = FILEFLAGS_MANIFESTLOG
             else:
-                yield (FILETYPE_OTHER | t, u, s)
+                revlog_type = None
+            yield StoreEntry(
+                unencoded_path=u,
+                is_revlog=revlog_type is not None,
+                revlog_type=revlog_type,
+                is_revlog_main=bool(t & FILEFLAGS_REVLOG_MAIN),
+                is_volatile=bool(t & FILEFLAGS_VOLATILE),
+                file_size=s,
+            )
 
-    def walk(self, matcher=None):
+    def walk(self, matcher=None) -> Generator[StoreEntry, None, None]:
         """return files related to data storage (ie: revlogs)
 
         yields (file_type, unencoded, size)
@@ -576,9 +609,12 @@
     # However that might change so we should probably add a test and encoding
     # decoding for it too. see issue6548
 
-    def datafiles(self, matcher=None, undecodable=None):
-        for t, f1, size in super(encodedstore, self).datafiles():
+    def datafiles(
+        self, matcher=None, undecodable=None
+    ) -> Generator[StoreEntry, None, None]:
+        for entry in super(encodedstore, self).datafiles():
             try:
+                f1 = entry.unencoded_path
                 f2 = decodefilename(f1)
             except KeyError:
                 if undecodable is None:
@@ -589,7 +625,8 @@
                     continue
             if not _matchtrackedpath(f2, matcher):
                 continue
-            yield t, f2, size
+            entry.unencoded_path = f2
+            yield entry
 
     def join(self, f):
         return self.path + b'/' + encodefilename(f)
@@ -785,7 +822,9 @@
     def getsize(self, path):
         return self.rawvfs.stat(path).st_size
 
-    def datafiles(self, matcher=None, undecodable=None):
+    def datafiles(
+        self, matcher=None, undecodable=None
+    ) -> Generator[StoreEntry, None, None]:
         for f in sorted(self.fncache):
             if not _matchtrackedpath(f, matcher):
                 continue
@@ -799,7 +838,14 @@
                 continue
             t |= FILEFLAGS_FILELOG
             try:
-                yield t, f, self.getsize(ef)
+                yield StoreEntry(
+                    unencoded_path=f,
+                    is_revlog=True,
+                    revlog_type=FILEFLAGS_FILELOG,
+                    is_revlog_main=bool(t & FILEFLAGS_REVLOG_MAIN),
+                    is_volatile=bool(t & FILEFLAGS_VOLATILE),
+                    file_size=self.getsize(ef),
+                )
             except FileNotFoundError:
                 pass