diff options
Diffstat (limited to 'src/ignore.c')
-rw-r--r-- | src/ignore.c | 128 |
1 files changed, 75 insertions, 53 deletions
diff --git a/src/ignore.c b/src/ignore.c index 0fb042a34..3ee7ba03a 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -1,7 +1,7 @@ #include "git2/ignore.h" #include "common.h" #include "ignore.h" -#include "attr.h" +#include "attr_file.h" #include "path.h" #include "config.h" @@ -10,26 +10,27 @@ #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n" static int parse_ignore_file( - git_repository *repo, void *parsedata, const char *buffer, git_attr_file *ignores) + git_repository *repo, + git_attr_file *attrs, + const char *data, + void *payload) { int error = 0; - git_attr_fnmatch *match = NULL; - const char *scan = NULL, *context = NULL; int ignore_case = false; + const char *scan = data, *context = NULL; + git_attr_fnmatch *match = NULL; - /* Prefer to have the caller pass in a git_ignores as the parsedata - * object. If they did not, then look up the value of ignore_case */ - if (parsedata != NULL) - ignore_case = ((git_ignores *)parsedata)->ignore_case; + /* either read ignore_case from ignores structure or use repo config */ + if (payload != NULL) + ignore_case = ((git_ignores *)payload)->ignore_case; else if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0) - return error; - - if (ignores->key && - git_path_root(ignores->key + 2) < 0 && - git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) - context = ignores->key + 2; + giterr_clear(); - scan = buffer; + /* if subdir file path, convert context for file paths */ + if (attrs->ce && + git_path_root(attrs->ce->path) < 0 && + !git__suffixcmp(attrs->ce->path, "/" GIT_IGNORE_FILE)) + context = attrs->ce->path; while (!error && *scan) { if (!match) { @@ -40,7 +41,7 @@ static int parse_ignore_file( match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; if (!(error = git_attr_fnmatch__parse( - match, ignores->pool, context, &scan))) + match, &attrs->pool, context, &scan))) { match->flags |= GIT_ATTR_FNMATCH_IGNORE; @@ -48,7 +49,7 @@ static int parse_ignore_file( match->flags |= GIT_ATTR_FNMATCH_ICASE; scan = git__next_line(scan); - error = git_vector_insert(&ignores->rules, match); + error = git_vector_insert(&attrs->rules, match); } if (error != 0) { @@ -67,28 +68,46 @@ static int parse_ignore_file( return error; } -#define push_ignore_file(R,IGN,S,B,F) \ - git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S)) +static int push_ignore_file( + git_ignores *ignores, + git_vector *which_list, + const char *base, + const char *filename) +{ + int error = 0; + git_attr_file *file = NULL; + + if ((error = git_attr_cache__get( + &file, ignores->repo, GIT_ATTR_CACHE__FROM_FILE, + base, filename, parse_ignore_file, ignores)) < 0 || + (error = git_vector_insert(which_list, file)) < 0) + git_attr_file__free(file); + + return error; +} static int push_one_ignore(void *payload, git_buf *path) { git_ignores *ign = payload; - ign->depth++; - - return push_ignore_file( - ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); + return push_ignore_file(ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); } -static int get_internal_ignores(git_attr_file **ign, git_repository *repo) +static int get_internal_ignores(git_attr_file **out, git_repository *repo) { int error; - if (!(error = git_attr_cache__init(repo))) - error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign); + if ((error = git_attr_cache__init(repo)) < 0) + return error; + + /* get with NULL parser, gives existing or empty git_attr_file */ + error = git_attr_cache__get( + out, repo, GIT_ATTR_CACHE__FROM_FILE, + NULL, GIT_IGNORE_INTERNAL, NULL, NULL); - if (!error && !(*ign)->rules.length) - error = parse_ignore_file(repo, NULL, GIT_IGNORE_DEFAULT_RULES, *ign); + /* if internal rules list is empty, insert default rules */ + if (!error && !(*out)->rules.length) + error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES, NULL); return error; } @@ -127,8 +146,7 @@ int git_ignore__for_path( goto cleanup; /* set up internals */ - error = get_internal_ignores(&ignores->ign_internal, repo); - if (error < 0) + if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0) goto cleanup; /* load .gitignore up the path */ @@ -140,14 +158,16 @@ int git_ignore__for_path( } /* load .git/info/exclude */ - error = push_ignore_file(repo, ignores, &ignores->ign_global, + error = push_ignore_file( + ignores, &ignores->ign_global, git_repository_path(repo), GIT_IGNORE_FILE_INREPO); if (error < 0) goto cleanup; /* load core.excludesfile */ if (git_repository_attr_cache(repo)->cfg_excl_file != NULL) - error = push_ignore_file(repo, ignores, &ignores->ign_global, NULL, + error = push_ignore_file( + ignores, &ignores->ign_global, NULL, git_repository_attr_cache(repo)->cfg_excl_file); cleanup: @@ -165,35 +185,33 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir) ign->depth++; return push_ignore_file( - ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); + ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); } int git_ignore__pop_dir(git_ignores *ign) { if (ign->ign_path.length > 0) { git_attr_file *file = git_vector_last(&ign->ign_path); - const char *start, *end, *scan; - size_t keylen; + const char *start = file->ce->path, *end; - /* - ign->dir looks something like "a/b/" (or "a/b/c/d/") - * - file->key looks something like "0#a/b/.gitignore + /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/") + * - file->path looks something like "a/b/.gitignore * - * We are popping the last directory off ign->dir. We also want to - * remove the file from the vector if the directory part of the key - * matches the ign->dir path. We need to test if the "a/b" part of + * We are popping the last directory off ign->dir. We also want + * to remove the file from the vector if the popped directory + * matches the ignore path. We need to test if the "a/b" part of * the file key matches the path we are about to pop. */ - for (start = end = scan = &file->key[2]; *scan; ++scan) - if (*scan == '/') - end = scan; /* point 'end' to last '/' in key */ - keylen = (end - start) + 1; + if ((end = strrchr(start, '/')) != NULL) { + size_t dirlen = (end - start) + 1; - if (ign->dir.size >= keylen && - !memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen)) - { - git_attr_file__free(git_vector_last(&ign->ign_path)); - git_vector_pop(&ign->ign_path); + if (ign->dir.size >= dirlen && + !memcmp(ign->dir.ptr + ign->dir.size - dirlen, start, dirlen)) + { + git_vector_pop(&ign->ign_path); + git_attr_file__free(file); + } } } @@ -210,7 +228,7 @@ void git_ignore__free(git_ignores *ignores) unsigned int i; git_attr_file *file; - /* don't need to free ignores->ign_internal it is cached exactly once */ + git_attr_file__free(ignores->ign_internal); git_vector_foreach(&ignores->ign_path, i, file) { git_attr_file__free(file); @@ -283,10 +301,12 @@ int git_ignore_add_rule( const char *rules) { int error; - git_attr_file *ign_internal; + git_attr_file *ign_internal = NULL; - if (!(error = get_internal_ignores(&ign_internal, repo))) + if (!(error = get_internal_ignores(&ign_internal, repo))) { error = parse_ignore_file(repo, NULL, rules, ign_internal); + git_attr_file__free(ign_internal); + } return error; } @@ -300,8 +320,10 @@ int git_ignore_clear_internal_rules( if (!(error = get_internal_ignores(&ign_internal, repo))) { git_attr_file__clear_rules(ign_internal); - return parse_ignore_file( + error = parse_ignore_file( repo, NULL, GIT_IGNORE_DEFAULT_RULES, ign_internal); + + git_attr_file__free(ign_internal); } return error; |