@@ -21,56 +21,89 @@ class IndexEntry(tuple):
2121
2222 See the properties for a mapping between names and tuple indices.
2323 """
24- @property
25- def path (self ):
26- return self [0 ]
27-
2824 @property
2925 def ctime (self ):
3026 """
3127 Returns
3228 Tuple(int_time_seconds_since_epoch, int_nano_seconds) of the
3329 file's creation time
3430 """
35- return struct .unpack (">LL" , self [1 ])
31+ return struct .unpack (">LL" , self [0 ])
3632
3733 @property
3834 def mtime (self ):
3935 """
4036 See ctime property, but returns modification time
4137 """
42- return struct .unpack (">LL" , self [2 ])
38+ return struct .unpack (">LL" , self [1 ])
4339
4440 @property
4541 def dev (self ):
46- return self [3 ]
42+ """
43+ Device ID
44+ """
45+ return self [2 ]
4746
4847 @property
4948 def inode (self ):
50- return self [4 ]
49+ """
50+ Inode ID
51+ """
52+ return self [3 ]
5153
5254 @property
5355 def mode (self ):
54- return self [5 ]
56+ """
57+ File Mode, compatible to stat module constants
58+ """
59+ return self [4 ]
5560
5661 @property
5762 def uid (self ):
58- return self [6 ]
63+ """
64+ User ID
65+ """
66+ return self [5 ]
5967
6068 @property
6169 def gid (self ):
62- return self [7 ]
70+ """
71+ Group ID
72+ """
73+ return self [6 ]
6374
6475 @property
6576 def size (self ):
66- return self [8 ]
77+ """
78+ Uncompressed size of the blob
79+
80+ Note
81+ Will be 0 if the stage is not 0 ( hence it is an unmerged entry )
82+ """
83+ return self [7 ]
6784
6885 @property
6986 def sha (self ):
70- return self [9 ]
87+ """
88+ hex sha of the blob
89+ """
90+ return self [8 ]
7191
7292 @property
7393 def stage (self ):
94+ """
95+ Stage of the entry, either:
96+ 0 = default stage
97+ 1 = stage before a merge or common ancestor entry in case of a 3 way merge
98+ 2 = stage of entries from the 'left' side of the merge
99+ 3 = stage of entries from the right side of the merge
100+ Note:
101+ For more information, see http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html
102+ """
103+ return self [9 ]
104+
105+ @property
106+ def path (self ):
74107 return self [10 ]
75108
76109
@@ -80,22 +113,25 @@ class Index(object):
80113 order to save git command function calls wherever possible.
81114
82115 It provides custom merging facilities and to create custom commits.
116+
117+ ``Entries``
118+ The index contains an entries dict whose keys are tuples of
83119 """
84- __slots__ = ( "version" , "entries" )
120+ __slots__ = ( "version" , "entries" , "_extension_data" )
121+ _VERSION = 2 # latest version we support
85122
86123 def __init__ (self , stream = None ):
87124 """
88125 Initialize this Index instance, optionally from the given ``stream``
89-
90- Note
91- Reading is based on the dulwich project.
92126 """
93127 self .entries = dict ()
94- self .version = - 1
128+ self .version = self ._VERSION
129+ self ._extension_data = ''
95130 if stream is not None :
96131 self ._read_from_stream (stream )
97132
98- def _read_entry (self , stream ):
133+ @classmethod
134+ def _read_entry (cls , stream ):
99135 """Return: One entry of the given stream"""
100136 beginoffset = stream .tell ()
101137 ctime = struct .unpack (">8s" , stream .read (8 ))[0 ]
@@ -107,11 +143,11 @@ def _read_entry(self, stream):
107143
108144 real_size = ((stream .tell () - beginoffset + 8 ) & ~ 7 )
109145 data = stream .read ((beginoffset + real_size ) - stream .tell ())
110- return IndexEntry ((path , ctime , mtime , dev , ino , mode , uid , gid , size ,
111- binascii .hexlify (sha ), flags >> 12 ))
146+ return IndexEntry ((ctime , mtime , dev , ino , mode , uid , gid , size ,
147+ binascii .hexlify (sha ), flags >> 12 , path ))
112148
113-
114- def _read_header (self , stream ):
149+ @ classmethod
150+ def _read_header (cls , stream ):
115151 """Return tuple(version_long, num_entries) from the given stream"""
116152 type_id = stream .read (4 )
117153 if type_id != "DIRC" :
@@ -123,15 +159,20 @@ def _read_header(self, stream):
123159 def _read_from_stream (self , stream ):
124160 """
125161 Initialize this instance with index values read from the given stream
162+
163+ Note
164+ We explicitly do not clear the entries dict here to allow for reading
165+ multiple chunks from multiple streams into the same Index instance
126166 """
127167 self .version , num_entries = self ._read_header (stream )
128- self .entries = dict ()
129168 count = 0
130169 while count < num_entries :
131170 entry = self ._read_entry (stream )
132171 self .entries [(entry .path ,entry .stage )] = entry
133172 count += 1
134173 # END for each entry
174+ # this data chunk is the footer of the index, don't yet know what it is for
175+ self ._extension_data = stream .read (~ 0 )
135176
136177 @classmethod
137178 def from_file (cls , file_path ):
@@ -141,6 +182,9 @@ def from_file(cls, file_path):
141182
142183 ``file_pa ``
143184 File path pointing to git index file
185+
186+ Note
187+ Reading is based on the dulwich project.
144188 """
145189 fp = open (file_path , "r" )
146190
@@ -157,6 +201,49 @@ def from_file(cls, file_path):
157201 finally :
158202 fp .close ()
159203
204+
205+ @classmethod
206+ def to_file (cls , index , file_path ):
207+ """
208+ Write the index data to the given file path.
209+
210+ ``index``
211+ Index you wish to write.
212+
213+ ``file_path``
214+ Path at which to write the index data. Please note that missing directories
215+ will lead to an exception to be thrown.
216+
217+ Raise
218+ IOError if the file could not be written
219+ """
220+ fp = open (file_path , "w" )
221+ try :
222+ return index .write (fp )
223+ finally :
224+ fp .close ()
225+ # END exception handling
226+
227+
228+ @classmethod
229+ def _write_cache_entry (cls , stream , entry ):
230+ """
231+ Write an IndexEntry to a stream
232+ """
233+ beginoffset = stream .tell ()
234+ stream .write (entry [0 ]) # ctime
235+ stream .write (entry [1 ]) # mtime
236+ path = entry [10 ]
237+ plen = len (path ) & 0x0fff # path length
238+ assert plen == len (path ), "Path %s too long to fit into index" % entry [10 ]
239+ flags = plen | (entry [9 ] << 12 )# stage and path length are 2 byte flags
240+ stream .write (struct .pack (">LLLLLL20sH" , entry [2 ], entry [3 ], entry [4 ],
241+ entry [5 ], entry [6 ], entry [7 ], binascii .unhexlify (entry [8 ]), flags ))
242+ stream .write (path )
243+ real_size = ((stream .tell () - beginoffset + 8 ) & ~ 7 )
244+ stream .write ("\0 " * ((beginoffset + real_size ) - stream .tell ()))
245+
246+
160247 def write (self , stream ):
161248 """
162249 Write the current state to the given stream
@@ -166,5 +253,20 @@ def write(self, stream):
166253
167254 Returns
168255 self
256+
257+ Note
258+ Index writing based on the dulwich implementation
169259 """
170- raise NotImplementedError ( "TODO" )
260+ # header
261+ stream .write ("DIRC" )
262+ stream .write (struct .pack (">LL" , self .version , len (self .entries )))
263+
264+ # body
265+ entries_sorted = self .entries .values ()
266+ entries_sorted .sort (key = lambda e : (e [10 ], e [9 ])) # use path/stage as sort key
267+ for entry in entries_sorted :
268+ self ._write_cache_entry (stream , entry )
269+ # END for each entry
270+ # write extension_data which we currently cannot interprete
271+ stream .write (self ._extension_data )
272+
0 commit comments