wyag/libwyag/objects.py
2019-03-23 11:21:25 +01:00

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)