chadtmiller.com/knotless
git that lives in your Atmosphere account
A branch renamed to one under its old name syncs
MergedThis splits settle's single update-ref --stdin transaction into two: first the deletes, then the updates. A branch a renamed to a/b, or back, now syncs.
I checked update_refs and settle around the change. update_refs skips an empty script, so the common case with nothing to delete adds no git call. Both transactions run under the mirror's ref lock, so no other process sees the state between them. A failure after the deletes leaves the mirror with fewer refs. The next sync recomputes from local, so it recovers. The test walks compat, compat/pdsjs, compat and checks the refs after each step. The change file is present. I didn't run the test or the checks.
8 passed, 3 skipped
A mirror couldn't follow a branch renamed to one under its old name (
compat→compat/pdsjs): every read of spec-pds that synced failed with 502 on prod today. Small fix to howsettlesets refs; a quick look is enough.Claude Opus 5.5
What changes
A mirror's refs are set in two
update-ref --stdintransactions instead of one: first the refs that are gone are deleted, then the rest are set. A brancharenamed toa/b, or back, now syncs.Why
knotless.dev logged
getRepoanswering 502 for chadtmiller.com/spec-pds at 17:22 and 17:33 UTC on 2026-11-10 withgit update-ref: fatal: cannot lock ref 'refs/heads/compat/pdsjs': 'refs/heads/compat' exists; cannot create 'refs/heads/compat/pdsjs'. git refuses to createa/bwhileaexists even when the same transaction deletesa, in either order (checked with git 2.50). Splitting the deletes out is the only way throughupdate-ref; between the two transactions each ref is either as it was or as it will be, and both run under the mirror's ref lock.How it was checked
a_branch_renamed_under_its_old_name_movessettlescompat, thencompat/pdsjs, thencompatagain, and passes. The same steps through oneupdate-ref --stdintransaction reproduce prod's error with git alone. The rest is left to CI.Shortcomings
A mirror already stuck in this state is fixed by its next sync, not before.