Commit 0bd547a8 by Edward Thomson

attr: introduce GIT_ATTR_CHECK_INCLUDE_COMMIT

Introduce `GIT_ATTR_CHECK_INCLUDE_COMMIT`, which like 4fd5748c allows
attribute information to be read from files in the repository.  4fd5748c
always reads the information from HEAD, while
`GIT_ATTR_CHECK_INCLUDE_COMMIT` allows users to provide the commit to
read the attributes from.
parent 093d579f
......@@ -130,9 +130,13 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
*
* Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes
* from a `.gitattributes` file in the repository at the HEAD revision.
*
* Passing the `GIT_ATTR_CHECK_INCLUDE_COMMIT` flag will use attributes
* from a `.gitattributes` file in a specific commit.
*/
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
#define GIT_ATTR_CHECK_INCLUDE_COMMIT (1 << 4)
/**
* An options structure for querying attributes.
......@@ -142,6 +146,12 @@ typedef struct {
/** A combination of GIT_ATTR_CHECK flags */
unsigned int flags;
/**
* The commit to load attributes from, when
* `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified.
*/
git_oid *commit_id;
} git_attr_options;
#define GIT_ATTR_OPTIONS_VERSION 1
......
......@@ -381,8 +381,9 @@ static int attr_setup(
git_attr_options *opts)
{
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 };
git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE };
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_COMMIT, 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;
......@@ -430,6 +431,13 @@ static int attr_setup(
(error = preload_attr_source(repo, attr_session, &head_source)) < 0)
goto out;
if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) {
commit_source.commit_id = opts->commit_id;
if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0)
goto out;
}
if (attr_session)
attr_session->init_setup = 1;
......@@ -480,7 +488,7 @@ int git_attr_add_macro(
typedef struct {
git_repository *repo;
git_attr_session *attr_session;
uint32_t flags;
git_attr_options *opts;
const char *workdir;
git_index *index;
git_vector *files;
......@@ -513,7 +521,8 @@ static int attr_decide_sources(
break;
}
if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 ||
(flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)
srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
return count;
......@@ -563,13 +572,19 @@ static int push_one_attr(void *ref, const char *path)
int error = 0, n_src, i;
bool allow_macros;
n_src = attr_decide_sources(
info->flags, info->workdir != NULL, info->index != NULL, src);
n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
info->workdir != NULL,
info->index != NULL,
src);
allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
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)
source.commit_id = info->opts->commit_id;
error = push_attr_source(info->repo, info->attr_session, info->files,
&source, allow_macros);
}
......@@ -631,7 +646,7 @@ static int collect_attr_files(
info.repo = repo;
info.attr_session = attr_session;
info.flags = opts ? opts->flags : 0;
info.opts = opts;
info.workdir = workdir;
if (git_repository_index__weakptr(&info.index, repo) < 0)
git_error_clear(); /* no error even if there is no index */
......
......@@ -113,6 +113,7 @@ int git_attr_file__load(
bool allow_macros)
{
int error = 0;
git_commit *commit = NULL;
git_tree *tree = NULL;
git_tree_entry *tree_entry = NULL;
git_blob *blob = NULL;
......@@ -163,8 +164,14 @@ int git_attr_file__load(
break;
}
case GIT_ATTR_FILE_SOURCE_COMMIT: {
if (source->commit_id) {
if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0 ||
(error = git_commit_tree(&tree, commit)) < 0)
goto cleanup;
} else {
if ((error = git_repository_head_tree(&tree, repo)) < 0)
goto cleanup;
}
if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) {
/*
......@@ -239,6 +246,7 @@ cleanup:
git_blob_free(blob);
git_tree_entry_free(tree_entry);
git_tree_free(tree);
git_commit_free(commit);
git_buf_dispose(&content);
return error;
......@@ -247,7 +255,8 @@ cleanup:
int git_attr_file__out_of_date(
git_repository *repo,
git_attr_session *attr_session,
git_attr_file *file)
git_attr_file *file,
git_attr_file_source *source)
{
if (!file)
return 1;
......@@ -280,13 +289,26 @@ int git_attr_file__out_of_date(
}
case GIT_ATTR_FILE_SOURCE_COMMIT: {
git_tree *tree;
git_tree *tree = NULL;
int error;
if ((error = git_repository_head_tree(&tree, repo)) < 0)
if (source->commit_id) {
git_commit *commit = NULL;
if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0)
return error;
error = git_commit_tree(&tree, commit);
git_commit_free(commit);
} else {
error = git_repository_head_tree(&tree, repo);
}
if (error < 0)
return error;
error = git_oid__cmp(&file->cache_data.oid, git_tree_id(tree));
error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0);
git_tree_free(tree);
return error;
......
......@@ -55,6 +55,12 @@ typedef struct {
*/
const char *base;
const char *filename;
/*
* The commit ID when the given source type is a commit (or NULL
* for the repository's HEAD commit.)
*/
git_oid *commit_id;
} git_attr_file_source;
extern const char *git_attr__true;
......@@ -171,7 +177,7 @@ int git_attr_file__load_standalone(
git_attr_file **out, const char *path);
int git_attr_file__out_of_date(
git_repository *repo, git_attr_session *session, git_attr_file *file);
git_repository *repo, git_attr_session *session, git_attr_file *file, git_attr_file_source *source);
int git_attr_file__parse_buffer(
git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros);
......
......@@ -224,16 +224,17 @@ int git_attr_cache__get(
return error;
/* load file if we don't have one or if existing one is out of date */
if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0)
if (!file ||
(error = git_attr_file__out_of_date(repo, attr_session, file, source)) > 0)
error = git_attr_file__load(&updated, repo, attr_session,
entry, source, parser,
allow_macros);
/* if we loaded the file, insert into and/or update cache */
if (updated) {
if ((error = attr_cache_upsert(cache, updated)) < 0)
if ((error = attr_cache_upsert(cache, updated)) < 0) {
git_attr_file__free(updated);
else {
} else {
git_attr_file__free(file); /* offset incref from lookup */
file = updated;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment