From 937a515a15f776aa84430574f71367292a52b978 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Jul 2006 21:28:20 -0700 Subject: [PATCH] upload-pack: stop the other side when they have more roots than we do. When the downloader has more roots than we do, we will see many "have" that leads to the root we do not have and let the other side walk all the way to that root. Tell them to stop sending the branch'es ancestors by sending a fake "ACK" when we already have common ancestor for the wanted refs. Signed-off-by: Junio C Hamano --- upload-pack.c | 101 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 9 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index 35c7ecb7b4..e8f4be373c 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -11,9 +11,15 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] "; -#define THEY_HAVE (1U << 0) -#define OUR_REF (1U << 1) -#define WANTED (1U << 2) +/* bits #0..7 in revision.h, #8..10 in commit.c */ +#define THEY_HAVE (1u << 11) +#define OUR_REF (1u << 12) +#define WANTED (1u << 13) +#define COMMON_KNOWN (1u << 14) +#define REACHABLE (1u << 15) + +static unsigned long oldest_have = 0; + static int multi_ack = 0, nr_our_refs = 0; static int use_thin_pack = 0; static struct object_array have_obj; @@ -323,11 +329,12 @@ static void create_pack_file(void) static int got_sha1(char *hex, unsigned char *sha1) { struct object *o; + int we_knew_they_have = 0; if (get_sha1_hex(hex, sha1)) die("git-upload-pack: expected SHA1 object, got '%s'", hex); if (!has_sha1_file(sha1)) - return 0; + return -1; o = lookup_object(sha1); if (!(o && o->parsed)) @@ -336,15 +343,84 @@ static int got_sha1(char *hex, unsigned char *sha1) die("oops (%s)", sha1_to_hex(sha1)); if (o->type == OBJ_COMMIT) { struct commit_list *parents; + struct commit *commit = (struct commit *)o; if (o->flags & THEY_HAVE) - return 0; - o->flags |= THEY_HAVE; - for (parents = ((struct commit*)o)->parents; + we_knew_they_have = 1; + else + o->flags |= THEY_HAVE; + if (!oldest_have || (commit->date < oldest_have)) + oldest_have = commit->date; + for (parents = commit->parents; parents; parents = parents->next) parents->item->object.flags |= THEY_HAVE; } - add_object_array(o, NULL, &have_obj); + if (!we_knew_they_have) { + add_object_array(o, NULL, &have_obj); + return 1; + } + return 0; +} + +static int reachable(struct commit *want) +{ + struct commit_list *work = NULL; + + insert_by_date(want, &work); + while (work) { + struct commit_list *list = work->next; + struct commit *commit = work->item; + free(work); + work = list; + + if (commit->object.flags & THEY_HAVE) { + want->object.flags |= COMMON_KNOWN; + break; + } + if (!commit->object.parsed) + parse_object(commit->object.sha1); + if (commit->object.flags & REACHABLE) + continue; + commit->object.flags |= REACHABLE; + if (commit->date < oldest_have) + continue; + for (list = commit->parents; list; list = list->next) { + struct commit *parent = list->item; + if (!(parent->object.flags & REACHABLE)) + insert_by_date(parent, &work); + } + } + want->object.flags |= REACHABLE; + clear_commit_marks(want, REACHABLE); + free_commit_list(work); + return (want->object.flags & COMMON_KNOWN); +} + +static int ok_to_give_up(void) +{ + int i; + + if (!have_obj.nr) + return 0; + + for (i = 0; i < want_obj.nr; i++) { + struct object *want = want_obj.objects[i].item; + + if (want->flags & COMMON_KNOWN) + continue; + want = deref_tag(want, "a want line", 0); + if (!want || want->type != OBJ_COMMIT) { + /* no way to tell if this is reachable by + * looking at the ancestry chain alone, so + * leave a note to ourselves not to worry about + * this object anymore. + */ + want_obj.objects[i].item->flags |= COMMON_KNOWN; + continue; + } + if (!reachable((struct commit *)want)) + return 0; + } return 1; } @@ -369,7 +445,13 @@ static int get_common_commits(void) } len = strip(line, len); if (!strncmp(line, "have ", 5)) { - if (got_sha1(line+5, sha1)) { + switch (got_sha1(line+5, sha1)) { + case -1: /* they have what we do not */ + if (multi_ack && ok_to_give_up()) + packet_write(1, "ACK %s continue\n", + sha1_to_hex(sha1)); + break; + default: memcpy(hex, sha1_to_hex(sha1), 41); if (multi_ack) { const char *msg = "ACK %s continue\n"; @@ -378,6 +460,7 @@ static int get_common_commits(void) } else if (have_obj.nr == 1) packet_write(1, "ACK %s\n", hex); + break; } continue; }