Don't create a final empty packfile in fast-import.
[gitweb.git] / fast-import.c
index f0f51a68998222d48b4c3f8e2c5727f0842690f4..207acb3230f1c3a8b7843e82da282729ad4be37a 100644 (file)
@@ -40,6 +40,9 @@ Format of STDIN stream:
     ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
     lf;
 
+  checkpoint ::= 'checkpoint' lf
+    lf;
+
      # note: the first idnum in a stream should be 1 and subsequent
      # idnums should not have gaps between values as this will cause
      # the stream parser to reserve space for the gapped values.  An
@@ -110,8 +113,9 @@ Format of STDIN stream:
 struct object_entry
 {
        struct object_entry *next;
-       enum object_type type;
        unsigned long offset;
+       unsigned type : TYPE_BITS;
+       unsigned pack_id : 16;
        unsigned char sha1[20];
 };
 
@@ -216,13 +220,11 @@ static unsigned long max_depth = 10;
 static unsigned long alloc_count;
 static unsigned long branch_count;
 static unsigned long branch_load_count;
-static unsigned long remap_count;
 static unsigned long object_count;
-static unsigned long duplicate_count;
 static unsigned long marks_set_count;
-static unsigned long object_count_by_type[9];
-static unsigned long duplicate_count_by_type[9];
-static unsigned long delta_count_by_type[9];
+static unsigned long object_count_by_type[1 << TYPE_BITS];
+static unsigned long duplicate_count_by_type[1 << TYPE_BITS];
+static unsigned long delta_count_by_type[1 << TYPE_BITS];
 
 /* Memory pools */
 static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -235,7 +237,11 @@ static unsigned int atom_cnt;
 static struct atom_str **atom_table;
 
 /* The .pack file being generated */
+static const char *base_name;
+static unsigned int pack_id;
+static char *idx_name;
 static struct packed_git *pack_data;
+static struct packed_git **all_packs;
 static int pack_fd;
 static unsigned long pack_size;
 static unsigned char pack_sha1[20];
@@ -276,7 +282,7 @@ static struct dbuf new_data;
 static FILE* branch_log;
 
 
-static void alloc_objects(int cnt)
+static void alloc_objects(unsigned int cnt)
 {
        struct object_entry_pool *b;
 
@@ -297,6 +303,7 @@ static struct object_entry* new_object(unsigned char *sha1)
                alloc_objects(object_entry_alloc);
 
        e = blocks->next_free++;
+       e->pack_id = pack_id;
        hashcpy(e->sha1, sha1);
        return e;
 }
@@ -593,6 +600,147 @@ static void yread(int fd, void *buffer, size_t length)
        }
 }
 
