Skip to content

Commit cf98b69

Browse files
committed
Merge branch 'tb/midx-with-changing-preferred-pack-fix'
Multi-pack index got corrupted when preferred pack changed from one pack to another in a certain way, which has been corrected. * tb/midx-with-changing-preferred-pack-fix: midx.c: avoid adding preferred objects twice midx.c: include preferred pack correctly with existing MIDX midx.c: extract `midx_fanout_add_pack_fanout()` midx.c: extract `midx_fanout_add_midx_fanout()` midx.c: extract `struct midx_fanout` t/lib-bitmap.sh: avoid silencing stderr t5326: demonstrate potential bitmap corruption
2 parents be1a02a + 99e4d08 commit cf98b69

File tree

3 files changed

+140
-47
lines changed

3 files changed

+140
-47
lines changed

midx.c

Lines changed: 95 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,78 @@ static void fill_pack_entry(uint32_t pack_int_id,
577577
entry->preferred = !!preferred;
578578
}
579579

580+
struct midx_fanout {
581+
struct pack_midx_entry *entries;
582+
uint32_t nr;
583+
uint32_t alloc;
584+
};
585+
586+
static void midx_fanout_grow(struct midx_fanout *fanout, uint32_t nr)
587+
{
588+
ALLOC_GROW(fanout->entries, nr, fanout->alloc);
589+
}
590+
591+
static void midx_fanout_sort(struct midx_fanout *fanout)
592+
{
593+
QSORT(fanout->entries, fanout->nr, midx_oid_compare);
594+
}
595+
596+
static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
597+
struct multi_pack_index *m,
598+
uint32_t cur_fanout,
599+
int preferred_pack)
600+
{
601+
uint32_t start = 0, end;
602+
uint32_t cur_object;
603+
604+
if (cur_fanout)
605+
start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
606+
end = ntohl(m->chunk_oid_fanout[cur_fanout]);
607+
608+
for (cur_object = start; cur_object < end; cur_object++) {
609+
if ((preferred_pack > -1) &&
610+
(preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
611+
/*
612+
* Objects from preferred packs are added
613+
* separately.
614+
*/
615+
continue;
616+
}
617+
618+
midx_fanout_grow(fanout, fanout->nr + 1);
619+
nth_midxed_pack_midx_entry(m,
620+
&fanout->entries[fanout->nr],
621+
cur_object);
622+
fanout->entries[fanout->nr].preferred = 0;
623+
fanout->nr++;
624+
}
625+
}
626+
627+
static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
628+
struct pack_info *info,
629+
uint32_t cur_pack,
630+
int preferred,
631+
uint32_t cur_fanout)
632+
{
633+
struct packed_git *pack = info[cur_pack].p;
634+
uint32_t start = 0, end;
635+
uint32_t cur_object;
636+
637+
if (cur_fanout)
638+
start = get_pack_fanout(pack, cur_fanout - 1);
639+
end = get_pack_fanout(pack, cur_fanout);
640+
641+
for (cur_object = start; cur_object < end; cur_object++) {
642+
midx_fanout_grow(fanout, fanout->nr + 1);
643+
fill_pack_entry(cur_pack,
644+
info[cur_pack].p,
645+
cur_object,
646+
&fanout->entries[fanout->nr],
647+
preferred);
648+
fanout->nr++;
649+
}
650+
}
651+
580652
/*
581653
* It is possible to artificially get into a state where there are many
582654
* duplicate copies of objects. That can create high memory pressure if
@@ -595,8 +667,8 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
595667
int preferred_pack)
596668
{
597669
uint32_t cur_fanout, cur_pack, cur_object;
598-
uint32_t alloc_fanout, alloc_objects, total_objects = 0;
599-
struct pack_midx_entry *entries_by_fanout = NULL;
670+
uint32_t alloc_objects, total_objects = 0;
671+
struct midx_fanout fanout = { 0 };
600672
struct pack_midx_entry *deduplicated_entries = NULL;
601673
uint32_t start_pack = m ? m->num_packs : 0;
602674

@@ -608,74 +680,51 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
608680
* slices to be evenly distributed, with some noise. Hence,
609681
* allocate slightly more than one 256th.
610682
*/
611-
alloc_objects = alloc_fanout = total_objects > 3200 ? total_objects / 200 : 16;
683+
alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
612684

613-
ALLOC_ARRAY(entries_by_fanout, alloc_fanout);
685+
ALLOC_ARRAY(fanout.entries, fanout.alloc);
614686
ALLOC_ARRAY(deduplicated_entries, alloc_objects);
615687
*nr_objects = 0;
616688

