checkpoint: resolve, write, and list case-folded shard refs

Commit

Stefan Haubold3d ago

#2403 made ParseRef tolerate a checkpoint ref stored under the other spelling of its shard directory, which fixes checkpoint list. Every other consumer goes the other way β€” RefName(cid) then repo.Reference β€” and there it was the filesystem masking the collision, not ParseRef: a case-insensitive filesystem resolves the canonical name onto the folded directory, but only while the ref is loose. git pack-refs (git gc --auto runs it) moves the ref into packed-refs under its stored name, where lookup is an exact string match.

resolveLocalRef falls back to the one alternate-cased spelling, which is exhaustive rather than a heuristic: each ID format uses one case exclusively, so a shard bucket has exactly two possible names. That fixes Read (and so checkpoint explain <id>), refBase, and GetCheckpointAuthor.

writeRefName then targets the spelling an existing ref actually uses, so a write extends the checkpoint instead of forking it. A tolerant read on its own is not enough: refBase would find the old ref and hand back its tip while the CAS created a second ref at the canonical name β€” and the CAS could not succeed there anyway, since git resolves the expected old value under the name being updated.

List dedupes the two spellings for repos a pre-fix CLI already forked, preferring the canonical one so the listing agrees with what Read serves.

doctor reports both conditions. It offers no rename for a folded ref: on the filesystem that produces it, update-ref to the canonical name and update-ref -d on the folded one hit the same file while the refs are loose, so the pair that looks like a rename is a delete.

Refs #2402

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com Entire-Checkpoint: 01M2JP7ZYMCG44RMG2RGZS7304