unpack-objects: fix --strict handling
authorJunio C Hamano <gitster@pobox.com>
Wed, 5 Mar 2008 07:46:51 +0000 (23:46 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Mar 2008 18:53:11 +0000 (10:53 -0800)
Earlier attempt (which was reverted) called added_object() (by the way,
the function should be renamed to resolve_dependents() --- it is called
when we have a complete object data, and is responsible to resolve pending
deltified objects that use this object as their delta base object) without
updating obj_list[nr].sha1 with the correct value.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-unpack-objects.c
t/t5300-pack-object.sh

index 9d2a8549509658d32f25a1bc6af0552dc8380988..fecf0be7796f34fbb06a6bbbe61e9e3d042f6780 100644 (file)
@@ -21,6 +21,11 @@ static unsigned int offset, len;
 static off_t consumed_bytes;
 static SHA_CTX ctx;
 
+/*
+ * When running under --strict mode, objects whose reachability are
+ * suspect are kept in core without getting written in the object
+ * store.
+ */
 struct obj_buffer {
        char *buffer;
        unsigned long size;
@@ -155,6 +160,10 @@ struct obj_info {
 static struct obj_info *obj_list;
 unsigned nr_objects;
 
+/*
+ * Called only from check_object() after it verified this object
+ * is Ok.
+ */
 static void write_cached_object(struct object *obj)
 {
        unsigned char sha1[20];
@@ -164,6 +173,11 @@ static void write_cached_object(struct object *obj)
        obj->flags |= FLAG_WRITTEN;
 }
 
+/*
+ * At the very end of the processing, write_rest() scans the objects
+ * that have reachability requirements and calls this function.
+ * Verify its reachability and validity recursively and write it out.
+ */
 static int check_object(struct object *obj, int type, void *data)
 {
        if (!obj)
@@ -202,19 +216,25 @@ static void write_rest(void)
 static void added_object(unsigned nr, enum object_type type,
                         void *data, unsigned long size);
 
+/*
+ * Write out nr-th object from the list, now we know the contents
+ * of it.  Under --strict, this buffers structured objects in-core,
+ * to be checked at the end.
+ */
 static void write_object(unsigned nr, enum object_type type,
                         void *buf, unsigned long size)
 {
-       added_object(nr, type, buf, size);
        if (!strict) {
                if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
                        die("failed to write object");
+               added_object(nr, type, buf, size);
                free(buf);
-               obj_list[nr].obj = 0;
+               obj_list[nr].obj = NULL;
        } else if (type == OBJ_BLOB) {
                struct blob *blob;
                if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
                        die("failed to write object");
+               added_object(nr, type, buf, size);
                free(buf);
 
                blob = lookup_blob(obj_list[nr].sha1);
@@ -222,15 +242,15 @@ static void write_object(unsigned nr, enum object_type type,
                        blob->object.flags |= FLAG_WRITTEN;
                else
                        die("invalid blob object");
-               obj_list[nr].obj = 0;
+               obj_list[nr].obj = NULL;
        } else {
                struct object *obj;
                int eaten;
                hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
+               added_object(nr, type, buf, size);
                obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
                if (!obj)
                        die("invalid %s", typename(type));
-               /* buf is stored via add_object_buffer and in obj, if its a tree or commit */
                add_object_buffer(obj, buf, size);
                obj->flags |= FLAG_OPEN;
                obj_list[nr].obj = obj;
@@ -253,6 +273,10 @@ static void resolve_delta(unsigned nr, enum object_type type,
        write_object(nr, type, result, result_size);
 }
 
+/*
+ * We now know the contents of an object (which is nr-th in the pack);
+ * resolve all the deltified objects that are based on it.
+ */
 static void added_object(unsigned nr, enum object_type type,
                         void *data, unsigned long size)
 {
@@ -284,13 +308,28 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
                free(buf);
 }
 
+static int resolve_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)
+               return 0;
+       obj_buffer = lookup_object_buffer(obj);
+       if (!obj_buffer)
+               return 0;
+       resolve_delta(nr, obj->type, obj_buffer->buffer,
+                     obj_buffer->size, delta_data, delta_size);
+       return 1;
+}
+
 static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                               unsigned nr)
 {
        void *delta_data, *base;
        unsigned long base_size;
        unsigned char base_sha1[20];
-       struct object *obj;
 
        if (type == OBJ_REF_DELTA) {
                hashcpy(base_sha1, fill(20));
@@ -300,7 +339,13 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                        free(delta_data);
                        return;
                }
-               if (!has_sha1_file(base_sha1)) {
+               if (has_sha1_file(base_sha1))
+                       ; /* Ok we have this one */
+               else if (resolve_against_held(nr, base_sha1,
+                                             delta_data, delta_size))
+                       return; /* we are done */
+               else {
+                       /* cannot resolve yet --- queue it */
                        hashcpy(obj_list[nr].sha1, null_sha1);
                        add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
                        return;
@@ -346,22 +391,18 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                        }
                }
                if (!base_found) {
-                       /* The delta base object is itself a delta that
-                          has not been resolved yet. */
+                       /*
+                        * The delta base object is itself a delta that
+                        * has not been resolved yet.
+                        */
                        hashcpy(obj_list[nr].sha1, null_sha1);
                        add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
                        return;
                }
        }
 
-       obj = lookup_object(base_sha1);
-       if (obj) {
-               struct obj_buffer *obj_buf = lookup_object_buffer(obj);
-               if (obj_buf) {
-                       resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size);
-                       return;
-               }
-       }
+       if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
+               return;
 
        base = read_sha1_file(base_sha1, &type, &base_size);
        if (!base) {
index ffc638ab2c7ac7bbf73db7e29bb3ec3c998e1594..7647f2142bb861db8f989c2c24e81721a1d079de 100755 (executable)
@@ -274,7 +274,7 @@ test_expect_success \
      packname_4=$(git pack-objects test-4 <obj-list) &&
      test 3 = $(ls test-4-*.pack | wc -l)'
 
-test_expect_failure 'unpacking with --strict' '
+test_expect_success 'unpacking with --strict' '
 
        git config --unset pack.packsizelimit &&
        for j in a b c d e f g