summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2013-11-01 17:25:32 +0100
committerVicent Marti <tanoku@gmail.com>2013-11-01 17:25:32 +0100
commit653ec420f9db9924e669a749d7c4e226f824a1d2 (patch)
treed908db20abefa16069c96906f3137180de9ef288 /src
parentab44c62e548373c1494e967f54720faa06ce38b7 (diff)
parent376454d03dbb0c78b1266a85b29ec8bf48930a4d (diff)
downloadlibgit2-653ec420f9db9924e669a749d7c4e226f824a1d2.tar.gz
Merge remote-tracking branch 'drodriguez/fix-remote-save' into development
Diffstat (limited to 'src')
-rw-r--r--src/config.c13
-rw-r--r--src/config_file.c92
-rw-r--r--src/remote.c9
3 files changed, 111 insertions, 3 deletions
diff --git a/src/config.c b/src/config.c
index c98d6a52d..0d9471383 100644
--- a/src/config.c
+++ b/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);
}
+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)
{
return iter->next(entry, iter);
diff --git a/src/config_file.c b/src/config_file.c
index 8fb43b990..5f36a3f8b 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -120,6 +120,18 @@ static void cvar_free(cvar_t *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)
{
char *scan;
@@ -531,6 +543,83 @@ static int config_delete(git_config_backend *cfg, const char *name)
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)
{
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_multivar = config_set_multivar;
backend->parent.del = config_delete;
+ backend->parent.del_multivar = config_delete_multivar;
backend->parent.iterator = config_iterator_new;
backend->parent.refresh = config_refresh;
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 */
- if (preg != NULL) {
+ if (preg != NULL && section_matches) {
data_start = post_start;
continue;
}
diff --git a/src/remote.c b/src/remote.c
index bdfa08642..3528b1c46 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -365,16 +365,18 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i
const char *dir;
size_t i;
int error = 0;
+ const char *cname;
push = direction == GIT_DIRECTION_PUSH;
dir = push ? "push" : "fetch";
if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
return -1;
+ cname = git_buf_cstr(&name);
/* Clear out the existing config */
while (!error)
- error = git_config_delete_entry(config, git_buf_cstr(&name));
+ error = git_config_delete_multivar(config, cname, ".*");
if (error != GIT_ENOTFOUND)
return error;
@@ -385,8 +387,11 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i
if (spec->push != push)
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(
- config, git_buf_cstr(&name), "", spec->string)) < 0) {
+ config, cname, "$^", spec->string)) < 0) {
goto cleanup;
}
}