t/perf: add perf test for ref tombstone scenarios

Add performance tests for update-ref when many tombstones are present in a reftable. The first test exercises the scenario where all refs are deleted (creating tombstones) and then re-created with the same names, which currently exhibits quadratic behavior. The second test uses a separate repository with an asymmetric variant where refs are deleted and then new, differently-named refs are created. When the tombstones sort after the new refs, every create scans all tombstones, making this case even worse than re-creating the same refs. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Kristofer Karlsson committed Jul 10, 2026 at 10:36 UTC 13d8160f4922d88aa26eb2eea74cab3fa29848b8
1 file changed +46
t/perf/p1401-ref-store-tombstones.sh
+46
new file mode 100755 index 0000000000..9e3d8031aa --- /dev/null +++ b/t/perf/p1401-ref-store-tombstones.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +test_description="Tests performance of ref operations with many tombstones" + +. ./perf-lib.sh + +test_expect_success "setup" ' + git init --ref-format=reftable repo && + blob=$(echo foo | git -C repo hash-object -w --stdin) && + for i in $(test_seq 8000) + do + printf "create refs/tags/tag-%d %s\n" "$i" "$blob" || + return 1 + done >repo/input && + git -C repo update-ref --stdin <repo/input && + git -C repo for-each-ref --format="delete %(refname)" | + git -C repo update-ref --stdin +' + +test_perf "recreate refs after mass delete" ' + git -C repo update-ref --stdin <repo/input && + git -C repo for-each-ref --format="delete %(refname)" | + git -C repo update-ref --stdin +' + +test_expect_success "setup asymmetric" ' + git init --ref-format=reftable repo2 && + blob=$(echo foo | git -C repo2 hash-object -w --stdin) && + for i in $(test_seq 8000) + do + printf "create refs/tags/old-%d %s\n" "$i" "$blob" || + return 1 + done >repo2/input-old && + sed "s/old-/new-/" <repo2/input-old >repo2/input-new && + git -C repo2 update-ref --stdin <repo2/input-old && + git -C repo2 for-each-ref --format="delete %(refname)" | + git -C repo2 update-ref --stdin +' + +test_perf "create new refs after deleting differently-named refs" ' + git -C repo2 update-ref --stdin <repo2/input-new && + git -C repo2 for-each-ref --format="delete %(refname)" refs/tags/ | + git -C repo2 update-ref --stdin +' + +test_done