Skip to content

Commit 6de4808

Browse files
authored
Merge pull request libgit2#5189 from libgit2/ethomson/attrs_from_head
Optionally read `.gitattributes` from HEAD
2 parents aaa48d0 + cdbbb36 commit 6de4808

File tree

86 files changed

+459
-31
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+459
-31
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.

include/git2/blob.h

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,39 @@ GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
9696
*/
9797
GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob);
9898

99+
/**
100+
* Flags to control the functionality of `git_blob_filter`.
101+
*/
102+
typedef enum {
103+
/** When set, filters will not be applied to binary files. */
104+
GIT_BLOB_FILTER_CHECK_FOR_BINARY = (1 << 0),
105+
106+
/**
107+
* When set, filters will not load configuration from the
108+
* system-wide `gitattributes` in `/etc` (or system equivalent).
109+
*/
110+
GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES = (1 << 1),
111+
112+
/**
113+
* When set, filters will be loaded from a `.gitattributes` file
114+
* in the HEAD commit.
115+
*/
116+
GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD = (1 << 2),
117+
} git_blob_filter_flag_t;
118+
119+
/**
120+
* The options used when applying filter options to a file.
121+
*/
122+
typedef struct {
123+
int version;
124+
125+
/** Flags to control the filtering process */
126+
git_blob_filter_flag_t flags;
127+
} git_blob_filter_options;
128+
129+
#define GIT_BLOB_FILTER_OPTIONS_VERSION 1
130+
#define GIT_BLOB_FILTER_OPTIONS_INIT {GIT_BLOB_FILTER_OPTIONS_VERSION, GIT_BLOB_FILTER_CHECK_FOR_BINARY}
131+
99132
/**
100133
* Get a buffer with the filtered content of a blob.
101134
*
@@ -115,15 +148,14 @@ GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob);
115148
* @param out The git_buf to be filled in
116149
* @param blob Pointer to the blob
117150
* @param as_path Path used for file attribute lookups, etc.
118-
* @param check_for_binary_data Should this test if blob content contains
119-
* NUL bytes / looks like binary data before applying filters?
151+
* @param opts Options to use for filtering the blob
120152
* @return 0 on success or an error code
121153
*/
122-
GIT_EXTERN(int) git_blob_filtered_content(
154+
GIT_EXTERN(int) git_blob_filter(
123155
git_buf *out,
124156
git_blob *blob,
125157
const char *as_path,
126-
int check_for_binary_data);
158+
git_blob_filter_options *opts);
127159

128160
/**
129161
* Read a file from the working folder of a repository

include/git2/deprecated.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ GIT_EXTERN(int) git_blob_create_fromstream_commit(
9090
GIT_EXTERN(int) git_blob_create_frombuffer(
9191
git_oid *id, git_repository *repo, const void *buffer, size_t len);
9292

93+
/** Deprecated in favor of @see git_blob_filter */
94+
GIT_EXTERN(int) git_blob_filtered_content(
95+
git_buf *out,
96+
git_blob *blob,
97+
const char *as_path,
98+
int check_for_binary_data);
99+
93100
/**@}*/
94101

95102
/** @name Deprecated Buffer Functions

include/git2/filter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,15 @@ typedef enum {
4040
*/
4141
typedef enum {
4242
GIT_FILTER_DEFAULT = 0u,
43+
44+
/** Don't error for `safecrlf` violations, allow them to continue. */
4345
GIT_FILTER_ALLOW_UNSAFE = (1u << 0),
46+
47+
/** Don't load `/etc/gitattributes` (or the system equivalent) */
48+
GIT_FILTER_NO_SYSTEM_ATTRIBUTES = (1u << 1),
49+
50+
/** Load attributes from `.gitattributes` in the root of HEAD */
51+
GIT_FILTER_ATTRIBUTES_FROM_HEAD = (1u << 2),
4452
} git_filter_flag_t;
4553

4654
/**

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;

src/blob.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,25 +400,40 @@ int git_blob_is_binary(const git_blob *blob)
400400
return git_buf_text_is_binary(&content);
401401
}
402402

403-
int git_blob_filtered_content(
403+
int git_blob_filter(
404404
git_buf *out,
405405
git_blob *blob,
406406
const char *path,
407-
int check_for_binary_data)
407+
git_blob_filter_options *given_opts)
408408
{
409409
int error = 0;
410410
git_filter_list *fl = NULL;
411+
git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
412+
git_filter_flag_t flags = GIT_FILTER_DEFAULT;
411413

412414
assert(blob && path && out);
413415

414416
git_buf_sanitize(out);
415417

416-
if (check_for_binary_data && git_blob_is_binary(blob))
418+
GIT_ERROR_CHECK_VERSION(
419+
given_opts, GIT_BLOB_FILTER_OPTIONS_VERSION, "git_blob_filter_options");
420+
421+
if (given_opts != NULL)
422+
memcpy(&opts, given_opts, sizeof(git_blob_filter_options));
423+
424+
if ((opts.flags & GIT_BLOB_FILTER_CHECK_FOR_BINARY) != 0 &&
425+
git_blob_is_binary(blob))
417426
return 0;
418427

428+
if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
429+
flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES;
430+
431+
if ((opts.flags & GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD) != 0)
432+
flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD;
433+
419434
if (!(error = git_filter_list_load(
420435
&fl, git_blob_owner(blob), blob, path,
421-
GIT_FILTER_TO_WORKTREE, GIT_FILTER_DEFAULT))) {
436+
GIT_FILTER_TO_WORKTREE, flags))) {
422437

423438
error = git_filter_list_apply_to_blob(out, fl, blob);
424439

@@ -460,3 +475,19 @@ int git_blob_create_fromstream_commit(
460475
{
461476
return git_blob_create_from_stream_commit(out, stream);
462477
}
478+
479+
int git_blob_filtered_content(
480+
git_buf *out,
481+
git_blob *blob,
482+
const char *path,
483+
int check_for_binary_data)
484+
{
485+
git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
486+
487+
if (check_for_binary_data)
488+
opts.flags |= GIT_BLOB_FILTER_CHECK_FOR_BINARY;
489+
else
490+
opts.flags &= ~GIT_BLOB_FILTER_CHECK_FOR_BINARY;
491+
492+
return git_blob_filter(out, blob, path, &opts);
493+
}

src/filter.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,13 +428,21 @@ static int filter_list_check_attributes(
428428
git_filter_def *fdef,
429429
const git_filter_source *src)
430430
{
431-
int error;
432-
size_t i;
433431
const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
432+
uint32_t flags = 0;
433+
size_t i;
434+
int error;
435+
434436
GIT_ERROR_CHECK_ALLOC(strs);
435437

438+
if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
439+
flags |= GIT_ATTR_CHECK_NO_SYSTEM;
440+
441+
if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
442+
flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
443+
436444
error = git_attr_get_many_with_session(
437-
strs, repo, attr_session, 0, src->path, fdef->nattrs, fdef->attrs);
445+
strs, repo, attr_session, flags, src->path, fdef->nattrs, fdef->attrs);
438446

439447
/* if no values were found but no matches are needed, it's okay! */
440448
if (error == GIT_ENOTFOUND && !fdef->nmatches) {

0 commit comments

Comments
 (0)