Skip to content

Commit 9c4a036

Browse files
bjornggitster
authored andcommitted
Teach the --all option to 'git fetch'
'git remote' is meant for managing remotes and 'git fetch' is meant for actually fetching data from remote repositories. Therefore, it is not logical that you must use 'git remote update' to fetch from more than one repository at once. Add the --all option to 'git fetch', to tell it to attempt to fetch from all remotes. Also, if --all is not given, the <repository> argument is allowed to be the name of a group, to allow fetching from all repositories in the group. Other options except -v and -q are silently ignored. Signed-off-by: Björn Gustavsson <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6b276e1 commit 9c4a036

File tree

6 files changed

+247
-22
lines changed

6 files changed

+247
-22
lines changed

Documentation/fetch-options.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
--all::
2+
Fetch all remotes.
3+
14
-a::
25
--append::
36
Append ref names and object names of fetched refs to the

Documentation/git-fetch.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ SYNOPSIS
1010
--------
1111
'git fetch' <options> <repository> <refspec>...
1212

13+
'git fetch' <options> <group>
14+
15+
'git fetch' --all <options>
16+
1317

1418
DESCRIPTION
1519
-----------
16-
Fetches named heads or tags from another repository, along with
17-
the objects necessary to complete them.
20+
Fetches named heads or tags from one or more other repositories,
21+
along with the objects necessary to complete them.
1822

1923
The ref names and their object names of fetched refs are stored
2024
in `.git/FETCH_HEAD`. This information is left for a later merge
@@ -28,6 +32,10 @@ pointed by remote tags that it does not yet have, then fetch
2832
those missing tags. If the other end has tags that point at
2933
branches you are not interested in, you will not get them.
3034

35+
'git fetch' can fetch from either a single named repository, or
36+
or from several repositories at once if <group> is given and
37+
there is a remotes.<group> entry in the configuration file.
38+
(See linkgit:git-config[1]).
3139

3240
OPTIONS
3341
-------

Documentation/pull-fetch-param.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
(see the section <<URLS,GIT URLS>> below) or the name
55
of a remote (see the section <<REMOTES,REMOTES>> below).
66

7+
ifndef::git-pull[]
8+
<group>::
9+
A name referring to a list of repositories as the value
10+
of remotes.<group> in the configuration file.
11+
(See linkgit:git-config[1]).
12+
endif::git-pull[]
13+
714
<refspec>::
815
The format of a <refspec> parameter is an optional plus
916
`{plus}`, followed by the source ref <src>, followed

builtin-fetch.c

Lines changed: 132 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
static const char * const builtin_fetch_usage[] = {
1616
"git fetch [options] [<repository> <refspec>...]",
17+
"git fetch [options] <group>",
18+
"git fetch --all [options]",
1719
NULL
1820
};
1921

@@ -23,7 +25,7 @@ enum {
2325
TAGS_SET = 2
2426
};
2527

26-
static int append, force, keep, update_head_ok, verbosity;
28+
static int all, append, force, keep, update_head_ok, verbosity;
2729
static int tags = TAGS_DEFAULT;
2830
static const char *depth;
2931
static const char *upload_pack;
@@ -32,6 +34,8 @@ static struct transport *transport;
3234

3335
static struct option builtin_fetch_options[] = {
3436
OPT__VERBOSITY(&verbosity),
37+
OPT_BOOLEAN(0, "all", &all,
38+
"fetch from all remotes"),
3539
OPT_BOOLEAN('a', "append", &append,
3640
"append to .git/FETCH_HEAD instead of overwriting"),
3741
OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
@@ -639,27 +643,89 @@ static void set_option(const char *name, const char *value)
639643
name, transport->url);
640644
}
641645

