Skip to content

Commit 3fd13cb

Browse files
committed
Merge branch 'dt/cache-tree-repair'
Add a few more places in "commit" and "checkout" that make sure that the cache-tree is fully populated in the index. * dt/cache-tree-repair: cache-tree: do not try to use an invalidated subtree info to build a tree cache-tree: Write updated cache-tree after commit cache-tree: subdirectory tests test-dump-cache-tree: invalid trees are not errors cache-tree: create/update cache-tree on checkout
2 parents 01d678a + 4ed115e commit 3fd13cb

File tree

6 files changed

+184
-22
lines changed

6 files changed

+184
-22
lines changed

builtin/checkout.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,12 @@ static int merge_working_tree(const struct checkout_opts *opts,
552552
}
553553
}
554554

555+
if (!active_cache_tree)
556+
active_cache_tree = cache_tree();
557+
558+
if (!cache_tree_fully_valid(active_cache_tree))
559+
cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
560+
555561
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
556562
die(_("unable to write new index file"));
557563

builtin/commit.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,13 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
353353

354354
discard_cache();
355355
read_cache_from(index_lock.filename);
356+
if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
357+
if (reopen_lock_file(&index_lock) < 0)
358+
die(_("unable to write index file"));
359+
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
360+
die(_("unable to update temporary index"));
361+
} else
362+
warning(_("Failed to update main cache tree"));
356363

