Commit 5c54e2162a21cb909e961c62f53e7d4c64f80cb0

Carlos Martín Nieto 2014-11-05T16:07:07

ignore: consider files with a CR in their names We currently consider CR to start the end of the line, but that means that we miss cases with CR CR LF which can be used with git to match files whose names have CR at the end of their names. The fix from the patch comes from Russell's comment in the issue. This fixes #2536.

diff --git a/src/attr_file.c b/src/attr_file.c
index 07ffacb..5620752 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -543,7 +543,7 @@ int git_attr_fnmatch__parse(
 	for (scan = pattern; *scan != '\0'; ++scan) {
 		/* scan until (non-escaped) white space */
 		if (git__isspace(*scan) && *(scan - 1) != '\\') {
-			if (!allow_space || (*scan != ' ' && *scan != '\t'))
+			if (!allow_space || (*scan != ' ' && *scan != '\t' && *scan != '\r'))
 				break;
 		}
 
@@ -564,6 +564,15 @@ int git_attr_fnmatch__parse(
 	if ((spec->length = scan - pattern) == 0)
 		return GIT_ENOTFOUND;
 
+	/*
+	 * Remove one trailing \r in case this is a CRLF delimited
+	 * file, in the case of Icon\r\r\n, we still leave the first
+	 * \r there to match against.
+	 */
+	if (pattern[spec->length - 1] == '\r')
+		if (--spec->length == 0)
+			return GIT_ENOTFOUND;
+
 	if (pattern[spec->length - 1] == '/') {
 		spec->length--;
 		spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY;
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index b2af790..7cf8803 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -883,3 +883,35 @@ void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(
 	cl_assert(found_parent_child2_file);
 }
 
+void test_status_ignore__filename_with_cr(void)
+{
+	int ignored;
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+	cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\r\n");
+
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
+	cl_assert_equal_i(1, ignored);
+
+	cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\n");
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn"));
+	cl_assert_equal_i(1, ignored);
+
+	cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\n");
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn"));
+	cl_assert_equal_i(1, ignored);
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r"));
+	cl_assert_equal_i(0, ignored);
+
+	cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\r\n");
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r"));
+	cl_assert_equal_i(1, ignored);
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
+	cl_assert_equal_i(0, ignored);
+
+	cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\n");
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
+	cl_assert_equal_i(0, ignored);
+	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon"));
+	cl_assert_equal_i(1, ignored);
+}