Skip to content

Commit 4dbefc1

Browse files
committed
index: write blobs via git hash-object, not gitdb's odb.store
1 parent 9729ed3 commit 4dbefc1

1 file changed

Lines changed: 23 additions & 14 deletions

File tree

git/index/base.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,13 @@
1111
import contextlib
1212
import datetime
1313
import glob
14-
from io import BytesIO
1514
import os
1615
import os.path as osp
1716
from stat import S_ISLNK
1817
import subprocess
1918
import sys
2019
import tempfile
2120

22-
from gitdb.base import IStream
2321
from gitdb.db import MemoryDB
2422

2523
from git.compat import defenc, force_bytes
@@ -34,6 +32,7 @@
3432
LockedFD,
3533
join_path_native,
3634
file_contents_ro,
35+
hex_to_bin,
3736
_is_path_rooted,
3837
_to_relative_path,
3938
to_native_path_linux,
@@ -58,7 +57,6 @@
5857

5958
from typing import (
6059
Any,
61-
BinaryIO,
6260
Callable,
6361
cast,
6462
Dict,
@@ -726,24 +724,35 @@ def _store_path(self, filepath: PathLike, fprogress: Callable) -> BaseIndexEntry
726724
"""
727725
st = os.lstat(filepath) # Handles non-symlinks as well.
728726

727+
fprogress(filepath, False, filepath)
729728
if S_ISLNK(st.st_mode):
730729
# In PY3, readlink is a string, but we need bytes.
731730
# In PY2, it was just OS encoded bytes, we assumed UTF-8.
732-
def open_stream() -> BinaryIO:
733-
return BytesIO(force_bytes(os.readlink(filepath), encoding=defenc))
731+
#
732+
# `git hash-object` on a symlink path follows the link and hashes the
733+
# target file's content, whereas Git stores the link's target string as
734+
# the blob content. So the target string is written to a regular
735+
# temporary file and hashed from there instead of passing the symlink
736+
# path directly.
737+
target = force_bytes(os.readlink(filepath), encoding=defenc)
738+
fd, tmp_path = tempfile.mkstemp()
739+
try:
740+
os.write(fd, target)
741+
os.close(fd)
742+
hexsha = self.repo.git.hash_object(tmp_path, w=True, no_filters=True)
743+
finally:
744+
os.remove(tmp_path)
734745
else:
735-
736-
def open_stream() -> BinaryIO:
737-
return open(filepath, "rb")
738-
739-
with open_stream() as stream:
740-
fprogress(filepath, False, filepath)
741-
istream = self.repo.odb.store(IStream(Blob.type, st.st_size, stream))
742-
fprogress(filepath, True, filepath)
746+
# Let the `git` binary write the object. This matches `git add`'s
747+
# behavior exactly, including applying any clean filters configured via
748+
# `.gitattributes`, and avoids reimplementing object creation (and its
749+
# object-file permission handling) in Python.
750+
hexsha = self.repo.git.hash_object(filepath, w=True)
751+
fprogress(filepath, True, filepath)
743752
return BaseIndexEntry(
744753
(
745754
stat_mode_to_index_mode(st.st_mode),
746-
istream.binsha,
755+
hex_to_bin(hexsha),
747756
0,
748757
to_native_path_linux(filepath),
749758
)

0 commit comments

Comments
 (0)