Skip to content

Commit fc1b924

Browse files
pratham-pcgitster
authored andcommitted
submodule: port submodule subcommand 'foreach' from shell to C
This aims to make git-submodule foreach a builtin. 'foreach' is ported to the submodule--helper, and submodule--helper is called from git-submodule.sh. Helped-by: Brandon Williams <[email protected]> Mentored-by: Christian Couder <[email protected]> Mentored-by: Stefan Beller <[email protected]> Signed-off-by: Prathamesh Chavan <[email protected]> Signed-off-by: Stefan Beller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b6f7ac8 commit fc1b924

File tree

2 files changed

+145
-38
lines changed

2 files changed

+145
-38
lines changed

builtin/submodule--helper.c

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,149 @@ static void for_each_listed_submodule(const struct module_list *list,
439439
fn(list->entries[i], cb_data);
440440
}
441441

442+
struct cb_foreach {
443+
int argc;
444+
const char **argv;
445+
const char *prefix;
446+
int quiet;
447+
int recursive;
448+
};
449+
#define CB_FOREACH_INIT { 0 }
450+
451+
static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
452+
void *cb_data)
453+
{
454+
struct cb_foreach *info = cb_data;
455+
const char *path = list_item->name;
456+
const struct object_id *ce_oid = &list_item->oid;
457+
458+
const struct submodule *sub;
459+
struct child_process cp = CHILD_PROCESS_INIT;
460+
char *displaypath;
461+
462+
displaypath = get_submodule_displaypath(path, info->prefix);
463+
464+
sub = submodule_from_path(the_repository, &null_oid, path);
465+
466+
if (!sub)
467+
die(_("No url found for submodule path '%s' in .gitmodules"),
468+
displaypath);
469+
470+
if (!is_submodule_populated_gently(path, NULL))
471+
goto cleanup;
472+
473+
prepare_submodule_repo_env(&cp.env_array);
474+
475+
/*
476+
* For the purpose of executing <command> in the submodule,
477+
* separate shell is used for the purpose of running the
478+
* child process.
479+
*/
480+
cp.use_shell = 1;
481+
cp.dir = path;
482+
483+
/*
484+
* NEEDSWORK: the command currently has access to the variables $name,
485+
* $sm_path, $displaypath, $sha1 and $toplevel only when the command
486+
* contains a single argument. This is done for maintaining a faithful
487+
* translation from shell script.
488+
*/
489+
if (info->argc == 1) {
490+
char *toplevel = xgetcwd();
491+
struct strbuf sb = STRBUF_INIT;
492+
493+
argv_array_pushf(&cp.env_array, "name=%s", sub->name);
494+
argv_array_pushf(&cp.env_array, "sm_path=%s", path);
495+
argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
496+
argv_array_pushf(&cp.env_array, "sha1=%s",
497+
oid_to_hex(ce_oid));
498+
argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
499+
500+
/*
501+
* Since the path variable was accessible from the script
502+
* before porting, it is also made available after porting.
503+
* The environment variable "PATH" has a very special purpose
504+
* on windows. And since environment variables are
505+
* case-insensitive in windows, it interferes with the
506+
* existing PATH variable. Hence, to avoid that, we expose
507+
* path via the args argv_array and not via env_array.
508+
*/
509+
sq_quote_buf(&sb, path);
510+
argv_array_pushf(&cp.args, "path=%s; %s",
511+
sb.buf, info->argv[0]);
512+
strbuf_release(&sb);
513+
free(toplevel);
514+
} else {
515+
argv_array_pushv(&cp.args, info->argv);
516+
}
517+
518+
if (!info->quiet)
519+
printf(_("Entering '%s'\n"), displaypath);
520+
521+
if (info->argv[0] && run_command(&cp))
522+
die(_("run_command returned non-zero status for %s\n."),
523+
displaypath);
524+
525+
if (info->recursive) {
526+
struct child_process cpr = CHILD_PROCESS_INIT;
527+
528+
cpr.git_cmd = 1;
529+
cpr.dir = path;
530+
prepare_submodule_repo_env(&cpr.env_array);
531+
532+
argv_array_pushl(&cpr.args, "--super-prefix", NULL);
533+
argv_array_pushf(&cpr.args, "%s/", displaypath);
534+
argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
535+
NULL);
536+
537+
if (info->quiet)
538+
argv_array_push(&cpr.args, "--quiet");
539+
540+
argv_array_pushv(&cpr.args, info->argv);
541+
542+
if (run_command(&cpr))
543+
die(_("run_command returned non-zero status while"
544+
"recursing in the nested submodules of %s\n."),
545+
displaypath);
546+
}
547+
548+
cleanup:
549+
free(displaypath);
550+
}
551+
552+
static int module_foreach(int argc, const char **argv, const char *prefix)
553+
{
554+
struct cb_foreach info = CB_FOREACH_INIT;
555+
struct pathspec pathspec;
556+
struct module_list list = MODULE_LIST_INIT;
557+
558+
struct option module_foreach_options[] = {
559+
OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
560+
OPT_BOOL(0, "recursive", &info.recursive,
561+
N_("Recurse into nested submodules")),
562+
OPT_END()
563+
};
564+
565+
const char *const git_submodule_helper_usage[] = {
566+
N_("git submodule--helper foreach [--quiet] [--recursive] <command>"),
567+
NULL
568+
};
569+
570+
argc = parse_options(argc, argv, prefix, module_foreach_options,
571+
git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
572+
573+
if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
574+
return 1;
575+
576+
info.argc = argc;
577+
info.argv = argv;
578+
info.prefix = prefix;
579+
580+
for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
581+
582+
return 0;
583+
}
584+
442585
struct init_cb {
443586
const char *prefix;
444587
unsigned int flags;
@@ -1841,6 +1984,7 @@ static struct cmd_struct commands[] = {
18411984
{"relative-path", resolve_relative_path, 0},
18421985
{"resolve-relative-url", resolve_relative_url, 0},
18431986
{"resolve-relative-url-test", resolve_relative_url_test, 0},
1987+
{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
18441988
{"init", module_init, SUPPORT_SUPER_PREFIX},
18451989
{"status", module_status, SUPPORT_SUPER_PREFIX},
18461990
{"print-default-remote", print_default_remote, 0},

git-submodule.sh

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -323,44 +323,7 @@ cmd_foreach()
323323
shift
324324
done
325325

326-
toplevel=$(pwd)
327-
328-
# dup stdin so that it can be restored when running the external
329-
# command in the subshell (and a recursive call to this function)
330-
exec 3<&0
331-
332-
{
333-
git submodule--helper list --prefix "$wt_prefix" ||
334-
echo "#unmatched" $?
335-
} |
336-
while read -r mode sha1 stage sm_path
337-
do
338-
die_if_unmatched "$mode" "$sha1"
339-
if test -e "$sm_path"/.git
340-
then
341-
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
342-
say "$(eval_gettext "Entering '\$displaypath'")"
343-
name=$(git submodule--helper name "$sm_path")
344-
(
345-
prefix="$prefix$sm_path/"
346-
sanitize_submodule_env
347-
cd "$sm_path" &&
348-
# we make $path available to scripts ...
349-
path=$sm_path &&
350-
if test $# -eq 1
351-
then
352-
eval "$1"
353-
else
354-
"$@"
355-
fi &&
356-
if test -n "$recursive"
357-
then
358-
cmd_foreach "--recursive" "$@"
359-
fi
360-
) <&3 3<&- ||
361-
die "$(eval_gettext "Stopping at '\$displaypath'; script returned non-zero status.")"
362-
fi
363-
done
326+
git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
364327
}
365328

366329
#

0 commit comments

Comments
 (0)