77import os
88import blob
99import base
10+ import binascii
11+
12+ def sha_to_hex (sha ):
13+ """Takes a string and returns the hex of the sha within"""
14+ hexsha = binascii .hexlify (sha )
15+ assert len (hexsha ) == 40 , "Incorrect length of sha1 string: %d" % hexsha
16+ return hexsha
1017
1118class Tree (base .IndexObject ):
1219 """
@@ -29,18 +36,23 @@ class Tree(base.IndexObject):
2936 type = "tree"
3037 __slots__ = "_cache"
3138
39+ # using ascii codes for comparison
40+ ascii_commit_id = (0x31 << 4 ) + 0x36
41+ ascii_blob_id = (0x31 << 4 ) + 0x30
42+ ascii_tree_id = (0x34 << 4 ) + 0x30
43+
44+
3245 def __init__ (self , repo , id , mode = 0 , path = None ):
3346 super (Tree , self ).__init__ (repo , id , mode , path )
3447
3548 def _set_cache_ (self , attr ):
3649 if attr == "_cache" :
3750 # Set the data when we need it
38- self ._cache = self ._get_tree_cache (self . repo , self . id )
51+ self ._cache = self ._get_tree_cache ()
3952 else :
4053 super (Tree , self )._set_cache_ (attr )
4154
42- @classmethod
43- def _get_tree_cache (cls , repo , treeish ):
55+ def _get_tree_cache (self ):
4456 """
4557 Return
4658 list(object_instance, ...)
@@ -49,45 +61,71 @@ def _get_tree_cache(cls, repo, treeish):
4961 sha or ref identifying a tree
5062 """
5163 out = list ()
52- for line in repo .git .ls_tree (treeish ).splitlines ():
53- obj = cls ._from_string (repo , line )
64+ for obj in self ._iter_from_data ():
5465 if obj is not None :
5566 out .append (obj )
5667 # END if object was handled
5768 # END for each line from ls-tree
5869 return out
5970
60-
61- @classmethod
62- def _from_string (cls , repo , text ):
71+
72+ def _iter_from_data (self ):
6373 """
64- Parse a content item and create the appropriate object
65-
66- ``repo``
67- is the Repo
68-
69- ``text``
70- is the single line containing the items data in `git ls-tree` format
71-
74+ Reads the binary non-pretty printed representation of a tree and converts
75+ it into Blob, Tree or Commit objects.
76+
77+ Note: This method was inspired by the parse_tree method in dulwich.
78+
7279 Returns
73- ``git.Blob`` or ``git.Tree``
74-
75- NOTE: Currently sub-modules are ignored !
80+ list(IndexObject, ...)
7681 """
77- try :
78- mode , typ , id , path = text .expandtabs (1 ).split (" " , 3 )
79- except :
80- return None
82+ ord_zero = ord ('0' )
83+ data = self .data
84+ len_data = len (data )
85+ i = 0
86+ while i < len_data :
87+ mode = 0
88+ mode_boundary = i + 6
89+
90+ # keep it ascii - we compare against the respective values
91+ type_id = (ord (data [i ])<< 4 ) + ord (data [i + 1 ])
92+ i += 2
93+
94+ while data [i ] != ' ' :
95+ # move existing mode integer up one level being 3 bits
96+ # and add the actual ordinal value of the character
97+ mode = (mode << 3 ) + (ord (data [i ]) - ord_zero )
98+ i += 1
99+ # END while reading mode
100+
101+ # byte is space now, skip it
102+ i += 1
103+
104+ # parse name, it is NULL separated
105+
106+ ns = i
107+ while data [i ] != '\0 ' :
108+ i += 1
109+ # END while not reached NULL
110+ name = data [ns :i ]
111+
112+ # byte is NULL, get next 20
113+ i += 1
114+ sha = data [i :i + 20 ]
115+ i = i + 20
116+
117+ hexsha = sha_to_hex (sha )
118+ if type_id == self .ascii_blob_id :
119+ yield blob .Blob (self .repo , hexsha , mode , name )
120+ elif type_id == self .ascii_tree_id :
121+ yield Tree (self .repo , hexsha , mode , name )
122+ elif type_id == self .ascii_commit_id :
123+ # todo
124+ yield None
125+ else :
126+ raise TypeError ( "Unknown type found in tree data: %i" % type_id )
127+ # END for each byte in data stream
81128
82- if typ == "tree" :
83- return Tree (repo , id , mode , path )
84- elif typ == "blob" :
85- return blob .Blob (repo , id , mode , path )
86- elif typ == "commit" :
87- # TODO: Return a submodule
88- return None
89- else :
90- raise (TypeError , "Invalid type: %s" % typ )
91129
92130 def __div__ (self , file ):
93131 """
0 commit comments