2424from git .utils import SHA1Writer , LazyMixin , ConcurrentWriteOperation , join_path_native
2525
2626
27+ class CheckoutError ( Exception ):
28+ """Thrown if a file could not be checked out from the index as it contained
29+ changes.
30+
31+ The .failed_files attribute contains a list of relative paths that failed
32+ to be checked out as they contained changes that did not exist in the index"""
33+ def __init__ (self , message , failed_files ):
34+ super (CheckoutError , self ).__init__ (message )
35+ self .failed_files = failed_files
36+
37+ def __str__ (self ):
38+ return super (CheckoutError , self ).__str__ () + ":%s" % self .failed_files
39+
40+
2741class _TemporaryFileSwap (object ):
2842 """
2943 Utility class moving a file to a temporary location within the same directory
@@ -800,7 +814,7 @@ def add(self, items, force=True, fprogress=lambda *args: None):
800814 # to get suitable progress information, pipe paths to stdin
801815 args = ("--add" , "--replace" , "--verbose" , "--stdin" )
802816 proc = self .repo .git .update_index (* args , ** {'as_process' :True , 'istream' :subprocess .PIPE })
803- make_exc = lambda : GitCommandError (("git-update-index" ,)+ args , 128 , proc .stderr .readline ())
817+ make_exc = lambda : GitCommandError (("git-update-index" ,)+ args , 128 , proc .stderr .read ())
804818 added_files = list ()
805819
806820 for filepath in self ._iter_expand_paths (paths ):
@@ -829,7 +843,7 @@ def add(self, items, force=True, fprogress=lambda *args: None):
829843 # send progress for these now.
830844 args = ("-w" , "--stdin-paths" )
831845 proc = self .repo .git .hash_object (* args , ** {'istream' :subprocess .PIPE , 'as_process' :True })
832- make_exc = lambda : GitCommandError (("git-hash-object" ,)+ args , 128 , proc .stderr .readline ())
846+ make_exc = lambda : GitCommandError (("git-hash-object" ,)+ args , 128 , proc .stderr .read ())
833847 obj_ids = list ()
834848 for ei in null_entries_indices :
835849 entry = entries [ei ]
@@ -999,13 +1013,16 @@ def checkout(self, paths=None, force=False, fprogress=lambda *args: None, **kwar
9991013
10001014 ``paths``
10011015 If None, all paths in the index will be checked out. Otherwise an iterable
1002- or single path of relative or absolute paths pointing to files is expected.
1003- The command will ignore paths that do not exist.
1004- The provided progress information will contain None as path and item.
1016+ of relative or absolute paths or a single path pointing to files in the index
1017+ is expected.
1018+ The command will raise of files do not exist in the index ( as opposed to the
1019+ original git command who ignores them )
1020+ The provided progress information will contain None as path and item if no
1021+ explicit paths are given.
10051022
10061023 ``force``
1007- If True, existing files will be overwritten. If False, these will
1008- be skipped .
1024+ If True, existing files will be overwritten even if they contain local modifications.
1025+ If False, these will trigger a CheckoutError .
10091026
10101027 ``fprogress``
10111028 see Index.add_ for signature and explanation
@@ -1015,28 +1032,66 @@ def checkout(self, paths=None, force=False, fprogress=lambda *args: None, **kwar
10151032
10161033 Returns
10171034 self
1035+
1036+ Raise CheckoutError
1037+ If at least one file failed to be checked out. This is a summary,
1038+ hence it will checkout as many files as it can anyway.
1039+ Raise GitCommandError if error lines could not be parsed - this truly is
1040+ an exceptional state
10181041 """
10191042 args = ["--index" ]
10201043 if force :
10211044 args .append ("--force" )
10221045
1046+ def handle_stderr (proc ):
1047+ stderr = proc .stderr .read ()
1048+ if not stderr :
1049+ return
1050+ # line contents:
1051+ # git-checkout-index: this already exists
1052+ failed_files = list ()
1053+ unknown_lines = list ()
1054+ endings = (' already exists' , ' is not in the cache' , ' does not exist at stage' , ' is unmerged' )
1055+ for line in stderr .splitlines ():
1056+ if not line .startswith ("git checkout-index: " ) and not line .startswith ("git-checkout-index: " ):
1057+ unknown_lines .append (line )
1058+ continue
1059+ # END unkown lines parsing
1060+
1061+ for e in endings :
1062+ if line .endswith (e ):
1063+ failed_files .append (line [20 :- len (e )])
1064+ break
1065+ # END if ending matches
1066+ # END for each possible ending
1067+ # END for each line
1068+ if unknown_lines :
1069+ raise GitCommandError (("git-checkout-index" , ), 128 , stderr )
1070+ if failed_files :
1071+ raise CheckoutError ("Some files could not be checked out from the index due to local modifications" , failed_files )
1072+ # END stderr handler
1073+
10231074 if paths is None :
10241075 args .append ("--all" )
1076+ kwargs ['as_process' ] = 1
10251077 fprogress (None , False , None )
1026- self .repo .git .checkout_index (* args , ** kwargs )
1078+ proc = self .repo .git .checkout_index (* args , ** kwargs )
1079+ proc .wait ()
10271080 fprogress (None , True , None )
1081+ handle_stderr (proc )
10281082 else :
1029- if not isinstance (paths , ( tuple , list ) ):
1083+ if isinstance (paths , basestring ):
10301084 paths = [paths ]
10311085
10321086 args .append ("--stdin" )
1033- co_proc = self .repo .git .checkout_index (args , as_process = True , istream = subprocess .PIPE , ** kwargs )
1034- make_exc = lambda : GitCommandError (("git-checkout-index" ,)+ args , 128 , co_proc .stderr .readline ())
1087+ proc = self .repo .git .checkout_index (args , as_process = True , istream = subprocess .PIPE , ** kwargs )
1088+ make_exc = lambda : GitCommandError (("git-checkout-index" ,)+ args , 128 , proc .stderr .read ())
10351089 for path in paths :
10361090 path = self ._to_relative_path (path )
1037- self ._write_path_to_stdin (co_proc , path , path , make_exc , fprogress , read_from_stdout = False )
1038- # END for each path
1039- self ._flush_stdin_and_wait (co_proc )
1091+ self ._write_path_to_stdin (proc , path , path , make_exc , fprogress , read_from_stdout = False )
1092+ # END for each path
1093+ self ._flush_stdin_and_wait (proc )
1094+ handle_stderr (proc )
10401095 # END paths handling
10411096 return self
10421097
0 commit comments