Commit Diff


commit - df1660cb6e65b4a211b8f5c8f547293ec65664d0
commit + 217768b3435b3cd6a99ae9e48fa2aabe76a53be8
blob - 13b839d893f897375de0e9464be3c3b29dadffc0
blob + 343b6117ebe64d95a55a143ce494976a471fdf46
--- NEWS
+++ NEWS
@@ -6,6 +6,10 @@
 
  * Support ``init.defaultBranch`` config.
    (Jelmer Vernooij)
+
+ * Fix ``ObjectStore.iterobjects_subset()`` when
+   hex shas are passed for objects that live in packs.
+   (Jelmer Vernooij, #1166)
 
 0.21.3	2023-02-17
 
blob - cdeb4e48504158eddfcbeb91dc1f6a6e24a834b6
blob + 3da419739f842a0eb30c491438864ac228e6df17
--- dulwich/objects.py
+++ dulwich/objects.py
@@ -34,6 +34,7 @@ from typing import (
     Tuple,
     Type,
     Union,
+    BinaryIO,
 )
 import zlib
 from collections import namedtuple
@@ -252,11 +253,11 @@ class FixedSha:
         self._hexsha = hexsha
         self._sha = hex_to_sha(hexsha)
 
-    def digest(self):
+    def digest(self) -> bytes:
         """Return the raw SHA digest."""
         return self._sha
 
-    def hexdigest(self):
+    def hexdigest(self) -> str:
         """Return the hex SHA digest."""
         return self._hexsha.decode("ascii")
 
@@ -273,7 +274,7 @@ class ShaFile:
     _sha: Union[FixedSha, None, HASH]
 
     @staticmethod
-    def _parse_legacy_object_header(magic, f) -> "ShaFile":
+    def _parse_legacy_object_header(magic, f: BinaryIO) -> "ShaFile":
         """Parse a legacy object, creating it but not reading the file."""
         bufsize = 1024
         decomp = zlib.decompressobj()
@@ -823,6 +824,7 @@ class Tag(ShaFile):
             if field == _OBJECT_HEADER:
                 self._object_sha = value
             elif field == _TYPE_HEADER:
+                assert isinstance(value, bytes)
                 obj_class = object_class(value)
                 if not obj_class:
                     raise ObjectFormatException("Not a known type: %s" % value)
@@ -895,7 +897,7 @@ class Tag(ShaFile):
                     self.as_raw_string(), mode=gpg.constants.sig.mode.DETACH
                 )
 
-    def verify(self, keyids: Optional[Iterable[str]] = None):
+    def verify(self, keyids: Optional[Iterable[str]] = None) -> None:
         """Verify GPG signature for this tag (if it is signed).
 
         Args:
@@ -937,7 +939,7 @@ class Tag(ShaFile):
 class TreeEntry(namedtuple("TreeEntry", ["path", "mode", "sha"])):
     """Named tuple encapsulating a single tree entry."""
 
-    def in_path(self, path):
+    def in_path(self, path: bytes):
         """Return a copy of this entry with the given path prepended."""
         if not isinstance(self.path, bytes):
             raise TypeError("Expected bytes for path, got %r" % path)
@@ -1011,11 +1013,11 @@ def sorted_tree_items(entries, name_order: bool):
         yield TreeEntry(name, mode, hexsha)
 
 
-def key_entry(entry):
+def key_entry(entry) -> bytes:
     """Sort key for tree entry.
 
     Args:
-      entry: (name, value) tuplee
+      entry: (name, value) tuple
     """
     (name, value) = entry
     if stat.S_ISDIR(value[0]):
@@ -1028,7 +1030,7 @@ def key_entry_name_order(entry):
     return entry[0]
 
 
-def pretty_format_tree_entry(name, mode, hexsha, encoding="utf-8"):
+def pretty_format_tree_entry(name, mode, hexsha, encoding="utf-8") -> str:
     """Pretty format tree entry.
 
     Args:
@@ -1185,7 +1187,7 @@ class Tree(ShaFile):
         return list(serialize_tree(self.iteritems()))
 
     def as_pretty_string(self):
-        text = []
+        text: List[str] = []
         for name, mode, hexsha in self.iteritems():
             text.append(pretty_format_tree_entry(name, mode, hexsha))
         return "".join(text)
blob - 6dd8ba484fd5d35b923e5307556454df49fdcab1
blob + 7bdfd764ef15678186d48e46dc6dc2bc575497f0
--- dulwich/pack.py
+++ dulwich/pack.py
@@ -2389,16 +2389,16 @@ class Pack:
             PackInflater.for_pack_data(self.data, resolve_ext_ref=self.resolve_ext_ref)
         )
 
-    def iterobjects_subset(self, shas, *, allow_missing: bool = False) -> Iterator[ShaFile]:
+    def iterobjects_subset(self, shas: Iterable[ObjectID], *, allow_missing: bool = False) -> Iterator[ShaFile]:
         return (
             uo
             for uo in
             PackInflater.for_pack_subset(
                 self, shas, allow_missing=allow_missing,
                 resolve_ext_ref=self.resolve_ext_ref)
-            if uo.sha() in shas)
+            if uo.id in shas)
 
-    def iter_unpacked_subset(self, shas, *, include_comp: bool = False, allow_missing: bool = False, convert_ofs_delta: bool = False) -> Iterator[UnpackedObject]:
+    def iter_unpacked_subset(self, shas: Iterable[ObjectID], *, include_comp: bool = False, allow_missing: bool = False, convert_ofs_delta: bool = False) -> Iterator[UnpackedObject]:
         ofs_pending: Dict[int, List[UnpackedObject]] = defaultdict(list)
         ofs: Dict[bytes, int] = {}
         todo = set(shas)
blob - 2e943609986c7ea6ebc3589588190336f10efdc4
blob + e9387bc69128f2a9e0054fe03a1a1934269f0d7f
--- dulwich/tests/test_pack.py
+++ dulwich/tests/test_pack.py
@@ -494,7 +494,13 @@ class TestPack(PackTests):
             self.assertIsInstance(objs[tree_sha], Tree)
             self.assertIsInstance(objs[commit_sha], Commit)
 
+    def test_iterobjects_subset(self):
+        with self.get_pack(pack1_sha) as p:
+            objs = {o.id: o for o in p.iterobjects_subset([commit_sha])}
+            self.assertEqual(1, len(objs))
+            self.assertIsInstance(objs[commit_sha], Commit)
 
+
 class TestThinPack(PackTests):
     def setUp(self):
         super().setUp()
@@ -1003,6 +1009,9 @@ class DeltaChainIteratorTests(TestCase):
         self.assertEntriesMatch([], entries, self.make_pack_iter_subset(f, []))
         f.seek(0)
         self.assertEntriesMatch([1, 0], entries, self.make_pack_iter_subset(f, [entries[0][3], entries[1][3]]))
+        f.seek(0)
+        self.assertEntriesMatch(
+            [1, 0], entries, self.make_pack_iter_subset(f, [sha_to_hex(entries[0][3]), sha_to_hex(entries[1][3])]))
 
     def test_ofs_deltas(self):
         f = BytesIO()