Documentation
¶
Overview ¶
Package git wraps all git CLI interactions. No other package shells out to git.
Index ¶
- Constants
- func ArchiveSpec(ctx context.Context, cfg *config.SpecsRepoConfig, specID, archiveDir string) error
- func BranchExists(ctx context.Context, dir, branch string) bool
- func CheckoutBranch(ctx context.Context, dir, branch string) error
- func Clone(ctx context.Context, url, dir string) error
- func Commit(ctx context.Context, dir, message string) error
- func CommitsBehind(ctx context.Context, dir, remoteBranch string) (int, error)
- func CommittedFiles(ctx context.Context, dir, ref string) ([]string, error)
- func ConfigGet(ctx context.Context, dir, key string) (string, error)
- func CreateBranch(ctx context.Context, dir, branch string) error
- func CurrentBranch(ctx context.Context, dir string) (string, error)
- func DetectSpecFromBranch(ctx context.Context, dir string) string
- func Diff(ctx context.Context, dir, baseRef string) (string, error)
- func DiffNameOnly(ctx context.Context, dir, refA, refB string) ([]string, error)
- func DiffStaged(ctx context.Context, dir string) (string, error)
- func EnsureSpecsRepo(ctx context.Context, cfg *config.SpecsRepoConfig) (string, error)
- func Fetch(ctx context.Context, dir string) error
- func FlushQueue(ctx context.Context, cfg *config.SpecsRepoConfig, opts SyncOptions)
- func HasChanges(ctx context.Context, dir string) (bool, error)
- func HasUnpushedCommits(ctx context.Context, dir, remoteBranch string) (bool, error)
- func IsGitRepo(dir string) bool
- func IsSectionConflict(err error) bool
- func ListArchiveFiles(cfg *config.SpecsRepoConfig, archiveDir string) ([]string, error)
- func ListSpecFiles(cfg *config.SpecsRepoConfig) ([]string, error)
- func ListTriageFiles(cfg *config.SpecsRepoConfig) ([]string, error)
- func Log(ctx context.Context, dir string, n int, format string) (string, error)
- func Push(ctx context.Context, dir, branch string) error
- func PushLocalEdits(ctx context.Context, cfg *config.SpecsRepoConfig, commitMsg string) (bool, error)
- func PushLocalEditsOpts(ctx context.Context, cfg *config.SpecsRepoConfig, commitMsg string, ...) (bool, error)
- func ReadSpecFile(cfg *config.SpecsRepoConfig, filename string) ([]byte, error)
- func Rebase(ctx context.Context, dir, ref string) error
- func RebaseAbort(ctx context.Context, dir string)
- func ResetHard(ctx context.Context, dir, ref string) error
- func RestoreSpec(ctx context.Context, cfg *config.SpecsRepoConfig, specID, archiveDir string) error
- func RevParse(ctx context.Context, dir, ref string) (string, error)
- func Run(ctx context.Context, dir string, args ...string) (string, error)
- func SetReadSurface(surface string)
- func SetRecorder(r Recorder)
- func SpecBranchName(specID string, stepNumber int, slug string) string
- func SpecFilePath(cfg *config.SpecsRepoConfig, filename string) string
- func SpecsRepoDir(cfg *config.SpecsRepoConfig) string
- func SpecsRepoURL(cfg *config.SpecsRepoConfig) string
- func Status(ctx context.Context, dir string) (string, error)
- func TriageFilePath(cfg *config.SpecsRepoConfig, filename string) string
- func UserEmail(ctx context.Context) string
- func UserName(ctx context.Context) string
- func WithSpecsRepo(ctx context.Context, cfg *config.SpecsRepoConfig, ...) error
- func WithSpecsRepoOpts(ctx context.Context, cfg *config.SpecsRepoConfig, opts SyncOptions, ...) error
- type AuditEvent
- type BranchInfo
- type Freshness
- type QueuedItem
- type Recorder
- type SyncOptions
Constants ¶
const ( OpFetch = "fetch" OpCommit = "commit" OpPush = "push" OpRecover = "recover" OpQueueFlush = "queue-flush" )
Sync audit operation kinds recorded via the injected Recorder.
const ( OutcomeOK = "ok" OutcomeQueued = "queued" OutcomeConflict = "conflict" OutcomeError = "error" )
Sync outcomes mirrored from the store layer so callers in git don't need to import internal/store (adapter-isolation: only internal/store touches SQLite).
const ( // SpecsSubDir is the sub-directory within the specs repo where spec // files are stored. All spec, triage, and archive content lives under // this path. SpecsSubDir = "specs" )
Variables ¶
This section is empty.
Functions ¶
func ArchiveSpec ¶ added in v0.8.0
ArchiveSpec moves a spec from specs/ to archive/ and commits the change.
func BranchExists ¶
BranchExists checks if a branch exists locally.
func CheckoutBranch ¶
CheckoutBranch checks out an existing branch.
func CommitsBehind ¶ added in v0.12.0
CommitsBehind returns how many commits the local HEAD is behind the remote branch (i.e. new upstream commits not yet on HEAD). Returns 0 if the remote ref is unknown.
func CommittedFiles ¶ added in v0.7.0
CommittedFiles returns the list of files changed in the given commit.
func CreateBranch ¶
CreateBranch creates and checks out a new branch.
func CurrentBranch ¶
CurrentBranch returns the current branch name.
func DetectSpecFromBranch ¶
DetectSpecFromBranch attempts to detect the spec ID from the current branch.
func DiffNameOnly ¶ added in v0.7.0
DiffNameOnly returns file paths changed between two refs.
func DiffStaged ¶
DiffStaged returns the diff of staged changes.
func EnsureSpecsRepo ¶
EnsureSpecsRepo clones the specs repo if not present, otherwise fetches the latest under a SHARED (read) lock. It is the non-destructive read path: it never runs `reset --hard` and never blocks on or discards local edits, so a working tree with in-flight changes survives a read command untouched (SPEC-013 axes 1–3). The destructive reset is reserved for the mutate path (WithSpecsRepo). A fetch is skipped entirely when one completed within the freshness TTL.
func FlushQueue ¶ added in v0.12.0
func FlushQueue(ctx context.Context, cfg *config.SpecsRepoConfig, opts SyncOptions)
FlushQueue drains queued (offline / contention-exhausted) pushes for the specs repo. It is best-effort and non-fatal: it is invoked opportunistically by read and mutate paths and by `spec status`. Each queued entry is reconciled independently — a same-section conflict marks only that entry needs-resolution and never strands the rest (SPEC-013 §Decision 010, AC-24).
Because autoRecover already pushes any unpushed commits at the start of every committing operation, the common case is that the branch is pushable; FlushQueue resolves the queued markers once the branch lands.
func HasChanges ¶
HasChanges returns true if there are uncommitted changes.
func HasUnpushedCommits ¶ added in v0.7.0
HasUnpushedCommits returns true if there are local commits not on the remote branch.
func IsSectionConflict ¶ added in v0.12.0
IsSectionConflict reports whether err is (or wraps) a genuine same-section collision — the only push failure that must block a human (vs. a queued transient/contention failure, which is non-fatal).
func ListArchiveFiles ¶
func ListArchiveFiles(cfg *config.SpecsRepoConfig, archiveDir string) ([]string, error)
ListArchiveFiles returns all archived spec files.
func ListSpecFiles ¶
func ListSpecFiles(cfg *config.SpecsRepoConfig) ([]string, error)
ListSpecFiles returns all spec files in the specs/ directory of the specs repo, resolved from the fetched remote ref so the listing reflects upstream without a working-tree reset. Falls back to the working tree on error.
func ListTriageFiles ¶
func ListTriageFiles(cfg *config.SpecsRepoConfig) ([]string, error)
ListTriageFiles returns all triage files in the specs/triage/ directory.
func PushLocalEdits ¶
func PushLocalEdits(ctx context.Context, cfg *config.SpecsRepoConfig, commitMsg string) (bool, error)
PushLocalEdits commits any uncommitted changes in the specs repo and pushes them. Unlike WithSpecsRepo, which resets to remote state before applying a mutation, PushLocalEdits preserves existing local edits — it is the backing implementation for `spec push`. Returns true if changes were found and pushed. On a push conflict it fetches and rebases rather than hard-resetting, preserving the committed local work.
func PushLocalEditsOpts ¶ added in v0.12.0
func PushLocalEditsOpts(ctx context.Context, cfg *config.SpecsRepoConfig, commitMsg string, opts SyncOptions) (bool, error)
PushLocalEditsOpts is PushLocalEdits with surface/trigger attribution. Unlike WithSpecsRepoOpts it preserves already-committed local work on a conflict abort — it must never hard-reset away the user's pushed-intent commits (SPEC-013 §7.1 / §7.2). It shares the identical section-aware conflict check so `spec push` can no longer silently auto-merge concurrent same-section edits (closing the previously-unguarded path).
func ReadSpecFile ¶
func ReadSpecFile(cfg *config.SpecsRepoConfig, filename string) ([]byte, error)
ReadSpecFile reads a spec file from the specs repo. It resolves content from the fetched remote ref (`git show origin/<branch>:specs/<file>`) so it reflects upstream without touching the working tree (SPEC-013 axis 2). It falls back to the on-disk working-tree copy when the ref read fails (e.g. a brand-new local file not yet committed).
func RebaseAbort ¶ added in v0.7.0
RebaseAbort cancels an in-progress rebase, restoring the repo to its pre-rebase state. Safe to call even if no rebase is in progress.
func RestoreSpec ¶ added in v0.8.0
RestoreSpec moves a spec from archive/ back to specs/ and commits the change.
func Run ¶
Run executes a git command in the given directory with a timeout. If ctx has no deadline, the defaultTimeout is applied automatically.
func SetReadSurface ¶ added in v0.12.0
func SetReadSurface(surface string)
SetReadSurface sets the surface label attributed to read-path fetches.
func SetRecorder ¶ added in v0.12.0
func SetRecorder(r Recorder)
SetRecorder injects the process-wide recorder used by the read path for freshness bookkeeping. Callers that own a store inject it once at startup. Passing nil resets to a no-op. git never imports the store directly — the recorder is the only bridge (AGENTS.md adapter-isolation rules).
func SpecBranchName ¶
SpecBranchName generates a branch name for a build step.
func SpecFilePath ¶
func SpecFilePath(cfg *config.SpecsRepoConfig, filename string) string
SpecFilePath returns the absolute path to a spec file in the specs repo.
func SpecsRepoDir ¶
func SpecsRepoDir(cfg *config.SpecsRepoConfig) string
SpecsRepoDir returns the local path for the specs repo clone.
func SpecsRepoURL ¶
func SpecsRepoURL(cfg *config.SpecsRepoConfig) string
SpecsRepoURL returns the clone URL for the specs repo. If a token is configured, it is embedded in the URL for passwordless auth.
func TriageFilePath ¶
func TriageFilePath(cfg *config.SpecsRepoConfig, filename string) string
TriageFilePath returns the absolute path to a triage file.
func WithSpecsRepo ¶
func WithSpecsRepo(ctx context.Context, cfg *config.SpecsRepoConfig, mutate func(repoPath string) (commitMsg string, err error)) error
WithSpecsRepo is the legacy entry point: a committing operation attributed to the CLI with no audit recorder. Prefer WithSpecsRepoOpts for surface and trigger attribution.
func WithSpecsRepoOpts ¶ added in v0.12.0
func WithSpecsRepoOpts(ctx context.Context, cfg *config.SpecsRepoConfig, opts SyncOptions, mutate func(repoPath string) (commitMsg string, err error)) error
WithSpecsRepoOpts runs a committing operation through the full lifecycle: exclusive-lock the local critical section (fetch → recover → mutate → commit), release it, then push under a separate narrower push-lock with the section-aware conflict check, randomized backoff, and queue-on-exhaustion.
The exclusive lock covers only the local work, so a shared-lock reader (notably the same-host MCP agent) waits milliseconds, never the network push (SPEC-013 §Decision 008, AC-21). The commit is durable before the lock is released, so a reader that proceeds during the push sees committed state.
Cross-teammate safety is git's atomic push + the section-aware rebase-retry, NOT the lock (which is single-host only).
Types ¶
type AuditEvent ¶ added in v0.12.0
type AuditEvent struct {
Op string
Actor string
Surface string
Trigger string
SpecID string
Outcome string
Detail string
}
AuditEvent is one sync action to record. It is surface/trigger attributed so the audit log can answer "what synced, when, by whom, via which surface".
type BranchInfo ¶
type BranchInfo struct {
SpecNumber string // e.g., "042"
StepNumber string // e.g., "1"
Slug string // e.g., "token-bucket"
}
BranchInfo represents parsed branch name information.
func ParseSpecBranch ¶
func ParseSpecBranch(branch string) *BranchInfo
ParseSpecBranch extracts spec and step info from a branch name. Returns nil if the branch doesn't match the spec branch pattern.
type Freshness ¶ added in v0.12.0
type Freshness struct {
LastFetch time.Time // zero if never recorded
CommitsBehind int // new upstream commits since HEAD
QueuedPushes int // committed-but-unpushed operations awaiting flush
}
Freshness summarizes how current the local view of the specs repo is.
func SyncFreshness ¶ added in v0.12.0
SyncFreshness reports the freshness/health of the specs-repo clone for the `spec status` line (AC-9). It does not fetch — it reads the cached last-fetch timestamp and counts new upstream commits against the already-fetched ref.
type QueuedItem ¶ added in v0.12.0
QueuedItem is a committed-but-unpushed operation awaiting flush, surfaced to git's flush loop without exposing the store's row type.
type Recorder ¶ added in v0.12.0
type Recorder interface {
// Record appends one audit event. Implementations must not fail the
// surrounding sync operation on a recording error.
Record(ev AuditEvent)
// Enqueue records a committed-but-unpushed operation for later flush.
Enqueue(repoKey, branch, commitSHA string, ev AuditEvent)
// Pending returns flushable queued items for a repo, oldest first.
Pending(repoKey string) []QueuedItem
// ResolveQueued removes a successfully-flushed queued item.
ResolveQueued(id int64)
// MarkQueued flags a queued item (e.g. needs-resolution on conflict).
MarkQueued(id int64, status, detail string)
// LastFetch / SetLastFetch back the freshness TTL.
LastFetch(repoKey string) (seconds int64, ok bool)
SetLastFetch(repoKey string, seconds int64)
}
Recorder persists sync audit events and manages the queued-push backlog. It is injected into internal/git as an interface so git never imports internal/store directly (AGENTS.md: engines depend on interfaces; only internal/store touches SQLite).
type SyncOptions ¶ added in v0.12.0
type SyncOptions struct {
Surface string
Trigger string
SpecID string
Actor string
Recorder Recorder
}
SyncOptions carries surface/trigger attribution and an optional recorder through a committing operation. A zero value is valid: it attributes to the CLI with a no-op recorder.