[PATCH 3/5] submodule: teach and plumb append-superproject behavior
- From
- William Hatfield <whatfield.git@gmail.com>
- Date
- Jan 31, 2026, 21:43 UTC
- Message-ID
- <20260131214309.1899376-4-whatfield.git@gmail.com>
- In-Reply-To
- <20260131214309.1899376-1-whatfield.git@gmail.com>
Add --append-superproject flag to 'git submodule foreach' that runs the command in the superproject after processing all submodules. This is only executed at the top-level invocation, not during recursion.
The superproject execution sets the same environment variables as for submodules (name, sm_path, displaypath, sha1, toplevel) so scripts using these variables work uniformly. The '../<name>' displaypath mirrors submodule output style and visually indicates ascent to the parent repository.
When combined with --recursive and --reverse-traversal, this enables full post-order traversal from deepest submodules up to the superproject.
Implementation: - Add append_superproject field to struct foreach_cb - Parse --append-superproject in module_foreach() and git-submodule.sh - Add superproject execution block after submodule traversal - Only run at top-level (when super_prefix is not set)
Signed-off-by: William Hatfield <whatfield.git@gmail.com> --- builtin/submodule--helper.c | 31 ++++++++++++++++++++++++++++++- git-submodule.sh | 4 ++++ t/t7425-submodule-reversion.sh | 29 +++++++---------------------- 3 files changed, 41 insertions(+), 23 deletions(-)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index b1e202399e..f6cba87a05 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -286,6 +286,7 @@ struct foreach_cb { int quiet; int recursive; int reverse_traversal; + int append_superproject; }; #define FOREACH_CB_INIT { 0 } @@ -438,10 +439,12 @@ static int module_foreach(int argc, const char **argv, const char *prefix, N_("recurse into nested submodules")), OPT_BOOL(0, "reverse-traversal", &info.reverse_traversal, N_("traverse submodules in reverse order (post-order)")), + OPT_BOOL(0, "append-superproject", &info.append_superproject, + N_("also run command in superproject after submodules")), OPT_END() }; const char *const git_submodule_helper_usage[] = { - N_("git submodule foreach [--quiet] [--recursive] [--reverse-traversal] [--] <command>"), + N_("git submodule foreach [--quiet] [--recursive] [--reverse-traversal] [--append-superproject] [--] <command>"), NULL }; int ret = 1; @@ -459,6 +462,32 @@ static int module_foreach(int argc, const char **argv, const char *prefix, for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info, info.reverse_traversal); + /* + * Run command in superproject after all submodules, but only at the + * top-level invocation (not during recursion into nested submodules). + */ + if (info.append_superproject && !info.super_prefix) { + struct child_process cp = CHILD_PROCESS_INIT; + char *toplevel = xgetcwd(); + const char *slash = find_last_dir_sep(toplevel); + const char *super_name = slash ? slash + 1 : toplevel; + char *displaypath = xstrfmt("../%s", super_name); + + cp.use_shell = 1; + cp.dir = toplevel; + strvec_pushf(&cp.env, "displaypath=%s", displaypath); + strvec_pushv(&cp.args, info.argv); + + if (!info.quiet) + printf(_("Entering '%s'\n"), displaypath); + + if (run_command(&cp)) + die(_("command failed in superproject\n")); + + free(displaypath); + free(toplevel); + } + ret = 0; cleanup: module_list_release(&list); diff --git a/git-submodule.sh b/git-submodule.sh index 49e9541a3d..58682a287d 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -176,6 +176,9 @@ cmd_foreach() --reverse-traversal) reverse_traversal=$1 ;; + --append-superproject) + append_superproject=$1 + ;; -*) usage ;; @@ -190,6 +193,7 @@ cmd_foreach() $quiet \ $recursive \ $reverse_traversal \ + $append_superproject \ -- \ "$@" } diff --git a/t/t7425-submodule-reversion.sh b/t/t7425-submodule-reversion.sh index c6733234aa..7a6a54de15 100755 --- a/t/t7425-submodule-reversion.sh +++ b/t/t7425-submodule-reversion.sh @@ -168,14 +168,14 @@ EOF test_cmp expect actual ' -test_expect_failure '--recursive and --append-superproject parses' ' +test_expect_success '--recursive and --append-superproject parses' ' ( cd reversive/top && git submodule foreach --recursive --append-superproject "true" ) ' -test_expect_failure '--recursive and --append-superproject runs' ' +test_expect_success '--recursive and --append-superproject runs' ' ( cd reversive/top && git submodule --quiet foreach --recursive \ @@ -199,7 +199,7 @@ EOF test_cmp expect actual ' -test_expect_failure '--reverse-traversal and --append-superproject parses' ' +test_expect_success '--reverse-traversal and --append-superproject parses' ' ( cd reversive/top && git submodule foreach \ @@ -207,7 +207,7 @@ test_expect_failure '--reverse-traversal and --append-superproject parses' ' ) ' -test_expect_failure '--reverse-traversal and --append-superproject runs' ' +test_expect_success '--reverse-traversal and --append-superproject runs' ' ( cd reversive/top && git submodule --quiet foreach --recursive \ @@ -270,7 +270,7 @@ test_expect_failure '--reversive stops on command failure' ' ) ' -test_expect_failure '--append-superproject with no submodules runs only superproject' ' +test_expect_success '--append-superproject with no submodules runs only superproject' ' test_create_repo empty_repo && ( cd empty_repo && @@ -285,22 +285,7 @@ EOF test_cmp expect actual ' -test_expect_failure '--append-superproject sets all expected variables' ' - ( - cd reversive/top && - git submodule --quiet foreach --append-superproject \ - "echo name=\$name path=\$path displaypath=\$displaypath" | - tail -n 1 - ) >actual && - - cat >expect <<-\EOF && -name=top path=../top displaypath=../top -EOF - - test_cmp expect actual -' - -test_expect_failure '--append-superproject from nested submodule appends correct superproject' ' +test_expect_success '--append-superproject from nested submodule appends correct superproject' ' ( cd reversive/top/sub6 && git submodule --quiet foreach --recursive --append-superproject \ @@ -317,7 +302,7 @@ EOF test_cmp expect actual ' -test_expect_failure '--quiet suppresses Entering message for superproject' ' +test_expect_success '--quiet suppresses Entering message for superproject' ' ( cd reversive/top && git submodule foreach --quiet --append-superproject "true"
-- 2.53.0-rc0