Skip to content

Commit bd54cf1

Browse files
peffgitster
authored andcommitted
archive: handle commits with an empty tree
git-archive relies on get_pathspec to convert its argv into a list of pathspecs. When get_pathspec is given an empty argv list, it returns a single pathspec, the empty string, to indicate that everything matches. When we feed this to our path_exists function, we typically see that the pathspec turns up at least one item in the tree, and we are happy. But when our tree is empty, we erroneously think it is because the pathspec is too limited, when in fact it is simply that there is nothing to be found in the tree. This is a weird corner case, but the correct behavior is almost certainly to produce an empty archive, not to exit with an error. This patch teaches git-archive to create empty archives when there is no pathspec given (we continue to complain if a pathspec is given, since it obviously is not matched). It also confirms that the tar and zip writers produce sane output in this instance. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f838ce5 commit bd54cf1

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

archive.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ static void parse_pathspec_arg(const char **pathspec,
234234
ar_args->pathspec = pathspec = get_pathspec("", pathspec);
235235
if (pathspec) {
236236
while (*pathspec) {
237-
if (!path_exists(ar_args->tree, *pathspec))
237+
if (**pathspec && !path_exists(ar_args->tree, *pathspec))
238238
die("path not found: %s", *pathspec);
239239
pathspec++;
240240
}

t/t5004-archive-corner-cases.sh

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/bin/sh
2+
3+
test_description='test corner cases of git-archive'
4+
. ./test-lib.sh
5+
6+
test_expect_success 'create commit with empty tree' '
7+
git commit --allow-empty -m foo
8+
'
9+
10+
# Make a dir and clean it up afterwards
11+
make_dir() {
12+
mkdir "$1" &&
13+
test_when_finished "rm -rf '$1'"
14+
}
15+
16+
# Check that the dir given in "$1" contains exactly the
17+
# set of paths given as arguments.
18+
check_dir() {
19+
dir=$1; shift
20+
{
21+
echo "$dir" &&
22+
for i in "$@"; do
23+
echo "$dir/$i"
24+
done
25+
} | sort >expect &&
26+
find "$dir" -print | sort >actual &&
27+
test_cmp expect actual
28+
}
29+
30+
test_expect_success 'tar archive of empty tree is empty' '
31+
git archive --format=tar HEAD >empty.tar &&
32+
make_dir extract &&
33+
"$TAR" xf empty.tar -C extract &&
34+
check_dir extract
35+
'
36+
37+
test_expect_success 'tar archive of empty tree with prefix' '
38+
git archive --format=tar --prefix=foo/ HEAD >prefix.tar &&
39+
make_dir extract &&
40+
"$TAR" xf prefix.tar -C extract &&
41+
check_dir extract foo
42+
'
43+
44+
test_expect_success UNZIP 'zip archive of empty tree is empty' '
45+
# Detect the exit code produced when our particular flavor of unzip
46+
# sees an empty archive. Infozip will generate a warning and exit with
47+
# code 1. But in the name of sanity, we do not expect other unzip
48+
# implementations to do the same thing (it would be perfectly
49+
# reasonable to exit 0, for example).
50+
#
51+
# This makes our test less rigorous on some platforms (unzip may not
52+
# handle the empty repo at all, making our later check of its exit code
53+
# a no-op). But we cannot do anything reasonable except skip the test
54+
# on such platforms anyway, and this is the moral equivalent.
55+
"$GIT_UNZIP" "$TEST_DIRECTORY"/t5004/empty.zip
56+
expect_code=$?
57+
58+
git archive --format=zip HEAD >empty.zip &&
59+
make_dir extract &&
60+
(
61+
cd extract &&
62+
test_expect_code $expect_code "$GIT_UNZIP" ../empty.zip
63+
) &&
64+
check_dir extract
65+
'
66+
67+
test_expect_success UNZIP 'zip archive of empty tree with prefix' '
68+
# We do not have to play exit-code tricks here, because our
69+
# result should not be empty; it has a directory in it.
70+
git archive --format=zip --prefix=foo/ HEAD >prefix.zip &&
71+
make_dir extract &&
72+
(
73+
cd extract &&
74+
"$GIT_UNZIP" ../prefix.zip
75+
) &&
76+
check_dir extract foo
77+
'
78+
79+
test_expect_success 'archive complains about pathspec on empty tree' '
80+
test_must_fail git archive --format=tar HEAD -- foo >/dev/null
81+
'
82+
83+
test_expect_success 'create a commit with an empty subtree' '
84+
empty_tree=$(git hash-object -t tree /dev/null) &&
85+
root_tree=$(printf "040000 tree $empty_tree\tsub\n" | git mktree)
86+
'
87+
88+
test_expect_success 'archive empty subtree with no pathspec' '
89+
git archive --format=tar $root_tree >subtree-all.tar &&
90+
make_dir extract &&
91+
"$TAR" xf subtree-all.tar -C extract &&
92+
check_dir extract sub
93+
'
94+
95+
test_expect_success 'archive empty subtree by direct pathspec' '
96+
git archive --format=tar $root_tree -- sub >subtree-path.tar &&
97+
make_dir extract &&
98+
"$TAR" xf subtree-path.tar -C extract &&
99+
check_dir extract sub
100+
'
101+
102+
test_done

t/t5004/empty.zip

62 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)