Skip to content

Commit 71b0436

Browse files
author
Git for Windows Build Agent
committed
Merge pull request #988 from jeffhostetler/jeffhostetler/quick_add_index_entry
read-cache: speed up add_index_entry during checkout
2 parents 2cb9d38 + 958a8a6 commit 71b0436

6 files changed

+144
-1
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
637637
TEST_PROGRAMS_NEED_X += test-sha1
638638
TEST_PROGRAMS_NEED_X += test-sha1-array
639639
TEST_PROGRAMS_NEED_X += test-sigchain
640+
TEST_PROGRAMS_NEED_X += test-strcmp-offset
640641
TEST_PROGRAMS_NEED_X += test-string-list
641642
TEST_PROGRAMS_NEED_X += test-submodule-config
642643
TEST_PROGRAMS_NEED_X += test-subprocess

cache.h

+1
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ extern int write_locked_index(struct index_state *, struct lock_file *lock, unsi
596596
extern int discard_index(struct index_state *);
597597
extern int unmerged_index(const struct index_state *);
598598
extern int verify_path(const char *path);
599+
extern int strcmp_offset(const char *s1_in, const char *s2_in, int *first_change);
599600
extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
600601
extern void adjust_dirname_case(struct index_state *istate, char *name);
601602
extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);

read-cache.c

+71-1
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,34 @@ static int has_file_name(struct index_state *istate,
891891
return retval;
892892
}
893893

894+
/*
895+
* Like strcmp(), but also return the offset of the first change.
896+
*/
897+
int strcmp_offset(const char *s1_in, const char *s2_in, int *first_change)
898+
{
899+
const unsigned char *s1 = (const unsigned char *)s1_in;
900+
const unsigned char *s2 = (const unsigned char *)s2_in;
901+
int diff = 0;
902+
int k;
903+
904+
*first_change = 0;
905+
for (k=0; s1[k]; k++)
906+
if ((diff = (s1[k] - s2[k])))
907+
goto found_it;
908+
if (!s2[k])
909+
return 0;
910+
diff = -1;
911+
912+
found_it:
913+
*first_change = k;
914+
if (diff > 0)
915+
return 1;
916+
else if (diff < 0)
917+
return -1;
918+
else
919+
return 0;
920+
}
921+
894922
/*
895923
* Do we have another file with a pathname that is a proper
896924
* subset of the name we're trying to add?
@@ -902,6 +930,21 @@ static int has_dir_name(struct index_state *istate,
902930
int stage = ce_stage(ce);
903931
const char *name = ce->name;
904932
const char *slash = name + ce_namelen(ce);
933+
int len_eq_last;
934+
int cmp_last = 0;
935+
936+
if (istate->cache_nr > 0) {
937+
/*
938+
* Compare the entry's full path with the last path in the index.
939+
* If it sorts AFTER the last entry in the index and they have no
940+
* common prefix, then there cannot be any F/D name conflicts.
941+
*/
942+
cmp_last = strcmp_offset(name,
943+
istate->cache[istate->cache_nr-1]->name,
944+
&len_eq_last);
945+
if (cmp_last > 0 && len_eq_last == 0)
946+
return retval;
947+
}
905948

906949
for (;;) {
907950
int len;
@@ -914,6 +957,24 @@ static int has_dir_name(struct index_state *istate,
914957
}
915958
len = slash - name;
916959

960+
if (cmp_last > 0) {
961+
/*
962+
* If this part of the directory prefix (including the trailing
963+
* slash) already appears in the path of the last entry in the
964+
* index, then we cannot also have a file with this prefix (or
965+
* any parent directory prefix).
966+
*/
967+
if (len+1 <= len_eq_last)
968+
return retval;
969+
/*
970+
* If this part of the directory prefix (excluding the trailing
971+
* slash) is longer than the known equal portions, then this part
972+
* of the prefix cannot collide with a file. Go on to the parent.
973+
*/
974+
if (len > len_eq_last)
975+
continue;
976+
}
977+
917978
pos = index_name_stage_pos(istate, name, len, stage);
918979
if (pos >= 0) {
919980
/*
@@ -1005,7 +1066,16 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
10051066

10061067
if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
10071068
cache_tree_invalidate_path(istate, ce->name);
1008-
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
1069+
1070+
/*
1071+
* If this entry's path sorts after the last entry in the index,
1072+
* we can avoid searching for it.
1073+
*/
1074+
if (istate->cache_nr > 0 &&
1075+
strcmp(ce->name, istate->cache[istate->cache_nr - 1]->name) > 0)
1076+
pos = -istate->cache_nr - 1;
1077+
else
1078+
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
10091079

10101080
/* existing match? Just replace it. */
10111081
if (pos >= 0) {

t/helper/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
/test-sha1
2727
/test-sha1-array
2828
/test-sigchain
29+
/test-strcmp-offset
2930
/test-string-list
3031
/test-submodule-config
3132
/test-subprocess

t/helper/test-strcmp-offset.c

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "cache.h"
2+
3+
struct test_data {
4+
const char *s1;
5+
const char *s2;
6+
int first_change;
7+
};
8+
9+
static struct test_data data[] = {
10+
{ "abc", "abc", 0 },
11+
{ "abc", "def", 0 },
12+
13+
{ "abc", "abz", 2 },
14+
15+
{ "abc", "abcdef", 3 },
16+
17+
{ "abc\xF0zzz", "abc\xFFzzz", 3 },
18+
19+
{ NULL, NULL, 0 }
20+
};
21+
22+
int try_pair(const char *sa, const char *sb, int first_change)
23+
{
24+
int failed = 0;
25+
int offset, r_exp, r_tst;
26+
27+
r_exp = strcmp(sa, sb);
28+
r_tst = strcmp_offset(sa, sb, &offset);
29+
if (r_tst != r_exp) {
30+
if ((r_tst < 0 && r_exp < 0) || (r_tst > 0 && r_exp > 0))
31+
warning("'%s' vs '%s', imprecise result: %d != %d",
32+
sa, sb, r_exp, r_tst);
33+
else {
34+
error("'%s' vs '%s', result expect %d, observed %d",
35+
sa, sb, r_exp, r_tst);
36+
failed = 1;
37+
}
38+
}
39+
if (offset != first_change) {
40+
error("'%s' vs '%s', offset expect %d, observed %d",
41+
sa, sb, first_change, offset);
42+
failed = 1;
43+
}
44+
45+
return failed;
46+
}
47+
48+
int cmd_main(int argc, const char **argv)
49+
{
50+
int failed = 0;
51+
int k;
52+
53+
for (k=0; data[k].s1; k++) {
54+
failed += try_pair(data[k].s1, data[k].s2, data[k].first_change);
55+
failed += try_pair(data[k].s2, data[k].s1, data[k].first_change);
56+
}
57+
58+
return failed;
59+
}

t/t0065-strcmp-offset.sh

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
3+
test_description='Test strcmp_offset functionality'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success run_helper '
8+
test-strcmp-offset
9+
'
10+
11+
test_done

0 commit comments

Comments
 (0)