read-tree A B C: do not create a bogus index and do not segfault
authorJunio C Hamano <gitster@pobox.com>
Thu, 12 Mar 2009 07:02:12 +0000 (00:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 13 Mar 2009 00:06:07 +0000 (17:06 -0700)
"git read-tree A B C..." without the "-m" (merge) option is a way to read
these trees on top of each other to get an overlay of them.

An ancient commit ee6566e (Rewrite read-tree, 2005-09-05) passed the
ADD_CACHE_SKIP_DFCHECK flag when calling add_index_entry() to add the
paths obtained from these trees to the index, but it is an incorrect use
of the flag.  The flag is meant to be used by callers who know the
addition of the entry does not introduce a D/F conflict to the index in
order to avoid the overhead of checking.

This bug resulted in a bogus index that records both "x" and "x/z" as a
blob after reading three trees that have paths ("x"), ("x", "y"), and
("x/z", "y") respectively.  34110cd (Make 'unpack_trees()' have a separate
source and destination index, 2008-03-06) refactored the callsites of
add_index_entry() incorrectly and added more codepaths that use this flag
when it shouldn't be used.

Also, 0190457 (Move 'unpack_trees()' over to 'traverse_trees()' interface,
2008-03-05) introduced a bug to call add_index_entry() for the tree that
does not have the path in it, passing NULL as a cache entry.  This caused
reading multiple trees, one of which has path "x" but another doesn't, to
segfault.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
t/t1008-read-tree-overlay.sh [new file with mode: 0755]
unpack-trees.c

diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
new file mode 100755 (executable)
index 0000000..f9e0028
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='test multi-tree read-tree without merging'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo one >a &&
+       git add a &&
+       git commit -m initial &&
+       git tag initial &&
+       echo two >b &&
+       git add b &&
+       git commit -m second &&
+       git checkout -b side initial &&
+       echo three >a &&
+       mkdir b &&
+       echo four >b/c &&
+       git add b/c &&
+       git commit -m third
+'
+
+test_expect_success 'multi-read' '
+       git read-tree initial master side &&
+       (echo a; echo b/c) >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual
+'
+
+test_done
+
index cba0aca062f201c5cd5f8799f2190d4a6f06e7c7..5b0a8c1728b30ebe06d12cc0a1e8c1a08e6caaef 100644 (file)
@@ -49,7 +49,7 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
        memcpy(new, ce, size);
        new->next = NULL;
        new->ce_flags = (new->ce_flags & ~clear) | set;
-       add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
+       add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
 /* Unlink the last component and attempt to remove leading
@@ -283,9 +283,9 @@ static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmas
        if (o->merge)
                return call_unpack_fn(src, o);
 
-       n += o->merge;
        for (i = 0; i < n; i++)
-               add_entry(o, src[i], 0, 0);
+               if (src[i] && src[i] != o->df_conflict_entry)
+                       add_entry(o, src[i], 0, 0);
        return 0;
 }