diff options
| author | Russell Belfer <rb@github.com> | 2012-05-03 16:37:25 -0700 |
|---|---|---|
| committer | Russell Belfer <rb@github.com> | 2012-05-03 16:37:25 -0700 |
| commit | f917481ee84cbba481c1854cccdedb2d98377d43 (patch) | |
| tree | b6276158b43f8d54358df5609fd0e508928306b2 /src/attr.c | |
| parent | 3fbcac89c47cb66ea193f66da6d93d1c36ed0f5e (diff) | |
| download | libgit2-f917481ee84cbba481c1854cccdedb2d98377d43.tar.gz | |
Support reading attributes from index
Depending on the operation, we need to consider gitattributes
in both the work dir and the index. This adds a parameter to
all of the gitattributes related functions that allows user
control of attribute reading behavior (i.e. prefer workdir,
prefer index, only use index).
This fix also covers allowing us to check attributes (and
hence do diff and status) on bare repositories.
This was a somewhat larger change that I hoped because it had
to change the cache key used for gitattributes files.
Diffstat (limited to 'src/attr.c')
| -rw-r--r-- | src/attr.c | 286 |
1 files changed, 213 insertions, 73 deletions
diff --git a/src/attr.c b/src/attr.c index 120d12737..56d04d3a9 100644 --- a/src/attr.c +++ b/src/attr.c @@ -6,12 +6,18 @@ GIT__USE_STRMAP; static int collect_attr_files( - git_repository *repo, const char *path, git_vector *files); + git_repository *repo, + uint32_t flags, + const char *path, + git_vector *files); int git_attr_get( - git_repository *repo, const char *pathname, - const char *name, const char **value) + git_repository *repo, + uint32_t flags, + const char *pathname, + const char *name, + const char **value) { int error; git_attr_path path; @@ -26,7 +32,7 @@ int git_attr_get( if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; - if ((error = collect_attr_files(repo, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) goto cleanup; attr.name = name; @@ -58,8 +64,12 @@ typedef struct { } attr_get_many_info; int git_attr_get_many( - git_repository *repo, const char *pathname, - size_t num_attr, const char **names, const char **values) + git_repository *repo, + uint32_t flags, + const char *pathname, + size_t num_attr, + const char **names, + const char **values) { int error; git_attr_path path; @@ -75,7 +85,7 @@ int git_attr_get_many( if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; - if ((error = collect_attr_files(repo, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) goto cleanup; info = git__calloc(num_attr, sizeof(attr_get_many_info)); @@ -119,7 +129,9 @@ cleanup: int git_attr_foreach( - git_repository *repo, const char *pathname, + git_repository *repo, + uint32_t flags, + const char *pathname, int (*callback)(const char *name, const char *value, void *payload), void *payload) { @@ -135,7 +147,7 @@ int git_attr_foreach( if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; - if ((error = collect_attr_files(repo, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) goto cleanup; seen = git_strmap_alloc(); @@ -203,113 +215,230 @@ int git_attr_add_macro( return error; } -bool git_attr_cache__is_cached(git_repository *repo, const char *path) +bool git_attr_cache__is_cached( + git_repository *repo, git_attr_file_source source, const char *path) { - const char *cache_key = path; + git_buf cache_key = GIT_BUF_INIT; git_strmap *files = git_repository_attr_cache(repo)->files; + const char *workdir = git_repository_workdir(repo); + bool rval; + + if (workdir && git__prefixcmp(path, workdir) == 0) + path += strlen(workdir); + if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0) + return false; + + rval = git_strmap_exists(files, git_buf_cstr(&cache_key)); + + git_buf_free(&cache_key); + + return rval; +} + +static int load_attr_file(const char *filename, const char **data) +{ + int error; + git_buf content = GIT_BUF_INIT; + + error = git_futils_readbuffer(&content, filename); + *data = error ? NULL : git_buf_detach(&content); + + return error; +} - if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0) - cache_key += strlen(git_repository_workdir(repo)); +static int load_attr_blob_from_index( + git_repository *repo, const char *filename, git_blob **blob) +{ + int error; + git_index *index; + git_index_entry *entry; + + if ((error = git_repository_index__weakptr(&index, repo)) < 0 || + (error = git_index_find(index, filename)) < 0) + return error; - return git_strmap_exists(files, cache_key); + entry = git_index_get(index, error); + + return git_blob_lookup(blob, repo, &entry->oid); } -int git_attr_cache__lookup_or_create_file( +int git_attr_cache__internal_file( git_repository *repo, - const char *key, const char *filename, - int (*loader)(git_repository *, const char *, git_attr_file *), - git_attr_file **file_ptr) + git_attr_file **file) { - int error; + int error = 0; git_attr_cache *cache = git_repository_attr_cache(repo); - git_attr_file *file = NULL; - khiter_t pos; - - pos = git_strmap_lookup_index(cache->files, key); - if (git_strmap_valid_index(cache->files, pos)) { - *file_ptr = git_strmap_value_at(cache->files, pos); - return 0; - } + khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename); - if (loader && git_path_exists(filename) == false) { - *file_ptr = NULL; + if (git_strmap_valid_index(cache->files, cache_pos)) { + *file = git_strmap_value_at(cache->files, cache_pos); return 0; } - if (git_attr_file__new(&file, &cache->pool) < 0) + if (git_attr_file__new(file, 0, filename, &cache->pool) < 0) return -1; - if (loader) - error = loader(repo, filename, file); - else - error = git_attr_file__set_path(repo, key, file); - - if (!error) { - git_strmap_insert(cache->files, file->path, file, error); - if (error > 0) - error = 0; - } - - if (error < 0) { - git_attr_file__free(file); - file = NULL; - } + git_strmap_insert(cache->files, (*file)->key + 2, *file, error); + if (error > 0) + error = 0; - *file_ptr = file; return error; } -/* add git_attr_file to vector of files, loading if needed */ int git_attr_cache__push_file( git_repository *repo, - git_vector *stack, - const char *base, - const char *filename, - int (*loader)(git_repository *, const char *, git_attr_file *)) + const char *base, + const char *filename, + git_attr_file_source source, + git_attr_file_parser parse, + git_vector *stack) { - int error; + int error = 0; git_buf path = GIT_BUF_INIT; + const char *workdir = git_repository_workdir(repo); + const char *relfile, *content = NULL; + git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file *file = NULL; - const char *cache_key; + git_blob *blob = NULL; - if (base != NULL) { + assert(filename && stack); + + /* join base and path as needed */ + if (base != NULL && git_path_root(filename) < 0) { if (git_buf_joinpath(&path, base, filename) < 0) return -1; filename = path.ptr; } - /* either get attr_file from cache or read from disk */ - cache_key = filename; - if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0) - cache_key += strlen(git_repository_workdir(repo)); + relfile = filename; + if (workdir && git__prefixcmp(relfile, workdir) == 0) + relfile += strlen(workdir); + + /* check cache */ + if (cache && cache->files) { + git_buf cache_key = GIT_BUF_INIT; + khiter_t cache_pos; + + if (git_buf_printf(&cache_key, "%d#%s", (int)source, relfile) < 0) + return -1; + + cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr); + + git_buf_free(&cache_key); + + if (git_strmap_valid_index(cache->files, cache_pos)) { + file = git_strmap_value_at(cache->files, cache_pos); + goto finish; + } + } + + /* if not in cache, load data, parse, and cache */ + if (git_attr_file__new(&file, source, relfile, &cache->pool) < 0) + return -1; + + if (source == GIT_ATTR_FILE_FROM_FILE) + error = load_attr_file(filename, &content); + else + error = load_attr_blob_from_index(repo, relfile, &blob); + + if (error) { + /* not finding a file is not an error for this function */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + goto finish; + } + + if (blob) + content = git_blob_rawcontent(blob); + + if (parse && (error = parse(repo, content, file)) < 0) + goto finish; - error = git_attr_cache__lookup_or_create_file( - repo, cache_key, filename, loader, &file); + git_strmap_insert(cache->files, file->key, file, error); + if (error > 0) + error = 0; +finish: + /* push file onto vector if we found one*/ if (!error && file != NULL) error = git_vector_insert(stack, file); + if (error != 0) + git_attr_file__free(file); + + if (blob) + git_blob_free(blob); + else + git__free((void *)content); + git_buf_free(&path); + return error; } -#define push_attrs(R,S,B,F) \ - git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file) +#define push_attr_file(R,S,B,F) \ + git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S)) typedef struct { git_repository *repo; + uint32_t flags; + const char *workdir; + git_index *index; git_vector *files; } attr_walk_up_info; +int git_attr_cache__decide_sources( + uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs) +{ + int count = 0; + + switch (flags & 0x03) { + case GIT_ATTR_CHECK_FILE_THEN_INDEX: + if (has_wd) + srcs[count++] = GIT_ATTR_FILE_FROM_FILE; + if (has_index) + srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + break; + case GIT_ATTR_CHECK_INDEX_THEN_FILE: + if (has_index) + srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + if (has_wd) + srcs[count++] = GIT_ATTR_FILE_FROM_FILE; + break; + case GIT_ATTR_CHECK_INDEX_ONLY: + if (has_index) + srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + break; + } + + return count; +} + static int push_one_attr(void *ref, git_buf *path) { + int error = 0, n_src, i; attr_walk_up_info *info = (attr_walk_up_info *)ref; - return push_attrs(info->repo, info->files, path->ptr, GIT_ATTR_FILE); + git_attr_file_source src[2]; + + n_src = git_attr_cache__decide_sources( + info->flags, info->workdir != NULL, info->index != NULL, src); + + for (i = 0; !error && i < n_src; ++i) + error = git_attr_cache__push_file( + info->repo, path->ptr, GIT_ATTR_FILE, src[i], + git_attr_file__parse_buffer, info->files); + + return error; } static int collect_attr_files( - git_repository *repo, const char *path, git_vector *files) + git_repository *repo, + uint32_t flags, + const char *path, + git_vector *files) { int error; git_buf dir = GIT_BUF_INIT; @@ -320,7 +449,11 @@ static int collect_attr_files( git_vector_init(files, 4, NULL) < 0) return -1; - error = git_path_find_dir(&dir, path, workdir); + /* given a unrooted path in a non-bare repo, resolve it */ + if (workdir && git_path_root(path) < 0) + error = git_path_find_dir(&dir, path, workdir); + else + error = git_buf_sets(&dir, path); if (error < 0) goto cleanup; @@ -331,29 +464,36 @@ static int collect_attr_files( * - $GIT_PREFIX/etc/gitattributes */ - error = push_attrs( + error = push_attr_file( repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO); if (error < 0) goto cleanup; - info.repo = repo; + info.repo = repo; + info.flags = flags; + info.workdir = workdir; + if (git_repository_index__weakptr(&info.index, repo) < 0) + giterr_clear(); /* no error even if there is no index */ info.files = files; + error = git_path_walk_up(&dir, workdir, push_one_attr, &info); if (error < 0) goto cleanup; if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { - error = push_attrs( + error = push_attr_file( repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file); if (error < 0) goto cleanup; } - error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); - if (!error) - error = push_attrs(repo, files, NULL, dir.ptr); - else if (error == GIT_ENOTFOUND) - error = 0; + if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { + error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); + if (!error) + error = push_attr_file(repo, files, NULL, dir.ptr); + else if (error == GIT_ENOTFOUND) + error = 0; + } cleanup: if (error < 0) |
