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 <mac@mcrowe.com> Signed-off-by: Jeff King <peff@peff.net>
Mike Crowe committed
Nov 17, 2015 at 11:05 UTC
b33a15b08131514b593015cb3e719faf9db20208
7 files changed
+234
-26
Documentation/config.txt
+14
@@ -2226,6 +2226,20 @@ push.gpgSign::
2226
override a value from a lower-priority config file. An explicit
2227
command-line flag always overrides this config option.
2228
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
+
2243
rebase.stat::
2244
Whether to show a diffstat of what changed upstream since the last
2245
rebase. False by default.
Documentation/git-push.txt
+14
-10
@@ -257,16 +257,20 @@ origin +master` to force a push to the `master` branch). See the
257
is specified. This flag forces progress status even if the
258
standard error stream is not directed to a terminal.
259
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.
274
275
--[no-]verify::
276
Toggle the pre-push hook (see linkgit:githooks[5]). The
builtin/push.c
+24
-15
@@ -9,6 +9,7 @@
9
#include "transport.h"
10
#include "parse-options.h"
11
#include "submodule.h"
12
+#include "submodule-config.h"
13
#include "send-pack.h"
14
15
static const char * const push_usage[] = {
@@ -20,7 +21,7 @@ static int thin = 1;
21
static int deleterefs;
22
static const char *receivepack;
23
static int verbosity;
23
-static int progress = -1;
24
+static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
25
26
static struct push_cas_option cas;
27
@@ -452,22 +453,17 @@ static int do_push(const char *repo, int flags)
453
static int option_parse_recurse_submodules(const struct option *opt,
454
const char *arg, int unset)
455
{
455
- int *flags = opt->value;
456
+ int *recurse_submodules = opt->value;
457
457
- if (*flags & (TRANSPORT_RECURSE_SUBMODULES_CHECK |
458
- TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND))
458
+ if (*recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
459
die("%s can only be used once.", opt->long_name);
460
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);
467
468
return 0;
469
}
@@ -522,6 +518,10 @@ static int git_push_config(const char *k, const char *v, void *cb)
518
return error("Invalid value for '%s'", k);
519
}
520
}
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);
525
}
526
527
return git_default_config(k, v, NULL);
@@ -532,6 +532,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
532
int flags = 0;
533
int tags = 0;
534
int push_cert = -1;
535
+ int recurse_submodules_from_cmdline = RECURSE_SUBMODULES_DEFAULT;
536
int rc;
537
const char *repo = NULL; /* default repository */
538
struct option options[] = {
@@ -549,7 +550,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
550
0, CAS_OPT_NAME, &cas, N_("refname>:<expect"),
551
N_("require old value of ref to be at this value"),
552
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"),
554
N_("control recursive pushing of submodules"),
555
PARSE_OPT_OPTARG, option_parse_recurse_submodules },
556
OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")),
@@ -580,6 +581,14 @@ int cmd_push(int argc, const char **argv, const char *prefix)
581
if (deleterefs && argc < 2)
582
die(_("--delete doesn't make sense without any refs"));
583
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
+
592
if (tags)
593
add_refspec("refs/tags/*");
594
submodule-config.c
+29
@@ -228,6 +228,35 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
228
return parse_fetch_recurse(opt, arg, 1);
229
}
230
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
+
260
static void warn_multiple_config(const unsigned char *commit_sha1,
261
const char *name, const char *option)
262
{
submodule-config.h
+1
@@ -19,6 +19,7 @@ struct submodule {
19
};
20
21
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);
23
int parse_submodule_config_option(const char *var, const char *value);
24
const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
25
const char *name);
submodule.h
+1
@@ -5,6 +5,7 @@ struct diff_options;
5
struct argv_array;
6
7
enum {
8
+ RECURSE_SUBMODULES_CHECK = -4,
9
RECURSE_SUBMODULES_ERROR = -3,
10
RECURSE_SUBMODULES_NONE = -2,
11
RECURSE_SUBMODULES_ON_DEMAND = -1,
t/t5531-deep-submodule-push.sh
+151
-1
@@ -64,7 +64,12 @@ test_expect_success 'push fails if submodule commit not on remote' '
64
cd work &&
65
git add gar/bage &&
66
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
73
)
74
'
75
@@ -79,6 +84,151 @@ test_expect_success 'push succeeds after commit was pushed to remote' '
84
)
85
'
86
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
+
232
test_expect_success 'push fails when commit on multiple branches if one branch has no remote' '
233
(
234
cd work/gar/bage &&