357364
commit_style = COMMIT_NORMAL;
358365
return index_lock.filename;
@@ -393,8 +400,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
393400
if (!only && !pathspec.nr) {
394401
hold_locked_index(&index_lock, 1);
395402
refresh_cache_or_die(refresh_flags);
396-
if (active_cache_changed) {
403+
if (active_cache_changed
404+
|| !cache_tree_fully_valid(active_cache_tree)) {
397405
update_main_cache_tree(WRITE_TREE_SILENT);
406+
active_cache_changed = 1;
407+
}
408+
if (active_cache_changed) {
398409
if (write_locked_index(&the_index, &index_lock,
399410
COMMIT_LOCK))
400411
die(_("unable to write new_index file"));
@@ -444,6 +455,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
444455
hold_locked_index(&index_lock, 1);
445456
add_remove_files(&partial);
446457
refresh_cache(REFRESH_QUIET);
458+
update_main_cache_tree(WRITE_TREE_SILENT);
447459
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
448460
die(_("unable to write new_index file"));
449461

cache-tree.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,12 @@ static int update_one(struct cache_tree *it,
246246
struct strbuf buffer;
247247
int missing_ok = flags & WRITE_TREE_MISSING_OK;
248248
int dryrun = flags & WRITE_TREE_DRY_RUN;
249+
int repair = flags & WRITE_TREE_REPAIR;
249250
int to_invalidate = 0;
250251
int i;
251252

253+
assert(!(dryrun && repair));
254+
252255
*skip_count = 0;
253256

254257
if (0 <= it->entry_count && has_sha1_file(it->sha1))
@@ -320,6 +323,7 @@ static int update_one(struct cache_tree *it,
320323
int pathlen, entlen;
321324
const unsigned char *sha1;
322325
unsigned mode;
326+
int expected_missing = 0;
323327

324328
path = ce->name;
325329
pathlen = ce_namelen(ce);
@@ -336,8 +340,10 @@ static int update_one(struct cache_tree *it,
336340
i += sub->count;
337341
sha1 = sub->cache_tree->sha1;
338342
mode = S_IFDIR;
339-
if (sub->cache_tree->entry_count < 0)
343+
if (sub->cache_tree->entry_count < 0) {
340344
to_invalidate = 1;
345+
expected_missing = 1;
346+
}
341347
}
342348
else {
343349
sha1 = ce->sha1;
@@ -347,6 +353,8 @@ static int update_one(struct cache_tree *it,
347353
}
348354
if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
349355
strbuf_release(&buffer);
356+
if (expected_missing)
357+
return -1;
350358
return error("invalid object %06o %s for '%.*s'",
351359
mode, sha1_to_hex(sha1), entlen+baselen, path);
352360
}
@@ -381,7 +389,14 @@ static int update_one(struct cache_tree *it,
381389
#endif
382390
}
383391

384-
if (dryrun)
392+
if (repair) {
393+
unsigned char sha1[20];
394+
hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1);
395+
if (has_sha1_file(sha1))
396+
hashcpy(it->sha1, sha1);
397+
else
398+
to_invalidate = 1;
399+
} else if (dryrun)
385400
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
386401
else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
387402
strbuf_release(&buffer);

cache-tree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ int update_main_cache_tree(int);
3939
#define WRITE_TREE_IGNORE_CACHE_TREE 2
4040
#define WRITE_TREE_DRY_RUN 4
4141
#define WRITE_TREE_SILENT 8
42+
#define WRITE_TREE_REPAIR 16
4243

4344
/* error return codes */
4445
#define WRITE_TREE_UNREADABLE_INDEX (-1)

t/t0090-cache-tree.sh

Lines changed: 145 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,58 +8,116 @@ cache-tree extension.
88
. ./test-lib.sh
99

1010
cmp_cache_tree () {
11-
test-dump-cache-tree >actual &&
11+
test-dump-cache-tree | sed -e '/#(ref)/d' >actual &&
1212
sed "s/$_x40/SHA/" <actual >filtered &&
1313
test_cmp "$1" filtered
1414
}
1515

1616
# We don't bother with actually checking the SHA1:
1717
# test-dump-cache-tree already verifies that all existing data is
1818
# correct.
19-
test_shallow_cache_tree () {
20-
printf "SHA (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >expect &&
19+
generate_expected_cache_tree_rec () {
20+
dir="$1${1:+/}" &&
21+
parent="$2" &&
22+
# ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
23+
# We want to count only foo because it's the only direct child
24+
subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
25+
subtree_count=$(echo "$subtrees"|awk '$1 {++c} END {print c}') &&
26+
entries=$(git ls-files|wc -l) &&
27+
printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
28+
for subtree in $subtrees
29+
do
30+
cd "$subtree"
31+
generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1
32+
cd ..
33+
done &&
34+
dir=$parent
35+
}
36+
37+
generate_expected_cache_tree () {
38+
(
39+
generate_expected_cache_tree_rec
40+
)
41+
}
42+
43+
test_cache_tree () {
44+
generate_expected_cache_tree >expect &&
2145
cmp_cache_tree expect
2246
}
2347

2448
test_invalid_cache_tree () {
25-
echo "invalid (0 subtrees)" >expect &&
26-
printf "SHA #(ref) (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >>expect &&
27-
cmp_cache_tree expect
49+
printf "invalid %s ()\n" "" "$@" >expect &&
50+
test-dump-cache-tree |
51+
sed -n -e "s/[0-9]* subtrees//" -e '/#(ref)/d' -e '/^invalid /p' >actual &&
52+
test_cmp expect actual
2853
}
2954

3055
test_no_cache_tree () {
3156
: >expect &&
3257
cmp_cache_tree expect
3358
}
3459

35-
test_expect_failure 'initial commit has cache-tree' '
60+
test_expect_success 'initial commit has cache-tree' '
3661
test_commit foo &&
37-
test_shallow_cache_tree
62+
test_cache_tree
3863
'
3964

4065
test_expect_success 'read-tree HEAD establishes cache-tree' '
4166
git read-tree HEAD &&
42-
test_shallow_cache_tree
67+
test_cache_tree
4368
'
4469

4570
test_expect_success 'git-add invalidates cache-tree' '
4671
test_when_finished "git reset --hard; git read-tree HEAD" &&
47-
echo "I changed this file" > foo &&
72+
echo "I changed this file" >foo &&
4873
git add foo &&
4974
test_invalid_cache_tree
5075
'
5176

77+
test_expect_success 'git-add in subdir invalidates cache-tree' '
78+
test_when_finished "git reset --hard; git read-tree HEAD" &&
79+
mkdir dirx &&
80+
echo "I changed this file" >dirx/foo &&
81+
git add dirx/foo &&
82+
test_invalid_cache_tree
83+
'
84+
85+
cat >before <<\EOF
86+
SHA (3 entries, 2 subtrees)
87+
SHA dir1/ (1 entries, 0 subtrees)
88+
SHA dir2/ (1 entries, 0 subtrees)
89+
EOF
90+
91+
cat >expect <<\EOF
92+
invalid (2 subtrees)
93+
invalid dir1/ (0 subtrees)
94+
SHA dir2/ (1 entries, 0 subtrees)
95+
EOF
96+
97+
test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
98+
git tag no-children &&
99+
test_when_finished "git reset --hard no-children; git read-tree HEAD" &&
100+
mkdir dir1 dir2 &&
101+
test_commit dir1/a &&
102+
test_commit dir2/b &&
103+
echo "I changed this file" >dir1/a &&
104+
cmp_cache_tree before &&
105+
echo "I changed this file" >dir1/a &&
106+
git add dir1/a &&
107+
cmp_cache_tree expect
108+
'
109+
52110
test_expect_success 'update-index invalidates cache-tree' '
53111
test_when_finished "git reset --hard; git read-tree HEAD" &&
54-
echo "I changed this file" > foo &&
112+
echo "I changed this file" >foo &&
55113
git update-index --add foo &&
56114
test_invalid_cache_tree
57115
'
58116

59117
test_expect_success 'write-tree establishes cache-tree' '
60118
test-scrap-cache-tree &&
61119
git write-tree &&
62-
test_shallow_cache_tree
120+
test_cache_tree
63121
'
64122

65123
test_expect_success 'test-scrap-cache-tree works' '
@@ -70,24 +128,94 @@ test_expect_success 'test-scrap-cache-tree works' '
70128

71129
test_expect_success 'second commit has cache-tree' '
72130
test_commit bar &&
73-
test_shallow_cache_tree
131+
test_cache_tree
132+
'
133+
134+
test_expect_success 'commit --interactive gives cache-tree on partial commit' '
135+
cat <<-\EOT >foo.c &&
136+
int foo()
137+
{
138+
return 42;
139+
}
140+
int bar()
141+
{
142+
return 42;
143+
}
144+
EOT
145+
git add foo.c &&
146+
test_invalid_cache_tree &&
147+
git commit -m "add a file" &&
148+
test_cache_tree &&
149+
cat <<-\EOT >foo.c &&
150+
int foo()
151+
{
152+
return 43;
153+
}
154+
int bar()
155+
{
156+
return 44;
157+
}
158+
EOT
159+
(echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
160+
git commit --interactive -m foo &&
161+
test_cache_tree
162+
'
163+
164+
test_expect_success 'commit in child dir has cache-tree' '
165+
mkdir dir &&
166+
>dir/child.t &&
167+
git add dir/child.t &&
168+
git commit -m dir/child.t &&
169+
test_cache_tree
74170
'
75171

76172
test_expect_success 'reset --hard gives cache-tree' '
77173
test-scrap-cache-tree &&
78174
git reset --hard &&
79-
test_shallow_cache_tree
175+
test_cache_tree
80176
'
81177

82178
test_expect_success 'reset --hard without index gives cache-tree' '
83179
rm -f .git/index &&
84180
git reset --hard &&
85-
test_shallow_cache_tree
181+
test_cache_tree
86182
'
87183

88-
test_expect_failure 'checkout gives cache-tree' '
184+
test_expect_success 'checkout gives cache-tree' '
185+
git tag current &&
89186
git checkout HEAD^ &&
90-
test_shallow_cache_tree
187+
test_cache_tree
188+
'
189+
190+
test_expect_success 'checkout -b gives cache-tree' '
191+
git checkout current &&
192+
git checkout -b prev HEAD^ &&
193+
test_cache_tree
194+
'
195+
196+
test_expect_success 'checkout -B gives cache-tree' '
197+
git checkout current &&
198+
git checkout -B prev HEAD^ &&
199+
test_cache_tree
200+
'
201+
202+
test_expect_success 'partial commit gives cache-tree' '
203+
git checkout -b partial no-children &&
204+
test_commit one &&
205+
test_commit two &&
206+
echo "some change" >one.t &&
207+
git add one.t &&
208+
echo "some other change" >two.t &&
209+
git commit two.t -m partial &&
210+
test_cache_tree
211+
'
212+
213+
test_expect_success 'no phantom error when switching trees' '
214+
mkdir newdir &&
215+
>newdir/one &&
216+
git add newdir/one &&
217+
git checkout 2>errors &&
218+
! test -s errors
91219
'
92220

93221
test_done

test-dump-cache-tree.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ static int dump_cache_tree(struct cache_tree *it,
2626
return 0;
2727

2828
if (it->entry_count < 0) {
29+
/* invalid */
2930
dump_one(it, pfx, "");
3031
dump_one(ref, pfx, "#(ref) ");
31-
if (it->subtree_nr != ref->subtree_nr)
32-
errs = 1;
3332
}
3433
else {
3534
dump_one(it, pfx, "");
3635
if (hashcmp(it->sha1, ref->sha1) ||
3736
ref->entry_count != it->entry_count ||
3837
ref->subtree_nr != it->subtree_nr) {
38+
/* claims to be valid but is lying */
3939
dump_one(ref, pfx, "#(ref) ");
4040
errs = 1;
4141
}

0 commit comments

Comments
 (0)