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 new
+46
@@ -0,0 +1,46 @@
1 +#!/bin/sh
2 +
3 +test_description="Tests performance of ref operations with many tombstones"
4 +
5 +. ./perf-lib.sh
6 +
7 +test_expect_success "setup" '
8 + git init --ref-format=reftable repo &&
9 + blob=$(echo foo | git -C repo hash-object -w --stdin) &&
10 + for i in $(test_seq 8000)
11 + do
12 + printf "create refs/tags/tag-%d %s\n" "$i" "$blob" ||
13 + return 1
14 + done >repo/input &&
15 + git -C repo update-ref --stdin <repo/input &&
16 + git -C repo for-each-ref --format="delete %(refname)" |
17 + git -C repo update-ref --stdin
18 +'
19 +
20 +test_perf "recreate refs after mass delete" '
21 + git -C repo update-ref --stdin <repo/input &&
22 + git -C repo for-each-ref --format="delete %(refname)" |
23 + git -C repo update-ref --stdin
24 +'
25 +
26 +test_expect_success "setup asymmetric" '
27 + git init --ref-format=reftable repo2 &&
28 + blob=$(echo foo | git -C repo2 hash-object -w --stdin) &&
29 + for i in $(test_seq 8000)
30 + do
31 + printf "create refs/tags/old-%d %s\n" "$i" "$blob" ||
32 + return 1
33 + done >repo2/input-old &&
34 + sed "s/old-/new-/" <repo2/input-old >repo2/input-new &&
35 + git -C repo2 update-ref --stdin <repo2/input-old &&
36 + git -C repo2 for-each-ref --format="delete %(refname)" |
37 + git -C repo2 update-ref --stdin
38 +'
39 +
40 +test_perf "create new refs after deleting differently-named refs" '
41 + git -C repo2 update-ref --stdin <repo2/input-new &&
42 + git -C repo2 for-each-ref --format="delete %(refname)" refs/tags/ |
43 + git -C repo2 update-ref --stdin
44 +'
45 +
46 +test_done