cache-tree: verify valid cache-tree in the test suite

This makes sure that cache-tree is consistent with the index. The main purpose is to catch potential problems by saving the index in unpack_trees() but the line in write_index() would also help spot missing invalidation in other code. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Aug 18, 2018 at 16:41 UTC 4592e6080ff0f9eb0218162be0e40b2d6abc979a
5 files changed +90
cache-tree.c
+78
@@ -4,6 +4,7 @@
4 #include "tree-walk.h"
5 #include "cache-tree.h"
6 #include "object-store.h"
7 +#include "replace-object.h"
8
9 #ifndef DEBUG
10 #define DEBUG 0
@@ -732,3 +733,80 @@ int update_main_cache_tree(int flags)
733 the_index.cache_tree = cache_tree();
734 return cache_tree_update(&the_index, flags);
735 }
736 +
737 +static void verify_one(struct index_state *istate,
738 + struct cache_tree *it,
739 + struct strbuf *path)
740 +{
741 + int i, pos, len = path->len;
742 + struct strbuf tree_buf = STRBUF_INIT;
743 + struct object_id new_oid;
744 +
745 + for (i = 0; i < it->subtree_nr; i++) {
746 + strbuf_addf(path, "%s/", it->down[i]->name);
747 + verify_one(istate, it->down[i]->cache_tree, path);
748 + strbuf_setlen(path, len);
749 + }
750 +
751 + if (it->entry_count < 0 ||
752 + /* no verification on tests (t7003) that replace trees */
753 + lookup_replace_object(the_repository, &it->oid) != &it->oid)
754 + return;
755 +
756 + if (path->len) {
757 + pos = index_name_pos(istate, path->buf, path->len);
758 + pos = -pos - 1;
759 + } else {
760 + pos = 0;
761 + }
762 +
763 + i = 0;
764 + while (i < it->entry_count) {
765 + struct cache_entry *ce = istate->cache[pos + i];
766 + const char *slash;
767 + struct cache_tree_sub *sub = NULL;
768 + const struct object_id *oid;
769 + const char *name;
770 + unsigned mode;
771 + int entlen;
772 +
773 + if (ce->ce_flags & (CE_STAGEMASK | CE_INTENT_TO_ADD | CE_REMOVE))
774 + BUG("%s with flags 0x%x should not be in cache-tree",
775 + ce->name, ce->ce_flags);
776 + name = ce->name + path->len;
777 + slash = strchr(name, '/');
778 + if (slash) {
779 + entlen = slash - name;
780 + sub = find_subtree(it, ce->name + path->len, entlen, 0);
781 + if (!sub || sub->cache_tree->entry_count < 0)
782 + BUG("bad subtree '%.*s'", entlen, name);
783 + oid = &sub->cache_tree->oid;
784 + mode = S_IFDIR;
785 + i += sub->cache_tree->entry_count;
786 + } else {
787 + oid = &ce->oid;
788 + mode = ce->ce_mode;
789 + entlen = ce_namelen(ce) - path->len;
790 + i++;
791 + }
792 + strbuf_addf(&tree_buf, "%o %.*s%c", mode, entlen, name, '\0');
793 + strbuf_add(&tree_buf, oid->hash, the_hash_algo->rawsz);
794 + }
795 + hash_object_file(tree_buf.buf, tree_buf.len, tree_type, &new_oid);
796 + if (oidcmp(&new_oid, &it->oid))
797 + BUG("cache-tree for path %.*s does not match. "
798 + "Expected %s got %s", len, path->buf,
799 + oid_to_hex(&new_oid), oid_to_hex(&it->oid));
800 + strbuf_setlen(path, len);
801 + strbuf_release(&tree_buf);
802 +}
803 +
804 +void cache_tree_verify(struct index_state *istate)
805 +{
806 + struct strbuf path = STRBUF_INIT;
807 +
808 + if (!istate->cache_tree)
809 + return;
810 + verify_one(istate, istate->cache_tree, &path);
811 + strbuf_release(&path);
812 +}
cache-tree.h
+1
@@ -32,6 +32,7 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
32
33 int cache_tree_fully_valid(struct cache_tree *);
34 int cache_tree_update(struct index_state *, int);
35 +void cache_tree_verify(struct index_state *);
36
37 int update_main_cache_tree(int);
38
read-cache.c
+3
@@ -2744,6 +2744,9 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
2744 int new_shared_index, ret;
2745 struct split_index *si = istate->split_index;
2746
2747 + if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
2748 + cache_tree_verify(istate);
2749 +
2750 if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
2751 if (flags & COMMIT_LOCK)
2752 rollback_lock_file(lock);
t/test-lib.sh
+6
@@ -1083,6 +1083,12 @@ else
1083 test_set_prereq C_LOCALE_OUTPUT
1084 fi
1085
1086 +if test -z "$GIT_TEST_CHECK_CACHE_TREE"
1087 +then
1088 + GIT_TEST_CHECK_CACHE_TREE=true
1089 + export GIT_TEST_CHECK_CACHE_TREE
1090 +fi
1091 +
1092 test_lazy_prereq PIPE '
1093 # test whether the filesystem supports FIFOs
1094 test_have_prereq !MINGW,!CYGWIN &&
unpack-trees.c
+2
@@ -1578,6 +1578,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
1578 if (o->dst_index) {
1579 move_index_extensions(&o->result, o->src_index);
1580 if (!ret) {
1581 + if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
1582 + cache_tree_verify(&o->result);
1583 if (!o->result.cache_tree)
1584 o->result.cache_tree = cache_tree();
1585 if (!cache_tree_fully_valid(o->result.cache_tree))