[PATCH 2/7] archive: add user-configurable tar-filter infrastructure

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Archive supports two output formats: tar and zip. The tar
files are totally uncompressed, and it is up to the user to
pipe them through gzip or similar. This is no more than
a minor inconvenience when running archive locally. However,
for remote calls to upload-archive, having the server do the
compression can save a lot of bandwidth.

This patch lays the foundation for tar filters; it parses
user configuration into an internal representation, but
doesn't yet do anything with the result.

It also introduces some tests to document the intended
usage.

Signed-off-by: Jeff King <peff@xxxxxxxx>
---
 Makefile             |    1 +
 archive-tar-filter.c |  112 ++++++++++++++++++++++++++++++++++++++++++++++++++
 archive.c            |    1 +
 archive.h            |   15 +++++++
 t/t5000-tar-tree.sh  |   54 ++++++++++++++++++++++++
 5 files changed, 183 insertions(+), 0 deletions(-)
 create mode 100644 archive-tar-filter.c

diff --git a/Makefile b/Makefile
index e40ac0c..bd3002f 100644
--- a/Makefile
+++ b/Makefile
@@ -573,6 +573,7 @@ LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
 LIB_OBJS += archive.o
+LIB_OBJS += archive-tar-filter.o
 LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
 LIB_OBJS += attr.o
diff --git a/archive-tar-filter.c b/archive-tar-filter.c
new file mode 100644
index 0000000..211f1df
--- /dev/null
+++ b/archive-tar-filter.c
@@ -0,0 +1,112 @@
+#include "cache.h"
+#include "archive.h"
+
+struct tar_filter *tar_filters;
+static struct tar_filter **tar_filters_tail = &tar_filters;
+
+static struct tar_filter *tar_filter_new(const char *name, int namelen)
+{
+	struct tar_filter *tf;
+	tf = xcalloc(1, sizeof(*tf));
+	tf->name = xmemdupz(name, namelen);
+	tf->extensions.strdup_strings = 1;
+	*tar_filters_tail = tf;
+	tar_filters_tail = &tf->next;
+	return tf;
+}
+
+static void tar_filter_free(struct tar_filter *tf)
+{
+	string_list_clear(&tf->extensions, 0);
+	free(tf->name);
+	free(tf->command);
+	free(tf);
+}
+
+static struct tar_filter *tar_filter_by_namelen(const char *name,
+						int len)
+{
+	struct tar_filter *p;
+	for (p = tar_filters; p; p = p->next)
+		if (!strncmp(p->name, name, len) && !p->name[len])
+			return p;
+	return NULL;
+}
+
+struct tar_filter *tar_filter_by_name(const char *name)
+{
+	return tar_filter_by_namelen(name, strlen(name));
+}
+
+static int tar_filter_config(const char *var, const char *value, void *data)
+{
+	struct tar_filter *tf;
+	const char *dot;
+	const char *name;
+	const char *type;
+	int namelen;
+
+	if (prefixcmp(var, "tarfilter."))
+		return 0;
+	dot = strrchr(var, '.');
+	if (dot == var + 9)
+		return 0;
+
+	name = var + 10;
+	namelen = dot - name;
+	type = dot + 1;
+
+	tf = tar_filter_by_namelen(name, namelen);
+	if (!tf)
+		tf = tar_filter_new(name, namelen);
+
+	if (!strcmp(type, "command")) {
+		if (!value)
+			return config_error_nonbool(var);
+		tf->command = xstrdup(value);
+		return 0;
+	}
+	else if (!strcmp(type, "extension")) {
+		if (!value)
+			return config_error_nonbool(var);
+		string_list_append(&tf->extensions, value);
+		return 0;
+	}
+	else if (!strcmp(type, "compressionlevels")) {
+		tf->use_compression = git_config_bool(var, value);
+		return 0;
+	}
+
+	return 0;
+}
+
+static void remove_filters_without_command(void)
+{
+	struct tar_filter *p = tar_filters;
+	struct tar_filter **last = &tar_filters;
+
+	while (p) {
+		if (p->command && *p->command)
+			last = &p->next;
+		else {
+			*last = p->next;
+			tar_filter_free(p);
+		}
+		p = *last;
+	}
+}
+
+/*
+ * We don't want to load twice, since some of our
+ * values actually append rather than overwrite.
+ */
+static int tar_filter_config_loaded;
+extern void tar_filter_load_config(void)
+{
+	if (tar_filter_config_loaded)
+		return;
+	tar_filter_config_loaded = 1;
+
+	git_config(tar_filter_config, NULL);
+	remove_filters_without_command();
+}
diff --git a/archive.c b/archive.c
index 2616676..2ed9259 100644
--- a/archive.c
+++ b/archive.c
@@ -395,6 +395,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
 		prefix = setup_git_directory_gently(&nongit);
 
 	git_config(git_default_config, NULL);
