132 lines
3.1 KiB
Python
132 lines
3.1 KiB
Python
|
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)
|