On 07/25, Prathamesh Chavan wrote: > This aims to make git-submodule foreach a builtin. This is the very > first step taken in this direction. Hence, 'foreach' is ported to > submodule--helper, and submodule--helper is called from git-submodule.sh. > The code is split up to have one function to obtain all the list of > submodules. This function acts as the front-end of git-submodule foreach > subcommand. It calls the function for_each_submodule_list, which basically > loops through the list and calls function fn, which in this case is > runcommand_in_submodule. This third function is a calling function that > takes care of running the command in that submodule, and recursively > perform the same when --recursive is flagged. > > The first function module_foreach first parses the options present in > argv, and then with the help of module_list_compute, generates the list of > submodules present in the current working tree. > > The second function for_each_submodule_list traverses through the > list, and calls function fn (which in case of submodule subcommand > foreach is runcommand_in_submodule) is called for each entry. > > The third function runcommand_in_submodule, generates a submodule struct sub > for $name, value and then later prepends name=sub->name; and other > value assignment to the env argv_array structure of a child_process. > Also the <command> of submodule-foreach is push to args argv_array > structure and finally, using run_command the commands are executed > using a shell. > > The third function also takes care of the recursive flag, by creating > a separate child_process structure and prepending "--super-prefix displaypath", > to the args argv_array structure. Other required arguments and the > input <command> of submodule-foreach is also appended to this argv_array. > > Helped-by: Brandon Williams <bmwill@xxxxxxxxxx> > Mentored-by: Christian Couder <christian.couder@xxxxxxxxx> > Mentored-by: Stefan Beller <sbeller@xxxxxxxxxx> > Signed-off-by: Prathamesh Chavan <pc44800@xxxxxxxxx> > --- > builtin/submodule--helper.c | 129 ++++++++++++++++++++++++++++++++++++++++++++ > git-submodule.sh | 39 +------------- > 2 files changed, 130 insertions(+), 38 deletions(-) > > diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c > index 94d6254f0..be278bf8d 100644 > --- a/builtin/submodule--helper.c > +++ b/builtin/submodule--helper.c > @@ -765,6 +765,134 @@ static int module_name(int argc, const char **argv, const char *prefix) > return 0; > } > > +struct cb_foreach { > + int argc; > + const char **argv; > + const char *prefix; > + unsigned int quiet: 1; > + unsigned int recursive: 1; > +}; > +#define CB_FOREACH_INIT { 0, NULL, NULL, 0, 0 } > + > +static void runcommand_in_submodule(const struct cache_entry *list_item, > + void *cb_data) > +{ > + struct cb_foreach *info = cb_data; > + const struct submodule *sub; > + struct child_process cp = CHILD_PROCESS_INIT; > + char *displaypath; > + > + displaypath = get_submodule_displaypath(list_item->name, info->prefix); > + > + sub = submodule_from_path(null_sha1, list_item->name); > + > + if (!sub) > + die(_("No url found for submodule path '%s' in .gitmodules"), > + displaypath); > + > + if (!is_submodule_populated_gently(list_item->name, NULL)) > + goto cleanup; > + > + prepare_submodule_repo_env(&cp.env_array); > + /* For the purpose of executing <command> in the submodule, > + * separate shell is used for the purpose of running the > + * child process. > + */ comment style > + cp.use_shell = 1; > + cp.dir = list_item->name; > + > + if (info->argc == 1) { Why are you only exposing these variables if argc == 1? > + char *toplevel = xgetcwd(); > + > + argv_array_pushf(&cp.env_array, "name=%s", sub->name); > + argv_array_pushf(&cp.env_array, "sm_path=%s", list_item->name); > + argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath); > + argv_array_pushf(&cp.env_array, "sha1=%s", > + oid_to_hex(&list_item->oid)); > + argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel); > + > + /* > + * Since still the path variable was accessible from the > + * script before porting, it is also made available. > + */ > + argv_array_pushf(&cp.args, "path=%s; %s", > + list_item->name, info->argv[0]); This bit looks odd. Why are you appending argv[0] after a semicolon? Oh...its to handle the funny path stuff. I'd add a comment indicating why you have to expose path via the args argv_array and not the env_array. > + free(toplevel); > + } else { > + argv_array_pushv(&cp.args, info->argv); > + } > + > + if (!info->quiet) > + printf(_("Entering '%s'\n"), displaypath); > + > + if (info->argv[0] && run_command(&cp)) > + die(_("run_command returned non-zero status for %s\n."), > + displaypath); > + > + if (info->recursive) { > + struct child_process cpr = CHILD_PROCESS_INIT; > + > + cpr.git_cmd = 1; > + cpr.dir = list_item->name; > + prepare_submodule_repo_env(&cpr.env_array); > + > + argv_array_pushl(&cpr.args, "--super-prefix", displaypath, Same comment as a few of the other commands about super-prefix. > + "submodule--helper", "foreach", "--recursive", > + NULL); > + > + if (info->quiet) > + argv_array_push(&cpr.args, "--quiet"); > + > + argv_array_pushv(&cpr.args, info->argv); > + > + if (run_command(&cpr)) > + die(_("run_command returned non-zero status while" > + "recursing in the nested submodules of %s\n."), > + displaypath); > + } > + > +cleanup: > + free(displaypath); > +} > + > +static int module_foreach(int argc, const char **argv, const char *prefix) > +{ > + struct cb_foreach info; > + struct pathspec pathspec; > + struct module_list list = MODULE_LIST_INIT; > + int quiet = 0; > + int recursive = 0; > + > + struct option module_foreach_options[] = { > + OPT__QUIET(&quiet, N_("Suppress output of entering each submodule command")), > + OPT_BOOL(0, "recursive", &recursive, > + N_("Recurse into nested submodules")), > + OPT_END() > + }; > + > + const char *const git_submodule_helper_usage[] = { > + N_("git submodule--helper foreach [--quiet] [--recursive] <command>"), > + NULL > + }; > + > + argc = parse_options(argc, argv, prefix, module_foreach_options, > + git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN); > + > + if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) > + BUG("module_list_compute should not choke on empty pathspec"); > + > + info.argc = argc; > + info.argv = argv; > + info.prefix = prefix; > + info.quiet = !!quiet; > + info.recursive = !!recursive; > + > + gitmodules_config(); > + for_each_submodule_list(list, runcommand_in_submodule, &info); > + > + return 0; > +} > + > struct module_cb { > unsigned int mod_src; > unsigned int mod_dst; > @@ -2203,6 +2331,7 @@ static struct cmd_struct commands[] = { > {"resolve-relative-url", resolve_relative_url, 0}, > {"resolve-relative-url-test", resolve_relative_url_test, 0}, > {"print-name-rev", print_name_rev, 0}, > + {"foreach", module_foreach, SUPPORT_SUPER_PREFIX}, > {"init", module_init, SUPPORT_SUPER_PREFIX}, > {"status", module_status, SUPPORT_SUPER_PREFIX}, > {"print-default-remote", print_default_remote, 0}, > diff --git a/git-submodule.sh b/git-submodule.sh > index 493a64372..e25b2c613 100755 > --- a/git-submodule.sh > +++ b/git-submodule.sh > @@ -298,44 +298,7 @@ cmd_foreach() > shift > done > > - toplevel=$(pwd) > - > - # dup stdin so that it can be restored when running the external > - # command in the subshell (and a recursive call to this function) > - exec 3<&0 > - > - { > - git submodule--helper list --prefix "$wt_prefix" || > - echo "#unmatched" $? > - } | > - while read -r mode sha1 stage sm_path > - do > - die_if_unmatched "$mode" "$sha1" > - if test -e "$sm_path"/.git > - then > - displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix") > - say "$(eval_gettext "Entering '\$displaypath'")" > - name=$(git submodule--helper name "$sm_path") > - ( > - prefix="$prefix$sm_path/" > - sanitize_submodule_env > - cd "$sm_path" && > - # we make $path available to scripts ... > - path=$sm_path && > - if test $# -eq 1 > - then > - eval "$1" > - else > - "$@" > - fi && > - if test -n "$recursive" > - then > - cmd_foreach "--recursive" "$@" > - fi > - ) <&3 3<&- || > - die "$(eval_gettext "Stopping at '\$displaypath'; script returned non-zero status.")" > - fi > - done > + git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@" > } > > # > -- > 2.13.0 > -- Brandon Williams