-#include "common.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "attr.h"
+
#include "repository.h"
-#include "fileops.h"
+#include "sysdir.h"
#include "config.h"
-#include "attr.h"
+#include "attr_file.h"
#include "ignore.h"
#include "git2/oid.h"
#include <ctype.h>
-GIT__USE_STRMAP;
-
const char *git_attr__true = "[internal]__TRUE__";
const char *git_attr__false = "[internal]__FALSE__";
const char *git_attr__unset = "[internal]__UNSET__";
-git_attr_t git_attr_value(const char *attr)
+git_attr_value_t git_attr_value(const char *attr)
{
if (attr == NULL || attr == git_attr__unset)
- return GIT_ATTR_UNSPECIFIED_T;
+ return GIT_ATTR_VALUE_UNSPECIFIED;
if (attr == git_attr__true)
- return GIT_ATTR_TRUE_T;
+ return GIT_ATTR_VALUE_TRUE;
if (attr == git_attr__false)
- return GIT_ATTR_FALSE_T;
+ return GIT_ATTR_VALUE_FALSE;
- return GIT_ATTR_VALUE_T;
+ return GIT_ATTR_VALUE_STRING;
}
static int collect_attr_files(
git_repository *repo,
- uint32_t flags,
+ git_attr_session *attr_session,
+ git_attr_options *opts,
const char *path,
git_vector *files);
+static void release_attr_files(git_vector *files);
-int git_attr_get(
+int git_attr_get_ext(
const char **value,
git_repository *repo,
- uint32_t flags,
+ git_attr_options *opts,
const char *pathname,
const char *name)
{
git_attr_file *file;
git_attr_name attr;
git_attr_rule *rule;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
+
+ GIT_ASSERT_ARG(value);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
*value = NULL;
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
goto cleanup;
+ memset(&attr, 0, sizeof(attr));
attr.name = name;
attr.name_hash = git_attr_file__name_hash(name);
}
cleanup:
- git_vector_free(&files);
+ release_attr_files(&files);
git_attr_path__free(&path);
return error;
}
+int git_attr_get(
+ const char **value,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ const char *name)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_get_ext(value, repo, &opts, pathname, name);
+}
+
typedef struct {
git_attr_name name;
git_attr_assignment *found;
} attr_get_many_info;
-int git_attr_get_many(
+int git_attr_get_many_with_session(
const char **values,
git_repository *repo,
- uint32_t flags,
+ git_attr_session *attr_session,
+ git_attr_options *opts,
const char *pathname,
size_t num_attr,
const char **names)
git_attr_rule *rule;
attr_get_many_info *info = NULL;
size_t num_found = 0;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
+
+ if (!num_attr)
+ return 0;
+
+ GIT_ASSERT_ARG(values);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(pathname);
+ GIT_ASSERT_ARG(names);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
+
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
goto cleanup;
info = git__calloc(num_attr, sizeof(attr_get_many_info));
- GITERR_CHECK_ALLOC(info);
+ GIT_ERROR_CHECK_ALLOC(info);
git_vector_foreach(&files, i, file) {
}
cleanup:
- git_vector_free(&files);
+ release_attr_files(&files);
git_attr_path__free(&path);
git__free(info);
return error;
}
+int git_attr_get_many(
+ const char **values,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_get_many_with_session(
+ values, repo, NULL, &opts, pathname, num_attr, names);
+}
+
+int git_attr_get_many_ext(
+ const char **values,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
+{
+ return git_attr_get_many_with_session(
+ values, repo, NULL, opts, pathname, num_attr, names);
+}
int git_attr_foreach(
git_repository *repo,
const char *pathname,
int (*callback)(const char *name, const char *value, void *payload),
void *payload)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
+}
+
+int git_attr_foreach_ext(
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ int (*callback)(const char *name, const char *value, void *payload),
+ void *payload)
{
int error;
git_attr_path path;
git_attr_rule *rule;
git_attr_assignment *assign;
git_strmap *seen = NULL;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(callback);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
+
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
+ (error = git_strmap_new(&seen)) < 0)
goto cleanup;
- seen = git_strmap_alloc();
- GITERR_CHECK_ALLOC(seen);
-
git_vector_foreach(&files, i, file) {
git_attr_file__foreach_matching_rule(file, &path, j, rule) {
if (git_strmap_exists(seen, assign->name))
continue;
- git_strmap_insert(seen, assign->name, assign, error);
- if (error < 0)
+ if ((error = git_strmap_set(seen, assign->name, assign)) < 0)
goto cleanup;
error = callback(assign->name, assign->value, payload);
if (error) {
- giterr_set_after_callback(error);
+ git_error_set_after_callback(error);
goto cleanup;
}
}
cleanup:
git_strmap_free(seen);
- git_vector_free(&files);
+ release_attr_files(&files);
git_attr_path__free(&path);
return error;
}
-
-int git_attr_add_macro(
+static int preload_attr_source(
git_repository *repo,
- const char *name,
- const char *values)
+ git_attr_session *attr_session,
+ git_attr_file_source *source)
{
int error;
- git_attr_rule *macro = NULL;
- git_pool *pool;
-
- if (git_attr_cache__init(repo) < 0)
- return -1;
-
- macro = git__calloc(1, sizeof(git_attr_rule));
- GITERR_CHECK_ALLOC(macro);
-
- pool = &git_repository_attr_cache(repo)->pool;
-
- macro->match.pattern = git_pool_strdup(pool, name);
- GITERR_CHECK_ALLOC(macro->match.pattern);
+ git_attr_file *preload = NULL;
- macro->match.length = strlen(macro->match.pattern);
- macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
+ if (!source)
+ return 0;
- error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values);
+ error = git_attr_cache__get(&preload, repo, attr_session, source,
+ git_attr_file__parse_buffer, true);
if (!error)
- error = git_attr_cache__insert_macro(repo, macro);
-
- if (error < 0)
- git_attr_rule__free(macro);
+ git_attr_file__free(preload);
return error;
}
-bool git_attr_cache__is_cached(
- git_repository *repo, git_attr_file_source source, const char *path)
+GIT_INLINE(int) preload_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ const char *base,
+ const char *filename)
{
- 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;
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
- 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));
+ if (!filename)
+ return 0;
- git_buf_free(&cache_key);
+ source.base = base;
+ source.filename = filename;
- return rval;
+ return preload_attr_source(repo, attr_session, &source);
}
-static int load_attr_file(
- const char **data,
- git_futils_filestamp *stamp,
- const char *filename)
+static int system_attr_file(
+ git_buf *out,
+ git_attr_session *attr_session)
{
int error;
- git_buf content = GIT_BUF_INIT;
- error = git_futils_filestamp_check(stamp, filename);
- if (error < 0)
+ if (!attr_session) {
+ error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM);
+
+ if (error == GIT_ENOTFOUND)
+ git_error_clear();
+
return error;
+ }
- /* if error == 0, then file is up to date. By returning GIT_ENOTFOUND,
- * we tell the caller not to reparse this file...
- */
- if (!error)
- return GIT_ENOTFOUND;
+ if (!attr_session->init_sysdir) {
+ error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM);
- error = git_futils_readbuffer(&content, filename);
- if (error < 0) {
- /* convert error into ENOTFOUND so failed permissions / invalid
- * file type don't actually stop the operation in progress.
- */
- return GIT_ENOTFOUND;
+ if (error == GIT_ENOTFOUND)
+ git_error_clear();
+ else if (error)
+ return error;
- /* TODO: once warnings are available, issue a warning callback */
+ attr_session->init_sysdir = 1;
}
- *data = git_buf_detach(&content);
+ if (attr_session->sysdir.size == 0)
+ return GIT_ENOTFOUND;
+ /* We can safely provide a git_buf with no allocation (asize == 0) to
+ * a consumer. This allows them to treat this as a regular `git_buf`,
+ * but their call to `git_buf_dispose` will not attempt to free it.
+ */
+ git_buf_attach_notowned(
+ out, attr_session->sysdir.ptr, attr_session->sysdir.size);
return 0;
}
-static int load_attr_blob_from_index(
- const char **content,
- git_blob **blob,
+static int attr_setup(
git_repository *repo,
- const git_oid *old_oid,
- const char *relfile)
+ git_attr_session *attr_session,
+ git_attr_options *opts)
{
- int error;
- size_t pos;
- git_index *index;
- const git_index_entry *entry;
-
- if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
- (error = git_index_find(&pos, index, relfile)) < 0)
- return error;
-
- entry = git_index_get_byindex(index, pos);
+ git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT;
+ git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_HEAD, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL };
+ git_index *idx = NULL;
+ const char *workdir;
+ int error = 0;
- if (old_oid && git_oid__cmp(old_oid, &entry->id) == 0)
- return GIT_ENOTFOUND;
+ if (attr_session && attr_session->init_setup)
+ return 0;
- if ((error = git_blob_lookup(blob, repo, &entry->id)) < 0)
+ if ((error = git_attr_cache__init(repo)) < 0)
return error;
- *content = git_blob_rawcontent(*blob);
- return 0;
-}
+ /*
+ * Preload attribute files that could contain macros so the
+ * definitions will be available for later file parsing.
+ */
-static int load_attr_from_cache(
- git_attr_file **file,
- git_attr_cache *cache,
- git_attr_file_source source,
- const char *relative_path)
-{
- git_buf cache_key = GIT_BUF_INIT;
- khiter_t cache_pos;
+ if ((error = system_attr_file(&system, attr_session)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
- *file = NULL;
+ error = 0;
+ }
- if (!cache || !cache->files)
- return 0;
+ if ((error = preload_attr_file(repo, attr_session, NULL,
+ git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
+ goto out;
- if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0)
- return -1;
+ if ((error = git_repository_item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
- cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);
+ error = 0;
+ }
- git_buf_free(&cache_key);
+ if ((workdir = git_repository_workdir(repo)) != NULL &&
+ (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0)
+ goto out;
- if (git_strmap_valid_index(cache->files, cache_pos))
- *file = git_strmap_value_at(cache->files, cache_pos);
+ if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
+ (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
+ goto out;
- return 0;
-}
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
+ (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
+ goto out;
-int git_attr_cache__internal_file(
- git_repository *repo,
- const char *filename,
- git_attr_file **file)
-{
- int error = 0;
- git_attr_cache *cache = git_repository_attr_cache(repo);
- khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) {
+#ifndef GIT_DEPRECATE_HARD
+ if (opts->commit_id)
+ commit_source.commit_id = opts->commit_id;
+ else
+#endif
+ commit_source.commit_id = &opts->attr_commit_id;
- if (git_strmap_valid_index(cache->files, cache_pos)) {
- *file = git_strmap_value_at(cache->files, cache_pos);
- return 0;
+ if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0)
+ goto out;
}
- if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)
- return -1;
+ if (attr_session)
+ attr_session->init_setup = 1;
- git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
- if (error > 0)
- error = 0;
+out:
+ git_buf_dispose(&system);
+ git_buf_dispose(&info);
return error;
}
-int git_attr_cache__push_file(
+int git_attr_add_macro(
git_repository *repo,
- const char *base,
- const char *filename,
- git_attr_file_source source,
- git_attr_file_parser parse,
- void* parsedata,
- git_vector *stack)
+ const char *name,
+ const char *values)
{
- 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;
- git_blob *blob = NULL;
- git_futils_filestamp stamp;
-
- 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;
- }
-
- relfile = filename;
- if (workdir && git__prefixcmp(relfile, workdir) == 0)
- relfile += strlen(workdir);
-
- /* check cache */
- if (load_attr_from_cache(&file, cache, source, relfile) < 0)
- return -1;
-
- /* if not in cache, load data, parse, and cache */
-
- if (source == GIT_ATTR_FILE_FROM_FILE) {
- git_futils_filestamp_set(
- &stamp, file ? &file->cache_data.stamp : NULL);
-
- error = load_attr_file(&content, &stamp, filename);
- } else {
- error = load_attr_blob_from_index(&content, &blob,
- repo, file ? &file->cache_data.oid : NULL, relfile);
- }
+ int error;
+ git_attr_rule *macro = NULL;
+ git_pool *pool;
- if (error) {
- /* not finding a file is not an error for this function */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
- goto finish;
- }
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
- /* if we got here, we have to parse and/or reparse the file */
- if (file)
- git_attr_file__clear_rules(file);
- else {
- error = git_attr_file__new(&file, source, relfile, &cache->pool);
- if (error < 0)
- goto finish;
- }
+ if ((error = git_attr_cache__init(repo)) < 0)
+ return error;
- if (parse && (error = parse(repo, parsedata, content, file)) < 0)
- goto finish;
+ macro = git__calloc(1, sizeof(git_attr_rule));
+ GIT_ERROR_CHECK_ALLOC(macro);
- git_strmap_insert(cache->files, file->key, file, error); //-V595
- if (error > 0)
- error = 0;
+ pool = &git_repository_attr_cache(repo)->pool;
- /* remember "cache buster" file signature */
- if (blob)
- git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
- else
- git_futils_filestamp_set(&file->cache_data.stamp, &stamp);
+ macro->match.pattern = git_pool_strdup(pool, name);
+ GIT_ERROR_CHECK_ALLOC(macro->match.pattern);
-finish:
- /* push file onto vector if we found one*/
- if (!error && file != NULL)
- error = git_vector_insert(stack, file);
+ macro->match.length = strlen(macro->match.pattern);
+ macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
- if (error != 0)
- git_attr_file__free(file);
+ error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values);
- if (blob)
- git_blob_free(blob);
- else
- git__free((void *)content);
+ if (!error)
+ error = git_attr_cache__insert_macro(repo, macro);
- git_buf_free(&path);
+ if (error < 0)
+ git_attr_rule__free(macro);
return error;
}
-#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,NULL,(S))
-
typedef struct {
git_repository *repo;
- uint32_t flags;
+ git_attr_session *attr_session;
+ git_attr_options *opts;
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)
+static int attr_decide_sources(
+ uint32_t flags,
+ bool has_wd,
+ bool has_index,
+ git_attr_file_source_t *srcs)
{
int count = 0;
switch (flags & 0x03) {
case GIT_ATTR_CHECK_FILE_THEN_INDEX:
if (has_wd)
- srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
if (has_index)
- srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
break;
case GIT_ATTR_CHECK_INDEX_THEN_FILE:
if (has_index)
- srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
if (has_wd)
- srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
break;
case GIT_ATTR_CHECK_INDEX_ONLY:
if (has_index)
- srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
break;
}
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_HEAD;
+
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
+
return count;
}
-static int push_one_attr(void *ref, git_buf *path)
+static int push_attr_source(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_vector *list,
+ git_attr_file_source *source,
+ bool allow_macros)
+{
+ int error = 0;
+ git_attr_file *file = NULL;
+
+ error = git_attr_cache__get(&file, repo, attr_session,
+ source,
+ git_attr_file__parse_buffer,
+ allow_macros);
+
+ if (error < 0)
+ return error;
+
+ if (file != NULL) {
+ if ((error = git_vector_insert(list, file)) < 0)
+ git_attr_file__free(file);
+ }
+
+ return error;
+}
+
+GIT_INLINE(int) push_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_vector *list,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
+ return push_attr_source(repo, attr_session, list, &source, true);
+}
+
+static int push_one_attr(void *ref, const char *path)
{
- int error = 0, n_src, i;
attr_walk_up_info *info = (attr_walk_up_info *)ref;
- git_attr_file_source src[2];
+ git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES];
+ int error = 0, n_src, i;
+ bool allow_macros;
+
+ n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
+ info->workdir != NULL,
+ info->index != NULL,
+ src);
- n_src = git_attr_cache__decide_sources(
- info->flags, info->workdir != NULL, info->index != NULL, src);
+ allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
- 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, NULL, info->files);
+ for (i = 0; !error && i < n_src; ++i) {
+ git_attr_file_source source = { src[i], path, GIT_ATTR_FILE };
+
+ if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts) {
+#ifndef GIT_DEPRECATE_HARD
+ if (info->opts->commit_id)
+ source.commit_id = info->opts->commit_id;
+ else
+#endif
+ source.commit_id = &info->opts->attr_commit_id;
+ }
+
+ error = push_attr_source(info->repo, info->attr_session, info->files,
+ &source, allow_macros);
+ }
return error;
}
+static void release_attr_files(git_vector *files)
+{
+ size_t i;
+ git_attr_file *file;
+
+ git_vector_foreach(files, i, file) {
+ git_attr_file__free(file);
+ files->contents[i] = NULL;
+ }
+ git_vector_free(files);
+}
+
static int collect_attr_files(
git_repository *repo,
- uint32_t flags,
+ git_attr_session *attr_session,
+ git_attr_options *opts,
const char *path,
git_vector *files)
{
- int error;
- git_buf dir = GIT_BUF_INIT;
+ int error = 0;
+ git_buf dir = GIT_BUF_INIT, attrfile = GIT_BUF_INIT;
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL };
- if (git_attr_cache__init(repo) < 0 ||
- git_vector_init(files, 4, NULL) < 0)
- return -1;
+ GIT_ASSERT(!git_path_is_absolute(path));
+
+ if ((error = attr_setup(repo, attr_session, opts)) < 0)
+ return error;
/* Resolve path in a non-bare repo */
- if (workdir != NULL)
- error = git_path_find_dir(&dir, path, workdir);
- else
+ if (workdir != NULL) {
+ if (!(error = git_repository_workdir_path(&dir, repo, path)))
+ error = git_path_find_dir(&dir);
+ }
+ else {
error = git_path_dirname_r(&dir, path);
+ }
+
if (error < 0)
goto cleanup;
* - $GIT_PREFIX/etc/gitattributes
*/
- error = push_attr_file(
- repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
- if (error < 0)
- goto cleanup;
+ if ((error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto cleanup;
+ }
- info.repo = repo;
- info.flags = flags;
+ info.repo = repo;
+ info.attr_session = attr_session;
+ info.opts = opts;
info.workdir = workdir;
if (git_repository_index__weakptr(&info.index, repo) < 0)
- giterr_clear(); /* no error even if there is no index */
+ git_error_clear(); /* no error even if there is no index */
info.files = files;
- error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
+ if (!strcmp(dir.ptr, "."))
+ error = push_one_attr(&info, "");
+ else
+ 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_attr_file(
- repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
+ error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
if (error < 0)
goto cleanup;
}
- if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
- error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
+ if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
+ error = system_attr_file(&dir, attr_session);
+
if (!error)
- error = push_attr_file(repo, files, NULL, dir.ptr);
- else if (error == GIT_ENOTFOUND) {
- giterr_clear();
+ error = push_attr_file(repo, attr_session, files, NULL, dir.ptr);
+ else if (error == GIT_ENOTFOUND)
error = 0;
- }
}
cleanup:
if (error < 0)
- git_vector_free(files);
- git_buf_free(&dir);
-
- return error;
-}
-
-static int attr_cache__lookup_path(
- char **out, git_config *cfg, const char *key, const char *fallback)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
- const git_config_entry *entry = NULL;
-
- *out = NULL;
-
- if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0)
- return error;
-
- if (entry) {
- const char *cfgval = entry->value;
-
- /* expand leading ~/ as needed */
- if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
- !git_futils_find_global_file(&buf, &cfgval[2]))
- *out = git_buf_detach(&buf);
- else if (cfgval)
- *out = git__strdup(cfgval);
-
- }
- else if (!git_futils_find_xdg_file(&buf, fallback))
- *out = git_buf_detach(&buf);
-
- git_buf_free(&buf);
+ release_attr_files(files);
+ git_buf_dispose(&attrfile);
+ git_buf_dispose(&dir);
return error;
}
-
-int git_attr_cache__init(git_repository *repo)
-{
- int ret;
- git_attr_cache *cache = git_repository_attr_cache(repo);
- git_config *cfg;
-
- if (cache->initialized)
- return 0;
-
- /* cache config settings for attributes and ignores */
- if (git_repository_config__weakptr(&cfg, repo) < 0)
- return -1;
-
- ret = attr_cache__lookup_path(
- &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
- if (ret < 0)
- return ret;
-
- ret = attr_cache__lookup_path(
- &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
- if (ret < 0)
- return ret;
-
- /* allocate hashtable for attribute and ignore file contents */
- if (cache->files == NULL) {
- cache->files = git_strmap_alloc();
- GITERR_CHECK_ALLOC(cache->files);
- }
-
- /* allocate hashtable for attribute macros */
- if (cache->macros == NULL) {
- cache->macros = git_strmap_alloc();
- GITERR_CHECK_ALLOC(cache->macros);
- }
-
- /* allocate string pool */
- if (git_pool_init(&cache->pool, 1, 0) < 0)
- return -1;
-
- cache->initialized = 1;
-
- /* insert default macros */
- return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
-}
-
-void git_attr_cache_flush(
- git_repository *repo)
-{
- git_attr_cache *cache;
-
- if (!repo)
- return;
-
- cache = git_repository_attr_cache(repo);
-
- if (cache->files != NULL) {
- git_attr_file *file;
-
- git_strmap_foreach_value(cache->files, file, {
- git_attr_file__free(file);
- });
-
- git_strmap_free(cache->files);
- }
-
- if (cache->macros != NULL) {
- git_attr_rule *rule;
-
- git_strmap_foreach_value(cache->macros, rule, {
- git_attr_rule__free(rule);
- });
-
- git_strmap_free(cache->macros);
- }
-
- git_pool_clear(&cache->pool);
-
- git__free(cache->cfg_attr_file);
- cache->cfg_attr_file = NULL;
-
- git__free(cache->cfg_excl_file);
- cache->cfg_excl_file = NULL;
-
- cache->initialized = 0;
-}
-
-int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
-{
- git_strmap *macros = git_repository_attr_cache(repo)->macros;
- int error;
-
- /* TODO: generate warning log if (macro->assigns.length == 0) */
- if (macro->assigns.length == 0)
- return 0;
-
- git_strmap_insert(macros, macro->match.pattern, macro, error);
- return (error < 0) ? -1 : 0;
-}
-
-git_attr_rule *git_attr_cache__lookup_macro(
- git_repository *repo, const char *name)
-{
- git_strmap *macros = git_repository_attr_cache(repo)->macros;
- khiter_t pos;
-
- pos = git_strmap_lookup_index(macros, name);
-
- if (!git_strmap_valid_index(macros, pos))
- return NULL;
-
- return (git_attr_rule *)git_strmap_value_at(macros, pos);
-}
-