Expand description
The index locks the migration holds, marked with a token that names the migration.
What:
The index locks the migration holds: one index.lock file for the main index and one for the index of each linked
worktree, each carrying a token that names this migration.
Why:
Git writes an index by creating index.lock exclusively, writing it and renaming it over index. A migration that
swaps the checkout directory under such a writer, or copies a half-written lock into the new tree, damages the
index. So the migration creates the lock files itself, in the old tree before its last copy and in the new tree
before the exchange, and keeps them until the indexes are rewritten. The token makes a lock the migration’s own:
a lock file that exists when the migration tries to create one is accepted only if it holds this migration’s token,
so another Git command’s lock is never mistaken for it, never exempted from the refusals, and never removed.
The linked worktrees’ administrative directories live inside .git, so their locks and indexes travel with the
main checkout whether or not the worktree directory is inside it.
Why these locks stay:
They are Git’s own protocol for writing an index, not a lock of this wrapper’s invention: Git creates index.lock
exclusively and treats its presence as “an index writer is active”, so no design that leaves the name alone can keep
Git out of an index while the checkout directory is exchanged. Each lock covers one index (never the repository), is
created with O_EXCL and never waited for (a foreign lock stops the migration with the file named), and is held from
before the last copy to the end of the index rewrite, which is the span in which a Git writer could be lost in the
exchange. Which command may migrate a checkout at all is not decided here: single_owner_claim.rs decides that
without a lock. Owner rule: concurrency is lock-free first; a lock that must stay covers the smallest unit, with its
reason beside it.
In TS you’d write (pseudocode):
for (const lock of lockPaths(tree)) await acquire(lock, token); ... for (const lock of lockPaths(tree)) await release(lock, token);Functions§
- acquire
- What: Create the lock file with the token, or accept one that already holds the token. Why: Another Git command’s lock means that command is writing an index now; the migration stops and says which file.
- is_ours
- Whether the lock file holds the token: it is this migration’s.
- lock_
paths - What: The lock file paths of a tree: the main index’s and one per administrative directory of a linked worktree, sorted. Why: Every index that a Git command could rewrite while the migration runs.
- relative_
lock_ paths - What: The lock file paths of a tree relative to it. Why: The tree passes leave these paths alone, in both trees: they are never copied and never deleted.
- release_
all - What: Remove every lock file of the tree that holds the token; report how many were removed. Why: Only the migration’s own locks are released; a lock that holds other text belongs to a Git command.
- token_
of - What: The text written into every lock file of this migration. Why: The journal’s start time is unique to the migration and survives a kill, so a rerun recognises the locks of the run it resumes and nobody else’s.