pack-objects: match prune logic for discarding objects
[gitweb.git] / builtin / pack-objects.c
index 0ee5f1ff94e1689b099f04fff8a05728c70f2f4f..4df9499040d4b740906f3a74c0217295371c4272 100644 (file)
@@ -20,6 +20,8 @@
 #include "streaming.h"
 #include "thread-utils.h"
 #include "pack-bitmap.h"
+#include "reachable.h"
+#include "sha1-array.h"
 
 static const char *pack_usage[] = {
        N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@ -89,8 +91,7 @@ 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 *));
+               REALLOC_ARRAY(indexed_commits, indexed_commits_alloc);
        }
 
        indexed_commits[indexed_commits_nr++] = commit;
@@ -708,7 +709,7 @@ static struct object_entry **compute_write_order(void)
 static off_t write_reused_pack(struct sha1file *f)
 {
        unsigned char buffer[8192];
-       off_t to_write;
+       off_t to_write, total;
        int fd;
 
        if (!is_pack_valid(reuse_packfile))
@@ -725,7 +726,7 @@ static off_t write_reused_pack(struct sha1file *f)
        if (reuse_packfile_offset < 0)
                reuse_packfile_offset = reuse_packfile->pack_size - 20;
 
-       to_write = reuse_packfile_offset - sizeof(struct pack_header);
+       total = to_write = reuse_packfile_offset - sizeof(struct pack_header);
 
        while (to_write) {
                int read_pack = xread(fd, buffer, sizeof(buffer));
@@ -738,10 +739,23 @@ static off_t write_reused_pack(struct sha1file *f)
 
                sha1write(f, buffer, read_pack);
                to_write -= read_pack;
+
+               /*
+                * We don't know the actual number of objects written,
+                * only how many bytes written, how many bytes total, and
+                * how many objects total. So we can fake it by pretending all
+                * objects we are writing are the same size. This gives us a
+                * smooth progress meter, and at the end it matches the true
+                * answer.
+                */
+               written = reuse_packfile_objects *
+                               (((double)(total - to_write)) / total);
+               display_progress(progress_state, written);
        }
 
        close(fd);
-       written += reuse_packfile_objects;
+       written = reuse_packfile_objects;
+       display_progress(progress_state, written);
        return reuse_packfile_offset - sizeof(struct pack_header);
 }
 
@@ -995,6 +1009,10 @@ static void create_object_entry(const unsigned char *sha1,
        entry->no_try_delta = no_try_delta;
 }
 
+static const char no_closure_warning[] = N_(
+"disabling bitmap writing, as some objects are not being packed"
+);
+
 static int add_object_entry(const unsigned char *sha1, enum object_type type,
                            const char *name, int exclude)
 {
@@ -1005,14 +1023,20 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
        if (have_duplicate_entry(sha1, exclude, &index_pos))
                return 0;
 
-       if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset))
+       if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) {
+               /* The pack is missing an object, so it will not have closure */
+               if (write_bitmap_index) {
+                       warning(_(no_closure_warning));
+                       write_bitmap_index = 0;
+               }
                return 0;
+       }
 
        create_object_entry(sha1, type, pack_name_hash(name),
                            exclude, name && no_try_delta(name),
                            index_pos, found_pack, found_offset);
 
-       display_progress(progress_state, to_pack.nr_objects);
+       display_progress(progress_state, nr_result);
        return 1;
 }
 
@@ -1028,7 +1052,7 @@ static int add_object_entry_from_bitmap(const unsigned char *sha1,
 
        create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
 
-       display_progress(progress_state, to_pack.nr_objects);
+       display_progress(progress_state, nr_result);
        return 1;
 }
 
