Merge branch 'eb/mail' into next
[gitweb.git] / fetch-pack.c
index 09738fee91d536aa095d256fe24c07741041b7c4..7d23a8071a9c2923b1f9bcf1d897e590e04f432b 100644 (file)
@@ -7,8 +7,9 @@
 static int keep_pack;
 static int quiet;
 static int verbose;
+static int fetch_all;
 static const char fetch_pack_usage[] =
-"git-fetch-pack [-q] [-v] [-k] [--thin] [--exec=upload-pack] [host:]directory <refs>...";
+"git-fetch-pack [--all] [-q] [-v] [-k] [--thin] [--exec=upload-pack] [host:]directory <refs>...";
 static const char *exec = "git-upload-pack";
 
 #define COMPLETE       (1U << 0)
@@ -17,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;
 
@@ -39,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;
@@ -82,7 +89,7 @@ static void mark_common(struct commit *commit,
   Get the next rev to send, ignoring the common.
 */
 
-static const unsigned char* get_rev()
+static const unsigned char* get_rev(void)
 {
        struct commit *commit = NULL;
 
@@ -133,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);
 
@@ -171,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;
 
@@ -199,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:
@@ -234,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);
@@ -261,21 +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)) ||
-                               !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)
@@ -298,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;
@@ -317,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)) {
@@ -376,7 +430,11 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
                goto all_done;
        }
        if (find_common(fd, sha1, ref) < 0)
-               fprintf(stderr, "warning: no common commits\n");
+               if (!keep_pack)
+                       /* When cloning, it is not unusual to have
+                        * no common commit.
+                        */
+                       fprintf(stderr, "warning: no common commits\n");
 
        if (keep_pack)
                status = receive_keep_pack(fd, "git-fetch-pack", quiet);
@@ -426,6 +484,10 @@ int main(int argc, char **argv)
                                use_thin_pack = 1;
                                continue;
                        }
+                       if (!strcmp("--all", arg)) {
+                               fetch_all = 1;
+                               continue;
+                       }
                        if (!strcmp("-v", arg)) {
                                verbose = 1;
                                continue;