617689
for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
618-
uint32_t nr_fanout = 0;
619-
620-
if (m) {
621-
uint32_t start = 0, end;
622-
623-
if (cur_fanout)
624-
start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
625-
end = ntohl(m->chunk_oid_fanout[cur_fanout]);
626-
627-
for (cur_object = start; cur_object < end; cur_object++) {
628-
ALLOC_GROW(entries_by_fanout, nr_fanout + 1, alloc_fanout);
629-
nth_midxed_pack_midx_entry(m,
630-
&entries_by_fanout[nr_fanout],
631-
cur_object);
632-
if (nth_midxed_pack_int_id(m, cur_object) == preferred_pack)
633-
entries_by_fanout[nr_fanout].preferred = 1;
634-
else
635-
entries_by_fanout[nr_fanout].preferred = 0;
636-
nr_fanout++;
637-
}
638-
}
690+
fanout.nr = 0;
691+
692+
if (m)
693+
midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
694+
preferred_pack);
639695

640696
for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
641-
uint32_t start = 0, end;
642697
int preferred = cur_pack == preferred_pack;
643-
644-
if (cur_fanout)
645-
start = get_pack_fanout(info[cur_pack].p, cur_fanout - 1);
646-
end = get_pack_fanout(info[cur_pack].p, cur_fanout);
647-
648-
for (cur_object = start; cur_object < end; cur_object++) {
649-
ALLOC_GROW(entries_by_fanout, nr_fanout + 1, alloc_fanout);
650-
fill_pack_entry(cur_pack,
651-
info[cur_pack].p,
652-
cur_object,
653-
&entries_by_fanout[nr_fanout],
654-
preferred);
655-
nr_fanout++;
656-
}
698+
midx_fanout_add_pack_fanout(&fanout,
699+
info, cur_pack,
700+
preferred, cur_fanout);
657701
}
658702

659-
QSORT(entries_by_fanout, nr_fanout, midx_oid_compare);
703+
if (-1 < preferred_pack && preferred_pack < start_pack)
704+
midx_fanout_add_pack_fanout(&fanout, info,
705+
preferred_pack, 1,
706+
cur_fanout);
707+
708+
midx_fanout_sort(&fanout);
660709

661710
/*
662711
* The batch is now sorted by OID and then mtime (descending).
663712
* Take only the first duplicate.
664713
*/
665-
for (cur_object = 0; cur_object < nr_fanout; cur_object++) {
666-
if (cur_object && oideq(&entries_by_fanout[cur_object - 1].oid,
667-
&entries_by_fanout[cur_object].oid))
714+
for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
715+
if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
716+
&fanout.entries[cur_object].oid))
668717
continue;
669718

670719
ALLOC_GROW(deduplicated_entries, *nr_objects + 1, alloc_objects);
671720
memcpy(&deduplicated_entries[*nr_objects],
672-
&entries_by_fanout[cur_object],
721+
&fanout.entries[cur_object],
673722
sizeof(struct pack_midx_entry));
674723
(*nr_objects)++;
675724
}
676725
}
677726

678-
free(entries_by_fanout);
727+
free(fanout.entries);
679728
return deduplicated_entries;
680729
}
681730

t/lib-bitmap.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ midx_bitmap_partial_tests () {
440440
test_commit packed &&
441441
git repack &&
442442
test_commit loose &&
443-
git multi-pack-index write --bitmap 2>err &&
443+
git multi-pack-index write --bitmap &&
444444
test_path_is_file $midx &&
445445
test_path_is_file $midx-$(midx_checksum $objdir).bitmap
446446
'

t/t5326-multi-pack-bitmaps.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,48 @@ test_expect_success 'graceful fallback when missing reverse index' '
307307
)
308308
'
309309

310+
test_expect_success 'preferred pack change with existing MIDX bitmap' '
311+
git init preferred-pack-with-existing &&
312+
(
313+
cd preferred-pack-with-existing &&
314+
315+
test_commit base &&
316+
test_commit other &&
317+
318+
git rev-list --objects --no-object-names base >p1.objects &&
319+
git rev-list --objects --no-object-names other >p2.objects &&
320+
321+
p1="$(git pack-objects "$objdir/pack/pack" \
322+
--delta-base-offset <p1.objects)" &&
323+
p2="$(git pack-objects "$objdir/pack/pack" \
324+
--delta-base-offset <p2.objects)" &&
325+
326+
# Generate a MIDX containing the first two packs,
327+
# marking p1 as preferred, and ensure that it can be
328+
# successfully cloned.
329+
git multi-pack-index write --bitmap \
330+
--preferred-pack="pack-$p1.pack" &&
331+
test_path_is_file $midx &&
332+
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
333+
git clone --no-local . clone1 &&
334+
335+
# Then generate a new pack which sorts ahead of any
336+
# existing pack (by tweaking the pack prefix).
337+
test_commit foo &&
338+
git pack-objects --all --unpacked $objdir/pack/pack0 &&
339+
340+
# Generate a new MIDX which changes the preferred pack
341+
# to a pack contained in the existing MIDX.
342+
git multi-pack-index write --bitmap \
343+
--preferred-pack="pack-$p2.pack" &&
344+
test_path_is_file $midx &&
345+
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
346+
347+
# When the above circumstances are met, the preferred
348+
# pack should change appropriately and clones should
349+
# (still) succeed.
350+
git clone --no-local . clone2
351+
)
352+
'
353+
310354
test_done

0 commit comments

Comments
 (0)