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 <sbel...@google.com>
---
 builtin/submodule--helper.c | 247 ++++++++++++++++++++++++++++++++++++++++++++
 git-submodule.sh            | 135 +-----------------------
 run-command.h               |   2 +
 3 files changed, 252 insertions(+), 132 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f4c3eff..baa7563 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -255,6 +255,252 @@ 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, "update_module=%s", 
update_module);
+
+               cp->git_cmd = 1;
+               cp->stdout_to_stderr = 1;
+               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 +510,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)
diff --git a/run-command.h b/run-command.h
index 3807fd1..0c1b363 100644
--- a/run-command.h
+++ b/run-command.h
@@ -155,4 +155,6 @@ int run_processes_parallel(int n, void *data,
                           start_failure_fn,
                           return_value_fn);
 
+void run_processes_parallel_schedule_error(struct strbuf *err);
+
 #endif
-- 
2.5.0.275.ge015d2a

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to