prepare_packed_git(): sort packs by age and localness.
[gitweb.git] / sha1_file.c
index 219a10f403e0c3aa2f8d60ecde5807cf51d1a659..5691448d732ffabc16d854001659870e503f665a 100644 (file)
@@ -739,6 +739,7 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
        p->windows = NULL;
        p->pack_fd = -1;
        p->pack_local = local;
+       p->mtime = st.st_mtime;
        if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
                hashcpy(p->sha1, sha1);
        return p;
@@ -823,6 +824,60 @@ static void prepare_packed_git_one(char *objdir, int local)
        closedir(dir);
 }
 
+static int sort_pack(const void *a_, const void *b_)
+{
+       struct packed_git *a = *((struct packed_git **)a_);
+       struct packed_git *b = *((struct packed_git **)b_);
+       int st;
+
+       /*
+        * Local packs tend to contain objects specific to our
+        * variant of the project than remote ones.  In addition,
+        * remote ones could be on a network mounted filesystem.
+        * Favor local ones for these reasons.
+        */
+       st = a->pack_local - b->pack_local;
+       if (st)
+               return -st;
+
+       /*
+        * Younger packs tend to contain more recent objects,
+        * and more recent objects tend to get accessed more
+        * often.
+        */
+       if (a->mtime < b->mtime)
+               return 1;
+       else if (a->mtime == b->mtime)
+               return 0;
+       return -1;
+}
+
+static void rearrange_packed_git(void)
+{
+       struct packed_git **ary, *p;
+       int i, n;
+
+       for (n = 0, p = packed_git; p; p = p->next)
+               n++;
+       if (n < 2)
+               return;
+
+       /* prepare an array of packed_git for easier sorting */
+       ary = xcalloc(n, sizeof(struct packed_git *));
+       for (n = 0, p = packed_git; p; p = p->next)
+               ary[n++] = p;
+
+       qsort(ary, n, sizeof(struct packed_git *), sort_pack);
+
+       /* link them back again */
+       for (i = 0; i < n - 1; i++)
+               ary[i]->next = ary[i + 1];
+       ary[n - 1]->next = NULL;
+       packed_git = ary[0];
+
+       free(ary);
+}
+
 static int prepare_packed_git_run_once = 0;
 void prepare_packed_git(void)
 {
@@ -837,6 +892,7 @@ void prepare_packed_git(void)
                prepare_packed_git_one(alt->base, 0);
                alt->name[-1] = '/';
        }
+       rearrange_packed_git();
        prepare_packed_git_run_once = 1;
 }
 
@@ -967,11 +1023,12 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
        return 0;
 }
 
-static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
+static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
 {
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmalloc(1+size);
        unsigned long n;
+       int status = Z_OK;
 
        n = stream->total_out - bytes;
        if (n > size)
@@ -981,12 +1038,22 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
        if (bytes < size) {
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
-               while (inflate(stream, Z_FINISH) == Z_OK)
-                       /* nothing */;
+               while (status == Z_OK)
+                       status = inflate(stream, Z_FINISH);
        }
        buf[size] = 0;
-       inflateEnd(stream);
-       return buf;
+       if ((status == Z_OK || status == Z_STREAM_END) && !stream->avail_in) {
+               inflateEnd(stream);
+               return buf;
+       }
+
+       if (status < 0)
+               error("corrupt loose object '%s'", sha1_to_hex(sha1));
+       else if (stream->avail_in)
+               error("garbage at end of loose object '%s'",
+                     sha1_to_hex(sha1));
+       free(buf);
+       return NULL;
 }
 
 /*
@@ -1040,7 +1107,7 @@ static int parse_sha1_header(const char *hdr, unsigned long *sizep)
        return *hdr ? -1 : type_from_string(type);
 }
 
-void * unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size)
+static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
 {
        int ret;
        z_stream stream;
@@ -1050,7 +1117,7 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type
        if (ret < Z_OK || (*type = parse_sha1_header(hdr, size)) < 0)
                return NULL;
 
-       return unpack_sha1_rest(&stream, hdr, *size);
+       return unpack_sha1_rest(&stream, hdr, *size, sha1);
 }
 
 static off_t get_delta_base(struct packed_git *p,
@@ -1571,7 +1638,7 @@ void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
                return buf;
        map = map_sha1_file(sha1, &mapsize);
        if (map) {
-               buf = unpack_sha1_file(map, mapsize, type, size);
+               buf = unpack_sha1_file(map, mapsize, type, size, sha1);
                munmap(map, mapsize);
                return buf;
        }