Commit 8286300a by Russell Belfer

Fix git_submodule_sync and add new config helper

This fixes `git_submodule_sync` to correctly update the remote URL
of the default branch of the submodule along with the URL in the
parent repository config (i.e. match core Git's behavior).

Also move some useful helper logic from the submodule code into
a shared config API `git_config__update_entry` that can either set
or delete an entry with constraints like not overwriting or not
creating a new entry.  I used that helper to update a couple other
places in the code.
parent 18234b14
......@@ -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
***********/
......
......@@ -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
......
......@@ -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;
error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf));
if (error < 0 && error != GIT_ENOTFOUND)
goto on_error;
if ((error = git_buf_printf(&buf, "remote.%s.tagopt", remote->name)) < 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 ((error = git_config__lookup_entry(
&existing, cfg, git_buf_cstr(&buf), false)) < 0)
goto cleanup;
git_buf_free(&buf);
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;
return 0;
error = git_config__update_entry(
cfg, git_buf_cstr(&buf), tagopt, true, false);
on_error:
cleanup:
git_buf_free(&buf);
return -1;
return error;
}
const char *git_remote_name(const git_remote *remote)
......
......@@ -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.<name>.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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment