diff options
-rw-r--r-- | include/git2/filter.h | 17 | ||||
-rw-r--r-- | include/git2/sys/filter.h | 37 | ||||
-rw-r--r-- | src/buffer.c | 3 | ||||
-rw-r--r-- | src/crlf.c | 14 | ||||
-rw-r--r-- | src/filter.c | 52 | ||||
-rw-r--r-- | tests-clar/filter/crlf.c | 83 |
6 files changed, 181 insertions, 25 deletions
diff --git a/include/git2/filter.h b/include/git2/filter.h index cb23ae4f4..8ef88d81b 100644 --- a/include/git2/filter.h +++ b/include/git2/filter.h @@ -63,23 +63,6 @@ typedef struct git_filter git_filter; typedef struct git_filter_list git_filter_list; /** - * Look up a filter by name - */ -GIT_EXTERN(git_filter *) git_filter_lookup(const char *name); - -#define GIT_FILTER_CRLF "crlf" - -/** - * Apply a single filter to a buffer of data - */ -GIT_EXTERN(int) git_filter_apply_to_buffer( - git_buffer *out, - git_filter *filter, - const git_buffer *input, - const char *as_path, - git_filter_mode_t mode); - -/** * Load the filter list for a given path. * * This will return 0 (success) but set the output git_filter_list to NULL diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index dbb086b0e..ca5738a53 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -19,6 +19,43 @@ GIT_BEGIN_DECL /** + * Look up a filter by name + * + * @param name The name of the filter + * @return Pointer to the filter object or NULL if not found + */ +GIT_EXTERN(git_filter *) git_filter_lookup(const char *name); + +#define GIT_FILTER_CRLF "crlf" + +/** + * Create a new empty filter list + * + * Normally you won't use this because `git_filter_list_load` will create + * the filter list for you, but you can use this in combination with the + * `git_filter_lookup` and `git_filter_list_push` functions to assemble + * your own chains of filters. + */ +GIT_EXTERN(int) git_filter_list_new( + git_filter_list **out, git_repository *repo, git_filter_mode_t mode); + +/** + * Add a filter to a filter list with the given payload. + * + * Normally you won't have to do this because the filter list is created + * by calling the "check" function on registered filters when the filter + * attributes are set, but this does allow more direct manipulation of + * filter lists when desired. + * + * Note that normally the "check" function can set up a payload for the + * filter. Using this function, you can either pass in a payload if you + * know the expected payload format, or you can pass NULL. Some filters + * may fail with a NULL payload. Good luck! + */ +GIT_EXTERN(int) git_filter_list_push( + git_filter_list *fl, git_filter *filter, void *payload); + +/** * A filter source represents a file/blob to be processed */ typedef struct git_filter_source git_filter_source; diff --git a/src/buffer.c b/src/buffer.c index aaebac776..07725b9cc 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -548,9 +548,10 @@ int git_buffer_resize(git_buffer *buffer, size_t want_size) int git_buffer_copy( git_buffer *buffer, const void *data, size_t datalen) { - if (git_buffer__resize(buffer, datalen, false) < 0) + if (git_buffer__resize(buffer, datalen + 1, false) < 0) return -1; memcpy(buffer->ptr, data, datalen); + buffer->ptr[datalen] = '\0'; buffer->size = datalen; return 0; } diff --git a/src/crlf.c b/src/crlf.c index e974208a6..99c154f70 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -86,6 +86,9 @@ static int has_cr_in_index(const git_filter_source *src) git_off_t blobsize; bool found_cr; + if (!path) + return false; + if (git_repository_index__weakptr(&index, repo) < 0) { giterr_clear(); return false; @@ -189,9 +192,7 @@ static const char *line_ending(struct crlf_attrs *ca) switch (ca->eol) { case GIT_EOL_UNSET: - return GIT_EOL_NATIVE == GIT_EOL_CRLF - ? "\r\n" - : "\n"; + return GIT_EOL_NATIVE == GIT_EOL_CRLF ? "\r\n" : "\n"; case GIT_EOL_CRLF: return "\r\n"; @@ -302,7 +303,12 @@ static int crlf_apply( const git_buffer *from, const git_filter_source *src) { - GIT_UNUSED(self); + /* initialize payload in case `check` was bypassed */ + if (!*payload) { + int error = crlf_check(self, payload, src, NULL); + if (error < 0 && error != GIT_ENOTFOUND) + return error; + } if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE) return crlf_apply_to_workdir(*payload, to, from); diff --git a/src/filter.c b/src/filter.c index 73c2ceacb..79ccac0cf 100644 --- a/src/filter.c +++ b/src/filter.c @@ -181,7 +181,13 @@ static int filter_def_name_key_check(const void *key, const void *fdef) { const char *name = fdef ? ((const git_filter_def *)fdef)->filter_name : NULL; - return name ? -1 : git__strcmp(key, name); + return name ? git__strcmp(key, name) : -1; +} + +static int filter_def_filter_key_check(const void *key, const void *fdef) +{ + const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL; + return (key == filter) ? 0 : -1; } static int filter_registry_find(size_t *pos, const char *name) @@ -331,7 +337,7 @@ git_filter_mode_t git_filter_source_mode(const git_filter_source *src) return src->mode; } -static int git_filter_list_new( +static int filter_list_new( git_filter_list **out, const git_filter_source *src) { git_filter_list *fl = NULL; @@ -391,6 +397,16 @@ static int filter_list_check_attributes( return error; } +int git_filter_list_new( + git_filter_list **out, git_repository *repo, git_filter_mode_t mode) +{ + git_filter_source src = { 0 }; + src.repo = repo; + src.path = NULL; + src.mode = mode; + return filter_list_new(out, &src); +} + int git_filter_list_load( git_filter_list **filters, git_repository *repo, @@ -441,7 +457,7 @@ int git_filter_list_load( else if (error < 0) break; else { - if (!fl && (error = git_filter_list_new(&fl, &src)) < 0) + if (!fl && (error = filter_list_new(&fl, &src)) < 0) return error; fe = git_array_alloc(fl->filters); @@ -478,6 +494,36 @@ void git_filter_list_free(git_filter_list *fl) git__free(fl); } +int git_filter_list_push( + git_filter_list *fl, git_filter *filter, void *payload) +{ + int error = 0; + size_t pos; + git_filter_def *fdef; + git_filter_entry *fe; + + assert(fl && filter); + + if (git_vector_search2( + &pos, &git__filter_registry->filters, + filter_def_filter_key_check, filter) < 0) { + giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); + return -1; + } + + fdef = git_vector_get(&git__filter_registry->filters, pos); + + if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) + return error; + + fe = git_array_alloc(fl->filters); + GITERR_CHECK_ALLOC(fe); + fe->filter = filter; + fe->payload = payload; + + return 0; +} + static int filter_list_out_buffer_from_raw( git_buffer *out, const void *ptr, size_t size) { diff --git a/tests-clar/filter/crlf.c b/tests-clar/filter/crlf.c new file mode 100644 index 000000000..098a85d4c --- /dev/null +++ b/tests-clar/filter/crlf.c @@ -0,0 +1,83 @@ +#include "clar_libgit2.h" +#include "git2/sys/filter.h" + +static git_repository *g_repo = NULL; + +void test_filter_crlf__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"); +} + +void test_filter_crlf__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_filter_crlf__to_worktree(void) +{ + git_filter_list *fl; + git_filter *crlf; + git_buffer in = GIT_BUFFER_INIT, out = GIT_BUFFER_INIT; + + { + git_config *cfg; + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "core.autocrlf", "true")); + git_config_free(cfg); + } + + cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE)); + + crlf = git_filter_lookup(GIT_FILTER_CRLF); + cl_assert(crlf != NULL); + + cl_git_pass(git_filter_list_push(fl, crlf, NULL)); + + in.ptr = "Some text\nRight here\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + +#ifdef GIT_WIN32 + cl_assert_equal_s("Some text\r\nRight here\r\n", out.ptr); +#else + cl_assert_equal_s("Some text\nRight here\n", out.ptr); +#endif + + git_filter_list_free(fl); + git_buffer_free(&out); +} + +void test_filter_crlf__to_odb(void) +{ + git_filter_list *fl; + git_filter *crlf; + git_buffer in = GIT_BUFFER_INIT, out = GIT_BUFFER_INIT; + + { + git_config *cfg; + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "core.autocrlf", "true")); + git_config_free(cfg); + } + + cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB)); + + crlf = git_filter_lookup(GIT_FILTER_CRLF); + cl_assert(crlf != NULL); + + cl_git_pass(git_filter_list_push(fl, crlf, NULL)); + + in.ptr = "Some text\r\nRight here\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + + cl_assert_equal_s("Some text\nRight here\n", out.ptr); + + git_filter_list_free(fl); + git_buffer_free(&out); +} |