Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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 <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information...
commit bd54cf17a4acfb9555b8a83b7e74fb0274c38bb4 1 parent f838ce5
Jeff King peff authored gitster committed
2  archive.c
@@ -234,7 +234,7 @@ static void parse_pathspec_arg(const char **pathspec,
234 234 ar_args->pathspec = pathspec = get_pathspec("", pathspec);
235 235 if (pathspec) {
236 236 while (*pathspec) {
237   - if (!path_exists(ar_args->tree, *pathspec))
  237 + if (**pathspec && !path_exists(ar_args->tree, *pathspec))
238 238 die("path not found: %s", *pathspec);
239 239 pathspec++;
240 240 }
102 t/t5004-archive-corner-cases.sh
... ... @@ -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
BIN  t/t5004/empty.zip
Binary file not shown

0 comments on commit bd54cf1

Please sign in to comment.
Something went wrong with that request. Please try again.