Merge branch 'pb/config' into next
[gitweb.git] / fetch-pack.c
index a3bcad016f52c09897c836112e03eec859a5eb1e..7d23a8071a9c2923b1f9bcf1d897e590e04f432b 100644 (file)
@@ -18,6 +18,12 @@ static const char *exec = "git-upload-pack";
 #define SEEN           (1U << 3)
 #define POPPED         (1U << 4)
 
+/*
+ * After sending this many "have"s if we do not get any new ACK , we
+ * give up traversing our history.
+ */
+#define MAX_IN_VAIN 256
+
 static struct commit_list *rev_list = NULL;
 static int non_common_revs = 0, multi_ack = 0, use_thin_pack = 0;
 
@@ -40,7 +46,7 @@ static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
 {
        struct object *o = deref_tag(parse_object(sha1), path, 0);
 
-       if (o && o->type == commit_type)
+       if (o && o->type == TYPE_COMMIT)
                rev_list_push((struct commit *)o, SEEN);
 
        return 0;
@@ -134,6 +140,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        int fetching;
        int count = 0, flushes = 0, retval;
        const unsigned char *sha1;
+       unsigned in_vain = 0;
+       int got_continue = 0;
 
        for_each_ref(rev_list_insert_ref);
 
@@ -172,6 +180,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
                if (verbose)
                        fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
+               in_vain++;
                if (!(31 & ++count)) {
                        int ack;
 
@@ -200,9 +209,16 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                                                lookup_commit(result_sha1);
                                        mark_common(commit, 0, 1);
                                        retval = 0;
+                                       in_vain = 0;
+                                       got_continue = 1;
                                }
                        } while (ack);
                        flushes--;
+                       if (got_continue && MAX_IN_VAIN < in_vain) {
+                               if (verbose)
+                                       fprintf(stderr, "giving up\n");
+                               break; /* give up */
+                       }
                }
        }
 done:
@@ -235,14 +251,14 @@ static int mark_complete(const char *path, const unsigned char *sha1)
 {
        struct object *o = parse_object(sha1);
 
-       while (o && o->type == tag_type) {
+       while (o && o->type == TYPE_TAG) {
                struct tag *t = (struct tag *) o;
                if (!t->tagged)
                        break; /* broken repository */
                o->flags |= COMPLETE;
                o = parse_object(t->tagged->sha1);
        }
-       if (o && o->type == commit_type) {
+       if (o && o->type == TYPE_COMMIT) {
                struct commit *commit = (struct commit *)o;
                commit->object.flags |= COMPLETE;
                insert_by_date(commit, &complete);
@@ -262,22 +278,58 @@ static void mark_recent_complete_commits(unsigned long cutoff)
 
 static void filter_refs(struct ref **refs, int nr_match, char **match)
 {
-       struct ref *prev, *current, *next;
-
-       for (prev = NULL, current = *refs; current; current = next) {
-               next = current->next;
-               if ((!memcmp(current->name, "refs/", 5) &&
-                    check_ref_format(current->name + 5)) ||
-                   (!fetch_all &&
-                    !path_match(current->name, nr_match, match))) {
-                       if (prev == NULL)
-                               *refs = next;
-                       else
-                               prev->next = next;
-                       free(current);
-               } else
-                       prev = current;
+       struct ref **return_refs;
+       struct ref *newlist = NULL;
+       struct ref **newtail = &newlist;
+       struct ref *ref, *next;
+       struct ref *fastarray[32];
+
+       if (nr_match && !fetch_all) {
+               if (ARRAY_SIZE(fastarray) < nr_match)
+                       return_refs = xcalloc(nr_match, sizeof(struct ref *));
+               else {
+                       return_refs = fastarray;
+                       memset(return_refs, 0, sizeof(struct ref *) * nr_match);
+               }
+       }
+       else
+               return_refs = NULL;
+
+       for (ref = *refs; ref; ref = next) {
+               next = ref->next;
+               if (!memcmp(ref->name, "refs/", 5) &&
+                   check_ref_format(ref->name + 5))
+                       ; /* trash */
+               else if (fetch_all) {
+                       *newtail = ref;
+                       ref->next = NULL;
+                       newtail = &ref->next;
+                       continue;
+               }
+               else {
+                       int order = path_match(ref->name, nr_match, match);
+                       if (order) {
+                               return_refs[order-1] = ref;
+                               continue; /* we will link it later */
+                       }
+               }
+               free(ref);
+       }
+
+       if (!fetch_all) {
+               int i;
+               for (i = 0; i < nr_match; i++) {
+                       ref = return_refs[i];
+                       if (ref) {
+                               *newtail = ref;
+                               ref->next = NULL;
+                               newtail = &ref->next;
+                       }
+               }
+               if (return_refs != fastarray)
+                       free(return_refs);
        }
+       *refs = newlist;
 }
 
 static int everything_local(struct ref **refs, int nr_match, char **match)
@@ -300,7 +352,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                 * in sync with the other side at some time after
                 * that (it is OK if we guess wrong here).
                 */
-               if (o->type == commit_type) {
+               if (o->type == TYPE_COMMIT) {
                        struct commit *commit = (struct commit *)o;
                        if (!cutoff || cutoff < commit->date)
                                cutoff = commit->date;
@@ -319,7 +371,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                struct object *o = deref_tag(lookup_object(ref->old_sha1),
                                             NULL, 0);
 
-               if (!o || o->type != commit_type || !(o->flags & COMPLETE))
+               if (!o || o->type != TYPE_COMMIT || !(o->flags & COMPLETE))
                        continue;
 
                if (!(o->flags & SEEN)) {