upload-pack: Do not choke on too many heads request.
authorJunio C Hamano <junkio@cox.net>
Wed, 5 Oct 2005 21:49:54 +0000 (14:49 -0700)
committerJunio C Hamano <junkio@cox.net>
Wed, 5 Oct 2005 21:49:54 +0000 (14:49 -0700)
Cloning from a repository with more than 256 refs (heads and tags
included) will choke, because upload-pack has a built-in limit of
feeding not more than MAX_NEEDS (currently 256) heads to underlying
git-rev-list.  This is a problem when cloning a repository with many
tags, like http://www.linux-mips.org/pub/scm/linux.git, which has 290+
tags.

This commit introduces a new flag, --all, to git-rev-list, to include
all refs in the repository.  Updated upload-pack detects requests that
ask more than MAX_NEEDS refs, and sends everything back instead.

We may probably want to tweak the definitions of MAX_NEEDS and
MAX_HAS, but that is a separate topic.

Signed-off-by: Junio C Hamano <junkio@cox.net>
rev-list.c
rev-parse.c
upload-pack.c

index 5ec9ccb..c60aa72 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "refs.h"
 #include "tag.h"
 #include "commit.h"
 #include "tree.h"
@@ -489,6 +490,22 @@ static void handle_one_commit(struct commit *com, struct commit_list **lst)
        commit_list_insert(com, lst);
 }
 
+/* for_each_ref() callback does not allow user data -- Yuck. */
+static struct commit_list **global_lst;
+
+static int include_one_commit(const char *path, const unsigned char *sha1)
+{
+       struct commit *com = get_commit_reference(path, 0);
+       handle_one_commit(com, global_lst);
+       return 0;
+}
+
+static void handle_all(struct commit_list **lst)
+{
+       global_lst = lst;
+       for_each_ref(include_one_commit);
+       global_lst = NULL;
+}
 
 int main(int argc, char **argv)
 {
@@ -542,6 +559,10 @@ int main(int argc, char **argv)
                        bisect_list = 1;
                        continue;
                }
+               if (!strcmp(arg, "--all")) {
+                       handle_all(&list);
+                       continue;
+               }
                if (!strcmp(arg, "--objects")) {
                        tag_objects = 1;
                        tree_objects = 1;
index 507b531..41b9dae 100644 (file)
@@ -32,6 +32,7 @@ static int revs_count = 0;
 static int is_rev_argument(const char *arg)
 {
        static const char *rev_args[] = {
+               "--all",
                "--bisect",
                "--header",
                "--max-age=",
index da10742..83f5a35 100644 (file)
@@ -30,10 +30,18 @@ static void create_pack_file(void)
 
        if (!pid) {
                int i;
-               int args = nr_has + nr_needs + 5;
-               char **argv = xmalloc(args * sizeof(char *));
-               char *buf = xmalloc(args * 45);
-               char **p = argv;
+               int args;
+               char **argv;
+               char *buf;
+               char **p;
+
+               if (MAX_NEEDS <= nr_needs)
+                       args = nr_has + 10;
+               else
+                       args = nr_has + nr_needs + 5;
+               argv = xmalloc(args * sizeof(char *));
+               buf = xmalloc(args * 45);
+               p = argv;
 
                dup2(fd[1], 1);
                close(0);
@@ -41,10 +49,14 @@ static void create_pack_file(void)
                close(fd[1]);
                *p++ = "git-rev-list";
                *p++ = "--objects";
-               for (i = 0; i < nr_needs; i++) {
-                       *p++ = buf;
-                       memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
-                       buf += 41;
+               if (MAX_NEEDS <= nr_needs)
+                       *p++ = "--all";
+               else {
+                       for (i = 0; i < nr_needs; i++) {
+                               *p++ = buf;
+                               memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
+                               buf += 41;
+                       }
                }
                for (i = 0; i < nr_has; i++) {
                        *p++ = buf;
@@ -129,18 +141,24 @@ static int receive_needs(void)
 
        needs = 0;
        for (;;) {
+               unsigned char dummy[20], *sha1_buf;
                len = packet_read_line(0, line, sizeof(line));
                if (!len)
                        return needs;
 
-               /*
-                * This is purely theoretical right now: git-fetch-pack only
-                * ever asks for a single HEAD
-                */
-               if (needs >= MAX_NEEDS)
-                       die("I'm only doing a max of %d requests", MAX_NEEDS);
-               if (strncmp("want ", line, 5) || get_sha1_hex(line+5, needs_sha1[needs]))
-                       die("git-upload-pack: protocol error, expected to get sha, not '%s'", line);
+               sha1_buf = dummy;
+               if (needs == MAX_NEEDS) {
+                       fprintf(stderr,
+                               "warning: supporting only a max of %d requests. "
+                               "sending everything instead.\n",
+                               MAX_NEEDS);
+               }
+               else if (needs < MAX_NEEDS)
+                       sha1_buf = needs_sha1[needs];
+
+               if (strncmp("want ", line, 5) || get_sha1_hex(line+5, sha1_buf))
+                       die("git-upload-pack: protocol error, "
+                           "expected to get sha, not '%s'", line);
                needs++;
        }
 }