This is the most common case for delta trees. In fact it's the only
kind that's produced by packv4-create. It fits well in the way
index-pack resolves deltas and benefits from threading (the set of
objects depending on this base does not overlap with the set of
objects depending on another base)

Multi-base trees will be probably processed differently.
---
 builtin/index-pack.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 179 insertions(+), 16 deletions(-)

diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index ce06473..88340b5 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -12,6 +12,8 @@
 #include "streaming.h"
 #include "thread-utils.h"
 #include "packv4-parse.h"
+#include "varint.h"
+#include "tree-walk.h"
 
 static const char index_pack_usage[] =
 "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] 
[--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -38,8 +40,8 @@ struct base_data {
        struct object_entry *obj;
        void *data;
        unsigned long size;
-       int ref_first, ref_last;
-       int ofs_first, ofs_last;
+       int ref_first, ref_last, tree_first;
+       int ofs_first, ofs_last, tree_last;
 };
 
 #if !defined(NO_PTHREADS) && defined(NO_THREAD_SAFE_PREAD)
@@ -430,6 +432,7 @@ static struct base_data *alloc_base_data(void)
        memset(base, 0, sizeof(*base));
        base->ref_last = -1;
        base->ofs_last = -1;
+       base->tree_last = -1;
        return base;
 }
 
@@ -670,6 +673,8 @@ static void *unpack_tree_v4(struct object_entry *obj,
        }
 
        if (last_base) {
+               if (nr_deltas - delta_start > 1)
+                       die("sorry guys, multi-base trees are not supported 
yet");
                strbuf_release(&sb);
                return NULL;
        } else {
@@ -800,6 +805,84 @@ static void *unpack_raw_entry(struct object_entry *obj,
 }
 
 /*
+ * Some checks are skipped because they are already done by
+ * unpack_tree_v4() in the first pass.
+ */
+static void *patch_one_base_tree(const struct object_entry *src,
+                                const unsigned char *src_buf,
+                                const unsigned char *delta_buf,
+                                unsigned long delta_size,
+                                unsigned long *dst_size)
+{
+       int nr;
+       const unsigned char *last_base = NULL;
+       struct strbuf sb = STRBUF_INIT;
+       const unsigned char *p = delta_buf;
+
+       nr = decode_varint(&p);
+       while (nr > 0 && p < delta_buf + delta_size) {
+               unsigned int copy_start_or_path = decode_varint(&p);
+               if (copy_start_or_path & 1) { /* copy_start */
+                       struct tree_desc desc;
+                       struct name_entry entry;
+                       unsigned int copy_count = decode_varint(&p);
+                       unsigned int copy_start = copy_start_or_path >> 1;
+                       if (!src)
+                               die("we are not supposed to copy from another 
tree!");
+                       if (copy_count & 1) { /* first delta */
+                               unsigned int id = decode_varint(&p);
+                               if (!id) {
+                                       last_base = p;
+                                       p += 20;
+                               } else
+                                       last_base = sha1_table + (id - 1) * 20;
+                               if (hashcmp(last_base, src->idx.sha1))
+                                       die(_("bad tree base in 
patch_one_base_tree"));
+                       }
+
+                       copy_count >>= 1;
+                       nr -= copy_count;
+
+                       init_tree_desc(&desc, src_buf, src->size);
+                       while (tree_entry(&desc, &entry)) {
+                               if (copy_start)
+                                       copy_start--;
+                               else if (copy_count) {
+                                       strbuf_addf(&sb, "%o %s%c",
+                                                   entry.mode, entry.path, 
'\0');
+                                       strbuf_add(&sb, entry.sha1, 20);
+                                       copy_count--;
+                               } else
+                                       break;
+                       }
+               } else {        /* path */
+                       unsigned int path_idx = copy_start_or_path >> 1;
+                       const unsigned char *path;
+                       unsigned mode;
+                       unsigned int id;
+                       const unsigned char *entry_sha1;
+
+                       id = decode_varint(&p);
+                       if (!id) {
+                               entry_sha1 = p;
+                               p += 20;
+                       } else
+                               entry_sha1 = sha1_table + (id - 1) * 20;
+                       nr--;
+
+                       path = path_dict->data + path_dict->offsets[path_idx];
+                       mode = (path[0] << 8) | path[1];
+                       strbuf_addf(&sb, "%o %s%c", mode, path+2, '\0');
+                       strbuf_add(&sb, entry_sha1, 20);
+               }
+       }
+       if (nr != 0 || p != delta_buf + delta_size)
+               die(_("bad delta tree"));
+       *dst_size = sb.len;
+       return sb.buf;
+}
+
+/*
  * Unpack entry data in the second pass when the pack is already
  * stored on disk. consume call back is used for large-blob case. Must
  * be thread safe.
@@ -865,8 +948,33 @@ static void *unpack_data(struct object_entry *obj,
        return data;
 }
 
+static void *get_tree_v4_from_pack(struct object_entry *obj,
+                                  unsigned long *len_p)
+{
+       off_t from = obj[0].idx.offset + obj[0].hdr_size;
+       unsigned long len = obj[1].idx.offset - from;
+       unsigned char *data;
+       ssize_t n;
+
+       data = xmalloc(len);
+       n = pread(pack_fd, data, len, from);
+       if (n < 0)
+               die_errno(_("cannot pread pack file"));
+       if (!n)
+               die(Q_("premature end of pack file, %lu byte missing",
+                      "premature end of pack file, %lu bytes missing",
+                      len),
+                   len);
+       if (len_p)
+               *len_p = len;
+       return data;
+}
+
 static void *get_data_from_pack(struct object_entry *obj)
 {
+       if (obj->type == OBJ_PV4_COMMIT || obj->type == OBJ_PV4_TREE)
+               die("BUG: unsupported code path");
+
        return unpack_data(obj, NULL, NULL);
 }
 
@@ -1093,14 +1201,25 @@ static void *get_base_data(struct base_data *c)
                struct object_entry *obj = c->obj;
                struct base_data **delta = NULL;
                int delta_nr = 0, delta_alloc = 0;
+               unsigned long size, len;
 
-               while (is_delta_type(c->obj->type) && !c->data) {
+               while ((is_delta_type(c->obj->type) ||
+                       (c->base && c->obj->type == OBJ_PV4_TREE)) &&
+                      !c->data) {
                        ALLOC_GROW(delta, delta_nr + 1, delta_alloc);
                        delta[delta_nr++] = c;
                        c = c->base;
                }
                if (!delta_nr) {
-                       c->data = get_data_from_pack(obj);
+                       if (c->obj->type == OBJ_PV4_TREE) {
+                               void *tree_v4 = get_tree_v4_from_pack(obj, 
&len);
+                               c->data = patch_one_base_tree(NULL, NULL,
+                                                             tree_v4, len, 
&size);
+                               if (size != obj->size)
+                                       die("size mismatch");
+                               free(tree_v4);
+                       } else
+                               c->data = get_data_from_pack(obj);
                        c->size = obj->size;
                        get_thread_data()->base_cache_used += c->size;
                        prune_base_data(c);
@@ -1110,11 +1229,18 @@ static void *get_base_data(struct base_data *c)
                        c = delta[delta_nr - 1];
                        obj = c->obj;
                        base = get_base_data(c->base);
-                       raw = get_data_from_pack(obj);
-                       c->data = patch_delta(
-                               base, c->base->size,
-                               raw, obj->size,
-                               &c->size);
+                       if (c->obj->type == OBJ_PV4_TREE) {
+                               raw = get_tree_v4_from_pack(obj, &len);
+                               c->data = patch_one_base_tree(c->base->obj, 
base,
+                                                             raw, len, &size);
+                               if (size != obj->size)
+                                       die("size mismatch");
+                       } else {
+                               raw = get_data_from_pack(obj);
+                               c->data = patch_delta(base, c->base->size,
+                                                     raw, obj->size,
+                                                     &c->size);
+                       }
                        free(raw);
                        if (!c->data)
                                bad_object(obj->idx.offset, _("failed to apply 
delta"));
@@ -1130,6 +1256,8 @@ static void resolve_delta(struct object_entry *delta_obj,
                          struct base_data *base, struct base_data *result)
 {
        void *base_data, *delta_data;
+       int tree_v4 = delta_obj->type == OBJ_PV4_TREE;
+       unsigned long tree_size;
 
        delta_obj->real_type = base->obj->real_type;
        if (show_stat) {
@@ -1140,10 +1268,18 @@ static void resolve_delta(struct object_entry 
*delta_obj,
                deepest_delta_unlock();
        }
        delta_obj->base_object_no = base->obj - objects;
-       delta_data = get_data_from_pack(delta_obj);
+       if (tree_v4)
+               delta_data = get_tree_v4_from_pack(delta_obj, &tree_size);
+       else
+               delta_data = get_data_from_pack(delta_obj);
        base_data = get_base_data(base);
        result->obj = delta_obj;
-       result->data = patch_delta(base_data, base->size,
+       if (tree_v4)
+               result->data = patch_one_base_tree(base->obj, base_data,
+                                                  delta_data, tree_size,
+                                                  &result->size);
+       else
+               result->data = patch_delta(base_data, base->size,
                                   delta_data, delta_obj->size, &result->size);
        free(delta_data);
        if (!result->data)
@@ -1166,7 +1302,8 @@ static void resolve_delta(struct object_entry *delta_obj,
 static struct base_data *find_unresolved_deltas_1(struct base_data *base,
                                                  struct base_data *prev_base)
 {
-       if (base->ref_last == -1 && base->ofs_last == -1) {
+       if (base->ref_last == -1 && base->ofs_last == -1 &&
+           base->tree_last == -1) {
                union delta_base base_spec;
 
                hashcpy(base_spec.sha1, base->obj->idx.sha1);
@@ -1179,9 +1316,15 @@ static struct base_data *find_unresolved_deltas_1(struct 
base_data *base,
                        find_delta_children(&base_spec,
                                            &base->ofs_first, &base->ofs_last,
                                            OBJ_OFS_DELTA);
+               } else {
+                       hashcpy(base_spec.sha1, base->obj->idx.sha1);
+                       find_delta_children(&base_spec,
+                                           &base->tree_first, &base->tree_last,
+                                           OBJ_PV4_TREE);
                }
 
-               if (base->ref_last == -1 && base->ofs_last == -1) {
+               if (base->ref_last == -1 && base->ofs_last == -1 &&
+                   base->tree_last == -1) {
                        free(base->data);
                        return NULL;
                }
@@ -1215,6 +1358,25 @@ static struct base_data *find_unresolved_deltas_1(struct 
base_data *base,
                return result;
        }
 
+       while (base->tree_first <= base->tree_last) {
+               struct object_entry *child = objects + 
deltas[base->tree_first].obj_no;
+               struct base_data *result;
+
+               assert(child->type == OBJ_PV4_TREE);
+               if (child->nr_bases > 1) {
+                       /* maybe resolved in the third pass or something */
+                       base->tree_first++;
+                       continue;
+               }
+               result = alloc_base_data();
+               resolve_delta(child, base, result);
+               if (base->tree_first == base->tree_last)
+                       free_base_data(base);
+
+               base->tree_first++;
+               return result;
+       }
+
        unlink_base_data(base);
        return NULL;
 }
@@ -1273,7 +1435,8 @@ static void *threaded_second_pass(void *data)
                counter_unlock();
                work_lock();
                while (nr_dispatched < nr_objects &&
-                      is_delta_type(objects[nr_dispatched].type))
+                      (is_delta_type(objects[nr_dispatched].type) ||
+                       is_delta_tree(objects + nr_dispatched)))
                        nr_dispatched++;
                if (nr_dispatched >= nr_objects) {
                        work_unlock();
@@ -1427,7 +1590,7 @@ static void resolve_deltas(void)
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
 
-               if (is_delta_type(obj->type))
+               if (is_delta_type(obj->type) || is_delta_tree(obj))
                        continue;
                resolve_base(obj);
                display_progress(progress, nr_resolved_deltas);
@@ -1972,7 +2135,7 @@ int cmd_index_pack(int argc, const char **argv, const 
char *prefix)
                show_pack_info(stat_only);
 
        if (packv4)
-               die("we're not there yet");
+               opts.version = 3;
 
        idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
        for (i = 0; i < nr_objects; i++)
-- 
1.8.2.83.gc99314b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to