Skip to content

Commit 139d0b1

Browse files
Byroncodex
andcommitted
fix: index.add() now supports filters (#2021)
This is done by calling into `git hash-object` for correctness, instead of using a mostly incorrect custom implementation for this (lacks filters). <!-- agent --> GitCmdObjectDB inherited LooseObjectDB.store(), so despite its name, object writes bypassed Git and used gitdb's loose-object implementation. That path creates and chmods object files itself, which can fail during Index.add() on filesystems where those permission changes are unsupported. Override store() to stream new objects through `git hash-object -w --stdin`. This lets Git manage object creation and permissions consistently with the repository configuration. Retain the inherited implementation for pre-hashed objects and custom output streams, whose existing semantics hash-object cannot provide. Assisted-by: GPT 5.6 Co-authored-by: GPT 5.6 <codex@openai.com>
1 parent 9729ed3 commit 139d0b1

2 files changed

Lines changed: 35 additions & 2 deletions

File tree

git/db.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55

66
__all__ = ["GitCmdObjectDB", "GitDB"]
77

8-
from gitdb.base import OInfo, OStream
8+
from subprocess import PIPE
9+
10+
from gitdb.base import IStream, OInfo, OStream
911
from gitdb.db import GitDB, LooseObjectDB
1012
from gitdb.exc import BadObject
13+
from gitdb.fun import stream_copy
1114

1215
from git.util import bin_to_hex, hex_to_bin
1316
from git.exc import GitCommandError
@@ -46,6 +49,23 @@ def stream(self, binsha: bytes) -> OStream:
4649
hexsha, typename, size, stream = self._git.stream_object_data(bin_to_hex(binsha))
4750
return OStream(hex_to_bin(hexsha), typename, size, stream)
4851

52+
def store(self, istream: IStream) -> IStream:
53+
"""Store an object using git itself."""
54+
if istream.binsha is not None or self.ostream() is not None:
55+
return super().store(istream)
56+
57+
proc = self._git.hash_object("-t", istream.type, "-w", "--stdin", as_process=True, istream=PIPE)
58+
assert proc.stdin is not None
59+
try:
60+
stream_copy(istream.read, proc.stdin.write, istream.size, self.stream_chunk_size)
61+
finally:
62+
proc.stdin.close()
63+
assert proc.stdout is not None
64+
hexsha = proc.stdout.read().strip()
65+
proc.wait()
66+
istream.binsha = hex_to_bin(hexsha)
67+
return istream
68+
4969
# { Interface
5070

5171
def partial_to_complete_sha_hex(self, partial_hexsha: str) -> bytes:

test/test_db.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,29 @@
33
# This module is part of GitPython and is released under the
44
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
55

6+
from io import BytesIO
67
import os.path as osp
8+
from unittest import mock
9+
10+
from gitdb import IStream
11+
from gitdb.db import LooseObjectDB
712

813
from git.db import GitCmdObjectDB
914
from git.exc import BadObject
1015
from git.util import bin_to_hex
1116

12-
from test.lib import TestBase
17+
from test.lib import TestBase, with_rw_repo
1318

1419

1520
class TestDB(TestBase):
21+
@with_rw_repo("HEAD")
22+
def test_store_uses_hash_object(self, rw_repo):
23+
data = b"hello world"
24+
with mock.patch.object(LooseObjectDB, "store", side_effect=AssertionError("unexpected loose-object write")):
25+
istream = rw_repo.odb.store(IStream("blob", len(data), BytesIO(data)))
26+
27+
assert rw_repo.odb.stream(istream.binsha).read() == data
28+
1629
def test_base(self):
1730
gdb = GitCmdObjectDB(osp.join(self.rorepo.git_dir, "objects"), self.rorepo.git)
1831

0 commit comments

Comments
 (0)