Skip to content

Commit b33a15b

Browse files
mikecrowepeff
authored andcommitted
push: add recurseSubmodules config option
The --recurse-submodules command line parameter has existed for some time but it has no config file equivalent. Following the style of the corresponding parameter for git fetch, let's invent push.recurseSubmodules to provide a default for this parameter. This also requires the addition of --recurse-submodules=no to allow the configuration to be overridden on the command line when required. The most straightforward way to implement this appears to be to make push use code in submodule-config in a similar way to fetch. Signed-off-by: Mike Crowe <[email protected]> Signed-off-by: Jeff King <[email protected]>
1 parent 0c83680 commit b33a15b

File tree

7 files changed

+234
-26
lines changed

7 files changed

+234
-26
lines changed

Documentation/config.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,6 +2226,20 @@ push.gpgSign::
22262226
override a value from a lower-priority config file. An explicit
22272227
command-line flag always overrides this config option.
22282228

2229+
push.recurseSubmodules::
2230+
Make sure all submodule commits used by the revisions to be pushed
2231+
are available on a remote-tracking branch. If the value is 'check'
2232+
then Git will verify that all submodule commits that changed in the
2233+
revisions to be pushed are available on at least one remote of the
2234+
submodule. If any commits are missing, the push will be aborted and
2235+
exit with non-zero status. If the value is 'on-demand' then all
2236+
submodules that changed in the revisions to be pushed will be
2237+
pushed. If on-demand was not able to push all necessary revisions
2238+
it will also be aborted and exit with non-zero status. If the value
2239+
is 'no' then default behavior of ignoring submodules when pushing
2240+
is retained. You may override this configuration at time of push by
2241+
specifying '--recurse-submodules=check|on-demand|no'.
2242+
22292243
rebase.stat::
22302244
Whether to show a diffstat of what changed upstream since the last
22312245
rebase. False by default.

