repack: do not accidentally pack kept objects by default
[gitweb.git] / builtin / pack-objects.c
index 13b171d6498a086329ad617ad9b900db82220e84..fd741970e61c674c628f2e9c8ae48e08d4996961 100644 (file)
@@ -19,6 +19,7 @@
 #include "refs.h"
 #include "streaming.h"
 #include "thread-utils.h"
+#include "pack-bitmap.h"
 
 static const char *pack_usage[] = {
        N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@ -57,6 +58,14 @@ static struct progress *progress_state;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
 static int pack_compression_seen;
 
+static struct packed_git *reuse_packfile;
+static uint32_t reuse_packfile_objects;
+static off_t reuse_packfile_offset;
+
+static int use_bitmap_index = 1;
+static int write_bitmap_index;
+static uint16_t write_bitmap_options;
+
 static unsigned long delta_cache_size = 0;
 static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
 static unsigned long cache_max_small_delta_size = 1000;
@@ -69,6 +78,24 @@ static unsigned long window_memory_limit = 0;
 static uint32_t written, written_delta;
 static uint32_t reused, reused_delta;
 
+/*
+ * Indexed commits
+ */
+static struct commit **indexed_commits;
+static unsigned int indexed_commits_nr;
+static unsigned int indexed_commits_alloc;
+
+static void index_commit_for_bitmap(struct commit *commit)
+{
+       if (indexed_commits_nr >= indexed_commits_alloc) {
+               indexed_commits_alloc = (indexed_commits_alloc + 32) * 2;
+               indexed_commits = xrealloc(indexed_commits,
+                       indexed_commits_alloc * sizeof(struct commit *));
+       }
+
+       indexed_commits[indexed_commits_nr++] = commit;
+}
+
 static void *get_delta(struct object_entry *entry)
 {
        unsigned long size, base_size, delta_size;
@@ -678,6 +705,46 @@ static struct object_entry **compute_write_order(void)
        return wo;
 }
 
+static off_t write_reused_pack(struct sha1file *f)
+{
+       unsigned char buffer[8192];
+       off_t to_write;
+       int fd;
+
+       if (!is_pack_valid(reuse_packfile))
+               die("packfile is invalid: %s", reuse_packfile->pack_name);
+
+       fd = git_open_noatime(reuse_packfile->pack_name);
+       if (fd < 0)
+               die_errno("unable to open packfile for reuse: %s",
+                         reuse_packfile->pack_name);
+
+       if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1)
+               die_errno("unable to seek in reused packfile");
+
+       if (reuse_packfile_offset < 0)
+               reuse_packfile_offset = reuse_packfile->pack_size - 20;
+
+       to_write = reuse_packfile_offset - sizeof(struct pack_header);
+
+       while (to_write) {
+               int read_pack = xread(fd, buffer, sizeof(buffer));
+
+               if (read_pack <= 0)
+                       die_errno("unable to read from reused packfile");
+
+               if (read_pack > to_write)
+                       read_pack = to_write;
+
+               sha1write(f, buffer, read_pack);
+               to_write -= read_pack;
+       }
+
+       close(fd);
+       written += reuse_packfile_objects;
+       return reuse_packfile_offset - sizeof(struct pack_header);
+}
+
 static void write_pack_file(void)
 {
        uint32_t i = 0, j;
@@ -704,6 +771,15 @@ static void write_pack_file(void)
                offset = write_pack_header(f, nr_remaining);
                if (!offset)
                        die_errno("unable to write pack header");
+
+               if (reuse_packfile) {
+                       off_t packfile_size;
+                       assert(pack_to_stdout);
+
+                       packfile_size = write_reused_pack(f);
+                       offset += packfile_size;
+               }
+
                nr_written = 0;
                for (; i < to_pack.nr_objects; i++) {
                        struct object_entry *e = write_order[i];
@@ -756,9 +832,31 @@ static void write_pack_file(void)
                        if (sizeof(tmpname) <= strlen(base_name) + 50)
                                die("pack base name '%s' too long", base_name);
                        snprintf(tmpname, sizeof(tmpname), "%s-", base_name);
+
+                       if (write_bitmap_index) {
+                               bitmap_writer_set_checksum(sha1);
+                               bitmap_writer_build_type_index(written_list, nr_written);
+                       }
+
                        finish_tmp_packfile(tmpname, pack_tmp_name,
                                            written_list, nr_written,
                                            &pack_idx_opts, sha1);
+
+                       if (write_bitmap_index) {
+                               char *end_of_name_prefix = strrchr(tmpname, 0);
+                               sprintf(end_of_name_prefix, "%s.bitmap", sha1_to_hex(sha1));
+
+                               stop_progress(&progress_state);
+
+                               bitmap_writer_show_progress(progress);
+                               bitmap_writer_reuse_bitmaps(&to_pack);
+                               bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
+                               bitmap_writer_build(&to_pack);
+                               bitmap_writer_finish(written_list, nr_written,
+                                                    tmpname, write_bitmap_options);
+                               write_bitmap_index = 0;
+                       }
+
                        free(pack_tmp_name);
                        puts(sha1_to_hex(sha1));
                }
@@ -923,6 +1021,22 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
        return 1;
 }
 
