Skip to content

Instantly share code, notes, and snippets.

@mildsunrise
Last active June 10, 2024 22:57
Show Gist options
  • Save mildsunrise/2f9084ecce20cfe22179473ca508eb3c to your computer and use it in GitHub Desktop.
Save mildsunrise/2f9084ecce20cfe22179473ca508eb3c to your computer and use it in GitHub Desktop.
recursively creates git objects (trees, blobs) mirroring a directory in disk
import os, stat
from contextlib import contextmanager
from subprocess import run
@contextmanager
def fd_context(fd: int):
try:
yield fd
finally:
os.close(fd)
run_git = lambda *args, **kwargs: \
run(['git', *args], **kwargs, check=True, capture_output=True) \
.stdout.decode('ascii').strip()
def write_object(fd: int) -> tuple[str, str, str]:
''' recursively mirrors a file (or directory) as a git object, returning a (mode, type, object_id) tuple '''
st = os.fstat(fd)
if stat.S_ISREG(st.st_mode):
return '100644', 'blob', run_git('hash-object', '-w', '--no-filters', '--stdin', stdin=fd)
if stat.S_ISDIR(st.st_mode):
tree_desc = bytearray()
for fname in map(os.fsencode, os.listdir(fd)):
with fd_context(os.open(fname, os.O_RDONLY, dir_fd=fd)) as cfd:
entry_head = ' '.join(write_object(cfd)) + '\t'
tree_desc += entry_head.encode('ascii') + fname + b'\0'
return '040000', 'tree', run_git('mktree', '-z', input=tree_desc)
raise AssertionError(f'type {stat.S_IFMT(st.st_mode)} unsupported')
# FIXME: add support for symlinks (right now it errors out)
# FIXME: preserve mode
# FIXME: improve performance by using batch mode
# FIXME: ignore ENOENT errors when opening child (TOCTOU)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment