Skip to content

Commit

Permalink
Merge branch 'bg/fetch-multi'
Browse files Browse the repository at this point in the history
* 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'
  • Loading branch information
Junio C Hamano committed Nov 23, 2009
2 parents fc13aa3 + 8db3559 commit 65c042d
Show file tree
Hide file tree
Showing 13 changed files with 492 additions and 115 deletions.
8 changes: 7 additions & 1 deletion Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions Documentation/fetch-options.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
--all::
Fetch all remotes.

-a::
--append::
Append ref names and object names of fetched refs to the
Expand All @@ -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>`
Expand All @@ -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[]
Expand Down
14 changes: 12 additions & 2 deletions Documentation/git-fetch.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
-------
Expand Down
7 changes: 7 additions & 0 deletions Documentation/pull-fetch-param.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
198 changes: 178 additions & 20 deletions builtin-fetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand All @@ -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;
Expand All @@ -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"),
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?");

Expand All @@ -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++;
Expand All @@ -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;
}
Loading

0 comments on commit 65c042d

Please sign in to comment.