@@ -894,6 +894,34 @@ static int has_file_name(struct index_state *istate,
894
894
return retval ;
895
895
}
896
896
897
+ /*
898
+ * Like strcmp(), but also return the offset of the first change.
899
+ */
900
+ int strcmp_offset (const char * s1_in , const char * s2_in , int * first_change )
901
+ {
902
+ const unsigned char * s1 = (const unsigned char * )s1_in ;
903
+ const unsigned char * s2 = (const unsigned char * )s2_in ;
904
+ int diff = 0 ;
905
+ int k ;
906
+
907
+ * first_change = 0 ;
908
+ for (k = 0 ; s1 [k ]; k ++ )
909
+ if ((diff = (s1 [k ] - s2 [k ])))
910
+ goto found_it ;
911
+ if (!s2 [k ])
912
+ return 0 ;
913
+ diff = -1 ;
914
+
915
+ found_it :
916
+ * first_change = k ;
917
+ if (diff > 0 )
918
+ return 1 ;
919
+ else if (diff < 0 )
920
+ return -1 ;
921
+ else
922
+ return 0 ;
923
+ }
924
+
897
925
/*
898
926
* Do we have another file with a pathname that is a proper
899
927
* subset of the name we're trying to add?
@@ -905,6 +933,21 @@ static int has_dir_name(struct index_state *istate,
905
933
int stage = ce_stage (ce );
906
934
const char * name = ce -> name ;
907
935
const char * slash = name + ce_namelen (ce );
936
+ int len_eq_last ;
937
+ int cmp_last = 0 ;
938
+
939
+ if (istate -> cache_nr > 0 ) {
940
+ /*
941
+ * Compare the entry's full path with the last path in the index.
942
+ * If it sorts AFTER the last entry in the index and they have no
943
+ * common prefix, then there cannot be any F/D name conflicts.
944
+ */
945
+ cmp_last = strcmp_offset (name ,
946
+ istate -> cache [istate -> cache_nr - 1 ]-> name ,
947
+ & len_eq_last );
948
+ if (cmp_last > 0 && len_eq_last == 0 )
949
+ return retval ;
950
+ }
908
951
909
952
for (;;) {
910
953
int len ;
@@ -917,6 +960,24 @@ static int has_dir_name(struct index_state *istate,
917
960
}
918
961
len = slash - name ;
919
962
963
+ if (cmp_last > 0 ) {
964
+ /*
965
+ * If this part of the directory prefix (including the trailing
966
+ * slash) already appears in the path of the last entry in the
967
+ * index, then we cannot also have a file with this prefix (or
968
+ * any parent directory prefix).
969
+ */
970
+ if (len + 1 <= len_eq_last )
971
+ return retval ;
972
+ /*
973
+ * If this part of the directory prefix (excluding the trailing
974
+ * slash) is longer than the known equal portions, then this part
975
+ * of the prefix cannot collide with a file. Go on to the parent.
976
+ */
977
+ if (len > len_eq_last )
978
+ continue ;
979
+ }
980
+
920
981
pos = index_name_stage_pos (istate , name , len , stage );
921
982
if (pos >= 0 ) {
922
983
/*
@@ -1008,7 +1069,16 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
1008
1069
1009
1070
if (!(option & ADD_CACHE_KEEP_CACHE_TREE ))
1010
1071
cache_tree_invalidate_path (istate , ce -> name );
1011
- pos = index_name_stage_pos (istate , ce -> name , ce_namelen (ce ), ce_stage (ce ));
1072
+
1073
+ /*
1074
+ * If this entry's path sorts after the last entry in the index,
1075
+ * we can avoid searching for it.
1076
+ */
1077
+ if (istate -> cache_nr > 0 &&
1078
+ strcmp (ce -> name , istate -> cache [istate -> cache_nr - 1 ]-> name ) > 0 )
1079
+ pos = - istate -> cache_nr - 1 ;
1080
+ else
1081
+ pos = index_name_stage_pos (istate , ce -> name , ce_namelen (ce ), ce_stage (ce ));
1012
1082
1013
1083
/* existing match? Just replace it. */
1014
1084
if (pos >= 0 ) {
0 commit comments