diff --git a/src/attr_file.c b/src/attr_file.c index 85cd87624..93f6df1d9 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -85,7 +85,7 @@ int git_attr_file__parse_buffer( } /* parse the next "pattern attr attr attr" line */ - if (!(error = git_attr_fnmatch__parse( + if (!(error = git_attr_fnmatch__parse_gitattr_format( &rule->match, attrs->pool, context, &scan)) && !(error = git_attr_assignment__parse( repo, attrs->pool, &rule->assigns, &scan))) @@ -337,23 +337,16 @@ void git_attr_path__free(git_attr_path *info) * GIT_ENOTFOUND if the fnmatch does not require matching, or * another error code there was an actual problem. */ -int git_attr_fnmatch__parse( +int git_attr_fnmatch__parse_gitattr_format( git_attr_fnmatch *spec, git_pool *pool, const char *source, const char **base) { - const char *pattern, *scan; - int slash_count, allow_space; + const char *pattern; assert(spec && base && *base); - if (parse_optimized_patterns(spec, pool, *base)) - return 0; - - spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE); - allow_space = (spec->flags != 0); - pattern = *base; while (git__isspace(*pattern)) pattern++; @@ -375,6 +368,39 @@ int git_attr_fnmatch__parse( pattern++; } + if (git_attr_fnmatch__parse_shellglob_format(spec, pool, + source, &pattern) < 0) + return -1; + + *base = pattern; + + return 0; +} + +/* + * Fills a spec for the purpose of pure pathspec matching, not + * related to a gitattribute file parsing. + * + * This will return 0 if the spec was filled out, or + * another error code there was an actual problem. + */ +int git_attr_fnmatch__parse_shellglob_format( + git_attr_fnmatch *spec, + git_pool *pool, + const char *source, + const char **base) +{ + const char *pattern, *scan; + int slash_count, allow_space; + + assert(spec && base && *base); + + if (parse_optimized_patterns(spec, pool, *base)) + return 0; + + allow_space = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0; + pattern = *base; + slash_count = 0; for (scan = pattern; *scan != '\0'; ++scan) { /* scan until (non-escaped) white space */ @@ -609,6 +635,7 @@ static void git_attr_rule__clear(git_attr_rule *rule) /* match.pattern is stored in a git_pool, so no need to free */ rule->match.pattern = NULL; rule->match.length = 0; + rule->match.flags = 0; } void git_attr_rule__free(git_attr_rule *rule) diff --git a/src/attr_file.h b/src/attr_file.h index d8abcda58..8ca7e4eb7 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -115,7 +115,13 @@ extern uint32_t git_attr_file__name_hash(const char *name); * other utilities */ -extern int git_attr_fnmatch__parse( +extern int git_attr_fnmatch__parse_gitattr_format( + git_attr_fnmatch *spec, + git_pool *pool, + const char *source, + const char **base); + +extern int git_attr_fnmatch__parse_shellglob_format( git_attr_fnmatch *spec, git_pool *pool, const char *source, diff --git a/src/ignore.c b/src/ignore.c index 17779522c..dae974b6e 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -49,7 +49,7 @@ static int parse_ignore_file( match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE; - if (!(error = git_attr_fnmatch__parse( + if (!(error = git_attr_fnmatch__parse_gitattr_format( match, ignores->pool, context, &scan))) { match->flags |= GIT_ATTR_FNMATCH_IGNORE; diff --git a/src/pathspec.c b/src/pathspec.c index d4eb12582..9dee55ea1 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -78,7 +78,7 @@ int git_pathspec_init( match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE; - ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern); + ret = git_attr_fnmatch__parse_shellglob_format(match, strpool, NULL, &pattern); if (ret == GIT_ENOTFOUND) { git__free(match); continue; diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c index 2d3898ba4..6c17d2c39 100644 --- a/tests-clar/status/ignore.c +++ b/tests-clar/status/ignore.c @@ -459,3 +459,37 @@ void test_status_ignore__automatically_ignore_bad_files(void) cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c")); cl_assert(!ignored); } + +void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void) +{ + status_entry_single st; + char *test_cases[] = { + "!file", + "#blah", + "[blah]", + "[attr]", + "[attr]blah", + NULL + }; + int i; + + for (i = 0; *(test_cases + i) != NULL; i++) { + git_buf file = GIT_BUF_INIT; + char *file_name = *(test_cases + i); + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(git_buf_joinpath(&file, "empty_standard_repo", file_name)); + cl_git_mkfile(git_buf_cstr(&file), "Please don't ignore me!"); + + memset(&st, 0, sizeof(st)); + cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); + cl_assert(st.count == 1); + cl_assert(st.status == GIT_STATUS_WT_NEW); + + cl_git_pass(git_status_file(&st.status, repo, file_name)); + cl_assert(st.status == GIT_STATUS_WT_NEW); + + cl_git_sandbox_cleanup(); + git_buf_free(&file); + } +}