@@ -900,6 +900,34 @@ static int has_file_name(struct index_state *istate,
900
900
return retval ;
901
901
}
902
902
903
+ /*
904
+ * Like strcmp(), but also return the offset of the first change.
905
+ */
906
+ int strcmp_offset (const char * s1_in , const char * s2_in , int * first_change )
907
+ {
908
+ const unsigned char * s1 = (const unsigned char * )s1_in ;
909
+ const unsigned char * s2 = (const unsigned char * )s2_in ;
910
+ int diff = 0 ;
911
+ int k ;
912
+
913
+ * first_change = 0 ;
914
+ for (k = 0 ; s1 [k ]; k ++ )
915
+ if ((diff = (s1 [k ] - s2 [k ])))
916
+ goto found_it ;
917
+ if (!s2 [k ])
918
+ return 0 ;
919
+ diff = -1 ;
920
+
921
+ found_it :
922
+ * first_change = k ;
923
+ if (diff > 0 )
924
+ return 1 ;
925
+ else if (diff < 0 )
926
+ return -1 ;
927
+ else
928
+ return 0 ;
929
+ }
930
+
903
931
/*
904
932
* Do we have another file with a pathname that is a proper
905
933
* subset of the name we're trying to add?
@@ -911,6 +939,21 @@ static int has_dir_name(struct index_state *istate,
911
939
int stage = ce_stage (ce );
912
940
const char * name = ce -> name ;
913
941
const char * slash = name + ce_namelen (ce );
942
+ int len_eq_last ;
943
+ int cmp_last = 0 ;
944
+
945
+ if (istate -> cache_nr > 0 ) {
946
+ /*
947
+ * Compare the entry's full path with the last path in the index.
948
+ * If it sorts AFTER the last entry in the index and they have no
949
+ * common prefix, then there cannot be any F/D name conflicts.
950
+ */
951
+ cmp_last = strcmp_offset (name ,
952
+ istate -> cache [istate -> cache_nr - 1 ]-> name ,
953
+ & len_eq_last );
954
+ if (cmp_last > 0 && len_eq_last == 0 )
955
+ return retval ;
956
+ }
914
957
915
958
for (;;) {
916
959
int len ;
@@ -923,6 +966,24 @@ static int has_dir_name(struct index_state *istate,
923
966
}
924
967
len = slash - name ;
925
968
969
+ if (cmp_last > 0 ) {
970
+ /*
971
+ * If this part of the directory prefix (including the trailing
972
+ * slash) already appears in the path of the last entry in the
973
+ * index, then we cannot also have a file with this prefix (or
974
+ * any parent directory prefix).
975
+ */
976
+ if (len + 1 <= len_eq_last )
977
+ return retval ;
978
+ /*
979
+ * If this part of the directory prefix (excluding the trailing
980
+ * slash) is longer than the known equal portions, then this part
981
+ * of the prefix cannot collide with a file. Go on to the parent.
982
+ */
983
+ if (len > len_eq_last )
984
+ continue ;
985
+ }
986
+
926
987
pos = index_name_stage_pos (istate , name , len , stage );
927
988
if (pos >= 0 ) {
928
989
/*
@@ -1014,7 +1075,16 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
1014
1075
1015
1076
if (!(option & ADD_CACHE_KEEP_CACHE_TREE ))
1016
1077
cache_tree_invalidate_path (istate , ce -> name );
1017
- pos = index_name_stage_pos (istate , ce -> name , ce_namelen (ce ), ce_stage (ce ));
1078
+
1079
+ /*
1080
+ * If this entry's path sorts after the last entry in the index,
1081
+ * we can avoid searching for it.
1082
+ */
1083
+ if (istate -> cache_nr > 0 &&
1084
+ strcmp (ce -> name , istate -> cache [istate -> cache_nr - 1 ]-> name ) > 0 )
1085
+ pos = - istate -> cache_nr - 1 ;
1086
+ else
1087
+ pos = index_name_stage_pos (istate , ce -> name , ce_namelen (ce ), ce_stage (ce ));
1018
1088
1019
1089
/* existing match? Just replace it. */
1020
1090
if (pos >= 0 ) {
0 commit comments