Documentation/git-push.txt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -257,16 +257,20 @@ origin +master` to force a push to the `master` branch). See the
257257
is specified. This flag forces progress status even if the
258258
standard error stream is not directed to a terminal.
259259

260-
--recurse-submodules=check|on-demand::
261-
Make sure all submodule commits used by the revisions to be
262-
pushed are available on a remote-tracking branch. If 'check' is
263-
used Git will verify that all submodule commits that changed in
264-
the revisions to be pushed are available on at least one remote
265-
of the submodule. If any commits are missing the push will be
266-
aborted and exit with non-zero status. If 'on-demand' is used
267-
all submodules that changed in the revisions to be pushed will
268-
be pushed. If on-demand was not able to push all necessary
269-
revisions it will also be aborted and exit with non-zero status.
260+
--no-recurse-submodules::
261+
--recurse-submodules=check|on-demand|no::
262+
May be used to make sure all submodule commits used by the
263+
revisions to be pushed are available on a remote-tracking branch.
264+
If 'check' is used Git will verify that all submodule commits that
265+
changed in the revisions to be pushed are available on at least one
266+
remote of the submodule. If any commits are missing the push will
267+
be aborted and exit with non-zero status. If 'on-demand' is used
268+
all submodules that changed in the revisions to be pushed will be
269+
pushed. If on-demand was not able to push all necessary revisions
270+
it will also be aborted and exit with non-zero status. A value of
271+
'no' or using '--no-recurse-submodules' can be used to override the
272+
push.recurseSubmodules configuration variable when no submodule
273+
recursion is required.
270274

271275
--[no-]verify::
272276
Toggle the pre-push hook (see linkgit:githooks[5]). The

builtin/push.c

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "transport.h"
1010
#include "parse-options.h"
1111
#include "submodule.h"
12+
#include "submodule-config.h"
1213
#include "send-pack.h"
1314

1415
static const char * const push_usage[] = {
@@ -20,7 +21,7 @@ static int thin = 1;
2021
static int deleterefs;
2122
static const char *receivepack;
2223
static int verbosity;
23-
static int progress = -1;
24+
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
2425

2526
static struct push_cas_option cas;
2627

@@ -452,22 +453,17 @@ static int do_push(const char *repo, int flags)
452453
static int option_parse_recurse_submodules(const struct option *opt,
453454
const char *arg, int unset)
454455
{
455-
int *flags = opt->value;
456+
int *recurse_submodules = opt->value;
456457

457-
if (*flags & (TRANSPORT_RECURSE_SUBMODULES_CHECK |
458-
TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND))
458+
if (*recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
459459
die("%s can only be used once.", opt->long_name);
460460

461-
if (arg) {
462-
if (!strcmp(arg, "check"))
463-
*flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
464-
else if (!strcmp(arg, "on-demand"))
465-
*flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND;
466-
else
467-
die("bad %s argument: %s", opt->long_name, arg);
468-
} else
469-
die("option %s needs an argument (check|on-demand)",
470-
opt->long_name);
461+
if (unset)
462+
*recurse_submodules = RECURSE_SUBMODULES_OFF;
463+
else if (arg)
464+
*recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
465+
else
466+
die("%s missing parameter", opt->long_name);
471467

472468
return 0;
473469
}
@@ -522,6 +518,10 @@ static int git_push_config(const char *k, const char *v, void *cb)
522518
return error("Invalid value for '%s'", k);
523519
}
524520
}
521+
} else if (!strcmp(k, "push.recursesubmodules")) {
522+
const char *value;
523+
if (!git_config_get_value("push.recursesubmodules", &value))
524+
recurse_submodules = parse_push_recurse_submodules_arg(k, value);
525525
}
526526

527527
return git_default_config(k, v, NULL);
@@ -532,6 +532,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
532532
int flags = 0;
533533
int tags = 0;
534534
int push_cert = -1;
535+
int recurse_submodules_from_cmdline = RECURSE_SUBMODULES_DEFAULT;
535536
int rc;
536537
const char *repo = NULL; /* default repository */
537538
struct option options[] = {
@@ -549,7 +550,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
549550
0, CAS_OPT_NAME, &cas, N_("refname>:<expect"),
550551
N_("require old value of ref to be at this value"),
551552
PARSE_OPT_OPTARG, parseopt_push_cas_option },
552-
{ OPTION_CALLBACK, 0, "recurse-submodules", &flags, "check|on-demand",
553+
{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules_from_cmdline, N_("check|on-demand|no"),
553554
N_("control recursive pushing of submodules"),
554555
PARSE_OPT_OPTARG, option_parse_recurse_submodules },
555556
OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")),
@@ -580,6 +581,14 @@ int cmd_push(int argc, const char **argv, const char *prefix)
580581
if (deleterefs && argc < 2)
581582
die(_("--delete doesn't make sense without any refs"));
582583

584+
if (recurse_submodules_from_cmdline != RECURSE_SUBMODULES_DEFAULT)
585+
recurse_submodules = recurse_submodules_from_cmdline;
586+
587+
if (recurse_submodules == RECURSE_SUBMODULES_CHECK)
588+
flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
589+
else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
590+
flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND;
591+
583592
if (tags)
584593
add_refspec("refs/tags/*");
585594

submodule-config.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,35 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
228228
return parse_fetch_recurse(opt, arg, 1);
229229
}
230230

231+
static int parse_push_recurse(const char *opt, const char *arg,
232+
int die_on_error)
233+
{
234+
switch (git_config_maybe_bool(opt, arg)) {
235+
case 1:
236+
/* There's no simple "on" value when pushing */
237+
if (die_on_error)
238+
die("bad %s argument: %s", opt, arg);
239+
else
240+
return RECURSE_SUBMODULES_ERROR;
241+
case 0:
242+
return RECURSE_SUBMODULES_OFF;
243+
default:
244+
if (!strcmp(arg, "on-demand"))
245+
return RECURSE_SUBMODULES_ON_DEMAND;
246+
else if (!strcmp(arg, "check"))
247+
return RECURSE_SUBMODULES_CHECK;
248+
else if (die_on_error)
249+
die("bad %s argument: %s", opt, arg);
250+
else
251+
return RECURSE_SUBMODULES_ERROR;
252+
}
253+
}
254+
255+
int parse_push_recurse_submodules_arg(const char *opt, const char *arg)
256+
{
257+
return parse_push_recurse(opt, arg, 1);
258+
}
259+
231260
static void warn_multiple_config(const unsigned char *commit_sha1,
232261
const char *name, const char *option)
233262
{

submodule-config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct submodule {
1919
};
2020

2121
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
22+
int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
2223
int parse_submodule_config_option(const char *var, const char *value);
2324
const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
2425
const char *name);

submodule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ struct diff_options;
55
struct argv_array;
66

77
enum {
8+
RECURSE_SUBMODULES_CHECK = -4,
89
RECURSE_SUBMODULES_ERROR = -3,
910
RECURSE_SUBMODULES_NONE = -2,
1011
RECURSE_SUBMODULES_ON_DEMAND = -1,

t/t5531-deep-submodule-push.sh

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ test_expect_success 'push fails if submodule commit not on remote' '
6464
cd work &&
6565
git add gar/bage &&
6666
git commit -m "Third commit for gar/bage" &&
67-
test_must_fail git push --recurse-submodules=check ../pub.git master
67+
# the push should fail with --recurse-submodules=check
68+
# on the command line...
69+
test_must_fail git push --recurse-submodules=check ../pub.git master &&
70+
71+
# ...or if specified in the configuration..
72+
test_must_fail git -c push.recurseSubmodules=check push ../pub.git master
6873
)
6974
'
7075

@@ -79,6 +84,151 @@ test_expect_success 'push succeeds after commit was pushed to remote' '
7984
)
8085
'
8186

87+
test_expect_success 'push succeeds if submodule commit not on remote but using on-demand on command line' '
88+
(
89+
cd work/gar/bage &&
90+
>recurse-on-demand-on-command-line &&
91+
git add recurse-on-demand-on-command-line &&
92+
git commit -m "Recurse on-demand on command line junk"
93+
) &&
94+
(
95+
cd work &&
96+
git add gar/bage &&
97+
git commit -m "Recurse on-demand on command line for gar/bage" &&
98+
git push --recurse-submodules=on-demand ../pub.git master &&
99+
# Check that the supermodule commit got there
100+
git fetch ../pub.git &&
101+
git diff --quiet FETCH_HEAD master &&
102+
# Check that the submodule commit got there too
103+
cd gar/bage &&
104+
git diff --quiet origin/master master
105+
)
106+
'
107+
108+
test_expect_success 'push succeeds if submodule commit not on remote but using on-demand from config' '
109+
(
110+
cd work/gar/bage &&
111+
>recurse-on-demand-from-config &&
112+
git add recurse-on-demand-from-config &&
113+
git commit -m "Recurse on-demand from config junk"
114+
) &&
115+
(
116+
cd work &&
117+
git add gar/bage &&
118+
git commit -m "Recurse on-demand from config for gar/bage" &&
119+
git -c push.recurseSubmodules=on-demand push ../pub.git master &&
120+
# Check that the supermodule commit got there
121+
git fetch ../pub.git &&
122+
git diff --quiet FETCH_HEAD master &&
123+
# Check that the submodule commit got there too
124+
cd gar/bage &&
125+
git diff --quiet origin/master master
126+
)
127+
'
128+
129+
test_expect_success 'push fails if submodule commit not on remote using check from cmdline overriding config' '
130+
(
131+
cd work/gar/bage &&
132+
>recurse-check-on-command-line-overriding-config &&
133+
git add recurse-check-on-command-line-overriding-config &&
134+
git commit -m "Recurse on command-line overridiing config junk"
135+
) &&
136+
(
137+
cd work &&
138+
git add gar/bage &&
139+
git commit -m "Recurse on command-line overriding config for gar/bage" &&
140+
test_must_fail git -c push.recurseSubmodules=on-demand push --recurse-submodules=check ../pub.git master &&
141+
# Check that the supermodule commit did not get there
142+
git fetch ../pub.git &&
143+
git diff --quiet FETCH_HEAD master^ &&
144+
# Check that the submodule commit did not get there
145+
cd gar/bage &&
146+
git diff --quiet origin/master master^
147+
)
148+
'
149+
150+
test_expect_success 'push succeeds if submodule commit not on remote using on-demand from cmdline overriding config' '
151+
(
152+
cd work/gar/bage &&
153+
>recurse-on-demand-on-command-line-overriding-config &&
154+
git add recurse-on-demand-on-command-line-overriding-config &&
155+
git commit -m "Recurse on-demand on command-line overriding config junk"
156+
) &&
157+
(
158+
cd work &&
159+
git add gar/bage &&
160+
git commit -m "Recurse on-demand on command-line overriding config for gar/bage" &&
161+
git -c push.recurseSubmodules=check push --recurse-submodules=on-demand ../pub.git master &&
162+
# Check that the supermodule commit got there
163+
git fetch ../pub.git &&
164+
git diff --quiet FETCH_HEAD master &&
165+
# Check that the submodule commit got there
166+
cd gar/bage &&
167+
git diff --quiet origin/master master
168+
)
169+
'
170+
171+
test_expect_success 'push succeeds if submodule commit disabling recursion from cmdline overriding config' '
172+
(
173+
cd work/gar/bage &&
174+
>recurse-disable-on-command-line-overriding-config &&
175+
git add recurse-disable-on-command-line-overriding-config &&
176+
git commit -m "Recurse disable on command-line overriding config junk"
177+
) &&
178+
(
179+
cd work &&
180+
git add gar/bage &&
181+
git commit -m "Recurse disable on command-line overriding config for gar/bage" &&
182+
git -c push.recurseSubmodules=check push --recurse-submodules=no ../pub.git master &&
183+
# Check that the supermodule commit got there
184+
git fetch ../pub.git &&
185+
git diff --quiet FETCH_HEAD master &&
186+
# But that the submodule commit did not
187+
( cd gar/bage && git diff --quiet origin/master master^ ) &&
188+
# Now push it to avoid confusing future tests
189+
git push --recurse-submodules=on-demand ../pub.git master
190+
)
191+
'
192+
193+
test_expect_success 'push succeeds if submodule commit disabling recursion from cmdline (alternative form) overriding config' '
194+
(
195+
cd work/gar/bage &&
196+
>recurse-disable-on-command-line-alt-overriding-config &&
197+
git add recurse-disable-on-command-line-alt-overriding-config &&
198+
git commit -m "Recurse disable on command-line alternative overriding config junk"
199+
) &&
200+
(
201+
cd work &&
202+
git add gar/bage &&
203+
git commit -m "Recurse disable on command-line alternative overriding config for gar/bage" &&
204+
git -c push.recurseSubmodules=check push --no-recurse-submodules ../pub.git master &&
205+
# Check that the supermodule commit got there
206+
git fetch ../pub.git &&
207+
git diff --quiet FETCH_HEAD master &&
208+
# But that the submodule commit did not
209+
( cd gar/bage && git diff --quiet origin/master master^ ) &&
210+
# Now push it to avoid confusing future tests
211+
git push --recurse-submodules=on-demand ../pub.git master
212+
)
213+
'
214+
215+
test_expect_success 'push fails if recurse submodules option passed as yes' '
216+
(
217+
cd work/gar/bage &&
218+
>recurse-push-fails-if-recurse-submodules-passed-as-yes &&
219+
git add recurse-push-fails-if-recurse-submodules-passed-as-yes &&
220+
git commit -m "Recurse push fails if recurse submodules option passed as yes"
221+
) &&
222+
(
223+
cd work &&
224+
git add gar/bage &&
225+
git commit -m "Recurse push fails if recurse submodules option passed as yes for gar/bage" &&
226+
test_must_fail git push --recurse-submodules=yes ../pub.git master &&
227+
test_must_fail git -c push.recurseSubmodules=yes push ../pub.git master &&
228+
git push --recurse-submodules=on-demand ../pub.git master
229+
)
230+
'
231+
82232
test_expect_success 'push fails when commit on multiple branches if one branch has no remote' '
83233
(
84234
cd work/gar/bage &&

0 commit comments

Comments
 (0)