642-
int cmd_fetch(int argc, const char **argv, const char *prefix)
646+
static int get_one_remote_for_fetch(struct remote *remote, void *priv)
647+
{
648+
struct string_list *list = priv;
649+
string_list_append(remote->name, list);
650+
return 0;
651+
}
652+
653+
struct remote_group_data {
654+
const char *name;
655+
struct string_list *list;
656+
};
657+
658+
static int get_remote_group(const char *key, const char *value, void *priv)
659+
{
660+
struct remote_group_data *g = priv;
661+
662+
if (!prefixcmp(key, "remotes.") &&
663+
!strcmp(key + 8, g->name)) {
664+
/* split list by white space */
665+
int space = strcspn(value, " \t\n");
666+
while (*value) {
667+
if (space > 1) {
668+
string_list_append(xstrndup(value, space),
669+
g->list);
670+
}
671+
value += space + (value[space] != '\0');
672+
space = strcspn(value, " \t\n");
673+
}
674+
}
675+
676+
return 0;
677+
}
678+
679+
static int add_remote_or_group(const char *name, struct string_list *list)
680+
{
681+
int prev_nr = list->nr;
682+
struct remote_group_data g = { name, list };
683+
684+
git_config(get_remote_group, &g);
685+
if (list->nr == prev_nr) {
686+
struct remote *remote;
687+
if (!remote_is_configured(name))
688+
return 0;
689+
remote = remote_get(name);
690+
string_list_append(remote->name, list);
691+
}
692+
return 1;
693+
}
694+
695+
static int fetch_multiple(struct string_list *list)
696+
{
697+
int i, result = 0;
698+
const char *argv[] = { "fetch", NULL, NULL, NULL, NULL };
699+
int argc = 1;
700+
701+
if (verbosity >= 2)
702+
argv[argc++] = "-v";
703+
if (verbosity >= 1)
704+
argv[argc++] = "-v";
705+
else if (verbosity < 0)
706+
argv[argc++] = "-q";
707+
708+
for (i = 0; i < list->nr; i++) {
709+
const char *name = list->items[i].string;
710+
argv[argc] = name;
711+
if (verbosity >= 0)
712+
printf("Fetching %s\n", name);
713+
if (run_command_v_opt(argv, RUN_GIT_CMD)) {
714+
error("Could not fetch %s", name);
715+
result = 1;
716+
}
717+
}
718+
719+
return result;
720+
}
721+
722+
static int fetch_one(struct remote *remote, int argc, const char **argv)
643723
{
644-
struct remote *remote;
645724
int i;
646725
static const char **refs = NULL;
647726
int ref_nr = 0;
648727
int exit_code;
649728

650-
/* Record the command line for the reflog */
651-
strbuf_addstr(&default_rla, "fetch");
652-
for (i = 1; i < argc; i++)
653-
strbuf_addf(&default_rla, " %s", argv[i]);
654-
655-
argc = parse_options(argc, argv, prefix,
656-
builtin_fetch_options, builtin_fetch_usage, 0);
657-
658-
if (argc == 0)
659-
remote = remote_get(NULL);
660-
else
661-
remote = remote_get(argv[0]);
662-
663729
if (!remote)
664730
die("Where do you want to fetch from today?");
665731

@@ -675,10 +741,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
675741
if (depth)
676742
set_option(TRANS_OPT_DEPTH, depth);
677743

678-
if (argc > 1) {
744+
if (argc > 0) {
679745
int j = 0;
680746
refs = xcalloc(argc + 1, sizeof(const char *));
681-
for (i = 1; i < argc; i++) {
747+
for (i = 0; i < argc; i++) {
682748
if (!strcmp(argv[i], "tag")) {
683749
char *ref;
684750
i++;
@@ -705,3 +771,51 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
705771
transport = NULL;
706772
return exit_code;
707773
}
774+
775+
int cmd_fetch(int argc, const char **argv, const char *prefix)
776+
{
777+
int i;
778+
struct string_list list = { NULL, 0, 0, 0 };
779+
struct remote *remote;
780+
int result = 0;
781+
782+
/* Record the command line for the reflog */
783+
strbuf_addstr(&default_rla, "fetch");
784+
for (i = 1; i < argc; i++)
785+
strbuf_addf(&default_rla, " %s", argv[i]);
786+
787+
argc = parse_options(argc, argv, prefix,
788+
builtin_fetch_options, builtin_fetch_usage, 0);
789+
790+
if (all) {
791+
if (argc == 1)
792+
die("fetch --all does not take a repository argument");
793+
else if (argc > 1)
794+
die("fetch --all does not make sense with refspecs");
795+
(void) for_each_remote(get_one_remote_for_fetch, &list);
796+
result = fetch_multiple(&list);
797+
} else if (argc == 0) {
798+
/* No arguments -- use default remote */
799+
remote = remote_get(NULL);
800+
result = fetch_one(remote, argc, argv);
801+
} else {
802+
/* Single remote or group */
803+
(void) add_remote_or_group(argv[0], &list);
804+
if (list.nr > 1) {
805+
/* More than one remote */
806+
if (argc > 1)
807+
die("Fetching a group and specifying refspecs does not make sense");
808+
result = fetch_multiple(&list);
809+
} else {
810+
/* Zero or one remotes */
811+
remote = remote_get(argv[0]);
812+
result = fetch_one(remote, argc-1, argv+1);
813+
}
814+
}
815+
816+
/* All names were strdup()ed or strndup()ed */
817+
list.strdup_strings = 1;
818+
string_list_clear(&list, 0);
819+
820+
return result;
821+
}

t/t5506-remote-groups.sh

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ test_expect_success 'nonexistant group produces error' '
5151
! repo_fetched two
5252
'
5353

54-
test_expect_success 'updating group updates all members' '
54+
test_expect_success 'updating group updates all members (remote update)' '
5555
mark group-all &&
5656
update_repos &&
5757
git config --add remotes.all one &&
@@ -61,7 +61,15 @@ test_expect_success 'updating group updates all members' '
6161
repo_fetched two
6262
'
6363

64-
test_expect_success 'updating group does not update non-members' '
64+
test_expect_success 'updating group updates all members (fetch)' '
65+
mark fetch-group-all &&
66+
update_repos &&
67+
git fetch all &&
68+
repo_fetched one &&
69+
repo_fetched two
70+
'
71+
72+
test_expect_success 'updating group does not update non-members (remote update)' '
6573
mark group-some &&
6674
update_repos &&
6775
git config --add remotes.some one &&
@@ -70,6 +78,15 @@ test_expect_success 'updating group does not update non-members' '
7078
! repo_fetched two
7179
'
7280

81+
test_expect_success 'updating group does not update non-members (fetch)' '
82+
mark fetch-group-some &&
83+
update_repos &&
84+
git config --add remotes.some one &&
85+
git remote update some &&
86+
repo_fetched one &&
87+
! repo_fetched two
88+
'
89+
7390
test_expect_success 'updating remote name updates that remote' '
7491
mark remote-name &&
7592
update_repos &&

t/t5514-fetch-multiple.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/sh
2+
3+
test_description='fetch --all works correctly'
4+
5+
. ./test-lib.sh
6+
7+
setup_repository () {
8+
mkdir "$1" && (
9+
cd "$1" &&
10+
git init &&
11+
>file &&
12+
git add file &&
13+
test_tick &&
14+
git commit -m "Initial" &&
15+
git checkout -b side &&
16+
>elif &&
17+
git add elif &&
18+
test_tick &&
19+
git commit -m "Second" &&
20+
git checkout master
21+
)
22+
}
23+
24+
test_expect_success setup '
25+
setup_repository one &&
26+
setup_repository two &&
27+
(
28+
cd two && git branch another
29+
) &&
30+
git clone --mirror two three
31+
git clone one test
32+
'
33+
34+
cat > test/expect << EOF
35+
one/master
36+
one/side
37+
origin/HEAD -> origin/master
38+
origin/master
39+
origin/side
40+
three/another
41+
three/master
42+
three/side
43+
two/another
44+
two/master
45+
two/side
46+
EOF
47+
48+
test_expect_success 'git fetch --all' '
49+
(cd test &&
50+
git remote add one ../one &&
51+
git remote add two ../two &&
52+
git remote add three ../three &&
53+
git fetch --all &&
54+
git branch -r > output &&
55+
test_cmp expect output)
56+
'
57+
58+
test_expect_success 'git fetch --all should continue if a remote has errors' '
59+
(git clone one test2 &&
60+
cd test2 &&
61+
git remote add bad ../non-existing &&
62+
git remote add one ../one &&
63+
git remote add two ../two &&
64+
git remote add three ../three &&
65+
test_must_fail git fetch --all &&
66+
git branch -r > output &&
67+
test_cmp ../test/expect output)
68+
'
69+
70+
test_expect_success 'git fetch --all does not allow non-option arguments' '
71+
(cd test &&
72+
test_must_fail git fetch --all origin &&
73+
test_must_fail git fetch --all origin master)
74+
'
75+
76+
test_done

0 commit comments

Comments
 (0)