From: William Hatfield <whatfield.git@gmail.com>
To: git@vger.kernel.org
Cc: glencbz@gmail.com, avarab@gmail.com, gitster@pobox.com,
ps@pks.im, William Hatfield <whatfield.git@gmail.com>
Subject: [PATCH 3/5] submodule: teach and plumb append-superproject behavior
Date: Sat, 31 Jan 2026 16:43:07 -0500 [thread overview]
Message-ID: <20260131214309.1899376-4-whatfield.git@gmail.com> (raw)
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
next prev parent reply other threads:[~2026-01-31 21:43 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-31 21:43 [PATCH 0/5] submodule: add 'reversive' traversal options to foreach William Hatfield
2026-01-31 21:43 ` [PATCH 1/5] t7425: add tests for reversive submodule traversal William Hatfield
2026-01-31 21:43 ` [PATCH 2/5] submodule: teach and plumb reverse-traversal behavior William Hatfield
2026-01-31 21:43 ` William Hatfield [this message]
2026-01-31 21:43 ` [PATCH 4/5] submodule: introduce reversive shorthand mode William Hatfield
2026-01-31 21:43 ` [PATCH 5/5] doc: document reversive traversal and related modes William Hatfield
2026-02-01 9:03 ` Jean-Noël AVILA
2026-02-02 21:10 ` William Hatfield
2026-02-02 18:52 ` [PATCH 0/5] submodule: add 'reversive' traversal options to foreach Junio C Hamano
2026-02-02 21:02 ` William Hatfield
2026-02-02 21:25 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260131214309.1899376-4-whatfield.git@gmail.com \
--to=whatfield.git@gmail.com \
--cc=avarab@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=glencbz@gmail.com \
--cc=ps@pks.im \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.