Expand description
The lock files a stopped Git child leaves behind, found and removed once the child and what it started are gone.
What:
The reference locks a stopped Git child may have left behind, judged and reported once the child and what it started
are gone.
Why:
Git removes its .lock files when it ends normally or on a catchable signal, but a child that is killed, or that is
in an uninterruptible call when the stop arrives, cannot. On 2026-11-03 two pushes were stopped at their deadline;
about 37 seconds later, in the same instant, empty refs/remotes/origin/*.lock files appeared for exactly their two
tracking refs, with no pid file beside them, and every later git fetch of those refs failed with “Unable to create
… File exists”. The service had started those pushes without core.lockfilePid, so no pid file was written. That
the creators were the stopped pushes, released by the kernel late, is the inference that fits these facts; nothing
observed the processes themselves.
Releases 0.5.23 to 0.5.26 removed two kinds of file after the stop: a lock whose pid file named an ended member of the
stopped tree, and an empty lock without a pid file that was born after the child started and stayed empty for a settle
time. A cross-vendor review of 2026-11-03T11:33:50-05:00 (finding S-1) showed that neither is an ownership proof: a
live writer of another Git that does not write pid files holds its lock empty with no descriptor open, for as long as
its transaction lasts, and a pid file older than a lock names an earlier holder, so both rules could delete the lock
of a Git that is running, and two writers could then update one reference. The sweep removes nothing on those rules.
It hands the repository to the same judgement that runs before every fetch and push (stale_ref_locks.rs), which
removes a reference lock only on a proof and through a conditional removal, and it says what it found and which
command clears a lock it will not remove. The judgement is given one proof of its own, which a container’s view of the
machine cannot give it: the numbers of the processes of the stopped tree that have ended. A pid file born at or after a
lock that was itself born after the child started, naming one of them, names a process the wrapper started and saw end, in
its own namespace (the container gate found the sweep leaving its own child’s locks without it).
Locks of other protocols (the index, the shallow file, object and pack locks) are not touched or mentioned.
In TS you’d write (pseudocode):
const ledger = LockLedger.beforeStart(command); const tree = await stop(child);
for (const lock of ledger.sweep(tree)) log(`removed ${lock}`);Structs§
- Lock
Ledger - What: Where a command’s Git child can leave reference locks. Why: The sweep after a stop judges the locks of this repository, the one the child worked in; the time taken before the child started tells a lock born in its life from an older one.