@@ -887,6 +887,34 @@ static int has_file_name(struct index_state *istate,
887
887
return retval ;
888
888
}
889
889
890
+ /*
891
+ * Like strcmp(), but also return the offset of the first change.
892
+ */
893
+ int strcmp_offset (const char * s1_in , const char * s2_in , int * first_change )
894
+ {
895
+ const unsigned char * s1 = (const unsigned char * )s1_in ;
896
+ const unsigned char * s2 = (const unsigned char * )s2_in ;
897
+ int diff = 0 ;
898
+ int k ;
899
+
900
+ * first_change = 0 ;
901
+ for (k = 0 ; s1 [k ]; k ++ )
902
+ if ((diff = (s1 [k ] - s2 [k ])))
903
+ goto found_it ;
904
+ if (!s2 [k ])
905
+ return 0 ;
906
+ diff = -1 ;
907
+
908
+ found_it :
909
+ * first_change = k ;
910
+ if (diff > 0 )
911
+ return 1 ;
912
+ else if (diff < 0 )
913
+ return -1 ;
914
+ else
915
+ return 0 ;
916
+ }
917
+
890
918
/*
891
919
* Do we have another file with a pathname that is a proper
892
920
* subset of the name we're trying to add?
@@ -898,6 +926,21 @@ static int has_dir_name(struct index_state *istate,
898
926
int stage = ce_stage (ce );
899
927
const char * name = ce -> name ;
900
928
const char * slash = name + ce_namelen (ce );
929
+ int len_eq_last ;
930
+ int cmp_last = 0 ;
931
+
932
+ if (istate -> cache_nr > 0 ) {
933
+ /*
934
+ * Compare the entry's full path with the last path in the index.
935
+ * If it sorts AFTER the last entry in the index and they have no
936
+ * common prefix, then there cannot be any F/D name conflicts.
937
+ */
938
+ cmp_last = strcmp_offset (name ,
939
+ istate -> cache [istate -> cache_nr - 1 ]-> name ,
940
+ & len_eq_last );
941
+ if (cmp_last > 0 && len_eq_last == 0 )
942
+ return retval ;
943
+ }
901
944
902
945
for (;;) {
903
946
int len ;
@@ -910,6 +953,24 @@ static int has_dir_name(struct index_state *istate,
910
953
}
911
954
len = slash - name ;
912
955
956
+ if (cmp_last > 0 ) {
957
+ /*
958
+ * If this part of the directory prefix (including the trailing
959
+ * slash) already appears in the path of the last entry in the
960
+ * index, then we cannot also have a file with this prefix (or
961
+ * any parent directory prefix).
962
+ */
963
+ if (len + 1 <= len_eq_last )
964
+ return retval ;
965
+ /*
966
+ * If this part of the directory prefix (excluding the trailing
967
+ * slash) is longer than the known equal portions, then this part
968
+ * of the prefix cannot collide with a file. Go on to the parent.
969
+ */
970
+ if (len > len_eq_last )
971
+ continue ;
972
+ }
973
+
913
974
pos = index_name_stage_pos (istate , name , len , stage );
914
975
if (pos >= 0 ) {
915
976
/*
@@ -1001,7 +1062,16 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
1001
1062
1002
1063
if (!(option & ADD_CACHE_KEEP_CACHE_TREE ))
1003
1064
cache_tree_invalidate_path (istate , ce -> name );
1004
- pos = index_name_stage_pos (istate , ce -> name , ce_namelen (ce ), ce_stage (ce ));
1065
+
1066
+ /*
1067
+ * If this entry's path sorts after the last entry in the index,
1068
+ * we can avoid searching for it.
1069
+ */
1070
+ if (istate -> cache_nr > 0 &&
1071
+ strcmp (ce -> name , istate -> cache [istate -> cache_nr - 1 ]-> name ) > 0 )
1072
+ pos = - istate -> cache_nr - 1 ;
1073
+ else
1074
+ pos = index_name_stage_pos (istate , ce -> name , ce_namelen (ce ), ce_stage (ce ));
1005
1075
1006
1076
/* existing match? Just replace it. */
1007
1077
if (pos >= 0 ) {
0 commit comments