Teach new attribute 'export-ignore' to git-archive

Paths marked with this attribute are not output to git-archive
output.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 471754e..6e67990 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -502,6 +502,12 @@
 Creating an archive
 ~~~~~~~~~~~~~~~~~~~
 
+`export-ignore`
+^^^^^^^^^^^^^^^
+
+Files and directories with the attribute `export-ignore` won't be added to
+archive files.
+
 `export-subst`
 ^^^^^^^^^^^^^^
 
diff --git a/archive-tar.c b/archive-tar.c
index d7598f9..99db58f 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -247,6 +247,8 @@
 	strbuf_grow(&path, PATH_MAX);
 	strbuf_add(&path, base, baselen);
 	strbuf_addstr(&path, filename);
+	if (is_archive_path_ignored(path.buf + base_len))
+		return 0;
 	if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
 		strbuf_addch(&path, '/');
 		buffer = NULL;
diff --git a/archive-zip.c b/archive-zip.c
index 18c0f87..5742762 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -176,6 +176,8 @@
 	crc = crc32(0, NULL, 0);
 
 	path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
+	if (is_archive_path_ignored(path + base_len))
+		return 0;
 	if (verbose)
 		fprintf(stderr, "%s\n", path);
 	if (pathlen > 0xffff) {
diff --git a/archive.c b/archive.c
index 7a32c19..6502b76 100644
--- a/archive.c
+++ b/archive.c
@@ -82,3 +82,16 @@
 	return buffer;
 }
 
+int is_archive_path_ignored(const char *path)
+{
+	static struct git_attr *attr_export_ignore;
+	struct git_attr_check check[1];
+
+	if (!attr_export_ignore)
+		attr_export_ignore = git_attr("export-ignore", 13);
+
+	check[0].attr = attr_export_ignore;
+	if (git_checkattr(path, ARRAY_SIZE(check), check))
+		return 0;
+	return ATTR_TRUE(check[0].value);
+}
diff --git a/archive.h b/archive.h
index 5791e65..ddf004a 100644
--- a/archive.h
+++ b/archive.h
@@ -44,5 +44,6 @@
 extern void *parse_extra_zip_args(int argc, const char **argv);
 
 extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit);
+extern int is_archive_path_ignored(const char *path);
 
 #endif	/* ARCHIVE_H */
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 9b0baac..3f1e25d 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -45,6 +45,11 @@
      (cd a && find .) | sort >a.lst'
 
 test_expect_success \
+    'add ignored file' \
+    'echo ignore me >a/ignored &&
+     echo ignored export-ignore >.gitattributes'
+
+test_expect_success \
     'add files to repository' \
     'find a -type f | xargs git update-index --add &&
      find a -type l | xargs git update-index --add &&
@@ -54,6 +59,10 @@
      git commit-tree $treeid </dev/null)'
 
 test_expect_success \
+    'remove ignored file' \
+    'rm a/ignored'
+
+test_expect_success \
     'git archive' \
     'git archive HEAD >b.tar'