From c041d54a741e516b5a743f4d27532e484f2a05d3 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Sat, 16 Jul 2016 07:06:26 +0200 Subject: cache-tree.c: fix i-t-a entry skipping directory updates sometimes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 3cf773e (cache-tree: fix writing cache-tree when CE_REMOVE is present - 2012-12-16) skips i-t-a entries when building trees objects from the index. Unfortunately it may skip too much. The code in question checks if an entry is an i-t-a one, then no tree entry will be written. But it does not take into account that directories can also be written with the same code. Suppose we have this in the index. a-file subdir/file1 subdir/file2 subdir/file3 the-last-file We write an entry for a-file as normal and move on to subdir/file1, where we realize the entry name for this level is simply just "subdir", write down an entry for "subdir" then jump three items ahead to the-last-file. That is what happens normally when the first file in subdir is not an i-t-a entry. If subdir/file1 is an i-t-a, because of the broken condition in this code, we still think "subdir" is an i-t-a file and not writing "subdir" down and jump to the-last-file. The result tree now only has two items: a-file and the-last-file. subdir should be there too (even though it only records two sub-entries, file2 and file3). If the i-t-a entry is subdir/file2 or subdir/file3, this is not a problem because we jump over them anyway. Which may explain why the bug is hidden for nearly four years. Fix it by making sure we only skip i-t-a entries when the entry in question is actual an index entry, not a directory. Reported-by: Yuri Kanivetsky Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- cache-tree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cache-tree.c') diff --git a/cache-tree.c b/cache-tree.c index ddf0cc9f9a..c2676e8a31 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -319,7 +319,7 @@ static int update_one(struct cache_tree *it, i = 0; while (i < entries) { const struct cache_entry *ce = cache[i]; - struct cache_tree_sub *sub; + struct cache_tree_sub *sub = NULL; const char *path, *slash; int pathlen, entlen; const unsigned char *sha1; @@ -375,7 +375,7 @@ 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_intent_to_add(ce)) { + if (!sub && ce_intent_to_add(ce)) { to_invalidate = 1; continue; } -- cgit v1.3 From 6d6a782fbf83927212f348f91823d886c5cd6d85 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Sat, 16 Jul 2016 07:06:27 +0200 Subject: cache-tree: do not generate empty trees as a result of all i-t-a subentries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a subdirectory contains nothing but i-t-a entries, we generate an empty tree object and add it to its parent tree. Which is wrong. Such a subdirectory should not be added. Note that this has a cascading effect. If subdir 'a/b/c' contains nothing but i-t-a entries, we ignore it. But then if 'a/b' contains only (the non-existing) 'a/b/c', then we should ignore 'a/b' while building 'a' too. And it goes all the way up to top directory. Noticed-by: Junio C Hamano Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- cache-tree.c | 10 +++++++++- t/t2203-add-intent.sh | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'cache-tree.c') diff --git a/cache-tree.c b/cache-tree.c index c2676e8a31..f28b1f45a4 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -325,6 +325,7 @@ static int update_one(struct cache_tree *it, const unsigned char *sha1; unsigned mode; int expected_missing = 0; + int contains_ita = 0; path = ce->name; pathlen = ce_namelen(ce); @@ -341,7 +342,8 @@ 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) { + contains_ita = sub->cache_tree->entry_count < 0; + if (contains_ita) { to_invalidate = 1; expected_missing = 1; } @@ -380,6 +382,12 @@ static int update_one(struct cache_tree *it, continue; } + /* + * "sub" can be an empty tree if all subentries are i-t-a. + */ + if (contains_ita && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN)) + continue; + strbuf_grow(&buffer, entlen + 100); strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); strbuf_add(&buffer, sha1, 20); diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index 24aed2e541..8f22c43e24 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -99,5 +99,19 @@ test_expect_success 'cache-tree does not ignore dir that has i-t-a entries' ' ) ' +test_expect_success 'cache-tree does skip dir that becomes empty' ' + rm -fr ita-in-dir && + git init ita-in-dir && + ( + cd ita-in-dir && + mkdir -p 1/2/3 && + echo 4 >1/2/3/4 && + git add -N 1/2/3/4 && + git write-tree >actual && + echo $EMPTY_TREE >expected && + test_cmp expected actual + ) +' + test_done -- cgit v1.3