--ignore-case in ListRefs negates the performance benefit of Gitaly-side ref search
Background
Rails currently implements ref search (Branches/Tags API + UI) by fetching all refs from Gitaly and filtering in Ruby. The cost scales with the total number of refs in the repository, not with the number of matches: for a repository with 10,000+ tags, every search request makes Gitaly build, serialize, and stream all 10,000 tags so Rails can keep ~20 of them. A customer has reported ~20 s Tags API responses because of this.
We want to move the search down to ListRefs so the filtering happens inside Gitaly and only matching refs are returned (epic &11057).
Constraint
Ref search in GitLab has always been case-insensitive, and we need to keep it that way (context).
Problem
ListRefs supports case-insensitive matching only through git's --ignore-case flag. That flag is expensive. It prevents git from using its fast ref iteration path. In our benchmarks, the slowdown from --ignore-case outweighs the gain from transferring fewer refs — so overall, searching via ListRefs ends up slower than the current fetch-everything approach:
| Legacy (fetch all + Ruby filter) | ListRefs + --ignore-case |
|
|---|---|---|
| Branches, 12k refs (benchmark) | 620 ms | 691 ms |
| Tags, 2.6k refs (benchmark) | 44 ms | 45.5 ms |
We also tried encoding case-insensitivity as character-class globs (test → [Tt][Ee][Ss][Tt]): fast, but it cannot express Unicode case rules (Turkish İ/ı, German ß → SS), so it was shipped and reverted.
Questions
- Is there a way to perform case-insensitive matching in ListRefs without the --ignore-case penalty — for example, a casefolded comparison that keeps git's optimized ref iteration?
- If not feasible in git as-is, can gitaly filter (outside of git)?, e.g. iterating refs with the fast path and matching case-insensitively in Go before streaming?
- If neither is viable, what approach would you recommend for making ref search scale with the number of matches instead of the repository size?
Impact
- Blocks Gitaly-side tag search (gitlab#372051)
- Branch search currently falls back to the legacy fetch-all finder for the same reason