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); ...@@ -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 * Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes
* from a `.gitattributes` file in the repository at the HEAD revision. * 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_NO_SYSTEM (1 << 2)
#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3) #define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
#define GIT_ATTR_CHECK_INCLUDE_COMMIT (1 << 4)
/** /**
* An options structure for querying attributes. * An options structure for querying attributes.
...@@ -142,6 +146,12 @@ typedef struct { ...@@ -142,6 +146,12 @@ typedef struct {
/** A combination of GIT_ATTR_CHECK flags */ /** A combination of GIT_ATTR_CHECK flags */
unsigned int 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; } git_attr_options;
#define GIT_ATTR_OPTIONS_VERSION 1 #define GIT_ATTR_OPTIONS_VERSION 1
......
...@@ -381,8 +381,9 @@ static int attr_setup( ...@@ -381,8 +381,9 @@ static int attr_setup(
git_attr_options *opts) git_attr_options *opts)
{ {
git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT; 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 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 }; 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; git_index *idx = NULL;
const char *workdir; const char *workdir;
int error = 0; int error = 0;
...@@ -430,6 +431,13 @@ static int attr_setup( ...@@ -430,6 +431,13 @@ static int attr_setup(
(error = preload_attr_source(repo, attr_session, &head_source)) < 0) (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
goto out; 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) if (attr_session)
attr_session->init_setup = 1; attr_session->init_setup = 1;
...@@ -480,7 +488,7 @@ int git_attr_add_macro( ...@@ -480,7 +488,7 @@ int git_attr_add_macro(
typedef struct { typedef struct {
git_repository *repo; git_repository *repo;
git_attr_session *attr_session; git_attr_session *attr_session;
uint32_t flags; git_attr_options *opts;
const char *workdir; const char *workdir;
git_index *index; git_index *index;
git_vector *files; git_vector *files;
...@@ -513,7 +521,8 @@ static int attr_decide_sources( ...@@ -513,7 +521,8 @@ static int attr_decide_sources(
break; 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; srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
return count; return count;
...@@ -563,13 +572,19 @@ static int push_one_attr(void *ref, const char *path) ...@@ -563,13 +572,19 @@ static int push_one_attr(void *ref, const char *path)
int error = 0, n_src, i; int error = 0, n_src, i;
bool allow_macros; bool allow_macros;
n_src = attr_decide_sources( n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
info->flags, info->workdir != NULL, info->index != NULL, src); info->workdir != NULL,
info->index != NULL,
src);
allow_macros = info->workdir ? !strcmp(info->workdir, path) : false; allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
for (i = 0; !error && i < n_src; ++i) { for (i = 0; !error && i < n_src; ++i) {
git_attr_file_source source = { src[i], path, GIT_ATTR_FILE }; 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, error = push_attr_source(info->repo, info->attr_session, info->files,
&source, allow_macros); &source, allow_macros);
} }
...@@ -631,7 +646,7 @@ static int collect_attr_files( ...@@ -631,7 +646,7 @@ static int collect_attr_files(
info.repo = repo; info.repo = repo;
info.attr_session = attr_session; info.attr_session = attr_session;
info.flags = opts ? opts->flags : 0; info.opts = opts;
info.workdir = workdir; info.workdir = workdir;
if (git_repository_index__weakptr(&info.index, repo) < 0) if (git_repository_index__weakptr(&info.index, repo) < 0)
git_error_clear(); /* no error even if there is no index */ git_error_clear(); /* no error even if there is no index */
......
...@@ -113,6 +113,7 @@ int git_attr_file__load( ...@@ -113,6 +113,7 @@ int git_attr_file__load(
bool allow_macros) bool allow_macros)
{ {
int error = 0; int error = 0;
git_commit *commit = NULL;
git_tree *tree = NULL; git_tree *tree = NULL;
git_tree_entry *tree_entry = NULL; git_tree_entry *tree_entry = NULL;
git_blob *blob = NULL; git_blob *blob = NULL;
...@@ -163,8 +164,14 @@ int git_attr_file__load( ...@@ -163,8 +164,14 @@ int git_attr_file__load(
break; break;
} }
case GIT_ATTR_FILE_SOURCE_COMMIT: { case GIT_ATTR_FILE_SOURCE_COMMIT: {
if ((error = git_repository_head_tree(&tree, repo)) < 0) if (source->commit_id) {
goto cleanup; 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) { if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) {
/* /*
...@@ -239,6 +246,7 @@ cleanup: ...@@ -239,6 +246,7 @@ cleanup:
git_blob_free(blob); git_blob_free(blob);
git_tree_entry_free(tree_entry); git_tree_entry_free(tree_entry);
git_tree_free(tree); git_tree_free(tree);
git_commit_free(commit);
git_buf_dispose(&content); git_buf_dispose(&content);
return error; return error;
...@@ -247,7 +255,8 @@ cleanup: ...@@ -247,7 +255,8 @@ cleanup:
int git_attr_file__out_of_date( int git_attr_file__out_of_date(
git_repository *repo, git_repository *repo,
git_attr_session *attr_session, git_attr_session *attr_session,
git_attr_file *file) git_attr_file *file,
git_attr_file_source *source)
{ {
if (!file) if (!file)
return 1; return 1;
...@@ -280,13 +289,26 @@ int git_attr_file__out_of_date( ...@@ -280,13 +289,26 @@ int git_attr_file__out_of_date(
} }
case GIT_ATTR_FILE_SOURCE_COMMIT: { case GIT_ATTR_FILE_SOURCE_COMMIT: {
git_tree *tree; git_tree *tree = NULL;
int error; 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; 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); git_tree_free(tree);
return error; return error;
......
...@@ -55,6 +55,12 @@ typedef struct { ...@@ -55,6 +55,12 @@ typedef struct {
*/ */
const char *base; const char *base;
const char *filename; 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; } git_attr_file_source;
extern const char *git_attr__true; extern const char *git_attr__true;
...@@ -171,7 +177,7 @@ int git_attr_file__load_standalone( ...@@ -171,7 +177,7 @@ int git_attr_file__load_standalone(
git_attr_file **out, const char *path); git_attr_file **out, const char *path);
int git_attr_file__out_of_date( 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( int git_attr_file__parse_buffer(
git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros); git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros);
......
...@@ -224,16 +224,17 @@ int git_attr_cache__get( ...@@ -224,16 +224,17 @@ int git_attr_cache__get(
return error; return error;
/* load file if we don't have one or if existing one is out of date */ /* 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, error = git_attr_file__load(&updated, repo, attr_session,
entry, source, parser, entry, source, parser,
allow_macros); allow_macros);
/* if we loaded the file, insert into and/or update cache */ /* if we loaded the file, insert into and/or update cache */
if (updated) { if (updated) {
if ((error = attr_cache_upsert(cache, updated)) < 0) if ((error = attr_cache_upsert(cache, updated)) < 0) {
git_attr_file__free(updated); git_attr_file__free(updated);
else { } else {
git_attr_file__free(file); /* offset incref from lookup */ git_attr_file__free(file); /* offset incref from lookup */
file = updated; 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