Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- builtin/unpack-objects.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 2 deletions(-) diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 044a087..9fd5640 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -12,6 +12,7 @@ #include "decorate.h" #include "packv4-parse.h" #include "fsck.h" +#include "varint.h" static int dry_run, quiet, recover, has_errors, strict; static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict] < pack-file"; @@ -148,6 +149,27 @@ static const unsigned char *read_sha1ref(void) return sha1_table + index * 20; } +static void check_against_sha1table(const unsigned char *sha1) +{ + const unsigned char *found; + if (!packv4) + return; + + found = bsearch(sha1, sha1_table, nr_objects, 20, + (int (*)(const void *, const void *))hashcmp); + if (!found) + die(_("object %s not found in SHA-1 table"), + sha1_to_hex(sha1)); +} + +static const unsigned char *read_sha1table_ref(void) +{ + const unsigned char *sha1 = read_sha1ref(); + if (sha1 < sha1_table || sha1 >= sha1_table + nr_objects * 20) + check_against_sha1table(sha1); + return sha1; +} + static const unsigned char *read_dictref(struct packv4_dict *dict) { unsigned int index = read_varint(); @@ -327,6 +349,84 @@ static void write_object(unsigned nr, enum object_type type, } } +static void resolve_tree_v4(unsigned long nr_obj, + const void *tree, + unsigned long tree_len, + const unsigned char *base_sha1, + const void *base, + unsigned long base_size) +{ + int nr; + struct strbuf sb = STRBUF_INIT; + const unsigned char *p = tree; + const unsigned char *end = p + tree_len; + + nr = decode_varint(&p); + while (nr > 0 && p < end) { + 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 (!base_sha1) + die("we are not supposed to copy from another tree!"); + if (copy_count & 1) { /* first delta */ + unsigned int id = decode_varint(&p); + const unsigned char *last_base; + if (!id) { + last_base = p; + p += 20; + } else + last_base = sha1_table + (id - 1) * 20; + if (hashcmp(last_base, base_sha1)) + die("bad base tree in resolve_tree_v4"); + } + + copy_count >>= 1; + nr -= copy_count; + + init_tree_desc(&desc, base, base_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 != end) + die(_("bad delta tree")); + if (!dry_run) + write_object(nr_obj, OBJ_TREE, sb.buf, sb.len); + else + strbuf_release(&sb); +} + static void resolve_delta(unsigned nr, enum object_type type, void *base, unsigned long base_size, void *delta, unsigned long delta_size) @@ -358,8 +458,13 @@ static void added_object(unsigned nr, enum object_type type, info->base_offset == obj_list[nr].offset) { *p = info->next; p = &delta_list; - resolve_delta(info->nr, type, data, size, - info->delta, info->size); + if (type == OBJ_TREE && packv4) + resolve_tree_v4(info->nr, info->delta, + info->size, info->base_sha1, + data, size); + else + resolve_delta(info->nr, type, data, size, + info->delta, info->size); free(info); continue; } @@ -493,6 +598,85 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, free(base); } +static int resolve_tree_against_held(unsigned nr, const unsigned char *base, + void *delta_data, unsigned long delta_size) +{ + struct object *obj; + struct obj_buffer *obj_buffer; + obj = lookup_object(base); + if (!obj || obj->type != OBJ_TREE) + return 0; + obj_buffer = lookup_object_buffer(obj); + if (!obj_buffer) + return 0; + resolve_tree_v4(nr, delta_data, delta_size, + base, obj_buffer->buffer, obj_buffer->size); + return 1; +} + +static void unpack_tree_v4(unsigned long size, unsigned long nr_obj) +{ + unsigned int nr; + const unsigned char *last_base = NULL; + + copy_back_buffer(1); + strbuf_reset(&back_buffer); + nr = read_varint(); + while (nr) { + unsigned int copy_start_or_path = read_varint(); + if (copy_start_or_path & 1) { /* copy_start */ + unsigned int copy_count = read_varint(); + if (copy_count & 1) { /* first delta */ + const unsigned char *old_base = last_base; + last_base = read_sha1table_ref(); + if (old_base && hashcmp(last_base, old_base)) + die("multi-base trees are not supported"); + } else if (!last_base) + die("missing delta base unpack_tree_v4 at %lu", + (unsigned long)consumed_bytes); + copy_count >>= 1; + if (!copy_count || copy_count > nr) + die("bad copy count index in unpack_tree_v4 at %lu", + (unsigned long)consumed_bytes); + nr -= copy_count; + } else { /* path */ + unsigned int path_idx = copy_start_or_path >> 1; + if (path_idx >= path_dict->nb_entries) + die("bad path index in unpack_tree_v4 at %lu", + (unsigned long)consumed_bytes); + read_sha1ref(); + nr--; + } + } + copy_back_buffer(0); + + if (last_base) { + if (has_sha1_file(last_base)) { + enum object_type type; + unsigned long base_size; + void *base = read_sha1_file(last_base, &type, &base_size); + if (type != OBJ_TREE) { + die("base tree %s is not a tree", sha1_to_hex(last_base)); + last_base = NULL; + } + resolve_tree_v4(nr_obj, back_buffer.buf, back_buffer.len, + last_base, base, base_size); + free(base); + } else if (resolve_tree_against_held(nr_obj, last_base, + back_buffer.buf, back_buffer.len)) + ; /* resolved */ + else { + unsigned long delta_size = back_buffer.len; + char *delta = strbuf_detach(&back_buffer, NULL); + /* cannot resolve yet --- queue it */ + hashcpy(obj_list[nr].sha1, null_sha1); + add_delta_to_list(nr, last_base, 0, delta, delta_size); + } + } else + resolve_tree_v4(nr_obj, back_buffer.buf, back_buffer.len, NULL, NULL, 0); + strbuf_release(&back_buffer); +} + static void unpack_commit_v4(unsigned long size, unsigned long nr) { unsigned int nb_parents; @@ -588,6 +772,9 @@ static int unpack_one(unsigned nr) case OBJ_PV4_COMMIT: unpack_commit_v4(size, nr); break; + case OBJ_PV4_TREE: + unpack_tree_v4(size, nr); + break; default: error("bad object type %d", type); has_errors = 1; -- 1.8.2.82.gc24b958 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html