From b888d61c8308027433df9c243fa551f42db1c76a Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 10 Sep 2007 23:03:25 -0400 Subject: Make fetch a builtin Thanks to Johannes Schindelin for review and fixes, and Julian Phillips for the original C translation. This changes a few small bits of behavior: branch..merge is parsed as if it were the lhs of a fetch refspec, and does not have to exactly match the actual lhs of a refspec, so long as it is a valid abbreviation for the same ref. branch..merge is no longer ignored if the remote is configured with a branches/* file. Neither behavior is useful, because there can only be one ref that gets fetched, but this is more consistant. Also, fetch prints different information to standard out. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 552 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 552 insertions(+) create mode 100644 builtin-fetch.c (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c new file mode 100644 index 000000000..64392f317 --- /dev/null +++ b/builtin-fetch.c @@ -0,0 +1,552 @@ +/* + * "git fetch" + */ +#include "cache.h" +#include "refs.h" +#include "commit.h" +#include "builtin.h" +#include "path-list.h" +#include "remote.h" +#include "transport.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 int unpacklimit; + +static char *default_rla = NULL; + +static void find_merge_config(struct ref *ref_map, struct remote *remote) +{ + struct ref *rm = ref_map; + struct branch *branch = branch_get(NULL); + + 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)) + rm->merge = 1; + } + } +} + +static struct ref *get_ref_map(struct transport *transport, + struct refspec *refs, int ref_count, int tags, + int *autotags) +{ + int i; + struct ref *rm; + struct ref *ref_map = NULL; + struct ref **tail = &ref_map; + + 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); + if (refs[i].dst && refs[i].dst[0]) + *autotags = 1; + } + /* Merge everything on the command line, but not --tags */ + for (rm = ref_map; rm; rm = rm->next) + rm->merge = 1; + if (tags) { + struct refspec refspec; + refspec.src = "refs/tags/"; + refspec.dst = "refs/tags/"; + refspec.pattern = 1; + refspec.force = 0; + get_fetch_map(remote_refs, &refspec, &tail); + } + } else { + /* Use the defaults */ + struct remote *remote = transport->remote; + if (remote->fetch_refspec_nr) { + for (i = 0; i < remote->fetch_refspec_nr; i++) { + get_fetch_map(remote_refs, &remote->fetch[i], &tail); + if (remote->fetch[i].dst && + remote->fetch[i].dst[0]) + *autotags = 1; + } + find_merge_config(ref_map, remote); + } else { + ref_map = get_remote_ref(remote_refs, "HEAD"); + + ref_map->merge = 1; + } + } + + 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) +{ + char msg[1024]; + char *rla = getenv("GIT_REFLOG_ACTION"); + static struct ref_lock *lock; + + if (!rla) + rla = default_rla; + snprintf(msg, sizeof(msg), "%s: %s", rla, action); + lock = lock_any_ref_for_update(ref->name, + check_old ? ref->old_sha1 : NULL, 0); + if (!lock) + return 1; + if (write_ref_sha1(lock, ref->new_sha1, msg) < 0) + return 1; + return 0; +} + +static int update_local_ref(struct ref *ref, + const char *note, + int verbose) +{ + char oldh[41], newh[41]; + struct commit *current = NULL, *updated; + enum object_type type; + struct branch *current_branch = branch_get(NULL); + + 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); + } + 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); + } + return 0; + } + + if (!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); + 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); + return s_update_ref("updating tag", ref, 0); + } + + current = lookup_commit_reference(ref->old_sha1); + updated = lookup_commit_reference(ref->new_sha1); + if (!current || !updated) { + char *msg; + if (!strncmp(ref->name, "refs/tags/", 10)) + msg = "storing tag"; + else + msg = "storing head"; + fprintf(stderr, "* %s: storing %s\n", + ref->name, note); + show_new(type, ref->new_sha1); + 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); + 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); + 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) +{ + FILE *fp; + struct commit *commit; + int url_len, i, note_len; + char note[1024]; + const char *what, *kind; + struct ref *rm; + + fp = fopen(git_path("FETCH_HEAD"), "a"); + for (rm = ref_map; rm; rm = rm->next) { + struct ref *ref = NULL; + + if (rm->peer_ref) { + ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1); + 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; + } + + commit = lookup_commit_reference(rm->old_sha1); + if (!commit) + rm->merge = 0; + + if (!strcmp(rm->name, "HEAD")) { + kind = ""; + what = ""; + } + else if (!prefixcmp(rm->name, "refs/heads/")) { + kind = "branch"; + what = rm->name + 11; + } + else if (!prefixcmp(rm->name, "refs/tags/")) { + kind = "tag"; + what = rm->name + 10; + } + else if (!prefixcmp(rm->name, "refs/remotes/")) { + kind = "remote branch"; + what = rm->name + 13; + } + else { + kind = ""; + what = rm->name; + } + + url_len = strlen(url); + for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) + ; + url_len = i + 1; + if (4 < i && !strncmp(".git", url + i - 3, 4)) + url_len = i - 3; + + note_len = 0; + if (*what) { + if (*kind) + note_len += sprintf(note + note_len, "%s ", + kind); + note_len += sprintf(note + note_len, "'%s' of ", what); + } + note_len += sprintf(note + note_len, "%.*s", url_len, url); + fprintf(fp, "%s\t%s\t%s\n", + sha1_to_hex(commit ? commit->object.sha1 : + rm->old_sha1), + rm->merge ? "" : "not-for-merge", + note); + + if (ref) + update_local_ref(ref, note, verbose); + } + fclose(fp); +} + +static int fetch_refs(struct transport *transport, struct ref *ref_map) +{ + int ret = transport_fetch_refs(transport, ref_map); + if (!ret) + store_updated_refs(transport->url, ref_map); + return ret; +} + +static int add_existing(const char *refname, const unsigned char *sha1, + int flag, void *cbdata) +{ + struct path_list *list = (struct path_list *)cbdata; + path_list_insert(refname, list); + return 0; +} + +static struct ref *find_non_local_tags(struct transport *transport, + struct ref *fetch_map) +{ + static struct path_list existing_refs = { NULL, 0, 0, 0 }; + struct path_list new_refs = { NULL, 0, 0, 1 }; + char *ref_name; + int ref_name_len; + unsigned char *ref_sha1; + struct ref *tag_ref; + struct ref *rm = NULL; + struct ref *ref_map = NULL; + struct ref **tail = &ref_map; + struct ref *ref; + + for_each_ref(add_existing, &existing_refs); + for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { + if (prefixcmp(ref->name, "refs/tags")) + continue; + + ref_name = xstrdup(ref->name); + ref_name_len = strlen(ref_name); + ref_sha1 = ref->old_sha1; + + if (!strcmp(ref_name + ref_name_len - 3, "^{}")) { + ref_name[ref_name_len - 3] = 0; + tag_ref = transport_get_remote_refs(transport); + while (tag_ref) { + if (!strcmp(tag_ref->name, ref_name)) { + ref_sha1 = tag_ref->old_sha1; + break; + } + tag_ref = tag_ref->next; + } + } + + 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); + + path_list_insert(ref_name, &new_refs); + + rm = alloc_ref(strlen(ref_name) + 1); + strcpy(rm->name, ref_name); + rm->peer_ref = alloc_ref(strlen(ref_name) + 1); + strcpy(rm->peer_ref->name, ref_name); + hashcpy(rm->old_sha1, ref_sha1); + + *tail = rm; + tail = &rm->next; + } + free(ref_name); + } + + return ref_map; +} + +static int do_fetch(struct transport *transport, + struct refspec *refs, int ref_count) +{ + struct ref *ref_map, *fetch_map; + struct ref *rm; + int autotags = (transport->remote->fetch_tags == 1); + if (transport->remote->fetch_tags == 2 && !no_tags) + tags = 1; + 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)) + 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")); + + 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)) { + free_refs(ref_map); + return 1; + } + + fetch_map = ref_map; + + /* if neither --no-tags nor --tags was specified, do automated tag + * following ... */ + if (!(tags || no_tags) && autotags) { + ref_map = find_non_local_tags(transport, fetch_map); + if (ref_map) { + transport_set_option(transport, TRANS_OPT_DEPTH, "0"); + fetch_refs(transport, ref_map); + } + free_refs(ref_map); + } + + free_refs(fetch_map); + + return 0; +} + +static int fetch_config(const char *var, 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 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; + int keep = 0; + + git_config(fetch_config); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + cmd_len += strlen(arg); + + if (arg[0] != '-') + break; + if (!strcmp(arg, "--append") || !strcmp(arg, "-a")) { + append = 1; + continue; + } + if (!prefixcmp(arg, "--upload-pack=")) { + upload_pack = arg + 14; + continue; + } + if (!strcmp(arg, "--upload-pack")) { + i++; + if (i == argc) + usage(fetch_usage); + upload_pack = argv[i]; + continue; + } + if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) { + force = 1; + continue; + } + if (!strcmp(arg, "--no-tags")) { + no_tags = 1; + continue; + } + if (!strcmp(arg, "--tags") || !strcmp(arg, "-t")) { + tags = 1; + continue; + } + if (!strcmp(arg, "--keep") || !strcmp(arg, "-k")) { + keep = 1; + continue; + } + if (!strcmp(arg, "--update-head-ok") || !strcmp(arg, "-u")) { + update_head_ok = 1; + continue; + } + if (!prefixcmp(arg, "--depth=")) { + depth = arg + 8; + continue; + } + if (!strcmp(arg, "--depth")) { + i++; + if (i == argc) + usage(fetch_usage); + depth = argv[i]; + continue; + } + if (!strcmp(arg, "--quiet")) { + quiet = 1; + continue; + } + if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { + verbose++; + continue; + } + usage(fetch_usage); + } + + for (j = i; j < argc; j++) + cmd_len += strlen(argv[j]); + + default_rla = xmalloc(cmd_len + 5 + argc + 1); + sprintf(default_rla, "fetch"); + rla_offset = strlen(default_rla); + for (j = 1; j < argc; j++) { + sprintf(default_rla + rla_offset, " %s", argv[j]); + rla_offset += strlen(argv[j]); + } + + if (i == argc) + remote = remote_get(NULL); + else + remote = remote_get(argv[i++]); + + transport = transport_get(remote, remote->uri[0], 1); + if (verbose >= 2) + transport->verbose = 1; + if (quiet) + transport->verbose = 0; + if (upload_pack) + transport_set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack); + if (keep) + transport_set_option(transport, TRANS_OPT_KEEP, "yes"); + transport_set_option(transport, TRANS_OPT_DEPTH, depth); + + if (!transport->url) + die("Where do you want to fetch from today?"); + + if (i < argc) { + int j = 0; + refs = xcalloc(argc - i + 1, sizeof(const char *)); + while (i < argc) { + if (!strcmp(argv[i], "tag")) { + char *ref; + i++; + ref = xmalloc(strlen(argv[i]) * 2 + 22); + strcpy(ref, "refs/tags/"); + strcat(ref, argv[i]); + strcat(ref, ":refs/tags/"); + strcat(ref, argv[i]); + refs[j++] = ref; + } else + refs[j++] = argv[i]; + i++; + } + refs[j] = NULL; + ref_nr = j; + for (j = 0; refs[j]; j++) + printf("ref: %s\n", refs[j]); + } + + return do_fetch(transport, parse_ref_spec(ref_nr, refs), ref_nr); +} -- cgit v1.2.1 From 1aad91f5a715af92892aea7764beb829938ab111 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 14 Sep 2007 03:31:07 -0400 Subject: Correct builtin-fetch to handle + in refspecs If we are fetching to a local reference (the so called peer_ref) and the refspec that created this ref/peer_ref association had started with '+' we are supposed to allow a non-fast-forward update during fetch, even if --force was not supplied on the command line. The builtin-fetch implementation was not honoring this setting as it was copied from the wrong struct ref instance. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 64392f317..c8c24d28a 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -217,7 +217,7 @@ 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); -- cgit v1.2.1 From 4ad1eada9774a1f340beb4fdf78f1735534741bb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 14 Sep 2007 03:31:09 -0400 Subject: Fix off by one bug in reflog messages written by builtin-fetch We are adding a space between each argument in the sprintf above so we must account for this as we update our position within the reflog message and append in any remaining arguments. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index c8c24d28a..016c6e43e 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -503,7 +503,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) -- cgit v1.2.1 From 133296f00cd441b5525ccc3e82ee13cbfc62d246 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 14 Sep 2007 03:31:11 -0400 Subject: Remove unnecessary debugging from builtin-fetch The older git-fetch client did not produce all of this debugging information to stdout. Most end-users and Porcelain (e.g. StGIT, git-gui, qgit) do not want to see these low-level details on the console so they should be removed. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 016c6e43e..33b740cd1 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -370,15 +370,6 @@ static int do_fetch(struct transport *transport, 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)) { -- cgit v1.2.1 From f1ae391e17be638a14c7505598b73430dbf328f1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 14 Sep 2007 03:31:13 -0400 Subject: Remove unused unpacklimit variable from builtin-fetch Never referenced. This should actually be handled down inside of builtin-fetch-pack, not up here in the generic user frontend. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 33b740cd1..a041df9fa 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -12,9 +12,6 @@ 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 int unpacklimit; - static char *default_rla = NULL; static void find_merge_config(struct ref *ref_map, struct remote *remote) @@ -395,21 +392,6 @@ static int do_fetch(struct transport *transport, return 0; } -static int fetch_config(const char *var, 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 cmd_fetch(int argc, const char **argv, const char *prefix) { struct remote *remote; @@ -421,8 +403,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) const char *depth = NULL, *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); -- cgit v1.2.1 From 425b1393139d99d89c7a95906686d9b041f2ee3d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 14 Sep 2007 03:31:21 -0400 Subject: Simplify fetch transport API to just one function Commit walkers need to know the SHA-1 name of any objects they have been asked to fetch while the native pack transport only wants to know the names of the remote refs as the remote side must do the name->SHA-1 translation. Since we only have three fetch implementations and one of them (bundle) doesn't even need the name information we can reduce the code required to perform a fetch by having just one function and passing of the filtered list of refs to be fetched. Each transport can then obtain the information it needs from that ref array to construct its own internal operation state. Signed-off-by: Shawn O. Pearce Conflicts: transport.c Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index a041df9fa..f5a2718ac 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -355,7 +355,7 @@ static int do_fetch(struct transport *transport, no_tags = 1; if (!transport->ops || !transport->ops->get_refs_list || - !(transport->ops->fetch_refs || transport->ops->fetch_objs)) + !transport->ops->fetch) die("Don't know how to fetch from %s", transport->url); /* if not appending, truncate FETCH_HEAD */ -- cgit v1.2.1 From 1788c39cd0742439b9bedc28bc10bc4d105b6c0f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 14 Sep 2007 03:31:23 -0400 Subject: Remove pack.keep after ref updates in git-fetch If we are using a native packfile to perform a git-fetch invocation and the received packfile contained more than the configured limits of fetch.unpackLimit/transfer.unpackLimit then index-pack will output a single line saying "keep\t$sha1\n" to stdout. This line needs to be captured and retained so we can delete the corresponding .keep file ("$GIT_DIR/objects/pack/pack-$sha1.keep") once all refs have been safely updated. This trick has long been in use with git-fetch.sh and its lower level helper git-fetch--tool as a way to allow index-pack to save the new packfile before the refs have been updated and yet avoid a race with any concurrently running git-repack process. It was unfortunately lost when git-fetch.sh was converted to pure C and fetch--tool was no longer being invoked. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index f5a2718ac..8e433d1bf 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -274,6 +274,7 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map) int ret = transport_fetch_refs(transport, ref_map); if (!ret) store_updated_refs(transport->url, ref_map); + transport_unlock_pack(transport); return ret; } -- cgit v1.2.1 From e4022ed2c85825f238661e3e532b0bb108b5e318 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 14 Sep 2007 03:31:25 -0400 Subject: Always ensure the pack.keep file is removed by git-fetch If we are using a native transport and the transport chose to save the packfile it may have created a .keep file to protect the packfile from a concurrently running git-repack process. In such a case the git-fetch process should make sure it will unlink the .keep file even if it fails to update any refs as otherwise the newly downloaded packfile's diskspace will never be reclaimed if the objects are not actually referenced. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 8e433d1bf..8b0fdbe90 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -13,6 +13,20 @@ static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack Date: Sat, 15 Sep 2007 03:23:14 -0400 Subject: Remove unnecessary 'fetch' argument from transport_get API We don't actually need to know at the time of transport_get if the caller wants to fetch, push, or do both on the returned object. It is easier to just delay the initialization of the HTTP walker until we know we will need it by providing a CURL specific fetch function in the curl_transport that makes sure the walker instance is initialized before use. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 8b0fdbe90..300d5635b 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -496,7 +496,7 @@ 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->uri[0]); if (verbose >= 2) transport->verbose = 1; if (quiet) -- cgit v1.2.1 From b3abdd9d216c578383b66bb10b95edb3380640e7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 16 Sep 2007 02:31:26 -0400 Subject: Allow builtin-fetch to work on a detached HEAD If we are running fetch in a repository that has a detached HEAD then there is no current_branch available. In such a case any ref that the fetch might update by definition cannot also be the current branch so we should always bypass the "don't update HEAD" test. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 300d5635b..d9272edae 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -151,7 +151,8 @@ static int update_local_ref(struct ref *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)) { /* -- cgit v1.2.1 From f38395905b3d49bd68e0c01ca2310bf3387e1063 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 16 Sep 2007 02:32:17 -0400 Subject: Remove more debugging from builtin-fetch Older git-fetch.sh doesn't print "ref: X" when invoked as `git fetch $url X" so we shouldn't do that now in the new builtin version. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index d9272edae..20926e054 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -530,8 +530,6 @@ 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); -- cgit v1.2.1 From 85682c1903a4ae776b0bf2d30d9ecd1e19689131 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 18 Sep 2007 04:54:53 -0400 Subject: Correct handling of branch.$name.merge in builtin-fetch My prior bug fix for git-push titled "Don't configure remote "." to fetch everything to itself" actually broke t5520 as we were unable to evaluate a branch configuration of: [branch "copy"] remote = . merge = refs/heads/master as remote "." did not have a "remote...fetch" configuration entry to offer up refs/heads/master as a possible candidate available to be fetched and merged. In shell script git-fetch and prior to the above mentioned commit this was hardcoded for a url of "." to be the set of local branches. Chasing down this bug led me to the conclusion that our prior behavior with regards to branch.$name.merge was incorrect. In the shell script based git-fetch implementation we only fetched and merged a branch if it appeared both in branch.$name.merge *and* in remote.$r.fetch, where $r = branch.$name.remote. In other words in the following config file: [remote "origin"] url = git://git.kernel.org/pub/scm/git/git.git fetch = refs/heads/master:refs/remotes/origin/master [branch "master"] remote = origin merge = refs/heads/master [branch "pu"] remote = origin merge = refs/heads/pu Attempting to run `git pull` while on branch "pu" would always give the user "Already up-to-date" as git-fetch did not fetch pu and thus did not mark it for merge in .git/FETCH_HEAD. The configured merge would always be ignored and the user would be left scratching her confused head wondering why merge did not work on "pu" but worked fine on "master". If we are using the "default fetch" specification for the current branch and the current branch has a branch.$name.merge configured we now union it with the list of refs in remote.$r.fetch. This way the above configuration does what the user expects it to do, which is to fetch only "master" by default but when on "pu" to fetch both "master" and "pu". This uncovered some breakage in the test suite where old-style Cogito branches (.git/branches/$r) did not fetch the branches listed in .git/config for merging and thus did not actually merge them if the user tried to use `git pull` on that branch. Junio and I discussed it on list and felt that the union approach here makes more sense to DWIM for the end-user than silently ignoring their configured request so the test vectors for t5515 have been updated to include for-merge lines in .git/FETCH_HEAD where they have been configured for-merge in .git/config. Since we are now performing a union of the fetch specification and the merge specification and we cannot allow a branch to be listed twice (otherwise it comes out twice in .git/FETCH_HEAD) we need to perform a double loop here over all of the branch.$name.merge lines and try to set their merge flag if we have already schedule that branch for fetching by remote.$r.fetch. If no match is found then we must add new specifications to fetch the branch but not store it as no local tracking branch has been designated. Signed-off-by: Shawn O. Pearce --- builtin-fetch.c | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 20926e054..670af0b53 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -28,20 +28,37 @@ static void unlock_pack_on_signal(int signo) raise(signo); } -static void find_merge_config(struct ref *ref_map, struct remote *remote) +static void add_merge_config(struct ref **head, + 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. + */ + refspec.src = branch->merge[i]->src; + refspec.dst = NULL; + refspec.pattern = 0; + refspec.force = 0; + get_fetch_map(remote_refs, &refspec, tail); + for (rm = *old_tail; rm; rm = rm->next) + rm->merge = 1; } } @@ -76,17 +93,22 @@ static struct ref *get_ref_map(struct transport *transport, } 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); if (remote->fetch[i].dst && remote->fetch[i].dst[0]) *autotags = 1; + if (!i && !has_merge && ref_map && + !strcmp(remote->fetch[0].src, ref_map->name)) + ref_map->merge = 1; } - find_merge_config(ref_map, remote); + if (has_merge) + add_merge_config(&ref_map, remote_refs, branch, &tail); } else { ref_map = get_remote_ref(remote_refs, "HEAD"); - ref_map->merge = 1; } } -- cgit v1.2.1 From ab865e6eec1f40938604b1c28a32525c1fdc7227 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 18 Sep 2007 04:54:57 -0400 Subject: Avoid printing unnecessary warnings during fetch and push If a transport doesn't support an option we already are telling the higher level application (fetch or push) that the option is not valid by sending back a >0 return value from transport_set_option so there's not a strong motivation to have the function perform the output itself. Instead we should let the higher level application do the output if it is necessary. This avoids always telling the user that depth isn't supported on HTTP urls even when they did not pass a --depth option to git-fetch. If the user passes an option and the option value is invalid we now properly die in git-fetch instead of just spitting out a message and running anyway. This mimics prior behavior better where incorrect/malformed options are not accepted by the process. Signed-off-by: Shawn O. Pearce --- builtin-fetch.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 670af0b53..b9722e5fb 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -430,6 +430,17 @@ static int do_fetch(struct transport *transport, return 0; } +static void set_option(const char *name, const char *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; @@ -525,10 +536,11 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (quiet) transport->verbose = 0; 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?"); -- cgit v1.2.1 From 28b91f8ad9e4791b5c35ca6bffbb78725b4e5bbf Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 19 Sep 2007 00:49:27 -0400 Subject: Rename remote.uri to remote.url within remote handling internals Anyplace we talk about the address of a remote repository we always refer to it as a URL, especially in the configuration file and .git/remotes where we call it "remote.$n.url" or start the first line with "URL:". Calling this value a uri within the internal C code just doesn't jive well with our commonly accepted terms. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index b9722e5fb..997a8ff95 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -530,7 +530,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) else remote = remote_get(argv[i++]); - transport = transport_get(remote, remote->uri[0]); + transport = transport_get(remote, remote->url[0]); if (verbose >= 2) transport->verbose = 1; if (quiet) -- cgit v1.2.1 From 824d5776c3f275c644c71b8c7e254bd2d7b08071 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 19 Sep 2007 00:49:31 -0400 Subject: Refactor struct transport_ops inlined into struct transport Aside from reducing the code by 20 lines this refactoring removes a level of indirection when trying to access the operations of a given transport "instance", making the code clearer and easier to follow. It also has the nice effect of giving us the benefits of C99 style struct initialization (namely ".fetch = X") without requiring that level of language support from our compiler. We don't need to worry about new operation methods being added as they will now be NULL'd out automatically by the xcalloc() we use to create the new struct transport we supply to the caller. This pattern already exists in struct walker, so we already have a precedent for it in Git. We also don't really need to worry about any sort of performance decreases that may occur as a result of filling out 4-8 op pointers when we make a "struct transport". The extra few CPU cycles this requires over filling in the "struct transport_ops" is killed by the time it will take Git to actually *use* one of those functions, as most transport operations are going over the wire or will be copying object data locally between two directories. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 997a8ff95..2f639ccef 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -392,8 +392,7 @@ 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) + if (!transport->get_refs_list || !transport->fetch) die("Don't know how to fetch from %s", transport->url); /* if not appending, truncate FETCH_HEAD */ -- cgit v1.2.1 From cfb8f898a883e2fb2fd5ecec0fe83662b64f1373 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Fri, 28 Sep 2007 19:34:17 -0400 Subject: Allow abbreviations in the first refspec to be merged The config item for a refspec side and the ref name that it matches aren't necessarily character-for-character identical. We actually want to merge a ref by default if: there is no per-branch config, it is the found result of looking for the match for the first refspec, and the first refspec is not a pattern. Beyond that, anything that get_fetch_map() thinks matches is fine. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 2f639ccef..ac68ff592 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -102,7 +102,7 @@ static struct ref *get_ref_map(struct transport *transport, remote->fetch[i].dst[0]) *autotags = 1; if (!i && !has_merge && ref_map && - !strcmp(remote->fetch[0].src, ref_map->name)) + !remote->fetch[0].pattern) ref_map->merge = 1; } if (has_merge) -- cgit v1.2.1 From 2b5a06edca8f7237aad6464b349b79772024d2a2 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Tue, 2 Oct 2007 22:49:15 -0400 Subject: Restore default verbosity for http fetches. This adds a verbosity level below 0 for suppressing default messages with --quiet, and makes the default for http be verbose instead of quiet. This matches the behavior of the shell script version of git-fetch. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index ac68ff592..cf7498b15 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -533,7 +533,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (verbose >= 2) transport->verbose = 1; if (quiet) - transport->verbose = 0; + transport->verbose = -1; if (upload_pack) set_option(TRANS_OPT_UPLOADPACK, upload_pack); if (keep) -- cgit v1.2.1 From 2467a4fa03ff849fcf2f6a93b89057aebd49c62b Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 8 Oct 2007 00:25:07 -0400 Subject: Remove duplicate ref matches in fetch If multiple refspecs matched the same ref, the update would be processed multiple times. Now having the same destination for the same source has no additional effect, and having the same destination for different sources is an error. Signed-off-by: Daniel Barkalow Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- builtin-fetch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index cf7498b15..caaca6364 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -112,6 +112,7 @@ static struct ref *get_ref_map(struct transport *transport, ref_map->merge = 1; } } + ref_remove_duplicates(ref_map); return ref_map; } -- cgit v1.2.1 From da0204df587ae76cd291bc7e495fc60d873c2f20 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 11 Oct 2007 01:47:55 +0100 Subject: fetch: if not fetching from default remote, ignore default merge When doing "git fetch " on a remote that does not have the branch referenced in branch..merge, git fetch failed. It failed because it tried to add the "merge" ref to the refs to be fetched. Fix that. And add a test case. Incidentally, this unconvered a bug in our own test suite, where "git pull " was expected to merge the ref given in the defaults, even if not pulling from the default remote. Signed-off-by: Johannes Schindelin Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- builtin-fetch.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index caaca6364..3442f3d09 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -105,7 +105,13 @@ static struct ref *get_ref_map(struct transport *transport, !remote->fetch[0].pattern) ref_map->merge = 1; } - if (has_merge) + /* + * 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"); -- cgit v1.2.1 From cfa5b2b7faa1a620ec6599a63fa5b9b45bd62b57 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 19 Oct 2007 00:54:59 -0400 Subject: Avoid scary errors about tagged trees/blobs during git-fetch This is the same bug as 42a32174b600f139b489341b1281fb1bfa14c252. The warning "Object $X is a tree, not a commit" is bogus and is not relevant here. If its not a commit we just need to make sure we don't mark it for merge as we fill out FETCH_HEAD. Signed-off-by: Shawn O. Pearce --- builtin-fetch.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'builtin-fetch.c') diff --git a/builtin-fetch.c b/builtin-fetch.c index 3442f3d09..b9d2b0c27 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -202,8 +202,8 @@ static int update_local_ref(struct ref *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)) @@ -261,7 +261,7 @@ static void store_updated_refs(const char *url, struct ref *ref_map) 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; -- cgit v1.2.1