Current behavior 😯
When updating a file ref, gitoxide reads the current value to check against the Edit, for example PreviousValue::MustExistAndMatch() before locking that ref for updates. This means multiple concurrent edits can overwrite each other silently, and the returned value that's meant to show what got overwritten will be incorrect.
This is particularly important when multiple concurrent pushes are using --force-with-lease and the ref updates are being handled server-side by gitoxide. Two pushes could read the same ref, then both try to lock, and then both update. Only one of them should have been successful, and someone's push looked successful but was then silently thrown away 😱
I have an Opus-written fix for this, and am happy to open a PR. Basically it comes down to this block beiing moved into a function that can then be called whilst holding the lock:
-
- Must then be called in both arms of the match for Delete as-well-as Update
Expected behavior 🤔
I would expect lock_ref_and_apply_change to be "safe" in concurrent environments, in the sense that it would compare against the same value it was overwriting.
Git behavior
No response
Steps to reproduce 🕹
It's kind of a fiddly one tbh. There's a test in mizzle that reproduces it quite consistently on the slow github actions runners: https://github.com/willstott101/mizzle/actions/runs/25799779550/job/75786315209
But the broad steps are:
- open a repo multiple times in separate threads
- try to update the refs with constraints like
MustExistAndMatch where they all expect the same initial value
- the bug is surfaced if more than one thread returns as successfully updating the ref
Current behavior 😯
When updating a file ref, gitoxide reads the current value to check against the
Edit, for examplePreviousValue::MustExistAndMatch()before locking that ref for updates. This means multiple concurrent edits can overwrite each other silently, and the returned value that's meant to show what got overwritten will be incorrect.This is particularly important when multiple concurrent pushes are using
--force-with-leaseand the ref updates are being handled server-side by gitoxide. Two pushes could read the same ref, then both try to lock, and then both update. Only one of them should have been successful, and someone's push looked successful but was then silently thrown away 😱I have an Opus-written fix for this, and am happy to open a PR. Basically it comes down to this block beiing moved into a function that can then be called whilst holding the lock:
gitoxide/gix-ref/src/store/file/transaction/prepare.rs
Line 29 in 87433ed
Expected behavior 🤔
I would expect
lock_ref_and_apply_changeto be "safe" in concurrent environments, in the sense that it would compare against the same value it was overwriting.Git behavior
No response
Steps to reproduce 🕹
It's kind of a fiddly one tbh. There's a test in mizzle that reproduces it quite consistently on the slow github actions runners: https://github.com/willstott101/mizzle/actions/runs/25799779550/job/75786315209
But the broad steps are:
MustExistAndMatchwhere they all expect the same initial value