builtin/repack.c: make largest pack preferred

When repacking into a geometric series and writing a multi-pack bitmap, it is beneficial to have the largest resulting pack be the preferred object source in the bitmap's MIDX, since selecting the large packs can lead to fewer broken delta chains and better compression. Teach 'git repack' to identify this pack and pass it to the MIDX write machinery in order to mark it as preferred. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed Sep 28, 2021 at 21:55 UTC 6d08b9d4caa230441b7d9e2b4f23deaf9ff74c13
6 files changed +78 -3
Documentation/git-repack.txt
+4
@@ -190,6 +190,10 @@ this "roll-up", without respect to their reachability. This is subject
190 to change in the future. This option (implying a drastically different
191 repack mode) is not guaranteed to work with all other combinations of
192 option to `git repack`.
193 ++
194 +When writing a multi-pack bitmap, `git repack` selects the largest resulting
195 +pack as the preferred pack for object selection by the MIDX (see
196 +linkgit:git-multi-pack-index[1]).
197
198 -m::
199 --write-midx::
builtin/repack.c
+26 -1
@@ -423,6 +423,25 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
423 geometry->split = split;
424 }
425
426 +static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
427 +{
428 + if (!geometry) {
429 + /*
430 + * No geometry means either an all-into-one repack (in which
431 + * case there is only one pack left and it is the largest) or an
432 + * incremental one.
433 + *
434 + * If repacking incrementally, then we could check the size of
435 + * all packs to determine which should be preferred, but leave
436 + * this for later.
437 + */
438 + return NULL;
439 + }
440 + if (geometry->split == geometry->pack_nr)
441 + return NULL;
442 + return geometry->pack[geometry->pack_nr - 1];
443 +}
444 +
445 static void clear_pack_geometry(struct pack_geometry *geometry)
446 {
447 if (!geometry)
@@ -468,10 +487,12 @@ static void midx_included_packs(struct string_list *include,
487 }
488
489 static int write_midx_included_packs(struct string_list *include,
490 + struct pack_geometry *geometry,
491 int show_progress, int write_bitmaps)
492 {
493 struct child_process cmd = CHILD_PROCESS_INIT;
494 struct string_list_item *item;
495 + struct packed_git *largest = get_largest_active_pack(geometry);
496 FILE *in;
497 int ret;
498
@@ -492,6 +513,10 @@ static int write_midx_included_packs(struct string_list *include,
513 if (write_bitmaps)
514 strvec_push(&cmd.args, "--bitmap");
515
516 + if (largest)
517 + strvec_pushf(&cmd.args, "--preferred-pack=%s",
518 + pack_basename(largest));
519 +
520 ret = start_command(&cmd);
521 if (ret)
522 return ret;
@@ -783,7 +808,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
808 midx_included_packs(&include, &existing_nonkept_packs,
809 &existing_kept_packs, &names, geometry);
810
786 - ret = write_midx_included_packs(&include,
811 + ret = write_midx_included_packs(&include, geometry,
812 show_progress, write_bitmaps > 0);
813
814 string_list_clear(&include, 0);
pack-bitmap.c
+1 -1
@@ -1418,7 +1418,7 @@ static int try_partial_reuse(struct packed_git *pack,
1418 return 0;
1419 }
1420
1421 -static uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
1421 +uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
1422 {
1423 struct multi_pack_index *m = bitmap_git->midx;
1424 if (!m)
pack-bitmap.h
+1
@@ -55,6 +55,7 @@ int test_bitmap_commits(struct repository *r);
55 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
56 struct list_objects_filter_options *filter,
57 int filter_provided_objects);
58 +uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
59 int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
60 struct packed_git **packfile,
61 uint32_t *entries,
t/helper/test-read-midx.c
+24 -1
@@ -3,6 +3,7 @@
3 #include "midx.h"
4 #include "repository.h"
5 #include "object-store.h"
6 +#include "pack-bitmap.h"
7
8 static int read_midx_file(const char *object_dir, int show_objects)
9 {
@@ -72,14 +73,36 @@ static int read_midx_checksum(const char *object_dir)
73 return 0;
74 }
75
76 +static int read_midx_preferred_pack(const char *object_dir)
77 +{
78 + struct multi_pack_index *midx = NULL;
79 + struct bitmap_index *bitmap = NULL;
80 +
81 + setup_git_directory();
82 +
83 + midx = load_multi_pack_index(object_dir, 1);
84 + if (!midx)
85 + return 1;
86 +
87 + bitmap = prepare_bitmap_git(the_repository);
88 + if (!(bitmap && bitmap_is_midx(bitmap)))
89 + return 1;
90 +
91 +
92 + printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]);
93 + return 0;
94 +}
95 +
96 int cmd__read_midx(int argc, const char **argv)
97 {
98 if (!(argc == 2 || argc == 3))
78 - usage("read-midx [--show-objects|--checksum] <object-dir>");
99 + usage("read-midx [--show-objects|--checksum|--preferred-pack] <object-dir>");
100
101 if (!strcmp(argv[1], "--show-objects"))
102 return read_midx_file(argv[2], 1);
103 else if (!strcmp(argv[1], "--checksum"))
104 return read_midx_checksum(argv[2]);
105 + else if (!strcmp(argv[1], "--preferred-pack"))
106 + return read_midx_preferred_pack(argv[2]);
107 return read_midx_file(argv[1], 0);
108 }
t/t7703-repack-geometric.sh
+22
@@ -180,4 +180,26 @@ test_expect_success '--geometric ignores kept packs' '
180 )
181 '
182
183 +test_expect_success '--geometric chooses largest MIDX preferred pack' '
184 + git init geometric &&
185 + test_when_finished "rm -fr geometric" &&
186 + (
187 + cd geometric &&
188 +
189 + # These packs already form a geometric progression.
190 + test_commit_bulk --start=1 1 && # 3 objects
191 + test_commit_bulk --start=2 2 && # 6 objects
192 + ls $objdir/pack/pack-*.idx >before &&
193 + test_commit_bulk --start=4 4 && # 12 objects
194 + ls $objdir/pack/pack-*.idx >after &&
195 +
196 + git repack --geometric 2 -dbm &&
197 +
198 + comm -3 before after | xargs -n 1 basename >expect &&
199 + test-tool read-midx --preferred-pack $objdir >actual &&
200 +
201 + test_cmp expect actual
202 + )
203 +'
204 +
205 test_done