git

package
v1.55.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
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 AddRemote

func AddRemote(ctx context.Context, dir, name, url string) error

AddRemote adds a named remote to the repo at dir.

func AdoptBranchRef

func AdoptBranchRef(run RefRunner, branch, newHeadSHA, recordedHeadSHA string) error

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

func BoundContext(ctx context.Context, args ...string) (context.Context, context.CancelFunc)

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

func BranchRef(branch string) string

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

func CommandWaitDelay() time.Duration

CommandWaitDelay is the cmd.WaitDelay every git subprocess must carry, whoever builds it. The commandWaitDelay comment owns why it is this wide.

func CommitAll

func CommitAll(ctx context.Context, dir, message string) error

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

func CommitAuthorEmail(ctx context.Context, dir, sha string) (string, error)

CommitAuthorEmail returns the author email for a SHA.

func CommitTime

func CommitTime(ctx context.Context, dir, sha string) (time.Time, error)

CommitTime returns the committer timestamp for a SHA in UTC.

func CopyLocalUserIdentity

func CopyLocalUserIdentity(ctx context.Context, srcDir, dstDir string) error

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

func CreateBranch(ctx context.Context, dir, name string) error

CreateBranch creates a new branch with the given name and switches to it. Fails if the branch already exists.

func CurrentBranch

func CurrentBranch(ctx context.Context, dir string) (string, error)

CurrentBranch returns the current branch name.

func DefaultBranch

func DefaultBranch(ctx context.Context, dir, remote string) string

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 Diff

func Diff(ctx context.Context, dir, base, head string) (string, error)

Diff returns the unified diff between two commits.

func DiffHead

func DiffHead(ctx context.Context, dir string) (string, error)

DiffHead returns the unified diff between HEAD and the working tree (both staged and unstaged changes).

func DiffNameOnly

func DiffNameOnly(ctx context.Context, dir, base, head string) ([]string, error)

DiffNameOnly returns the list of files changed between base and head. Output is split on newlines with empty entries removed.

func DiffStat

func DiffStat(ctx context.Context, dir, base, head string) (files, lines int, err error)

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 EnsureHooksPathIsolation

func EnsureHooksPathIsolation(ctx context.Context, bareDir string) (bool, error)

func EnsureRemote

func EnsureRemote(ctx context.Context, dir, name, url string) error

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

func FetchRemoteBranch(ctx context.Context, dir, remote, branch string) error

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

func FetchRemoteBranchToPrivateRef(ctx context.Context, dir, remote, branch, localRef string) error

FetchRemoteBranchToPrivateRef fetches one branch into a caller-owned private ref without touching FETCH_HEAD or ordinary remote-tracking refs.

func FetchRemoteBranchToRef

func FetchRemoteBranchToRef(ctx context.Context, dir, remote, branch, localRef string) error

func FindGitRoot

func FindGitRoot(path string) (string, error)

FindGitRoot walks up from path to find the git repository root. Resolves symlinks for consistency on macOS (e.g. /tmp -> /private/tmp).

func FindMainRepoRoot

func FindMainRepoRoot(path string) (string, error)

FindMainRepoRoot returns the root of the main working tree for a git repository. Three layouts are supported:

  1. A regular repository or a linked worktree: the git common dir is <root>/.git, so the main working tree is filepath.Dir(commonDir).
  2. 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).
  3. 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

func GateConfigCurrent(bareDir string) bool

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

func GetConfiguredRemoteURL(ctx context.Context, dir, name string) (string, error)

GetConfiguredRemoteURL returns the literal remote URL from git config, without applying url.*.insteadOf rewrites.

func GetConfiguredRemoteURLs

func GetConfiguredRemoteURLs(ctx context.Context, dir, name string) ([]string, error)

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

func GetRemoteURL(ctx context.Context, dir, name string) (string, error)

GetRemoteURL returns the URL of a named remote.

func HasRemote

func HasRemote(ctx context.Context, dir, name string) (bool, error)

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

func HasUncommittedChanges(ctx context.Context, dir string) (bool, error)

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 HeadSHA

func HeadSHA(ctx context.Context, dir string) (string, error)

