This will make parallelisation easier in a followup patch. This is just a translation from shell to C, hopefully not introducing any bugs. Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> --- builtin/submodule--helper.c | 251 ++++++++++++++++++++++++++++++++++++++++++++ git-submodule.sh | 135 +----------------------- 2 files changed, 254 insertions(+), 132 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f4c3eff..b79117a 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -255,6 +255,256 @@ static int module_clone(int argc, const char **argv, const char *prefix) return 0; } +struct module_update_data { + struct module_list list; + int count; + + struct pathspec pathspec; + + int no_fetch; + int force; + int update; + int quiet; + int recursive; + int remote; + char *prefix; + char *reference; + char *depth; + + struct argv_array args; + + int result; +}; + +static void module_update_data_init(struct module_update_data *mud) +{ + mud->list.entries = NULL; + mud->list.alloc = 0; + mud->list.nr = 0; + + memset(&mud->pathspec, 0, sizeof(mud->pathspec)); + + mud->count = 0; + mud->no_fetch = 0; + mud->force = 0; + mud->update = 0; + mud->quiet = 0; + mud->remote = 0; + mud->recursive = 0; + mud->result = 0; + + mud->prefix = NULL; + mud->reference = NULL; + mud->depth = NULL; + + argv_array_init(&mud->args); +} + +static int update_next_task(void *data, + struct child_process *cp, + struct strbuf *err) +{ + int i; + struct module_update_data *mud = data; + struct strbuf sb = STRBUF_INIT; + const char *displaypath; + + for (; mud->count < mud->list.nr; mud->count++) { + const char *update_module; + const char *sm_gitdir; + const struct submodule *sub; + const struct cache_entry *ce = mud->list.entries[mud->count]; + + displaypath = relative_path(ce->name, mud->prefix, &sb); + strbuf_reset(&sb); + + if (ce_stage(ce)) { + strbuf_addf(err, "Skipping unmerged submodule %s", + displaypath); + continue; + } + + sub = submodule_from_path(null_sha1, ce->name); + if (!sub) { + mud->result = 1; + return 0; + } + + switch (mud->update) { + case 'r': + update_module = "rebase"; + break; + case 'c': + update_module = "checkout"; + break; + case 'm': + update_module = "merge"; + break; + case 0: + /* not specified by command line */ + if (sub->update) + update_module = sub->update; + else + update_module = "checkout"; + break; + default: + die("BUG: update mode not covered"); + } + + if (!strcmp(update_module, "none")) { + strbuf_addf(err, "Skipping submodule '%s'", displaypath); + continue; + } + + if (!sub->url) { + /* + * Only mention uninitialized submodules when its + * path have been specified + */ + if (!mud->pathspec.nr) + continue; + + strbuf_addf(err, + _("Submodule path '%s' not initialized \n" + "Maybe you want to use 'update --init'?"), + displaypath); + continue; + } + + strbuf_addf(&sb, "%s/.git", ce->name); + sm_gitdir = strbuf_detach(&sb, NULL); + + child_process_init(cp); + for (i = 0; local_repo_env[i]; i++) + argv_array_pushf(&cp->env_array, "%s", local_repo_env[i]); + + argv_array_pushf(&cp->env_array, "displaypath=%s", displaypath); + argv_array_pushf(&cp->env_array, "sm_path=%s", sub->path); + argv_array_pushf(&cp->env_array, "name=%s", sub->name); + argv_array_pushf(&cp->env_array, "url=%s", sub->url); + argv_array_pushf(&cp->env_array, "sha1=%s", sha1_to_hex(ce->sha1)); + argv_array_pushf(&cp->env_array, "update_module=%s", update_module); + + cp->git_cmd = 1; + cp->no_stdin = 1; + cp->stdout_to_stderr = 1; + cp->err = -1; + argv_array_init(&cp->args); + argv_array_push(&cp->args, "submodule"); + if (!file_exists(sm_gitdir)) + argv_array_push(&cp->args, "update_clone"); + else + argv_array_push(&cp->args, "update_fetch"); + + argv_array_pushf(&cp->args, "%s", ce->name); + mud->count++; + return 1; + } + return 0; +} + +void update_subcommand_failure(void *data, + struct child_process *cp, + struct strbuf *err) +{ + struct module_update_data *mud = data; + strbuf_addf(err, _("Could not start child process")); + mud->result = 1; +} + +void update_child_return(void *data, + struct child_process *cp, + int result) +{ + struct module_update_data *mud = data; + mud->result = 1; +} + +static int module_update(int argc, const char **argv, const char *prefix) +{ + int init; + struct module_update_data mud; + + struct option module_list_options[] = { + OPT_STRING(0, "prefix", &prefix, + N_("path"), + N_("alternative anchor for relative paths")), + OPT_BOOL('i', "init", &init, + N_("Initialize the submodule if not yet done")), + OPT_BOOL(0, "remote", &mud.remote, + N_("Update the submodule to the remote branch instead " + "of the superprojects specification")), + OPT_BOOL('N', "no-fetch", &mud.no_fetch, + N_("Don’t fetch new objects from the remote site.")), + OPT_BOOL('f', "force", &mud.force, + N_("Ignore local changes in submodules")), + OPT_CMDMODE('r', "rebase", &mud.update, + N_("Rebase local changes in submodules"), 'r'), + OPT_CMDMODE('m', "merge", &mud.update, + N_("Merge local changes in submodules"), 'm'), + OPT_CMDMODE(0, "checkout", &mud.update, + N_("Checkout to a detached HEAD in submodules"), 'c'), + OPT_BOOL(0, "recursive", &mud.recursive, + N_("Update nested submodules")), + OPT_STRING(0, "reference", &mud.reference, "<repository>", + N_("Use the local reference repository " + "instead of a full clone")), + OPT_STRING(0, "depth", &mud.depth, "<depth>", + N_("Create a shallow clone truncated to the " + "specified number of revisions")), + OPT__QUIET(&mud.quiet, N_("be quiet")), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule--helper list [--prefix=<path>] [<path>...]"), + NULL + }; + + module_update_data_init(&mud); + gitmodules_config(); + + argc = parse_options(argc, argv, prefix, module_list_options, + git_submodule_helper_usage, 0); + + if (mud.force) + argv_array_push(&mud.args, "force=1"); + if (mud.quiet) + argv_array_push(&mud.args, "GIT_QUIET=1"); + if (mud.recursive) + argv_array_push(&mud.args, "recursive=1"); + if (mud.prefix) + argv_array_pushf(&mud.args, "prefix=%s", mud.prefix); + if (mud.reference) + argv_array_pushf(&mud.args, "reference=%s", mud.reference); + if (mud.depth) + argv_array_pushf(&mud.args, "depth=%s", mud.depth); + + if (module_list_compute(argc, argv, prefix, &mud.pathspec, &mud.list) < 0) + return 1; + + if (init) { + const char **argv_init = xmalloc((2 + mud.list.nr) * sizeof(char*)); + int argc = 0, i, code; + argv_init[argc++] = "submodule"; + argv_init[argc++] = "init"; + + for (i = 0; i < mud.list.nr; i++) { + const struct cache_entry *ce = mud.list.entries[i]; + argv_init[argc++] = ce->name; + } + code = run_command_v_opt(argv_init, RUN_GIT_CMD); + if (code) + return code; + } + + run_processes_parallel(1, &mud, + update_next_task, + update_subcommand_failure, + update_child_return); + return 0; +} + struct cmd_struct { const char *cmd; int (*fn)(int, const char **, const char *); @@ -264,6 +514,7 @@ static struct cmd_struct commands[] = { {"list", module_list}, {"name", module_name}, {"clone", module_clone}, + {"update", module_update} }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) diff --git a/git-submodule.sh b/git-submodule.sh index a1bc8d5..63e9b3b 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -640,6 +640,7 @@ cmd_update_fetch() die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" fi remote_name=$(clear_local_git_env; cd "$sm_path" && get_default_remote) + branch=$(get_submodule_config "$name" branch master) sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify "${remote_name}/${branch}") || die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")" @@ -715,137 +716,7 @@ cmd_update_fetch() # cmd_update() { - # parse $args after "submodule ... update". - while test $# -ne 0 - do - case "$1" in - -q|--quiet) - GIT_QUIET=1 - ;; - -i|--init) - init=1 - ;; - --remote) - remote=1 - ;; - -N|--no-fetch) - nofetch=1 - ;; - -f|--force) - force=$1 - ;; - -r|--rebase) - update="rebase" - ;; - --reference) - case "$2" in '') usage ;; esac - reference="--reference=$2" - shift - ;; - --reference=*) - reference="$1" - ;; - -m|--merge) - update="merge" - ;; - --recursive) - recursive=1 - ;; - --checkout) - update="checkout" - ;; - --depth) - case "$2" in '') usage ;; esac - depth="--depth=$2" - shift - ;; - --depth=*) - depth=$1 - ;; - --) - shift - break - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift - done - - if test -n "$init" - then - cmd_init "--" "$@" || return - fi - - git submodule--helper list --prefix "$wt_prefix" "$@" | { - err= - while read mode sha1 stage sm_path - do - die_if_unmatched "$mode" - if test "$stage" = U - then - echo >&2 "Skipping unmerged submodule $prefix$sm_path" - continue - fi - name=$(git submodule--helper name "$sm_path") || exit - url=$(git config submodule."$name".url) - branch=$(get_submodule_config "$name" branch master) - if ! test -z "$update" - then - update_module=$update - else - update_module=$(git config submodule."$name".update) - if test -z "$update_module" - then - update_module="checkout" - fi - fi - - displaypath=$(relative_path "$prefix$sm_path") - - if test "$update_module" = "none" - then - echo "Skipping submodule '$displaypath'" - continue - fi - - if test -z "$url" - then - # Only mention uninitialized submodules when its - # path have been specified - test "$#" != "0" && - say "$(eval_gettext "Submodule path '\$displaypath' not initialized -Maybe you want to use 'update --init'?")" - continue - fi - - if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git - then - cmd_update_clone - else - cmd_update_fetch - fi - done - - if test -n "$err" - then - OIFS=$IFS - IFS=';' - for e in $err - do - if test -n "$e" - then - echo >&2 "$e" - fi - done - IFS=$OIFS - exit 1 - fi - } + git submodule--helper update ${prefix:+--prefix "$prefix"} "$@" } set_name_rev () { @@ -1243,7 +1114,7 @@ cmd_sync() while test $# != 0 && test -z "$command" do case "$1" in - add | foreach | init | deinit | update | status | summary | sync) + add | foreach | init | deinit | update | update_fetch | update_clone | status | summary | sync) command=$1 ;; -q|--quiet) -- 2.5.0.272.ga84127c.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html