git-svn: fetch tracks initial change with --follow-parent
[gitweb.git] / builtin-pack-objects.c
index 41e1e74533d64e999ec1e564e15b8dea29e4c9c6..b5ed9ce2c89daab7a69d399a6599e203dc04971a 100644 (file)
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
-#include <sys/time.h>
-#include <signal.h>
 
-static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
+static const char pack_usage[] = "\
+git-pack-objects [{ -q | --progress | --all-progress }] \n\
+       [--local] [--incremental] [--window=N] [--depth=N] \n\
+       [--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\
+       [--revs [--unpacked | --all]*] [--reflog] [--stdout | base-name] \n\
+       [<ref-list | <object-list]";
 
 struct object_entry {
        unsigned char sha1[20];
@@ -273,7 +276,52 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
  * we are going to reuse the existing object data as is.  make
  * sure it is not corrupt.
  */
-static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect)
+static int check_pack_inflate(struct packed_git *p,
+               struct pack_window **w_curs,
+               unsigned long offset,
+               unsigned long len,
+               unsigned long expect)
+{
+       z_stream stream;
+       unsigned char fakebuf[4096], *in;
+       int st;
+
+       memset(&stream, 0, sizeof(stream));
+       inflateInit(&stream);
+       do {
+               in = use_pack(p, w_curs, offset, &stream.avail_in);
+               stream.next_in = in;
+               stream.next_out = fakebuf;
+               stream.avail_out = sizeof(fakebuf);
+               st = inflate(&stream, Z_FINISH);
+               offset += stream.next_in - in;
+       } while (st == Z_OK || st == Z_BUF_ERROR);
+       inflateEnd(&stream);
+       return (st == Z_STREAM_END &&
+               stream.total_out == expect &&
+               stream.total_in == len) ? 0 : -1;
+}
+
+static void copy_pack_data(struct sha1file *f,
+               struct packed_git *p,
+               struct pack_window **w_curs,
+               unsigned long offset,
+               unsigned long len)
+{
+       unsigned char *in;
+       unsigned int avail;
+
+       while (len) {
+               in = use_pack(p, w_curs, offset, &avail);
+               if (avail > len)
+                       avail = len;
+               sha1write(f, in, avail);
+               offset += avail;
+               len -= avail;
+       }
+}
+
+static int check_loose_inflate(unsigned char *data, unsigned long len, unsigned long expect)
 {
        z_stream stream;
        unsigned char fakebuf[4096];
@@ -320,7 +368,7 @@ static int revalidate_loose_object(struct object_entry *entry,
                return -1;
        map += used;
        mapsize -= used;
-       return check_inflate(map, mapsize, size);
+       return check_loose_inflate(map, mapsize, size);
 }
 
 static unsigned long write_object(struct sha1file *f,
@@ -413,6 +461,8 @@ static unsigned long write_object(struct sha1file *f,
        }
        else {
                struct packed_git *p = entry->in_pack;
+               struct pack_window *w_curs = NULL;
+               unsigned long offset;
 
                if (entry->delta) {
                        obj_type = (allow_ofs_delta && entry->delta->offset) ?
@@ -434,16 +484,14 @@ static unsigned long write_object(struct sha1file *f,
                        hdrlen += 20;
                }
 
-               use_packed_git(p);
-               buf = (char *) p->pack_base
-                       + entry->in_pack_offset
-                       + entry->in_pack_header_size;
+               offset = entry->in_pack_offset + entry->in_pack_header_size;
                datalen = find_packed_object_size(p, entry->in_pack_offset)
                                - entry->in_pack_header_size;
-               if (!pack_to_stdout && check_inflate(buf, datalen, entry->size))
+               if (!pack_to_stdout && check_pack_inflate(p, &w_curs,
+                               offset, datalen, entry->size))
                        die("corrupt delta in pack %s", sha1_to_hex(entry->sha1));
-               sha1write(f, buf, datalen);
-               unuse_packed_git(p);
+               copy_pack_data(f, p, &w_curs, offset, datalen);
+               unuse_pack(&w_curs);
                reused++;
        }
        if (entry->delta)
@@ -475,15 +523,15 @@ static void write_pack_file(void)
        unsigned long offset;
        struct pack_header hdr;
        unsigned last_percent = 999;
-       int do_progress = 0;
+       int do_progress = progress;
 
-       if (!base_name)
+       if (!base_name) {
                f = sha1fd(1, "<stdout>");
-       else {
+               do_progress >>= 1;
+       }
+       else
                f = sha1create("%s-%s.%s", base_name,
                               sha1_to_hex(object_list_sha1), "pack");
-               do_progress = progress;
-       }
        if (do_progress)
                fprintf(stderr, "Writing %d objects.\n", nr_result);
 
@@ -509,6 +557,8 @@ static void write_pack_file(void)
        if (do_progress)
                fputc('\n', stderr);
  done:
+       if (written != nr_result)
+               die("wrote %d objects while expecting %d", written, nr_result);
        sha1close(f, pack_file_sha1, 1);
 }
 
@@ -519,7 +569,7 @@ static void write_index_file(void)
                                        sha1_to_hex(object_list_sha1), "idx");
        struct object_entry **list = sorted_by_sha;
        struct object_entry **last = list + nr_result;
-       unsigned int array[256];
+       uint32_t array[256];
 
        /*
         * Write the first-level table (the list is sorted,
@@ -537,7 +587,7 @@ static void write_index_file(void)
                array[i] = htonl(next - sorted_by_sha);
                list = next;
        }
-       sha1write(f, array, 256 * sizeof(int));
+       sha1write(f, array, 256 * 4);
 
        /*
         * Write the actual SHA1 entries..
@@ -545,7 +595,7 @@ static void write_index_file(void)
        list = sorted_by_sha;
        for (i = 0; i < nr_result; i++) {
                struct object_entry *entry = *list++;
-               unsigned int offset = htonl(entry->offset);
+               uint32_t offset = htonl(entry->offset);
                sha1write(f, &offset, 4);
                sha1write(f, entry->sha1, 20);
        }
@@ -932,22 +982,19 @@ static void check_object(struct object_entry *entry)
 
        if (entry->in_pack && !entry->preferred_base) {
                struct packed_git *p = entry->in_pack;
+               struct pack_window *w_curs = NULL;
                unsigned long left = p->pack_size - entry->in_pack_offset;
                unsigned long size, used;
                unsigned char *buf;
                struct object_entry *base_entry = NULL;
 
-               use_packed_git(p);
-               buf = p->pack_base;
-               buf += entry->in_pack_offset;
+               buf = use_pack(p, &w_curs, entry->in_pack_offset, NULL);
 
                /* We want in_pack_type even if we do not reuse delta.
                 * There is no point not reusing non-delta representations.
                 */
                used = unpack_object_header_gently(buf, left,
                                                   &entry->in_pack_type, &size);
-               if (!used || left - used <= 20)
-                       die("corrupt pack for %s", sha1_to_hex(entry->sha1));
 
                /* Check if it is delta, and the base is also an object
                 * we are going to pack.  If so we will reuse the existing
@@ -956,21 +1003,26 @@ static void check_object(struct object_entry *entry)
                if (!no_reuse_delta) {
                        unsigned char c, *base_name;
                        unsigned long ofs;
+                       unsigned long used_0;
                        /* there is at least 20 bytes left in the pack */
                        switch (entry->in_pack_type) {
                        case OBJ_REF_DELTA:
-                               base_name = buf + used;
+                               base_name = use_pack(p, &w_curs,
+                                       entry->in_pack_offset + used, NULL);
                                used += 20;
                                break;
                        case OBJ_OFS_DELTA:
-                               c = buf[used++];
+                               buf = use_pack(p, &w_curs,
+                                       entry->in_pack_offset + used, NULL);
+                               used_0 = 0;
+                               c = buf[used_0++];
                                ofs = c & 127;
                                while (c & 128) {
                                        ofs += 1;
                                        if (!ofs || ofs & ~(~0UL >> 7))
                                                die("delta base offset overflow in pack for %s",
                                                    sha1_to_hex(entry->sha1));
-                                       c = buf[used++];
+                                       c = buf[used_0++];
                                        ofs = (ofs << 7) + (c & 127);
                                }
                                if (ofs >= entry->in_pack_offset)
@@ -978,6 +1030,7 @@ static void check_object(struct object_entry *entry)
                                            sha1_to_hex(entry->sha1));
                                ofs = entry->in_pack_offset - ofs;
                                base_name = find_packed_object_name(p, ofs);
+                               used += used_0;
                                break;
                        default:
                                base_name = NULL;
@@ -985,7 +1038,7 @@ static void check_object(struct object_entry *entry)
                        if (base_name)
                                base_entry = locate_object_entry(base_name);
                }
