diff options
author | Vicent Marti <tanoku@gmail.com> | 2013-11-01 17:25:32 +0100 |
---|---|---|
committer | Vicent Marti <tanoku@gmail.com> | 2013-11-01 17:25:32 +0100 |
commit | 653ec420f9db9924e669a749d7c4e226f824a1d2 (patch) | |
tree | d908db20abefa16069c96906f3137180de9ef288 /src | |
parent | ab44c62e548373c1494e967f54720faa06ce38b7 (diff) | |
parent | 376454d03dbb0c78b1266a85b29ec8bf48930a4d (diff) | |
download | libgit2-653ec420f9db9924e669a749d7c4e226f824a1d2.tar.gz |
Merge remote-tracking branch 'drodriguez/fix-remote-save' into development
Diffstat (limited to 'src')
-rw-r--r-- | src/config.c | 13 | ||||
-rw-r--r-- | src/config_file.c | 92 | ||||
-rw-r--r-- | src/remote.c | 9 |
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; } } |