Commit e18d5e52 by Vicent Marti

Merge pull request #2331 from libgit2/rb/dont-stop-diff-on-safecrlf

Add filter options and ALLOW_UNSAFE
parents 4df53a64 bb45e390
......@@ -35,6 +35,11 @@ typedef enum {
GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB,
} git_filter_mode_t;
typedef enum {
GIT_FILTER_OPT_DEFAULT = 0u,
GIT_FILTER_OPT_ALLOW_UNSAFE = (1u << 0),
} git_filter_opt_t;
/**
* A filter that can transform file data
*
......@@ -75,6 +80,7 @@ typedef struct git_filter_list git_filter_list;
* @param blob The blob to which the filter will be applied (if known)
* @param path Relative path of the file to be filtered
* @param mode Filtering direction (WT->ODB or ODB->WT)
* @param options Combination of `git_filter_opt_t` flags
* @return 0 on success (which could still return NULL if no filters are
* needed for the requested file), <0 on error
*/
......@@ -83,7 +89,8 @@ GIT_EXTERN(int) git_filter_list_load(
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode);
git_filter_mode_t mode,
uint32_t options);
/**
* Apply filter list to a data buffer.
......
......@@ -546,6 +546,10 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(
* hash a file in the repository and you want to apply filtering rules (e.g.
* crlf filters) before generating the SHA, then use this function.
*
* Note: if the repository has `core.safecrlf` set to fail and the
* filtering triggers that failure, then this function will return an
* error and not calculate the hash of the file.
*
* @param out Output value of calculated SHA
* @param repo Repository pointer
* @param path Path to file on disk whose contents should be hashed. If the
......@@ -555,6 +559,7 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(
* NULL, then the `path` parameter will be used instead. If
* this is passed as the empty string, then no filters will be
* applied when calculating the hash.
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_hashfile(
git_oid *out,
......
......@@ -55,7 +55,10 @@ GIT_EXTERN(git_filter *) git_filter_lookup(const char *name);
* your own chains of filters.
*/
GIT_EXTERN(int) git_filter_list_new(
git_filter_list **out, git_repository *repo, git_filter_mode_t mode);
git_filter_list **out,
git_repository *repo,
git_filter_mode_t mode,
uint32_t options);
/**
* Add a filter to a filter list with the given payload.
......@@ -115,10 +118,15 @@ GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src);
GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src);
/**
* Get the git_filter_mode_t to be applied
* Get the git_filter_mode_t to be used
*/
GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src);
/**
* Get the combination git_filter_opt_t options to be applied
*/
GIT_EXTERN(uint32_t) git_filter_source_options(const git_filter_source *src);
/*
* struct git_filter
*
......
......@@ -198,7 +198,8 @@ int git_blob__create_from_paths(
if (try_load_filters)
/* Load the filters for writing this file to the ODB */
error = git_filter_list_load(
&fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB);
&fl, repo, NULL, hint_path,
GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT);
if (error < 0)
/* well, that didn't work */;
......@@ -356,7 +357,8 @@ int git_blob_filtered_content(
return 0;
if (!(error = git_filter_list_load(
&fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) {
&fl, git_blob_owner(blob), blob, path,
GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT))) {
error = git_filter_list_apply_to_blob(out, fl, blob);
......
......@@ -1212,7 +1212,8 @@ static int blob_content_to_file(
if (!opts->disable_filters)
error = git_filter_list_load(
&fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE);
&fl, git_blob_owner(blob), blob, hint_path,
GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT);
if (!error)
error = git_filter_list_apply_to_blob(&out, fl, blob);
......
......@@ -139,10 +139,19 @@ static int crlf_apply_to_odb(
return GIT_PASSTHROUGH;
/* If safecrlf is enabled, sanity-check the result. */
if (ca->safe_crlf && (stats.cr != stats.crlf || stats.lf != stats.crlf)) {
giterr_set(GITERR_FILTER, "LF would be replaced by CRLF in '%s'",
if (stats.cr != stats.crlf || stats.lf != stats.crlf) {
switch (ca->safe_crlf) {
case GIT_SAFE_CRLF_FAIL:
giterr_set(
GITERR_FILTER, "LF would be replaced by CRLF in '%s'",
git_filter_source_path(src));
return -1;
case GIT_SAFE_CRLF_WARN:
/* TODO: issue warning when warning API is available */;
break;
default:
break;
}
}
/*
......@@ -267,6 +276,7 @@ static int crlf_check(
if (ca.crlf_action == GIT_CRLF_GUESS ||
(ca.crlf_action == GIT_CRLF_AUTO &&
git_filter_source_mode(src) == GIT_FILTER_SMUDGE)) {
error = git_repository__cvar(
&ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF);
if (error < 0)
......@@ -285,6 +295,11 @@ static int crlf_check(
&ca.safe_crlf, git_filter_source_repo(src), GIT_CVAR_SAFE_CRLF);
if (error < 0)
return error;
/* downgrade FAIL to WARN if ALLOW_UNSAFE option is used */
if ((git_filter_source_options(src) & GIT_FILTER_OPT_ALLOW_UNSAFE) &&
ca.safe_crlf == GIT_SAFE_CRLF_FAIL)
ca.safe_crlf = GIT_SAFE_CRLF_WARN;
}
*payload = git__malloc(sizeof(ca));
......
......@@ -589,7 +589,8 @@ int git_diff__oid_for_entry(
entry.path);
error = -1;
} else if (!(error = git_filter_list_load(
&fl, diff->repo, NULL, entry.path, GIT_FILTER_TO_ODB)))
&fl, diff->repo, NULL, entry.path,
GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE)))
{
int fd = git_futils_open_ro(full_path.ptr);
if (fd < 0)
......
......@@ -300,7 +300,8 @@ static int diff_file_content_load_workdir_file(
goto cleanup;
if ((error = git_filter_list_load(
&fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
&fl, fc->repo, NULL, fc->file->path,
GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE)) < 0)
goto cleanup;
/* if there are no filters, try to mmap the file */
......
......@@ -23,6 +23,7 @@ struct git_filter_source {
git_oid oid; /* zero if unknown (which is likely) */
uint16_t filemode; /* zero if unknown */
git_filter_mode_t mode;
uint32_t options;
};
typedef struct {
......@@ -358,6 +359,11 @@ git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
return src->mode;
}
uint32_t git_filter_source_options(const git_filter_source *src)
{
return src->options;
}
static int filter_list_new(
git_filter_list **out, const git_filter_source *src)
{
......@@ -372,6 +378,7 @@ static int filter_list_new(
fl->source.repo = src->repo;
fl->source.path = fl->path;
fl->source.mode = src->mode;
fl->source.options = src->options;
*out = fl;
return 0;
......@@ -419,12 +426,16 @@ static int filter_list_check_attributes(
}
int git_filter_list_new(
git_filter_list **out, git_repository *repo, git_filter_mode_t mode)
git_filter_list **out,
git_repository *repo,
git_filter_mode_t mode,
uint32_t options)
{
git_filter_source src = { 0 };
src.repo = repo;
src.path = NULL;
src.mode = mode;
src.options = options;
return filter_list_new(out, &src);
}
......@@ -433,7 +444,8 @@ int git_filter_list_load(
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode)
git_filter_mode_t mode,
uint32_t options)
{
int error = 0;
git_filter_list *fl = NULL;
......@@ -448,6 +460,7 @@ int git_filter_list_load(
src.repo = repo;
src.path = path;
src.mode = mode;
src.options = options;
if (blob)
git_oid_cpy(&src.oid, git_blob_id(blob));
......
......@@ -1789,7 +1789,8 @@ int git_repository_hashfile(
/* passing empty string for "as_path" indicated --no-filters */
if (strlen(as_path) > 0) {
error = git_filter_list_load(
&fl, repo, NULL, as_path, GIT_FILTER_TO_ODB);
&fl, repo, NULL, as_path,
GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT);
if (error < 0)
return error;
} else {
......
......@@ -25,7 +25,8 @@ void test_filter_crlf__to_worktree(void)
git_filter *crlf;
git_buf in = { 0 }, out = { 0 };
cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_WORKTREE, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
......@@ -53,7 +54,8 @@ void test_filter_crlf__to_odb(void)
git_filter *crlf;
git_buf in = { 0 }, out = { 0 };
cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
......@@ -79,7 +81,8 @@ void test_filter_crlf__with_safecrlf(void)
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
......@@ -111,13 +114,57 @@ void test_filter_crlf__with_safecrlf(void)
git_buf_free(&out);
}
void test_filter_crlf__with_safecrlf_and_unsafe_allowed(void)
{
git_filter_list *fl;
git_filter *crlf;
git_buf in = {0}, out = GIT_BUF_INIT;
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
/* Normalized \r\n succeeds with safecrlf */
in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
in.size = strlen(in.ptr);
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
/* Mix of line endings fails with safecrlf, but allowed to pass */
in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
in.size = strlen(in.ptr);
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
/* TODO: check for warning */
cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
/* Normalized \n fails with safecrlf, but allowed to pass */
in.ptr = "Normal\nLF\nonly\nline-endings.\n";
in.size = strlen(in.ptr);
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
/* TODO: check for warning */
cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr);
git_filter_list_free(fl);
git_buf_free(&out);
}
void test_filter_crlf__no_safecrlf(void)
{
git_filter_list *fl;
git_filter *crlf;
git_buf in = {0}, out = GIT_BUF_INIT;
cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
......
......@@ -194,7 +194,7 @@ void test_filter_custom__to_odb(void)
git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data));
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB));
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB, 0));
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
......@@ -215,7 +215,7 @@ void test_filter_custom__to_workdir(void)
bitflipped_and_reversed_data, BITFLIPPED_AND_REVERSED_DATA_LEN);
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0));
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
......@@ -233,13 +233,13 @@ void test_filter_custom__can_register_a_custom_filter_in_the_repository(void)
git_filter_list *fl;
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip, reverse, crlf */
cl_assert_equal_sz(3, git_filter_list_length(fl));
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE));
&fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip, reverse - possibly crlf depending on global config */
{
size_t flen = git_filter_list_length(fl);
......@@ -248,19 +248,20 @@ void test_filter_custom__can_register_a_custom_filter_in_the_repository(void)
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE));
&fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip, reverse */
cl_assert_equal_sz(2, git_filter_list_length(fl));
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE));
&fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip (because of -reverse) */
cl_assert_equal_sz(1, git_filter_list_length(fl));
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "doesntapplytome.bin", GIT_FILTER_TO_WORKTREE));
&fl, g_repo, NULL, "doesntapplytome.bin",
GIT_FILTER_TO_WORKTREE, 0));
/* expect: none */
cl_assert_equal_sz(0, git_filter_list_length(fl));
git_filter_list_free(fl);
......
......@@ -39,7 +39,8 @@ void test_filter_ident__to_worktree(void)
git_filter_list *fl;
git_filter *ident;
cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_WORKTREE, 0));
ident = git_filter_lookup(GIT_FILTER_IDENT);
cl_assert(ident != NULL);
......@@ -78,7 +79,8 @@ void test_filter_ident__to_odb(void)
git_filter_list *fl;
git_filter *ident;
cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, 0));
ident = git_filter_lookup(GIT_FILTER_IDENT);
cl_assert(ident != NULL);
......
......@@ -121,7 +121,7 @@ void test_object_blob_filter__to_odb(void)
cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB));
&fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB, 0));
cl_assert(fl != NULL);
for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
......
......@@ -190,17 +190,22 @@ void test_threads_refdb__edit_while_iterate(void)
}
id[t] = t;
#ifdef GIT_THREADS
cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t]));
#else
/* It appears with all reflog writing changes, etc., that this
* test has started to fail quite frequently, so let's disable it
* for now by just running on a single thread...
*/
/* #ifdef GIT_THREADS */
/* cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t])); */
/* #else */
fn(&id[t]);
#endif
/* #endif */
}
#ifdef GIT_THREADS
for (t = 0; t < THREADS; ++t) {
cl_git_pass(git_thread_join(th[t], NULL));
}
/* for (t = 0; t < THREADS; ++t) { */
/* cl_git_pass(git_thread_join(th[t], NULL)); */
/* } */
memset(th, 0, sizeof(th));
......
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