repack: pack objects mentioned by the index

When we pack all objects, we use only the objects reachable from references and reflogs. This misses any objects which are reachable from the index, but not yet referenced. By itself this isn't a big deal; the objects can remain loose until they are actually used in a commit. However, it does create a problem when we drop packed but unreachable objects. We try to optimize out the writing of objects that we will immediately prune, which means we must follow the same rules as prune in determining what is reachable. And prune uses the index for this purpose. This is rather uncommon in practice, as objects in the index would not usually have been packed in the first place. But it could happen in a sequence like: 1. You make a commit on a branch that references blob X. 2. You repack, moving X into the pack. 3. You delete the branch (and its reflog), so that X is unreferenced. 4. You "git add" blob X so that it is now referenced only by the index. 5. You repack again with git-gc. The pack-objects we invoke will see that X is neither referenced nor recent and not bother loosening it. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Oct 16, 2014 at 20:44 UTC c90f9e13abae630551ada3e895633bdc2cf4e080
3 files changed +22
builtin/pack-objects.c
+8
@@ -2617,6 +2617,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
2617 int all_progress_implied = 0;
2618 struct argv_array rp = ARGV_ARRAY_INIT;
2619 int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
2620 + int rev_list_index = 0;
2621 struct option pack_objects_options[] = {
2622 OPT_SET_INT('q', "quiet", &progress,
2623 N_("do not show progress meter"), 0),
@@ -2663,6 +2664,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
2664 { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
2665 N_("include objects referred by reflog entries"),
2666 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
2667 + { OPTION_SET_INT, 0, "indexed-objects", &rev_list_index, NULL,
2668 + N_("include objects referred to by the index"),
2669 + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
2670 OPT_BOOL(0, "stdout", &pack_to_stdout,
2671 N_("output pack to stdout")),
2672 OPT_BOOL(0, "include-tag", &include_tag,
@@ -2720,6 +2724,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
2724 use_internal_rev_list = 1;
2725 argv_array_push(&rp, "--reflog");
2726 }
2727 + if (rev_list_index) {
2728 + use_internal_rev_list = 1;
2729 + argv_array_push(&rp, "--indexed-objects");
2730 + }
2731 if (rev_list_unpacked) {
2732 use_internal_rev_list = 1;
2733 argv_array_push(&rp, "--unpacked");
builtin/repack.c
+1
@@ -209,6 +209,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
209 argv_array_push(&cmd_args, "--non-empty");
210 argv_array_push(&cmd_args, "--all");
211 argv_array_push(&cmd_args, "--reflog");
212 + argv_array_push(&cmd_args, "--indexed-objects");
213 if (window)
214 argv_array_pushf(&cmd_args, "--window=%s", window);
215 if (window_memory)
t/t7701-repack-unpack-unreachable.sh
+13
@@ -109,4 +109,17 @@ test_expect_success 'do not bother loosening old objects' '
109 test_must_fail git cat-file -p $obj2
110 '
111
112 +test_expect_success 'keep packed objects found only in index' '
113 + echo my-unique-content >file &&
114 + git add file &&
115 + git commit -m "make it reachable" &&
116 + git gc &&
117 + git reset HEAD^ &&
118 + git reflog expire --expire=now --all &&
119 + git add file &&
120 + test-chmtime =-86400 .git/objects/pack/* &&
121 + git gc --prune=1.hour.ago &&
122 + git cat-file blob :file
123 +'
124 +
125 test_done