diff --git a/src/config.c b/src/config.c index 1a205fe13..b3168f735 100644 --- a/src/config.c +++ b/src/config.c @@ -615,6 +615,36 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) return error; } +int git_config__update_entry( + git_config *config, + const char *key, + const char *value, + bool overwrite_existing, + bool only_if_existing) +{ + int error = 0; + const git_config_entry *ce = NULL; + + if ((error = git_config__lookup_entry(&ce, config, key, false)) < 0) + return error; + + if (!ce && only_if_existing) /* entry doesn't exist */ + return 0; + if (ce && !overwrite_existing) /* entry would be overwritten */ + return 0; + if (value && ce && ce->value && !strcmp(ce->value, value)) /* no change */ + return 0; + if (!value && (!ce || !ce->value)) /* asked to delete absent entry */ + return 0; + + if (!value) + error = git_config_delete_entry(config, key); + else + error = git_config_set_string(config, key, value); + + return error; +} + /*********** * Getters ***********/ diff --git a/src/config.h b/src/config.h index 03d910616..00b6063e7 100644 --- a/src/config.h +++ b/src/config.h @@ -53,6 +53,14 @@ extern int git_config__lookup_entry( const char *key, bool no_errors); +/* internal only: update and/or delete entry string with constraints */ +extern int git_config__update_entry( + git_config *cfg, + const char *key, + const char *value, + bool overwrite_existing, + bool only_if_existing); + /* * Lookup functions that cannot fail. These functions look up a config * value and return a fallback value if the value is missing or if any diff --git a/src/remote.c b/src/remote.c index 62ee90375..53ff79707 100644 --- a/src/remote.c +++ b/src/remote.c @@ -495,9 +495,10 @@ cleanup: int git_remote_save(const git_remote *remote) { int error; - git_config *config; + git_config *cfg; const char *tagopt = NULL; git_buf buf = GIT_BUF_INIT; + const git_config_entry *existing; assert(remote); @@ -509,43 +510,31 @@ int git_remote_save(const git_remote *remote) if ((error = ensure_remote_name_is_valid(remote->name)) < 0) return error; - if (git_repository_config__weakptr(&config, remote->repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) + return error; - if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0) - return -1; + if ((error = git_buf_printf(&buf, "remote.%s.url", remote->name)) < 0) + return error; - if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) { - git_buf_free(&buf); - return -1; - } + /* after this point, buffer is allocated so end with cleanup */ + + if ((error = git_config_set_string( + cfg, git_buf_cstr(&buf), remote->url)) < 0) + goto cleanup; git_buf_clear(&buf); - if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0) - return -1; + if ((error = git_buf_printf(&buf, "remote.%s.pushurl", remote->name)) < 0) + goto cleanup; - if (remote->pushurl) { - if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) { - git_buf_free(&buf); - return -1; - } - } else { - int error = git_config_delete_entry(config, git_buf_cstr(&buf)); - if (error == GIT_ENOTFOUND) { - error = 0; - giterr_clear(); - } - if (error < 0) { - git_buf_free(&buf); - return error; - } - } + if ((error = git_config__update_entry( + cfg, git_buf_cstr(&buf), remote->pushurl, true, false)) < 0) + goto cleanup; - if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0) - goto on_error; + if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_FETCH)) < 0) + goto cleanup; - if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0) - goto on_error; + if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_PUSH)) < 0) + goto cleanup; /* * What action to take depends on the old and new values. This @@ -561,31 +550,26 @@ int git_remote_save(const git_remote *remote) */ git_buf_clear(&buf); - if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) - goto on_error; + if ((error = git_buf_printf(&buf, "remote.%s.tagopt", remote->name)) < 0) + goto cleanup; - error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf)); - if (error < 0 && error != GIT_ENOTFOUND) - goto on_error; + if ((error = git_config__lookup_entry( + &existing, cfg, git_buf_cstr(&buf), false)) < 0) + goto cleanup; - if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { - if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0) - goto on_error; - } else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) { - if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0) - goto on_error; - } else if (tagopt) { - if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0) - goto on_error; - } + if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) + tagopt = "--tags"; + else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) + tagopt = "--no-tags"; + else if (existing != NULL) + tagopt = NULL; + error = git_config__update_entry( + cfg, git_buf_cstr(&buf), tagopt, true, false); + +cleanup: git_buf_free(&buf); - - return 0; - -on_error: - git_buf_free(&buf); - return -1; + return error; } const char *git_remote_name(const git_remote *remote) diff --git a/src/submodule.c b/src/submodule.c index a0ce5317f..dc0ea435e 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -83,7 +83,6 @@ static int lookup_head_remote(git_buf *url, git_repository *repo); static int submodule_get(git_submodule **, git_repository *, const char *, const char *); static int submodule_load_from_config(const git_config_entry *, void *); static int submodule_load_from_wd_lite(git_submodule *); -static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool); static void submodule_get_index_status(unsigned int *, git_submodule *); static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); @@ -716,46 +715,105 @@ git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules( return old; } -int git_submodule_init(git_submodule *submodule, int overwrite) +int git_submodule_init(git_submodule *sm, int overwrite) { int error; const char *val; + git_buf key = GIT_BUF_INIT; + git_config *cfg = NULL; - /* write "submodule.NAME.url" */ - - if (!submodule->url) { + if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", submodule->name); + "No URL configured for submodule '%s'", sm->name); return -1; } - error = submodule_update_config( - submodule, "url", submodule->url, overwrite != 0, false); - if (error < 0) + if ((error = git_repository_config(&cfg, sm->repo)) < 0) return error; + /* write "submodule.NAME.url" */ + + if ((error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 || + (error = git_config__update_entry( + cfg, key.ptr, sm->url, overwrite != 0, false)) < 0) + goto cleanup; + /* write "submodule.NAME.update" if not default */ - val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ? - NULL : git_submodule_update_to_str(submodule->update); - error = submodule_update_config( - submodule, "update", val, (overwrite != 0), false); + val = (sm->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ? + NULL : git_submodule_update_to_str(sm->update); + + if ((error = git_buf_printf(&key, "submodule.%s.update", sm->name)) < 0 || + (error = git_config__update_entry( + cfg, key.ptr, val, overwrite != 0, false)) < 0) + goto cleanup; + + /* success */ + +cleanup: + git_config_free(cfg); + git_buf_free(&key); return error; } -int git_submodule_sync(git_submodule *submodule) +int git_submodule_sync(git_submodule *sm) { - if (!submodule->url) { + int error = 0; + git_config *cfg = NULL; + git_buf key = GIT_BUF_INIT; + + if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", submodule->name); + "No URL configured for submodule '%s'", sm->name); return -1; } /* copy URL over to config only if it already exists */ - return submodule_update_config( - submodule, "url", submodule->url, true, true); + if (!(error = git_repository_config__weakptr(&cfg, sm->repo)) && + !(error = git_buf_printf(&key, "submodule.%s.url", sm->name))) + error = git_config__update_entry(cfg, key.ptr, sm->url, true, true); + + /* if submodule exists in the working directory, update remote url */ + + if (!error && (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0) { + git_repository *smrepo = NULL; + git_reference *smhead = NULL; + const char *remote = "origin"; + + if ((error = git_submodule_open(&smrepo, sm)) < 0 || + (error = git_repository_head(&smhead, smrepo)) < 0 || + (error = git_repository_config__weakptr(&cfg, smrepo)) < 0) + goto smcleanup; + + /* get remote for default branch and set remote..url */ + + if (git_reference_type(smhead) == GIT_REF_SYMBOLIC) { + const char *bname = git_reference_shorthand(smhead); + const git_config_entry *ce; + + git_buf_clear(&key); + if ((error = git_buf_printf(&key, "branch.%s.remote", bname)) < 0 || + (error = git_config__lookup_entry(&ce, cfg, key.ptr, 0)) < 0) + goto smcleanup; + + if (ce && ce->value) + remote = ce->value; + } + + git_buf_clear(&key); + if (!(error = git_buf_printf(&key, "remote.%s.url", remote))) + error = git_config__update_entry(cfg, key.ptr, sm->url, true, true); + +smcleanup: + git_reference_free(smhead); + git_repository_free(smrepo); + } + + git_buf_free(&key); + + return error; } static int git_submodule__open( @@ -1640,50 +1698,6 @@ cleanup: return error; } -static int submodule_update_config( - git_submodule *submodule, - const char *attr, - const char *value, - bool overwrite, - bool only_existing) -{ - int error; - git_config *config; - git_buf key = GIT_BUF_INIT; - const git_config_entry *ce = NULL; - - assert(submodule); - - error = git_repository_config__weakptr(&config, submodule->repo); - if (error < 0) - return error; - - error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr); - if (error < 0) - goto cleanup; - - if ((error = git_config__lookup_entry(&ce, config, key.ptr, false)) < 0) - goto cleanup; - - if (!ce && only_existing) - goto cleanup; - if (ce && !overwrite) - goto cleanup; - if (value && ce && ce->value && !strcmp(ce->value, value)) - goto cleanup; - if (!value && (!ce || !ce->value)) - goto cleanup; - - if (!value) - error = git_config_delete_entry(config, key.ptr); - else - error = git_config_set_string(config, key.ptr, value); - -cleanup: - git_buf_free(&key); - return error; -} - static void submodule_get_index_status(unsigned int *status, git_submodule *sm) { const git_oid *head_oid = git_submodule_head_id(sm);