-               unuse_packed_git(p);
+               unuse_pack(&w_curs);
                entry->in_pack_header_size = used;
 
                if (base_entry) {
@@ -1171,7 +1224,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
         * on an earlier try, but only when reusing delta data.
         */
        if (!no_reuse_delta && trg_entry->in_pack &&
-           trg_entry->in_pack == src_entry->in_pack)
+           trg_entry->in_pack == src_entry->in_pack &&
+           trg_entry->in_pack_type != OBJ_REF_DELTA &&
+           trg_entry->in_pack_type != OBJ_OFS_DELTA)
                return 0;
 
        /*
@@ -1520,22 +1575,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        local = 1;
                        continue;
                }
-               if (!strcmp("--progress", arg)) {
-                       progress = 1;
-                       continue;
-               }
                if (!strcmp("--incremental", arg)) {
                        incremental = 1;
                        continue;
                }
-               if (!strncmp("--window=", arg, 9)) {
+               if (!prefixcmp(arg, "--window=")) {
                        char *end;
                        window = strtoul(arg+9, &end, 0);
                        if (!arg[9] || *end)
                                usage(pack_usage);
                        continue;
                }
-               if (!strncmp("--depth=", arg, 8)) {
+               if (!prefixcmp(arg, "--depth=")) {
                        char *end;
                        depth = strtoul(arg+8, &end, 0);
                        if (!arg[8] || *end)
@@ -1546,6 +1597,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        progress = 1;
                        continue;
                }
+               if (!strcmp("--all-progress", arg)) {
+                       progress = 2;
+                       continue;
+               }
                if (!strcmp("-q", arg)) {
                        progress = 0;
                        continue;
@@ -1567,7 +1622,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp("--unpacked", arg) ||
-                   !strncmp("--unpacked=", arg, 11) ||
+                   !prefixcmp(arg, "--unpacked=") ||
+                   !strcmp("--reflog", arg) ||
                    !strcmp("--all", arg)) {
                        use_internal_rev_list = 1;
                        if (ARRAY_SIZE(rp_av) - 1 <= rp_ac)
@@ -1641,7 +1697,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        else {
                if (nr_result)
                        prepare_pack(window, depth);
-               if (progress && pack_to_stdout) {
+               if (progress == 1 && pack_to_stdout) {
                        /* the other end usually displays progress itself */
                        struct itimerval v = {{0,},};
                        setitimer(ITIMER_REAL, &v, NULL);
@@ -1655,7 +1711,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                }
        }
        if (progress)
-               fprintf(stderr, "Total %d, written %d (delta %d), reused %d (delta %d)\n",
-                       nr_result, written, written_delta, reused, reused_delta);
+               fprintf(stderr, "Total %d (delta %d), reused %d (delta %d)\n",
+                       written, written_delta, reused, reused_delta);
        return 0;
 }