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 <iveqy@iveqy.com> Mentored-by: Jens Lehmann <Jens.Lehmann@web.de> Mentored-by: Heiko Voigt <hvoigt@hvoigt.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Heiko Voigt committed
Mar 29, 2012 at 09:21 UTC
eb21c732d6b642a8f33abd69071a95de01d5061b
7 files changed
+179
-6
Documentation/git-push.txt
+10
-4
@@ -162,10 +162,16 @@ useful if you write an alias or script around 'git push'.
162
is specified. This flag forces progress status even if the
163
standard error stream is not directed to a terminal.
164
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.
175
176
177
include::urls-remotes.txt[]
builtin/push.c
+9
-1
@@ -224,13 +224,21 @@ static int option_parse_recurse_submodules(const struct option *opt,
224
const char *arg, int unset)
225
{
226
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
+
232
if (arg) {
233
if (!strcmp(arg, "check"))
234
*flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
235
+ else if (!strcmp(arg, "on-demand"))
236
+ *flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND;
237
else
238
die("bad %s argument: %s", opt->long_name, arg);
239
} 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);
242
243
return 0;
244
}
submodule.c
+48
@@ -410,6 +410,54 @@ int find_unpushed_submodules(unsigned char new_sha1[20],
410
return needs_pushing->nr;
411
}
412
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
+
461
static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
462
{
463
int is_present = 0;
submodule.h
+1
@@ -31,5 +31,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c
31
const unsigned char a[20], const unsigned char b[20], int search);
32
int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
33
struct string_list *needs_pushing);
34
+int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
35
36
#endif
t/t5531-deep-submodule-push.sh
+94
@@ -119,4 +119,98 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs
119
)
120
'
121
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
+
216
test_done
transport.c
+16
-1
@@ -1009,6 +1009,11 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing)
1009
"not be found on any remote:\n");
1010
for (i = 0; i < needs_pushing->nr; i++)
1011
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");
1017
1018
string_list_clear(needs_pushing, 0);
1019
@@ -1053,7 +1058,17 @@ int transport_push(struct transport *transport,
1058
flags & TRANSPORT_PUSH_MIRROR,
1059
flags & TRANSPORT_PUSH_FORCE);
1060
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()) {
1072
struct ref *ref = remote_refs;
1073
struct string_list needs_pushing;
1074
transport.h
+1
@@ -102,6 +102,7 @@ struct transport {
102
#define TRANSPORT_PUSH_PORCELAIN 16
103
#define TRANSPORT_PUSH_SET_UPSTREAM 32
104
#define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
105
+#define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
106
107
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
108