Unverified Commit 9084712b by Edward Thomson Committed by GitHub

Merge pull request #4904 from libgit2/ethomson/crlf

Update CRLF filtering to match modern git
parents 77f1460f e385e647

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

......@@ -310,6 +310,7 @@ bool git_buf_text_gather_stats(
}
}
return (stats->nul > 0 ||
/* Treat files with a bare CR as binary */
return (stats->cr != stats->crlf || stats->nul > 0 ||
((stats->printable >> 7) < stats->nonprintable));
}
......@@ -15,16 +15,6 @@
/* Amount of file to examine for NUL byte when checking binary-ness */
#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
/* Possible CRLF values */
typedef enum {
GIT_CRLF_GUESS = -1,
GIT_CRLF_BINARY = 0,
GIT_CRLF_TEXT,
GIT_CRLF_INPUT,
GIT_CRLF_CRLF,
GIT_CRLF_AUTO,
} git_crlf_t;
typedef struct {
git_attr_session *attr_session;
git_buf *temp_buf;
......
......@@ -13,14 +13,40 @@ static git_repository *g_repo;
static const char *systype;
static git_buf expected_fixture = GIT_BUF_INIT;
static int unlink_file(void *payload, git_buf *path)
{
char *fn;
cl_assert(fn = git_path_basename(path->ptr));
GIT_UNUSED(payload);
if (strcmp(fn, ".git"))
cl_must_pass(p_unlink(path->ptr));
git__free(fn);
return 0;
}
void test_checkout_crlf__initialize(void)
{
git_buf reponame = GIT_BUF_INIT;
g_repo = cl_git_sandbox_init("crlf");
/*
* remove the contents of the working directory so that we can
* check out over it.
*/
cl_git_pass(git_buf_puts(&reponame, "crlf"));
cl_git_pass(git_path_direach(&reponame, 0, unlink_file, NULL));
if (GIT_EOL_NATIVE == GIT_EOL_CRLF)
systype = "windows";
else
systype = "posix";
git_buf_dispose(&reponame);
}
void test_checkout_crlf__cleanup(void)
......@@ -99,41 +125,45 @@ static void test_checkout(const char *autocrlf, const char *attrs)
{
git_buf attrbuf = GIT_BUF_INIT;
git_buf expected_dirname = GIT_BUF_INIT;
git_buf systype_and_direction = GIT_BUF_INIT;
git_buf sandboxname = GIT_BUF_INIT;
git_buf reponame = GIT_BUF_INIT;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
struct compare_data compare_data = { NULL, autocrlf, attrs };
const char *c;
git_buf_puts(&reponame, "crlf");
cl_git_pass(git_buf_puts(&reponame, "crlf"));
cl_git_pass(git_buf_puts(&systype_and_direction, systype));
cl_git_pass(git_buf_puts(&systype_and_direction, "_to_workdir"));
git_buf_puts(&sandboxname, "autocrlf_");
git_buf_puts(&sandboxname, autocrlf);
cl_git_pass(git_buf_puts(&sandboxname, "autocrlf_"));
cl_git_pass(git_buf_puts(&sandboxname, autocrlf));
if (*attrs) {
git_buf_puts(&sandboxname, ",");
cl_git_pass(git_buf_puts(&sandboxname, ","));
for (c = attrs; *c; c++) {
if (*c == ' ')
git_buf_putc(&sandboxname, ',');
cl_git_pass(git_buf_putc(&sandboxname, ','));
else if (*c == '=')
git_buf_putc(&sandboxname, '_');
cl_git_pass(git_buf_putc(&sandboxname, '_'));
else
git_buf_putc(&sandboxname, *c);
cl_git_pass(git_buf_putc(&sandboxname, *c));
}
git_buf_printf(&attrbuf, "* %s\n", attrs);
cl_git_pass(git_buf_printf(&attrbuf, "* %s\n", attrs));
cl_git_mkfile("crlf/.gitattributes", attrbuf.ptr);
}
cl_repo_set_string(g_repo, "core.autocrlf", autocrlf);
git_buf_joinpath(&expected_dirname, systype, sandboxname.ptr);
git_buf_joinpath(&expected_fixture, "crlf_data", expected_dirname.ptr);
cl_git_pass(git_buf_joinpath(&expected_dirname, systype_and_direction.ptr, sandboxname.ptr));
cl_git_pass(git_buf_joinpath(&expected_fixture, "crlf_data", expected_dirname.ptr));
cl_fixture_sandbox(expected_fixture.ptr);
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
compare_data.dirname = sandboxname.ptr;
cl_git_pass(git_path_direach(&reponame, 0, compare_file, &compare_data));
......@@ -145,25 +175,27 @@ static void test_checkout(const char *autocrlf, const char *attrs)
git_buf_dispose(&expected_fixture);
git_buf_dispose(&expected_dirname);
git_buf_dispose(&sandboxname);
git_buf_dispose(&systype_and_direction);
git_buf_dispose(&reponame);
}
static void empty_workdir(const char *name)
{
git_vector contents = GIT_VECTOR_INIT;
char *basename;
int cmp;
size_t i;
const char *fn;
git_path_dirload(&contents, name, 0, 0);
cl_git_pass(git_path_dirload(&contents, name, 0, 0));
git_vector_foreach(&contents, i, fn) {
char *basename = git_path_basename(fn);
int cmp = strncasecmp(basename, ".git", 4);
cl_assert(basename = git_path_basename(fn));
cmp = strncasecmp(basename, ".git", 4);
git__free(basename);
if (cmp == 0)
continue;
p_unlink(fn);
if (cmp)
cl_git_pass(p_unlink(fn));
}
git_vector_free_deep(&contents);
}
......@@ -173,7 +205,7 @@ void test_checkout_crlf__matches_core_git(void)
const char *autocrlf[] = { "true", "false", "input", NULL };
const char *attrs[] = { "", "-crlf", "-text", "eol=crlf", "eol=lf",
"text", "text eol=crlf", "text eol=lf",
"text=auto", "text=auto eol=crlf", "text=auto eol=lf",
"text=auto", "text=auto eol=crlf", "text=auto eol=lf",
NULL };
const char **a, **b;
......@@ -335,7 +367,7 @@ void test_checkout_crlf__with_ident(void)
p_unlink("crlf/more1.identlf");
p_unlink("crlf/more2.identcrlf");
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
cl_assert_equal_file(
ALL_LF_TEXT_AS_CRLF
......@@ -364,7 +396,7 @@ void test_checkout_crlf__autocrlf_false_no_attrs(void)
cl_repo_set_bool(g_repo, "core.autocrlf", false);
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
......@@ -377,7 +409,7 @@ void test_checkout_crlf__autocrlf_true_no_attrs(void)
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
......@@ -390,7 +422,7 @@ void test_checkout_crlf__autocrlf_input_no_attrs(void)
cl_repo_set_string(g_repo, "core.autocrlf", "input");
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
......@@ -405,7 +437,7 @@ void test_checkout_crlf__autocrlf_false_text_auto_attr(void)
cl_repo_set_bool(g_repo, "core.autocrlf", false);
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
if (GIT_EOL_NATIVE == GIT_EOL_CRLF) {
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
......@@ -425,7 +457,7 @@ void test_checkout_crlf__autocrlf_true_text_auto_attr(void)
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
......@@ -440,7 +472,7 @@ void test_checkout_crlf__autocrlf_input_text_auto_attr(void)
cl_repo_set_string(g_repo, "core.autocrlf", "input");
git_checkout_head(g_repo, &opts);
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
......@@ -453,8 +485,8 @@ void test_checkout_crlf__can_write_empty_file(void)
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_repository_set_head(g_repo, "refs/heads/empty-files");
git_checkout_head(g_repo, &opts);
cl_git_pass(git_repository_set_head(g_repo, "refs/heads/empty-files"));
cl_git_pass(git_checkout_head(g_repo, &opts));
check_file_contents("./crlf/test1.txt", "");
......
......@@ -99,12 +99,19 @@ void test_filter_crlf__with_safecrlf(void)
cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER);
/* Normalized \n is reversible, so does not fail with safecrlf */
/* Normalized \n fails for autocrlf=true when safecrlf=true */
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);
/* String with \r but without \r\n does not fail with safecrlf */
in.ptr = "Normal\nCR only\rand some more\nline-endings.\n";
in.size = strlen(in.ptr);
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_s(in.ptr, out.ptr);
cl_assert_equal_s("Normal\nCR only\rand some more\nline-endings.\n", out.ptr);
git_filter_list_free(fl);
git_buf_dispose(&out);
......
......@@ -14,9 +14,11 @@
static git_repository *g_repo;
static git_index *g_index;
static git_buf expected_fixture = GIT_BUF_INIT;
void test_index_crlf__initialize(void)
{
g_repo = cl_git_sandbox_init("crlf");
g_repo = cl_git_sandbox_init_new("crlf");
cl_git_pass(git_repository_index(&g_index, g_repo));
}
......@@ -24,6 +26,208 @@ void test_index_crlf__cleanup(void)
{
git_index_free(g_index);
cl_git_sandbox_cleanup();
if (expected_fixture.size) {
cl_fixture_cleanup(expected_fixture.ptr);
git_buf_free(&expected_fixture);
}
}
struct compare_data
{
const char *systype;
const char *dirname;
const char *safecrlf;
const char *autocrlf;
const char *attrs;
};
static int add_and_check_file(void *payload, git_buf *actual_path)
{
git_buf expected_path = GIT_BUF_INIT;
git_buf expected_path_fail = GIT_BUF_INIT;
git_buf expected_contents = GIT_BUF_INIT;
struct compare_data *cd = payload;
char *basename;
const git_index_entry *entry;
git_blob *blob;
bool failed = true;
basename = git_path_basename(actual_path->ptr);
if (!strcmp(basename, ".git") || !strcmp(basename, ".gitattributes")) {
failed = false;
goto done;
}
cl_git_pass(git_buf_joinpath(&expected_path, cd->dirname, basename));
cl_git_pass(git_buf_puts(&expected_path_fail, expected_path.ptr));
cl_git_pass(git_buf_puts(&expected_path_fail, ".fail"));
if (git_path_isfile(expected_path.ptr)) {
cl_git_pass(git_index_add_bypath(g_index, basename));
cl_assert(entry = git_index_get_bypath(g_index, basename, 0));
cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
cl_git_pass(git_futils_readbuffer(&expected_contents, expected_path.ptr));
if (strcmp(expected_contents.ptr, git_blob_rawcontent(blob)) != 0)
goto done;
git_blob_free(blob);
} else if (git_path_isfile(expected_path_fail.ptr)) {
cl_git_pass(git_futils_readbuffer(&expected_contents, expected_path_fail.ptr));
git_buf_rtrim(&expected_contents);
if (git_index_add_bypath(g_index, basename) == 0 ||
giterr_last()->klass != GITERR_FILTER ||
strcmp(expected_contents.ptr, giterr_last()->message) != 0)
goto done;
} else {
cl_fail("unexpected index failure");
}
failed = false;
done:
if (failed) {
git_buf details = GIT_BUF_INIT;
git_buf_printf(&details, "filename=%s, system=%s, autocrlf=%s, safecrlf=%s, attrs={%s}",
basename, cd->systype, cd->autocrlf, cd->safecrlf, cd->attrs);
clar__fail(__FILE__, __LINE__,
"index contents did not match expected", details.ptr, 0);
git_buf_dispose(&details);
}
git__free(basename);
git_buf_dispose(&expected_contents);
git_buf_dispose(&expected_path);
git_buf_dispose(&expected_path_fail);
return 0;
}
static const char *system_type(void)
{
if (GIT_EOL_NATIVE == GIT_EOL_CRLF)
return "windows";
else
return "posix";
}
static void test_add_index(const char *safecrlf, const char *autocrlf, const char *attrs)
{
git_buf attrbuf = GIT_BUF_INIT;
git_buf expected_dirname = GIT_BUF_INIT;
git_buf sandboxname = GIT_BUF_INIT;
git_buf reponame = GIT_BUF_INIT;
struct compare_data compare_data = { system_type(), NULL, safecrlf, autocrlf, attrs };
const char *c;
git_buf_puts(&reponame, "crlf");
git_buf_puts(&sandboxname, "autocrlf_");
git_buf_puts(&sandboxname, autocrlf);
git_buf_puts(&sandboxname, ",safecrlf_");
git_buf_puts(&sandboxname, safecrlf);
if (*attrs) {
git_buf_puts(&sandboxname, ",");
for (c = attrs; *c; c++) {
if (*c == ' ')
git_buf_putc(&sandboxname, ',');
else if (*c == '=')
git_buf_putc(&sandboxname, '_');
else
git_buf_putc(&sandboxname, *c);
}
git_buf_printf(&attrbuf, "* %s\n", attrs);
cl_git_mkfile("crlf/.gitattributes", attrbuf.ptr);
}
cl_repo_set_string(g_repo, "core.safecrlf", safecrlf);
cl_repo_set_string(g_repo, "core.autocrlf", autocrlf);
cl_git_pass(git_index_clear(g_index));
git_buf_joinpath(&expected_dirname, "crlf_data", system_type());
git_buf_puts(&expected_dirname, "_to_odb");
git_buf_joinpath(&expected_fixture, expected_dirname.ptr, sandboxname.ptr);
cl_fixture_sandbox(expected_fixture.ptr);
compare_data.dirname = sandboxname.ptr;
cl_git_pass(git_path_direach(&reponame, 0, add_and_check_file, &compare_data));
cl_fixture_cleanup(expected_fixture.ptr);
git_buf_free(&expected_fixture);
git_buf_free(&attrbuf);
git_buf_free(&expected_fixture);
git_buf_free(&expected_dirname);
git_buf_free(&sandboxname);
git_buf_free(&reponame);
}
static void set_up_workingdir(const char *name)
{
git_vector contents = GIT_VECTOR_INIT;
size_t i;
const char *fn;
git_path_dirload(&contents, name, 0, 0);
git_vector_foreach(&contents, i, fn) {
char *basename = git_path_basename(fn);
bool skip = strncasecmp(basename, ".git", 4) == 0 && strlen(basename) == 4;
git__free(basename);
if (skip)
continue;
p_unlink(fn);
}
git_vector_free_deep(&contents);
/* copy input files */
git_path_dirload(&contents, cl_fixture("crlf"), 0, 0);
git_vector_foreach(&contents, i, fn) {
char *basename = git_path_basename(fn);
git_buf dest_filename = GIT_BUF_INIT;
if (strcmp(basename, ".gitted") &&
strcmp(basename, ".gitattributes")) {
git_buf_joinpath(&dest_filename, name, basename);
cl_git_pass(git_futils_cp(fn, dest_filename.ptr, 0644));
}
git__free(basename);
git_buf_free(&dest_filename);
}
git_vector_free_deep(&contents);
}
void test_index_crlf__matches_core_git(void)
{
const char *safecrlf[] = { "true", "false", "warn", NULL };
const char *autocrlf[] = { "true", "false", "input", NULL };
const char *attrs[] = { "", "-crlf", "-text", "eol=crlf", "eol=lf",
"text", "text eol=crlf", "text eol=lf",
"text=auto", "text=auto eol=crlf", "text=auto eol=lf",
NULL };
const char **a, **b, **c;
for (a = safecrlf; *a; a++) {
for (b = autocrlf; *b; b++) {
for (c = attrs; *c; c++) {
set_up_workingdir("crlf");
test_add_index(*a, *b, *c);
}
}
}
}
void test_index_crlf__autocrlf_false_no_attrs(void)
......@@ -135,13 +339,55 @@ void test_index_crlf__autocrlf_input_text_auto_attr(void)
cl_assert_equal_oid(&oid, &entry->id);
}
void test_index_crlf__safecrlf_true_autocrlf_input_text_auto_attr(void)
{
const git_index_entry *entry;
git_oid oid;
cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
cl_repo_set_string(g_repo, "core.autocrlf", "input");
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_mkfile("./crlf/newfile.txt", FILE_CONTENTS_LF);
cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
entry = git_index_get_bypath(g_index, "newfile.txt", 0);
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
cl_assert_equal_oid(&oid, &entry->id);
cl_git_mkfile("./crlf/newfile2.txt", FILE_CONTENTS_CRLF);
cl_git_fail(git_index_add_bypath(g_index, "newfile2.txt"));
}
void test_index_crlf__safecrlf_true_autocrlf_input_text__no_attr(void)
{
const git_index_entry *entry;
git_oid oid;
cl_repo_set_string(g_repo, "core.autocrlf", "input");
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_mkfile("./crlf/newfile.txt", FILE_CONTENTS_LF);
cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
entry = git_index_get_bypath(g_index, "newfile.txt", 0);
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
cl_assert_equal_oid(&oid, &entry->id);
cl_git_mkfile("./crlf/newfile2.txt", FILE_CONTENTS_CRLF);
cl_git_fail(git_index_add_bypath(g_index, "newfile2.txt"));
}
void test_index_crlf__safecrlf_true_no_attrs(void)
{
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_mkfile("crlf/newfile.txt", ALL_LF_TEXT_RAW);
cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
cl_git_mkfile("crlf/newfile.txt", ALL_CRLF_TEXT_RAW);
cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
......
* binary
*.sh text diff merge eol=lf
This test data was generated using the `tests/resources/generate_crlf.sh`
script. Please see that script for usage information.
(posix and windows directories) and `tests/resources/generate_crlf_checkin.sh`
(checkin_results directory) scripts. Please see these scripts for usage information.
⚽The rest is ASCII01.
The rest is ASCII02.
The rest is ASCII03.
The rest is ASCII04.
The rest is ASCII05.
The rest is ASCII06.
The rest is ASCII07.
The rest is ASCII08.
The rest is ASCII09.
The rest is ASCII10.
The rest is ASCII11.
The rest is ASCII12.
The rest is ASCII13.
The rest is ASCII14.
The rest is ASCII15.
The rest is ASCII16.
The rest is ASCII17.
The rest is ASCII18.
The rest is ASCII19.
The rest is ASCII20.
The rest is ASCII21.
The rest is ASCII22.
⚽The rest is ASCII01.
The rest is ASCII02.
The rest is ASCII03.
The rest is ASCII04.
The rest is ASCII05.
The rest is ASCII06.
The rest is ASCII07.
The rest is ASCII08.
The rest is ASCII09.
The rest is ASCII10.
The rest is ASCII11.
The rest is ASCII12.
The rest is ASCII13.
The rest is ASCII14.
The rest is ASCII15.
The rest is ASCII16.
The rest is ASCII17.
The rest is ASCII18.
The rest is ASCII19.
The rest is ASCII20.
The rest is ASCII21.
The rest is ASCII22.
one
two three
four
\ No newline at end of file
⚽The rest is ASCII01.
The rest is ASCII02.
The rest is ASCII03.
The rest is ASCII04.
The rest is ASCII05.
The rest is ASCII06.
The rest is ASCII07.
The rest is ASCII08.
The rest is ASCII09.
The rest is ASCII10.
The rest is ASCII11.
The rest is ASCII12.
The rest is ASCII13.
The rest is ASCII14.
The rest is ASCII15.
The rest is ASCII16.
The rest is ASCII17.
The rest is ASCII18.
The rest is ASCII19.
The rest is ASCII20.
The rest is ASCII21.
The rest is ASCII22.
⚽The rest is ASCII01.
The rest is ASCII02.
The rest is ASCII03.
The rest is ASCII04.
The rest is ASCII05.
The rest is ASCII06.
The rest is ASCII07.
The rest is ASCII08.
The rest is ASCII09.
The rest is ASCII10.
The rest is ASCII11.
The rest is ASCII12.
The rest is ASCII13.
The rest is ASCII14.
The rest is ASCII15.
The rest is ASCII16.
The rest is ASCII17.
The rest is ASCII18.
The rest is ASCII19.
The rest is ASCII20.
The rest is ASCII21.
The rest is ASCII22.
⚽The rest is ASCII01.
The rest is ASCII02.
The rest is ASCII03.
The rest is ASCII04.
The rest is ASCII05.
The rest is ASCII06.
The rest is ASCII07.
The rest is ASCII08.
The rest is ASCII09.
The rest is ASCII10.
The rest is ASCII11.
The rest is ASCII12.
The rest is ASCII13.
The rest is ASCII14.
The rest is ASCII15.
The rest is ASCII16.
The rest is ASCII17.
The rest is ASCII18.
The rest is ASCII19.
The rest is ASCII20.
The rest is ASCII21.
The rest is ASCII22.
⚽The rest is ASCII01.
The rest is ASCII02.
The rest is ASCII03.
The rest is ASCII04.
The rest is ASCII05.
The rest is ASCII06.
The rest is ASCII07.
The rest is ASCII08.
The rest is ASCII09.
The rest is ASCII10.
The rest is ASCII11.
The rest is ASCII12.
The rest is ASCII13.
The rest is ASCII14.
The rest is ASCII15.
The rest is ASCII16.
The rest is ASCII17.
The rest is ASCII18.
The rest is ASCII19.
The rest is ASCII20.
The rest is ASCII21.
The rest is ASCII22.
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