Commit 40cb40fa by Russell Belfer

Add functions to manipulate filter lists

Extend the git2/sys/filter API with functions to look up a filter
and add it manually to a filter list.  This requires some trickery
because the regular attribute lookups and checks are bypassed when
this happens, but in the right hands, it will allow a user to have
granular control over applying filters.
parent 0646634e
......@@ -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
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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)
{
......
#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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment