diff options
author | Vicent Marti <vicent@github.com> | 2014-04-19 13:05:32 +0200 |
---|---|---|
committer | Vicent Marti <vicent@github.com> | 2014-04-19 13:05:32 +0200 |
commit | 7b0f8ba9a8d714de3d4432c9b5902f3a8d8889d7 (patch) | |
tree | f9a21fca19b8f024810f3278a8c99b18b715d403 /src | |
parent | 386777fd0dd7665a0aad65bf3589d7bf71024c0e (diff) | |
parent | ac16bd0a94e1f7254112c7585b843bdc2d0659c1 (diff) | |
download | libgit2-7b0f8ba9a8d714de3d4432c9b5902f3a8d8889d7.tar.gz |
Merge pull request #2279 from libgit2/rb/moar-eegnöre-fîxés
Fix several ignore and attribute file behavior bugs
Diffstat (limited to 'src')
-rw-r--r-- | src/attr.c | 74 | ||||
-rw-r--r-- | src/attr_file.c | 14 | ||||
-rw-r--r-- | src/attr_file.h | 6 | ||||
-rw-r--r-- | src/ignore.c | 13 | ||||
-rw-r--r-- | src/ignore.h | 1 | ||||
-rw-r--r-- | src/path.c | 2 | ||||
-rw-r--r-- | src/pathspec.c | 3 |
7 files changed, 98 insertions, 15 deletions
diff --git a/src/attr.c b/src/attr.c index 622874348..05b0c1b3c 100644 --- a/src/attr.c +++ b/src/attr.c @@ -217,6 +217,74 @@ cleanup: return error; } +static int preload_attr_file( + git_repository *repo, + git_attr_file_source source, + const char *base, + const char *file) +{ + int error; + git_attr_file *preload = NULL; + + if (!file) + return 0; + if (!(error = git_attr_cache__get( + &preload, repo, source, base, file, git_attr_file__parse_buffer))) + git_attr_file__free(preload); + + return error; +} + +static int attr_setup(git_repository *repo) +{ + int error = 0; + const char *workdir = git_repository_workdir(repo); + git_index *idx = NULL; + git_buf sys = GIT_BUF_INIT; + + if ((error = git_attr_cache__init(repo)) < 0) + return error; + + /* preload attribute files that could contain macros so the + * definitions will be available for later file parsing + */ + + if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) { + error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); + git_buf_free(&sys); + } + if (error < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } else + return error; + } + + if ((error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, + NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0) + return error; + + if ((error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, + git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0) + return error; + + if (workdir != NULL && + (error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0) + return error; + + if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || + (error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0) + return error; + + return error; +} + int git_attr_add_macro( git_repository *repo, const char *name, @@ -226,8 +294,8 @@ int git_attr_add_macro( git_attr_rule *macro = NULL; git_pool *pool; - if (git_attr_cache__init(repo) < 0) - return -1; + if ((error = git_attr_cache__init(repo)) < 0) + return error; macro = git__calloc(1, sizeof(git_attr_rule)); GITERR_CHECK_ALLOC(macro); @@ -348,7 +416,7 @@ static int collect_attr_files( const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; - if ((error = git_attr_cache__init(repo)) < 0) + if ((error = attr_setup(repo)) < 0) return error; /* Resolve path in a non-bare repo */ diff --git a/src/attr_file.c b/src/attr_file.c index 65bbf78e8..d107b5ab0 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -248,9 +248,7 @@ int git_attr_file__parse_buffer( repo, &attrs->pool, &rule->assigns, &scan))) { if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) - /* should generate error/warning if this is coming from any - * file other than .gitattributes at repo root. - */ + /* TODO: warning if macro found in file below repo root */ error = git_attr_cache__insert_macro(repo, rule); else error = git_vector_insert(&attrs->rules, rule); @@ -355,6 +353,8 @@ bool git_attr_fnmatch__match( if (match->flags & GIT_ATTR_FNMATCH_ICASE) flags |= FNM_CASEFOLD; + if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR) + flags |= FNM_LEADING_DIR; if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) { filename = path->path; @@ -545,6 +545,14 @@ int git_attr_fnmatch__parse( if (--slash_count <= 0) spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH; } + if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 && + spec->length >= 2 && + pattern[spec->length - 1] == '*' && + pattern[spec->length - 2] == '/') { + spec->length -= 2; + spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR; + /* leave FULLPATH match on, however */ + } if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 && context != NULL && git_path_root(pattern) < 0) diff --git a/src/attr_file.h b/src/attr_file.h index c906be44d..e50aec07c 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -30,10 +30,12 @@ #define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8) #define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9) #define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10) +#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11) +#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12) #define GIT_ATTR_FNMATCH__INCOMING \ - (GIT_ATTR_FNMATCH_ALLOWSPACE | \ - GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO) + (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \ + GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR) typedef enum { GIT_ATTR_FILE__IN_MEMORY = 0, diff --git a/src/ignore.c b/src/ignore.c index deae204f8..b08ff2200 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -123,7 +123,7 @@ int git_ignore__for_path( int error = 0; const char *workdir = git_repository_workdir(repo); - assert(ignores); + assert(ignores && path); memset(ignores, 0, sizeof(*ignores)); ignores->repo = repo; @@ -140,10 +140,13 @@ int git_ignore__for_path( if (workdir && git_path_root(path) < 0) error = git_path_find_dir(&ignores->dir, path, workdir); else - error = git_buf_sets(&ignores->dir, path); + error = git_buf_joinpath(&ignores->dir, path, ""); if (error < 0) goto cleanup; + if (workdir && !git__prefixcmp(ignores->dir.ptr, workdir)) + ignores->dir_root = strlen(workdir); + /* set up internals */ if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0) goto cleanup; @@ -204,10 +207,10 @@ int git_ignore__pop_dir(git_ignores *ign) if ((end = strrchr(start, '/')) != NULL) { size_t dirlen = (end - start) + 1; + const char *relpath = ign->dir.ptr + ign->dir_root; + size_t pathlen = ign->dir.size - ign->dir_root; - if (ign->dir.size >= dirlen && - !memcmp(ign->dir.ptr + ign->dir.size - dirlen, start, dirlen)) - { + if (pathlen == dirlen && !memcmp(relpath, start, dirlen)) { git_vector_pop(&ign->ign_path); git_attr_file__free(file); } diff --git a/src/ignore.h b/src/ignore.h index 46172c72f..ff9369000 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -28,6 +28,7 @@ typedef struct { git_attr_file *ign_internal; git_vector ign_path; git_vector ign_global; + size_t dir_root; /* offset in dir to repo root */ int ignore_case; int depth; } git_ignores; diff --git a/src/path.c b/src/path.c index 7cad28d45..a990b005f 100644 --- a/src/path.c +++ b/src/path.c @@ -624,7 +624,7 @@ int git_path_find_dir(git_buf *dir, const char *path, const char *base) /* call dirname if this is not a directory */ if (!error) /* && git_path_isdir(dir->ptr) == false) */ - error = git_path_dirname_r(dir, dir->ptr); + error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0; if (!error) error = git_path_to_dir(dir); diff --git a/src/pathspec.c b/src/pathspec.c index 09650de7c..a01d74f07 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -83,7 +83,8 @@ int git_pathspec__vinit( if (!match) return -1; - match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; + match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | + GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_NOLEADINGDIR; ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern); if (ret == GIT_ENOTFOUND) { |