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
This commit is contained in:
Junio C Hamano 2014-09-11 10:33:32 -07:00
Родитель 01d678a226 4ed115e9c5
Коммит 3fd13cbcd5
6 изменённых файлов: 184 добавлений и 22 удалений

Просмотреть файл

@ -552,6 +552,12 @@ static int merge_working_tree(const struct checkout_opts *opts,
}
}
if (!active_cache_tree)
active_cache_tree = cache_tree();
if (!cache_tree_fully_valid(active_cache_tree))
cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));

Просмотреть файл

@ -353,6 +353,13 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
discard_cache();
read_cache_from(index_lock.filename);
if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to update temporary index"));
} else
warning(_("Failed to update main cache tree"));
commit_style = COMMIT_NORMAL;
return index_lock.filename;
@ -393,8 +400,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
if (!only && !pathspec.nr) {
hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
if (active_cache_changed) {
if (active_cache_changed
|| !cache_tree_fully_valid(active_cache_tree)) {
update_main_cache_tree(WRITE_TREE_SILENT);
active_cache_changed = 1;
}
if (active_cache_changed) {
if (write_locked_index(&the_index, &index_lock,
COMMIT_LOCK))
die(_("unable to write new_index file"));
@ -444,6 +455,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
hold_locked_index(&index_lock, 1);
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
update_main_cache_tree(WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to write new_index file"));

Просмотреть файл

@ -246,9 +246,12 @@ static int update_one(struct cache_tree *it,
struct strbuf buffer;
int missing_ok = flags & WRITE_TREE_MISSING_OK;
int dryrun = flags & WRITE_TREE_DRY_RUN;
int repair = flags & WRITE_TREE_REPAIR;
int to_invalidate = 0;
int i;
assert(!(dryrun && repair));
*skip_count = 0;
if (0 <= it->entry_count && has_sha1_file(it->sha1))
@ -320,6 +323,7 @@ static int update_one(struct cache_tree *it,
int pathlen, entlen;
const unsigned char *sha1;
unsigned mode;
int expected_missing = 0;
path = ce->name;
pathlen = ce_namelen(ce);
@ -336,8 +340,10 @@ static int update_one(struct cache_tree *it,
i += sub->count;
sha1 = sub->cache_tree->sha1;
mode = S_IFDIR;
if (sub->cache_tree->entry_count < 0)
if (sub->cache_tree->entry_count < 0) {
to_invalidate = 1;
expected_missing = 1;
}
}
else {
sha1 = ce->sha1;
@ -347,6 +353,8 @@ static int update_one(struct cache_tree *it,
}
if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
strbuf_release(&buffer);
if (expected_missing)
return -1;
return error("invalid object %06o %s for '%.*s'",
mode, sha1_to_hex(sha1), entlen+baselen, path);
}
@ -381,7 +389,14 @@ static int update_one(struct cache_tree *it,
#endif
}
if (dryrun)
if (repair) {
unsigned char sha1[20];
hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1);
if (has_sha1_file(sha1))
hashcpy(it->sha1, sha1);
else
to_invalidate = 1;
} else if (dryrun)
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
strbuf_release(&buffer);

Просмотреть файл

@ -39,6 +39,7 @@ int update_main_cache_tree(int);
#define WRITE_TREE_IGNORE_CACHE_TREE 2
#define WRITE_TREE_DRY_RUN 4
#define WRITE_TREE_SILENT 8
#define WRITE_TREE_REPAIR 16
/* error return codes */
#define WRITE_TREE_UNREADABLE_INDEX (-1)

Просмотреть файл

@ -8,7 +8,7 @@ cache-tree extension.
. ./test-lib.sh
cmp_cache_tree () {
test-dump-cache-tree >actual &&
test-dump-cache-tree | sed -e '/#(ref)/d' >actual &&
sed "s/$_x40/SHA/" <actual >filtered &&
test_cmp "$1" filtered
}
@ -16,15 +16,40 @@ cmp_cache_tree () {
# We don't bother with actually checking the SHA1:
# test-dump-cache-tree already verifies that all existing data is
# correct.
test_shallow_cache_tree () {
printf "SHA (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >expect &&
generate_expected_cache_tree_rec () {
dir="$1${1:+/}" &&
parent="$2" &&
# ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
# We want to count only foo because it's the only direct child
subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
subtree_count=$(echo "$subtrees"|awk '$1 {++c} END {print c}') &&
entries=$(git ls-files|wc -l) &&
printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
for subtree in $subtrees
do
cd "$subtree"
generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1
cd ..
done &&
dir=$parent
}
generate_expected_cache_tree () {
(
generate_expected_cache_tree_rec
)
}
test_cache_tree () {
generate_expected_cache_tree >expect &&
cmp_cache_tree expect
}
test_invalid_cache_tree () {
echo "invalid (0 subtrees)" >expect &&
printf "SHA #(ref) (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >>expect &&
cmp_cache_tree expect
printf "invalid %s ()\n" "" "$@" >expect &&
test-dump-cache-tree |
sed -n -e "s/[0-9]* subtrees//" -e '/#(ref)/d' -e '/^invalid /p' >actual &&
test_cmp expect actual
}
test_no_cache_tree () {
@ -32,26 +57,59 @@ test_no_cache_tree () {
cmp_cache_tree expect
}
test_expect_failure 'initial commit has cache-tree' '
test_expect_success 'initial commit has cache-tree' '
test_commit foo &&
test_shallow_cache_tree
test_cache_tree
'
test_expect_success 'read-tree HEAD establishes cache-tree' '
git read-tree HEAD &&
test_shallow_cache_tree
test_cache_tree
'
test_expect_success 'git-add invalidates cache-tree' '
test_when_finished "git reset --hard; git read-tree HEAD" &&
echo "I changed this file" > foo &&
echo "I changed this file" >foo &&
git add foo &&
test_invalid_cache_tree
'
test_expect_success 'git-add in subdir invalidates cache-tree' '
test_when_finished "git reset --hard; git read-tree HEAD" &&
mkdir dirx &&
echo "I changed this file" >dirx/foo &&
git add dirx/foo &&
test_invalid_cache_tree
'
cat >before <<\EOF
SHA (3 entries, 2 subtrees)
SHA dir1/ (1 entries, 0 subtrees)
SHA dir2/ (1 entries, 0 subtrees)
EOF
cat >expect <<\EOF
invalid (2 subtrees)
invalid dir1/ (0 subtrees)
SHA dir2/ (1 entries, 0 subtrees)
EOF
test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
git tag no-children &&
test_when_finished "git reset --hard no-children; git read-tree HEAD" &&
mkdir dir1 dir2 &&
test_commit dir1/a &&
test_commit dir2/b &&
echo "I changed this file" >dir1/a &&
cmp_cache_tree before &&
echo "I changed this file" >dir1/a &&
git add dir1/a &&
cmp_cache_tree expect
'
test_expect_success 'update-index invalidates cache-tree' '
test_when_finished "git reset --hard; git read-tree HEAD" &&
echo "I changed this file" > foo &&
echo "I changed this file" >foo &&
git update-index --add foo &&
test_invalid_cache_tree
'
@ -59,7 +117,7 @@ test_expect_success 'update-index invalidates cache-tree' '
test_expect_success 'write-tree establishes cache-tree' '
test-scrap-cache-tree &&
git write-tree &&
test_shallow_cache_tree
test_cache_tree
'
test_expect_success 'test-scrap-cache-tree works' '
@ -70,24 +128,94 @@ test_expect_success 'test-scrap-cache-tree works' '
test_expect_success 'second commit has cache-tree' '
test_commit bar &&
test_shallow_cache_tree
test_cache_tree
'
test_expect_success 'commit --interactive gives cache-tree on partial commit' '
cat <<-\EOT >foo.c &&
int foo()
{
return 42;
}
int bar()
{
return 42;
}
EOT
git add foo.c &&
test_invalid_cache_tree &&
git commit -m "add a file" &&
test_cache_tree &&
cat <<-\EOT >foo.c &&
int foo()
{
return 43;
}
int bar()
{
return 44;
}
EOT
(echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
git commit --interactive -m foo &&
test_cache_tree
'
test_expect_success 'commit in child dir has cache-tree' '
mkdir dir &&
>dir/child.t &&
git add dir/child.t &&
git commit -m dir/child.t &&
test_cache_tree
'
test_expect_success 'reset --hard gives cache-tree' '
test-scrap-cache-tree &&
git reset --hard &&
test_shallow_cache_tree
test_cache_tree
'
test_expect_success 'reset --hard without index gives cache-tree' '
rm -f .git/index &&
git reset --hard &&
test_shallow_cache_tree
test_cache_tree
'
test_expect_failure 'checkout gives cache-tree' '
test_expect_success 'checkout gives cache-tree' '
git tag current &&
git checkout HEAD^ &&
test_shallow_cache_tree
test_cache_tree
'
test_expect_success 'checkout -b gives cache-tree' '
git checkout current &&
git checkout -b prev HEAD^ &&
test_cache_tree
'
test_expect_success 'checkout -B gives cache-tree' '
git checkout current &&
git checkout -B prev HEAD^ &&
test_cache_tree
'
test_expect_success 'partial commit gives cache-tree' '
git checkout -b partial no-children &&
test_commit one &&
test_commit two &&
echo "some change" >one.t &&
git add one.t &&
echo "some other change" >two.t &&
git commit two.t -m partial &&
test_cache_tree
'
test_expect_success 'no phantom error when switching trees' '
mkdir newdir &&
>newdir/one &&
git add newdir/one &&
git checkout 2>errors &&
! test -s errors
'
test_done

Просмотреть файл

@ -26,16 +26,16 @@ static int dump_cache_tree(struct cache_tree *it,
return 0;
if (it->entry_count < 0) {
/* invalid */
dump_one(it, pfx, "");
dump_one(ref, pfx, "#(ref) ");
if (it->subtree_nr != ref->subtree_nr)
errs = 1;
}
else {
dump_one(it, pfx, "");
if (hashcmp(it->sha1, ref->sha1) ||
ref->entry_count != it->entry_count ||
ref->subtree_nr != it->subtree_nr) {
/* claims to be valid but is lying */
dump_one(ref, pfx, "#(ref) ");
errs = 1;
}