fetch: add new config option fetch.all

Introduce a boolean configuration option fetch.all which allows to fetch all available remotes by default. The config option can be overridden by explicitly specifying a remote or by using --no-all. The behavior for --all is unchanged and calling git-fetch with --all and a remote will still result in an error. Additionally, describe the configuration variable in the config documentation and implement new tests to cover the expected behavior. Also add --no-all to the command-line documentation of git-fetch. Signed-off-by: Tamino Bauknecht <dev@tb6.eu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Tamino Bauknecht committed Jan 8, 2024 at 22:13 UTC 39487a1510fcdbb4124882f531a077dbdfe1ef60
4 files changed +186 -3
Documentation/config/fetch.txt
+6
@@ -50,6 +50,12 @@ fetch.pruneTags::
50 refs. See also `remote.<name>.pruneTags` and the PRUNING
51 section of linkgit:git-fetch[1].
52
53 +fetch.all::
54 + If true, fetch will attempt to update all available remotes.
55 + This behavior can be overridden by passing `--no-all` or by
56 + explicitly specifying one or more remote(s) to fetch from.
57 + Defaults to false.
58 +
59 fetch.output::
60 Control how ref update status is printed. Valid values are
61 `full` and `compact`. Default value is `full`. See the
Documentation/fetch-options.txt
+3 -2
@@ -1,5 +1,6 @@
1 ---all::
2 - Fetch all remotes.
1 +--[no-]all::
2 + Fetch all remotes. This overrides the configuration variable
3 + `fetch.all`.
4
5 -a::
6 --append::
builtin/fetch.c
+16 -1
@@ -102,6 +102,7 @@ static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
102
103 struct fetch_config {
104 enum display_format display_format;
105 + int all;
106 int prune;
107 int prune_tags;
108 int show_forced_updates;
@@ -115,6 +116,11 @@ static int git_fetch_config(const char *k, const char *v,
116 {
117 struct fetch_config *fetch_config = cb;
118
119 + if (!strcmp(k, "fetch.all")) {
120 + fetch_config->all = git_config_bool(k, v);
121 + return 0;
122 + }
123 +
124 if (!strcmp(k, "fetch.prune")) {
125 fetch_config->prune = git_config_bool(k, v);
126 return 0;
@@ -2132,7 +2138,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
2138 const char *bundle_uri;
2139 struct string_list list = STRING_LIST_INIT_DUP;
2140 struct remote *remote = NULL;
2135 - int all = 0, multiple = 0;
2141 + int all = -1, multiple = 0;
2142 int result = 0;
2143 int prune_tags_ok = 1;
2144 int enable_auto_gc = 1;
@@ -2337,11 +2343,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
2343 fetch_bundle_uri(the_repository, bundle_uri, NULL))
2344 warning(_("failed to fetch bundles from '%s'"), bundle_uri);
2345
2346 + if (all < 0) {
2347 + /*
2348 + * no --[no-]all given;
2349 + * only use config option if no remote was explicitly specified
2350 + */
2351 + all = (!argc) ? config.all : 0;
2352 + }
2353 +
2354 if (all) {
2355 if (argc == 1)
2356 die(_("fetch --all does not take a repository argument"));
2357 else if (argc > 1)
2358 die(_("fetch --all does not make sense with refspecs"));
2359 +
2360 (void) for_each_remote(get_one_remote_for_fetch, &list);
2361
2362 /* do not do fetch_multiple() of one */
t/t5514-fetch-multiple.sh
+161
@@ -24,6 +24,15 @@ setup_repository () {
24 )
25 }
26
27 +setup_test_clone () {
28 + test_dir="$1" &&
29 + git clone one "$test_dir" &&
30 + for r in one two three
31 + do
32 + git -C "$test_dir" remote add "$r" "../$r" || return 1
33 + done
34 +}
35 +
36 test_expect_success setup '
37 setup_repository one &&
38 setup_repository two &&
@@ -209,4 +218,156 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
218 git fetch --multiple --jobs=0)
219 '
220
221 +create_fetch_all_expect () {
222 + cat >expect <<-\EOF
223 + one/main
224 + one/side
225 + origin/HEAD -> origin/main
226 + origin/main
227 + origin/side
228 + three/another
229 + three/main
230 + three/side
231 + two/another
232 + two/main
233 + two/side
234 + EOF
235 +}
236 +
237 +for fetch_all in true false
238 +do
239 + test_expect_success "git fetch --all (works with fetch.all = $fetch_all)" '
240 + test_dir="test_fetch_all_$fetch_all" &&
241 + setup_test_clone "$test_dir" &&
242 + (
243 + cd "$test_dir" &&
244 + git config fetch.all $fetch_all &&
245 + git fetch --all &&
246 + create_fetch_all_expect &&
247 + git branch -r >actual &&
248 + test_cmp expect actual
249 + )
250 + '
251 +done
252 +
253 +test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
254 + setup_test_clone test9 &&
255 + (
256 + cd test9 &&
257 + git config fetch.all true &&
258 + git fetch &&
259 + git branch -r >actual &&
260 + create_fetch_all_expect &&
261 + test_cmp expect actual
262 + )
263 +'
264 +
265 +create_fetch_one_expect () {
266 + cat >expect <<-\EOF
267 + one/main
268 + one/side
269 + origin/HEAD -> origin/main
270 + origin/main
271 + origin/side
272 + EOF
273 +}
274 +
275 +test_expect_success 'git fetch one (explicit remote overrides fetch.all)' '
276 + setup_test_clone test10 &&
277 + (
278 + cd test10 &&
279 + git config fetch.all true &&
280 + git fetch one &&
281 + create_fetch_one_expect &&
282 + git branch -r >actual &&
283 + test_cmp expect actual
284 + )
285 +'
286 +
287 +create_fetch_two_as_origin_expect () {
288 + cat >expect <<-\EOF
289 + origin/HEAD -> origin/main
290 + origin/another
291 + origin/main
292 + origin/side
293 + EOF
294 +}
295 +
296 +test_expect_success 'git config fetch.all false (fetch only default remote)' '
297 + setup_test_clone test11 &&
298 + (
299 + cd test11 &&
300 + git config fetch.all false &&
301 + git remote set-url origin ../two &&
302 + git fetch &&
303 + create_fetch_two_as_origin_expect &&
304 + git branch -r >actual &&
305 + test_cmp expect actual
306 + )
307 +'
308 +
309 +for fetch_all in true false
310 +do
311 + test_expect_success "git fetch --no-all (fetch only default remote with fetch.all = $fetch_all)" '
312 + test_dir="test_no_all_fetch_all_$fetch_all" &&
313 + setup_test_clone "$test_dir" &&
314 + (
315 + cd "$test_dir" &&
316 + git config fetch.all $fetch_all &&
317 + git remote set-url origin ../two &&
318 + git fetch --no-all &&
319 + create_fetch_two_as_origin_expect &&
320 + git branch -r >actual &&
321 + test_cmp expect actual
322 + )
323 + '
324 +done
325 +
326 +test_expect_success 'git fetch --no-all (fetch only default remote without fetch.all)' '
327 + setup_test_clone test12 &&
328 + (
329 + cd test12 &&
330 + git config --unset-all fetch.all || true &&
331 + git remote set-url origin ../two &&
332 + git fetch --no-all &&
333 + create_fetch_two_as_origin_expect &&
334 + git branch -r >actual &&
335 + test_cmp expect actual
336 + )
337 +'
338 +
339 +test_expect_success 'git fetch --all --no-all (fetch only default remote)' '
340 + setup_test_clone test13 &&
341 + (
342 + cd test13 &&
343 + git remote set-url origin ../two &&
344 + git fetch --all --no-all &&
345 + create_fetch_two_as_origin_expect &&
346 + git branch -r >actual &&
347 + test_cmp expect actual
348 + )
349 +'
350 +
351 +test_expect_success 'git fetch --no-all one (fetch only explicit remote)' '
352 + setup_test_clone test14 &&
353 + (
354 + cd test14 &&
355 + git fetch --no-all one &&
356 + create_fetch_one_expect &&
357 + git branch -r >actual &&
358 + test_cmp expect actual
359 + )
360 +'
361 +
362 +test_expect_success 'git fetch --no-all --all (fetch all remotes)' '
363 + setup_test_clone test15 &&
364 + (
365 + cd test15 &&
366 + git fetch --no-all --all &&
367 + create_fetch_all_expect &&
368 + git branch -r >actual &&
369 + test_cmp expect actual
370 + )
371 +'
372 +
373 test_done