+static int add_object_entry_from_bitmap(const unsigned char *sha1,
+                                       enum object_type type,
+                                       int flags, uint32_t name_hash,
+                                       struct packed_git *pack, off_t offset)
+{
+       uint32_t index_pos;
+
+       if (have_duplicate_entry(sha1, 0, &index_pos))
+               return 0;
+
+       create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
+
+       display_progress(progress_state, to_pack.nr_objects);
+       return 1;
+}
+
 struct pbase_tree_cache {
        unsigned char sha1[20];
        int ref;
@@ -2085,6 +2199,20 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                cache_max_small_delta_size = git_config_int(k, v);
                return 0;
        }
+       if (!strcmp(k, "pack.writebitmaps")) {
+               write_bitmap_index = git_config_bool(k, v);
+               return 0;
+       }
+       if (!strcmp(k, "pack.writebitmaphashcache")) {
+               if (git_config_bool(k, v))
+                       write_bitmap_options |= BITMAP_OPT_HASH_CACHE;
+               else
+                       write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE;
+       }
+       if (!strcmp(k, "pack.usebitmaps")) {
+               use_bitmap_index = git_config_bool(k, v);
+               return 0;
+       }
        if (!strcmp(k, "pack.threads")) {
                delta_search_threads = git_config_int(k, v);
                if (delta_search_threads < 0)
@@ -2143,6 +2271,9 @@ static void show_commit(struct commit *commit, void *data)
 {
        add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
+
+       if (write_bitmap_index)
+               index_commit_for_bitmap(commit);
 }
 
 static void show_object(struct object *obj,
@@ -2293,6 +2424,29 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
        }
 }
 
+static int get_object_list_from_bitmap(struct rev_info *revs)
+{
+       if (prepare_bitmap_walk(revs) < 0)
+               return -1;
+
+       if (!reuse_partial_packfile_from_bitmap(
+                       &reuse_packfile,
+                       &reuse_packfile_objects,
+                       &reuse_packfile_offset)) {
+               assert(reuse_packfile_objects);
+               nr_result += reuse_packfile_objects;
+
+               if (progress) {
+                       fprintf(stderr, "Reusing existing pack: %d, done.\n",
+                               reuse_packfile_objects);
+                       fflush(stderr);
+               }
+       }
+
+       traverse_bitmap_commit_list(&add_object_entry_from_bitmap);
+       return 0;
+}
+
 static void get_object_list(int ac, const char **av)
 {
        struct rev_info revs;
@@ -2312,6 +2466,7 @@ static void get_object_list(int ac, const char **av)
                if (*line == '-') {
                        if (!strcmp(line, "--not")) {
                                flags ^= UNINTERESTING;
+                               write_bitmap_index = 0;
                                continue;
                        }
                        die("not a rev '%s'", line);
@@ -2320,6 +2475,9 @@ static void get_object_list(int ac, const char **av)
                        die("bad revision '%s'", line);
        }
 
+       if (use_bitmap_index && !get_object_list_from_bitmap(&revs))
+               return;
+
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(&revs, show_edge);
@@ -2449,6 +2607,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                            N_("pack compression level")),
                OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
                            N_("do not hide commits by grafts"), 0),
+               OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index,
+                        N_("use a bitmap index if available to speed up counting objects")),
+               OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
+                        N_("write a bitmap index together with the pack index")),
                OPT_END(),
        };
 
@@ -2515,6 +2677,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (keep_unreachable && unpack_unreachable)
                die("--keep-unreachable and --unpack-unreachable are incompatible.");
 
+       if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow())
+               use_bitmap_index = 0;
+
+       if (pack_to_stdout || !rev_list_all)
+               write_bitmap_index = 0;
+
        if (progress && all_progress_implied)
                progress = 2;