summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2021-07-22 15:29:46 -0400
committerEdward Thomson <ethomson@edwardthomson.com>2021-07-22 16:22:28 -0400
commit0bd547a8bee02bf984ea5c7acdc8172044fcb3a4 (patch)
tree4dc86b3fbc275413fccd28405016bbae472adf05
parent093d579f7be5636c52290a517eee1eebb7471e31 (diff)
downloadlibgit2-0bd547a8bee02bf984ea5c7acdc8172044fcb3a4.tar.gz
attr: introduce GIT_ATTR_CHECK_INCLUDE_COMMIT
Introduce `GIT_ATTR_CHECK_INCLUDE_COMMIT`, which like 4fd5748 allows attribute information to be read from files in the repository. 4fd5748 always reads the information from HEAD, while `GIT_ATTR_CHECK_INCLUDE_COMMIT` allows users to provide the commit to read the attributes from.
-rw-r--r--include/git2/attr.h10
-rw-r--r--src/attr.c29
-rw-r--r--src/attr_file.c34
-rw-r--r--src/attr_file.h8
-rw-r--r--src/attrcache.c7
5 files changed, 71 insertions, 17 deletions
diff --git a/include/git2/attr.h b/include/git2/attr.h
index 306893c67..62c2ed6e7 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -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
diff --git a/src/attr.c b/src/attr.c
index a2d78e68b..03b720c5a 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -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 */
diff --git a/src/attr_file.c b/src/attr_file.c
index 3b8965a10..f8627381c 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -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 ((error = git_repository_head_tree(&tree, repo)) < 0)
- goto cleanup;
+ 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;
diff --git a/src/attr_file.h b/src/attr_file.h
index 5c8a41291..16e33caf1 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -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);
diff --git a/src/attrcache.c b/src/attrcache.c
index f07712735..7fe2bfbdb 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -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;
}