summaryrefslogtreecommitdiff
path: root/src/ignore.c
diff options
context:
space:
mode:
authorRussell Belfer <arrbee@arrbee.com>2012-01-09 15:37:19 -0800
committerRussell Belfer <arrbee@arrbee.com>2012-01-11 14:39:51 -0800
commitdf743c7d3a04553ffc04ae7cbc64fb300e7f61d2 (patch)
tree7f0dfa714ddb292448cbeaa69f2b5d90a3274d85 /src/ignore.c
parent7e443f696068cd8c84a759e532c2845348e5a6ad (diff)
downloadlibgit2-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.c148
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;
+}
+