attr: optionally read attributes from repository When `GIT_ATTR_CHECK_INCLUDE_HEAD` is specified, read `gitattribute` files that are checked into the repository at the HEAD revision.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
diff --git a/include/git2/attr.h b/include/git2/attr.h
index cab9758..72e4380 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -119,13 +119,20 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
#define GIT_ATTR_CHECK_INDEX_ONLY 2
/**
- * Check attribute flags: Using the system attributes file.
+ * Check attribute flags: controlling extended attribute behavior.
*
* Normally, attribute checks include looking in the /etc (or system
* equivalent) directory for a `gitattributes` file. Passing this
* flag will cause attribute checks to ignore that file.
+ * equivalent) directory for a `gitattributes` file. Passing the
+ * `GIT_ATTR_CHECK_NO_SYSTEM` flag will cause attribute checks to
+ * ignore that file.
+ *
+ * Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes
+ * from a `.gitattributes` file in the repository at the HEAD revision.
*/
-#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
+#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
+#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
/**
* Look up the value of one git attribute for path.
diff --git a/src/attr.c b/src/attr.c
index 02a7148..bd517cd 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -305,7 +305,10 @@ static int system_attr_file(
return 0;
}
-static int attr_setup(git_repository *repo, git_attr_session *attr_session)
+static int attr_setup(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ uint32_t flags)
{
git_buf path = GIT_BUF_INIT;
git_index *idx = NULL;
@@ -352,6 +355,11 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session)
NULL, GIT_ATTR_FILE, true)) < 0)
goto out;
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 &&
+ (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_HEAD,
+ NULL, GIT_ATTR_FILE, true)) < 0)
+ goto out;
+
if (attr_session)
attr_session->init_setup = 1;
@@ -428,6 +436,9 @@ static int attr_decide_sources(
break;
}
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
+ srcs[count++] = GIT_ATTR_FILE__FROM_HEAD;
+
return count;
}
@@ -460,7 +471,7 @@ static int push_attr_file(
static int push_one_attr(void *ref, const char *path)
{
attr_walk_up_info *info = (attr_walk_up_info *)ref;
- git_attr_file_source src[2];
+ git_attr_file_source src[GIT_ATTR_FILE_NUM_SOURCES];
int error = 0, n_src, i;
bool allow_macros;
@@ -499,7 +510,7 @@ static int collect_attr_files(
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL };
- if ((error = attr_setup(repo, attr_session)) < 0)
+ if ((error = attr_setup(repo, attr_session, flags)) < 0)
return error;
/* Resolve path in a non-bare repo */
diff --git a/src/attr_file.c b/src/attr_file.c
index f8769c6..a1b4c73 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -109,6 +109,8 @@ int git_attr_file__load(
bool allow_macros)
{
int error = 0;
+ git_tree *tree = NULL;
+ git_tree_entry *tree_entry = NULL;
git_blob *blob = NULL;
git_buf content = GIT_BUF_INIT;
const char *content_str;
@@ -117,6 +119,8 @@ int git_attr_file__load(
bool nonexistent = false;
int bom_offset;
git_bom_t bom;
+ git_oid id;
+ git_off_t blobsize;
*out = NULL;
@@ -125,9 +129,6 @@ int git_attr_file__load(
/* in-memory attribute file doesn't need data */
break;
case GIT_ATTR_FILE__FROM_INDEX: {
- git_oid id;
- git_off_t blobsize;
-
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
(error = git_blob_lookup(&blob, repo, &id)) < 0)
return error;
@@ -157,6 +158,25 @@ int git_attr_file__load(
break;
}
+ case GIT_ATTR_FILE__FROM_HEAD: {
+ if ((error = git_repository_head_tree(&tree, repo)) < 0 ||
+ (error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0 ||
+ (error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
+ goto cleanup;
+
+ /*
+ * Do not assume that data straight from the ODB is NULL-terminated;
+ * copy the contents of a file to a buffer to work on.
+ */
+ blobsize = git_blob_rawsize(blob);
+
+ GIT_ERROR_CHECK_BLOBSIZE(blobsize);
+ if ((error = git_buf_put(&content,
+ git_blob_rawcontent(blob), (size_t)blobsize)) < 0)
+ goto cleanup;
+
+ break;
+ }
default:
git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source);
return -1;
@@ -188,6 +208,8 @@ int git_attr_file__load(
file->nonexistent = 1;
else if (source == GIT_ATTR_FILE__FROM_INDEX)
git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
+ else if (source == GIT_ATTR_FILE__FROM_HEAD)
+ git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
else if (source == GIT_ATTR_FILE__FROM_FILE)
git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
/* else always cacheable */
@@ -196,6 +218,8 @@ int git_attr_file__load(
cleanup:
git_blob_free(blob);
+ git_tree_entry_free(tree_entry);
+ git_tree_free(tree);
git_buf_dispose(&content);
return error;
@@ -236,6 +260,19 @@ int git_attr_file__out_of_date(
return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
}
+ case GIT_ATTR_FILE__FROM_HEAD: {
+ git_tree *tree;
+ int error;
+
+ if ((error = git_repository_head_tree(&tree, repo)) < 0)
+ return error;
+
+ error = git_oid__cmp(&file->cache_data.oid, git_tree_id(tree));
+
+ git_tree_free(tree);
+ return error;
+ }
+
default:
git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source);
return -1;
diff --git a/src/attr_file.h b/src/attr_file.h
index f4f9a09..2b6b1d6 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -40,8 +40,9 @@ typedef enum {
GIT_ATTR_FILE__IN_MEMORY = 0,
GIT_ATTR_FILE__FROM_FILE = 1,
GIT_ATTR_FILE__FROM_INDEX = 2,
+ GIT_ATTR_FILE__FROM_HEAD = 3,
- GIT_ATTR_FILE_NUM_SOURCES = 3
+ GIT_ATTR_FILE_NUM_SOURCES = 4
} git_attr_file_source;
extern const char *git_attr__true;