Skip to content

Commit 06bf4ad

Browse files
bmwillgitster
authored andcommitted
push: propagate remote and refspec with --recurse-submodules
Teach "push --recurse-submodules" to propagate, if given a name as remote, the provided remote and refspec recursively to the pushes performed in the submodules. The push will therefore only succeed if all submodules have a remote with such a name configured. Note that "push --recurse-submodules" with a path or URL as remote will not propagate the remote or refspec and instead use the default remote and refspec configured in the submodule, preserving the current behavior. Signed-off-by: Brandon Williams <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 93481a6 commit 06bf4ad

File tree

4 files changed

+117
-5
lines changed

4 files changed

+117
-5
lines changed

submodule.c

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "blob.h"
1515
#include "thread-utils.h"
1616
#include "quote.h"
17+
#include "remote.h"
1718
#include "worktree.h"
1819

1920
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
@@ -783,6 +784,8 @@ int find_unpushed_submodules(struct sha1_array *commits,
783784
}
784785

785786
static int push_submodule(const char *path,
787+
const struct remote *remote,
788+
const char **refspec, int refspec_nr,
786789
const struct string_list *push_options,
787790
int dry_run)
788791
{
@@ -801,6 +804,14 @@ static int push_submodule(const char *path,
801804
argv_array_pushf(&cp.args, "--push-option=%s",
802805
item->string);
803806
}
807+
808+
if (remote->origin != REMOTE_UNCONFIGURED) {
809+
int i;
810+
argv_array_push(&cp.args, remote->name);
811+
for (i = 0; i < refspec_nr; i++)
812+
argv_array_push(&cp.args, refspec[i]);
813+
}
814+
804815
prepare_submodule_repo_env(&cp.env_array);
805816
cp.git_cmd = 1;
806817
cp.no_stdin = 1;
@@ -813,21 +824,67 @@ static int push_submodule(const char *path,
813824
return 1;
814825
}
815826

827+
/*
828+
* Perform a check in the submodule to see if the remote and refspec work.
829+
* Die if the submodule can't be pushed.
830+
*/
831+
static void submodule_push_check(const char *path, const struct remote *remote,
832+
const char **refspec, int refspec_nr)
833+
{
834+
struct child_process cp = CHILD_PROCESS_INIT;
835+
int i;
836+
837+
argv_array_push(&cp.args, "submodule--helper");
838+
argv_array_push(&cp.args, "push-check");
839+
argv_array_push(&cp.args, remote->name);
840+
841+
for (i = 0; i < refspec_nr; i++)
842+
argv_array_push(&cp.args, refspec[i]);
843+
844+
prepare_submodule_repo_env(&cp.env_array);
845+
cp.git_cmd = 1;
846+
cp.no_stdin = 1;
847+
cp.no_stdout = 1;
848+
cp.dir = path;
849+
850+
/*
851+
* Simply indicate if 'submodule--helper push-check' failed.
852+
* More detailed error information will be provided by the
853+
* child process.
854+
*/
855+
if (run_command(&cp))
856+
die("process for submodule '%s' failed", path);
857+
}
858+
816859
int push_unpushed_submodules(struct sha1_array *commits,
817-
const char *remotes_name,
860+
const struct remote *remote,
861+
const char **refspec, int refspec_nr,
818862
const struct string_list *push_options,
819863
int dry_run)
820864
{
821865
int i, ret = 1;
822866
struct string_list needs_pushing = STRING_LIST_INIT_DUP;
823867

824-
if (!find_unpushed_submodules(commits, remotes_name, &needs_pushing))
868+
if (!find_unpushed_submodules(commits, remote->name, &needs_pushing))
825869
return 1;
826870

871+
/*
872+
* Verify that the remote and refspec can be propagated to all
873+
* submodules. This check can be skipped if the remote and refspec
874+
* won't be propagated due to the remote being unconfigured (e.g. a URL
875+
* instead of a remote name).
876+
*/
877+
if (remote->origin != REMOTE_UNCONFIGURED)
878+
for (i = 0; i < needs_pushing.nr; i++)
879+
submodule_push_check(needs_pushing.items[i].string,
880+
remote, refspec, refspec_nr);
881+
882+
/* Actually push the submodules */
827883
for (i = 0; i < needs_pushing.nr; i++) {
828884
const char *path = needs_pushing.items[i].string;
829885
fprintf(stderr, "Pushing submodule '%s'\n", path);
830-
if (!push_submodule(path, push_options, dry_run)) {
886+
if (!push_submodule(path, remote, refspec, refspec_nr,
887+
push_options, dry_run)) {
831888
fprintf(stderr, "Unable to push submodule '%s'\n", path);
832889
ret = 0;
833890
}

submodule.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
struct diff_options;
55
struct argv_array;
66
struct sha1_array;
7+
struct remote;
78

89
enum {
910
RECURSE_SUBMODULES_ONLY = -5,
@@ -91,7 +92,8 @@ extern int find_unpushed_submodules(struct sha1_array *commits,
9192
const char *remotes_name,
9293
struct string_list *needs_pushing);
9394
extern int push_unpushed_submodules(struct sha1_array *commits,
94-
const char *remotes_name,
95+
const struct remote *remote,
96+
const char **refspec, int refspec_nr,
9597
const struct string_list *push_options,
9698
int dry_run);
9799
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);

t/t5531-deep-submodule-push.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,4 +475,56 @@ test_expect_success 'push only unpushed submodules recursively' '
475475
test_cmp expected_pub actual_pub
476476
'
477477

478+
test_expect_success 'push propagating the remotes name to a submodule' '
479+
git -C work remote add origin ../pub.git &&
480+
git -C work remote add pub ../pub.git &&
481+
482+
> work/gar/bage/junk10 &&
483+
git -C work/gar/bage add junk10 &&
484+
git -C work/gar/bage commit -m "Tenth junk" &&
485+
git -C work add gar/bage &&
486+
git -C work commit -m "Tenth junk added to gar/bage" &&
487+
488+
# Fails when submodule does not have a matching remote
489+
test_must_fail git -C work push --recurse-submodules=on-demand pub master &&
490+
# Succeeds when submodules has matching remote and refspec
491+
git -C work push --recurse-submodules=on-demand origin master &&
492+
493+
git -C submodule.git rev-parse master >actual_submodule &&
494+
git -C pub.git rev-parse master >actual_pub &&
495+
git -C work/gar/bage rev-parse master >expected_submodule &&
496+
git -C work rev-parse master >expected_pub &&
497+
test_cmp expected_submodule actual_submodule &&
498+
test_cmp expected_pub actual_pub
499+
'
500+
501+
test_expect_success 'push propagating refspec to a submodule' '
502+
> work/gar/bage/junk11 &&
503+
git -C work/gar/bage add junk11 &&
504+
git -C work/gar/bage commit -m "Eleventh junk" &&
505+
506+
git -C work checkout branch2 &&
507+
git -C work add gar/bage &&
508+
git -C work commit -m "updating gar/bage in branch2" &&
509+
510+
# Fails when submodule does not have a matching branch
511+
test_must_fail git -C work push --recurse-submodules=on-demand origin branch2 &&
512+
# Fails when refspec includes an object id
513+
test_must_fail git -C work push --recurse-submodules=on-demand origin \
514+
"$(git -C work rev-parse branch2):refs/heads/branch2" &&
515+
# Fails when refspec includes 'HEAD' as it is unsupported at this time
516+
test_must_fail git -C work push --recurse-submodules=on-demand origin \
517+
HEAD:refs/heads/branch2 &&
518+
519+
git -C work/gar/bage branch branch2 master &&
520+
git -C work push --recurse-submodules=on-demand origin branch2 &&
521+
522+
git -C submodule.git rev-parse branch2 >actual_submodule &&
523+
git -C pub.git rev-parse branch2 >actual_pub &&
524+
git -C work/gar/bage rev-parse branch2 >expected_submodule &&
525+
git -C work rev-parse branch2 >expected_pub &&
526+
test_cmp expected_submodule actual_submodule &&
527+
test_cmp expected_pub actual_pub
528+
'
529+
478530
test_done

transport.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,8 @@ int transport_push(struct transport *transport,
10301030
sha1_array_append(&commits, ref->new_oid.hash);
10311031

10321032
if (!push_unpushed_submodules(&commits,
1033-
transport->remote->name,
1033+
transport->remote,
1034+
refspec, refspec_nr,
10341035
transport->push_options,
10351036
pretend)) {
10361037
sha1_array_clear(&commits);

0 commit comments

Comments
 (0)