git/t/t5001-archive-attr.sh
René Scharfe 4318094047 archive: don't add empty directories to archives
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>
2017-09-14 15:08:22 +09:00

132 lines
4.3 KiB
Bash
Executable File

#!/bin/sh
test_description='git archive attribute tests'
. ./test-lib.sh
SUBSTFORMAT='%H (%h)%n'
test_expect_exists() {
test_expect_${2:-success} " $1 exists" "test -e $1"
}
test_expect_missing() {
test_expect_${2:-success} " $1 does not exist" "test ! -e $1"
}
extract_tar_to_dir () {
(mkdir "$1" && cd "$1" && "$TAR" xf -) <"$1.tar"
}
test_expect_success 'setup' '
echo ignored >ignored &&
echo ignored export-ignore >>.git/info/attributes &&
git add ignored &&
echo ignored by tree >ignored-by-tree &&
echo ignored-by-tree export-ignore >.gitattributes &&
mkdir ignored-by-tree.d &&
>ignored-by-tree.d/file &&
echo ignored-by-tree.d export-ignore >>.gitattributes &&
git add ignored-by-tree ignored-by-tree.d .gitattributes &&
echo ignored by worktree >ignored-by-worktree &&
echo ignored-by-worktree export-ignore >.gitattributes &&
git add ignored-by-worktree &&
mkdir excluded-by-pathspec.d &&
>excluded-by-pathspec.d/file &&
git add excluded-by-pathspec.d &&
printf "A\$Format:%s\$O" "$SUBSTFORMAT" >nosubstfile &&
printf "A\$Format:%s\$O" "$SUBSTFORMAT" >substfile1 &&
printf "A not substituted O" >substfile2 &&
echo "substfile?" export-subst >>.git/info/attributes &&
git add nosubstfile substfile1 substfile2 &&
git commit -m. &&
git clone --bare . bare &&
cp .git/info/attributes bare/info/attributes
'
test_expect_success 'git archive' '
git archive HEAD >archive.tar &&
(mkdir archive && cd archive && "$TAR" xf -) <archive.tar
'
test_expect_missing archive/ignored
test_expect_missing archive/ignored-by-tree
test_expect_missing archive/ignored-by-tree.d
test_expect_missing archive/ignored-by-tree.d/file
test_expect_exists archive/ignored-by-worktree
test_expect_exists archive/excluded-by-pathspec.d
test_expect_exists archive/excluded-by-pathspec.d/file
test_expect_success 'git archive with pathspec' '
git archive HEAD ":!excluded-by-pathspec.d" >archive-pathspec.tar &&
extract_tar_to_dir archive-pathspec
'
test_expect_missing archive-pathspec/ignored
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
test_expect_missing archive-pathspec/excluded-by-pathspec.d/file
test_expect_success 'git archive with wildcard pathspec' '
git archive HEAD ":!excluded-by-p*" >archive-pathspec-wildcard.tar &&
extract_tar_to_dir archive-pathspec-wildcard
'
test_expect_missing archive-pathspec-wildcard/ignored
test_expect_missing archive-pathspec-wildcard/ignored-by-tree
test_expect_missing archive-pathspec-wildcard/ignored-by-tree.d
test_expect_missing archive-pathspec-wildcard/ignored-by-tree.d/file
test_expect_exists archive-pathspec-wildcard/ignored-by-worktree
test_expect_missing archive-pathspec-wildcard/excluded-by-pathspec.d
test_expect_missing archive-pathspec-wildcard/excluded-by-pathspec.d/file
test_expect_success 'git archive with worktree attributes' '
git archive --worktree-attributes HEAD >worktree.tar &&
(mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
'
test_expect_missing worktree/ignored
test_expect_exists worktree/ignored-by-tree
test_expect_missing worktree/ignored-by-worktree
test_expect_success 'git archive --worktree-attributes option' '
git archive --worktree-attributes --worktree-attributes HEAD >worktree.tar &&
(mkdir worktree2 && cd worktree2 && "$TAR" xf -) <worktree.tar
'
test_expect_missing worktree2/ignored
test_expect_exists worktree2/ignored-by-tree
test_expect_missing worktree2/ignored-by-worktree
test_expect_success 'git archive vs. bare' '
(cd bare && git archive HEAD) >bare-archive.tar &&
test_cmp_bin archive.tar bare-archive.tar
'
test_expect_success 'git archive with worktree attributes, bare' '
(cd bare && git archive --worktree-attributes HEAD) >bare-worktree.tar &&
(mkdir bare-worktree && cd bare-worktree && "$TAR" xf -) <bare-worktree.tar
'
test_expect_missing bare-worktree/ignored
test_expect_exists bare-worktree/ignored-by-tree
test_expect_exists bare-worktree/ignored-by-worktree
test_expect_success 'export-subst' '
git log "--pretty=format:A${SUBSTFORMAT}O" HEAD >substfile1.expected &&
test_cmp nosubstfile archive/nosubstfile &&
test_cmp substfile1.expected archive/substfile1 &&
test_cmp substfile2 archive/substfile2
'
test_done