55# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66"""Module containing Index implementation, allowing to perform all kinds of index
77manipulations such as querying and merging. """
8- import binascii
98import tempfile
109import os
1110import sys
1211import subprocess
1312import glob
1413from cStringIO import StringIO
14+ from binascii import b2a_hex
1515
1616from stat import (
1717 S_ISLNK ,
2525from typ import (
2626 BaseIndexEntry ,
2727 IndexEntry ,
28- CE_NAMEMASK ,
29- CE_STAGESHIFT
3028 )
3129
3230from util import (
3331 TemporaryFileSwap ,
3432 post_clear_cache ,
3533 default_index ,
36- pack ,
37- unpack
3834 )
3935
4036import git .objects
6056 LockedFD ,
6157 join_path_native ,
6258 file_contents_ro ,
63- LockFile
64- )
65-
66-
67- from gitdb .base import (
68- IStream
6959 )
7060
7161from fun import (
7262 write_cache ,
7363 read_cache ,
64+ write_tree_from_cache ,
7465 entry_key
7566 )
7667
68+ from gitdb .base import IStream
69+
7770__all__ = ( 'IndexFile' , 'CheckoutError' )
7871
7972
@@ -161,10 +154,15 @@ def _deserialize(self, stream):
161154 self .version , self .entries , self ._extension_data , conten_sha = read_cache (stream )
162155 return self
163156
164- def _serialize (self , stream , ignore_tree_extension_data = False ):
157+ def _entries_sorted (self ):
158+ """:return: list of entries, in a sorted fashion, first by path, then by stage"""
165159 entries_sorted = self .entries .values ()
166- entries_sorted .sort (key = lambda e : (e [3 ], e .stage )) # use path/stage as sort key
167- write_cache (entries_sorted ,
160+ entries_sorted .sort (key = lambda e : (e .path , e .stage )) # use path/stage as sort key
161+ return entries_sorted
162+
163+ def _serialize (self , stream , ignore_tree_extension_data = False ):
164+ entries = self ._entries_sorted ()
165+ write_cache (entries ,
168166 stream ,
169167 (ignore_tree_extension_data and None ) or self ._extension_data )
170168 return self
@@ -403,7 +401,7 @@ def iter_blobs(self, predicate = lambda t: True):
403401 # TODO: is it necessary to convert the mode ? We did that when adding
404402 # it to the index, right ?
405403 mode = self ._stat_mode_to_index_mode (entry .mode )
406- blob = Blob (self .repo , entry .sha , mode , entry .path )
404+ blob = Blob (self .repo , entry .hexsha , mode , entry .path )
407405 blob .size = entry .size
408406 output = (entry .stage , blob )
409407 if predicate (output ):
@@ -490,33 +488,31 @@ def update(self):
490488 # allows to lazily reread on demand
491489 return self
492490
493- def _write_tree (self , missing_ok = False ):
491+ def write_tree (self ):
494492 """Writes this index to a corresponding Tree object into the repository's
495493 object database and return it.
496-
497- :param missing_ok:
498- If True, missing objects referenced by this index will not result
499- in an error.
500-
501- :return: Tree object representing this index"""
494+
495+ :return: Tree object representing this index
496+ :note: The tree will be written even if one or more objects the tree refers to
497+ does not yet exist in the object database. This could happen if you added
498+ Entries to the index directly.
499+ :raise ValueError: if there are no entries in the cache
500+ :raise UnmergedEntriesError: """
502501 # we obtain no lock as we just flush our contents to disk as tree
503502 if not self .entries :
504503 raise ValueError ("Cannot write empty index" )
505504
505+ # TODO: use memory db, this helps to prevent IO if the resulting tree
506+ # already exists
507+ entries = self ._entries_sorted ()
508+ binsha , tree_items = write_tree_from_cache (entries , self .repo .odb , slice (0 , len (entries )))
506509
510+ # note: additional deserialization could be saved if write_tree_from_cache
511+ # would return sorted tree entries
512+ root_tree = Tree (self .repo , b2a_hex (binsha ), path = '' )
513+ root_tree ._cache = tree_items
514+ return root_tree
507515
508- return Tree (self .repo , tree_sha , 0 , '' )
509-
510- def write_tree (self , missing_ok = False ):
511- index_path = self ._index_path ()
512- tmp_index_mover = TemporaryFileSwap (index_path )
513-
514- self .write (index_path , ignore_tree_extension_data = True )
515- tree_sha = self .repo .git .write_tree (missing_ok = missing_ok )
516-
517- del (tmp_index_mover ) # as soon as possible
518- return Tree (self .repo , tree_sha , 0 , '' )
519-
520516 def _process_diff_args (self , args ):
521517 try :
522518 args .pop (args .index (self ))
@@ -525,7 +521,6 @@ def _process_diff_args(self, args):
525521 # END remove self
526522 return args
527523
528-
529524 def _to_relative_path (self , path ):
530525 """:return: Version of path relative to our git directory or raise ValueError
531526 if it is not within our git direcotory"""
@@ -599,7 +594,7 @@ def add(self, items, force=True, fprogress=lambda *args: None, path_rewriter=Non
599594
600595 - BaseIndexEntry or type
601596 Handling equals the one of Blob objects, but the stage may be
602- explicitly set.
597+ explicitly set. Please note that Index Entries require binary sha's.
603598
604599 :param force:
605600 If True, otherwise ignored or excluded files will be
@@ -666,7 +661,7 @@ def store_path(filepath):
666661 fprogress (filepath , True , filepath )
667662
668663 return BaseIndexEntry ((self ._stat_mode_to_index_mode (st .st_mode ),
669- istream .sha , 0 , filepath ))
664+ istream .binsha , 0 , filepath ))
670665 # END utility method
671666
672667
@@ -691,14 +686,14 @@ def store_path(filepath):
691686
692687 # HANLDE ENTRY OBJECT CREATION
693688 # create objects if required, otherwise go with the existing shas
694- null_entries_indices = [ i for i ,e in enumerate (entries ) if e .sha == Object .NULL_HEX_SHA ]
689+ null_entries_indices = [ i for i ,e in enumerate (entries ) if e .binsha == Object .NULL_BIN_SHA ]
695690 if null_entries_indices :
696691 for ei in null_entries_indices :
697692 null_entry = entries [ei ]
698693 new_entry = store_path (null_entry .path )
699694
700695 # update null entry
701- entries [ei ] = BaseIndexEntry ((null_entry .mode , new_entry .sha , null_entry .stage , null_entry .path ))
696+ entries [ei ] = BaseIndexEntry ((null_entry .mode , new_entry .binsha , null_entry .stage , null_entry .path ))
702697 # END for each entry index
703698 # END null_entry handling
704699
@@ -707,7 +702,7 @@ def store_path(filepath):
707702 # all object sha's
708703 if path_rewriter :
709704 for i ,e in enumerate (entries ):
710- entries [i ] = BaseIndexEntry ((e .mode , e .sha , e .stage , path_rewriter (e )))
705+ entries [i ] = BaseIndexEntry ((e .mode , e .binsha , e .stage , path_rewriter (e )))
711706 # END for each entry
712707 # END handle path rewriting
713708
0 commit comments