Merge branch 'bg/fetch-multi'
* bg/fetch-multi: Re-implement 'git remote update' using 'git fetch' builtin-fetch: add --dry-run option builtin-fetch: add --prune option teach warn_dangling_symref to take a FILE argument remote: refactor some logic into get_stale_heads() Add missing test for 'git remote update --prune' Add the configuration option skipFetchAll Teach the --multiple option to 'git fetch' Teach the --all option to 'git fetch'
This commit is contained in:
commit
65c042d44d
@ -1437,7 +1437,13 @@ remote.<name>.mirror::
|
||||
|
||||
remote.<name>.skipDefaultUpdate::
|
||||
If true, this remote will be skipped by default when updating
|
||||
using the update subcommand of linkgit:git-remote[1].
|
||||
using linkgit:git-fetch[1] or the `update` subcommand of
|
||||
linkgit:git-remote[1].
|
||||
|
||||
remote.<name>.skipFetchAll::
|
||||
If true, this remote will be skipped by default when updating
|
||||
using linkgit:git-fetch[1] or the `update` subcommand of
|
||||
linkgit:git-remote[1].
|
||||
|
||||
remote.<name>.receivepack::
|
||||
The default program to execute on the remote side when pushing. See
|
||||
|
@ -1,3 +1,6 @@
|
||||
--all::
|
||||
Fetch all remotes.
|
||||
|
||||
-a::
|
||||
--append::
|
||||
Append ref names and object names of fetched refs to the
|
||||
@ -9,6 +12,11 @@
|
||||
`git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
|
||||
by the specified number of commits.
|
||||
|
||||
ifndef::git-pull[]
|
||||
--dry-run::
|
||||
Show what would be done, without making any changes.
|
||||
endif::git-pull[]
|
||||
|
||||
-f::
|
||||
--force::
|
||||
When 'git-fetch' is used with `<rbranch>:<lbranch>`
|
||||
@ -21,6 +29,16 @@
|
||||
--keep::
|
||||
Keep downloaded pack.
|
||||
|
||||
ifndef::git-pull[]
|
||||
--multiple::
|
||||
Allow several <repository> and <group> arguments to be
|
||||
specified. No <refspec>s may be specified.
|
||||
|
||||
--prune::
|
||||
After fetching, remove any remote tracking branches which
|
||||
no longer exist on the remote.
|
||||
endif::git-pull[]
|
||||
|
||||
ifdef::git-pull[]
|
||||
--no-tags::
|
||||
endif::git-pull[]
|
||||
|
@ -10,11 +10,17 @@ SYNOPSIS
|
||||
--------
|
||||
'git fetch' <options> <repository> <refspec>...
|
||||
|
||||
'git fetch' <options> <group>
|
||||
|
||||
'git fetch' --multiple <options> [<repository> | <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 +34,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
|
||||
|
198
builtin-fetch.c
198
builtin-fetch.c
@ -14,6 +14,9 @@
|
||||
|
||||
static const char * const builtin_fetch_usage[] = {
|
||||
"git fetch [options] [<repository> <refspec>...]",
|
||||
"git fetch [options] <group>",
|
||||
"git fetch --multiple [options] [<repository> | <group>]...",
|
||||
"git fetch --all [options]",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -23,7 +26,7 @@ enum {
|
||||
TAGS_SET = 2
|
||||
};
|
||||
|
||||
static int append, force, keep, update_head_ok, verbosity;
|
||||
static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
|
||||
static int tags = TAGS_DEFAULT;
|
||||
static const char *depth;
|
||||
static const char *upload_pack;
|
||||
@ -32,16 +35,24 @@ 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",
|
||||
"path to upload pack on remote end"),
|
||||
OPT_BOOLEAN('f', "force", &force,
|
||||
"force overwrite of local branch"),
|
||||
OPT_BOOLEAN('m', "multiple", &multiple,
|
||||
"fetch from multiple remotes"),
|
||||
OPT_SET_INT('t', "tags", &tags,
|
||||
"fetch all tags and associated objects", TAGS_SET),
|
||||
OPT_SET_INT('n', NULL, &tags,
|
||||
"do not fetch all tags (--no-tags)", TAGS_UNSET),
|
||||
OPT_BOOLEAN('p', "prune", &prune,
|
||||
"prune tracking branches no longer on remote"),
|
||||
OPT_BOOLEAN(0, "dry-run", &dry_run,
|
||||
"dry run"),
|
||||
OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
|
||||
OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
|
||||
"allow updating of HEAD ref"),
|
||||
@ -178,6 +189,8 @@ static int s_update_ref(const char *action,
|
||||
char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
static struct ref_lock *lock;
|
||||
|
||||
if (dry_run)
|
||||
return 0;
|
||||
if (!rla)
|
||||
rla = default_rla.buf;
|
||||
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
|
||||
@ -303,7 +316,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
|
||||
char note[1024];
|
||||
const char *what, *kind;
|
||||
struct ref *rm;
|
||||
char *url, *filename = git_path("FETCH_HEAD");
|
||||
char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
|
||||
|
||||
fp = fopen(filename, "a");
|
||||
if (!fp)
|
||||
@ -485,6 +498,28 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int prune_refs(struct transport *transport, struct ref *ref_map)
|
||||
{
|
||||
int result = 0;
|
||||
struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
|
||||
const char *dangling_msg = dry_run
|
||||
? " (%s will become dangling)\n"
|
||||
: " (%s has become dangling)\n";
|
||||
|
||||
for (ref = stale_refs; ref; ref = ref->next) {
|
||||
if (!dry_run)
|
||||
result |= delete_ref(ref->name, NULL, 0);
|
||||
if (verbosity >= 0) {
|
||||
fprintf(stderr, " x %-*s %-*s -> %s\n",
|
||||
SUMMARY_WIDTH, "[deleted]",
|
||||
REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
|
||||
warn_dangling_symref(stderr, dangling_msg, ref->name);
|
||||
}
|
||||
}
|
||||
free_refs(stale_refs);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int add_existing(const char *refname, const unsigned char *sha1,
|
||||
int flag, void *cbdata)
|
||||
{
|
||||
@ -633,7 +668,7 @@ static int do_fetch(struct transport *transport,
|
||||
die("Don't know how to fetch from %s", transport->url);
|
||||
|
||||
/* if not appending, truncate FETCH_HEAD */
|
||||
if (!append) {
|
||||
if (!append && !dry_run) {
|
||||
char *filename = git_path("FETCH_HEAD");
|
||||
FILE *fp = fopen(filename, "w");
|
||||
if (!fp)
|
||||
@ -661,6 +696,8 @@ static int do_fetch(struct transport *transport,
|
||||
free_refs(ref_map);
|
||||
return 1;
|
||||
}
|
||||
if (prune)
|
||||
prune_refs(transport, ref_map);
|
||||
free_refs(ref_map);
|
||||
|
||||
/* if neither --no-tags nor --tags was specified, do automated tag
|
||||
@ -691,27 +728,94 @@ 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;
|
||||
if (!remote->skip_default_update)
|
||||
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, NULL, NULL };
|
||||
int argc = 1;
|
||||
|
||||
if (dry_run)
|
||||
argv[argc++] = "--dry-run";
|
||||
if (prune)
|
||||
argv[argc++] = "--prune";
|
||||
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?");
|
||||
|
||||
@ -727,10 +831,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++;
|
||||
@ -757,3 +861,57 @@ 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 if (multiple) {
|
||||
/* All arguments are assumed to be remotes or groups */
|
||||
for (i = 0; i < argc; i++)
|
||||
if (!add_remote_or_group(argv[i], &list))
|
||||
die("No such remote or remote group: %s", argv[i]);
|
||||
result = fetch_multiple(&list);
|
||||
} 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;
|
||||
}
|
||||
|
122
builtin-remote.c
122
builtin-remote.c
@ -261,32 +261,10 @@ struct ref_states {
|
||||
int queried;
|
||||
};
|
||||
|
||||
static int handle_one_branch(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct ref_states *states = cb_data;
|
||||
struct refspec refspec;
|
||||
|
||||
memset(&refspec, 0, sizeof(refspec));
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec)) {
|
||||
struct string_list_item *item;
|
||||
const char *name = abbrev_branch(refspec.src);
|
||||
/* symbolic refs pointing nowhere were handled already */
|
||||
if ((flags & REF_ISSYMREF) ||
|
||||
string_list_has_string(&states->tracked, name) ||
|
||||
string_list_has_string(&states->new, name))
|
||||
return 0;
|
||||
item = string_list_append(name, &states->stale);
|
||||
item->util = xstrdup(refname);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
|
||||
{
|
||||
struct ref *fetch_map = NULL, **tail = &fetch_map;
|
||||
struct ref *ref;
|
||||
struct ref *ref, *stale_refs;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < states->remote->fetch_refspec_nr; i++)
|
||||
@ -302,11 +280,17 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
|
||||
else
|
||||
string_list_append(abbrev_branch(ref->name), &states->tracked);
|
||||
}
|
||||
stale_refs = get_stale_heads(states->remote, fetch_map);
|
||||
for (ref = stale_refs; ref; ref = ref->next) {
|
||||
struct string_list_item *item =
|
||||
string_list_append(abbrev_branch(ref->name), &states->stale);
|
||||
item->util = xstrdup(ref->name);
|
||||
}
|
||||
free_refs(stale_refs);
|
||||
free_refs(fetch_map);
|
||||
|
||||
sort_string_list(&states->new);
|
||||
sort_string_list(&states->tracked);
|
||||
for_each_ref(handle_one_branch, states);
|
||||
sort_string_list(&states->stale);
|
||||
|
||||
return 0;
|
||||
@ -1213,94 +1197,62 @@ static int prune_remote(const char *remote, int dry_run)
|
||||
|
||||
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
|
||||
abbrev_ref(refname, "refs/remotes/"));
|
||||
warn_dangling_symref(dangling_msg, refname);
|
||||
warn_dangling_symref(stdout, dangling_msg, refname);
|
||||
}
|
||||
|
||||
free_remote_ref_states(&states);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int get_one_remote_for_update(struct remote *remote, void *priv)
|
||||
static int get_remote_default(const char *key, const char *value, void *priv)
|
||||
{
|
||||
struct string_list *list = priv;
|
||||
if (!remote->skip_default_update)
|
||||
string_list_append(remote->name, list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct remote_group {
|
||||
const char *name;
|
||||
struct string_list *list;
|
||||
} remote_group;
|
||||
|
||||
static int get_remote_group(const char *key, const char *value, void *num_hits)
|
||||
{
|
||||
if (!prefixcmp(key, "remotes.") &&
|
||||
!strcmp(key + 8, remote_group.name)) {
|
||||
/* split list by white space */
|
||||
int space = strcspn(value, " \t\n");
|
||||
while (*value) {
|
||||
if (space > 1) {
|
||||
string_list_append(xstrndup(value, space),
|
||||
remote_group.list);
|
||||
++*((int *)num_hits);
|
||||
}
|
||||
value += space + (value[space] != '\0');
|
||||
space = strcspn(value, " \t\n");
|
||||
}
|
||||
if (strcmp(key, "remotes.default") == 0) {
|
||||
int *found = priv;
|
||||
*found = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update(int argc, const char **argv)
|
||||
{
|
||||
int i, result = 0, prune = 0;
|
||||
struct string_list list = { NULL, 0, 0, 0 };
|
||||
static const char *default_argv[] = { NULL, "default", NULL };
|
||||
int i, prune = 0;
|
||||
struct option options[] = {
|
||||
OPT_BOOLEAN('p', "prune", &prune,
|
||||
"prune remotes after fetching"),
|
||||
OPT_END()
|
||||
};
|
||||
const char **fetch_argv;
|
||||
int fetch_argc = 0;
|
||||
int default_defined = 0;
|
||||
|
||||
fetch_argv = xmalloc(sizeof(char *) * (argc+5));
|
||||
|
||||
argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
|
||||
PARSE_OPT_KEEP_ARGV0);
|
||||
|
||||
fetch_argv[fetch_argc++] = "fetch";
|
||||
|
||||
if (prune)
|
||||
fetch_argv[fetch_argc++] = "--prune";
|
||||
if (verbose)
|
||||
fetch_argv[fetch_argc++] = "-v";
|
||||
if (argc < 2) {
|
||||
argc = 2;
|
||||
argv = default_argv;
|
||||
fetch_argv[fetch_argc++] = "default";
|
||||
} else {
|
||||
fetch_argv[fetch_argc++] = "--multiple";
|
||||
for (i = 1; i < argc; i++)
|
||||
fetch_argv[fetch_argc++] = argv[i];
|
||||
}
|
||||
|
||||
remote_group.list = &list;
|
||||
for (i = 1; i < argc; i++) {
|
||||
int groups_found = 0;
|
||||
remote_group.name = argv[i];
|
||||
result = git_config(get_remote_group, &groups_found);
|
||||
if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
|
||||
struct remote *remote;
|
||||
if (!remote_is_configured(argv[i]))
|
||||
die("No such remote or remote group: %s",
|
||||
argv[i]);
|
||||
remote = remote_get(argv[i]);
|
||||
string_list_append(remote->name, remote_group.list);
|
||||
}
|
||||
if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
|
||||
git_config(get_remote_default, &default_defined);
|
||||
if (!default_defined)
|
||||
fetch_argv[fetch_argc-1] = "--all";
|
||||
}
|
||||
|
||||
if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default"))
|
||||
result = for_each_remote(get_one_remote_for_update, &list);
|
||||
fetch_argv[fetch_argc] = NULL;
|
||||
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
int err = fetch_remote(list.items[i].string);
|
||||
result |= err;
|
||||
if (!err && prune)
|
||||
result |= prune_remote(list.items[i].string, 0);
|
||||
}
|
||||
|
||||
/* all names were strdup()ed or strndup()ed */
|
||||
list.strdup_strings = 1;
|
||||
string_list_clear(&list, 0);
|
||||
|
||||
return result;
|
||||
return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static int get_one_entry(struct remote *remote, void *priv)
|
||||
|
7
refs.c
7
refs.c
@ -286,6 +286,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
|
||||
}
|
||||
|
||||
struct warn_if_dangling_data {
|
||||
FILE *fp;
|
||||
const char *refname;
|
||||
const char *msg_fmt;
|
||||
};
|
||||
@ -304,13 +305,13 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
|
||||
if (!resolves_to || strcmp(resolves_to, d->refname))
|
||||
return 0;
|
||||
|
||||
printf(d->msg_fmt, refname);
|
||||
fprintf(d->fp, d->msg_fmt, refname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void warn_dangling_symref(const char *msg_fmt, const char *refname)
|
||||
void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
|
||||
{
|
||||
struct warn_if_dangling_data data = { refname, msg_fmt };
|
||||
struct warn_if_dangling_data data = { fp, refname, msg_fmt };
|
||||
for_each_rawref(warn_if_dangling_symref, &data);
|
||||
}
|
||||
|
||||
|
2
refs.h
2
refs.h
@ -29,7 +29,7 @@ extern int for_each_replace_ref(each_ref_fn, void *);
|
||||
/* can be used to learn about broken ref and symref */
|
||||
extern int for_each_rawref(each_ref_fn, void *);
|
||||
|
||||
extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
|
||||
extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
|
||||
|
||||
/*
|
||||
* Extra refs will be listed by for_each_ref() before any actual refs
|
||||
|
42
remote.c
42
remote.c
@ -397,7 +397,8 @@ static int handle_config(const char *key, const char *value, void *cb)
|
||||
remote->mirror = git_config_bool(key, value);
|
||||
else if (!strcmp(subkey, ".skipdefaultupdate"))
|
||||
remote->skip_default_update = git_config_bool(key, value);
|
||||
|
||||
else if (!strcmp(subkey, ".skipfetchall"))
|
||||
remote->skip_default_update = git_config_bool(key, value);
|
||||
else if (!strcmp(subkey, ".url")) {
|
||||
const char *v;
|
||||
if (git_config_string(&v, key, value))
|
||||
@ -1591,3 +1592,42 @@ struct ref *guess_remote_head(const struct ref *head,
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
struct stale_heads_info {
|
||||
struct remote *remote;
|
||||
struct string_list *ref_names;
|
||||
struct ref **stale_refs_tail;
|
||||
};
|
||||
|
||||
static int get_stale_heads_cb(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct stale_heads_info *info = cb_data;
|
||||
struct refspec refspec;
|
||||
memset(&refspec, 0, sizeof(refspec));
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(info->remote, &refspec)) {
|
||||
if (!((flags & REF_ISSYMREF) ||
|
||||
string_list_has_string(info->ref_names, refspec.src))) {
|
||||
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
|
||||
hashcpy(ref->new_sha1, sha1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
|
||||
{
|
||||
struct ref *ref, *stale_refs = NULL;
|
||||
struct string_list ref_names = { NULL, 0, 0, 0 };
|
||||
struct stale_heads_info info;
|
||||
info.remote = remote;
|
||||
info.ref_names = &ref_names;
|
||||
info.stale_refs_tail = &stale_refs;
|
||||
for (ref = fetch_map; ref; ref = ref->next)
|
||||
string_list_append(ref->name, &ref_names);
|
||||
sort_string_list(&ref_names);
|
||||
for_each_ref(get_stale_heads_cb, &info);
|
||||
string_list_clear(&ref_names, 0);
|
||||
return stale_refs;
|
||||
}
|
||||
|
3
remote.h
3
remote.h
@ -154,4 +154,7 @@ struct ref *guess_remote_head(const struct ref *head,
|
||||
const struct ref *refs,
|
||||
int all);
|
||||
|
||||
/* Return refs which no longer exist on remote */
|
||||
struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map);
|
||||
|
||||
#endif
|
||||
|
@ -365,6 +365,17 @@ test_expect_success 'update with arguments' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'update --prune' '
|
||||
|
||||
(cd one &&
|
||||
git branch -m side2 side3) &&
|
||||
(cd test &&
|
||||
git remote update --prune &&
|
||||
(cd ../one && git branch -m side3 side2)
|
||||
git rev-parse refs/remotes/origin/side3 &&
|
||||
test_must_fail git rev-parse refs/remotes/origin/side2)
|
||||
'
|
||||
|
||||
cat > one/expect << EOF
|
||||
apis/master
|
||||
apis/side
|
||||
|
@ -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 &&
|
||||
|
154
t/t5514-fetch-multiple.sh
Executable file
154
t/t5514-fetch-multiple.sh
Executable file
@ -0,0 +1,154 @@
|
||||
#!/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)
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
origin/HEAD -> origin/master
|
||||
origin/master
|
||||
origin/side
|
||||
three/another
|
||||
three/master
|
||||
three/side
|
||||
EOF
|
||||
|
||||
test_expect_success 'git fetch --multiple (but only one remote)' '
|
||||
(git clone one test3 &&
|
||||
cd test3 &&
|
||||
git remote add three ../three &&
|
||||
git fetch --multiple three &&
|
||||
git branch -r > output &&
|
||||
test_cmp ../expect output)
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
one/master
|
||||
one/side
|
||||
two/another
|
||||
two/master
|
||||
two/side
|
||||
EOF
|
||||
|
||||
test_expect_success 'git fetch --multiple (two remotes)' '
|
||||
(git clone one test4 &&
|
||||
cd test4 &&
|
||||
git remote rm origin &&
|
||||
git remote add one ../one &&
|
||||
git remote add two ../two &&
|
||||
git fetch --multiple one two &&
|
||||
git branch -r > output &&
|
||||
test_cmp ../expect output)
|
||||
'
|
||||
|
||||
test_expect_success 'git fetch --multiple (bad remote names)' '
|
||||
(cd test4 &&
|
||||
test_must_fail git fetch --multiple four)
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'git fetch --all (skipFetchAll)' '
|
||||
(cd test4 &&
|
||||
for b in $(git branch -r)
|
||||
do
|
||||
git branch -r -d $b || break
|
||||
done &&
|
||||
git remote add three ../three &&
|
||||
git config remote.three.skipFetchAll true &&
|
||||
git fetch --all &&
|
||||
git branch -r > output &&
|
||||
test_cmp ../expect output)
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
one/master
|
||||
one/side
|
||||
three/another
|
||||
three/master
|
||||
three/side
|
||||
two/another
|
||||
two/master
|
||||
two/side
|
||||
EOF
|
||||
|
||||
test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
|
||||
(cd test4 &&
|
||||
for b in $(git branch -r)
|
||||
do
|
||||
git branch -r -d $b || break
|
||||
done &&
|
||||
git fetch --multiple one two three &&
|
||||
git branch -r > output &&
|
||||
test_cmp ../expect output)
|
||||
'
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user