pack-bitmap: sort bitmaps before XORing

Reachability bitmaps may be stored as XORs against nearby bitmaps, up to 10 away. However, when callers provide selected commits in an arbitrary order, the writer may miss good ancestor/descendant pairs and produce much larger bitmap files without changing query coverage. Sort the selected bitmaps in date order (from oldest to newest) before computing XOR offsets, leaving pseudo-merge bitmaps alone (which we will deal with separately in following commits). On our same testing repository from previous commits, this change shrunk our selection of 1,261 bitmaps from ~635.46 MiB to 176.4 MiB for a ~72.24% reduction in the on-disk size of our *.bitmap file. The time to generate the smaller bitmap file decreased by ~3.69 seconds, though this is likely mostly noise. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed May 27, 2026 at 15:56 UTC dcccd997462e2130bcc35f933285ff087454275e
1 file changed +29
pack-bitmap-write.c
+29
@@ -327,11 +327,40 @@ missing:
327 return 0;
328 }
329
330 +static int bitmapped_commit_date_cmp(const void *_a, const void *_b)
331 +{
332 + const struct bitmapped_commit *a = _a;
333 + const struct bitmapped_commit *b = _b;
334 +
335 + if (a->commit->date < b->commit->date)
336 + return -1;
337 + if (a->commit->date > b->commit->date)
338 + return 1;
339 + return 0;
340 +}
341 +
342 static void compute_xor_offsets(struct bitmap_writer *writer)
343 {
344 static const int MAX_XOR_OFFSET_SEARCH = 10;
345
346 int i, next = 0;
347 + int nr = bitmap_writer_nr_selected_commits(writer);
348 +
349 + if (nr > 1) {
350 + QSORT(writer->selected, nr, bitmapped_commit_date_cmp);
351 +
352 + for (i = 0; i < nr; i++) {
353 + struct bitmapped_commit *stored = &writer->selected[i];
354 + khiter_t hash_pos = kh_get_oid_map(writer->bitmaps,
355 + stored->commit->object.oid);
356 +
357 + if (hash_pos == kh_end(writer->bitmaps))
358 + BUG("selected commit missing from bitmap map: %s",
359 + oid_to_hex(&stored->commit->object.oid));
360 +
361 + kh_value(writer->bitmaps, hash_pos) = stored;
362 + }
363 + }
364
365 while (next < writer->selected_nr) {
366 struct bitmapped_commit *stored = &writer->selected[next];