Documentation
¶
Index ¶
- Constants
- func AddRemote(ctx context.Context, dir, name, url string) error
- func AdoptBranchRef(run RefRunner, branch, newHeadSHA, recordedHeadSHA string) error
- func BoundContext(ctx context.Context, args ...string) (context.Context, context.CancelFunc)
- func BranchRef(branch string) string
- func CommandWaitDelay() time.Duration
- func CommitAll(ctx context.Context, dir, message string) error
- func CommitAuthorEmail(ctx context.Context, dir, sha string) (string, error)
- func CommitTime(ctx context.Context, dir, sha string) (time.Time, error)
- func CopyLocalUserIdentity(ctx context.Context, srcDir, dstDir string) error
- func CreateBranch(ctx context.Context, dir, name string) error
- func CurrentBranch(ctx context.Context, dir string) (string, error)
- func DefaultBranch(ctx context.Context, dir, remote string) string
- func Diff(ctx context.Context, dir, base, head string) (string, error)
- func DiffHead(ctx context.Context, dir string) (string, error)
- func DiffNameOnly(ctx context.Context, dir, base, head string) ([]string, error)
- func DiffStat(ctx context.Context, dir, base, head string) (files, lines int, err error)
- func EnsureHooksPathIsolation(ctx context.Context, bareDir string) (bool, error)
- func EnsureRemote(ctx context.Context, dir, name, url string) error
- func FetchRemoteBranch(ctx context.Context, dir, remote, branch string) error
- func FetchRemoteBranchToPrivateRef(ctx context.Context, dir, remote, branch, localRef string) error
- func FetchRemoteBranchToRef(ctx context.Context, dir, remote, branch, localRef string) error
- func FindGitRoot(path string) (string, error)
- func FindMainRepoRoot(path string) (string, error)
- func GateConfigCurrent(bareDir string) bool
- func GetConfiguredRemoteURL(ctx context.Context, dir, name string) (string, error)
- func GetConfiguredRemoteURLs(ctx context.Context, dir, name string) ([]string, error)
- func GetRemoteURL(ctx context.Context, dir, name string) (string, error)
- func HasRemote(ctx context.Context, dir, name string) (bool, error)
- func HasUncommittedChanges(ctx context.Context, dir string) (bool, error)
- func HeadSHA(ctx context.Context, dir string) (string, error)
- func InitBare(ctx context.Context, path string) error
- func InspectRemoteDefaultBranch(ctx context.Context, dir, remote string) (string, []string, error)
- func InstallPostReceiveHook(bareDir string) error
- func IsDetachedHEAD(ctx context.Context, dir string) (bool, error)
- func IsZeroSHA(sha string) bool
- func IsolateHooksPath(ctx context.Context, bareDir string) error
- func Log(ctx context.Context, dir, base, head string) (string, error)
- func LooksLikeBareRepository(dir string) bool
- func LsRemote(ctx context.Context, dir, remote, ref string) (string, error)
- func MarkGateConfigCurrent(bareDir string) error
- func NonInteractiveEnv(dir string) []string
- func NonInteractiveEnvFrom(base []string, dir string) []string
- func Output(ctx context.Context, dir string, args ...string) (string, error)
- func OutputBare(ctx context.Context, bareDir string, args ...string) (string, error)
- func PostReceiveHookScript() string
- func PreReceiveHookScript() string
- func Push(ctx context.Context, dir, remote, ref, expectedSHA string, forceWithLease bool) error
- func PushCommit(ctx context.Context, dir, remote, commitSHA, ref, expectedSHA string, ...) error
- func PushWithOptions(ctx context.Context, dir, remote, ref, expectedSHA string, forceWithLease bool, ...) error
- func RefExists(ctx context.Context, dir, ref string) (bool, error)
- func RefreshManagedGateHooks(bareDir string) error
- func RefreshManagedPostReceiveHook(bareDir string) (bool, error)
- func RefreshManagedPreReceiveHook(bareDir string) (bool, error)
- func RemoveRemote(ctx context.Context, dir, name string) error
- func ResolveRef(ctx context.Context, dir, ref string) (string, error)
- func Run(ctx context.Context, dir string, args ...string) (string, error)
- func RunBare(ctx context.Context, bareDir string, args ...string) (string, error)
- func RunRaw(ctx context.Context, dir string, args ...string) ([]byte, error)
- func RunWithEnv(ctx context.Context, dir string, extraEnv []string, args ...string) (string, error)
- func ShowFile(ctx context.Context, dir, ref, path string) (string, error)
- func ValidateBareRepository(ctx context.Context, bareDir string) error
- func WorktreeAdd(ctx context.Context, repoDir, wtPath, sha string) error
- func WorktreeRemove(ctx context.Context, repoDir, wtPath string) error
- type RefRunner
Constants ¶
const EmptyTreeSHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
EmptyTreeSHA is the well-known SHA of an empty tree in git. Used as a base when there is no prior commit to diff against.
Variables ¶
This section is empty.
Functions ¶
func AdoptBranchRef ¶
AdoptBranchRef moves a run's branch ref in the gate onto a head that run produced, and is the single owner of that move for every writer of a run head: the pipeline worktree is detached, so a head recorded on the run but never adopted on a ref becomes unreachable when the worktree is removed and strands the branch in pipeline custody with no working recovery.
The move is anchored the same way the force push is (see forcepush.go): refuse whenever it would drop a commit the ref already holds. The ref may legitimately be behind the recorded head (a writer that ran before the adoption existed), already at the new head, or exactly at the recorded head that a rebase is about to rewrite - none of those lose anything. What must never happen is a move away from a commit this run never saw: a second push landing on the branch mid-run moves the ref, and the rebase adoption rewrites it non-fast-forward by construction, so an unguarded write there destroys that commit outright and recreates the very gate/head mismatch the adoption exists to remove.
The write itself compare-and-swaps against the value just read, so a push that lands inside the decision window fails the caller rather than racing it. When the ref does not resolve, the create is asserted rather than assumed: the empty old value makes Git itself refuse when the ref does exist, which covers both a branch created inside the decision window and a resolution failure that only looked like absence.
func BoundContext ¶
BoundContext and CommandWaitDelay expose this package's bounding policy to the few places that must build a git subprocess themselves and so cannot come through newCommand: the pipeline steps resolve git through a step-scoped PATH (`stepGitCmd`), and the intent scanner and doctor probe run git outside a repository this package owns. They are the same daemon-lifetime exposure - a git child that never exits blocks its caller and is orphaned when the caller dies - so they must carry the same bounds rather than a second, drifting set. Anything inside this package uses newCommand instead.
BoundContext derives the tiered ceiling only when ctx carries no deadline of its own, so a caller that already bounded itself keeps its tighter pacing. The returned cancel must be deferred.
func BranchRef ¶
BranchRef normalizes a recorded run branch to a full ref name. Runs store either shape, and every ref-advancing caller must agree on one.
func CommandWaitDelay ¶
CommandWaitDelay is the cmd.WaitDelay every git subprocess must carry, whoever builds it. The commandWaitDelay comment owns why it is this wide.
func CommitAll ¶
CommitAll stages every change in the working tree and creates a single commit with the given message. Fails if there are no changes to commit.
func CommitAuthorEmail ¶
CommitAuthorEmail returns the author email for a SHA.
func CommitTime ¶
CommitTime returns the committer timestamp for a SHA in UTC.
func CopyLocalUserIdentity ¶
CopyLocalUserIdentity copies local user.name and user.email from srcDir into dstDir. Missing values in srcDir are ignored.
The write into dstDir uses per-worktree scope (`git config --worktree`) when the repository has worktree config enabled. dstDir is typically a linked worktree of the shared gate bare repo, where an unscoped `git config --local` write lands in the bare's shared config and takes <bare>/config.lock. Two runs starting concurrently on different branches of the same repo then race on that single lock and one fails with "could not lock config file ... config: File exists". Writing per-worktree puts each run's identity in its own <bare>/worktrees/<id>/config.worktree, so concurrent startups never contend. Older Git without `--worktree` support falls back to `--local`.
func CreateBranch ¶
CreateBranch creates a new branch with the given name and switches to it. Fails if the branch already exists.
func CurrentBranch ¶
CurrentBranch returns the current branch name.
func DefaultBranch ¶
DefaultBranch queries a remote to determine its default branch name. Uses git ls-remote --symref to read the remote's HEAD symref. Falls back to "main" if detection fails (e.g. empty remote, unreachable).
func DiffHead ¶
DiffHead returns the unified diff between HEAD and the working tree (both staged and unstaged changes).
func DiffNameOnly ¶
DiffNameOnly returns the list of files changed between base and head. Output is split on newlines with empty entries removed.
func DiffStat ¶
DiffStat returns the bounded size of the diff between base and head: the number of changed files and the net changed lines (insertions + deletions) from `git diff --numstat`. Binary files (numstat "-") contribute a changed file but no line count. It carries no paths or content - just two counts.
func EnsureRemote ¶
EnsureRemote sets the named remote to url, adding it when absent and updating its URL when it already exists. Idempotent, so it is safe to call when repairing or re-running an init.
func FetchRemoteBranch ¶
FetchRemoteBranch fetches a single branch into a remote-tracking ref. Uses a force-update refspec (+) so non-fast-forward updates (e.g. after a force push on the remote) are accepted instead of silently rejected.
func FetchRemoteBranchToPrivateRef ¶
FetchRemoteBranchToPrivateRef fetches one branch into a caller-owned private ref without touching FETCH_HEAD or ordinary remote-tracking refs.
func FetchRemoteBranchToRef ¶
func FindGitRoot ¶
FindGitRoot walks up from path to find the git repository root. Resolves symlinks for consistency on macOS (e.g. /tmp -> /private/tmp).
func FindMainRepoRoot ¶
FindMainRepoRoot returns the root of the main working tree for a git repository. Three layouts are supported:
- A regular repository or a linked worktree: the git common dir is <root>/.git, so the main working tree is filepath.Dir(commonDir).
- An absorbed submodule (including nested .../modules/a/modules/b): the git common dir lives under the superproject's .git/modules/... and is detached from its working tree. Git writes core.worktree when it absorbs a submodule, pointing at the working tree whose remote.origin.url is the submodule's own origin (which is what callers like init and eject need).
- Exotic GIT_DIR layouts without a core.worktree: fall back to `git rev-parse --show-toplevel` from the original path, the same answer FindGitRoot returns.
In every branch the returned path is run through filepath.EvalSymlinks when possible so callers can compare it against other symlink-resolved paths (notably on macOS, where /tmp and /private/tmp refer to the same directory). Symlink resolution failures fall back to the unresolved path, matching the historical behavior.
func GateConfigCurrent ¶
GateConfigCurrent is a subprocess-free restart check for a gate that has completed the current hook and config migration. The stamp includes the rendered managed hook and a version marker for the non-hook config contract. Bump the marker when receive or worktree config requirements change.
func GetConfiguredRemoteURL ¶
GetConfiguredRemoteURL returns the literal remote URL from git config, without applying url.*.insteadOf rewrites.
func GetConfiguredRemoteURLs ¶
GetConfiguredRemoteURLs returns every literal URL configured for a remote. Callers that require an authoritative source can reject zero or multiple values rather than letting git silently select one.
func GetRemoteURL ¶
GetRemoteURL returns the URL of a named remote.
func HasRemote ¶
HasRemote reports whether a remote named name is configured in the repo at dir, returning an error if the remote list cannot be read.
func HasUncommittedChanges ¶
HasUncommittedChanges reports whether the working tree or index differs from HEAD. Returns true if any tracked file is modified, staged, or deleted, or if there are untracked files. Equivalent to a non-empty `git status --porcelain`.
func InspectRemoteDefaultBranch ¶
InspectRemoteDefaultBranch resolves a remote's HEAD and returns every branch ref that remote advertises. A reachable remote whose HEAD symref is absent or invalid keeps the historical "main" fallback; unlike DefaultBranch, a remote access failure is returned so registration can distinguish unreachable from reachable-but-missing.
func InstallPostReceiveHook ¶
InstallPostReceiveHook writes the post-receive hook script into the hooks directory of a bare repo at bareDir.
func IsDetachedHEAD ¶
IsDetachedHEAD reports whether the working tree is in a detached-HEAD state (HEAD points at a commit rather than a branch ref). Uses `git symbolic-ref` which fails cleanly when HEAD is not a symbolic ref.
func IsZeroSHA ¶
IsZeroSHA returns true if the SHA is the null/zero ref that git uses for new or deleted branches (40 zeros).
func IsolateHooksPath ¶
IsolateHooksPath protects the gate's post-receive hook from being disabled when a pipeline subprocess (e.g. husky during `pnpm install`) runs `git config core.hookspath` from inside a linked worktree.
Linked worktrees share the bare's local config, so an unscoped `git config` write lands in <bareDir>/config and silently overrides the gate's hooks lookup. To defend against this, we enable extensions.worktreeConfig on the bare and pin core.hookspath in the bare's per-worktree config (<bareDir>/config.worktree). Per-worktree scope wins over local, so the bare's main worktree always resolves hooks to its own absolute hooks dir, regardless of what tools write to the shared config.
Enabling extensions.worktreeConfig also forces us to relocate core.bare: once the extension is on, Git requires core.bare and core.worktree to live in per-worktree scope only. If we leave core.bare=true in shared config, it leaks into linked worktrees and causes commands like `git rebase` to fail with "this operation must be run in a work tree". It also prevents provider CLIs such as gh from resolving the repo from a CI step worktree cwd.
Best-effort only: if the installed Git does not support `git config --worktree`, this returns nil without changing config.
Idempotent: safe to call on an already-configured bare repo to migrate older installs when per-worktree config is available.
func LooksLikeBareRepository ¶
LooksLikeBareRepository performs the non-mutating structural half of bare repository validation. Call ValidateBareRepository before mutating an unstamped directory discovered from the filesystem.
func LsRemote ¶
LsRemote returns the SHA of a ref on a remote. Returns empty string if the ref doesn't exist.
func MarkGateConfigCurrent ¶
MarkGateConfigCurrent atomically records a fully completed gate migration. Callers must validate the gate and finish every mutation before marking it.
func NonInteractiveEnv ¶
NonInteractiveEnv returns the environment for a subprocess that may invoke git, with git forced into a fully non-interactive mode. It is intended for cmd.Env on any subprocess that may run git (our own git calls and the coding agents we spawn).
Without these overrides, git operations such as `git rebase --continue` or `git commit` open $EDITOR to confirm a commit message, and remote operations can block on a credential prompt. In a headless agent subprocess there is no TTY, so the editor or prompt hangs until the agent times out. Pointing the editors at `true` makes git accept the existing message immediately, and GIT_TERMINAL_PROMPT=0 fails fast instead of blocking on credentials. The overrides are appended last so they win over any ambient values (exec resolves duplicate keys using the last occurrence).
Pass the same directory assigned to cmd.Dir (or "" when it is unset). When cmd.Env is left nil, os/exec injects PWD=cmd.Dir automatically; assigning cmd.Env disables that, so callers must thread the working directory through here to preserve symlinked working-directory paths (for example /tmp vs /private/tmp on macOS, which os.Getwd reports differently depending on PWD).
func NonInteractiveEnvFrom ¶
NonInteractiveEnvFrom is NonInteractiveEnv applied to an explicit base environment. A nil base means the current process environment.
func Output ¶
Output executes a git command and returns stdout without trimming it, for callers whose stdout is NUL-delimited or is blob content, where trailing whitespace is data rather than formatting.
When dir is itself a bare repository (a gate repo), the repo is named explicitly via --git-dir instead of relying on cwd-based discovery, which safe.bareRepository=explicit forbids. Agent harnesses (e.g. Claude Code) and hardened CI inject that setting, so gate operations must never depend on discovering a bare repo from the working directory (issue #362).
func OutputBare ¶
OutputBare is RunBare without the trim, and keeps its exact-directory rule.
func PostReceiveHookScript ¶
func PostReceiveHookScript() string
PostReceiveHookScript returns the shell script for the post-receive hook. The hook notifies the daemon via the CLI so it works across platforms. It resolves the gate to an absolute bare-repo path before notifying. It never blocks the push - notification failures are surfaced to stderr and appended to notify-push.log inside the bare repo.
func PreReceiveHookScript ¶
func PreReceiveHookScript() string
PreReceiveHookScript returns the fail-closed admission hook that runs before Git mutates any managed gate ref. The daemon authenticates the hook process's ancestry, so a validation-step descendant cannot bypass CLI guards with a direct push.
func Push ¶
Push pushes HEAD to a remote ref. If forceWithLease is true, it uses an explicit expected remote SHA for safe force-push.
func PushCommit ¶
func PushCommit(ctx context.Context, dir, remote, commitSHA, ref, expectedSHA string, forceWithLease bool) error
PushCommit pushes one immutable commit object to a remote ref. Unlike Push, a concurrent worktree HEAD move cannot change the source selected by git.
func PushWithOptions ¶
func PushWithOptions(ctx context.Context, dir, remote, ref, expectedSHA string, forceWithLease bool, pushOptions []string) error
PushWithOptions pushes HEAD to a remote with per-push options.
func RefExists ¶
RefExists reports whether the given ref resolves to a commit. It uses `git rev-parse --verify --quiet` so a missing ref is a clean (nil, false) result rather than a loud error.
func RefreshManagedGateHooks ¶
RefreshManagedGateHooks owns the complete receive boundary.
func RefreshManagedPostReceiveHook ¶
RefreshManagedPostReceiveHook updates an existing no-slop-owned hook. Custom hooks are left untouched; missing hooks are installed for gate repos.
func RefreshManagedPreReceiveHook ¶
RefreshManagedPreReceiveHook installs or refreshes admission while preserving a genuine user hook behind the managed wrapper. A preserved hook that matches the managed-hook signature is disarmed instead of retained as executable.
func RemoveRemote ¶
RemoveRemote removes a named remote from the repo at dir.
func ResolveRef ¶
ResolveRef returns the commit SHA that ref resolves to via `git rev-parse --verify <ref>^{commit}`. Use it to pin an exact commit (e.g. the default-branch tip just fetched) before reading a file from it, so a shared-ref worktree cannot serve a stale remote-tracking ref. Returns an error if the ref does not resolve to a commit.
func Run ¶
Run executes a git command in the given directory and returns trimmed stdout. Returns an error that includes the command and stderr on failure. It is Output plus that trim, so it carries the same bare-repository handling.
func RunBare ¶
RunBare executes Git against exactly bareDir. Unlike Run, it never falls back to cwd-based repository discovery when bareDir is malformed. Gate recovery uses this after structural validation so an invalid directory under NS_HOME cannot discover or mutate an ancestor worktree.
func RunWithEnv ¶
RunWithEnv is Run with extra KEY=VALUE entries appended to the git environment. Later entries win, and the command keeps the same bounds and bare-repository handling as Run.
func ShowFile ¶
ShowFile returns the content of path as stored at the given ref (e.g. "HEAD", "origin/main", or a SHA) via `git show <ref>:<path>`. A failure (e.g. the path is absent at the ref) is returned as the underlying git error from Run; callers that need to distinguish "absent" from a real failure should check RefExists first or inspect the error text.
func ValidateBareRepository ¶
ValidateBareRepository verifies both the filesystem shape and Git's own bare repository classification. The Git query is explicitly scoped with --git-dir, so validation itself cannot discover an ancestor repository.
func WorktreeAdd ¶
WorktreeAdd creates a detached worktree at wtPath checked out to the given SHA.