diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2009-10-14 23:37:45 +0200 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2009-10-14 23:37:45 +0200 |
commit | 2e6d110fbfa1f2e6a96bc8329e936d0cf1192844 (patch) | |
tree | 2b792542f3de64e63763444ae350d2410c3d00e8 /lib/git | |
parent | 832b56394b079c9f6e4c777934447a9e224facfe (diff) | |
download | gitpython-2e6d110fbfa1f2e6a96bc8329e936d0cf1192844.tar.gz |
tree: now reads tress directly by parsing the binary data, allowing it to safe possibly hundreds of command calls
Diffstat (limited to 'lib/git')
-rw-r--r-- | lib/git/objects/base.py | 7 | ||||
-rw-r--r-- | lib/git/objects/tree.py | 102 | ||||
-rw-r--r-- | lib/git/repo.py | 16 |
3 files changed, 86 insertions, 39 deletions
diff --git a/lib/git/objects/base.py b/lib/git/objects/base.py index 6752a25e..07538ada 100644 --- a/lib/git/objects/base.py +++ b/lib/git/objects/base.py @@ -6,7 +6,8 @@ import os from git.utils import LazyMixin - +_assertion_msg_format = "Created object %r whose python type %r disagrees with the acutal git object type %r" + class Object(LazyMixin): """ Implements an Object which may be Blobs, Trees, Commits and Tags @@ -49,10 +50,10 @@ class Object(LazyMixin): """ if attr == "size": hexsha, typename, self.size = self.repo.git.get_object_header(self.id) - assert typename == self.type, "Created object whose python type %r disagrees with the acutal git object type %r" % (typename, self.type) + assert typename == self.type, _assertion_msg_format % (self.id, typename, self.type) elif attr == "data": hexsha, typename, self.size, self.data = self.repo.git.get_object_data(self.id) - assert typename == self.type, "Created object whose python type %r disagrees with the acutal git object type %r" % (typename, self.type) + assert typename == self.type, _assertion_msg_format % (self.id, typename, self.type) else: super(Object,self)._set_cache_(attr) diff --git a/lib/git/objects/tree.py b/lib/git/objects/tree.py index 01dfb37b..abfa9622 100644 --- a/lib/git/objects/tree.py +++ b/lib/git/objects/tree.py @@ -7,6 +7,13 @@ import os import blob import base +import binascii + +def sha_to_hex(sha): + """Takes a string and returns the hex of the sha within""" + hexsha = binascii.hexlify(sha) + assert len(hexsha) == 40, "Incorrect length of sha1 string: %d" % hexsha + return hexsha class Tree(base.IndexObject): """ @@ -29,18 +36,23 @@ class Tree(base.IndexObject): type = "tree" __slots__ = "_cache" + # using ascii codes for comparison + ascii_commit_id = (0x31 << 4) + 0x36 + ascii_blob_id = (0x31 << 4) + 0x30 + ascii_tree_id = (0x34 << 4) + 0x30 + + def __init__(self, repo, id, mode=0, path=None): super(Tree, self).__init__(repo, id, mode, path) def _set_cache_(self, attr): if attr == "_cache": # Set the data when we need it - self._cache = self._get_tree_cache(self.repo, self.id) + self._cache = self._get_tree_cache() else: super(Tree, self)._set_cache_(attr) - @classmethod - def _get_tree_cache(cls, repo, treeish): + def _get_tree_cache(self): """ Return list(object_instance, ...) @@ -49,45 +61,71 @@ class Tree(base.IndexObject): sha or ref identifying a tree """ out = list() - for line in repo.git.ls_tree(treeish).splitlines(): - obj = cls._from_string(repo, line) + for obj in self._iter_from_data(): if obj is not None: out.append(obj) # END if object was handled # END for each line from ls-tree return out - - @classmethod - def _from_string(cls, repo, text): + + def _iter_from_data(self): """ - Parse a content item and create the appropriate object - - ``repo`` - is the Repo - - ``text`` - is the single line containing the items data in `git ls-tree` format - + Reads the binary non-pretty printed representation of a tree and converts + it into Blob, Tree or Commit objects. + + Note: This method was inspired by the parse_tree method in dulwich. + Returns - ``git.Blob`` or ``git.Tree`` - - NOTE: Currently sub-modules are ignored ! + list(IndexObject, ...) """ - try: - mode, typ, id, path = text.expandtabs(1).split(" ", 3) - except: - return None + ord_zero = ord('0') + data = self.data + len_data = len(data) + i = 0 + while i < len_data: + mode = 0 + mode_boundary = i + 6 + + # keep it ascii - we compare against the respective values + type_id = (ord(data[i])<<4) + ord(data[i+1]) + i += 2 + + while data[i] != ' ': + # move existing mode integer up one level being 3 bits + # and add the actual ordinal value of the character + mode = (mode << 3) + (ord(data[i]) - ord_zero) + i += 1 + # END while reading mode + + # byte is space now, skip it + i += 1 + + # parse name, it is NULL separated + + ns = i + while data[i] != '\0': + i += 1 + # END while not reached NULL + name = data[ns:i] + + # byte is NULL, get next 20 + i += 1 + sha = data[i:i+20] + i = i + 20 + + hexsha = sha_to_hex(sha) + if type_id == self.ascii_blob_id: + yield blob.Blob(self.repo, hexsha, mode, name) + elif type_id == self.ascii_tree_id: + yield Tree(self.repo, hexsha, mode, name) + elif type_id == self.ascii_commit_id: + # todo + yield None + else: + raise TypeError( "Unknown type found in tree data: %i" % type_id ) + # END for each byte in data stream - if typ == "tree": - return Tree(repo, id, mode, path) - elif typ == "blob": - return blob.Blob(repo, id, mode, path) - elif typ == "commit": - # TODO: Return a submodule - return None - else: - raise(TypeError, "Invalid type: %s" % typ) def __div__(self, file): """ diff --git a/lib/git/repo.py b/lib/git/repo.py index d5dab242..f07edbe0 100644 --- a/lib/git/repo.py +++ b/lib/git/repo.py @@ -340,11 +340,19 @@ class Repo(object): if not isinstance(treeish, Ref): raise ValueError( "Treeish reference required, got %r" % treeish ) - # we should also check whether the ref has a valid commit ... but lets n - # not be over-critical + + # As we are directly reading object information, we must make sure + # we truly point to a tree object. We resolve the ref to a sha in all cases + # to assure the returned tree can be compared properly. Except for + # heads, ids should always be hexshas + hexsha, typename, size = self.git.get_object_header( treeish ) + if typename != "tree": + hexsha, typename, size = self.git.get_object_header( str(treeish)+'^{tree}' ) + # END tree handling + treeish = hexsha + # the root has an empty relative path and the default mode - root = Tree(self, treeish, 0, '') - return root + return Tree(self, treeish, 0, '') def diff(self, a, b, *paths): |