1313import utils
1414from git .utils import join_path
1515
16+ join = os .path .join
1617
1718def sha_to_hex (sha ):
1819 """Takes a string and returns the hex of the sha within"""
1920 hexsha = binascii .hexlify (sha )
2021 return hexsha
21-
22+
2223
2324class Tree (base .IndexObject , diff .Diffable , utils .Traversable , utils .Serializable ):
2425 """
@@ -47,38 +48,46 @@ class Tree(base.IndexObject, diff.Diffable, utils.Traversable, utils.Serializabl
4748 symlink_id = 012
4849 tree_id = 004
4950
51+ _map_id_to_type = {
52+ commit_id : Submodule ,
53+ blob_id : Blob ,
54+ symlink_id : Blob
55+ # tree id added once Tree is defined
56+ }
57+
5058
5159 def __init__ (self , repo , sha , mode = 0 , path = None ):
5260 super (Tree , self ).__init__ (repo , sha , mode , path )
5361
5462 @classmethod
5563 def _get_intermediate_items (cls , index_object ):
5664 if index_object .type == "tree" :
57- return index_object ._cache
65+ return tuple ( index_object ._iter_convert_to_object ( index_object . _cache ))
5866 return tuple ()
5967
60-
6168 def _set_cache_ (self , attr ):
6269 if attr == "_cache" :
6370 # Set the data when we need it
64- self ._cache = self ._get_tree_cache ()
71+ self ._cache = self ._get_tree_cache (self . data )
6572 else :
6673 super (Tree , self )._set_cache_ (attr )
6774
68- def _get_tree_cache (self , data = None ):
75+ def _get_tree_cache (self , data ):
6976 """ :return: list(object_instance, ...)
70- :param data: if not None, a byte string representing the tree data
71- If None, self.data will be used instead"""
72- out = list ()
73- if data is None :
74- data = self .data
75- for obj in self ._iter_from_data (data ):
76- if obj is not None :
77- out .append (obj )
78- # END if object was handled
79- # END for each line from ls-tree
80- return out
77+ :param data: data string containing our serialized information"""
78+ return list (self ._iter_from_data (data ))
8179
80+ def _iter_convert_to_object (self , iterable ):
81+ """Iterable yields tuples of (hexsha, mode, name), which will be converted
82+ to the respective object representation"""
83+ for hexsha , mode , name in iterable :
84+ path = join (self .path , name )
85+ type_id = mode >> 12
86+ try :
87+ yield self ._map_id_to_type [type_id ](self .repo , hexsha , mode , path )
88+ except KeyError :
89+ raise TypeError ( "Unknown type %i found in tree data for path '%s'" % (type_id , path ))
90+ # END for each item
8291
8392 def _iter_from_data (self , data ):
8493 """
@@ -87,8 +96,7 @@ def _iter_from_data(self, data):
8796
8897 Note: This method was inspired by the parse_tree method in dulwich.
8998
90- Returns
91- list(IndexObject, ...)
99+ :yield: Tuple(hexsha, mode, tree_relative_path)
92100 """
93101 ord_zero = ord ('0' )
94102 len_data = len (data )
@@ -105,7 +113,6 @@ def _iter_from_data(self, data):
105113 mode = (mode << 3 ) + (ord (data [i ]) - ord_zero )
106114 i += 1
107115 # END while reading mode
108- type_id = mode >> 12
109116
110117 # byte is space now, skip it
111118 i += 1
@@ -117,22 +124,13 @@ def _iter_from_data(self, data):
117124 i += 1
118125 # END while not reached NULL
119126 name = data [ns :i ]
120- path = join_path (self .path , name )
121127
122128 # byte is NULL, get next 20
123129 i += 1
124130 sha = data [i :i + 20 ]
125131 i = i + 20
126132
127- hexsha = sha_to_hex (sha )
128- if type_id == self .blob_id or type_id == self .symlink_id :
129- yield Blob (self .repo , hexsha , mode , path )
130- elif type_id == self .tree_id :
131- yield Tree (self .repo , hexsha , mode , path )
132- elif type_id == self .commit_id :
133- yield Submodule (self .repo , hexsha , mode , path )
134- else :
135- raise TypeError ( "Unknown type found in tree data %i for path '%s'" % (type_id , path ))
133+ yield (sha_to_hex (sha ), mode , name )
136134 # END for each byte in data stream
137135
138136
@@ -165,17 +163,17 @@ def __div__(self, file):
165163 else :
166164 # safety assertion - blobs are at the end of the path
167165 if i != len (tokens )- 1 :
168- raise KeyError (msg % file )
166+ raise KeyError (msg % file )
169167 return item
170168 # END handle item type
171169 # END for each token of split path
172170 if item == self :
173171 raise KeyError (msg % file )
174172 return item
175173 else :
176- for obj in self ._cache :
177- if obj . name == file :
178- return obj
174+ for info in self ._cache :
175+ if info [ 2 ] == file : # [2] == name
176+ return self . _map_id_to_type [ info [ 1 ] >> 12 ]( self . repo , info [ 0 ], info [ 1 ], join ( self . path , info [ 2 ]))
179177 # END for each obj
180178 raise KeyError ( msg % file )
181179 # END handle long paths
@@ -210,18 +208,19 @@ def traverse( self, predicate = lambda i,d: True,
210208 return super (Tree , self ).traverse (predicate , prune , depth , branch_first , visit_once , ignore_self )
211209
212210 # List protocol
213- def __getslice__ (self ,i , j ):
214- return self ._cache [i :j ]
211+ def __getslice__ (self , i , j ):
212+ return list ( self ._iter_convert_to_object ( self . _cache [i :j ]))
215213
216214 def __iter__ (self ):
217- return iter (self ._cache )
215+ return self . _iter_convert_to_object (self ._cache )
218216
219217 def __len__ (self ):
220218 return len (self ._cache )
221219
222- def __getitem__ (self ,item ):
220+ def __getitem__ (self , item ):
223221 if isinstance (item , int ):
224- return self ._cache [item ]
222+ info = self ._cache [item ]
223+ return self ._map_id_to_type [info [1 ] >> 12 ](self .repo , info [0 ], info [1 ], join (self .path , info [2 ]))
225224
226225 if isinstance (item , basestring ):
227226 # compatability
@@ -231,19 +230,26 @@ def __getitem__(self,item):
231230 raise TypeError ( "Invalid index type: %r" % item )
232231
233232
234- def __contains__ (self ,item ):
233+ def __contains__ (self , item ):
235234 if isinstance (item , base .IndexObject ):
236- return item in self ._cache
237-
235+ for info in self ._cache :
236+ if item .sha == info [0 ]:
237+ return True
238+ # END compare sha
239+ # END for each entry
240+ # END handle item is index object
238241 # compatability
239- for obj in self ._cache :
240- if item == obj .path :
242+
243+ # treat item as repo-relative path
244+ path = self .path
245+ for info in self ._cache :
246+ if item == join (path , info [2 ]):
241247 return True
242248 # END for each item
243249 return False
244250
245251 def __reversed__ (self ):
246- return reversed (self ._cache )
252+ return reversed (self ._iter_convert_to_object ( self . _cache ) )
247253
248254 def _serialize (self , stream , presort = False ):
249255 """Serialize this tree into the stream. Please note that we will assume
@@ -256,25 +262,27 @@ def _serialize(self, stream, presort=False):
256262 bit_mask = 7 # 3 bits set
257263 hex_to_bin = binascii .a2b_hex
258264
259- for item in self ._cache :
260- mode = ''
261- mb = item .mode
265+ for hexsha , mode , name in self ._cache :
266+ mode_str = ''
262267 for i in xrange (6 ):
263- mode = chr (((mb >> (i * 3 )) & bit_mask ) + ord_zero ) + mode
268+ mode_str = chr (((mode >> (i * 3 )) & bit_mask ) + ord_zero ) + mode_str
264269 # END for each 8 octal value
270+
265271 # git slices away the first octal if its zero
266- if mode [0 ] == '0' :
267- mode = mode [1 :]
272+ if mode_str [0 ] == '0' :
273+ mode_str = mode_str [1 :]
268274 # END save a byte
269275
270- # note: the cache currently contains repo-relative paths, not
271- # tree-relative ones. Maybe the cache should only contain
272- # actual tuples, which are converted to objects later
273- # TODO: do it so
274- stream .write ("%s %s\0 %s" % (mode , os .path .basename (item .path ), hex_to_bin (item .sha )))
276+ stream .write ("%s %s\0 %s" % (mode_str , name , hex_to_bin (hexsha )))
275277 # END for each item
276278 return self
277279
278280 def _deserialize (self , stream ):
279281 self ._cache = self ._get_tree_cache (stream .read ())
280282 return self
283+
284+
285+ # END tree
286+
287+ # finalize map definition
288+ Tree ._map_id_to_type [Tree .tree_id ] = Tree
0 commit comments