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 (
1717S_ISLNK ,
2525from typ import (
2626BaseIndexEntry ,
2727IndexEntry ,
28- CE_NAMEMASK ,
29- CE_STAGESHIFT
3028)
3129
3230from util import (
3331TemporaryFileSwap ,
3432post_clear_cache ,
3533default_index ,
36- pack ,
37- unpack
3834)
3935
4036import git .objects
6056LockedFD ,
6157join_path_native ,
6258file_contents_ro ,
63- LockFile
64- )
65-
66-
67- from gitdb .base import (
68- IStream
6959)
7060
7161from fun import (
7262write_cache ,
7363read_cache ,
64+ write_tree_from_cache ,
7465entry_key
7566)
7667
68+ from gitdb .base import IStream
69+
7770__all__ = ('IndexFile' ,'CheckoutError' )
7871
7972
@@ -161,10 +154,15 @@ def _deserialize(self, stream):
161154self .version ,self .entries ,self ._extension_data ,conten_sha = read_cache (stream )
162155return 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"""
165159entries_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 ,
168166stream ,
169167(ignore_tree_extension_data and None )or self ._extension_data )
170168return 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 ?
405403mode = 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 )
407405blob .size = entry .size
408406output = (entry .stage ,blob )
409407if predicate (output ):
@@ -490,33 +488,31 @@ def update(self):
490488# allows to lazily reread on demand
491489return 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
495493object 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
503502if not self .entries :
504503raise 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-
520516def _process_diff_args (self ,args ):
521517try :
522518args .pop (args .index (self ))
@@ -525,7 +521,6 @@ def _process_diff_args(self, args):
525521# END remove self
526522return args
527523
528-
529524def _to_relative_path (self ,path ):
530525""":return: Version of path relative to our git directory or raise ValueError
531526if 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
601596Handling 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:
605600If True, otherwise ignored or excluded files will be
@@ -666,7 +661,7 @@ def store_path(filepath):
666661fprogress (filepath ,True ,filepath )
667662
668663return 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 ]
695690if null_entries_indices :
696691for ei in null_entries_indices :
697692null_entry = entries [ei ]
698693new_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
708703if path_rewriter :
709704for 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