Doc: document push.recurseSubmodules=only

Git learned pushing submodules without pushing the superproject by
the user specifying --recurse-submodules=only through 6c656c3fe4
("submodules: add RECURSE_SUBMODULES_ONLY value", 2016-12-20) and
225e8bf778 ("push: add option to push only submodules", 2016-12-20).
For users who use this feature regularly, it is desirable to have an
equivalent configuration.

It turns out that such a configuration (push.recurseSubmodules=only) is
already supported, even though it is neither documented nor mentioned
in the commit messages, due to the way the --recurse-submodules=only
feature was implemented (a function used to parse --recurse-submodules
was updated to support "only", but that same function is used to parse
push.recurseSubmodules too). What is left is to document it and test it,
which is what this commit does.

There is a possible point of confusion when recursing into a submodule
that itself has the push.recurseSubmodules=only configuration, because
if a repository has only its submodules pushed and not itself, its
superproject can never be pushed. Therefore, treat such configurations
as being "on-demand", and print a warning message.

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
This commit is contained in:
Jonathan Tan 2022-11-14 13:37:12 -08:00 committed by Taylor Blau
parent 3b08839926
commit e62f779ae6
5 changed files with 73 additions and 15 deletions

View File

@ -110,18 +110,8 @@ This will result in only b (a and c are cleared).
----
push.recurseSubmodules::
Make sure all submodule commits used by the revisions to be pushed
are available on a remote-tracking branch. If the value is 'check'
then Git will verify that all submodule commits that changed in the
revisions to be pushed are available on at least one remote of the
submodule. If any commits are missing, the push will be aborted and
exit with non-zero status. If the value is 'on-demand' then all
submodules that changed in the revisions to be pushed will be
pushed. If on-demand was not able to push all necessary revisions
it will also be aborted and exit with non-zero status. If the value
is 'no' then default behavior of ignoring submodules when pushing
is retained. You may override this configuration at time of push by
specifying '--recurse-submodules=check|on-demand|no'.
May be "check", "on-demand", "only", or "no", with the same behavior
as that of "push --recurse-submodules".
If not set, 'no' is used by default, unless 'submodule.recurse' is
set (in which case a 'true' value means 'on-demand').

View File

@ -409,10 +409,14 @@ Specifying `--no-force-if-includes` disables this behavior.
all submodules that changed in the revisions to be pushed will be
pushed. If on-demand was not able to push all necessary revisions it will
also be aborted and exit with non-zero status. If 'only' is used all
submodules will be recursively pushed while the superproject is left
submodules will be pushed while the superproject is left
unpushed. A value of 'no' or using `--no-recurse-submodules` can be used
to override the push.recurseSubmodules configuration variable when no
submodule recursion is required.
+
When using 'on-demand' or 'only', if a submodule has a
"push.recurseSubmodules={on-demand,only}" or "submodule.recurse" configuration,
further recursion will occur. In this case, "only" is treated as "on-demand".
--[no-]verify::
Toggle the pre-push hook (see linkgit:githooks[5]). The

View File

@ -466,8 +466,16 @@ static int option_parse_recurse_submodules(const struct option *opt,
if (unset)
*recurse_submodules = RECURSE_SUBMODULES_OFF;
else
*recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
else {
if (!strcmp(arg, "only-is-on-demand")) {
if (*recurse_submodules == RECURSE_SUBMODULES_ONLY) {
warning(_("recursing into submodule with push.recurseSubmodules=only; using on-demand instead"));
*recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
}
} else {
*recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
}
}
return 0;
}

View File

@ -1130,6 +1130,12 @@ static int push_submodule(const char *path,
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
struct child_process cp = CHILD_PROCESS_INIT;
strvec_push(&cp.args, "push");
/*
* When recursing into a submodule, treat any "only" configurations as "on-
* demand", since "only" would not work (we need all submodules to be pushed
* in order to be able to push the superproject).
*/
strvec_push(&cp.args, "--recurse-submodules=only-is-on-demand");
if (dry_run)
strvec_push(&cp.args, "--dry-run");

View File

@ -512,6 +512,56 @@ test_expect_success 'push only unpushed submodules recursively' '
test_cmp expected_pub actual_pub
'
setup_subsub () {
git init upstream &&
git init upstream/sub &&
git init upstream/sub/deepsub &&
test_commit -C upstream/sub/deepsub innermost &&
git -C upstream/sub submodule add ./deepsub deepsub &&
git -C upstream/sub commit -m middle &&
git -C upstream submodule add ./sub sub &&
git -C upstream commit -m outermost &&
git -c protocol.file.allow=always clone --recurse-submodules upstream downstream &&
git -C downstream/sub/deepsub checkout -b downstream-branch &&
git -C downstream/sub checkout -b downstream-branch &&
git -C downstream checkout -b downstream-branch
}
new_downstream_commits () {
test_commit -C downstream/sub/deepsub new-innermost &&
git -C downstream/sub add deepsub &&
git -C downstream/sub commit -m new-middle &&
git -C downstream add sub &&
git -C downstream commit -m new-outermost
}
test_expect_success 'push with push.recurseSubmodules=only on superproject' '
test_when_finished rm -rf upstream downstream &&
setup_subsub &&
new_downstream_commits &&
git -C downstream config push.recurseSubmodules only &&
git -C downstream push origin downstream-branch &&
test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
git -C upstream/sub rev-parse refs/heads/downstream-branch &&
test_must_fail git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch
'
test_expect_success 'push with push.recurseSubmodules=only on superproject and top-level submodule' '
test_when_finished rm -rf upstream downstream &&
setup_subsub &&
new_downstream_commits &&
git -C downstream config push.recurseSubmodules only &&
git -C downstream/sub config push.recurseSubmodules only &&
git -C downstream push origin downstream-branch 2> err &&
test_must_fail git -C upstream rev-parse refs/heads/downstream-branch &&
git -C upstream/sub rev-parse refs/heads/downstream-branch &&
git -C upstream/sub/deepsub rev-parse refs/heads/downstream-branch &&
grep "recursing into submodule with push.recurseSubmodules=only; using on-demand instead" err
'
test_expect_success 'push propagating the remotes name to a submodule' '
git -C work remote add origin ../pub.git &&
git -C work remote add pub ../pub.git &&