[PATCH v2 3/3] submodule: add --reversive shorthand to foreach
flat view
From: William Hatfield <hidden>
Date: 2026-10-28 00:15:35
Subsystem:
documentation, the rest · Maintainers:
Jonathan Corbet, Linus Torvalds
Visiting each submodule before its superproject, ending with the top-level superproject, takes all of --recursive, --reverse-traversal and --include-superproject. Those are three options to remember and to type for what is, for this purpose, a single thing. Add --reversive as a shorthand for the three of them. The options keep working on their own, for the cases that need only one or two of them. Signed-off-by: William Hatfield <redacted> --- Documentation/git-submodule.adoc | 14 ++++++++++---- builtin/submodule--helper.c | 11 ++++++++++- contrib/completion/git-completion.bash | 2 +- git-submodule.sh | 7 ++++++- t/t7427-submodule-foreach-reversive.sh | 14 ++++++++++++-- 5 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/Documentation/git-submodule.adoc b/Documentation/git-submodule.adoc
index 5d4b06cb96..8f4bc002cc 100644
--- a/Documentation/git-submodule.adoc
+++ b/Documentation/git-submodule.adoc@@ -18,7 +18,7 @@ git submodule [--quiet] update [<options>] [--] [<path>...] git submodule [--quiet] set-branch [<options>] [--] <path> git submodule [--quiet] set-url [--] <path> <newurl> git submodule [--quiet] summary [<options>] [--] [<path>...] -git submodule [--quiet] foreach [--recursive] [--reverse-traversal] [--include-superproject] <command> +git submodule [--quiet] foreach [--recursive] [--reverse-traversal] [--include-superproject] [--reversive] <command> git submodule [--quiet] sync [--recursive] [--] [<path>...] git submodule [--quiet] absorbgitdirs [--] [<path>...]
@@ -222,7 +222,7 @@ specifications. Using the `--submodule=log` option with linkgit:git-diff[1] will provide that information too. -`foreach [--recursive] [--reverse-traversal] [--include-superproject] <command>`:: +`foreach [--recursive] [--reverse-traversal] [--include-superproject] [--reversive] <command>`:: Evaluate an arbitrary shell _<command>_ in each checked out submodule. The command has access to the variables `$name`, `$sm_path`, `$displaypath`, `$sha1` and `$toplevel`:
@@ -255,6 +255,8 @@ superproject, that is, at the top level of the repository `foreach` was started in, after all the submodules. There, Git sets `$displaypath` to the name of that directory and sets none of the other variables listed above, although they may come from the environment. +`--reversive` is a shorthand for giving `--recursive`, +`--reverse-traversal` and `--include-superproject` together. A non-zero return from the command in any submodule causes the processing to terminate. This can be overridden by adding ++||:++ to the end of the command.
@@ -270,8 +272,7 @@ And this shows the path of each submodule and then of the superproject, visiting the submodules nested in a submodule before the submodule itself: + -------------- -git submodule --quiet foreach --recursive --reverse-traversal \ - --include-superproject 'echo $displaypath' +git submodule --quiet foreach --reversive 'echo $displaypath' -------------- `sync [--recursive] [--] [<path>...]`::
@@ -473,6 +474,11 @@ NOTE: See the NOTE above for the `--reference` option. Also evaluate the command in the superproject, after all the submodules. This option is only valid for the `foreach` command. +`--reversive`:: + A shorthand for `--recursive --reverse-traversal + --include-superproject`. This option is only valid for the `foreach` + command. + `--depth=<depth>`:: Create a 'shallow' clone with a history truncated to the _<depth>_ revisions. This option is valid for `add` and `update` commands. See linkgit:git-clone[1]
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fff5142eda..f923d2e926 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c@@ -485,6 +485,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix, struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int i, j; + int reversive = 0; struct option module_foreach_options[] = { OPT__SUPER_PREFIX(&info.super_prefix), OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
@@ -494,10 +495,12 @@ static int module_foreach(int argc, const char **argv, const char *prefix, N_("visit submodules in the reverse of the usual order")), OPT_BOOL(0, "include-superproject", &info.include_superproject, N_("also run the command in the superproject, last")), + OPT_BOOL(0, "reversive", &reversive, + N_("shorthand for --recursive --reverse-traversal --include-superproject")), OPT_END() }; const char *const git_submodule_helper_usage[] = { - N_("git submodule foreach [--quiet] [--recursive] [--reverse-traversal] [--include-superproject] [--] <command>"), + N_("git submodule foreach [--quiet] [--recursive] [--reverse-traversal] [--include-superproject] [--reversive] [--] <command>"), NULL }; int ret = 1;
@@ -505,6 +508,12 @@ static int module_foreach(int argc, const char **argv, const char *prefix, argc = parse_options(argc, argv, prefix, module_foreach_options, git_submodule_helper_usage, 0); + if (reversive) { + info.recursive = 1; + info.reverse_traversal = 1; + info.include_superproject = 1; + } + if (module_list_compute(NULL, prefix, &pathspec, &list) < 0) goto cleanup;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index f47459d589..69a31316ca 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash@@ -3645,7 +3645,7 @@ _git_submodule () __gitcomp "--cached --files --summary-limit" ;; foreach,--*) - __gitcomp "--recursive --reverse-traversal --include-superproject" + __gitcomp "--recursive --reverse-traversal --include-superproject --reversive" ;; sync,--*) __gitcomp "--recursive"
diff --git a/git-submodule.sh b/git-submodule.sh
index 964f5a8bf0..c86cbe76d2 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh@@ -14,7 +14,7 @@ USAGE="[--quiet] [--cached] or: $dashless [--quiet] set-branch (--default|--branch <branch>) [--] <path> or: $dashless [--quiet] set-url [--] <path> <newurl> or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] - or: $dashless [--quiet] foreach [--recursive] [--reverse-traversal] [--include-superproject] <command> + or: $dashless [--quiet] foreach [--recursive] [--reverse-traversal] [--include-superproject] [--reversive] <command> or: $dashless [--quiet] sync [--recursive] [--] [<path>...] or: $dashless [--quiet] absorbgitdirs [--] [<path>...]" OPTIONS_SPEC=
@@ -38,6 +38,7 @@ cached= recursive= reverse_traversal= include_superproject= +reversive= init= require_init= files=
@@ -181,6 +182,9 @@ cmd_foreach() --include-superproject) include_superproject=$1 ;; + --reversive) + reversive=$1 + ;; -*) usage ;;
@@ -196,6 +200,7 @@ cmd_foreach() $recursive \ $reverse_traversal \ $include_superproject \ + $reversive \ -- \ "$@" }
diff --git a/t/t7427-submodule-foreach-reversive.sh b/t/t7427-submodule-foreach-reversive.sh
index a53ea0b851..d5aa29f08e 100755
--- a/t/t7427-submodule-foreach-reversive.sh
+++ b/t/t7427-submodule-foreach-reversive.sh@@ -3,13 +3,14 @@ # Copyright (c) 2026 William Hatfield # -test_description='git submodule foreach --reverse-traversal and --include-superproject +test_description='git submodule foreach --reverse-traversal, --include-superproject and --reversive Check that --reverse-traversal visits submodules in the reverse of their usual order, and that, together with --recursive, each submodule is visited after the submodules nested in it. Check that --include-superproject also runs the command in the superproject, after -all the submodules. +all the submodules, and that --reversive is a shorthand for all three of +these options. ' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
@@ -416,4 +417,13 @@ test_expect_success 'committing in the submodules and the superproject leaves no done ' +test_expect_success '--reversive is --recursive --reverse-traversal --include-superproject' ' + git -C reversive/top submodule --quiet foreach --recursive \ + --reverse-traversal --include-superproject \ + "echo \$displaypath" >expect && + git -C reversive/top submodule --quiet foreach --reversive \ + "echo \$displaypath" >actual && + test_cmp expect actual +' + test_done
--
2.56.0