24
24
from git .utils import SHA1Writer , LazyMixin , ConcurrentWriteOperation , join_path_native
25
25
26
26
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
+
27
41
class _TemporaryFileSwap (object ):
28
42
"""
29
43
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):
800
814
# to get suitable progress information, pipe paths to stdin
801
815
args = ("--add" , "--replace" , "--verbose" , "--stdin" )
802
816
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 ())
804
818
added_files = list ()
805
819
806
820
for filepath in self ._iter_expand_paths (paths ):
@@ -829,7 +843,7 @@ def add(self, items, force=True, fprogress=lambda *args: None):
829
843
# send progress for these now.
830
844
args = ("-w" , "--stdin-paths" )
831
845
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 ())
833
847
obj_ids = list ()
834
848
for ei in null_entries_indices :
835
849
entry = entries [ei ]
@@ -999,13 +1013,16 @@ def checkout(self, paths=None, force=False, fprogress=lambda *args: None, **kwar
999
1013
1000
1014
``paths``
1001
1015
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.
1005
1022
1006
1023
``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 .
1009
1026
1010
1027
``fprogress``
1011
1028
see Index.add_ for signature and explanation
@@ -1015,28 +1032,66 @@ def checkout(self, paths=None, force=False, fprogress=lambda *args: None, **kwar
1015
1032
1016
1033
Returns
1017
1034
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
1018
1041
"""
1019
1042
args = ["--index" ]
1020
1043
if force :
1021
1044
args .append ("--force" )
1022
1045
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
+
1023
1074
if paths is None :
1024
1075
args .append ("--all" )
1076
+ kwargs ['as_process' ] = 1
1025
1077
fprogress (None , False , None )
1026
- self .repo .git .checkout_index (* args , ** kwargs )
1078
+ proc = self .repo .git .checkout_index (* args , ** kwargs )
1079
+ proc .wait ()
1027
1080
fprogress (None , True , None )
1081
+ handle_stderr (proc )
1028
1082
else :
1029
- if not isinstance (paths , ( tuple , list ) ):
1083
+ if isinstance (paths , basestring ):
1030
1084
paths = [paths ]
1031
1085
1032
1086
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 ())
1035
1089
for path in paths :
1036
1090
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 )
1040
1095
# END paths handling
1041
1096
return self
1042
1097
0 commit comments