Commit 2eecc2886be68496bb9555e4c93e59e985a7d09f

Edward Thomson 2015-06-10T14:43:49

Introduce `git_filter_list_contains` `git_filter_list_contains` can be used to query a filter list to determine if a given filter will be run.

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d4b733..1fff2d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -133,6 +133,9 @@ support for HTTPS connections insead of OpenSSL.
   path. For this, `GIT_CREDTYPE_SSH_MEMORY` and
   `git_cred_ssh_key_memory_new()` have been added.
 
+* `git_filter_list_contains` will indicate whether a particular
+  filter will be run in the given filter list.
+
 ### API removals
 
 * `git_remote_save()` and `git_remote_clear_refspecs()` have been
diff --git a/include/git2/filter.h b/include/git2/filter.h
index dc59e63..1828903 100644
--- a/include/git2/filter.h
+++ b/include/git2/filter.h
@@ -96,6 +96,22 @@ GIT_EXTERN(int) git_filter_list_load(
 	uint32_t flags);
 
 /**
+ * Query the filter list to see if a given filter (by name) will run.
+ * The built-in filters "crlf" and "ident" can be queried, otherwise this
+ * is the name of the filter specified by the filter attribute.
+ *
+ * This will return 0 if the given filter is not in the list, or 1 if
+ * the filter will be applied.
+ *
+ * @param filters A loaded git_filter_list (or NULL)
+ * @param name The name of the filter to query
+ * @return 1 if the filter is in the list, 0 otherwise
+ */
+GIT_EXTERN(int) git_filter_list_contains(
+	git_filter_list *filters,
+	const char *name);
+
+/**
  * Apply filter list to a data buffer.
  *
  * See `git2/buffer.h` for background on `git_buf` objects.
diff --git a/src/filter.c b/src/filter.c
index 3c6a0a9..e25d37c 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -27,6 +27,7 @@ struct git_filter_source {
 };
 
 typedef struct {
+	const char *filter_name;
 	git_filter *filter;
 	void *payload;
 } git_filter_entry;
@@ -526,7 +527,9 @@ int git_filter_list__load_ext(
 
 			fe = git_array_alloc(fl->filters);
 			GITERR_CHECK_ALLOC(fe);
-			fe->filter  = fdef->filter;
+
+			fe->filter = fdef->filter;
+			fe->filter_name = fdef->filter_name;
 			fe->payload = payload;
 		}
 	}
@@ -574,6 +577,25 @@ void git_filter_list_free(git_filter_list *fl)
 	git__free(fl);
 }
 
+int git_filter_list_contains(
+	git_filter_list *fl,
+	const char *name)
+{
+	size_t i;
+
+	assert(name);
+
+	if (!fl)
+		return 0;
+
+	for (i = 0; i < fl->filters.size; i++) {
+		if (strcmp(fl->filters.ptr[i].filter_name, name) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
 int git_filter_list_push(
 	git_filter_list *fl, git_filter *filter, void *payload)
 {
diff --git a/tests/filter/query.c b/tests/filter/query.c
new file mode 100644
index 0000000..6889d71
--- /dev/null
+++ b/tests/filter/query.c
@@ -0,0 +1,91 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+#include "crlf.h"
+#include "buffer.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_query__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("crlf");
+
+	cl_git_mkfile("crlf/.gitattributes",
+		"*.txt text\n"
+		"*.bin binary\n"
+		"*.crlf text eol=crlf\n"
+		"*.lf text eol=lf\n"
+		"*.binident binary ident\n"
+		"*.ident text ident\n"
+		"*.identcrlf ident text eol=crlf\n"
+		"*.identlf ident text eol=lf\n"
+		"*.custom custom ident text\n");
+}
+
+void test_filter_query__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+static int filter_for(const char *filename, const char *filter)
+{
+	git_filter_list *fl;
+	int filtered;
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, filename, GIT_FILTER_TO_WORKTREE, 0));
+	filtered = git_filter_list_contains(fl, filter);
+	git_filter_list_free(fl);
+
+	return filtered;
+}
+
+void test_filter_query__filters(void)
+{
+	cl_assert_equal_i(1, filter_for("text.txt", "crlf"));
+	cl_assert_equal_i(0, filter_for("binary.bin", "crlf"));
+
+	cl_assert_equal_i(1, filter_for("foo.lf", "crlf"));
+	cl_assert_equal_i(0, filter_for("foo.lf", "ident"));
+
+	cl_assert_equal_i(1, filter_for("id.ident", "crlf"));
+	cl_assert_equal_i(1, filter_for("id.ident", "ident"));
+
+	cl_assert_equal_i(0, filter_for("id.binident", "crlf"));
+	cl_assert_equal_i(1, filter_for("id.binident", "ident"));
+}
+
+void test_filter_query__autocrlf_true_implies_crlf(void)
+{
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+	cl_assert_equal_i(1, filter_for("not_in_gitattributes", "crlf"));
+	cl_assert_equal_i(1, filter_for("foo.txt", "crlf"));
+	cl_assert_equal_i(0, filter_for("foo.bin", "crlf"));
+	cl_assert_equal_i(1, filter_for("foo.lf", "crlf"));
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", false);
+	cl_assert_equal_i(0, filter_for("not_in_gitattributes", "crlf"));
+	cl_assert_equal_i(1, filter_for("foo.txt", "crlf"));
+	cl_assert_equal_i(0, filter_for("foo.bin", "crlf"));
+	cl_assert_equal_i(1, filter_for("foo.lf", "crlf"));
+}
+
+void test_filter_query__unknown(void)
+{
+	cl_assert_equal_i(1, filter_for("foo.custom", "crlf"));
+	cl_assert_equal_i(1, filter_for("foo.custom", "ident"));
+	cl_assert_equal_i(0, filter_for("foo.custom", "custom"));
+}
+
+void test_filter_query__custom(void)
+{
+	git_filter custom = { GIT_FILTER_VERSION };
+
+	cl_git_pass(git_filter_register(
+		"custom", &custom, 42));
+
+	cl_assert_equal_i(1, filter_for("foo.custom", "crlf"));
+	cl_assert_equal_i(1, filter_for("foo.custom", "ident"));
+	cl_assert_equal_i(1, filter_for("foo.custom", "custom"));
+
+	git_filter_unregister("custom");
+}