Commit e3d7bccbec2a19f9bcda15679f28a33ad98ded93

Steve King Jr 2019-03-14T15:51:15

ignore: Do not match on prefix of negated patterns Matching on the prefix of a negated pattern was triggering false negatives on siblings of that pattern. e.g. Given the .gitignore: dir/* !dir/sub1/sub2/** The path `dir/a.text` would not be ignored.

diff --git a/src/attr_file.c b/src/attr_file.c
index 40c72ea..8619647 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -429,18 +429,6 @@ bool git_attr_fnmatch__match(
 		return (p_fnmatch(match->pattern, relpath, flags) != FNM_NOMATCH);
 	}
 
-	/* if path is a directory prefix of a negated pattern, then match */
-	if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) && path->is_dir) {
-		size_t pathlen = strlen(relpath);
-		bool prefixed = (pathlen <= match->length) &&
-			((match->flags & GIT_ATTR_FNMATCH_ICASE) ?
-			!strncasecmp(match->pattern, relpath, pathlen) :
-			!strncmp(match->pattern, relpath, pathlen));
-
-		if (prefixed && git_path_at_end_of_segment(&match->pattern[pathlen]))
-			return true;
-	}
-
 	return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
 }
 
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index f6c72c2..7256fb7 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -1218,16 +1218,36 @@ void test_status_ignore__ignored_subdirfiles_with_subdir_rule(void)
 {
 	static const char *test_files[] = {
     "empty_standard_repo/dir/a.test",
-		"empty_standard_repo/dir/sub1/b.test",
+		"empty_standard_repo/dir/sub1/sub2/b.test",
 		NULL
 	};
 
 	make_test_data("empty_standard_repo", test_files);
 	cl_git_mkfile(
 		"empty_standard_repo/.gitignore",
-		"/dir/*\n"
-		"!/dir/sub1/sub2/**/*\n");
+		"dir/*\n"
+		"!dir/sub1/sub2/**\n");
 
 	assert_is_ignored("dir/a.test");
 	assert_is_ignored("dir/sub1/a.test");
 }
+
+void test_status_ignore__ignored_subdirfiles_with_negations(void)
+{
+	static const char *test_files[] = {
+    "empty_standard_repo/dir/a.test",
+		"empty_standard_repo/dir/b.test",
+		"empty_standard_repo/dir/sub1/c.test",
+		NULL
+	};
+
+	make_test_data("empty_standard_repo", test_files);
+	cl_git_mkfile(
+		"empty_standard_repo/.gitignore",
+		"dir/*\n"
+		"!dir/a.test\n");
+
+	refute_is_ignored("dir/a.test");
+	assert_is_ignored("dir/b.test");
+	assert_is_ignored("dir/sub1/c.test");
+}