Documentation
¶
Index ¶
- Variables
- func AllChangedFiles(s git.Status) []string
- func AllDirtyPaths(s git.Status) []string
- func CountCommitsBetween(repo *git.Repository, fromRef, toRef string) (int, error)
- func DiffStats(repo *git.Repository, fromRef, toRef string) (files, insertions, deletions int, err error)
- func ExactTagAtHEAD(repo *git.Repository) (string, error)
- func HasStagedChanges(s git.Status) bool
- func HasUnstagedChanges(s git.Status) bool
- func IsAncestor(repo *git.Repository, ancestorRef, descendantRef string) (bool, error)
- func IsClean(s git.Status) bool
- func IsSSHURL(url string) bool
- func ListTagsSorted(repo *git.Repository) ([]string, error)
- func OpenRepo(rootDir string) (*git.Repository, error)
- func ParseCommitLog(repo *git.Repository, fromRef, toRef string) ([]*object.Commit, error)
- func RemoteRefHash(repo *git.Repository, remoteName, refName string, auth transport.AuthMethod) (plumbing.Hash, error)
- func RemoteURL(repo *git.Repository, remoteName string) (string, error)
- func RepoRoot(repo *git.Repository) (string, error)
- func RequireAttached(state RepoState) error
- func RequireClean(state RepoState) error
- func RequireState(state RepoState, action string, allowed ...StateClass) error
- func RequireUpstream(state RepoState) error
- func RequireValid(state RepoState) error
- func ResolveAuth(remoteURL string) (transport.AuthMethod, error)
- func ResolveHTTPAuth(_ string) (*githttp.BasicAuth, error)
- func ResolveRef(repo *git.Repository, ref string) (string, error)
- func StagedFiles(s git.Status) []string
- func TagMessage(repo *git.Repository, ref string) string
- func UnstagedDirtyPaths(s git.Status) []string
- func WorktreeStatus(wt *git.Worktree) (git.Status, error)
- type ErrHookRejected
- type ErrIndexDrift
- type ErrInvalidTransition
- type ErrPathTraversal
- type ErrReplayCorrupted
- type ErrReplayUnsafe
- type ErrTransport
- type RepoState
- type StateClass
- type StatusFileChange
- type SyncSession
- func (s *SyncSession) Auth() transport.AuthMethod
- func (s *SyncSession) FastForward(remote string) error
- func (s *SyncSession) Fetch(remote string) error
- func (s *SyncSession) FetchedUpstreamHash() plumbing.Hash
- func (s *SyncSession) Push(remote, refspec string, setUpstream bool) error
- func (s *SyncSession) Refresh() error
- func (s *SyncSession) Repo() *git.Repository
- func (s *SyncSession) State() RepoState
- type TransitionEvent
Constants ¶
This section is empty.
Variables ¶
var ErrDetachedHEAD = errors.New("HEAD is not on a branch — checkout a named branch first")
ErrDetachedHEAD is returned when HEAD is not on a named branch.
var ErrDirtyWorktree = errors.New("worktree has uncommitted changes — commit or stash first")
ErrDirtyWorktree is returned when uncommitted changes are present before replay.
var ErrNoUpstream = errors.New("branch has no upstream tracking ref — run: git branch --set-upstream-to=<remote>/<branch>")
ErrNoUpstream is returned when the current branch has no tracking upstream configured.
var ErrUpstreamMoved = errors.New("upstream ref changed between fetch and push — retry from fetch")
ErrUpstreamMoved is returned when the remote ref changes between fetch and push.
Functions ¶
func AllChangedFiles ¶
AllChangedFiles returns paths with any modification (staged, unstaged, or untracked).
func AllDirtyPaths ¶
AllDirtyPaths returns paths with staged or unstaged modifications, excluding untracked files. Use for bundle/artifact generation where both staged and unstaged changes must be captured. Output is sorted for deterministic ordering.
func CountCommitsBetween ¶
func CountCommitsBetween(repo *git.Repository, fromRef, toRef string) (int, error)
CountCommitsBetween counts commits reachable from `to` but not from `from`. Equivalent to `git rev-list --count <from>..<to>`.
func DiffStats ¶
func DiffStats(repo *git.Repository, fromRef, toRef string) (files, insertions, deletions int, err error)
DiffStats returns file/insertion/deletion counts between two refs.
func ExactTagAtHEAD ¶
func ExactTagAtHEAD(repo *git.Repository) (string, error)
ExactTagAtHEAD returns the tag name if HEAD is exactly at a tagged commit, matching `git describe --tags --exact-match HEAD`. Returns "" if not at a tag.
func HasStagedChanges ¶
HasStagedChanges returns true when any file has a staged modification.
func HasUnstagedChanges ¶
HasUnstagedChanges returns true when any tracked file has unstaged modifications.
func IsAncestor ¶
func IsAncestor(repo *git.Repository, ancestorRef, descendantRef string) (bool, error)
IsAncestor returns true if ancestorRef is a (possibly indirect) ancestor of descendantRef. Equivalent to `git merge-base --is-ancestor <ancestor> <descendant>`.
func ListTagsSorted ¶
func ListTagsSorted(repo *git.Repository) ([]string, error)
ListTagsSorted returns all tags sorted by version descending, matching git's --sort=-version:refname behaviour. Semver tags are sorted by semver; non-semver tags are sorted lexicographically after all semver tags.
Parity with git: Masterminds/semver parses the same set of tags as git's version-aware sort for real-world repos (semver + v-prefix). Both classify tags as version-like or lexicographic using equivalent rules, and both produce version-like tags before lexicographic tags in descending order. Edge case: tags with identical version values but different prefixes (v1.0.0 and 1.0.0) are sorted stably by the sort.Slice guarantee — same as git.
func OpenRepo ¶
func OpenRepo(rootDir string) (*git.Repository, error)
OpenRepo is the single entry point for all *git.Repository instances. No package outside src/gitstate/ or src/commit/ may call git.PlainOpen directly. DetectDotGit walks parent directories to find .git, matching git CLI behaviour.
func ParseCommitLog ¶
ParseCommitLog returns commits in the range fromRef..toRef as (hash, subject, body, author) tuples.
func RemoteRefHash ¶
func RemoteRefHash(repo *git.Repository, remoteName, refName string, auth transport.AuthMethod) (plumbing.Hash, error)
RemoteRefHash returns the hash of a specific ref on the remote. Equivalent to `git ls-remote origin refs/heads/<branch>`. Requires network access.
func RemoteURL ¶
func RemoteURL(repo *git.Repository, remoteName string) (string, error)
RemoteURL returns the URL for the given remote (typically "origin").
func RepoRoot ¶
func RepoRoot(repo *git.Repository) (string, error)
RepoRoot returns the absolute path of the repository root directory (the directory containing .git). Use this instead of wt.Filesystem.Root() directly — encapsulates the go-git worktree filesystem contract in one place.
func RequireAttached ¶
RequireAttached returns ErrDetachedHEAD if the repo is in detached HEAD state.
func RequireClean ¶
RequireClean returns ErrDirtyWorktree if the worktree has uncommitted changes.
func RequireState ¶
func RequireState(state RepoState, action string, allowed ...StateClass) error
RequireState verifies that the current state falls into one of the allowed StateClasses for the given action. Returns ErrInvalidTransition if not.
func RequireUpstream ¶
RequireUpstream returns ErrNoUpstream if no upstream tracking branch is configured.
func RequireValid ¶
RequireValid enforces that none of the X-class blocked states are active. This is the universal pre-mutation gate — call it before any operation that touches the repository.
func ResolveAuth ¶
func ResolveAuth(remoteURL string) (transport.AuthMethod, error)
ResolveAuth resolves the go-git SSH transport auth method for a remote URL.
Resolution order (exclusive — first match wins):
- SSH_PRIVATE_KEY env var (in-memory, no filesystem dependency)
- SSH agent (SSH_AUTH_SOCK)
- Standard key files: id_ed25519, id_ecdsa, id_rsa
Host key verification is resolved via sfxssh.ResolveHostKeyCallback (same priority as raw SSH transport — SSH_KNOWN_HOSTS_CONTENT, SSH_KNOWN_HOSTS, ~/.ssh/known_hosts, SSH_INSECURE_SKIP_HOST_KEY_CHECK).
Returns an error when no auth is available — SSH auth failure is always fatal.
func ResolveHTTPAuth ¶
ResolveHTTPAuth returns HTTP basic auth for an HTTPS remote, resolving a credential from the environment so CI write-back (e.g. the deps auto-commit push) authenticates instead of failing with "HTTP Basic: Access denied".
Resolution order (first match wins):
- STAGEFREIGHT_GIT_USERNAME + STAGEFREIGHT_GIT_PASSWORD — explicit override.
- GITLAB_TOKEN — a Personal/Project Access Token (username "oauth2").
- GITHUB_TOKEN — username "x-access-token".
- CI_JOB_TOKEN — GitLab's per-job token (username "gitlab-ci-token"). LAST resort: it is read-only for repository writes by default, so a push needs a write-scoped token from (1)/(2); the job token only authenticates reads.
Returns (nil, nil) when nothing is set — preserving anonymous access to public HTTPS repos. A nil return is not an error: SSH remotes never reach here, and an unauthenticated push to a private remote fails loudly at push time.
func ResolveRef ¶
func ResolveRef(repo *git.Repository, ref string) (string, error)
ResolveRef resolves any git ref (tag, branch, commit SHA, HEAD) to a commit SHA. Equivalent to `git rev-parse --verify <ref>^{commit}`.
func StagedFiles ¶
StagedFiles returns paths with staged changes (Staging != Unmodified).
func TagMessage ¶
func TagMessage(repo *git.Repository, ref string) string
TagMessage returns the annotation message for an annotated tag. Returns "" for lightweight tags or on error (best-effort).
func UnstagedDirtyPaths ¶
UnstagedDirtyPaths returns paths with unstaged modifications (not staged, not untracked).
Types ¶
type ErrHookRejected ¶
ErrHookRejected is returned when a pre-commit or commit-msg hook exits non-zero.
func (*ErrHookRejected) Error ¶
func (e *ErrHookRejected) Error() string
type ErrIndexDrift ¶
ErrIndexDrift is returned when the index diverges from the intended tree mid-replay. A hard reset to originalHEAD is performed before this error is returned.
func (*ErrIndexDrift) Error ¶
func (e *ErrIndexDrift) Error() string
type ErrInvalidTransition ¶
type ErrInvalidTransition struct {
From StateClass
Action string
Allowed []StateClass
}
ErrInvalidTransition is returned when an operation is requested from a state that does not permit it. This is the definitive "impossible transition" error.
func (*ErrInvalidTransition) Error ¶
func (e *ErrInvalidTransition) Error() string
type ErrPathTraversal ¶
type ErrPathTraversal struct {
Path string
}
ErrPathTraversal is returned when a diff path would escape the repository root.
func (*ErrPathTraversal) Error ¶
func (e *ErrPathTraversal) Error() string
type ErrReplayCorrupted ¶
ErrReplayCorrupted is returned when the post-replay tree does not match the original HEAD tree. Mutation occurred and diverged. A hard reset to originalHEAD is performed before returning.
func (*ErrReplayCorrupted) Error ¶
func (e *ErrReplayCorrupted) Error() string
type ErrReplayUnsafe ¶
type ErrReplayUnsafe struct {
Reasons []string // descriptions of gate violations per commit
}
ErrReplayUnsafe is returned when the replay gate rejects commits before any mutation. This is distinct from ErrReplayCorrupted: no mutation has occurred.
func (*ErrReplayUnsafe) Error ¶
func (e *ErrReplayUnsafe) Error() string
type ErrTransport ¶
ErrTransport wraps SSH auth or push failures with an actionable message.
func (*ErrTransport) Error ¶
func (e *ErrTransport) Error() string
func (*ErrTransport) Unwrap ¶
func (e *ErrTransport) Unwrap() error
type RepoState ¶
type RepoState struct {
Branch string // current branch name (empty if DetachedHEAD)
UpstreamRef string // e.g. "origin/main" — empty if not configured
UpstreamConfigured bool
AheadCount int // commits local has that remote does not
BehindCount int // commits remote has that local does not
DetachedHEAD bool
WorktreeClean bool // true when no staged or unstaged changes exist
HeadHash plumbing.Hash // current commit hash
UpstreamHash plumbing.Hash // remote tracking hash (zero if not configured)
RemoteName string // e.g. "origin"
}
RepoState is the result of interrogating the current repository condition. ReadRepoState always returns a fully populated struct — callers must check DetachedHEAD and UpstreamConfigured before interpreting other fields.
All fields are resolved once at read time. No polling.
func ReadRepoState ¶
func ReadRepoState(repo *git.Repository) (RepoState, error)
ReadRepoState reads the current repository state. This is the single read-only knowledge pool for all repo facts. No package should read HEAD, upstream, or branch state independently.
type StateClass ¶
type StateClass string
StateClass classifies a RepoState into a named state for the transition table.
S-class states are stable — transitions are permitted from them. X-class states are hard stops — no transitions are allowed until resolved.
Transition table:
S0 CLEAN_SYNCED → no-op S1 CLEAN_AHEAD → PUSH → S0 S2 CLEAN_BEHIND → FAST_FORWARD → S0 S3 DIVERGED → REPLAY → S1 → PUSH → S0 X1 DETACHED_HEAD → hard stop (checkout a branch) X2 DIRTY_WORKTREE → hard stop (commit or stash) X3 NO_UPSTREAM → hard stop (set upstream tracking)
const ( // Stable states — transitions permitted StateCleanSynced StateClass = "CLEAN_SYNCED" // S0: ahead=0, behind=0, clean StateCleanAhead StateClass = "CLEAN_AHEAD" // S1: ahead>0, behind=0 StateCleanBehind StateClass = "CLEAN_BEHIND" // S2: ahead=0, behind>0 StateDiverged StateClass = "DIVERGED" // S3: ahead>0, behind>0 // Blocked states — hard stops StateDetachedHEAD StateClass = "DETACHED_HEAD" // X1 StateDirtyWorktree StateClass = "DIRTY_WORKTREE" // X2 StateNoUpstream StateClass = "NO_UPSTREAM" // X3 )
func Classify ¶
func Classify(s RepoState) StateClass
Classify derives the StateClass from a RepoState snapshot. Classification order: blocked states are checked first (fail-fast), then stable states by ahead/behind counts.
This function is pure — same input always produces the same output.
func (StateClass) IsValid ¶
func (c StateClass) IsValid() bool
IsValid returns true for S-class states where transitions are permitted.
type StatusFileChange ¶
StatusFileChange represents a file with its change status for the forge backend.
func ChangedFiles ¶
func ChangedFiles(s git.Status) []StatusFileChange
ChangedFiles returns all changed files (staged + unstaged + untracked) with delete status. Equivalent to `git status --porcelain=v1 -z -uall`.
func ChangedFilesInDir ¶
func ChangedFilesInDir(s git.Status, dir string) []StatusFileChange
ChangedFilesInDir returns changed files within a specific directory prefix.
func StagedChanges ¶
func StagedChanges(s git.Status) []StatusFileChange
StagedChanges returns staged files with delete status. Equivalent to `git diff --cached --name-status`.
type SyncSession ¶
type SyncSession struct {
// contains filtered or unexported fields
}
SyncSession is opened once per sync/push operation. State is resolved once at Open() and explicitly refreshed only after state-changing operations (Fetch, FastForward). Remote polling never happens opportunistically.
func OpenSyncSession ¶
func OpenSyncSession(rootDir string) (*SyncSession, error)
OpenSyncSession opens a SyncSession for the repository at rootDir. Reads repo state and resolves auth once; both are reused throughout the session. Returns an error if the remote URL uses SSH and auth cannot be resolved.
func (*SyncSession) Auth ¶
func (s *SyncSession) Auth() transport.AuthMethod
Auth returns the resolved transport auth (may be nil for public HTTPS remotes).
func (*SyncSession) FastForward ¶
func (s *SyncSession) FastForward(remote string) error
FastForward performs a fast-forward pull from the tracked upstream. Returns git.ErrNonFastForwardUpdate if the histories have diverged.
func (*SyncSession) Fetch ¶
func (s *SyncSession) Fetch(remote string) error
Fetch fetches from the configured remote using targeted refspecs. Only fetches refs/heads/* to avoid fetching tags or other namespaces implicitly. Updates fetched flag and refreshes state.
func (*SyncSession) FetchedUpstreamHash ¶
func (s *SyncSession) FetchedUpstreamHash() plumbing.Hash
FetchedUpstreamHash returns the upstream hash as observed after the last Fetch. Used by the replay race guard to detect concurrent pushes.
func (*SyncSession) Push ¶
func (s *SyncSession) Push(remote, refspec string, setUpstream bool) error
Push pushes to remote. When setUpstream is true, also configures branch tracking.
func (*SyncSession) Refresh ¶
func (s *SyncSession) Refresh() error
Refresh re-reads repo state after a mutation (fetch, fast-forward, reset).
func (*SyncSession) Repo ¶
func (s *SyncSession) Repo() *git.Repository
Repo returns the underlying git.Repository.
func (*SyncSession) State ¶
func (s *SyncSession) State() RepoState
State returns the current resolved repo state.
type TransitionEvent ¶
type TransitionEvent struct {
From StateClass `json:"from"`
Action string `json:"action"`
To StateClass `json:"to,omitempty"`
Note string `json:"note,omitempty"`
}
TransitionEvent is emitted by the Engine for every state transition. Consumers may log, forward to a UI, or record for audit.
JSON encoding is intentional — these are the structured facts that CI logs and diagnostic tooling consume.