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:
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user