Skip to content

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.

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.

readonly code: number

Defined in: core/src/git.ts:59

readonly stderr: string

Defined in: core/src/git.ts:61

readonly stdout: string

Defined in: core/src/git.ts:60


GitRunner = (args, cwd) => Promise<GitResult>

Defined in: core/src/git.ts:69

Runs git with args in cwd — the seam a test replaces.

readonly string[]

string

Promise<GitResult>


const GIT_TIMEOUT_MS: 5000 = 5000

Defined in: core/src/git.ts:44

How long one git query may take.


const runGit: 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(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.

GitRunner

number = Count

Commits on HEAD that baseline.ref does not have; 0 when there is no ref.

{ commit: string | null; ref: string | null; rule: "head" | "upstream" | "remote-branch" | "remote-default" | "none"; } = EnsoGitBaseline

string | null = ...

The baseline commit — the merge-base with ref, or HEAD; null for none.

string | null = ...

The remote-tracking ref, short (origin/feature-x); null for head and none.

"head" | "upstream" | "remote-branch" | "remote-default" | "none" = EnsoGitBaselineRule

number = Count

Commits on baseline.ref that HEAD does not have; 0 when there is no ref.

string | null = ...

The checked-out branch, short; null when HEAD is detached.

{ 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.

number = Count

number = Count

number = Count

number = Count

string | null = ...

HEAD’s commit; null before the first commit.

"repository" = ...

"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.

string | null = ...

The branch’s configured upstream, short (origin/main); null when none is set.

Promise<{ files: object[]; truncated: boolean; }>


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).

GitRunner

string

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(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.

ProcessEnv

readonly string[]

string

Promise<GitResult>