Skip to main content

Module scratch_location

Module scratch_location 

Source
Expand description

Where short-lived private files live: the per-user scratch directory and the repository scratch directory, never the system temporary directory. What: Where the wrapper keeps short-lived private files: the per-user scratch directory (<state>/cli-git/scratch) for work that has no repository in hand, and the repository scratch directory (<common-dir>/cli-git-scratch) for work inside one. Both are checked as private (a real directory of this user, mode 0700) and swept of entries older than a day each time they are prepared. Why: The system temporary directory is never used (issue 1809, repo rule XTD): which device backs it is unknowable (a small tmpfs, a per-user quota, a slow disk), and a full one broke every session. A file left by a killed process must not accumulate without bound, so every preparation of a directory also collects what is stale in it. doc/planning/cli-git-scratch-locations.md records the choice per site.

In TS you’d write (pseudocode):

const scratch = join(env.XDG_STATE_HOME ?? join(env.HOME, '.local/state'), 'cli-git', 'scratch');
await mkdir(scratch, { recursive: true, mode: 0o700 });
await sweepOlderThan(scratch, 24 * 60 * 60 * 1000);

Constants§

REPOSITORY_SCRATCH_NAME
The entry in a Git common directory that holds repository scratch.
SCRATCH_REFRESH_EVERY
How often a command that waits renews its scratch directory: a twenty-fourth of SCRATCH_STALE_AFTER, so a sweep that runs at any moment finds a waiter’s directory at most an hour old, and a directory whose waiter died stops being renewed and ages out as before. The renewal is one utimensat call an hour.
SCRATCH_STALE_AFTER
How old an entry must be before a preparation removes it: far longer than any command of the wrapper runs, so a live process never loses a file. Measured: the longest command in the commit timing log (1,051 commits, 2026-11-02T09:01:37Z to 2026-11-03T15:10:12Z) took 750.6 s and the longest worktree add in the add timing log (48 adds) 539.7 s; a day is about a hundred times the longest. The patience for an owner of another namespace (3,600 s) is far under a day. A wait for a live program’s lock has no bound of its own, so a command that waits on one for more than a day is the case this age does not cover (none in the logs came near). Kept as the coordinator’s pick under the owner’s delegation, open to the owner’s veto (doc/planning/cli-git-shipped-limits-audit.md, “Sizes and ages of the service’s own housekeeping”).

Functions§

prepare_repository_scratch
What: Create the repository scratch directory beside the common Git directory’s other private storage, check that it is private, sweep its stale entries, and return it. Why: Git input for a commit transaction is written there: it is on the repository’s own device, needs no environment variable, and is collected with the repository.
prepare_user_scratch
What: Create the per-user scratch directory with its missing parents, check that it is private, sweep its stale entries, and return it. Why: The linter and the push probe write there; the check refuses a directory another account could write.
renew
What: Sets the modification time of path to now. Why: A command that waits for a lock holder or for the commit service with no bound can outlast SCRATCH_STALE_AFTER, and the sweep removes entries by modification time; renewing the time while the command lives ties the entry’s age to the command’s liveness, with no lock and no record of the process.
scratch_file
What: An anonymous file (no name in the directory) in the per-user scratch directory of environment, open for reading and writing. Why: Child output goes to a file so that no reader thread can outlive a stopped child; the file has no name from the start (an unnamed temporary file where the system has them, otherwise a name removed at once), so a killed process leaves nothing to collect, and the system temporary directory is never used.
state_directory
What: The per-user state directory of the wrapper: cli-git under an absolute XDG_STATE_HOME, or under .local/state in the home directory. Why: The injected environment decides the location, never a fixed path; a relative XDG_STATE_HOME is ignored as the XDG rules say. It mirrors the cache directory of the version cache.
sweep_stale
What: Remove every entry of directory last modified more than max_age before now, and return how many it removed. A link is removed itself and never followed; a directory is removed with its contents. Why: A process killed between making a scratch file and removing it leaves the file behind, and nothing else would ever collect it. The directory belongs to the wrapper alone, so every entry in it is the wrapper’s; an entry modified in the future is kept, because a wrong clock must not delete live files.
user_scratch_path
What: The per-user scratch directory’s path, or the sentence that says what the environment lacks. Nothing is created. Why: Callers that run without a repository learn the location first and create it only when they need it, so a command with nothing to write never touches the disk.