summaryrefslogtreecommitdiff
path: root/src/ignore.c
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2012-10-15 12:48:43 -0700
committerRussell Belfer <rb@github.com>2012-10-15 12:54:46 -0700
commit52032ae53689ac37350f6af3bf1834122e4b3cf0 (patch)
treed9d98e14fa8e2a5d5d024dbd623bf9beb42e1330 /src/ignore.c
parentd5a51910678f8aea2b7efe077efc678141762dfc (diff)
downloadlibgit2-52032ae53689ac37350f6af3bf1834122e4b3cf0.tar.gz
Fix single-file ignore checks
To answer if a single given file should be ignored, the path to that file has to be processed progressively checking that there are no intermediate ignored directories in getting to the file in question. This enables that, fixing the broken old behavior, and adds tests to exercise various ignore situations.
Diffstat (limited to 'src/ignore.c')
-rw-r--r--src/ignore.c69
1 files changed, 65 insertions, 4 deletions
diff --git a/src/ignore.c b/src/ignore.c
index e711be206..6a377e60d 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -277,15 +277,76 @@ int git_ignore_clear_internal_rules(
int git_ignore_path_is_ignored(
int *ignored,
git_repository *repo,
- const char *path)
+ const char *pathname)
{
int error;
+ const char *workdir;
+ git_attr_path path;
+ char *tail, *end;
+ bool full_is_dir;
git_ignores ignores;
+ unsigned int i;
+ git_attr_file *file;
- if (git_ignore__for_path(repo, path, &ignores) < 0)
- return -1;
+ assert(ignored && pathname);
+
+ workdir = repo ? git_repository_workdir(repo) : NULL;
+
+ if ((error = git_attr_path__init(&path, pathname, workdir)) < 0)
+ return error;
+
+ tail = path.path;
+ end = &path.full.ptr[path.full.size];
+ full_is_dir = path.is_dir;
- error = git_ignore__lookup(&ignores, path, ignored);
+ while (1) {
+ /* advance to next component of path */
+ path.basename = tail;
+
+ while (tail < end && *tail != '/') tail++;
+ *tail = '\0';
+
+ path.full.size = (tail - path.full.ptr);
+ path.is_dir = (tail == end) ? full_is_dir : true;
+
+ /* update ignores for new path fragment */
+ if (path.basename == path.path)
+ error = git_ignore__for_path(repo, path.path, &ignores);
+ else
+ error = git_ignore__push_dir(&ignores, path.basename);
+ if (error < 0)
+ break;
+
+ /* first process builtins - success means path was found */
+ if (ignore_lookup_in_rules(
+ &ignores.ign_internal->rules, &path, ignored))
+ goto cleanup;
+
+ /* next process files in the path */
+ git_vector_foreach(&ignores.ign_path, i, file) {
+ if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+ goto cleanup;
+ }
+
+ /* last process global ignores */
+ git_vector_foreach(&ignores.ign_global, i, file) {
+ if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+ goto cleanup;
+ }
+
+ /* if we found no rules before reaching the end, we're done */
+ if (tail == end)
+ break;
+
+ /* reinstate divider in path */
+ *tail = '/';
+ while (*tail == '/') tail++;
+ }
+
+ *ignored = 0;
+
+cleanup:
+ git_attr_path__free(&path);
git_ignore__free(&ignores);
return error;
}