diff --git a/src/attr_file.c b/src/attr_file.c index 07ffacbaf..562075291 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 b2af79074..7cf8803c2 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); +}