Skip to content

Commit 4fd5748

Browse files
committed
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 parent a5392ea commit 4fd5748

File tree

4 files changed

+65
-9
lines changed

4 files changed

+65
-9
lines changed

include/git2/attr.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,20 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
119119
#define GIT_ATTR_CHECK_INDEX_ONLY 2
120120

121121
/**
122-
* Check attribute flags: Using the system attributes file.
122+
* Check attribute flags: controlling extended attribute behavior.
123123
*
124124
* Normally, attribute checks include looking in the /etc (or system
125125
* equivalent) directory for a `gitattributes` file. Passing this
126126
* flag will cause attribute checks to ignore that file.
127+
* equivalent) directory for a `gitattributes` file. Passing the
128+
* `GIT_ATTR_CHECK_NO_SYSTEM` flag will cause attribute checks to
129+
* ignore that file.
130+
*
131+
* Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes
132+
* from a `.gitattributes` file in the repository at the HEAD revision.
127133
*/
128-
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
134+
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
135+
#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
129136

130137
/**
131138
* Look up the value of one git attribute for path.

src/attr.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,10 @@ static int system_attr_file(
305305
return 0;
306306
}
307307

308-
static int attr_setup(git_repository *repo, git_attr_session *attr_session)
308+
static int attr_setup(
309+
git_repository *repo,
310+
git_attr_session *attr_session,
311+
uint32_t flags)
309312
{
310313
git_buf path = GIT_BUF_INIT;
311314
git_index *idx = NULL;
@@ -352,6 +355,11 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session)
352355
NULL, GIT_ATTR_FILE, true)) < 0)
353356
goto out;
354357

358+
if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 &&
359+
(error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_HEAD,
360+
NULL, GIT_ATTR_FILE, true)) < 0)
361+
goto out;
362+
355363
if (attr_session)
356364
attr_session->init_setup = 1;
357365

@@ -428,6 +436,9 @@ static int attr_decide_sources(
428436
break;
429437
}
430438

439+
if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
440+
srcs[count++] = GIT_ATTR_FILE__FROM_HEAD;
441+
431442
return count;
432443
}
433444

@@ -460,7 +471,7 @@ static int push_attr_file(
460471
static int push_one_attr(void *ref, const char *path)
461472
{
462473
attr_walk_up_info *info = (attr_walk_up_info *)ref;
463-
git_attr_file_source src[2];
474+
git_attr_file_source src[GIT_ATTR_FILE_NUM_SOURCES];
464475
int error = 0, n_src, i;
465476
bool allow_macros;
466477

@@ -499,7 +510,7 @@ static int collect_attr_files(
499510
const char *workdir = git_repository_workdir(repo);
500511
attr_walk_up_info info = { NULL };
501512

502-
if ((error = attr_setup(repo, attr_session)) < 0)
513+
if ((error = attr_setup(repo, attr_session, flags)) < 0)
503514
return error;
504515

505516
/* Resolve path in a non-bare repo */

src/attr_file.c

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ int git_attr_file__load(
109109
bool allow_macros)
110110
{
111111
int error = 0;
112+
git_tree *tree = NULL;
113+
git_tree_entry *tree_entry = NULL;
112114
git_blob *blob = NULL;
113115
git_buf content = GIT_BUF_INIT;
114116
const char *content_str;
@@ -117,6 +119,8 @@ int git_attr_file__load(
117119
bool nonexistent = false;
118120
int bom_offset;
119121
git_bom_t bom;
122+
git_oid id;
123+
git_off_t blobsize;
120124

121125
*out = NULL;
122126

@@ -125,9 +129,6 @@ int git_attr_file__load(
125129
/* in-memory attribute file doesn't need data */
126130
break;
127131
case GIT_ATTR_FILE__FROM_INDEX: {
128-
git_oid id;
129-
git_off_t blobsize;
130-
131132
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
132133
(error = git_blob_lookup(&blob, repo, &id)) < 0)
133134
return error;
@@ -157,6 +158,25 @@ int git_attr_file__load(
157158

158159
break;
159160
}
161+
case GIT_ATTR_FILE__FROM_HEAD: {
162+
if ((error = git_repository_head_tree(&tree, repo)) < 0 ||
163+
(error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0 ||
164+
(error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
165+
goto cleanup;
166+
167+
/*
168+
* Do not assume that data straight from the ODB is NULL-terminated;
169+
* copy the contents of a file to a buffer to work on.
170+
*/
171+
blobsize = git_blob_rawsize(blob);
172+
173+
GIT_ERROR_CHECK_BLOBSIZE(blobsize);
174+
if ((error = git_buf_put(&content,
175+
git_blob_rawcontent(blob), (size_t)blobsize)) < 0)
176+
goto cleanup;
177+
178+
break;
179+
}
160180
default:
161181
git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source);
162182
return -1;
@@ -188,6 +208,8 @@ int git_attr_file__load(
188208
file->nonexistent = 1;
189209
else if (source == GIT_ATTR_FILE__FROM_INDEX)
190210
git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
211+
else if (source == GIT_ATTR_FILE__FROM_HEAD)
212+
git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
191213
else if (source == GIT_ATTR_FILE__FROM_FILE)
192214
git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
193215
/* else always cacheable */
@@ -196,6 +218,8 @@ int git_attr_file__load(
196218

197219
cleanup:
198220
git_blob_free(blob);
221+
git_tree_entry_free(tree_entry);
222+
git_tree_free(tree);
199223
git_buf_dispose(&content);
200224

201225
return error;
@@ -236,6 +260,19 @@ int git_attr_file__out_of_date(
236260
return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
237261
}
238262

263+
case GIT_ATTR_FILE__FROM_HEAD: {
264+
git_tree *tree;
265+
int error;
266+
267+
if ((error = git_repository_head_tree(&tree, repo)) < 0)
268+
return error;
269+
270+
error = git_oid__cmp(&file->cache_data.oid, git_tree_id(tree));
271+
272+
git_tree_free(tree);
273+
return error;
274+
}
275+
239276
default:
240277
git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source);
241278
return -1;

src/attr_file.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ typedef enum {
4040
GIT_ATTR_FILE__IN_MEMORY = 0,
4141
GIT_ATTR_FILE__FROM_FILE = 1,
4242
GIT_ATTR_FILE__FROM_INDEX = 2,
43+
GIT_ATTR_FILE__FROM_HEAD = 3,
4344

44-
GIT_ATTR_FILE_NUM_SOURCES = 3
45+
GIT_ATTR_FILE_NUM_SOURCES = 4
4546
} git_attr_file_source;
4647

4748
extern const char *git_attr__true;

0 commit comments

Comments
 (0)