archive: don't add empty directories to archives
authorRené Scharfe <l.s.r@web.de>
Tue, 12 Sep 2017 22:43:57 +0000 (00:43 +0200)
committerJunio C Hamano <gitster@pobox.com>
Thu, 14 Sep 2017 06:08:22 +0000 (15:08 +0900)
While git doesn't track empty directories, git archive can be tricked
into putting some into archives. One way is to construct an empty tree
object, as t5004 does. While that is supported by the object database,
it can't be represented in the index and thus it's unlikely to occur in
the wild.

Another way is using the literal name of a directory in an exclude
pathspec -- its contents are are excluded, but the directory stub is
included. That's inconsistent: exclude pathspecs containing wildcards
don't leave empty directories in the archive.

Yet another way is have a few levels of nested subdirectories (e.g.
d1/d2/d3/file1) and ignoring the entries at the leaves (e.g. file1).
The directories with the ignored content are ignored as well (e.g. d3),
but their empty parents are included (e.g. d2).

As empty directories are not supported by git, they should also not be
written into archives. If an empty directory is really needed then it
can be tracked and archived by placing an empty .gitignore file in it.

There already is a mechanism in place for suppressing empty directories.
When read_tree_recursive() encounters a directory excluded by a pathspec
then it enters it anyway because it might contain included entries. It
calls the callback function before it is able to decide if the directory
is actually needed. For that reason git archive adds directories to a
queue and writes entries for them only when it encounters the first
child item -- but currently only if pathspecs with wildcards are used.

Queue *all* directories, no matter if there even are pathspecs present.
This prevents git archive from writing entries for empty directories in
all cases.

Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: Rene Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
archive.c
t/t5001-archive-attr.sh
t/t5002-archive-attr-pattern.sh
t/t5004-archive-corner-cases.sh
index 2ad7e6cb9071e42faa59da18aeacc9bcc7549da5..f81bd3649a1f4bf20bc03100d12fc157401bdc14 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -121,11 +121,6 @@ static int check_attr_export_subst(const struct attr_check *check)
        return check && ATTR_TRUE(check->items[1].value);
 }
 
-static int should_queue_directories(const struct archiver_args *args)
-{
-       return args->pathspec.has_wildcard;
-}
-
 static int write_archive_entry(const unsigned char *sha1, const char *base,
                int baselen, const char *filename, unsigned mode, int stage,
                void *context)
@@ -147,7 +142,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                strbuf_addch(&path, '/');
        path_without_prefix = path.buf + args->baselen;
 
-       if (!S_ISDIR(mode) || !should_queue_directories(args)) {
+       if (!S_ISDIR(mode)) {
                const struct attr_check *check;
                check = get_archive_attrs(path_without_prefix);
                if (check_attr_export_ignore(check))
@@ -169,14 +164,6 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
-static int write_archive_entry_buf(const unsigned char *sha1, struct strbuf *base,
-               const char *filename, unsigned mode, int stage,
-               void *context)
-{
-       return write_archive_entry(sha1, base->buf, base->len,
-                                    filename, mode, stage, context);
-}
-
 static void queue_directory(const unsigned char *sha1,
                struct strbuf *base, const char *filename,
                unsigned mode, int stage, struct archiver_context *c)
@@ -290,9 +277,7 @@ int write_archive_entries(struct archiver_args *args,
        }
 
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
-                                 should_queue_directories(args) ?
-                                 queue_or_write_archive_entry :
-                                 write_archive_entry_buf,
+                                 queue_or_write_archive_entry,
                                  &context);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
index 897f6f06d55a9790a237b75c12eaab95763d1864..e9aa97117ab41beab708f4ded34628b015021e4e 100755 (executable)
@@ -73,7 +73,7 @@ test_expect_missing   archive-pathspec/ignored-by-tree
 test_expect_missing    archive-pathspec/ignored-by-tree.d
 test_expect_missing    archive-pathspec/ignored-by-tree.d/file
 test_expect_exists     archive-pathspec/ignored-by-worktree
-test_expect_missing    archive-pathspec/excluded-by-pathspec.d failure
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d
 test_expect_missing    archive-pathspec/excluded-by-pathspec.d/file
 
 test_expect_success 'git archive with wildcard pathspec' '
index 6667d159ab0950f10a3271f1cb81a63953214f17..bda6d7d7e9e835213f9b1323407222850db47ad6 100755 (executable)
@@ -76,7 +76,7 @@ test_expect_missing   archive/deep/and/slashless/ &&
 test_expect_missing    archive/deep/and/slashless/foo &&
 test_expect_missing    archive/deep/with/wildcard/ &&
 test_expect_missing    archive/deep/with/wildcard/foo &&
-test_expect_exists     archive/one-level-lower/
+test_expect_missing    archive/one-level-lower/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
 
index f6207f42b5a4ed382b819cb81631cd0acab61463..ced44355cab99fc4f5fd9768daf10f5a2c1b21c7 100755 (executable)
@@ -108,14 +108,14 @@ test_expect_success 'archive empty subtree with no pathspec' '
        git archive --format=tar $root_tree >subtree-all.tar &&
        make_dir extract &&
        "$TAR" xf subtree-all.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 test_expect_success 'archive empty subtree by direct pathspec' '
        git archive --format=tar $root_tree -- sub >subtree-path.tar &&
        make_dir extract &&
        "$TAR" xf subtree-path.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 ZIPINFO=zipinfo