Commit 3779a047 by Edward Thomson

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.
parent 1cd863fd
...@@ -135,6 +135,19 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr); ...@@ -135,6 +135,19 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3) #define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
/** /**
* An options structure for querying attributes.
*/
typedef struct {
unsigned int version;
/** A combination of GIT_ATTR_CHECK flags */
unsigned int flags;
} git_attr_options;
#define GIT_ATTR_OPTIONS_VERSION 1
#define GIT_ATTR_OPTIONS_INIT {GIT_ATTR_OPTIONS_VERSION}
/**
* Look up the value of one git attribute for path. * Look up the value of one git attribute for path.
* *
* @param value_out Output of the value of the attribute. Use the GIT_ATTR_... * @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
...@@ -157,6 +170,28 @@ GIT_EXTERN(int) git_attr_get( ...@@ -157,6 +170,28 @@ GIT_EXTERN(int) git_attr_get(
const char *name); const char *name);
/** /**
* Look up the value of one git attribute for path with extended options.
*
* @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
* macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
* use the string value for attributes set to a value. You
* should NOT modify or free this value.
* @param repo The repository containing the path.
* @param opts The `git_attr_options` to use when querying these attributes.
* @param path The path to check for attributes. Relative paths are
* interpreted relative to the repo root. The file does
* not have to exist, but if it does not, then it will be
* treated as a plain file (not a directory).
* @param name The name of the attribute to look up.
*/
GIT_EXTERN(int) git_attr_get_ext(
const char **value_out,
git_repository *repo,
git_attr_options *opts,
const char *path,
const char *name);
/**
* Look up a list of git attributes for path. * Look up a list of git attributes for path.
* *
* Use this if you have a known list of attributes that you want to * Use this if you have a known list of attributes that you want to
...@@ -194,6 +229,30 @@ GIT_EXTERN(int) git_attr_get_many( ...@@ -194,6 +229,30 @@ GIT_EXTERN(int) git_attr_get_many(
const char **names); const char **names);
/** /**
* Look up a list of git attributes for path with extended options.
*
* @param values_out An array of num_attr entries that will have string
* pointers written into it for the values of the attributes.
* You should not modify or free the values that are written
* into this array (although of course, you should free the
* array itself if you allocated it).
* @param repo The repository containing the path.
* @param opts The `git_attr_options` to use when querying these attributes.
* @param path The path inside the repo to check attributes. This
* does not have to exist, but if it does not, then
* it will be treated as a plain file (i.e. not a directory).
* @param num_attr The number of attributes being looked up
* @param names An array of num_attr strings containing attribute names.
*/
GIT_EXTERN(int) git_attr_get_many_ext(
const char **values_out,
git_repository *repo,
git_attr_options *opts,
const char *path,
size_t num_attr,
const char **names);
/**
* The callback used with git_attr_foreach. * The callback used with git_attr_foreach.
* *
* This callback will be invoked only once per attribute name, even if there * This callback will be invoked only once per attribute name, even if there
...@@ -232,6 +291,26 @@ GIT_EXTERN(int) git_attr_foreach( ...@@ -232,6 +291,26 @@ GIT_EXTERN(int) git_attr_foreach(
void *payload); void *payload);
/** /**
* Loop over all the git attributes for a path with extended options.
*
* @param repo The repository containing the path.
* @param opts The `git_attr_options` to use when querying these attributes.
* @param path Path inside the repo to check attributes. This does not have
* to exist, but if it does not, then it will be treated as a
* plain file (i.e. not a directory).
* @param callback Function to invoke on each attribute name and value.
* See git_attr_foreach_cb.
* @param payload Passed on as extra parameter to callback function.
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_attr_foreach_ext(
git_repository *repo,
git_attr_options *opts,
const char *path,
git_attr_foreach_cb callback,
void *payload);
/**
* Flush the gitattributes cache. * Flush the gitattributes cache.
* *
* Call this if you have reason to believe that the attributes files on * Call this if you have reason to believe that the attributes files on
......
...@@ -36,16 +36,16 @@ git_attr_value_t git_attr_value(const char *attr) ...@@ -36,16 +36,16 @@ git_attr_value_t git_attr_value(const char *attr)
static int collect_attr_files( static int collect_attr_files(
git_repository *repo, git_repository *repo,
git_attr_session *attr_session, git_attr_session *attr_session,
uint32_t flags, git_attr_options *opts,
const char *path, const char *path,
git_vector *files); git_vector *files);
static void release_attr_files(git_vector *files); static void release_attr_files(git_vector *files);
int git_attr_get( int git_attr_get_ext(
const char **value, const char **value,
git_repository *repo, git_repository *repo,
uint32_t flags, git_attr_options *opts,
const char *pathname, const char *pathname,
const char *name) const char *name)
{ {
...@@ -61,6 +61,7 @@ int git_attr_get( ...@@ -61,6 +61,7 @@ int git_attr_get(
GIT_ASSERT_ARG(value); GIT_ASSERT_ARG(value);
GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(name); GIT_ASSERT_ARG(name);
GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
*value = NULL; *value = NULL;
...@@ -70,7 +71,7 @@ int git_attr_get( ...@@ -70,7 +71,7 @@ int git_attr_get(
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0) if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1; return -1;
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0) if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
goto cleanup; goto cleanup;
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
...@@ -97,6 +98,20 @@ cleanup: ...@@ -97,6 +98,20 @@ cleanup:
return error; return error;
} }
int git_attr_get(
const char **value,
git_repository *repo,
uint32_t flags,
const char *pathname,
const char *name)
{
git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
opts.flags = flags;
return git_attr_get_ext(value, repo, &opts, pathname, name);
}
typedef struct { typedef struct {
git_attr_name name; git_attr_name name;
...@@ -107,7 +122,7 @@ int git_attr_get_many_with_session( ...@@ -107,7 +122,7 @@ int git_attr_get_many_with_session(
const char **values, const char **values,
git_repository *repo, git_repository *repo,
git_attr_session *attr_session, git_attr_session *attr_session,
uint32_t flags, git_attr_options *opts,
const char *pathname, const char *pathname,
size_t num_attr, size_t num_attr,
const char **names) const char **names)
...@@ -129,6 +144,7 @@ int git_attr_get_many_with_session( ...@@ -129,6 +144,7 @@ int git_attr_get_many_with_session(
GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(pathname); GIT_ASSERT_ARG(pathname);
GIT_ASSERT_ARG(names); GIT_ASSERT_ARG(names);
GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
if (git_repository_is_bare(repo)) if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE; dir_flag = GIT_DIR_FLAG_FALSE;
...@@ -136,7 +152,7 @@ int git_attr_get_many_with_session( ...@@ -136,7 +152,7 @@ int git_attr_get_many_with_session(
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0) if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1; return -1;
if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0) if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
goto cleanup; goto cleanup;
info = git__calloc(num_attr, sizeof(attr_get_many_info)); info = git__calloc(num_attr, sizeof(attr_get_many_info));
...@@ -190,8 +206,24 @@ int git_attr_get_many( ...@@ -190,8 +206,24 @@ int git_attr_get_many(
size_t num_attr, size_t num_attr,
const char **names) const char **names)
{ {
git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
opts.flags = flags;
return git_attr_get_many_with_session(
values, repo, NULL, &opts, pathname, num_attr, names);
}
int git_attr_get_many_ext(
const char **values,
git_repository *repo,
git_attr_options *opts,
const char *pathname,
size_t num_attr,
const char **names)
{
return git_attr_get_many_with_session( return git_attr_get_many_with_session(
values, repo, NULL, flags, pathname, num_attr, names); values, repo, NULL, opts, pathname, num_attr, names);
} }
int git_attr_foreach( int git_attr_foreach(
...@@ -201,6 +233,20 @@ int git_attr_foreach( ...@@ -201,6 +233,20 @@ int git_attr_foreach(
int (*callback)(const char *name, const char *value, void *payload), int (*callback)(const char *name, const char *value, void *payload),
void *payload) void *payload)
{ {
git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
opts.flags = flags;
return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
}
int git_attr_foreach_ext(
git_repository *repo,
git_attr_options *opts,
const char *pathname,
int (*callback)(const char *name, const char *value, void *payload),
void *payload)
{
int error; int error;
git_attr_path path; git_attr_path path;
git_vector files = GIT_VECTOR_INIT; git_vector files = GIT_VECTOR_INIT;
...@@ -213,6 +259,7 @@ int git_attr_foreach( ...@@ -213,6 +259,7 @@ int git_attr_foreach(
GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(callback); GIT_ASSERT_ARG(callback);
GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
if (git_repository_is_bare(repo)) if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE; dir_flag = GIT_DIR_FLAG_FALSE;
...@@ -220,7 +267,7 @@ int git_attr_foreach( ...@@ -220,7 +267,7 @@ int git_attr_foreach(
if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0) if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1; return -1;
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 || if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
(error = git_strmap_new(&seen)) < 0) (error = git_strmap_new(&seen)) < 0)
goto cleanup; goto cleanup;
...@@ -331,7 +378,7 @@ static int system_attr_file( ...@@ -331,7 +378,7 @@ static int system_attr_file(
static int attr_setup( static int attr_setup(
git_repository *repo, git_repository *repo,
git_attr_session *attr_session, git_attr_session *attr_session,
uint32_t flags) git_attr_options *opts)
{ {
git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT; 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 index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE };
...@@ -379,7 +426,7 @@ static int attr_setup( ...@@ -379,7 +426,7 @@ static int attr_setup(
(error = preload_attr_source(repo, attr_session, &index_source)) < 0) (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
goto out; goto out;
if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 && if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
(error = preload_attr_source(repo, attr_session, &head_source)) < 0) (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
goto out; goto out;
...@@ -545,7 +592,7 @@ static void release_attr_files(git_vector *files) ...@@ -545,7 +592,7 @@ static void release_attr_files(git_vector *files)
static int collect_attr_files( static int collect_attr_files(
git_repository *repo, git_repository *repo,
git_attr_session *attr_session, git_attr_session *attr_session,
uint32_t flags, git_attr_options *opts,
const char *path, const char *path,
git_vector *files) git_vector *files)
{ {
...@@ -554,7 +601,7 @@ static int collect_attr_files( ...@@ -554,7 +601,7 @@ static int collect_attr_files(
const char *workdir = git_repository_workdir(repo); const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL }; attr_walk_up_info info = { NULL };
if ((error = attr_setup(repo, attr_session, flags)) < 0) if ((error = attr_setup(repo, attr_session, opts)) < 0)
return error; return error;
/* Resolve path in a non-bare repo */ /* Resolve path in a non-bare repo */
...@@ -584,7 +631,7 @@ static int collect_attr_files( ...@@ -584,7 +631,7 @@ static int collect_attr_files(
info.repo = repo; info.repo = repo;
info.attr_session = attr_session; info.attr_session = attr_session;
info.flags = flags; info.flags = opts ? opts->flags : 0;
info.workdir = workdir; info.workdir = workdir;
if (git_repository_index__weakptr(&info.index, repo) < 0) if (git_repository_index__weakptr(&info.index, repo) < 0)
git_error_clear(); /* no error even if there is no index */ git_error_clear(); /* no error even if there is no index */
...@@ -604,7 +651,7 @@ static int collect_attr_files( ...@@ -604,7 +651,7 @@ static int collect_attr_files(
goto cleanup; goto cleanup;
} }
if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
error = system_attr_file(&dir, attr_session); error = system_attr_file(&dir, attr_session);
if (!error) if (!error)
......
...@@ -136,7 +136,7 @@ extern int git_attr_get_many_with_session( ...@@ -136,7 +136,7 @@ extern int git_attr_get_many_with_session(
const char **values_out, const char **values_out,
git_repository *repo, git_repository *repo,
git_attr_session *attr_session, git_attr_session *attr_session,
uint32_t flags, git_attr_options *opts,
const char *path, const char *path,
size_t num_attr, size_t num_attr,
const char **names); const char **names);
......
...@@ -430,20 +430,20 @@ static int filter_list_check_attributes( ...@@ -430,20 +430,20 @@ static int filter_list_check_attributes(
const git_filter_source *src) const git_filter_source *src)
{ {
const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
uint32_t flags = 0; git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT;
size_t i; size_t i;
int error; int error;
GIT_ERROR_CHECK_ALLOC(strs); GIT_ERROR_CHECK_ALLOC(strs);
if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
flags |= GIT_ATTR_CHECK_NO_SYSTEM; attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM;
if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0) if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
flags |= GIT_ATTR_CHECK_INCLUDE_HEAD; attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
error = git_attr_get_many_with_session( error = git_attr_get_many_with_session(
strs, repo, attr_session, flags, src->path, fdef->nattrs, fdef->attrs); strs, repo, attr_session, &attr_opts, src->path, fdef->nattrs, fdef->attrs);
/* if no values were found but no matches are needed, it's okay! */ /* if no values were found but no matches are needed, it's okay! */
if (error == GIT_ENOTFOUND && !fdef->nmatches) { if (error == GIT_ENOTFOUND && !fdef->nmatches) {
......
...@@ -341,7 +341,7 @@ void test_attr_repo__sysdir_with_session(void) ...@@ -341,7 +341,7 @@ void test_attr_repo__sysdir_with_session(void)
g_repo = cl_git_sandbox_reopen(); g_repo = cl_git_sandbox_reopen();
cl_git_pass(git_attr_session__init(&session, g_repo)); cl_git_pass(git_attr_session__init(&session, g_repo));
cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, 0, "file", ARRAY_SIZE(attrs), attrs)); cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, NULL, "file", ARRAY_SIZE(attrs), attrs));
cl_assert_equal_s(values[0], "1"); cl_assert_equal_s(values[0], "1");
cl_assert_equal_s(values[1], "2"); cl_assert_equal_s(values[1], "2");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment