Skip to content

Commit eb21c73

Browse files
hvoigtgitster
authored andcommitted
push: teach --recurse-submodules the on-demand option
When using this option git will search for all submodules that have changed in the revisions to be send. It will then try to push the currently checked out branch of each submodule. This helps when a user has finished working on a change which involves submodules and just wants to push everything in one go. Signed-off-by: Fredrik Gustafsson <[email protected]> Mentored-by: Jens Lehmann <[email protected]> Mentored-by: Heiko Voigt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a762e51 commit eb21c73

File tree

7 files changed

+179
-6
lines changed

7 files changed

+179
-6
lines changed

Documentation/git-push.txt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,16 @@ useful if you write an alias or script around 'git push'.
162162
is specified. This flag forces progress status even if the
163163
standard error stream is not directed to a terminal.
164164

165-
--recurse-submodules=check::
166-
Check whether all submodule commits used by the revisions to be
167-
pushed are available on a remote tracking branch. Otherwise the
168-
push will be aborted and the command will exit with non-zero status.
165+
--recurse-submodules=check|on-demand::
166+
Make sure all submodule commits used by the revisions to be
167+
pushed are available on a remote tracking branch. If 'check' is
168+
used git will verify that all submodule commits that changed in
169+
the revisions to be pushed are available on at least one remote
170+
of the submodule. If any commits are missing the push will be
171+
aborted and exit with non-zero status. If 'on-demand' is used
172+
all submodules that changed in the revisions to be pushed will
173+
be pushed. If on-demand was not able to push all necessary
174+
revisions it will also be aborted and exit with non-zero status.
169175

170176

171177
include::urls-remotes.txt[]

builtin/push.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,21 @@ static int option_parse_recurse_submodules(const struct option *opt,
224224
const char *arg, int unset)
225225
{
226226
int *flags = opt->value;
227+
228+
if (*flags & (TRANSPORT_RECURSE_SUBMODULES_CHECK |
229+
TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND))
230+
die("%s can only be used once.", opt->long_name);
231+
227232
if (arg) {
228233
if (!strcmp(arg, "check"))
229234
*flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
235+
else if (!strcmp(arg, "on-demand"))
236+
*flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND;
230237
else
231238
die("bad %s argument: %s", opt->long_name, arg);
232239
} else
233-
die("option %s needs an argument (check)", opt->long_name);
240+
die("option %s needs an argument (check|on-demand)",
241+
opt->long_name);
234242

235243
return 0;
236244
}

submodule.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,54 @@ int find_unpushed_submodules(unsigned char new_sha1[20],
410410
return needs_pushing->nr;
411411
}
412412

413+
static int push_submodule(const char *path)
414+
{
415+
if (add_submodule_odb(path))
416+
return 1;
417+
418+
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
419+
struct child_process cp;
420+
const char *argv[] = {"push", NULL};
421+
422+
memset(&cp, 0, sizeof(cp));
423+
cp.argv = argv;
424+
cp.env = local_repo_env;
425+
cp.git_cmd = 1;
426+
cp.no_stdin = 1;
427+
cp.dir = path;
428+
if (run_command(&cp))
429+
return 0;
430+
close(cp.out);
431+
}
432+
433+
return 1;
434+
}
435+
436+
int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
437+
{
438+
int i, ret = 1;
439+
struct string_list needs_pushing;
440+
441+
memset(&needs_pushing, 0, sizeof(struct string_list));
442+
needs_pushing.strdup_strings = 1;
443+
444+
if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
445+
return 1;
446+
447+
for (i = 0; i < needs_pushing.nr; i++) {
448+
const char *path = needs_pushing.items[i].string;
449+
fprintf(stderr, "Pushing submodule '%s'\n", path);
450+
if (!push_submodule(path)) {
451+
fprintf(stderr, "Unable to push submodule '%s'\n", path);
452+
ret = 0;
453+
}
454+
}
455+
456+
string_list_clear(&needs_pushing, 0);
457+
458+
return ret;
459+
}
460+
413461
static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
414462
{
415463
int is_present = 0;

submodule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c
3131
const unsigned char a[20], const unsigned char b[20], int search);
3232
int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
3333
struct string_list *needs_pushing);
34+
int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
3435

3536
#endif

t/t5531-deep-submodule-push.sh

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,98 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs
119119
)
120120
'
121121

