On Tue, Jul 18, 2017 at 1:49 PM, Prathamesh Chavan <[email protected]> wrote:
> Port the submodule subcommand 'sync' from shell to C using the same
> mechanism as that used for porting submodule subcommand 'status'.
> Hence, here the function cmd_sync() is ported from shell to C.
> This is done by introducing three functions: module_sync(),
> sync_submodule() and print_default_remote().
>
> The function print_default_remote() is introduced for getting
> the default remote as stdout.
>
> Mentored-by: Christian Couder <[email protected]>
> Mentored-by: Stefan Beller <[email protected]>
> Signed-off-by: Prathamesh Chavan <[email protected]>
> ---
> builtin/submodule--helper.c | 179
> ++++++++++++++++++++++++++++++++++++++++++++
> git-submodule.sh | 56 +-------------
> 2 files changed, 180 insertions(+), 55 deletions(-)
>
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 9c1630495..da91c489b 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -44,6 +44,20 @@ static char *get_default_remote(void)
> return ret;
> }
>
> +static int print_default_remote(int argc, const char **argv, const char
> *prefix)
> +{
> + const char *remote;
> +
> + if (argc != 1)
> + die(_("submodule--helper print-default-remote takes no
> arguments"));
> +
> + remote = get_default_remote();
> + if (remote)
> + puts(remote);
> +
> + return 0;
> +}
> +
> static int starts_with_dot_slash(const char *str)
> {
> return str[0] == '.' && is_dir_sep(str[1]);
> @@ -379,6 +393,25 @@ static void module_list_active(struct module_list *list)
> *list = active_modules;
> }
>
> +static char *get_up_path(const char *path)
> +{
> + int i;
> + struct strbuf sb = STRBUF_INIT;
> +
> + for (i = count_slashes(path); i; i--)
> + strbuf_addstr(&sb, "../");
> +
> + /*
> + * Check if 'path' ends with slash or not
> + * for having the same output for dir/sub_dir
> + * and dir/sub_dir/
> + */
> + if (!is_dir_sep(path[strlen(path) - 1]))
> + strbuf_addstr(&sb, "../");
> +
> + return strbuf_detach(&sb, NULL);
> +}
> +
> static int module_list(int argc, const char **argv, const char *prefix)
> {
> int i;
> @@ -724,6 +757,150 @@ static int module_name(int argc, const char **argv,
> const char *prefix)
> return 0;
> }
>
> +struct sync_cb {
> + const char *prefix;
> + unsigned int quiet: 1;
> + unsigned int recursive: 1;
> +};
> +#define SYNC_CB_INIT { NULL, 0, 0 }
> +
> +static void sync_submodule(const struct cache_entry *list_item, void
> *cb_data)
> +{
> + struct sync_cb *info = cb_data;
> + const struct submodule *sub;
> + char *sub_key, *remote_key;
> + char *sub_origin_url, *super_config_url, *displaypath;
> + struct strbuf sb = STRBUF_INIT;
> + struct child_process cp = CHILD_PROCESS_INIT;
> +
> + if (!is_submodule_active(the_repository, list_item->name))
> + return;
We can use the_repository here, as we also use child processes to
recurse, such that we always operate on the_repository as the
superproject.
> +
> + sub = submodule_from_path(null_sha1, list_item->name);
> +
> + if (!sub || !sub->url)
> + die(_("no url found for submodule path '%s' in .gitmodules"),
> + list_item->name);
We do not die in the shell script when the url is missing in the
.gitmodules file.
> +
> + if (starts_with_dot_dot_slash(sub->url) ||
> starts_with_dot_slash(sub->url)) {
> + char *remote_url, *up_path;
> + char *remote = get_default_remote();
> + char *remote_key = xstrfmt("remote.%s.url", remote);
> +
> + if (git_config_get_string(remote_key, &remote_url))
> + remote_url = xgetcwd();
> +
> + up_path = get_up_path(list_item->name);
> + sub_origin_url = relative_url(remote_url, sub->url, up_path);
> + super_config_url = relative_url(remote_url, sub->url, NULL);
> +
> + free(remote);
> + free(remote_key);
> + free(up_path);
> + free(remote_url);
> + } else {
> + sub_origin_url = xstrdup(sub->url);
> + super_config_url = xstrdup(sub->url);
> + }
> +
> + displaypath = get_submodule_displaypath(list_item->name,
> info->prefix);
> +
> + if (!info->quiet)
> + printf(_("Synchronizing submodule url for '%s'\n"),
> + displaypath);
> +
> + sub_key = xstrfmt("submodule.%s.url", sub->name);
> + if (git_config_set_gently(sub_key, super_config_url))
> + die(_("failed to register url for submodule path '%s'"),
> + displaypath);
> +
> + if (!is_submodule_populated_gently(list_item->name, NULL))
> + goto cleanup;
> +
> + prepare_submodule_repo_env(&cp.env_array);
> + cp.git_cmd = 1;
> + cp.dir = list_item->name;
> + argv_array_pushl(&cp.args, "submodule--helper",
> + "print-default-remote", NULL);
> + if (capture_command(&cp, &sb, 0))
> + die(_("failed to get the default remote for submodule '%s'"),
> + list_item->name);
> +
> + strbuf_strip_suffix(&sb, "\n");
> + remote_key = xstrfmt("remote.%s.url", sb.buf);
> + strbuf_release(&sb);
> +
> + child_process_init(&cp);
> + prepare_submodule_repo_env(&cp.env_array);
> + cp.git_cmd = 1;
> + cp.dir = list_item->name;
> + argv_array_pushl(&cp.args, "config", remote_key, sub_origin_url,
> NULL);
> + if (run_command(&cp))
> + die(_("failed to update remote for submodule '%s'"),
> + list_item->name);
While it is a strict conversion from the shell script, we could also
try to do this in-process:
1) we'd find out the submodules git dir using submodule_to_gitdir
2) construct the path the the config file as "%s/.gitconfig"
3) using git_config_set_in_file (which presumably takes file name,
key and value) the value can be set