Merge pull request #5095 from pks-t/pks/ignore-escaped-trailing-space ignore: handle escaped trailing whitespace
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
diff --git a/src/attr_file.c b/src/attr_file.c
index 51ecfbb..673f9a4 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -560,6 +560,21 @@ void git_attr_path__free(git_attr_path *info)
*/
/*
+ * Determine the length of trailing spaces. Escaped spaces do not count as
+ * trailing whitespace.
+ */
+static size_t trailing_space_length(const char *p, size_t len)
+{
+ size_t n;
+ for (n = len; n; n--) {
+ if ((p[n-1] != ' ' && p[n-1] != '\t') ||
+ (n > 1 && p[n-2] == '\\'))
+ break;
+ }
+ return len - n;
+}
+
+/*
* This will return 0 if the spec was filled out,
* GIT_ENOTFOUND if the fnmatch does not require matching, or
* another error code there was an actual problem.
@@ -647,9 +662,10 @@ int git_attr_fnmatch__parse(
return GIT_ENOTFOUND;
/* Remove trailing spaces. */
- while (pattern[spec->length - 1] == ' ' || pattern[spec->length - 1] == '\t')
- if (--spec->length == 0)
- return GIT_ENOTFOUND;
+ spec->length -= trailing_space_length(pattern, spec->length);
+
+ if (spec->length == 0)
+ return GIT_ENOTFOUND;
if (pattern[spec->length - 1] == '/') {
spec->length--;
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
index 1bf06fc..ea8a141 100644
--- a/tests/attr/ignore.c
+++ b/tests/attr/ignore.c
@@ -61,6 +61,52 @@ void test_attr_ignore__ignore_space(void)
assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
}
+void test_attr_ignore__intermittent_space(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "foo bar\n");
+
+ assert_is_ignored(false, "foo");
+ assert_is_ignored(false, "bar");
+ assert_is_ignored(true, "foo bar");
+}
+
+void test_attr_ignore__trailing_space(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "foo \n"
+ "bar \n"
+ );
+
+ assert_is_ignored(true, "foo");
+ assert_is_ignored(false, "foo ");
+ assert_is_ignored(true, "bar");
+ assert_is_ignored(false, "bar ");
+ assert_is_ignored(false, "bar ");
+}
+
+void test_attr_ignore__escaped_trailing_spaces(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "foo\\ \n"
+ "bar\\ \\ \n"
+ "baz \\ \n"
+ "qux\\ \n"
+ );
+
+ assert_is_ignored(false, "foo");
+ assert_is_ignored(true, "foo ");
+ assert_is_ignored(false, "bar");
+ assert_is_ignored(false, "bar ");
+ assert_is_ignored(true, "bar ");
+ assert_is_ignored(true, "baz ");
+ assert_is_ignored(false, "baz ");
+ assert_is_ignored(true, "qux ");
+ assert_is_ignored(false, "qux");
+ assert_is_ignored(false, "qux ");
+}
+
void test_attr_ignore__ignore_dir(void)
{
cl_git_rewritefile("attr/.gitignore", "dir/\n");