import zlib import hashlib from abc import ABC, abstractmethod from libwyag import repository class GitObject: fmt: bytes = b"" repo = None def __init__(self, repo, data=None): self.repo = repo if data is not None: self.deserialize(data) @abstractmethod def serialize(self): """ This function MUST be implemented by subclasses. It must read the object's contents from self.data, a byte string, and do whatever it takes to convert it into a meaningful representation. What exactly that means depend on each subclass. """ return NotImplemented @abstractmethod def deserialize(self, data): return NotImplemented class GitBlob(GitObject): fmt = b"blob" def serialize(self): return self.blobdata def deserialize(self, data): self.blobdata = data class GitCommit(GitObject): pass class GitTree(GitObject): pass class GitTag(GitObject): pass def read_object(repo, sha): """Read object object_id from Git repository repo. Return a GitObject whose exact type depends on the object.""" path = repository.repo_file(repo, "objects", sha[0:2], sha[2:]) with open(path, "rb") as f: raw = zlib.decompress(f.read()) # Read object type x = raw.find(b" ") fmt = raw[0:x] # Read and validate object size y = raw.find(b"\x00", x) size = int(raw[x:y].decode("ascii")) if size != len(raw) - y - 1: raise Exception("Malformed object {0}: bad length".format(sha)) # Pick constructor if fmt == b"commit": c = GitCommit elif fmt == b"tree": c = GitTree elif fmt == b"tag": c = GitTag elif fmt == b"blob": c = GitBlob else: raise Exception(f"Unknown type {fmt.decode('ascii')} for object {sha}") # Call constructor and return object return c(repo, raw[y + 1 :]) def find_object(repo, name: str, fmt=None, follow=True): """ Name resolution function """ pass def write_object(obj: GitObject, actually_write=True): # Serialize object data data = obj.serialize() # Add header result = obj.fmt + b" " + str(len(data)).encode() + b"\x00" + data # Compute hash sha = hashlib.sha1(result).hexdigest() if actually_write: # Compute path path = repository.repo_file( obj.repo, "objects", sha[0:2], sha[2:], mkdir=actually_write ) with open(path, "wb") as f: # Compress and write f.write(zlib.compress(result)) return sha def hash_object(fd, fmt, repo=None): data = fd.read() # Choose constructor depending on # object type found in header. if fmt == b"commit": obj = GitCommit(repo, data) elif fmt == b"tree": obj = GitTree(repo, data) elif fmt == b"tag": obj = GitTag(repo, data) elif fmt == b"blob": obj = GitBlob(repo, data) else: raise Exception("Unknown type %s!" % fmt) return write_object(obj, repo)