Skip to content

Commit 45fa23a

Browse files
committed
Merge branch 'tb/pack-with-duplicates' into seen
* tb/pack-with-duplicates: pack-bitmap: handle duplicate pack entries during MIDX reuse test-tool bitmap: reject packs with duplicate objects midx: verify duplicate pack entries by OID and offset packfile: recover delta cycles through duplicate entries t5308: test reverse indexes with duplicate objects
2 parents 93d9c8b + fd2739b commit 45fa23a

9 files changed

Lines changed: 595 additions & 32 deletions

builtin/pack-objects.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,12 +1188,12 @@ static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile,
11881188
size_t pos = 0;
11891189
size_t end;
11901190

1191-
if (reuse_packfile->bitmap_pos) {
1191+
if (reuse_packfile->bitmap_pos ||
1192+
reuse_packfile->bitmap_nr != reuse_packfile->p->num_objects) {
11921193
/*
1193-
* We can't reuse whole chunks verbatim out of
1194-
* non-preferred packs since we can't guarantee that
1195-
* all duplicate objects were resolved in favor of
1196-
* that pack.
1194+
* We can't reuse whole chunks verbatim from non-preferred
1195+
* packs or packs with entries missing from the bitmap because
1196+
* bitmap and pack positions may differ.
11971197
*
11981198
* Even if we have a whole eword_t worth of bits that
11991199
* could be reused, there may be objects between the
@@ -1202,7 +1202,7 @@ static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile,
12021202
* pack, causing us to send duplicate or unwanted
12031203
* objects.
12041204
*
1205-
* Handle non-preferred packs from within
1205+
* Handle these packs from within
12061206
* write_reused_pack(), which inspects and reuses
12071207
* individual bits.
12081208
*/
@@ -1269,20 +1269,18 @@ static void write_reused_pack(struct bitmapped_pack *reuse_packfile,
12691269
if (pos + offset >= reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr)
12701270
goto done;
12711271

1272-
if (reuse_packfile->bitmap_pos) {
1272+
if (reuse_packfile->bitmap_pos ||
1273+
reuse_packfile->bitmap_nr != reuse_packfile->p->num_objects) {
12731274
/*
1274-
* When doing multi-pack reuse on a
1275-
* non-preferred pack, translate bit positions
1276-
* from the MIDX pseudo-pack order back to their
1277-
* pack-relative positions before attempting
1278-
* reuse.
1275+
* Translate MIDX bitmap positions which do not
1276+
* correspond directly to physical pack positions.
12791277
*/
12801278
struct multi_pack_index *m = reuse_packfile->from_midx;
12811279
uint32_t midx_pos;
12821280
off_t pack_ofs;
12831281

12841282
if (!m)
1285-
BUG("non-zero bitmap position without MIDX");
1283+
BUG("cannot translate bitmap position without MIDX");
12861284

12871285
midx_pos = pack_pos_to_midx(m, pos + offset);
12881286
pack_ofs = nth_midxed_offset(m, midx_pos);

midx.c

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,48 @@ static int compare_pair_pos_vs_id(const void *_a, const void *_b)
906906
return b->pack_int_id - a->pack_int_id;
907907
}
908908

909+
/*
910+
* Return whether the pack index contains an entry with both "oid" and
911+
* "offset". A pack index may contain duplicate OIDs, so an arbitrary
912+
* OID lookup is not enough to validate a particular offset.
913+
*
914+
* Do not use offset_to_pack_pos() here: it may consult an optional '.rev'
915+
* file, which is verified separately, or build an in-memory reverse index
916+
* that remains attached to the pack. Verify the MIDX directly against its
917+
* source pack index instead.
918+
*/
919+
static int pack_index_has_oid_at_offset(struct packed_git *p,
920+
const struct object_id *oid,
921+
off_t offset)
922+
{
923+
struct object_id candidate;
924+
uint32_t pos, i;
925+
926+
if (!bsearch_pack(oid, p, &pos))
927+
return 0;
928+
929+
if (nth_packed_object_offset(p, pos) == offset)
930+
return 1;
931+
932+
for (i = pos; i > 0; i--) {
933+
if (nth_packed_object_id(&candidate, p, i - 1) ||
934+
!oideq(&candidate, oid))
935+
break;
936+
if (nth_packed_object_offset(p, i - 1) == offset)
937+
return 1;
938+
}
939+
940+
for (i = pos + 1; i < p->num_objects; i++) {
941+
if (nth_packed_object_id(&candidate, p, i) ||
942+
!oideq(&candidate, oid))
943+
break;
944+
if (nth_packed_object_offset(p, i) == offset)
945+
return 1;
946+
}
947+
948+
return 0;
949+
}
950+
909951
/*
910952
* Limit calls to display_progress() for performance reasons.
911953
* The interval here was arbitrarily chosen.
@@ -1015,7 +1057,6 @@ int verify_midx_file(struct odb_source_packed *source, unsigned flags)
10151057
for (i = 0; i < m->num_objects + m->num_objects_in_base; i++) {
10161058
struct object_id oid;
10171059
struct pack_entry e;
1018-
off_t m_offset, p_offset;
10191060

10201061
if (i > 0 && pairs[i-1].pack_int_id != pairs[i].pack_int_id &&
10211062
nth_midxed_pack(m, pairs[i-1].pack_int_id)) {
@@ -1040,12 +1081,16 @@ int verify_midx_file(struct odb_source_packed *source, unsigned flags)
10401081
break;
10411082
}
10421083

1043-
m_offset = e.offset;
1044-
p_offset = find_pack_entry_one(&oid, e.p);
1045-
1046-
if (m_offset != p_offset)
1047-
midx_report(_("incorrect object offset for oid[%d] = %s: %"PRIx64" != %"PRIx64),
1048-
pairs[i].pos, oid_to_hex(&oid), m_offset, p_offset);
1084+
/*
1085+
* Check that the exact offset recorded in the MIDX
1086+
* belongs to this OID. A pack index may contain
1087+
* duplicate OIDs, in which case an arbitrary OID lookup
1088+
* can return a different, equally valid copy than the
1089+
* one selected by the MIDX writer.
1090+
*/
1091+
if (!pack_index_has_oid_at_offset(e.p, &oid, e.offset))
1092+
midx_report(_("incorrect object offset for oid[%d] = %s: %"PRIx64),
1093+
pairs[i].pos, oid_to_hex(&oid), e.offset);
10491094

10501095
midx_display_sparse_progress(progress, i + 1);
10511096
}

pack-bitmap.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,7 +2388,8 @@ static void reuse_partial_packfile_from_bitmap_1(struct bitmap_index *bitmap_git
23882388
struct pack_window *w_curs = NULL;
23892389
size_t pos = pack->bitmap_pos / BITS_IN_EWORD;
23902390

2391-
if (allow_ref_delta && !pack->bitmap_pos) {
2391+
if (allow_ref_delta && !pack->bitmap_pos &&
2392+
pack->bitmap_nr == pack->p->num_objects) {
23922393
/*
23932394
* If we're processing the first (in the case of a MIDX, the
23942395
* preferred pack) or the only (in the case of single-pack
@@ -2406,6 +2407,9 @@ static void reuse_partial_packfile_from_bitmap_1(struct bitmap_index *bitmap_git
24062407
* When REF_DELTAs are allowed, we can therefore reuse whole
24072408
* words at a time without inspecting object headers. Otherwise,
24082409
* inspect each object below to avoid reusing a REF_DELTA entry.
2410+
*
2411+
* The range must also contain every physical pack entry so that
2412+
* bitmap and pack positions correspond.
24092413
*/
24102414
while (pos < result->word_alloc &&
24112415
pos < pack->bitmap_nr / BITS_IN_EWORD &&
@@ -2536,21 +2540,39 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
25362540
} else {
25372541
struct packed_git *pack;
25382542
uint32_t pack_int_id;
2543+
uint32_t bitmap_nr;
25392544

25402545
if (bitmap_is_midx(bitmap_git)) {
2546+
struct bitmapped_pack bitmapped_pack;
25412547
struct multi_pack_index *m = bitmap_git->midx;
25422548
uint32_t preferred_pack_pos;
25432549

25442550
while (m->base_midx)
25452551
m = m->base_midx;
25462552

2553+
if (!m->chunk_bitmapped_packs) {
2554+
/*
2555+
* Without BTMP, determining the preferred
2556+
* pack's range requires scanning the pseudo-pack.
2557+
* Skip reuse and leave the bitmap result for
2558+
* normal packing.
2559+
*/
2560+
return;
2561+
}
2562+
25472563
if (midx_preferred_pack(m, &preferred_pack_pos) < 0) {
25482564
warning(_("unable to compute preferred pack, disabling pack-reuse"));
25492565
return;
25502566
}
25512567

25522568
pack = nth_midxed_pack(m, preferred_pack_pos);
25532569
pack_int_id = preferred_pack_pos;
2570+
2571+
if (nth_bitmapped_pack(m, &bitmapped_pack,
2572+
pack_int_id) < 0)
2573+
return;
2574+
pack = bitmapped_pack.p;
2575+
bitmap_nr = bitmapped_pack.bitmap_nr;
25542576
} else {
25552577
pack = bitmap_git->pack;
25562578
/*
@@ -2563,13 +2585,14 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
25632585
* that we do not expect to read this field.
25642586
*/
25652587
pack_int_id = -1;
2588+
bitmap_nr = pack->num_objects;
25662589
}
25672590

25682591
if (is_pack_valid(pack)) {
25692592
ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
25702593
packs[packs_nr].p = pack;
25712594
packs[packs_nr].pack_int_id = pack_int_id;
2572-
packs[packs_nr].bitmap_nr = pack->num_objects;
2595+
packs[packs_nr].bitmap_nr = bitmap_nr;
25732596
packs[packs_nr].bitmap_pos = 0;
25742597
packs[packs_nr].from_midx = bitmap_git->midx;
25752598
packs_nr++;

0 commit comments

Comments
 (0)