mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-31 03:36:31 +00:00
Merge remote-tracking branch 'drodriguez/fix-remote-save' into development
This commit is contained in:
commit
653ec420f9
@ -434,6 +434,17 @@ GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_config_delete_entry(git_config *cfg, const char *name);
|
GIT_EXTERN(int) git_config_delete_entry(git_config *cfg, const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes one or several entries from a multivar in the local config file.
|
||||||
|
*
|
||||||
|
* @param cfg where to look for the variables
|
||||||
|
* @param name the variable's name
|
||||||
|
* @param regexp a regular expression to indicate which values to delete
|
||||||
|
*
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform an operation on each config variable.
|
* Perform an operation on each config variable.
|
||||||
*
|
*
|
||||||
|
@ -61,6 +61,7 @@ struct git_config_backend {
|
|||||||
int (*set)(struct git_config_backend *, const char *key, const char *value);
|
int (*set)(struct git_config_backend *, const char *key, const char *value);
|
||||||
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
|
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
|
||||||
int (*del)(struct git_config_backend *, const char *key);
|
int (*del)(struct git_config_backend *, const char *key);
|
||||||
|
int (*del_multivar)(struct git_config_backend *, const char *key, const char *regexp);
|
||||||
int (*iterator)(git_config_iterator **, struct git_config_backend *);
|
int (*iterator)(git_config_iterator **, struct git_config_backend *);
|
||||||
int (*refresh)(struct git_config_backend *);
|
int (*refresh)(struct git_config_backend *);
|
||||||
void (*free)(struct git_config_backend *);
|
void (*free)(struct git_config_backend *);
|
||||||
|
13
src/config.c
13
src/config.c
@ -862,6 +862,19 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
|
|||||||
return file->set_multivar(file, name, regexp, value);
|
return file->set_multivar(file, name, regexp, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
|
||||||
|
{
|
||||||
|
git_config_backend *file;
|
||||||
|
file_internal *internal;
|
||||||
|
|
||||||
|
internal = git_vector_get(&cfg->files, 0);
|
||||||
|
if (!internal || !internal->file)
|
||||||
|
return config_error_nofiles(name);
|
||||||
|
file = internal->file;
|
||||||
|
|
||||||
|
return file->del_multivar(file, name, regexp);
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_next(git_config_entry **entry, git_config_iterator *iter)
|
int git_config_next(git_config_entry **entry, git_config_iterator *iter)
|
||||||
{
|
{
|
||||||
return iter->next(entry, iter);
|
return iter->next(entry, iter);
|
||||||
|
@ -120,6 +120,18 @@ static void cvar_free(cvar_t *var)
|
|||||||
git__free(var);
|
git__free(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cvar_length(cvar_t *var)
|
||||||
|
{
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
while (var) {
|
||||||
|
length += 1;
|
||||||
|
var = var->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_file_normalize_section(char *start, char *end)
|
int git_config_file_normalize_section(char *start, char *end)
|
||||||
{
|
{
|
||||||
char *scan;
|
char *scan;
|
||||||
@ -531,6 +543,83 @@ static int config_delete(git_config_backend *cfg, const char *name)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
|
||||||
|
{
|
||||||
|
cvar_t *var, *prev = NULL, *new_head = NULL;
|
||||||
|
cvar_t **to_delete;
|
||||||
|
int to_delete_idx;
|
||||||
|
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||||
|
char *key;
|
||||||
|
regex_t preg;
|
||||||
|
int result;
|
||||||
|
khiter_t pos;
|
||||||
|
|
||||||
|
if ((result = git_config__normalize_name(name, &key)) < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
pos = git_strmap_lookup_index(b->values, key);
|
||||||
|
|
||||||
|
if (!git_strmap_valid_index(b->values, pos)) {
|
||||||
|
giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
|
||||||
|
git__free(key);
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
var = git_strmap_value_at(b->values, pos);
|
||||||
|
|
||||||
|
result = regcomp(&preg, regexp, REG_EXTENDED);
|
||||||
|
if (result < 0) {
|
||||||
|
git__free(key);
|
||||||
|
giterr_set_regex(&preg, result);
|
||||||
|
regfree(&preg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_delete = git__calloc(cvar_length(var), sizeof(cvar_t *));
|
||||||
|
GITERR_CHECK_ALLOC(to_delete);
|
||||||
|
to_delete_idx = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
cvar_t *var_next = var->next;
|
||||||
|
|
||||||
|
if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
|
||||||
|
// If we are past the head, reattach previous node to next one,
|
||||||
|
// otherwise set the new head for the strmap.
|
||||||
|
if (prev != NULL) {
|
||||||
|
prev->next = var_next;
|
||||||
|
} else {
|
||||||
|
new_head = var_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_delete[to_delete_idx++] = var;
|
||||||
|
} else {
|
||||||
|
prev = var;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var_next == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var = var_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_head != NULL) {
|
||||||
|
git_strmap_set_value_at(b->values, pos, new_head);
|
||||||
|
} else {
|
||||||
|
git_strmap_delete_at(b->values, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to_delete_idx > 0)
|
||||||
|
result = config_write(b, key, &preg, NULL);
|
||||||
|
|
||||||
|
while (to_delete_idx-- > 0)
|
||||||
|
cvar_free(to_delete[to_delete_idx]);
|
||||||
|
|
||||||
|
git__free(key);
|
||||||
|
git__free(to_delete);
|
||||||
|
regfree(&preg);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_file__ondisk(git_config_backend **out, const char *path)
|
int git_config_file__ondisk(git_config_backend **out, const char *path)
|
||||||
{
|
{
|
||||||
diskfile_backend *backend;
|
diskfile_backend *backend;
|
||||||
@ -548,6 +637,7 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
|
|||||||
backend->parent.set = config_set;
|
backend->parent.set = config_set;
|
||||||
backend->parent.set_multivar = config_set_multivar;
|
backend->parent.set_multivar = config_set_multivar;
|
||||||
backend->parent.del = config_delete;
|
backend->parent.del = config_delete;
|
||||||
|
backend->parent.del_multivar = config_delete_multivar;
|
||||||
backend->parent.iterator = config_iterator_new;
|
backend->parent.iterator = config_iterator_new;
|
||||||
backend->parent.refresh = config_refresh;
|
backend->parent.refresh = config_refresh;
|
||||||
backend->parent.free = backend_free;
|
backend->parent.free = backend_free;
|
||||||
@ -1214,7 +1304,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* multiline variable? we need to keep reading lines to match */
|
/* multiline variable? we need to keep reading lines to match */
|
||||||
if (preg != NULL) {
|
if (preg != NULL && section_matches) {
|
||||||
data_start = post_start;
|
data_start = post_start;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -365,16 +365,18 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i
|
|||||||
const char *dir;
|
const char *dir;
|
||||||
size_t i;
|
size_t i;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
const char *cname;
|
||||||
|
|
||||||
push = direction == GIT_DIRECTION_PUSH;
|
push = direction == GIT_DIRECTION_PUSH;
|
||||||
dir = push ? "push" : "fetch";
|
dir = push ? "push" : "fetch";
|
||||||
|
|
||||||
if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
|
if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
cname = git_buf_cstr(&name);
|
||||||
|
|
||||||
/* Clear out the existing config */
|
/* Clear out the existing config */
|
||||||
while (!error)
|
while (!error)
|
||||||
error = git_config_delete_entry(config, git_buf_cstr(&name));
|
error = git_config_delete_multivar(config, cname, ".*");
|
||||||
|
|
||||||
if (error != GIT_ENOTFOUND)
|
if (error != GIT_ENOTFOUND)
|
||||||
return error;
|
return error;
|
||||||
@ -385,8 +387,11 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i
|
|||||||
if (spec->push != push)
|
if (spec->push != push)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// "$^" is a unmatcheable regexp: it will not match anything at all, so
|
||||||
|
// all values will be considered new and we will not replace any
|
||||||
|
// present value.
|
||||||
if ((error = git_config_set_multivar(
|
if ((error = git_config_set_multivar(
|
||||||
config, git_buf_cstr(&name), "", spec->string)) < 0) {
|
config, cname, "$^", spec->string)) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,3 +221,68 @@ void test_config_multivar__replace_multiple(void)
|
|||||||
|
|
||||||
git_config_free(cfg);
|
git_config_free(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_config_multivar__delete(void)
|
||||||
|
{
|
||||||
|
git_config *cfg;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
|
||||||
|
cl_assert(n == 2);
|
||||||
|
|
||||||
|
cl_git_pass(git_config_delete_multivar(cfg, _name, "github"));
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
|
||||||
|
cl_assert(n == 1);
|
||||||
|
|
||||||
|
git_config_free(cfg);
|
||||||
|
|
||||||
|
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
|
||||||
|
cl_assert(n == 1);
|
||||||
|
|
||||||
|
git_config_free(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_config_multivar__delete_multiple(void)
|
||||||
|
{
|
||||||
|
git_config *cfg;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
|
||||||
|
cl_assert(n == 2);
|
||||||
|
|
||||||
|
cl_git_pass(git_config_delete_multivar(cfg, _name, "git"));
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
|
||||||
|
|
||||||
|
git_config_free(cfg);
|
||||||
|
|
||||||
|
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
|
||||||
|
|
||||||
|
git_config_free(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_config_multivar__delete_notfound(void)
|
||||||
|
{
|
||||||
|
git_config *cfg;
|
||||||
|
|
||||||
|
cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
|
||||||
|
|
||||||
|
cl_git_fail_with(git_config_delete_multivar(cfg, "remote.ab.noturl", "git"), GIT_ENOTFOUND);
|
||||||
|
|
||||||
|
git_config_free(cfg);
|
||||||
|
}
|
||||||
|
@ -150,8 +150,10 @@ void test_network_remote_remotes__add_pushspec(void)
|
|||||||
void test_network_remote_remotes__save(void)
|
void test_network_remote_remotes__save(void)
|
||||||
{
|
{
|
||||||
git_strarray array;
|
git_strarray array;
|
||||||
const char *fetch_refspec = "refs/heads/*:refs/remotes/upstream/*";
|
const char *fetch_refspec1 = "refs/heads/ns1/*:refs/remotes/upstream/ns1/*";
|
||||||
const char *push_refspec = "refs/heads/*:refs/heads/*";
|
const char *fetch_refspec2 = "refs/heads/ns2/*:refs/remotes/upstream/ns2/*";
|
||||||
|
const char *push_refspec1 = "refs/heads/ns1/*:refs/heads/ns1/*";
|
||||||
|
const char *push_refspec2 = "refs/heads/ns2/*:refs/heads/ns2/*";
|
||||||
|
|
||||||
git_remote_free(_remote);
|
git_remote_free(_remote);
|
||||||
_remote = NULL;
|
_remote = NULL;
|
||||||
@ -160,8 +162,10 @@ void test_network_remote_remotes__save(void)
|
|||||||
cl_git_pass(git_remote_create(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2"));
|
cl_git_pass(git_remote_create(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2"));
|
||||||
git_remote_clear_refspecs(_remote);
|
git_remote_clear_refspecs(_remote);
|
||||||
|
|
||||||
cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec));
|
cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec1));
|
||||||
cl_git_pass(git_remote_add_push(_remote, push_refspec));
|
cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec2));
|
||||||
|
cl_git_pass(git_remote_add_push(_remote, push_refspec1));
|
||||||
|
cl_git_pass(git_remote_add_push(_remote, push_refspec2));
|
||||||
cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push"));
|
cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push"));
|
||||||
cl_git_pass(git_remote_save(_remote));
|
cl_git_pass(git_remote_save(_remote));
|
||||||
git_remote_free(_remote);
|
git_remote_free(_remote);
|
||||||
@ -171,16 +175,19 @@ void test_network_remote_remotes__save(void)
|
|||||||
cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
|
cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
|
||||||
|
|
||||||
cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote));
|
cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote));
|
||||||
cl_assert_equal_i(1, (int)array.count);
|
cl_assert_equal_i(2, (int)array.count);
|
||||||
cl_assert_equal_s(fetch_refspec, array.strings[0]);
|
cl_assert_equal_s(fetch_refspec1, array.strings[0]);
|
||||||
|
cl_assert_equal_s(fetch_refspec2, array.strings[1]);
|
||||||
git_strarray_free(&array);
|
git_strarray_free(&array);
|
||||||
|
|
||||||
cl_git_pass(git_remote_get_push_refspecs(&array, _remote));
|
cl_git_pass(git_remote_get_push_refspecs(&array, _remote));
|
||||||
cl_assert_equal_i(1, (int)array.count);
|
cl_assert_equal_i(2, (int)array.count);
|
||||||
cl_assert_equal_s(push_refspec, array.strings[0]);
|
cl_assert_equal_s(push_refspec1, array.strings[0]);
|
||||||
|
cl_assert_equal_s(push_refspec2, array.strings[1]);
|
||||||
|
git_strarray_free(&array);
|
||||||
|
|
||||||
cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
|
cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
|
||||||
cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push");
|
cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push");
|
||||||
git_strarray_free(&array);
|
|
||||||
|
|
||||||
/* remove the pushurl again and see if we can save that too */
|
/* remove the pushurl again and see if we can save that too */
|
||||||
cl_git_pass(git_remote_set_pushurl(_remote, NULL));
|
cl_git_pass(git_remote_set_pushurl(_remote, NULL));
|
||||||
|
Loading…
Reference in New Issue
Block a user