+	tar_filter_load_config();
 
 	argc = parse_archive_args(argc, argv, &ar, &args);
 	if (nongit) {
diff --git a/archive.h b/archive.h
index 038ac35..8386c46 100644
--- a/archive.h
+++ b/archive.h
@@ -1,6 +1,8 @@
 #ifndef ARCHIVE_H
 #define ARCHIVE_H
 
+#include "string-list.h"
+
 struct archiver_args {
 	const char *base;
 	size_t baselen;
@@ -27,4 +29,17 @@ extern int write_zip_archive(struct archiver_args *);
 extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
 extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix);
 
+struct tar_filter {
+	char *name;
+	char *command;
+	struct string_list extensions;
+	unsigned use_compression:1;
+	struct tar_filter *next;
+};
+
+extern struct tar_filter *tar_filters;
+extern struct tar_filter *tar_filter_by_name(const char *name);
+
+extern void tar_filter_load_config(void);
+
 #endif	/* ARCHIVE_H */
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index cff1b3e..c3e1a4e 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -252,4 +252,58 @@ test_expect_success 'git-archive --prefix=olde-' '
 	test -f h/olde-a/bin/sh
 '
 
+test_expect_success 'setup fake tar filter' '
+	git config tarfilter.fake.command "cat >/dev/null; echo args: "
+'
+
+test_expect_failure 'filter does not allow compression levels by default' '
+	test_must_fail git archive --format=fake -9 HEAD >output
+'
+
+test_expect_failure 'filters can allow compression levels' '
+	git config tarfilter.fake.compressionlevels true &&
+	echo "args: -9" >expect &&
+	git archive --format=fake -9 HEAD >output &&
+	test_cmp expect output
+'
+
+test_expect_failure 'archive --list mentions user filter' '
+	git archive --list >output &&
+	grep "^fake\$" output
+'
+
+test_expect_failure 'archive --list shows remote user filters' '
+	git archive --list --remote=. >output &&
+	grep "^fake\$" output
+'
+
+test_expect_success 'setup slightly more useful tar filter' '
+	git config tarfilter.foo.command "tr ab ba" &&
+	git config --add tarfilter.foo.extension tar.foo &&
+	git config --add tarfilter.foo.extension bar
+'
+
+test_expect_failure 'archive outputs in configurable format' '
+	git archive --format=foo HEAD >config.tar.foo &&
+	tr ab ba <config.tar.foo >config.tar &&
+	test_cmp b.tar config.tar
+'
+
+test_expect_failure 'archive selects implicit format by configured extension' '
+	git archive -o config-implicit.tar.foo HEAD &&
+	test_cmp config.tar.foo config-implicit.tar.foo &&
+	git archive -o config-implicit.bar HEAD &&
+	test_cmp config.tar.foo config-implicit.bar
+'
+
+test_expect_success 'default output format remains tar' '
+	git archive -o config-implicit.baz HEAD &&
+	test_cmp b.tar config-implicit.baz
+'
+
+test_expect_failure 'extension matching requires dot' '
+	git archive -o config-implicittar.foo HEAD &&
+	test_cmp b.tar config-implicittar.foo
+'
+
 test_done
-- 
1.7.6.rc1.4.g49204

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]