core/src/git
git, read (#453) — THE one way Enso runs git: the run-context recorder’s revision probe
(#435) and the web server’s GET /api/threads/:threadId/git both come through runGitIn.
readGitState turns a cwd into an EnsoGitState, the last-push baseline included;
readGitChanges lists every file that differs from that baseline, with its patch (#452).
⚠ GIT IN THE GIVEN CWD, NOT THE ENVIRONMENT’S REPOSITORY. git honours an inherited GIT_DIR /
GIT_WORK_TREE / GIT_INDEX_FILE over its working directory, and git exports them to every
hook — so a server started from a hook’s shell would read the hook’s repository for every
session. The repository-locating variables are removed from the child’s environment; found
under a worktree’s pre-commit gate (#435).
⚠ READ-ONLY, AND NOTHING THE REPOSITORY CONFIGURES RUNS. Every command here only reads. The
agent works in the same tree and may be running git itself, so no query takes an optional lock
(GIT_OPTIONAL_LOCKS=0 — status would otherwise refresh the index under index.lock). A
repository’s own config can name programs git runs on a read: core.fsmonitor runs on every
status, and the agent can write .git/config. So core.fsmonitor is forced off on the
command line, the pager is cat and no prompt waits on a terminal.
⚠ BOUNDED. Each query is killed after GIT_TIMEOUT_MS; a killed or unstartable git is a
code of -1, which a reader turns into unavailable, never a hang.
⚠ node-only (node:child_process, node:fs): a subpath, @enso/core/git, never the barrel.
GitResult
Section titled “GitResult”Defined in: core/src/git.ts:58
What a git query answered. code is git’s exit code, or -1 when git could not be started or was
killed at the timeout.
Properties
Section titled “Properties”
readonlycode:number
Defined in: core/src/git.ts:59
stderr
Section titled “stderr”
readonlystderr:string
Defined in: core/src/git.ts:61
stdout
Section titled “stdout”
readonlystdout:string
Defined in: core/src/git.ts:60
GitRunner
Section titled “GitRunner”GitRunner = (
args,cwd) =>Promise<GitResult>
Defined in: core/src/git.ts:69
Runs git with args in cwd — the seam a test replaces.
Parameters
Section titled “Parameters”readonly string[]
string
Returns
Section titled “Returns”Promise<GitResult>
GIT_TIMEOUT_MS
Section titled “GIT_TIMEOUT_MS”
constGIT_TIMEOUT_MS:5000=5000
Defined in: core/src/git.ts:44
How long one git query may take.
runGit
Section titled “runGit”
construnGit:GitRunner
Defined in: core/src/git.ts:149
git under this process’s environment — the runner production uses. The environment is read per
call (the quarantine may have scrubbed it since load), and is on the environment allowlist
(scripts/lint/process-environment.ts) as the seam itself: every other caller injects one.
readGitChanges()
Section titled “readGitChanges()”readGitChanges(
runGit,repository):Promise<{files:object[];truncated:boolean; }>
Defined in: core/src/git.ts:554
Every file under repository.scope that differs from its baseline commit — commits not yet
pushed, staged and unstaged edits, deletions, renames — and every untracked file there, each with
its patch while the answer has room (MAX_* above). ⚠ Only the scope: a project inside a larger
repository (a dotfiles repository at $HOME) sends its own files, never its neighbours’. Run from
the repository’s root, so every path is the root’s. A baseline of none (no commit yet) compares
with the empty tree.
Parameters
Section titled “Parameters”runGit
Section titled “runGit”repository
Section titled “repository”number = Count
Commits on HEAD that baseline.ref does not have; 0 when there is no ref.
baseline
Section titled “baseline”{ commit: string | null; ref: string | null; rule: "head" | "upstream" | "remote-branch" | "remote-default" | "none"; } = EnsoGitBaseline
baseline.commit
Section titled “baseline.commit”string | null = ...
The baseline commit — the merge-base with ref, or HEAD; null for none.
baseline.ref
Section titled “baseline.ref”string | null = ...
The remote-tracking ref, short (origin/feature-x); null for head and none.
baseline.rule
Section titled “baseline.rule”"head" | "upstream" | "remote-branch" | "remote-default" | "none" = EnsoGitBaselineRule
behind
Section titled “behind”number = Count
Commits on baseline.ref that HEAD does not have; 0 when there is no ref.
branch
Section titled “branch”string | null = ...
The checked-out branch, short; null when HEAD is detached.
changes
Section titled “changes”{ conflicted: number; staged: number; unstaged: number; untracked: number; } = ...
Files under scope by where their changes sit (git status); one file can be staged and unstaged.
changes.conflicted
Section titled “changes.conflicted”number = Count
changes.staged
Section titled “changes.staged”number = Count
changes.unstaged
Section titled “changes.unstaged”number = Count
changes.untracked
Section titled “changes.untracked”number = Count
string | null = ...
HEAD’s commit; null before the first commit.
"repository" = ...
operation
Section titled “operation”"merge" | "rebase" | "cherry-pick" | "revert" | "bisect" | null = ...
string = ...
The working tree’s top level (git rev-parse --show-toplevel).
string = ...
Where the cwd sits in the working tree (git rev-parse --show-prefix): '' at the top level,
else a /-ended path. changes and the changed files are this directory’s alone; the commits
(ahead, behind) are the whole repository’s.
upstream
Section titled “upstream”string | null = ...
The branch’s configured upstream, short (origin/main); null when none is set.
Returns
Section titled “Returns”Promise<{ files: object[]; truncated: boolean; }>
readGitState()
Section titled “readGitState()”readGitState(
runGit,cwd):Promise<{ahead:number;baseline: {commit:string|null;ref:string|null;rule:"head"|"upstream"|"remote-branch"|"remote-default"|"none"; };behind:number;branch:string|null;changes: {conflicted:number;staged:number;unstaged:number;untracked:number; };head:string|null;kind:"repository";operation:"merge"|"rebase"|"cherry-pick"|"revert"|"bisect"|null;root:string;scope:string;upstream:string|null; } | {kind:"not-a-repository"; } | {kind:"unavailable";reason:string; }>
Defined in: core/src/git.ts:304
The git state of cwd: not-a-repository outside one, unavailable with git’s reason when
git could not answer, otherwise the repository with its last-push baseline (git-state.ts).
Parameters
Section titled “Parameters”runGit
Section titled “runGit”string
Returns
Section titled “Returns”Promise<{ ahead: number; baseline: { commit: string | null; ref: string | null; rule: "head" | "upstream" | "remote-branch" | "remote-default" | "none"; }; behind: number; branch: string | null; changes: { conflicted: number; staged: number; unstaged: number; untracked: number; }; head: string | null; kind: "repository"; operation: "merge" | "rebase" | "cherry-pick" | "revert" | "bisect" | null; root: string; scope: string; upstream: string | null; } | { kind: "not-a-repository"; } | { kind: "unavailable"; reason: string; }>
runGitIn()
Section titled “runGitIn()”runGitIn(
environment,args,cwd):Promise<GitResult>
Defined in: core/src/git.ts:125
git in cwd, under environment without its repository-locating variables and with the
read-only settings above. Exported as the test seam: a test hands in an environment, never the
process’s.
Parameters
Section titled “Parameters”environment
Section titled “environment”ProcessEnv
readonly string[]
string
Returns
Section titled “Returns”Promise<GitResult>