HeadSHA returns the full SHA of HEAD.

func InitBare

func InitBare(ctx context.Context, path string) error

InitBare creates a new bare git repository at the given path.

func InspectRemoteDefaultBranch

func InspectRemoteDefaultBranch(ctx context.Context, dir, remote string) (string, []string, error)

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

func InstallPostReceiveHook(bareDir string) error

InstallPostReceiveHook writes the post-receive hook script into the hooks directory of a bare repo at bareDir.

func IsDetachedHEAD

func IsDetachedHEAD(ctx context.Context, dir string) (bool, error)

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

func IsZeroSHA(sha string) bool

IsZeroSHA returns true if the SHA is the null/zero ref that git uses for new or deleted branches (40 zeros).

func IsolateHooksPath

func IsolateHooksPath(ctx context.Context, bareDir string) error

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 Log

func Log(ctx context.Context, dir, base, head string) (string, error)

Log returns oneline log entries between two commits.

func LooksLikeBareRepository

func LooksLikeBareRepository(dir string) bool

LooksLikeBareRepository performs the non-mutating structural half of bare repository validation. Call ValidateBareRepository before mutating an unstamped directory discovered from the filesystem.

func LsRemote

func LsRemote(ctx context.Context, dir, remote, ref string) (string, error)

LsRemote returns the SHA of a ref on a remote. Returns empty string if the ref doesn't exist.

func MarkGateConfigCurrent

func MarkGateConfigCurrent(bareDir string) error

MarkGateConfigCurrent atomically records a fully completed gate migration. Callers must validate the gate and finish every mutation before marking it.

func NonInteractiveEnv

func NonInteractiveEnv(dir string) []string

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

func NonInteractiveEnvFrom(base []string, dir string) []string

NonInteractiveEnvFrom is NonInteractiveEnv applied to an explicit base environment. A nil base means the current process environment.

func Output

func Output(ctx context.Context, dir string, args ...string) (string, error)

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

func OutputBare(ctx context.Context, bareDir string, args ...string) (string, error)

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

func Push(ctx context.Context, dir, remote, ref, expectedSHA string, forceWithLease bool) error

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

func RefExists(ctx context.Context, dir, ref string) (bool, error)

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

func RefreshManagedGateHooks(bareDir string) error

RefreshManagedGateHooks owns the complete receive boundary.

func RefreshManagedPostReceiveHook

func RefreshManagedPostReceiveHook(bareDir string) (bool, error)

RefreshManagedPostReceiveHook updates an existing no-slop-owned hook. Custom hooks are left untouched; missing hooks are installed for gate repos.

func RefreshManagedPreReceiveHook

func RefreshManagedPreReceiveHook(bareDir string) (bool, error)

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

func RemoveRemote(ctx context.Context, dir, name string) error

RemoveRemote removes a named remote from the repo at dir.

func ResolveRef

func ResolveRef(ctx context.Context, dir, ref string) (string, error)

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

func Run(ctx context.Context, dir string, args ...string) (string, error)

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

func RunBare(ctx context.Context, bareDir string, args ...string) (string, error)

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 RunRaw

func RunRaw(ctx context.Context, dir string, args ...string) ([]byte, error)

RunRaw executes a git command and returns stdout without modifying its bytes.

func RunWithEnv

func RunWithEnv(ctx context.Context, dir string, extraEnv []string, args ...string) (string, error)

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

func ShowFile(ctx context.Context, dir, ref, path string) (string, error)

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

func ValidateBareRepository(ctx context.Context, bareDir string) error

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

func WorktreeAdd(ctx context.Context, repoDir, wtPath, sha string) error

WorktreeAdd creates a detached worktree at wtPath checked out to the given SHA.

func WorktreeRemove

func WorktreeRemove(ctx context.Context, repoDir, wtPath string) error

WorktreeRemove removes a worktree at the given path.

Types

type RefRunner

type RefRunner func(args ...string) (string, error)

RefRunner runs a Git command against an already-chosen repository. It exists so the adoption policy below has exactly one implementation while each caller keeps its own command environment (a pipeline step carries step-scoped PATH and credential environment; the executor does not).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL