提交 9c4a036b 编写于 作者: B Björn Gustavsson 提交者: Junio C Hamano

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: NBjörn Gustavsson <bgustavsson@gmail.com>
Signed-off-by: NJunio C Hamano <gitster@pobox.com>
上级 6b276e19
--all::
Fetch all remotes.
-a::
--append::
Append ref names and object names of fetched refs to the
......
......@@ -10,11 +10,15 @@ SYNOPSIS
--------
'git fetch' <options> <repository> <refspec>...
'git fetch' <options> <group>
'git fetch' --all <options>
DESCRIPTION
-----------
Fetches named heads or tags from another repository, along with
the objects necessary to complete them.
Fetches named heads or tags from one or more other repositories,
along with the objects necessary to complete them.
The ref names and their object names of fetched refs are stored
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
those missing tags. If the other end has tags that point at
branches you are not interested in, you will not get them.
'git fetch' can fetch from either a single named repository, or
or from several repositories at once if <group> is given and
there is a remotes.<group> entry in the configuration file.
(See linkgit:git-config[1]).
OPTIONS
-------
......
......@@ -4,6 +4,13 @@
(see the section <<URLS,GIT URLS>> below) or the name
of a remote (see the section <<REMOTES,REMOTES>> below).
ifndef::git-pull[]
<group>::
A name referring to a list of repositories as the value
of remotes.<group> in the configuration file.
(See linkgit:git-config[1]).
endif::git-pull[]
<refspec>::
The format of a <refspec> parameter is an optional plus
`{plus}`, followed by the source ref <src>, followed
......
......@@ -14,6 +14,8 @@
static const char * const builtin_fetch_usage[] = {
"git fetch [options] [<repository> <refspec>...]",
"git fetch [options] <group>",
"git fetch --all [options]",
NULL
};
......@@ -23,7 +25,7 @@ enum {
TAGS_SET = 2
};
static int append, force, keep, update_head_ok, verbosity;
static int all, append, force, keep, update_head_ok, verbosity;
static int tags = TAGS_DEFAULT;
static const char *depth;
static const char *upload_pack;
......@@ -32,6 +34,8 @@ static struct transport *transport;
static struct option builtin_fetch_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOLEAN(0, "all", &all,
"fetch from all remotes"),
OPT_BOOLEAN('a', "append", &append,
"append to .git/FETCH_HEAD instead of overwriting"),
OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
......@@ -639,27 +643,89 @@ static void set_option(const char *name, const char *value)
name, transport->url);
}
int cmd_fetch(int argc, const char **argv, const char *prefix)
static int get_one_remote_for_fetch(struct remote *remote, void *priv)
{
struct string_list *list = priv;
string_list_append(remote->name, list);
return 0;
}
struct remote_group_data {
const char *name;
struct string_list *list;
};
static int get_remote_group(const char *key, const char *value, void *priv)
{
struct remote_group_data *g = priv;
if (!prefixcmp(key, "remotes.") &&
!strcmp(key + 8, g->name)) {
/* split list by white space */
int space = strcspn(value, " \t\n");
while (*value) {
if (space > 1) {
string_list_append(xstrndup(value, space),
g->list);
}
value += space + (value[space] != '\0');
space = strcspn(value, " \t\n");
}
}
return 0;
}
static int add_remote_or_group(const char *name, struct string_list *list)
{
int prev_nr = list->nr;
struct remote_group_data g = { name, list };
git_config(get_remote_group, &g);
if (list->nr == prev_nr) {
struct remote *remote;
if (!remote_is_configured(name))
return 0;
remote = remote_get(name);
string_list_append(remote->name, list);
}
return 1;
}
static int fetch_multiple(struct string_list *list)
{
int i, result = 0;
const char *argv[] = { "fetch", NULL, NULL, NULL, NULL };
int argc = 1;
if (verbosity >= 2)
argv[argc++] = "-v";
if (verbosity >= 1)
argv[argc++] = "-v";
else if (verbosity < 0)
argv[argc++] = "-q";
for (i = 0; i < list->nr; i++) {
const char *name = list->items[i].string;
argv[argc] = name;
if (verbosity >= 0)
printf("Fetching %s\n", name);
if (run_command_v_opt(argv, RUN_GIT_CMD)) {
error("Could not fetch %s", name);
result = 1;
}
}
return result;
}
static int fetch_one(struct remote *remote, int argc, const char **argv)
{
struct remote *remote;
int i;
static const char **refs = NULL;
int ref_nr = 0;
int exit_code;
/* Record the command line for the reflog */
strbuf_addstr(&default_rla, "fetch");
for (i = 1; i < argc; i++)
strbuf_addf(&default_rla, " %s", argv[i]);
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
if (argc == 0)
remote = remote_get(NULL);
else
remote = remote_get(argv[0]);
if (!remote)
die("Where do you want to fetch from today?");
......@@ -675,10 +741,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (depth)
set_option(TRANS_OPT_DEPTH, depth);
if (argc > 1) {
if (argc > 0) {
int j = 0;
refs = xcalloc(argc + 1, sizeof(const char *));
for (i = 1; i < argc; i++) {
for (i = 0; i < argc; i++) {
if (!strcmp(argv[i], "tag")) {
char *ref;
i++;
......@@ -705,3 +771,51 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
transport = NULL;
return exit_code;
}
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
struct string_list list = { NULL, 0, 0, 0 };
struct remote *remote;
int result = 0;
/* Record the command line for the reflog */
strbuf_addstr(&default_rla, "fetch");
for (i = 1; i < argc; i++)
strbuf_addf(&default_rla, " %s", argv[i]);
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
if (all) {
if (argc == 1)
die("fetch --all does not take a repository argument");
else if (argc > 1)
die("fetch --all does not make sense with refspecs");
(void) for_each_remote(get_one_remote_for_fetch, &list);
result = fetch_multiple(&list);
} else if (argc == 0) {
/* No arguments -- use default remote */
remote = remote_get(NULL);
result = fetch_one(remote, argc, argv);
} else {
/* Single remote or group */
(void) add_remote_or_group(argv[0], &list);
if (list.nr > 1) {
/* More than one remote */
if (argc > 1)
die("Fetching a group and specifying refspecs does not make sense");
result = fetch_multiple(&list);
} else {
/* Zero or one remotes */
remote = remote_get(argv[0]);
result = fetch_one(remote, argc-1, argv+1);
}
}
/* All names were strdup()ed or strndup()ed */
list.strdup_strings = 1;
string_list_clear(&list, 0);
return result;
}
......@@ -51,7 +51,7 @@ test_expect_success 'nonexistant group produces error' '
! repo_fetched two
'
test_expect_success 'updating group updates all members' '
test_expect_success 'updating group updates all members (remote update)' '
mark group-all &&
update_repos &&
git config --add remotes.all one &&
......@@ -61,7 +61,15 @@ test_expect_success 'updating group updates all members' '
repo_fetched two
'
test_expect_success 'updating group does not update non-members' '
test_expect_success 'updating group updates all members (fetch)' '
mark fetch-group-all &&
update_repos &&
git fetch all &&
repo_fetched one &&
repo_fetched two
'
test_expect_success 'updating group does not update non-members (remote update)' '
mark group-some &&
update_repos &&
git config --add remotes.some one &&
......@@ -70,6 +78,15 @@ test_expect_success 'updating group does not update non-members' '
! repo_fetched two
'
test_expect_success 'updating group does not update non-members (fetch)' '
mark fetch-group-some &&
update_repos &&
git config --add remotes.some one &&
git remote update some &&
repo_fetched one &&
! repo_fetched two
'
test_expect_success 'updating remote name updates that remote' '
mark remote-name &&
update_repos &&
......
#!/bin/sh
test_description='fetch --all works correctly'
. ./test-lib.sh
setup_repository () {
mkdir "$1" && (
cd "$1" &&
git init &&
>file &&
git add file &&
test_tick &&
git commit -m "Initial" &&
git checkout -b side &&
>elif &&
git add elif &&
test_tick &&
git commit -m "Second" &&
git checkout master
)
}
test_expect_success setup '
setup_repository one &&
setup_repository two &&
(
cd two && git branch another
) &&
git clone --mirror two three
git clone one test
'
cat > test/expect << EOF
one/master
one/side
origin/HEAD -> origin/master
origin/master
origin/side
three/another
three/master
three/side
two/another
two/master
two/side
EOF
test_expect_success 'git fetch --all' '
(cd test &&
git remote add one ../one &&
git remote add two ../two &&
git remote add three ../three &&
git fetch --all &&
git branch -r > output &&
test_cmp expect output)
'
test_expect_success 'git fetch --all should continue if a remote has errors' '
(git clone one test2 &&
cd test2 &&
git remote add bad ../non-existing &&
git remote add one ../one &&
git remote add two ../two &&
git remote add three ../three &&
test_must_fail git fetch --all &&
git branch -r > output &&
test_cmp ../test/expect output)
'
test_expect_success 'git fetch --all does not allow non-option arguments' '
(cd test &&
test_must_fail git fetch --all origin &&
test_must_fail git fetch --all origin master)
'
test_done
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册