]> git.proxmox.com Git - libgit2.git/commitdiff
Introduce core.safecrlf handling
authorEdward Thomson <ethomson@microsoft.com>
Thu, 27 Mar 2014 23:34:20 +0000 (16:34 -0700)
committerEdward Thomson <ethomson@microsoft.com>
Tue, 8 Apr 2014 04:09:09 +0000 (21:09 -0700)
src/config_cache.c
src/crlf.c
src/repository.h
tests/filter/crlf.c

index ec75d15019da0dded3ad6bab2a77d07fdc65a006..4bcbf02bf22ce5998848a564ddb7a54eefffa961 100644 (file)
@@ -68,6 +68,7 @@ static struct map_data _cvar_maps[] = {
        {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
        {"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
        {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
+       {"core.safecrlf", NULL, 0, GIT_SAFE_CRLF_DEFAULT},
 };
 
 int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
index 2480cc918ff7cc1ddf37b62e0b16434517ea0258..645ada2c6cb8ae451e405ac8cdd8543db15062f4 100644 (file)
@@ -21,6 +21,7 @@ struct crlf_attrs {
        int crlf_action;
        int eol;
        int auto_crlf;
+       int safe_crlf;
 };
 
 struct crlf_filter {
@@ -123,6 +124,9 @@ static int crlf_apply_to_odb(
        const git_buf *from,
        const git_filter_source *src)
 {
+       git_buf safe = GIT_BUF_INIT;
+       int error = 0;
+
        /* Empty file? Nothing to do */
        if (!git_buf_len(from))
                return 0;
@@ -154,12 +158,31 @@ static int crlf_apply_to_odb(
                                return GIT_PASSTHROUGH;
                }
 
-               if (!stats.cr)
+               if (!stats.cr && !ca->safe_crlf)
                        return GIT_PASSTHROUGH;
        }
 
        /* Actually drop the carriage returns */
-       return git_buf_text_crlf_to_lf(to, from);
+       if ((error = git_buf_text_crlf_to_lf(to, from)) < 0)
+               return error;
+
+       /* If safecrlf is enabled, sanity-check the result. */
+       if (ca->safe_crlf) {
+               if ((error = git_buf_grow(&safe, max(from->size, to->size))) < 0 ||
+                       (error = git_buf_text_lf_to_crlf(&safe, to)) < 0)
+                       goto done;
+
+               if (git_buf_cmp(from, &safe) != 0) {
+                       giterr_set(GITERR_FILTER, "LF would be replaced by CRLF in '%s'",
+                               git_filter_source_path(src));
+                       error = -1;
+               }
+       }
+
+done:
+       git_buf_free(&safe);
+
+       return error;
 }
 
 static const char *line_ending(struct crlf_attrs *ca)
@@ -272,6 +295,13 @@ static int crlf_check(
                        return GIT_PASSTHROUGH;
        }
 
+       if (git_filter_source_mode(src) == GIT_FILTER_CLEAN) {
+               error = git_repository__cvar(
+                       &ca.safe_crlf, git_filter_source_repo(src), GIT_CVAR_SAFE_CRLF);
+               if (error < 0)
+                       return error;
+       }
+
        *payload = git__malloc(sizeof(ca));
        GITERR_CHECK_ALLOC(*payload);
        memcpy(*payload, &ca, sizeof(ca));
index 99923b63b35d4c9e5e7072d2a019141abe8ab551..e401a82b868a339581d0e7aca7f34f0ab0d21aef 100644 (file)
@@ -38,6 +38,7 @@ typedef enum {
        GIT_CVAR_TRUSTCTIME,    /* core.trustctime */
        GIT_CVAR_ABBREV,        /* core.abbrev */
        GIT_CVAR_PRECOMPOSE,    /* core.precomposeunicode */
+       GIT_CVAR_SAFE_CRLF,             /* core.safecrlf */
        GIT_CVAR_CACHE_MAX
 } git_cvar_cached;
 
@@ -89,7 +90,8 @@ typedef enum {
        GIT_ABBREV_DEFAULT = 7,
        /* core.precomposeunicode */
        GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
-
+       /* core.safecrlf */
+       GIT_SAFE_CRLF_DEFAULT = GIT_CVAR_FALSE,
 } git_cvar_value;
 
 /* internal repository init flags */
index c9fb9cd7f837fa08a95e19aac6db48472c65bc17..75320efee306daf75b25e6743477e780950d793b 100644 (file)
@@ -1,5 +1,6 @@
 #include "clar_libgit2.h"
 #include "git2/sys/filter.h"
+#include "buffer.h"
 
 static git_repository *g_repo = NULL;
 
@@ -69,3 +70,82 @@ void test_filter_crlf__to_odb(void)
        git_filter_list_free(fl);
        git_buf_free(&out);
 }
+
+void test_filter_crlf__with_safecrlf(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));
+
+       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 */
+       in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+       in.size = strlen(in.ptr);
+
+       cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
+       cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER);
+
+       /* Normalized \n fails with safecrlf */
+       in.ptr = "Normal\nLF\nonly\nline-endings.\n";
+       in.size = strlen(in.ptr);
+
+       cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
+       cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER);
+
+       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));
+
+       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 */
+       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));
+       cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
+
+       /* Normalized \n fails with safecrlf */
+       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));
+       cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr);
+
+       git_filter_list_free(fl);
+       git_buf_free(&out);
+}
+