diff options
Diffstat (limited to 'cache-tree.c')
-rw-r--r-- | cache-tree.c | 505 |
1 files changed, 356 insertions, 149 deletions
diff --git a/cache-tree.c b/cache-tree.c index 37e4d008b5..90919f9e34 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -1,10 +1,15 @@ #include "cache.h" +#include "lockfile.h" #include "tree.h" #include "tree-walk.h" #include "cache-tree.h" +#include "object-store.h" +#include "replace-object.h" +#include "promisor-remote.h" +#include "sparse-index.h" -#ifndef DEBUG -#define DEBUG 0 +#ifndef DEBUG_CACHE_TREE +#define DEBUG_CACHE_TREE 0 #endif struct cache_tree *cache_tree(void) @@ -41,14 +46,14 @@ static int subtree_name_cmp(const char *one, int onelen, return memcmp(one, two, onelen); } -static int subtree_pos(struct cache_tree *it, const char *path, int pathlen) +int cache_tree_subtree_pos(struct cache_tree *it, const char *path, int pathlen) { struct cache_tree_sub **down = it->down; int lo, hi; lo = 0; hi = it->subtree_nr; while (lo < hi) { - int mi = (lo + hi) / 2; + int mi = lo + (hi - lo) / 2; struct cache_tree_sub *mdl = down[mi]; int cmp = subtree_name_cmp(path, pathlen, mdl->name, mdl->namelen); @@ -68,30 +73,23 @@ static struct cache_tree_sub *find_subtree(struct cache_tree *it, int create) { struct cache_tree_sub *down; - int pos = subtree_pos(it, path, pathlen); + int pos = cache_tree_subtree_pos(it, path, pathlen); if (0 <= pos) return it->down[pos]; if (!create) return NULL; pos = -pos-1; - if (it->subtree_alloc <= it->subtree_nr) { - it->subtree_alloc = alloc_nr(it->subtree_alloc); - it->down = xrealloc(it->down, it->subtree_alloc * - sizeof(*it->down)); - } + ALLOC_GROW(it->down, it->subtree_nr + 1, it->subtree_alloc); it->subtree_nr++; - down = xmalloc(sizeof(*down) + pathlen + 1); + FLEX_ALLOC_MEM(down, name, path, pathlen); down->cache_tree = NULL; down->namelen = pathlen; - memcpy(down->name, path, pathlen); - down->name[pathlen] = 0; if (pos < it->subtree_nr) - memmove(it->down + pos + 1, - it->down + pos, - sizeof(down) * (it->subtree_nr - pos - 1)); + MOVE_ARRAY(it->down + pos + 1, it->down + pos, + it->subtree_nr - pos - 1); it->down[pos] = down; return down; } @@ -102,7 +100,7 @@ struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path) return find_subtree(it, path, pathlen, 1); } -void cache_tree_invalidate_path(struct cache_tree *it, const char *path) +static int do_invalidate_path(struct cache_tree *it, const char *path) { /* a/b/c * ==> invalidate self @@ -115,18 +113,18 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path) int namelen; struct cache_tree_sub *down; -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree invalidate <%s>\n", path); #endif if (!it) - return; - slash = strchr(path, '/'); + return 0; + slash = strchrnul(path, '/'); + namelen = slash - path; it->entry_count = -1; - if (!slash) { + if (!*slash) { int pos; - namelen = strlen(path); - pos = subtree_pos(it, path, namelen); + pos = cache_tree_subtree_pos(it, path, namelen); if (0 <= pos) { cache_tree_free(&it->down[pos]->cache_tree); free(it->down[pos]); @@ -136,29 +134,33 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path) * move 4 and 5 up one place (2 entries) * 2 = 6 - 3 - 1 = subtree_nr - pos - 1 */ - memmove(it->down+pos, it->down+pos+1, - sizeof(struct cache_tree_sub *) * - (it->subtree_nr - pos - 1)); + MOVE_ARRAY(it->down + pos, it->down + pos + 1, + it->subtree_nr - pos - 1); it->subtree_nr--; } - return; + return 1; } - namelen = slash - path; down = find_subtree(it, path, namelen, 0); if (down) - cache_tree_invalidate_path(down->cache_tree, slash + 1); + do_invalidate_path(down->cache_tree, slash + 1); + return 1; +} + +void cache_tree_invalidate_path(struct index_state *istate, const char *path) +{ + if (do_invalidate_path(istate->cache_tree, path)) + istate->cache_changed |= CACHE_TREE_CHANGED; } -static int verify_cache(struct cache_entry **cache, - int entries, int flags) +static int verify_cache(struct index_state *istate, int flags) { - int i, funny; + unsigned i, funny; int silent = flags & WRITE_TREE_SILENT; /* Verify that the tree is merged */ funny = 0; - for (i = 0; i < entries; i++) { - struct cache_entry *ce = cache[i]; + for (i = 0; i < istate->cache_nr; i++) { + const struct cache_entry *ce = istate->cache[i]; if (ce_stage(ce)) { if (silent) return -1; @@ -167,7 +169,7 @@ static int verify_cache(struct cache_entry **cache, break; } fprintf(stderr, "%s: unmerged (%s)\n", - ce->name, sha1_to_hex(ce->sha1)); + ce->name, oid_to_hex(&ce->oid)); } } if (funny) @@ -178,17 +180,19 @@ static int verify_cache(struct cache_entry **cache, * stage 0 entries. */ funny = 0; - for (i = 0; i < entries - 1; i++) { + for (i = 0; i + 1 < istate->cache_nr; i++) { /* path/file always comes after path because of the way * the cache is sorted. Also path can appear only once, * which means conflicting one would immediately follow. */ - const char *this_name = cache[i]->name; - const char *next_name = cache[i+1]->name; - int this_len = strlen(this_name); - if (this_len < strlen(next_name) && - strncmp(this_name, next_name, this_len) == 0 && - next_name[this_len] == '/') { + const struct cache_entry *this_ce = istate->cache[i]; + const struct cache_entry *next_ce = istate->cache[i + 1]; + const char *this_name = this_ce->name; + const char *next_name = next_ce->name; + int this_len = ce_namelen(this_ce); + if (this_len < ce_namelen(next_ce) && + next_name[this_len] == '/' && + strncmp(this_name, next_name, this_len) == 0) { if (10 < ++funny) { fprintf(stderr, "...\n"); break; @@ -224,7 +228,7 @@ int cache_tree_fully_valid(struct cache_tree *it) int i; if (!it) return 0; - if (it->entry_count < 0 || !has_sha1_file(it->sha1)) + if (it->entry_count < 0 || !has_object_file(&it->oid)) return 0; for (i = 0; i < it->subtree_nr; i++) { if (!cache_tree_fully_valid(it->down[i]->cache_tree)) @@ -233,6 +237,11 @@ int cache_tree_fully_valid(struct cache_tree *it) return 1; } +static int must_check_existence(const struct cache_entry *ce) +{ + return !(has_promisor_remote() && ce_skip_worktree(ce)); +} + static int update_one(struct cache_tree *it, struct cache_entry **cache, int entries, @@ -244,12 +253,33 @@ 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)) + /* + * If the first entry of this region is a sparse directory + * entry corresponding exactly to 'base', then this cache_tree + * struct is a "leaf" in the data structure, pointing to the + * tree OID specified in the entry. + */ + if (entries > 0) { + const struct cache_entry *ce = cache[0]; + + if (S_ISSPARSEDIR(ce->ce_mode) && + ce->ce_namelen == baselen && + !strncmp(ce->name, base, baselen)) { + it->entry_count = 1; + oidcpy(&it->oid, &ce->oid); + return 1; + } + } + + if (0 <= it->entry_count && has_object_file(&it->oid)) return it->entry_count; /* @@ -265,7 +295,7 @@ static int update_one(struct cache_tree *it, */ i = 0; while (i < entries) { - struct cache_entry *ce = cache[i]; + const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, sublen, subcnt, subskip; @@ -297,6 +327,8 @@ static int update_one(struct cache_tree *it, flags); if (subcnt < 0) return subcnt; + if (!subcnt) + die("index cache-tree records empty sub-tree"); i += subcnt; sub->count = subcnt; /* to be used in the next loop */ *skip_count += subskip; @@ -312,12 +344,15 @@ static int update_one(struct cache_tree *it, i = 0; while (i < entries) { - struct cache_entry *ce = cache[i]; - struct cache_tree_sub *sub; + const struct cache_entry *ce = cache[i]; + struct cache_tree_sub *sub = NULL; const char *path, *slash; int pathlen, entlen; - const unsigned char *sha1; + const struct object_id *oid; unsigned mode; + int expected_missing = 0; + int contains_ita = 0; + int ce_missing_ok; path = ce->name; pathlen = ce_namelen(ce); @@ -332,21 +367,30 @@ static int update_one(struct cache_tree *it, die("cache-tree.c: '%.*s' in '%s' not found", entlen, path + baselen, path); i += sub->count; - sha1 = sub->cache_tree->sha1; + oid = &sub->cache_tree->oid; mode = S_IFDIR; - if (sub->cache_tree->entry_count < 0) + contains_ita = sub->cache_tree->entry_count < 0; + if (contains_ita) { to_invalidate = 1; + expected_missing = 1; + } } else { - sha1 = ce->sha1; + oid = &ce->oid; mode = ce->ce_mode; entlen = pathlen - baselen; i++; } - if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) { + + ce_missing_ok = mode == S_IFGITLINK || missing_ok || + !must_check_existence(ce); + if (is_null_oid(oid) || + (!ce_missing_ok && !has_object_file(oid))) { strbuf_release(&buffer); + if (expected_missing) + return -1; return error("invalid object %06o %s for '%.*s'", - mode, sha1_to_hex(sha1), entlen+baselen, path); + mode, oid_to_hex(oid), entlen+baselen, path); } /* @@ -364,55 +408,83 @@ static int update_one(struct cache_tree *it, * they are not part of generated trees. Invalidate up * to root to force cache-tree users to read elsewhere. */ - if (ce->ce_flags & CE_INTENT_TO_ADD) { + if (!sub && ce_intent_to_add(ce)) { to_invalidate = 1; continue; } + /* + * "sub" can be an empty tree if all subentries are i-t-a. + */ + if (contains_ita && is_empty_tree_oid(oid)) + continue; + strbuf_grow(&buffer, entlen + 100); strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); - strbuf_add(&buffer, sha1, 20); + strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz); -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif } - 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)) { + if (repair) { + struct object_id oid; + hash_object_file(the_hash_algo, buffer.buf, buffer.len, + tree_type, &oid); + if (has_object_file_with_flags(&oid, OBJECT_INFO_SKIP_FETCH_OBJECT)) + oidcpy(&it->oid, &oid); + else + to_invalidate = 1; + } else if (dryrun) { + hash_object_file(the_hash_algo, buffer.buf, buffer.len, + tree_type, &it->oid); + } else if (write_object_file(buffer.buf, buffer.len, tree_type, + &it->oid)) { strbuf_release(&buffer); return -1; } strbuf_release(&buffer); it->entry_count = to_invalidate ? -1 : i - *skip_count; -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); #endif return i; } -int cache_tree_update(struct cache_tree *it, - struct cache_entry **cache, - int entries, - int flags) +int cache_tree_update(struct index_state *istate, int flags) { - int i, skip; - i = verify_cache(cache, entries, flags); + int skip, i; + + i = verify_cache(istate, flags); + if (i) return i; - i = update_one(it, cache, entries, "", 0, &skip, flags); + + if (!istate->cache_tree) + istate->cache_tree = cache_tree(); + + if (!(flags & WRITE_TREE_MISSING_OK) && has_promisor_remote()) + prefetch_cache_entries(istate, must_check_existence); + + trace_performance_enter(); + trace2_region_enter("cache_tree", "update", the_repository); + i = update_one(istate->cache_tree, istate->cache, istate->cache_nr, + "", 0, &skip, flags); + trace2_region_leave("cache_tree", "update", the_repository); + trace_performance_leave("cache_tree_update"); if (i < 0) return i; + istate->cache_changed |= CACHE_TREE_CHANGED; return 0; } static void write_one(struct strbuf *buffer, struct cache_tree *it, - const char *path, int pathlen) + const char *path, int pathlen) { int i; @@ -426,18 +498,18 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it, strbuf_add(buffer, path, pathlen); strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr); -#if DEBUG +#if DEBUG_CACHE_TREE if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n", pathlen, path, it->entry_count, it->subtree_nr, - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); else fprintf(stderr, "cache-tree <%.*s> (%d subtree) invalid\n", pathlen, path, it->subtree_nr); #endif if (0 <= it->entry_count) { - strbuf_add(buffer, it->sha1, 20); + strbuf_add(buffer, it->oid.hash, the_hash_algo->rawsz); } for (i = 0; i < it->subtree_nr; i++) { struct cache_tree_sub *down = it->down[i]; @@ -453,7 +525,9 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it, void cache_tree_write(struct strbuf *sb, struct cache_tree *root) { + trace2_region_enter("cache_tree", "write", the_repository); write_one(sb, root, "", 0); + trace2_region_leave("cache_tree", "write", the_repository); } static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) @@ -464,6 +538,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) char *ep; struct cache_tree *it; int i, subtree_nr; + const unsigned rawsz = the_hash_algo->rawsz; it = NULL; /* skip name, but make sure name exists */ @@ -492,18 +567,18 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) goto free_return; buf++; size--; if (0 <= it->entry_count) { - if (size < 20) + if (size < rawsz) goto free_return; - hashcpy(it->sha1, (const unsigned char*)buf); - buf += 20; - size -= 20; + oidread(&it->oid, (const unsigned char *)buf); + buf += rawsz; + size -= rawsz; } -#if DEBUG +#if DEBUG_CACHE_TREE if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n", *buffer, it->entry_count, subtree_nr, - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); else fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n", *buffer, subtree_nr); @@ -515,7 +590,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) * hence +2. */ it->subtree_alloc = subtree_nr + 2; - it->down = xcalloc(it->subtree_alloc, sizeof(struct cache_tree_sub *)); + CALLOC_ARRAY(it->down, it->subtree_alloc); for (i = 0; i < subtree_nr; i++) { /* read each subtree */ struct cache_tree *sub; @@ -541,9 +616,16 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) struct cache_tree *cache_tree_read(const char *buffer, unsigned long size) { + struct cache_tree *result; + if (buffer[0]) return NULL; /* not the whole tree */ - return read_one(&buffer, &size); + + trace2_region_enter("cache_tree", "read", the_repository); + result = read_one(&buffer, &size); + trace2_region_leave("cache_tree", "read", the_repository); + + return result; } static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path) @@ -554,59 +636,97 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat const char *slash; struct cache_tree_sub *sub; - slash = strchr(path, '/'); - if (!slash) - slash = path + strlen(path); - /* between path and slash is the name of the - * subtree to look for. + slash = strchrnul(path, '/'); + /* + * Between path and slash is the name of the subtree + * to look for. */ sub = find_subtree(it, path, slash - path, 0); if (!sub) return NULL; it = sub->cache_tree; - if (slash) - while (*slash && *slash == '/') - slash++; - if (!slash || !*slash) - return it; /* prefix ended with slashes */ + path = slash; + while (*path == '/') + path++; } return it; } -int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix) +static int write_index_as_tree_internal(struct object_id *oid, + struct index_state *index_state, + int cache_tree_valid, + int flags, + const char *prefix) { - int entries, was_valid, newfd; - struct lock_file *lock_file; + if (flags & WRITE_TREE_IGNORE_CACHE_TREE) { + cache_tree_free(&index_state->cache_tree); + cache_tree_valid = 0; + } - /* - * We can't free this memory, it becomes part of a linked list - * parsed atexit() - */ - lock_file = xcalloc(1, sizeof(struct lock_file)); - - newfd = hold_locked_index(lock_file, 1); - - entries = read_cache(); - if (entries < 0) - return WRITE_TREE_UNREADABLE_INDEX; - if (flags & WRITE_TREE_IGNORE_CACHE_TREE) - cache_tree_free(&(active_cache_tree)); - - if (!active_cache_tree) - active_cache_tree = cache_tree(); - - was_valid = cache_tree_fully_valid(active_cache_tree); - if (!was_valid) { - if (cache_tree_update(active_cache_tree, - active_cache, active_nr, - flags) < 0) - return WRITE_TREE_UNMERGED_INDEX; - if (0 <= newfd) { - if (!write_cache(newfd, active_cache, active_nr) && - !commit_lock_file(lock_file)) - newfd = -1; + if (!cache_tree_valid && cache_tree_update(index_state, flags) < 0) + return WRITE_TREE_UNMERGED_INDEX; + + if (prefix) { + struct cache_tree *subtree; + subtree = cache_tree_find(index_state->cache_tree, prefix); + if (!subtree) + return WRITE_TREE_PREFIX_ERROR; + oidcpy(oid, &subtree->oid); + } + else + oidcpy(oid, &index_state->cache_tree->oid); + + return 0; +} + +struct tree* write_in_core_index_as_tree(struct repository *repo) { + struct object_id o; + int was_valid, ret; + + struct index_state *index_state = repo->index; + was_valid = index_state->cache_tree && + cache_tree_fully_valid(index_state->cache_tree); + + ret = write_index_as_tree_internal(&o, index_state, was_valid, 0, NULL); + if (ret == WRITE_TREE_UNMERGED_INDEX) { + int i; + fprintf(stderr, "BUG: There are unmerged index entries:\n"); + for (i = 0; i < index_state->cache_nr; i++) { + const struct cache_entry *ce = index_state->cache[i]; + if (ce_stage(ce)) + fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce), + (int)ce_namelen(ce), ce->name); } + BUG("unmerged index entries when writing inmemory index"); + } + + return lookup_tree(repo, &index_state->cache_tree->oid); +} + + +int write_index_as_tree(struct object_id *oid, struct index_state *index_state, const char *index_path, int flags, const char *prefix) +{ + int entries, was_valid; + struct lock_file lock_file = LOCK_INIT; + int ret; + + hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR); + + entries = read_index_from(index_state, index_path, get_git_dir()); + if (entries < 0) { + ret = WRITE_TREE_UNREADABLE_INDEX; + goto out; + } + + was_valid = !(flags & WRITE_TREE_IGNORE_CACHE_TREE) && + index_state->cache_tree && + cache_tree_fully_valid(index_state->cache_tree); + + ret = write_index_as_tree_internal(oid, index_state, was_valid, flags, + prefix); + if (!ret && !was_valid) { + write_locked_index(index_state, &lock_file, COMMIT_LOCK); /* Not being able to write is fine -- we are only interested * in updating the cache-tree part, and if the next caller * ends up using the old index with unupdated cache-tree part @@ -615,29 +735,20 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix) */ } - if (prefix) { - struct cache_tree *subtree = - cache_tree_find(active_cache_tree, prefix); - if (!subtree) - return WRITE_TREE_PREFIX_ERROR; - hashcpy(sha1, subtree->sha1); - } - else - hashcpy(sha1, active_cache_tree->sha1); - - if (0 <= newfd) - rollback_lock_file(lock_file); - - return 0; +out: + rollback_lock_file(&lock_file); + return ret; } -static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) +static void prime_cache_tree_rec(struct repository *r, + struct cache_tree *it, + struct tree *tree) { struct tree_desc desc; struct name_entry entry; int cnt; - hashcpy(it->sha1, tree->object.sha1); + oidcpy(&it->oid, &tree->object.oid); init_tree_desc(&desc, tree->buffer, tree->size); cnt = 0; while (tree_entry(&desc, &entry)) { @@ -645,23 +756,29 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) cnt++; else { struct cache_tree_sub *sub; - struct tree *subtree = lookup_tree(entry.sha1); + struct tree *subtree = lookup_tree(r, &entry.oid); if (!subtree->object.parsed) parse_tree(subtree); sub = cache_tree_sub(it, entry.path); sub->cache_tree = cache_tree(); - prime_cache_tree_rec(sub->cache_tree, subtree); + prime_cache_tree_rec(r, sub->cache_tree, subtree); cnt += sub->cache_tree->entry_count; } } it->entry_count = cnt; } -void prime_cache_tree(struct cache_tree **it, struct tree *tree) +void prime_cache_tree(struct repository *r, + struct index_state *istate, + struct tree *tree) { - cache_tree_free(it); - *it = cache_tree(); - prime_cache_tree_rec(*it, tree); + trace2_region_enter("cache-tree", "prime_cache_tree", the_repository); + cache_tree_free(&istate->cache_tree); + istate->cache_tree = cache_tree(); + + prime_cache_tree_rec(r, istate->cache_tree, tree); + istate->cache_changed |= CACHE_TREE_CHANGED; + trace2_region_leave("cache-tree", "prime_cache_tree", the_repository); } /* @@ -680,7 +797,7 @@ static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root if (!info->prev) return root; our_parent = find_cache_tree_from_traversal(root, info->prev); - return cache_tree_find(our_parent, info->name.path); + return cache_tree_find(our_parent, info->name); } int cache_tree_matches_traversal(struct cache_tree *root, @@ -691,15 +808,105 @@ int cache_tree_matches_traversal(struct cache_tree *root, it = find_cache_tree_from_traversal(root, info); it = cache_tree_find(it, ent->path); - if (it && it->entry_count > 0 && !hashcmp(ent->sha1, it->sha1)) + if (it && it->entry_count > 0 && oideq(&ent->oid, &it->oid)) return it->entry_count; return 0; } -int update_main_cache_tree(int flags) +static void verify_one_sparse(struct repository *r, + struct index_state *istate, + struct cache_tree *it, + struct strbuf *path, + int pos) +{ + struct cache_entry *ce = istate->cache[pos]; + + if (!S_ISSPARSEDIR(ce->ce_mode)) + BUG("directory '%s' is present in index, but not sparse", + path->buf); +} + +static void verify_one(struct repository *r, + struct index_state *istate, + struct cache_tree *it, + struct strbuf *path) { - if (!the_index.cache_tree) - the_index.cache_tree = cache_tree(); - return cache_tree_update(the_index.cache_tree, - the_index.cache, the_index.cache_nr, flags); + int i, pos, len = path->len; + struct strbuf tree_buf = STRBUF_INIT; + struct object_id new_oid; + + for (i = 0; i < it->subtree_nr; i++) { + strbuf_addf(path, "%s/", it->down[i]->name); + verify_one(r, istate, it->down[i]->cache_tree, path); + strbuf_setlen(path, len); + } + + if (it->entry_count < 0 || + /* no verification on tests (t7003) that replace trees */ + lookup_replace_object(r, &it->oid) != &it->oid) + return; + + if (path->len) { + pos = index_name_pos(istate, path->buf, path->len); + + if (pos >= 0) { + verify_one_sparse(r, istate, it, path, pos); + return; + } + + pos = -pos - 1; + } else { + pos = 0; + } + + i = 0; + while (i < it->entry_count) { + struct cache_entry *ce = istate->cache[pos + i]; + const char *slash; + struct cache_tree_sub *sub = NULL; + const struct object_id *oid; + const char *name; + unsigned mode; + int entlen; + + if (ce->ce_flags & (CE_STAGEMASK | CE_INTENT_TO_ADD | CE_REMOVE)) + BUG("%s with flags 0x%x should not be in cache-tree", + ce->name, ce->ce_flags); + name = ce->name + path->len; + slash = strchr(name, '/'); + if (slash) { + entlen = slash - name; + sub = find_subtree(it, ce->name + path->len, entlen, 0); + if (!sub || sub->cache_tree->entry_count < 0) + BUG("bad subtree '%.*s'", entlen, name); + oid = &sub->cache_tree->oid; + mode = S_IFDIR; + i += sub->cache_tree->entry_count; + } else { + oid = &ce->oid; + mode = ce->ce_mode; + entlen = ce_namelen(ce) - path->len; + i++; + } + strbuf_addf(&tree_buf, "%o %.*s%c", mode, entlen, name, '\0'); + strbuf_add(&tree_buf, oid->hash, r->hash_algo->rawsz); + } + hash_object_file(r->hash_algo, tree_buf.buf, tree_buf.len, tree_type, + &new_oid); + if (!oideq(&new_oid, &it->oid)) + BUG("cache-tree for path %.*s does not match. " + "Expected %s got %s", len, path->buf, + oid_to_hex(&new_oid), oid_to_hex(&it->oid)); + strbuf_setlen(path, len); + strbuf_release(&tree_buf); +} + +void cache_tree_verify(struct repository *r, struct index_state *istate) +{ + struct strbuf path = STRBUF_INIT; + + if (!istate->cache_tree) + return; + verify_one(r, istate, istate->cache_tree, &path); + strbuf_release(&path); } |