@@ -164,6 +164,26 @@ def from_blob(cls, blob):
164164 return IndexEntry ((blob .mode , blob .id , 0 , blob .path , time , time , 0 , 0 , 0 , 0 , blob .size ))
165165
166166
167+ def clear_cache (func ):
168+ """
169+ Decorator for functions that alter the index using the git command. This would
170+ invalidate our possibly existing entries dictionary which is why it must be
171+ deleted to allow it to be lazily reread later.
172+
173+ Note
174+ This decorator will not be required once all functions are implemented
175+ natively which in fact is possible, but probably not feasible performance wise.
176+ """
177+ def clear_cache_if_not_raised (self , * args , ** kwargs ):
178+ rval = func (self , * args , ** kwargs )
179+ del (self .entries )
180+ return rval
181+
182+ # END wrapper method
183+ clear_cache_if_not_raised .__name__ = func .__name__
184+ return clear_cache_if_not_raised
185+
186+
167187def default_index (func ):
168188 """
169189 Decorator assuring the wrapped method may only run if we are the default
@@ -194,10 +214,8 @@ class IndexFile(LazyMixin, diff.Diffable):
194214 The index contains an entries dict whose keys are tuples of type IndexEntry
195215 to facilitate access.
196216
197- As opposed to the Index type, the IndexFile represents the index on file level.
198- This can be considered an alternate, file-based implementation of the Index class
199- with less support for common functions. Use it for very special and custom index
200- handling.
217+ You may only read the entries dict or manipulate it through designated methods.
218+ Otherwise changes to it will be lost when changing the index using its methods.
201219 """
202220 __slots__ = ( "repo" , "version" , "entries" , "_extension_data" , "_file_path" )
203221 _VERSION = 2 # latest version we support
@@ -500,6 +518,10 @@ def resolve_blobs(self, iter_blobs):
500518
501519 Returns:
502520 self
521+
522+ Note
523+ You will have to write the index manually once you are done, i.e.
524+ index.resolve_blobs(blobs).write()
503525 """
504526 for blob in iter_blobs :
505527 stage_null_key = (blob .path , 0 )
@@ -561,45 +583,132 @@ def _process_diff_args(self, args):
561583 # END remove self
562584 return args
563585
586+
587+ def _to_relative_path (self , path ):
588+ """
589+ Return
590+ Version of path relative to our git directory or raise ValueError
591+ if it is not within our git direcotory
592+ """
593+ if not os .path .isabs (path ):
594+ return path
595+ relative_path = path .replace (self .repo .git .git_dir + "/" , "" )
596+ if relative_path == path :
597+ raise ValueError ("Absolute path %r is not in git repository at %r" % (path ,self .repo .git .git_dir ))
598+ return relative_path
599+
600+ @clear_cache
564601 @default_index
565602 def add (self , items , ** kwargs ):
566603 """
567- Add files from the working copy, specific blobs or IndexEntries
568- to the index.
569-
570- TODO: Its important to specify a way to add symlinks directly, even
571- on systems that do not support it, like ... erm ... windows.
604+ Add files from the working tree, specific blobs or BaseIndexEntries
605+ to the index. The underlying index file will be written immediately, hence
606+ you should provide as many items as possible to minimize the amounts of writes
572607
608+ ``items``
609+ Multiple types of items are supported, types can be mixed within one call.
610+ Different types imply a different handling. File paths may generally be
611+ relative or absolute.
612+
613+ - path string
614+ strings denote a relative or absolute path into the repository pointing to
615+ an existing file, i.e. CHANGES, lib/myfile.ext, /home/gitrepo/lib/myfile.ext.
616+
617+ Paths provided like this must exist. When added, they will be written
618+ into the object database.
619+
620+ This equals a straight git-add.
621+
622+ They are added at stage 0
623+
624+ - Blob object
625+ Blobs are added as they are assuming a valid mode is set.
626+ The file they refer to may or may not exist in the file system
627+
628+ If their sha is null ( 40*0 ), their path must exist in the file system
629+ as an object will be created from the data at the path.The handling
630+ now very much equals the way string paths are processed, except that
631+ the mode you have set will be kept. This allows you to create symlinks
632+ by settings the mode respectively and writing the target of the symlink
633+ directly into the file. This equals a default Linux-Symlink which
634+ is not dereferenced automatically, except that it can be created on
635+ filesystems not supporting it as well.
636+
637+ They are added at stage 0
638+
639+ - BaseIndexEntry or type
640+ Handling equals the one of Blob objects, but the stage may be
641+ explicitly set.
642+
573643 ``**kwargs``
574- Additional keyword arguments to be passed to git-update-index
644+ Additional keyword arguments to be passed to git-update-index, such
645+ as index_only.
575646
576647 Returns
577- List(IndexEntries ) representing the entries just added
648+ List(BaseIndexEntries ) representing the entries just actually added.
578649 """
579650 raise NotImplementedError ("todo" )
580651
652+ @clear_cache
581653 @default_index
582- def remove (self , items , affect_working_tree = False , ** kwargs ):
654+ def remove (self , items , working_tree = False , ** kwargs ):
583655 """
584- Remove the given file_paths or blobs from the index and optionally from
656+ Remove the given items from the index and optionally from
585657 the working tree as well.
586658
587659 ``items``
588- TODO
660+ Multiple types of items are supported which may be be freely mixed.
661+
662+ - path string
663+ Remove the given path at all stages. If it is a directory, you must
664+ specify the r=True keyword argument to remove all file entries
665+ below it. If absolute paths are given, they will be converted
666+ to a path relative to the git repository directory containing
667+ the working tree
668+
669+ The path string may include globs, such as *.c.
670+
671+ - Blob object
672+ Only the path portion is used in this case.
673+
674+ - BaseIndexEntry or compatible type
675+ The only relevant information here Yis the path. The stage is ignored.
589676
590- ``affect_working_tree ``
677+ ``working_tree ``
591678 If True, the entry will also be removed from the working tree, physically
592679 removing the respective file. This may fail if there are uncommited changes
593680 in it.
594681
595682 ``**kwargs``
596- Additional keyword arguments to be passed to git-update-index
683+ Additional keyword arguments to be passed to git-rm, such
684+ as 'r' to allow recurive removal of
597685
598686 Returns
599- self
600- """
601- raise NotImplementedError ("todo" )
602- return self
687+ List(path_string, ...) list of paths that have been removed effectively.
688+ This is interesting to know in case you have provided a directory or
689+ globs. Paths are relative to the
690+ """
691+ args = list ()
692+ if not working_tree :
693+ args .append ("--cached" )
694+ args .append ("--" )
695+
696+ # preprocess paths
697+ paths = list ()
698+ for item in items :
699+ if isinstance (item , (BaseIndexEntry ,Blob )):
700+ paths .append (self ._to_relative_path (item .path ))
701+ elif isinstance (item , basestring ):
702+ paths .append (self ._to_relative_path (item ))
703+ else :
704+ raise TypeError ("Invalid item type: %r" % item )
705+ # END for each item
706+
707+ removed_paths = self .repo .git .rm (args , paths , ** kwargs ).splitlines ()
708+
709+ # process output to gain proper paths
710+ # rm 'path'
711+ return [ p [4 :- 1 ] for p in removed_paths ]
603712
604713 @default_index
605714 def commit (self , message = None , parent_commits = None , ** kwargs ):
@@ -621,7 +730,8 @@ def commit(self, message=None, parent_commits=None, **kwargs):
621730 Commit object representing the new commit
622731 """
623732 raise NotImplementedError ("todo" )
624-
733+
734+ @clear_cache
625735 @default_index
626736 def reset (self , commit = 'HEAD' , working_tree = False , paths = None , ** kwargs ):
627737 """
0 commit comments