mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
common: support custom repository extensions
Allow users to specify additional repository extensions that they want to support. For example, callers can specify that they support `preciousObjects` and then may open repositories that support `extensions.preciousObjects`. Similarly, callers may opt out of supporting extensions that the library itself supports.
This commit is contained in:
@@ -209,7 +209,9 @@ typedef enum {
|
||||
GIT_OPT_GET_MWINDOW_FILE_LIMIT,
|
||||
GIT_OPT_SET_MWINDOW_FILE_LIMIT,
|
||||
GIT_OPT_SET_ODB_PACKED_PRIORITY,
|
||||
GIT_OPT_SET_ODB_LOOSE_PRIORITY
|
||||
GIT_OPT_SET_ODB_LOOSE_PRIORITY,
|
||||
GIT_OPT_GET_EXTENSIONS,
|
||||
GIT_OPT_SET_EXTENSIONS
|
||||
} git_libgit2_opt_t;
|
||||
|
||||
/**
|
||||
@@ -431,6 +433,22 @@ typedef enum {
|
||||
* > Override the default priority of the loose ODB backend which
|
||||
* > is added when default backends are assigned to a repository
|
||||
*
|
||||
* opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out)
|
||||
* > Returns the list of git extensions that are supported. This
|
||||
* > is the list of built-in extensions supported by libgit2 and
|
||||
* > custom extensions that have been added with
|
||||
* > `GIT_OPT_SET_EXTENSIONS`. Extensions that have been negated
|
||||
* > will not be returned. The returned list should be released
|
||||
* > with `git_strarray_dispose`.
|
||||
*
|
||||
* opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len)
|
||||
* > Set that the given git extensions are supported by the caller.
|
||||
* > Extensions supported by libgit2 may be negated by prefixing
|
||||
* > them with a `!`. For example: setting extensions to
|
||||
* > { "!noop", "newext" } indicates that the caller does not want
|
||||
* > to support repositories with the `noop` extension but does want
|
||||
* > to support repositories with the `newext` extension.
|
||||
*
|
||||
* @param option Option key
|
||||
* @param ... value to set the option
|
||||
* @return 0 on success, <0 on failure
|
||||
|
||||
@@ -52,6 +52,7 @@ static void libgit2_settings_global_shutdown(void)
|
||||
{
|
||||
git__free(git__user_agent);
|
||||
git__free(git__ssl_ciphers);
|
||||
git_repository__free_extensions();
|
||||
}
|
||||
|
||||
static int git_libgit2_settings_global_init(void)
|
||||
@@ -367,6 +368,28 @@ int git_libgit2_opts(int key, ...)
|
||||
git_odb__loose_priority = va_arg(ap, int);
|
||||
break;
|
||||
|
||||
case GIT_OPT_SET_EXTENSIONS:
|
||||
{
|
||||
const char **extensions = va_arg(ap, const char **);
|
||||
size_t len = va_arg(ap, size_t);
|
||||
error = git_repository__set_extensions(extensions, len);
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OPT_GET_EXTENSIONS:
|
||||
{
|
||||
git_strarray *out = va_arg(ap, git_strarray *);
|
||||
char **extensions;
|
||||
size_t len;
|
||||
|
||||
if ((error = git_repository__extensions(&extensions, &len)) < 0)
|
||||
break;
|
||||
|
||||
out->strings = extensions;
|
||||
out->count = len;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
git_error_set(GIT_ERROR_INVALID, "invalid option key");
|
||||
error = -1;
|
||||
|
||||
115
src/repository.c
115
src/repository.c
@@ -1427,15 +1427,60 @@ static int check_repositoryformatversion(int *version, git_config *config)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *builtin_extensions[] = {
|
||||
"noop"
|
||||
};
|
||||
|
||||
static git_vector user_extensions = GIT_VECTOR_INIT;
|
||||
|
||||
static int check_valid_extension(const git_config_entry *entry, void *payload)
|
||||
{
|
||||
git_buf cfg = GIT_BUF_INIT;
|
||||
bool reject;
|
||||
const char *extension;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
GIT_UNUSED(payload);
|
||||
|
||||
if (!strcmp(entry->name, "extensions.noop"))
|
||||
return 0;
|
||||
git_vector_foreach (&user_extensions, i, extension) {
|
||||
git_buf_clear(&cfg);
|
||||
|
||||
/*
|
||||
* Users can specify that they don't want to support an
|
||||
* extension with a '!' prefix.
|
||||
*/
|
||||
if ((reject = (extension[0] == '!')) == true)
|
||||
extension = &extension[1];
|
||||
|
||||
if ((error = git_buf_printf(&cfg, "extensions.%s", extension)) < 0)
|
||||
goto done;
|
||||
|
||||
if (strcmp(entry->name, cfg.ptr) == 0) {
|
||||
if (reject)
|
||||
goto fail;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
|
||||
extension = builtin_extensions[i];
|
||||
|
||||
if ((error = git_buf_printf(&cfg, "extensions.%s", extension)) < 0)
|
||||
goto done;
|
||||
|
||||
if (strcmp(entry->name, cfg.ptr) == 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
fail:
|
||||
git_error_set(GIT_ERROR_REPOSITORY, "unsupported extension name %s", entry->name);
|
||||
return -1;
|
||||
error = -1;
|
||||
|
||||
done:
|
||||
git_buf_dispose(&cfg);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int check_extensions(git_config *config, int version)
|
||||
@@ -1446,6 +1491,70 @@ static int check_extensions(git_config *config, int version)
|
||||
return git_config_foreach_match(config, "^extensions\\.", check_valid_extension, NULL);
|
||||
}
|
||||
|
||||
int git_repository__extensions(char ***out, size_t *out_len)
|
||||
{
|
||||
git_vector extensions;
|
||||
const char *builtin, *user;
|
||||
char *extension;
|
||||
size_t i, j;
|
||||
|
||||
if (git_vector_init(&extensions, 8, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
|
||||
bool match = false;
|
||||
|
||||
builtin = builtin_extensions[i];
|
||||
|
||||
git_vector_foreach (&user_extensions, j, user) {
|
||||
if (user[0] == '!' && strcmp(builtin, &user[1]) == 0) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
continue;
|
||||
|
||||
if ((extension = git__strdup(builtin)) == NULL ||
|
||||
git_vector_insert(&extensions, extension) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_vector_foreach (&user_extensions, i, user) {
|
||||
if (user[0] == '!')
|
||||
continue;
|
||||
|
||||
if ((extension = git__strdup(user)) == NULL ||
|
||||
git_vector_insert(&extensions, extension) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out = (char **)git_vector_detach(out_len, NULL, &extensions);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_repository__set_extensions(const char **extensions, size_t len)
|
||||
{
|
||||
char *extension;
|
||||
size_t i;
|
||||
|
||||
git_repository__free_extensions();
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((extension = git__strdup(extensions[i])) == NULL ||
|
||||
git_vector_insert(&user_extensions, extension) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_repository__free_extensions(void)
|
||||
{
|
||||
git_vector_free_deep(&user_extensions);
|
||||
}
|
||||
|
||||
int git_repository_create_head(const char *git_dir, const char *ref_name)
|
||||
{
|
||||
git_buf ref_path = GIT_BUF_INIT;
|
||||
|
||||
@@ -249,4 +249,8 @@ int git_repository_initialbranch(git_buf *out, git_repository *repo);
|
||||
*/
|
||||
int git_repository_workdir_path(git_buf *out, git_repository *repo, const char *path);
|
||||
|
||||
int git_repository__extensions(char ***out, size_t *out_len);
|
||||
int git_repository__set_extensions(const char **extensions, size_t len);
|
||||
void git_repository__free_extensions(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "cache.h"
|
||||
|
||||
void test_core_opts__cleanup(void)
|
||||
{
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0));
|
||||
}
|
||||
|
||||
void test_core_opts__readwrite(void)
|
||||
{
|
||||
size_t old_val = 0;
|
||||
@@ -23,3 +28,44 @@ void test_core_opts__invalid_option(void)
|
||||
cl_git_fail(git_libgit2_opts(-1, "foobar"));
|
||||
}
|
||||
|
||||
void test_core_opts__extensions_query(void)
|
||||
{
|
||||
git_strarray out = { 0 };
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
|
||||
|
||||
cl_assert_equal_sz(out.count, 1);
|
||||
cl_assert_equal_s("noop", out.strings[0]);
|
||||
|
||||
git_strarray_dispose(&out);
|
||||
}
|
||||
|
||||
void test_core_opts__extensions_add(void)
|
||||
{
|
||||
const char *in[] = { "foo" };
|
||||
git_strarray out = { 0 };
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
|
||||
|
||||
cl_assert_equal_sz(out.count, 2);
|
||||
cl_assert_equal_s("noop", out.strings[0]);
|
||||
cl_assert_equal_s("foo", out.strings[1]);
|
||||
|
||||
git_strarray_dispose(&out);
|
||||
}
|
||||
|
||||
void test_core_opts__extensions_remove(void)
|
||||
{
|
||||
const char *in[] = { "bar", "!negate", "!noop", "baz" };
|
||||
git_strarray out = { 0 };
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
|
||||
|
||||
cl_assert_equal_sz(out.count, 2);
|
||||
cl_assert_equal_s("bar", out.strings[0]);
|
||||
cl_assert_equal_s("baz", out.strings[1]);
|
||||
|
||||
git_strarray_dispose(&out);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ void test_repo_extensions__initialize(void)
|
||||
void test_repo_extensions__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0));
|
||||
}
|
||||
|
||||
void test_repo_extensions__builtin(void)
|
||||
@@ -33,6 +34,19 @@ void test_repo_extensions__builtin(void)
|
||||
git_repository_free(extended);
|
||||
}
|
||||
|
||||
void test_repo_extensions__negate_builtin(void)
|
||||
{
|
||||
const char *in[] = { "foo", "!noop", "baz" };
|
||||
git_repository *extended;
|
||||
|
||||
cl_repo_set_string(repo, "extensions.noop", "foobar");
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
|
||||
|
||||
cl_git_fail(git_repository_open(&extended, "empty_bare.git"));
|
||||
git_repository_free(extended);
|
||||
}
|
||||
|
||||
void test_repo_extensions__unsupported(void)
|
||||
{
|
||||
git_repository *extended = NULL;
|
||||
@@ -42,3 +56,17 @@ void test_repo_extensions__unsupported(void)
|
||||
cl_git_fail(git_repository_open(&extended, "empty_bare.git"));
|
||||
git_repository_free(extended);
|
||||
}
|
||||
|
||||
void test_repo_extensions__adds_extension(void)
|
||||
{
|
||||
const char *in[] = { "foo", "!noop", "newextension", "baz" };
|
||||
git_repository *extended;
|
||||
|
||||
cl_repo_set_string(repo, "extensions.newextension", "foobar");
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
|
||||
|
||||
cl_git_pass(git_repository_open(&extended, "empty_bare.git"));
|
||||
cl_assert(git_repository_path(extended) != NULL);
|
||||
cl_assert(git__suffixcmp(git_repository_path(extended), "/") == 0);
|
||||
git_repository_free(extended);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user