+static void start_packfile()
+{
+       struct packed_git *p;
+       struct pack_header hdr;
+
+       idx_name = xmalloc(strlen(base_name) + 11);
+       p = xcalloc(1, sizeof(*p) + strlen(base_name) + 13);
+       sprintf(p->pack_name, "%s%5.5i.pack", base_name, pack_id + 1);
+       sprintf(idx_name, "%s%5.5i.idx", base_name, pack_id + 1);
+
+       pack_fd = open(p->pack_name, O_RDWR|O_CREAT|O_EXCL, 0666);
+       if (pack_fd < 0)
+               die("Can't create %s: %s", p->pack_name, strerror(errno));
+       p->pack_fd = pack_fd;
+
+       hdr.hdr_signature = htonl(PACK_SIGNATURE);
+       hdr.hdr_version = htonl(2);
+       hdr.hdr_entries = 0;
+       write_or_die(pack_fd, &hdr, sizeof(hdr));
+
+       pack_data = p;
+       pack_size = sizeof(hdr);
+       object_count = 0;
+
+       all_packs = xrealloc(all_packs, sizeof(*all_packs) * (pack_id + 1));
+       all_packs[pack_id] = p;
+}
+
+static void fixup_header_footer()
+{
+       SHA_CTX c;
+       char hdr[8];
+       unsigned long cnt;
+       char *buf;
+
+       if (lseek(pack_fd, 0, SEEK_SET) != 0)
+               die("Failed seeking to start: %s", strerror(errno));
+
+       SHA1_Init(&c);
+       yread(pack_fd, hdr, 8);
+       SHA1_Update(&c, hdr, 8);
+
+       cnt = htonl(object_count);
+       SHA1_Update(&c, &cnt, 4);
+       write_or_die(pack_fd, &cnt, 4);
+
+       buf = xmalloc(128 * 1024);
+       for (;;) {
+               size_t n = xread(pack_fd, buf, 128 * 1024);
+               if (n <= 0)
+                       break;
+               SHA1_Update(&c, buf, n);
+       }
+       free(buf);
+
+       SHA1_Final(pack_sha1, &c);
+       write_or_die(pack_fd, pack_sha1, sizeof(pack_sha1));
+}
+
+static int oecmp (const void *a_, const void *b_)
+{
+       struct object_entry *a = *((struct object_entry**)a_);
+       struct object_entry *b = *((struct object_entry**)b_);
+       return hashcmp(a->sha1, b->sha1);
+}
+
+static void write_index(const char *idx_name)
+{
+       struct sha1file *f;
+       struct object_entry **idx, **c, **last, *e;
+       struct object_entry_pool *o;
+       unsigned int array[256];
+       int i;
+
+       /* Build the sorted table of object IDs. */
+       idx = xmalloc(object_count * sizeof(struct object_entry*));
+       c = idx;
+       for (o = blocks; o; o = o->next_pool)
+               for (e = o->entries; e != o->next_free; e++)
+                       if (pack_id == e->pack_id)
+                               *c++ = e;
+       last = idx + object_count;
+       qsort(idx, object_count, sizeof(struct object_entry*), oecmp);
+
+       /* Generate the fan-out array. */
+       c = idx;
+       for (i = 0; i < 256; i++) {
+               struct object_entry **next = c;;
+               while (next < last) {
+                       if ((*next)->sha1[0] != i)
+                               break;
+                       next++;
+               }
+               array[i] = htonl(next - idx);
+               c = next;
+       }
+
+       f = sha1create("%s", idx_name);
+       sha1write(f, array, 256 * sizeof(int));
+       for (c = idx; c != last; c++) {
+               unsigned int offset = htonl((*c)->offset);
+               sha1write(f, &offset, 4);
+               sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
+       }
+       sha1write(f, pack_sha1, sizeof(pack_sha1));
+       sha1close(f, NULL, 1);
+       free(idx);
+}
+
+static void end_packfile()
+{
+       struct packed_git *old_p = pack_data, *new_p;
+
+       if (object_count) {
+               fixup_header_footer();
+               write_index(idx_name);
+
+               /* Register the packfile with core git's machinary. */
+               new_p = add_packed_git(idx_name, strlen(idx_name), 1);
+               if (!new_p)
+                       die("core git rejected index %s", idx_name);
+               new_p->windows = old_p->windows;
+               new_p->pack_fd = old_p->pack_fd;
+               all_packs[pack_id++] = new_p;
+               install_packed_git(new_p);
+       }
+       else {
+               close(pack_fd);
+               unlink(old_p->pack_name);
+       }
+       free(old_p);
+       free(idx_name);
+
+       /* We can't carry a delta across packfiles. */
+       free(last_blob.data);
+       last_blob.data = NULL;
+       last_blob.len = 0;
+       last_blob.offset = 0;
+       last_blob.depth = 0;
+}
+
 static size_t encode_header(
        enum object_type type,
        size_t size,
@@ -644,7 +792,6 @@ static int store_object(
        if (mark)
                insert_mark(mark, e);
        if (e->offset) {
-               duplicate_count++;
                duplicate_count_by_type[type]++;
                return 1;
        }
@@ -713,11 +860,15 @@ static int store_object(
        return 0;
 }
 
-static void *gfi_unpack_entry(unsigned long ofs, unsigned long *sizep)
+static void *gfi_unpack_entry(
+       struct object_entry *oe,
+       unsigned long *sizep)
 {
-       char type[20];
-       pack_data->pack_size = pack_size + 20;
-       return unpack_entry(pack_data, ofs, type, sizep);
+       static char type[20];
+       struct packed_git *p = all_packs[oe->pack_id];
+       if (p == pack_data)
+               p->pack_size = pack_size + 20;
+       return unpack_entry(p, oe->offset, type, sizep);
 }
 
 static const char *get_mode(const char *str, unsigned int *modep)
@@ -752,7 +903,7 @@ static void load_tree(struct tree_entry *root)
                if (myoe->type != OBJ_TREE)
                        die("Not a tree: %s", sha1_to_hex(sha1));
                t->delta_depth = 0;
-               buf = gfi_unpack_entry(myoe->offset, &size);
+               buf = gfi_unpack_entry(myoe, &size);
        } else {
                char type[20];
                buf = read_sha1_file(sha1, type, &size);
@@ -852,7 +1003,9 @@ static void store_tree(struct tree_entry *root)
        }
 
        le = find_object(root->versions[0].sha1);
-       if (!S_ISDIR(root->versions[0].mode) || !le) {
+       if (!S_ISDIR(root->versions[0].mode)
+               || !le
+               || le->pack_id != pack_id) {
                lo.data = NULL;
                lo.depth = 0;
        } else {
@@ -994,100 +1147,6 @@ static int tree_content_remove(struct tree_entry *root, const char *p)
        return 1;
 }
 
-static void init_pack_header()
-{
-       struct pack_header hdr;
-
-       hdr.hdr_signature = htonl(PACK_SIGNATURE);
-       hdr.hdr_version = htonl(2);
-       hdr.hdr_entries = 0;
-
-       write_or_die(pack_fd, &hdr, sizeof(hdr));
-       pack_size = sizeof(hdr);
-}
-
-static void fixup_header_footer()
-{
-       SHA_CTX c;
-       char hdr[8];
-       unsigned long cnt;
-       char *buf;
-       size_t n;
-
-       if (lseek(pack_fd, 0, SEEK_SET) != 0)
-               die("Failed seeking to start: %s", strerror(errno));
-
-       SHA1_Init(&c);
-       yread(pack_fd, hdr, 8);
-       SHA1_Update(&c, hdr, 8);
-
-       cnt = htonl(object_count);
-       SHA1_Update(&c, &cnt, 4);
-       write_or_die(pack_fd, &cnt, 4);
-
-       buf = xmalloc(128 * 1024);
-       for (;;) {
-               n = xread(pack_fd, buf, 128 * 1024);
-               if (n <= 0)
-                       break;
-               SHA1_Update(&c, buf, n);
-       }
-       free(buf);
-
-       SHA1_Final(pack_sha1, &c);
-       write_or_die(pack_fd, pack_sha1, sizeof(pack_sha1));
-}
-
-static int oecmp (const void *_a, const void *_b)
-{
-       struct object_entry *a = *((struct object_entry**)_a);
-       struct object_entry *b = *((struct object_entry**)_b);
-       return hashcmp(a->sha1, b->sha1);
-}
-
-static void write_index(const char *idx_name)
-{
-       struct sha1file *f;
-       struct object_entry **idx, **c, **last;
-       struct object_entry *e;
-       struct object_entry_pool *o;
-       unsigned int array[256];
-       int i;
-
-       /* Build the sorted table of object IDs. */
-       idx = xmalloc(object_count * sizeof(struct object_entry*));
-       c = idx;
-       for (o = blocks; o; o = o->next_pool)
-               for (e = o->entries; e != o->next_free; e++)
-                       *c++ = e;
-       last = idx + object_count;
-       qsort(idx, object_count, sizeof(struct object_entry*), oecmp);
-
-       /* Generate the fan-out array. */
-       c = idx;
-       for (i = 0; i < 256; i++) {
-               struct object_entry **next = c;;
-               while (next < last) {
-                       if ((*next)->sha1[0] != i)
-                               break;
-                       next++;
-               }
-               array[i] = htonl(next - idx);
-               c = next;
-       }
-
-       f = sha1create("%s", idx_name);
-       sha1write(f, array, 256 * sizeof(int));
-       for (c = idx; c != last; c++) {
-               unsigned int offset = htonl((*c)->offset);
-               sha1write(f, &offset, 4);
-               sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
-       }
-       sha1write(f, pack_sha1, sizeof(pack_sha1));
-       sha1close(f, NULL, 1);
-       free(idx);
-}
-
 static void dump_branches()
 {
        static const char *msg = "fast-import";
@@ -1360,7 +1419,7 @@ static void cmd_from(struct branch *b)
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%lu not a commit", idnum);
                hashcpy(b->sha1, oe->sha1);
-               buf = gfi_unpack_entry(oe->offset, &size);
+               buf = gfi_unpack_entry(oe, &size);
                if (!buf || size < 46)
                        die("Not a valid commit: %s", from);
                if (memcmp("tree ", buf, 5)
@@ -1688,17 +1747,23 @@ static void cmd_reset_branch()
        cmd_from(b);
 }
 
+static void cmd_checkpoint()
+{
+       if (object_count) {
+               end_packfile();
+               start_packfile();
+       }
+       read_next_command();
+}
+
 static const char fast_import_usage[] =
 "git-fast-import [--objects=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file] [--branch-log=log] temp.pack";
 
 int main(int argc, const char **argv)
 {
-       const char *base_name;
        int i;
        unsigned long est_obj_cnt = object_entry_alloc;
-       char *pack_name;
-       char *idx_name;
-       struct stat sb;
+       unsigned long duplicate_count;
 
        setup_ident();
        git_config(git_default_config);
@@ -1728,20 +1793,6 @@ int main(int argc, const char **argv)
                usage(fast_import_usage);
        base_name = argv[i];
 
-       pack_name = xmalloc(strlen(base_name) + 6);
-       sprintf(pack_name, "%s.pack", base_name);
-       idx_name = xmalloc(strlen(base_name) + 5);
-       sprintf(idx_name, "%s.idx", base_name);
-
-       pack_fd = open(pack_name, O_RDWR|O_CREAT|O_EXCL, 0666);
-       if (pack_fd < 0)
-               die("Can't create %s: %s", pack_name, strerror(errno));
-
-       pack_data = xcalloc(1, sizeof(*pack_data) + strlen(pack_name) + 2);
-       strcpy(pack_data->pack_name, pack_name);
-       pack_data->pack_fd = pack_fd;
-
-       init_pack_header();
        alloc_objects(est_obj_cnt);
        strbuf_init(&command_buf);
 
@@ -1750,6 +1801,7 @@ int main(int argc, const char **argv)
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
        marks = pool_calloc(1, sizeof(struct mark_set));
 
+       start_packfile();
        for (;;) {
                read_next_command();
                if (command_buf.eof)
@@ -1762,19 +1814,22 @@ int main(int argc, const char **argv)
                        cmd_new_tag();
                else if (!strncmp("reset ", command_buf.buf, 6))
                        cmd_reset_branch();
+               else if (!strcmp("checkpoint", command_buf.buf))
+                       cmd_checkpoint();
                else
                        die("Unsupported command: %s", command_buf.buf);
        }
+       end_packfile();
 
-       fixup_header_footer();
-       close(pack_fd);
-       write_index(idx_name);
        dump_branches();
        dump_tags();
        dump_marks();
        if (branch_log)
                fclose(branch_log);
 
+       for (i = 0; i < ARRAY_SIZE(duplicate_count_by_type); i++)
+               duplicate_count += duplicate_count_by_type[i];
+
        fprintf(stderr, "%s statistics:\n", argv[0]);
        fprintf(stderr, "---------------------------------------------------------------------\n");
        fprintf(stderr, "Alloc'd objects: %10lu (%10lu overflow  )\n", alloc_count, alloc_count - est_obj_cnt);
@@ -1789,13 +1844,9 @@ int main(int argc, const char **argv)
        fprintf(stderr, "Memory total:    %10lu KiB\n", (total_allocd + alloc_count*sizeof(struct object_entry))/1024);
        fprintf(stderr, "       pools:    %10lu KiB\n", total_allocd/1024);
        fprintf(stderr, "     objects:    %10lu KiB\n", (alloc_count*sizeof(struct object_entry))/1024);
-       fprintf(stderr, "Pack remaps:     %10lu\n", remap_count);
-       stat(pack_name, &sb);
-       fprintf(stderr, "Pack size:       %10lu KiB\n", (unsigned long)(sb.st_size/1024));
-       stat(idx_name, &sb);
-       fprintf(stderr, "Index size:      %10lu KiB\n", (unsigned long)(sb.st_size/1024));
        fprintf(stderr, "---------------------------------------------------------------------\n");
-
+       pack_report();
+       fprintf(stderr, "---------------------------------------------------------------------\n");
        fprintf(stderr, "\n");
 
        return 0;