@@ -1053,7 +1077,7 @@ static int pbase_tree_cache_ix_incr(int ix)
 static struct pbase_tree {
        struct pbase_tree *next;
        /* This is a phony "cache" entry; we are not
-        * going to evict it nor find it through _get()
+        * going to evict it or find it through _get()
         * mechanism -- this is for the toplevel node that
         * would almost always change with any commit.
         */
@@ -2191,10 +2215,6 @@ 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;
@@ -2389,6 +2409,27 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
        return 0;
 }
 
+/*
+ * Store a list of sha1s that are should not be discarded
+ * because they are either written too recently, or are
+ * reachable from another object that was.
+ *
+ * This is filled by get_object_list.
+ */
+static struct sha1_array recent_objects;
+
+static int loosened_object_can_be_discarded(const unsigned char *sha1,
+                                           unsigned long mtime)
+{
+       if (!unpack_unreachable_expiration)
+               return 0;
+       if (mtime > unpack_unreachable_expiration)
+               return 0;
+       if (sha1_array_lookup(&recent_objects, sha1) >= 0)
+               return 0;
+       return 1;
+}
+
 static void loosen_unused_packed_objects(struct rev_info *revs)
 {
        struct packed_git *p;
@@ -2399,46 +2440,62 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
                if (!p->pack_local || p->pack_keep)
                        continue;
 
-               if (unpack_unreachable_expiration &&
-                   p->mtime < unpack_unreachable_expiration)
-                       continue;
-
                if (open_pack_index(p))
                        die("cannot open pack index");
 
                for (i = 0; i < p->num_objects; i++) {
                        sha1 = nth_packed_object_sha1(p, i);
                        if (!packlist_find(&to_pack, sha1, NULL) &&
-                               !has_sha1_pack_kept_or_nonlocal(sha1))
+                           !has_sha1_pack_kept_or_nonlocal(sha1) &&
+                           !loosened_object_can_be_discarded(sha1, p->mtime))
                                if (force_object_loose(sha1, p->mtime))
                                        die("unable to force loose object");
                }
        }
 }
 
+/*
+ * This tracks any options which a reader of the pack might
+ * not understand, and which would therefore prevent blind reuse
+ * of what we have on disk.
+ */
+static int pack_options_allow_reuse(void)
+{
+       return allow_ofs_delta;
+}
+
 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(
+       if (pack_options_allow_reuse() &&
+           !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);
-               }
+               display_progress(progress_state, nr_result);
        }
 
        traverse_bitmap_commit_list(&add_object_entry_from_bitmap);
        return 0;
 }
 
+static void record_recent_object(struct object *obj,
+                                const struct name_path *path,
+                                const char *last,
+                                void *data)
+{
+       sha1_array_append(&recent_objects, obj->sha1);
+}
+
+static void record_recent_commit(struct commit *commit, void *data)
+{
+       sha1_array_append(&recent_objects, commit->object.sha1);
+}
+
 static void get_object_list(int ac, const char **av)
 {
        struct rev_info revs;
@@ -2469,6 +2526,7 @@ static void get_object_list(int ac, const char **av)
                                if (get_sha1_hex(line + 10, sha1))
                                        die("not an SHA-1 '%s'", line + 10);
                                register_shallow(sha1);
+                               use_bitmap_index = 0;
                                continue;
                        }
                        die("not a rev '%s'", line);
@@ -2485,10 +2543,23 @@ static void get_object_list(int ac, const char **av)
        mark_edges_uninteresting(&revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object, NULL);
 
+       if (unpack_unreachable_expiration) {
+               revs.ignore_missing_links = 1;
+               if (add_unseen_recent_objects_to_traversal(&revs,
+                               unpack_unreachable_expiration))
+                       die("unable to add recent objects");
+               if (prepare_revision_walk(&revs))
+                       die("revision walk setup failed");
+               traverse_commit_list(&revs, record_recent_commit,
+                                    record_recent_object, NULL);
+       }
+
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
        if (unpack_unreachable)
                loosen_unused_packed_objects(&revs);
+
+       sha1_array_clear(&recent_objects);
 }
 
 static int option_parse_index_version(const struct option *opt,