Merge branch 'jc/pack'
[gitweb.git] / builtin-pack-objects.c
index a94ec789716969814e4f406b02ed88e4cdb2ce14..e64e3a03a0f0c81c45557fc1181d8af1945ad8d8 100644 (file)
@@ -26,9 +26,6 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
 struct object_entry {
        struct pack_idx_entry idx;
        unsigned long size;     /* uncompressed size */
-
-       unsigned int hash;      /* name hint hash */
-       unsigned int depth;     /* delta depth */
        struct packed_git *in_pack;     /* already in pack */
        off_t in_pack_offset;
        struct object_entry *delta;     /* delta base object */
@@ -38,6 +35,7 @@ struct object_entry {
                                             */
        void *delta_data;       /* cached delta (uncompressed) */
        unsigned long delta_size;       /* delta data size (uncompressed) */
+       unsigned int hash;      /* name hint hash */
        enum object_type type;
        enum object_type in_pack_type;  /* could be delta */
        unsigned char in_pack_header_size;
@@ -588,7 +586,7 @@ static off_t write_one(struct sha1file *f,
 static int open_object_dir_tmp(const char *path)
 {
     snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path);
-    return mkstemp(tmpname);
+    return xmkstemp(tmpname);
 }
 
 /* forward declaration for write_pack_file */
@@ -614,8 +612,6 @@ static void write_pack_file(void)
                        f = sha1fd(1, "<stdout>");
                } else {
                        int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
-                       if (fd < 0)
-                               die("unable to create %s: %s\n", tmpname, strerror(errno));
                        pack_tmp_name = xstrdup(tmpname);
                        f = sha1fd(fd, pack_tmp_name);
                }
@@ -983,6 +979,8 @@ static void add_pbase_object(struct tree_desc *tree,
        int cmp;
 
        while (tree_entry(tree,&entry)) {
+               if (S_ISGITLINK(entry.mode))
+                       continue;
                cmp = tree_entry_len(entry.path, entry.sha1) != cmplen ? 1 :
                      memcmp(name, entry.path, cmplen);
                if (cmp > 0)
@@ -1274,11 +1272,11 @@ struct unpacked {
        struct object_entry *entry;
        void *data;
        struct delta_index *index;
+       unsigned depth;
 };
 
-static int delta_cacheable(struct unpacked *trg, struct unpacked *src,
-                           unsigned long src_size, unsigned long trg_size,
-                           unsigned long delta_size)
+static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
+                          unsigned long delta_size)
 {
        if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size)
                return 0;
@@ -1332,7 +1330,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
                return 0;
 
        /* Let's not bust the allowed depth. */
-       if (src_entry->depth >= max_depth)
+       if (src->depth >= max_depth)
                return 0;
 
        /* Now some size filtering heuristics. */
@@ -1342,9 +1340,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
                ref_depth = 1;
        } else {
                max_size = trg_entry->delta_size;
-               ref_depth = trg_entry->depth;
+               ref_depth = trg->depth;
        }
-       max_size = max_size * (max_depth - src_entry->depth) /
+       max_size = max_size * (max_depth - src->depth) /
                                                (max_depth - ref_depth + 1);
        if (max_size == 0)
                return 0;
@@ -1358,6 +1356,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
        /* Load data if not already done */
        if (!trg->data) {
                trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
+               if (!trg->data)
+                       die("object %s cannot be read",
+                           sha1_to_hex(trg_entry->idx.sha1));
                if (sz != trg_size)
                        die("object %s inconsistent object length (%lu vs %lu)",
                            sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
@@ -1365,6 +1366,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
        }
        if (!src->data) {
                src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
+               if (!src->data)
+                       die("object %s cannot be read",
+                           sha1_to_hex(src_entry->idx.sha1));
                if (sz != src_size)
                        die("object %s inconsistent object length (%lu vs %lu)",
                            sha1_to_hex(src_entry->idx.sha1), sz, src_size);
@@ -1385,22 +1389,26 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
        if (!delta_buf)
                return 0;
 
-       if (trg_entry->delta_data) {
+       if (trg_entry->delta) {
                /* Prefer only shallower same-sized deltas. */
                if (delta_size == trg_entry->delta_size &&
-                   src_entry->depth + 1 >= trg_entry->depth) {
+                   src->depth + 1 >= trg->depth) {
                        free(delta_buf);
                        return 0;
                }
-               delta_cache_size -= trg_entry->delta_size;
-               free(trg_entry->delta_data);
        }
-       trg_entry->delta_data = 0;
+
        trg_entry->delta = src_entry;
        trg_entry->delta_size = delta_size;
-       trg_entry->depth = src_entry->depth + 1;
+       trg->depth = src->depth + 1;
 
-       if (delta_cacheable(src, trg, src_size, trg_size, delta_size)) {
+       if (trg_entry->delta_data) {
+               delta_cache_size -= trg_entry->delta_size;
+               free(trg_entry->delta_data);
+               trg_entry->delta_data = NULL;
+       }
+
+       if (delta_cacheable(src_size, trg_size, delta_size)) {
                trg_entry->delta_data = xrealloc(delta_buf, delta_size);
                delta_cache_size += trg_entry->delta_size;
        } else
@@ -1432,6 +1440,7 @@ static void free_unpacked(struct unpacked *n)
                window_memory_usage -= n->entry->size;
        }
        n->entry = NULL;
+       n->depth = 0;
 }
 
 static void find_deltas(struct object_entry **list, int window, int depth)
@@ -1451,7 +1460,7 @@ static void find_deltas(struct object_entry **list, int window, int depth)
        do {
                struct object_entry *entry = list[--i];
                struct unpacked *n = array + idx;
-               int j;
+               int j, best_base = -1;
 
                if (!entry->preferred_base)
                        processed++;
@@ -1496,6 +1505,7 @@ static void find_deltas(struct object_entry **list, int window, int depth)
 
                j = window;
                while (--j > 0) {
+                       int ret;
                        uint32_t other_idx = idx + j;
                        struct unpacked *m;
                        if (other_idx >= window)
@@ -1503,17 +1513,37 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                        m = array + other_idx;
                        if (!m->entry)
                                break;
-                       if (try_delta(n, m, max_depth) < 0)
+                       ret = try_delta(n, m, max_depth);
+                       if (ret < 0)
                                break;
+                       else if (ret > 0)
+                               best_base = other_idx;
                }
 
                /* if we made n a delta, and if n is already at max
                 * depth, leaving it in the window is pointless.  we
                 * should evict it first.
                 */
-               if (entry->delta && depth <= entry->depth)
+               if (entry->delta && depth <= n->depth)
                        continue;
 
+               /*
+                * Move the best delta base up in the window, after the
+                * currently deltified object, to keep it longer.  It will
+                * be the first base object to be attempted next.
+                */
+               if (entry->delta) {
+                       struct unpacked swap = array[best_base];
+                       int dist = (window + idx - best_base) % window;
+                       int dst = best_base;
+                       while (dist--) {
+                               int src = (dst + 1) % window;
+                               array[dst] = array[src];
+                               dst = src;
+                       }
+                       array[dst] = swap;
+               }
+
                next:
                idx++;
                if (count + 1 < window)