diff options
-rw-r--r-- | src/attr_file.c | 26 | ||||
-rw-r--r-- | src/attr_file.h | 2 | ||||
-rw-r--r-- | tests/status/ignore.c | 32 |
3 files changed, 60 insertions, 0 deletions
diff --git a/src/attr_file.c b/src/attr_file.c index 562075291..e3692cee9 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -347,6 +347,21 @@ bool git_attr_fnmatch__match( const char *filename; int flags = 0; + /* + * If the rule was generated in a subdirectory, we must only + * use it for paths inside that directory. We can thus return + * a non-match if the prefixes don't match. + */ + if (match->containing_dir) { + if (match->flags & GIT_ATTR_FNMATCH_ICASE) { + if (git__strncasecmp(path->path, match->containing_dir, match->containing_dir_length)) + return 0; + } else { + if (git__prefixcmp(path->path, match->containing_dir)) + return 0; + } + } + if (match->flags & GIT_ATTR_FNMATCH_ICASE) flags |= FNM_CASEFOLD; if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR) @@ -588,6 +603,17 @@ int git_attr_fnmatch__parse( /* leave FULLPATH match on, however */ } + if (context) { + char *slash = strchr(context, '/'); + size_t len; + if (slash) { + /* include the slash for easier matching */ + len = slash - context + 1; + spec->containing_dir = git_pool_strndup(pool, context, len); + spec->containing_dir_length = len; + } + } + if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 && context != NULL && git_path_root(pattern) < 0) { diff --git a/src/attr_file.h b/src/attr_file.h index 87cde7e35..93de84d12 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -52,6 +52,8 @@ extern const char *git_attr__unset; typedef struct { char *pattern; size_t length; + char *containing_dir; + size_t containing_dir_length; unsigned int flags; } git_attr_fnmatch; diff --git a/tests/status/ignore.c b/tests/status/ignore.c index 7cf8803c2..6b31e7785 100644 --- a/tests/status/ignore.c +++ b/tests/status/ignore.c @@ -915,3 +915,35 @@ void test_status_ignore__filename_with_cr(void) cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon")); cl_assert_equal_i(1, ignored); } + +void test_status_ignore__subdir_doesnt_match_above(void) +{ + int ignored, icase = 0, error; + git_config *cfg; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(git_repository_config_snapshot(&cfg, g_repo)); + error = git_config_get_bool(&icase, cfg, "core.ignorecase"); + if (error == GIT_ENOTFOUND) + error = 0; + + cl_git_pass(error); + + cl_git_pass(p_mkdir("empty_standard_repo/src", 0777)); + cl_git_pass(p_mkdir("empty_standard_repo/src/src", 0777)); + cl_git_mkfile("empty_standard_repo/src/.gitignore", "src\n"); + cl_git_mkfile("empty_standard_repo/.gitignore", ""); + + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/test.txt")); + cl_assert_equal_i(0, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/src/test.txt")); + cl_assert_equal_i(1, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/foo/test.txt")); + cl_assert_equal_i(0, ignored); + + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "SRC/src/test.txt")); + cl_assert_equal_i(icase, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/SRC/test.txt")); + cl_assert_equal_i(icase, ignored); +} |