diff options
| author | Russell Belfer <arrbee@arrbee.com> | 2012-01-09 15:37:19 -0800 |
|---|---|---|
| committer | Russell Belfer <arrbee@arrbee.com> | 2012-01-11 14:39:51 -0800 |
| commit | df743c7d3a04553ffc04ae7cbc64fb300e7f61d2 (patch) | |
| tree | 7f0dfa714ddb292448cbeaa69f2b5d90a3274d85 /src/ignore.c | |
| parent | 7e443f696068cd8c84a759e532c2845348e5a6ad (diff) | |
| download | libgit2-df743c7d3a04553ffc04ae7cbc64fb300e7f61d2.tar.gz | |
Initial implementation of gitignore support
Adds support for .gitignore files to git_status_foreach() and
git_status_file(). This includes refactoring the gitattributes
code to share logic where possible. The GIT_STATUS_IGNORED flag
will now be passed in for files that are ignored (provided they
are not already in the index or the head of repo).
Diffstat (limited to 'src/ignore.c')
| -rw-r--r-- | src/ignore.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/ignore.c b/src/ignore.c new file mode 100644 index 000000000..8bf22e34a --- /dev/null +++ b/src/ignore.c @@ -0,0 +1,148 @@ +#include "ignore.h" +#include "path.h" +#include "git2/config.h" + +#define GIT_IGNORE_INTERNAL "[internal]exclude" +#define GIT_IGNORE_FILE_INREPO "info/exclude" +#define GIT_IGNORE_FILE ".gitignore" +#define GIT_IGNORE_CONFIG "core.excludesfile" + +static int load_ignore_file( + git_repository *GIT_UNUSED(repo), const char *path, git_attr_file **out) +{ + int error = GIT_SUCCESS; + git_fbuffer fbuf = GIT_FBUFFER_INIT; + git_attr_file *ignores = NULL; + git_attr_fnmatch *match = NULL; + const char *scan = NULL; + + GIT_UNUSED_ARG(repo); + + *out = NULL; + + if ((error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS) + error = git_attr_file__new(&ignores); + + scan = fbuf.data; + + while (error == GIT_SUCCESS && *scan) { + if (!match && !(match = git__calloc(1, sizeof(git_attr_fnmatch)))) { + error = GIT_ENOMEM; + break; + } + + if (!(error = git_attr_fnmatch__parse(match, &scan))) { + match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE; + scan = git__next_line(scan); + error = git_vector_insert(&ignores->rules, match); + } + + if (error != GIT_SUCCESS) { + git__free(match->pattern); + match->pattern = NULL; + + if (error == GIT_ENOTFOUND) + error = GIT_SUCCESS; + } else { + match = NULL; /* vector now "owns" the match */ + } + } + + git_futils_freebuffer(&fbuf); + + if (error != GIT_SUCCESS) { + git__rethrow(error, "Could not open ignore file '%s'", path); + git__free(match); + git_attr_file__free(ignores); + } else { + *out = ignores; + } + + return error; +} + +#define push_ignore(R,S,B,F) \ + git_attr_cache__push_file((R),(S),(B),(F),load_ignore_file) + +int git_ignore__for_path(git_repository *repo, const char *path, git_vector *stack) +{ + int error = GIT_SUCCESS; + git_buf dir = GIT_BUF_INIT, scan; + git_config *cfg; + const char *workdir = git_repository_workdir(repo); + + if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS) + goto cleanup; + + if ((error = git_futils_dir_for_path(&dir, path, workdir)) < GIT_SUCCESS) + goto cleanup; + + /* insert internals */ + if ((error = push_ignore(repo, stack, NULL, GIT_IGNORE_INTERNAL)) < GIT_SUCCESS) + goto cleanup; + + /* load .gitignore up the path */ + git_path_walk_up(&dir, &scan, workdir, { + error = push_ignore(repo, stack, scan.ptr, GIT_IGNORE_FILE); + if (error < GIT_SUCCESS) break; + }); + if (error < GIT_SUCCESS) + goto cleanup; + + /* load .git/info/exclude */ + if ((error = push_ignore(repo, stack, repo->path_repository, GIT_IGNORE_FILE_INREPO)) < GIT_SUCCESS) + goto cleanup; + + /* load core.excludesfile */ + if (git_repository_config(&cfg, repo) == GIT_SUCCESS) { + const char *core_ignore; + error = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &core_ignore); + if (error == GIT_SUCCESS && core_ignore != NULL) + error = push_ignore(repo, stack, NULL, core_ignore); + else { + error = GIT_SUCCESS; + git_clearerror(); /* don't care if attributesfile is not set */ + } + git_config_free(cfg); + } + +cleanup: + if (error < GIT_SUCCESS) + git__rethrow(error, "Could not get ignore files for '%s'", path); + + git_buf_free(&dir); + + return error; +} + +void git_ignore__free(git_vector *stack) +{ + git_vector_free(stack); +} + +int git_ignore__lookup(git_vector *stack, const char *pathname, int *ignored) +{ + int error; + unsigned int i, j; + git_attr_file *file; + git_attr_path path; + git_attr_fnmatch *match; + + if ((error = git_attr_path__init(&path, pathname)) < GIT_SUCCESS) + return git__rethrow(error, "Could not get attribute for '%s'", pathname); + + *ignored = 0; + + git_vector_foreach(stack, i, file) { + git_vector_rforeach(&file->rules, j, match) { + if (git_attr_fnmatch__match(match, &path) == GIT_SUCCESS) { + *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0); + goto found; + } + } + } +found: + + return error; +} + |
