from pathlib import Path, PurePath import hashlib import os from collections import namedtuple GIT_DIR = ".ugit" def init(): Path.mkdir(GIT_DIR) Path.mkdir(f"{GIT_DIR}/objects") RefValue = namedtuple("RefValue", ["symbolic", "value"]) def update_ref(ref, value): assert not value.symbolic ref_path = f"{GIT_DIR}/{ref}" Path.mkdir(ref_path, exist_ok=True) with open(ref_path, "w") as f: f.write(value.value) def get_ref(ref): ref_path = f"{GIT_DIR}/{ref}" value = None if Path.is_file(ref_path): with open(ref_path) as f: value = f.read().strip() if value and value.startswith("ref:"): return get_ref(value.split(":", 1)[1].strip()) return RefValue(symbolic=False, value=value) def iter_refs(): refs = ["HEAD"] for root, _, filenames in Path.walk(f"{GIT_DIR}/refs"): root = PurePath.relative_to(root, GIT_DIR) refs.extend(f"{root}/{name}" for name in filenames) for refname in refs: yield refname, get_ref(refname) def hash_object(data, type_="blob"): obj = type_.encode() + b"\x00" + data oid = hashlib.sha1(obj).hexdigest() with open(f"{GIT_DIR}/objects/{oid}", "wb") as out: out.write(obj) return oid def get_object(oid, expected="blob"): with open(f"{GIT_DIR}/objects/{oid}", "rb") as f: obj = f.read() type_, _, content = obj.partition(b"\x00") type_ = type_.decode() if expected is not None: assert type_ == expected, f"Expected {expected}, got {type_}" return content