1111import binascii
1212import mmap
1313import objects
14+ import tempfile
15+ import os
1416
1517class IndexEntry (tuple ):
1618 """
@@ -115,7 +117,8 @@ class Index(object):
115117 It provides custom merging facilities and to create custom commits.
116118
117119 ``Entries``
118- The index contains an entries dict whose keys are tuples of
120+ The index contains an entries dict whose keys are tuples of type IndexEntry
121+ to facilitate access.
119122 """
120123 __slots__ = ( "version" , "entries" , "_extension_data" )
121124 _VERSION = 2 # latest version we support
@@ -243,7 +246,6 @@ def _write_cache_entry(cls, stream, entry):
243246 real_size = ((stream .tell () - beginoffset + 8 ) & ~ 7 )
244247 stream .write ("\0 " * ((beginoffset + real_size ) - stream .tell ()))
245248
246-
247249 def write (self , stream ):
248250 """
249251 Write the current state to the given stream
@@ -269,4 +271,77 @@ def write(self, stream):
269271 # END for each entry
270272 # write extension_data which we currently cannot interprete
271273 stream .write (self ._extension_data )
272-
274+
275+
276+ @classmethod
277+ def from_tree (cls , repo , * treeish , ** kwargs ):
278+ """
279+ Merge the given treeish revisions into a new index which is returned.
280+ The original index will remain unaltered
281+
282+ ``repo``
283+ The repository treeish are located in.
284+
285+ ``*treeish``
286+ One, two or three Tree Objects or Commits. The result changes according to the
287+ amoutn of trees.
288+ If 1 Tree is given, it will just be read into a new index
289+ If 2 Trees are given, they will be merged into a new index using a
290+ two way merge algorithm. Tree 1 is the 'current' tree, tree 2 is the 'other'
291+ one.
292+ If 3 Trees are given, a 3-way merge will be performed with the first tree
293+ being the common ancestor of tree 2 and tree 3. Tree 2 is the 'current' tree,
294+ tree 3 is the 'other' one
295+
296+ ``**kwargs``
297+ Additional arguments passed to git-read-tree
298+
299+ Note:
300+ In the three-way merge case, --aggressive will be specified to automatically
301+ resolve more cases in a commonly correct manner. Specify trivial=True as kwarg
302+ to override that.
303+ """
304+ if len (treeish ) == 0 or len (treeish ) > 3 :
305+ raise ValueError ("Please specify between 1 and 3 treeish, got %i" % len (treeish ))
306+
307+ arg_list = list ()
308+ # ignore that working tree and index possibly are out of date
309+ if len (treeish )> 1 :
310+ # drop unmerged entries when reading our index and merging
311+ arg_list .append ("--reset" )
312+ # handle non-trivial cases the way a real merge does
313+ arg_list .append ("--aggressive" )
314+ # END merge handling
315+
316+ # tmp file created in git home directory to be sure renaming
317+ # works - /tmp/ dirs could be on another device
318+ tmp_index = tempfile .mktemp ('' ,'' ,repo .path )
319+ arg_list .append ("--index-output=%s" % tmp_index )
320+ arg_list .extend (treeish )
321+
322+ # move current index out of the way - otherwise the merge may fail
323+ # as it considers existing entries. moving it essentially clears the index.
324+ # Unfortunately there is no 'soft' way to do it
325+ cur_index = os .path .join (repo .path , 'index' )
326+ moved_index = os .path .join (repo .path , 'index_moved' + tempfile .mktemp ('' ,'' ,'' ))
327+ try :
328+ os .rename (cur_index , moved_index )
329+ repo .git .read_tree (* arg_list , ** kwargs )
330+ index = cls .from_file (tmp_index )
331+ finally :
332+ # put back the original index first !
333+ if os .path .exists (moved_index ):
334+ os .rename (moved_index , cur_index )
335+ if os .path .exists (tmp_index ):
336+ os .remove (tmp_index )
337+ # END index merge handling
338+
339+ return index
340+
341+ def write_tree (self , stream ):
342+ """
343+ Writes the
344+ """
345+ raise NotImplementedError ("TODO" )
346+
347+
0 commit comments