X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-fetch.c;h=de9947e7ac2cc1236909fab813198b3df93bd39e;hb=32d75d29f99cca8e0874b1bdf94ded48b576c906;hp=64392f317d416e54554f634ece64c380c485c776;hpb=b888d61c8308027433df9c243fa551f42db1c76a;p=git.git diff --git a/builtin-fetch.c b/builtin-fetch.c index 64392f317..de9947e7a 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -8,29 +8,65 @@ #include "path-list.h" #include "remote.h" #include "transport.h" +#include "run-command.h" static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack ] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth ] [-v | --verbose] [ ...]"; static int append, force, tags, no_tags, update_head_ok, verbose, quiet; +static const char *depth; +static char *default_rla = NULL; +static struct transport *transport; -static int unpacklimit; +static void unlock_pack(void) +{ + if (transport) + transport_unlock_pack(transport); +} -static char *default_rla = NULL; +static void unlock_pack_on_signal(int signo) +{ + unlock_pack(); + signal(SIGINT, SIG_DFL); + raise(signo); +} -static void find_merge_config(struct ref *ref_map, struct remote *remote) +static void add_merge_config(struct ref **head, + const struct ref *remote_refs, + struct branch *branch, + struct ref ***tail) { - struct ref *rm = ref_map; - struct branch *branch = branch_get(NULL); + int i; - for (rm = ref_map; rm; rm = rm->next) { - if (!branch_has_merge_config(branch)) { - if (remote && remote->fetch && - !strcmp(remote->fetch[0].src, rm->name)) - rm->merge = 1; - } else { - if (branch_merges(branch, rm->name)) + for (i = 0; i < branch->merge_nr; i++) { + struct ref *rm, **old_tail = *tail; + struct refspec refspec; + + for (rm = *head; rm; rm = rm->next) { + if (branch_merge_matches(branch, i, rm->name)) { rm->merge = 1; + break; + } } + if (rm) + continue; + + /* + * Not fetched to a tracking branch? We need to fetch + * it anyway to allow this branch's "branch.$name.merge" + * to be honored by git-pull, but we do not have to + * fail if branch.$name.merge is misconfigured to point + * at a nonexisting branch. If we were indeed called by + * git-pull, it will notice the misconfiguration because + * there is no entry in the resulting FETCH_HEAD marked + * for merging. + */ + refspec.src = branch->merge[i]->src; + refspec.dst = NULL; + refspec.pattern = 0; + refspec.force = 0; + get_fetch_map(remote_refs, &refspec, tail, 1); + for (rm = *old_tail; rm; rm = rm->next) + rm->merge = 1; } } @@ -43,11 +79,11 @@ static struct ref *get_ref_map(struct transport *transport, struct ref *ref_map = NULL; struct ref **tail = &ref_map; - struct ref *remote_refs = transport_get_remote_refs(transport); + const struct ref *remote_refs = transport_get_remote_refs(transport); if (ref_count || tags) { for (i = 0; i < ref_count; i++) { - get_fetch_map(remote_refs, &refs[i], &tail); + get_fetch_map(remote_refs, &refs[i], &tail, 0); if (refs[i].dst && refs[i].dst[0]) *autotags = 1; } @@ -60,35 +96,43 @@ static struct ref *get_ref_map(struct transport *transport, refspec.dst = "refs/tags/"; refspec.pattern = 1; refspec.force = 0; - get_fetch_map(remote_refs, &refspec, &tail); + get_fetch_map(remote_refs, &refspec, &tail, 0); } } else { /* Use the defaults */ struct remote *remote = transport->remote; - if (remote->fetch_refspec_nr) { + struct branch *branch = branch_get(NULL); + int has_merge = branch_has_merge_config(branch); + if (remote && (remote->fetch_refspec_nr || has_merge)) { for (i = 0; i < remote->fetch_refspec_nr; i++) { - get_fetch_map(remote_refs, &remote->fetch[i], &tail); + get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0); if (remote->fetch[i].dst && remote->fetch[i].dst[0]) *autotags = 1; + if (!i && !has_merge && ref_map && + !remote->fetch[0].pattern) + ref_map->merge = 1; } - find_merge_config(ref_map, remote); + /* + * if the remote we're fetching from is the same + * as given in branch..remote, we add the + * ref given in branch..merge, too. + */ + if (has_merge && + !strcmp(branch->remote_name, remote->name)) + add_merge_config(&ref_map, remote_refs, branch, &tail); } else { ref_map = get_remote_ref(remote_refs, "HEAD"); - + if (!ref_map) + die("Couldn't find remote ref HEAD"); ref_map->merge = 1; } } + ref_remove_duplicates(ref_map); return ref_map; } -static void show_new(enum object_type type, unsigned char *sha1_new) -{ - fprintf(stderr, " %s: %s\n", typename(type), - find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); -} - static int s_update_ref(const char *action, struct ref *ref, int check_old) @@ -109,106 +153,121 @@ static int s_update_ref(const char *action, return 0; } +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) +#define REFCOL_WIDTH 10 + static int update_local_ref(struct ref *ref, - const char *note, - int verbose) + const char *remote, + int verbose, + char *display) { - char oldh[41], newh[41]; struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); + const char *pretty_ref = ref->name + ( + !prefixcmp(ref->name, "refs/heads/") ? 11 : + !prefixcmp(ref->name, "refs/tags/") ? 10 : + !prefixcmp(ref->name, "refs/remotes/") ? 13 : + 0); + *display = 0; type = sha1_object_info(ref->new_sha1, NULL); if (type < 0) die("object %s not found", sha1_to_hex(ref->new_sha1)); if (!*ref->name) { /* Not storing */ - if (verbose) { - fprintf(stderr, "* fetched %s\n", note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "* branch %s -> FETCH_HEAD", remote); return 0; } if (!hashcmp(ref->old_sha1, ref->new_sha1)) { - if (verbose) { - fprintf(stderr, "* %s: same as %s\n", - ref->name, note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH, + "[up to date]", REFCOL_WIDTH, remote, + pretty_ref); return 0; } - if (!strcmp(ref->name, current_branch->name) && + if (current_branch && + !strcmp(ref->name, current_branch->name) && !(update_head_ok || is_bare_repository()) && !is_null_sha1(ref->old_sha1)) { /* * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - fprintf(stderr, - " * %s: Cannot fetch into the current branch.\n", - ref->name); + sprintf(display, "! %-*s %-*s -> %s (can't fetch in current branch)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - fprintf(stderr, "* %s: updating with %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + sprintf(display, "- %-*s %-*s -> %s", + SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote, + pretty_ref); return s_update_ref("updating tag", ref, 0); } - current = lookup_commit_reference(ref->old_sha1); - updated = lookup_commit_reference(ref->new_sha1); + current = lookup_commit_reference_gently(ref->old_sha1, 1); + updated = lookup_commit_reference_gently(ref->new_sha1, 1); if (!current || !updated) { - char *msg; - if (!strncmp(ref->name, "refs/tags/", 10)) + const char *msg; + const char *what; + if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; - else + what = "[new tag]"; + } + else { msg = "storing head"; - fprintf(stderr, "* %s: storing %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + what = "[new branch]"; + } + + sprintf(display, "* %-*s %-*s -> %s", SUMMARY_WIDTH, what, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref(msg, ref, 0); } - strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); - strcpy(newh, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - if (in_merge_bases(current, &updated, 1)) { - fprintf(stderr, "* %s: fast forward to %s\n", - ref->name, note); - fprintf(stderr, " old..new: %s..%s\n", oldh, newh); + char quickref[83]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, ".."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, " %-*s %-*s -> %s", SUMMARY_WIDTH, quickref, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref("fast forward", ref, 1); - } - if (!force && !ref->force) { - fprintf(stderr, - "* %s: not updating to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, - " old...new: %s...%s\n", oldh, newh); + } else if (force || ref->force) { + char quickref[84]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, "..."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, "+ %-*s %-*s -> %s (forced update)", + SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref); + return s_update_ref("forced-update", ref, 1); + } else { + sprintf(display, "! %-*s %-*s -> %s (non fast forward)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } - fprintf(stderr, - "* %s: forcing update to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, " old...new: %s...%s\n", oldh, newh); - return s_update_ref("forced-update", ref, 1); } -static void store_updated_refs(const char *url, struct ref *ref_map) +static int store_updated_refs(const char *url, struct ref *ref_map) { FILE *fp; struct commit *commit; - int url_len, i, note_len; + int url_len, i, note_len, shown_url = 0; char note[1024]; const char *what, *kind; struct ref *rm; + char *filename = git_path("FETCH_HEAD"); - fp = fopen(git_path("FETCH_HEAD"), "a"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; @@ -217,10 +276,10 @@ static void store_updated_refs(const char *url, struct ref *ref_map) strcpy(ref->name, rm->peer_ref->name); hashcpy(ref->old_sha1, rm->peer_ref->old_sha1); hashcpy(ref->new_sha1, rm->old_sha1); - ref->force = rm->force; + ref->force = rm->peer_ref->force; } - commit = lookup_commit_reference(rm->old_sha1); + commit = lookup_commit_reference_gently(rm->old_sha1, 1); if (!commit) rm->merge = 0; @@ -266,17 +325,91 @@ static void store_updated_refs(const char *url, struct ref *ref_map) rm->merge ? "" : "not-for-merge", note); - if (ref) - update_local_ref(ref, note, verbose); + if (ref) { + update_local_ref(ref, what, verbose, note); + if (*note) { + if (!shown_url) { + fprintf(stderr, "From %.*s\n", + url_len, url); + shown_url = 1; + } + fprintf(stderr, " %s\n", note); + } + } } fclose(fp); + return 0; +} + +/* + * We would want to bypass the object transfer altogether if + * everything we are going to fetch already exists and connected + * locally. + * + * The refs we are going to fetch are in to_fetch (nr_heads in + * total). If running + * + * $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all + * + * does not error out, that means everything reachable from the + * refs we are going to fetch exists and is connected to some of + * our existing refs. + */ +static int quickfetch(struct ref *ref_map) +{ + struct child_process revlist; + struct ref *ref; + char **argv; + int i, err; + + /* + * If we are deepening a shallow clone we already have these + * objects reachable. Running rev-list here will return with + * a good (0) exit status and we'll bypass the fetch that we + * really need to perform. Claiming failure now will ensure + * we perform the network exchange to deepen our history. + */ + if (depth) + return -1; + + for (i = 0, ref = ref_map; ref; ref = ref->next) + i++; + if (!i) + return 0; + + argv = xmalloc(sizeof(*argv) * (i + 6)); + i = 0; + argv[i++] = xstrdup("rev-list"); + argv[i++] = xstrdup("--quiet"); + argv[i++] = xstrdup("--objects"); + for (ref = ref_map; ref; ref = ref->next) + argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1)); + argv[i++] = xstrdup("--not"); + argv[i++] = xstrdup("--all"); + argv[i++] = NULL; + + memset(&revlist, 0, sizeof(revlist)); + revlist.argv = (const char**)argv; + revlist.git_cmd = 1; + revlist.no_stdin = 1; + revlist.no_stdout = 1; + revlist.no_stderr = 1; + err = run_command(&revlist); + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); + return err; } static int fetch_refs(struct transport *transport, struct ref *ref_map) { - int ret = transport_fetch_refs(transport, ref_map); + int ret = quickfetch(ref_map); + if (ret) + ret = transport_fetch_refs(transport, ref_map); if (!ret) - store_updated_refs(transport->url, ref_map); + ret |= store_updated_refs(transport->url, ref_map); + transport_unlock_pack(transport); return ret; } @@ -295,12 +428,12 @@ static struct ref *find_non_local_tags(struct transport *transport, struct path_list new_refs = { NULL, 0, 0, 1 }; char *ref_name; int ref_name_len; - unsigned char *ref_sha1; - struct ref *tag_ref; + const unsigned char *ref_sha1; + const struct ref *tag_ref; struct ref *rm = NULL; struct ref *ref_map = NULL; struct ref **tail = &ref_map; - struct ref *ref; + const struct ref *ref; for_each_ref(add_existing, &existing_refs); for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { @@ -325,10 +458,7 @@ static struct ref *find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && - lookup_object(ref->old_sha1)) { - fprintf(stderr, "Auto-following %s\n", - ref_name); - + has_sha1_file(ref->old_sha1)) { path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); @@ -357,28 +487,23 @@ static int do_fetch(struct transport *transport, if (transport->remote->fetch_tags == -1) no_tags = 1; - if (!transport->ops || !transport->ops->get_refs_list || - !(transport->ops->fetch_refs || transport->ops->fetch_objs)) + if (!transport->get_refs_list || !transport->fetch) die("Don't know how to fetch from %s", transport->url); /* if not appending, truncate FETCH_HEAD */ - if (!append) - fclose(fopen(git_path("FETCH_HEAD"), "w")); + if (!append) { + char *filename = git_path("FETCH_HEAD"); + FILE *fp = fopen(filename, "w"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); + fclose(fp); + } ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); for (rm = ref_map; rm; rm = rm->next) { if (rm->peer_ref) read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1); - - printf("%s : %s\n", rm->name, rm->peer_ref ? rm->peer_ref->name : NULL); - printf(" < %s\n", sha1_to_hex(rm->old_sha1)); - if (rm->peer_ref) - printf(" > %s\n", sha1_to_hex(rm->peer_ref->old_sha1)); - if (!rm->peer_ref || - hashcmp(rm->old_sha1, rm->peer_ref->old_sha1)) { - printf("%s needs update.\n", rm->name); - } } if (fetch_refs(transport, ref_map)) { @@ -404,34 +529,27 @@ static int do_fetch(struct transport *transport, return 0; } -static int fetch_config(const char *var, const char *value) +static void set_option(const char *name, const char *value) { - if (strcmp(var, "fetch.unpacklimit") == 0) { - unpacklimit = git_config_int(var, value); - return 0; - } - - if (strcmp(var, "transfer.unpacklimit") == 0) { - unpacklimit = git_config_int(var, value); - return 0; - } - - return git_default_config(var, value); + int r = transport_set_option(transport, name, value); + if (r < 0) + die("Option \"%s\" value \"%s\" is not valid for %s\n", + name, value, transport->url); + if (r > 0) + warning("Option \"%s\" is ignored for %s\n", + name, transport->url); } int cmd_fetch(int argc, const char **argv, const char *prefix) { struct remote *remote; - struct transport *transport; int i, j, rla_offset; static const char **refs = NULL; int ref_nr = 0; int cmd_len = 0; - const char *depth = NULL, *upload_pack = NULL; + const char *upload_pack = NULL; int keep = 0; - git_config(fetch_config); - for (i = 1; i < argc; i++) { const char *arg = argv[i]; cmd_len += strlen(arg); @@ -484,7 +602,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) depth = argv[i]; continue; } - if (!strcmp(arg, "--quiet")) { + if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) { quiet = 1; continue; } @@ -503,7 +621,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) rla_offset = strlen(default_rla); for (j = 1; j < argc; j++) { sprintf(default_rla + rla_offset, " %s", argv[j]); - rla_offset += strlen(argv[j]); + rla_offset += strlen(argv[j]) + 1; } if (i == argc) @@ -511,16 +629,17 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) else remote = remote_get(argv[i++]); - transport = transport_get(remote, remote->uri[0], 1); + transport = transport_get(remote, remote->url[0]); if (verbose >= 2) transport->verbose = 1; if (quiet) - transport->verbose = 0; + transport->verbose = -1; if (upload_pack) - transport_set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack); + set_option(TRANS_OPT_UPLOADPACK, upload_pack); if (keep) - transport_set_option(transport, TRANS_OPT_KEEP, "yes"); - transport_set_option(transport, TRANS_OPT_DEPTH, depth); + set_option(TRANS_OPT_KEEP, "yes"); + if (depth) + set_option(TRANS_OPT_DEPTH, depth); if (!transport->url) die("Where do you want to fetch from today?"); @@ -544,9 +663,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } refs[j] = NULL; ref_nr = j; - for (j = 0; refs[j]; j++) - printf("ref: %s\n", refs[j]); } + signal(SIGINT, unlock_pack_on_signal); + atexit(unlock_pack); return do_fetch(transport, parse_ref_spec(ref_nr, refs), ref_nr); }