122+
test_expect_success 'push unpushed submodules when not needed' '
123+
(
124+
cd work &&
125+
(
126+
cd gar/bage &&
127+
git checkout master &&
128+
>junk5 &&
129+
git add junk5 &&
130+
git commit -m "Fifth junk" &&
131+
git push &&
132+
git rev-parse origin/master >../../../expected
133+
) &&
134+
git checkout master &&
135+
git add gar/bage &&
136+
git commit -m "Fifth commit for gar/bage" &&
137+
git push --recurse-submodules=on-demand ../pub.git master
138+
) &&
139+
(
140+
cd submodule.git &&
141+
git rev-parse master >../actual
142+
) &&
143+
test_cmp expected actual
144+
'
145+
146+
test_expect_success 'push unpushed submodules when not needed 2' '
147+
(
148+
cd submodule.git &&
149+
git rev-parse master >../expected
150+
) &&
151+
(
152+
cd work &&
153+
(
154+
cd gar/bage &&
155+
>junk6 &&
156+
git add junk6 &&
157+
git commit -m "Sixth junk"
158+
) &&
159+
>junk2 &&
160+
git add junk2 &&
161+
git commit -m "Second junk for work" &&
162+
git push --recurse-submodules=on-demand ../pub.git master
163+
) &&
164+
(
165+
cd submodule.git &&
166+
git rev-parse master >../actual
167+
) &&
168+
test_cmp expected actual
169+
'
170+
171+
test_expect_success 'push unpushed submodules recursively' '
172+
(
173+
cd work &&
174+
(
175+
cd gar/bage &&
176+
git checkout master &&
177+
> junk7 &&
178+
git add junk7 &&
179+
git commit -m "Seventh junk" &&
180+
git rev-parse master >../../../expected
181+
) &&
182+
git checkout master &&
183+
git add gar/bage &&
184+
git commit -m "Seventh commit for gar/bage" &&
185+
git push --recurse-submodules=on-demand ../pub.git master
186+
) &&
187+
(
188+
cd submodule.git &&
189+
git rev-parse master >../actual
190+
) &&
191+
test_cmp expected actual
192+
'
193+
194+
test_expect_success 'push unpushable submodule recursively fails' '
195+
(
196+
cd work &&
197+
(
198+
cd gar/bage &&
199+
git rev-parse origin/master >../../../expected &&
200+
git checkout master~0 &&
201+
> junk8 &&
202+
git add junk8 &&
203+
git commit -m "Eighth junk"
204+
) &&
205+
git add gar/bage &&
206+
git commit -m "Eighth commit for gar/bage" &&
207+
test_must_fail git push --recurse-submodules=on-demand ../pub.git master
208+
) &&
209+
(
210+
cd submodule.git &&
211+
git rev-parse master >../actual
212+
) &&
213+
test_cmp expected actual
214+
'
215+
122216
test_done

transport.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,11 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing)
10091009
"not be found on any remote:\n");
10101010
for (i = 0; i < needs_pushing->nr; i++)
10111011
printf(" %s\n", needs_pushing->items[i].string);
1012+
fprintf(stderr, "\nPlease try\n\n"
1013+
" git push --recurse-submodules=on-demand\n\n"
1014+
"or cd to the path and use\n\n"
1015+
" git push\n\n"
1016+
"to push them to a remote.\n\n");
10121017

10131018
string_list_clear(needs_pushing, 0);
10141019

@@ -1053,7 +1058,17 @@ int transport_push(struct transport *transport,
10531058
flags & TRANSPORT_PUSH_MIRROR,
10541059
flags & TRANSPORT_PUSH_FORCE);
10551060

1056-
if ((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) && !is_bare_repository()) {
1061+
if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
1062+
struct ref *ref = remote_refs;
1063+
for (; ref; ref = ref->next)
1064+
if (!is_null_sha1(ref->new_sha1) &&
1065+
!push_unpushed_submodules(ref->new_sha1,
1066+
transport->remote->name))
1067+
die ("Failed to push all needed submodules!");
1068+
}
1069+
1070+
if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
1071+
TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
10571072
struct ref *ref = remote_refs;
10581073
struct string_list needs_pushing;
10591074

transport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ struct transport {
102102
#define TRANSPORT_PUSH_PORCELAIN 16
103103
#define TRANSPORT_PUSH_SET_UPSTREAM 32
104104
#define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
105+
#define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
105106

106107
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
107108

0 commit comments

Comments
 (0)