diff --git a/src/ignore.c b/src/ignore.c index e150b9585..cc90b0c61 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -340,3 +340,61 @@ cleanup: return error; } + +int git_ignore__check_pathspec_for_exact_ignores( + git_repository *repo, + git_vector *vspec, + bool no_fnmatch) +{ + int error = 0; + size_t i; + git_attr_fnmatch *match; + int ignored; + git_buf path = GIT_BUF_INIT; + const char *wd, *filename; + git_index *idx; + + if ((error = git_repository__ensure_not_bare( + repo, "validate pathspec")) < 0 || + (error = git_repository_index(&idx, repo)) < 0) + return error; + + wd = git_repository_workdir(repo); + + git_vector_foreach(vspec, i, match) { + /* skip wildcard matches (if they are being used) */ + if ((match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0 && + !no_fnmatch) + continue; + + filename = match->pattern; + + /* if file is already in the index, it's fine */ + if (git_index_get_bypath(idx, filename, 0) != NULL) + continue; + + if ((error = git_buf_joinpath(&path, wd, filename)) < 0) + break; + + /* is there a file on disk that matches this exactly? */ + if (!git_path_isfile(path.ptr)) + continue; + + /* is that file ignored? */ + if ((error = git_ignore_path_is_ignored(&ignored, repo, filename)) < 0) + break; + + if (ignored) { + giterr_set(GITERR_INVALID, "pathspec contains ignored file '%s'", + filename); + error = GIT_EINVALIDSPEC; + break; + } + } + + git_index_free(idx); + git_buf_free(&path); + + return error; +} + diff --git a/src/ignore.h b/src/ignore.h index e00e4a8c8..cc114b001 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -41,4 +41,13 @@ extern void git_ignore__free(git_ignores *ign); extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored); +/* command line Git sometimes generates an error message if given a + * pathspec that contains an exact match to an ignored file (provided + * --force isn't also given). This makes it easy to check it that has + * happened. Returns GIT_EINVALIDSPEC if the pathspec contains ignored + * exact matches (that are not already present in the index). + */ +extern int git_ignore__check_pathspec_for_exact_ignores( + git_repository *repo, git_vector *pathspec, bool no_fnmatch); + #endif