Skip to content

Commit 3779a04

Browse files
committed
attr: introduce git_attr_options for extended queries
Allow more advanced attribute queries using a `git_attr_options`, and extended functions to use it. Presently there is no additional configuration in a `git_attr_options` beyond the flags, but this is for future growth.
1 parent 1cd863f commit 3779a04

File tree

5 files changed

+146
-20
lines changed

5 files changed

+146
-20
lines changed

include/git2/attr.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,19 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
134134
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
135135
#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
136136

137+
/**
138+
* An options structure for querying attributes.
139+
*/
140+
typedef struct {
141+
unsigned int version;
142+
143+
/** A combination of GIT_ATTR_CHECK flags */
144+
unsigned int flags;
145+
} git_attr_options;
146+
147+
#define GIT_ATTR_OPTIONS_VERSION 1
148+
#define GIT_ATTR_OPTIONS_INIT {GIT_ATTR_OPTIONS_VERSION}
149+
137150
/**
138151
* Look up the value of one git attribute for path.
139152
*
@@ -156,6 +169,28 @@ GIT_EXTERN(int) git_attr_get(
156169
const char *path,
157170
const char *name);
158171

172+
/**
173+
* Look up the value of one git attribute for path with extended options.
174+
*
175+
* @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
176+
* macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
177+
* use the string value for attributes set to a value. You
178+
* should NOT modify or free this value.
179+
* @param repo The repository containing the path.
180+
* @param opts The `git_attr_options` to use when querying these attributes.
181+
* @param path The path to check for attributes. Relative paths are
182+
* interpreted relative to the repo root. The file does
183+
* not have to exist, but if it does not, then it will be
184+
* treated as a plain file (not a directory).
185+
* @param name The name of the attribute to look up.
186+
*/
187+
GIT_EXTERN(int) git_attr_get_ext(
188+
const char **value_out,
189+
git_repository *repo,
190+
git_attr_options *opts,
191+
const char *path,
192+
const char *name);
193+
159194
/**
160195
* Look up a list of git attributes for path.
161196
*
@@ -193,6 +228,30 @@ GIT_EXTERN(int) git_attr_get_many(
193228
size_t num_attr,
194229
const char **names);
195230

231+
/**
232+
* Look up a list of git attributes for path with extended options.
233+
*
234+
* @param values_out An array of num_attr entries that will have string
235+
* pointers written into it for the values of the attributes.
236+
* You should not modify or free the values that are written
237+
* into this array (although of course, you should free the
238+
* array itself if you allocated it).
239+
* @param repo The repository containing the path.
240+
* @param opts The `git_attr_options` to use when querying these attributes.
241+
* @param path The path inside the repo to check attributes. This
242+
* does not have to exist, but if it does not, then
243+
* it will be treated as a plain file (i.e. not a directory).
244+
* @param num_attr The number of attributes being looked up
245+
* @param names An array of num_attr strings containing attribute names.
246+
*/
247+
GIT_EXTERN(int) git_attr_get_many_ext(
248+
const char **values_out,
249+
git_repository *repo,
250+
git_attr_options *opts,
251+
const char *path,
252+
size_t num_attr,
253+
const char **names);
254+
196255
/**
197256
* The callback used with git_attr_foreach.
198257
*
@@ -231,6 +290,26 @@ GIT_EXTERN(int) git_attr_foreach(
231290
git_attr_foreach_cb callback,
232291
void *payload);
233292

293+
/**
294+
* Loop over all the git attributes for a path with extended options.
295+
*
296+
* @param repo The repository containing the path.
297+
* @param opts The `git_attr_options` to use when querying these attributes.
298+
* @param path Path inside the repo to check attributes. This does not have
299+
* to exist, but if it does not, then it will be treated as a
300+
* plain file (i.e. not a directory).
301+
* @param callback Function to invoke on each attribute name and value.
302+
* See git_attr_foreach_cb.
303+
* @param payload Passed on as extra parameter to callback function.
304+
* @return 0 on success, non-zero callback return value, or error code
305+
*/
306+
GIT_EXTERN(int) git_attr_foreach_ext(
307+
git_repository *repo,
308+
git_attr_options *opts,
309+
const char *path,
310+
git_attr_foreach_cb callback,
311+
void *payload);
312+
234313
/**
235314
* Flush the gitattributes cache.
236315
*

src/attr.c

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ git_attr_value_t git_attr_value(const char *attr)
3636
static int collect_attr_files(
3737
git_repository *repo,
3838
git_attr_session *attr_session,
39-
uint32_t flags,
39+
git_attr_options *opts,
4040
const char *path,
4141
git_vector *files);
4242

4343
static void release_attr_files(git_vector *files);
4444

45-
int git_attr_get(
45+
int git_attr_get_ext(
4646
const char **value,
4747
git_repository *repo,
48-
uint32_t flags,
48+
git_attr_options *opts,
4949
const char *pathname,
5050
const char *name)
5151
{
@@ -61,6 +61,7 @@ int git_attr_get(
6161
GIT_ASSERT_ARG(value);
6262
GIT_ASSERT_ARG(repo);
6363
GIT_ASSERT_ARG(name);
64+
GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
6465

6566
*value = NULL;
6667

@@ -70,7 +71,7 @@ int git_attr_get(
7071
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
7172
return -1;
7273

73-
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0)
74+
if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
7475
goto cleanup;
7576

7677
memset(&attr, 0, sizeof(attr));
@@ -97,6 +98,20 @@ int git_attr_get(
9798
return error;
9899
}
99100

101+
int git_attr_get(
102+
const char **value,
103+
git_repository *repo,
104+
uint32_t flags,
105+
const char *pathname,
106+
const char *name)
107+
{
108+
git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
109+
110+
opts.flags = flags;
111+
112+
return git_attr_get_ext(value, repo, &opts, pathname, name);
113+
}
114+
100115

101116
typedef struct {
102117
git_attr_name name;
@@ -107,7 +122,7 @@ int git_attr_get_many_with_session(
107122
const char **values,
108123
git_repository *repo,
109124
git_attr_session *attr_session,
110-
uint32_t flags,
125+
git_attr_options *opts,
111126
const char *pathname,
112127
size_t num_attr,
113128
const char **names)
@@ -129,14 +144,15 @@ int git_attr_get_many_with_session(
129144
GIT_ASSERT_ARG(repo);
130145
GIT_ASSERT_ARG(pathname);
131146
GIT_ASSERT_ARG(names);
147+
GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
132148

133149
if (git_repository_is_bare(repo))
134150
dir_flag = GIT_DIR_FLAG_FALSE;
135151

136152
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
137153
return -1;
138154

139-
if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0)
155+
if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
140156
goto cleanup;
141157

142158
info = git__calloc(num_attr, sizeof(attr_get_many_info));
@@ -189,9 +205,25 @@ int git_attr_get_many(
189205
const char *pathname,
190206
size_t num_attr,
191207
const char **names)
208+
{
209+
git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
210+
211+
opts.flags = flags;
212+
213+
return git_attr_get_many_with_session(
214+
values, repo, NULL, &opts, pathname, num_attr, names);
215+
}
216+
217+
int git_attr_get_many_ext(
218+
const char **values,
219+
git_repository *repo,
220+
git_attr_options *opts,
221+
const char *pathname,
222+
size_t num_attr,
223+
const char **names)
192224
{
193225
return git_attr_get_many_with_session(
194-
values, repo, NULL, flags, pathname, num_attr, names);
226+
values, repo, NULL, opts, pathname, num_attr, names);
195227
}
196228

197229
int git_attr_foreach(
@@ -200,6 +232,20 @@ int git_attr_foreach(
200232
const char *pathname,
201233
int (*callback)(const char *name, const char *value, void *payload),
202234
void *payload)
235+
{
236+
git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
237+
238+
opts.flags = flags;
239+
240+
return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
241+
}
242+
243+
int git_attr_foreach_ext(
244+
git_repository *repo,
245+
git_attr_options *opts,
246+
const char *pathname,
247+
int (*callback)(const char *name, const char *value, void *payload),
248+
void *payload)
203249
{
204250
int error;
205251
git_attr_path path;
@@ -213,14 +259,15 @@ int git_attr_foreach(
213259

214260
GIT_ASSERT_ARG(repo);
215261
GIT_ASSERT_ARG(callback);
262+
GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
216263

217264
if (git_repository_is_bare(repo))
218265
dir_flag = GIT_DIR_FLAG_FALSE;
219266

220267
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
221268
return -1;
222269

223-
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 ||
270+
if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
224271
(error = git_strmap_new(&seen)) < 0)
225272
goto cleanup;
226273

@@ -331,7 +378,7 @@ static int system_attr_file(
331378
static int attr_setup(
332379
git_repository *repo,
333380
git_attr_session *attr_session,
334-
uint32_t flags)
381+
git_attr_options *opts)
335382
{
336383
git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT;
337384
git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE };
@@ -379,7 +426,7 @@ static int attr_setup(
379426
(error = preload_attr_source(repo, attr_session, &index_source)) < 0)
380427
goto out;
381428

382-
if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 &&
429+
if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
383430
(error = preload_attr_source(repo, attr_session, &head_source)) < 0)
384431
goto out;
385432

@@ -545,7 +592,7 @@ static void release_attr_files(git_vector *files)
545592
static int collect_attr_files(
546593
git_repository *repo,
547594
git_attr_session *attr_session,
548-
uint32_t flags,
595+
git_attr_options *opts,
549596
const char *path,
550597
git_vector *files)
551598
{
@@ -554,7 +601,7 @@ static int collect_attr_files(
554601
const char *workdir = git_repository_workdir(repo);
555602
attr_walk_up_info info = { NULL };
556603

557-
if ((error = attr_setup(repo, attr_session, flags)) < 0)
604+
if ((error = attr_setup(repo, attr_session, opts)) < 0)
558605
return error;
559606

560607
/* Resolve path in a non-bare repo */
@@ -584,7 +631,7 @@ static int collect_attr_files(
584631

585632
info.repo = repo;
586633
info.attr_session = attr_session;
587-
info.flags = flags;
634+
info.flags = opts ? opts->flags : 0;
588635
info.workdir = workdir;
589636
if (git_repository_index__weakptr(&info.index, repo) < 0)
590637
git_error_clear(); /* no error even if there is no index */
@@ -604,7 +651,7 @@ static int collect_attr_files(
604651
goto cleanup;
605652
}
606653

607-
if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
654+
if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
608655
error = system_attr_file(&dir, attr_session);
609656

610657
if (!error)

src/attr_file.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ extern int git_attr_get_many_with_session(
136136
const char **values_out,
137137
git_repository *repo,
138138
git_attr_session *attr_session,
139-
uint32_t flags,
139+
git_attr_options *opts,
140140
const char *path,
141141
size_t num_attr,
142142
const char **names);

src/filter.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,20 +430,20 @@ static int filter_list_check_attributes(
430430
const git_filter_source *src)
431431
{
432432
const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
433-
uint32_t flags = 0;
433+
git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT;
434434
size_t i;
435435
int error;
436436

437437
GIT_ERROR_CHECK_ALLOC(strs);
438438

439439
if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
440-
flags |= GIT_ATTR_CHECK_NO_SYSTEM;
440+
attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM;
441441

442442
if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
443-
flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
443+
attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
444444

445445
error = git_attr_get_many_with_session(
446-
strs, repo, attr_session, flags, src->path, fdef->nattrs, fdef->attrs);
446+
strs, repo, attr_session, &attr_opts, src->path, fdef->nattrs, fdef->attrs);
447447

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

tests/attr/repo.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ void test_attr_repo__sysdir_with_session(void)
341341
g_repo = cl_git_sandbox_reopen();
342342

343343
cl_git_pass(git_attr_session__init(&session, g_repo));
344-
cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, 0, "file", ARRAY_SIZE(attrs), attrs));
344+
cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, NULL, "file", ARRAY_SIZE(attrs), attrs));
345345

346346
cl_assert_equal_s(values[0], "1");
347347
cl_assert_equal_s(values[1], "2");

0 commit comments

Comments
 (0)