Documentation
¶
Overview ¶
Package git runs the git commands the overlay workflow needs (status, add, commit, push, pull, fetch) under a context, with network and local timeouts, and parses their output. GitExecutor is the seam: GitRunner runs the real git, MockGitRunner records calls for tests.
Index ¶
- Constants
- Variables
- type GitExecutor
- type GitRunner
- func (g *GitRunner) Add(ctx context.Context, paths ...string) error
- func (g *GitRunner) Commit(ctx context.Context, message, user, email string) error
- func (g *GitRunner) CountRange(ctx context.Context, from, to string) (int, error)
- func (g *GitRunner) CurrentBranch(ctx context.Context) (string, error)
- func (g *GitRunner) Fetch(ctx context.Context, remote string) error
- func (g *GitRunner) Merge(ctx context.Context, branch string) error
- func (g *GitRunner) MergeFFOnly(ctx context.Context, branch string) error
- func (g *GitRunner) Push(ctx context.Context) error
- func (g *GitRunner) PushDryRun(ctx context.Context) (string, error)
- func (g *GitRunner) Rebase(ctx context.Context, branch string) error
- func (g *GitRunner) StagedStatus(ctx context.Context) ([]StatusEntry, error)
- func (g *GitRunner) Status(ctx context.Context) ([]StatusEntry, error)
- func (g *GitRunner) Upstream(ctx context.Context) (string, error)
- func (g *GitRunner) WorkDir() string
- type GitRunnerOption
- type MockGitRunner
- func (m *MockGitRunner) Add(ctx context.Context, paths ...string) error
- func (m *MockGitRunner) Commit(ctx context.Context, message, user, email string) error
- func (m *MockGitRunner) CountRange(ctx context.Context, from, to string) (int, error)
- func (m *MockGitRunner) CurrentBranch(ctx context.Context) (string, error)
- func (m *MockGitRunner) Fetch(ctx context.Context, remote string) error
- func (m *MockGitRunner) Merge(ctx context.Context, branch string) error
- func (m *MockGitRunner) MergeFFOnly(ctx context.Context, branch string) error
- func (m *MockGitRunner) Push(ctx context.Context) error
- func (m *MockGitRunner) PushDryRun(ctx context.Context) (string, error)
- func (m *MockGitRunner) Rebase(ctx context.Context, branch string) error
- func (m *MockGitRunner) StagedStatus(ctx context.Context) ([]StatusEntry, error)
- func (m *MockGitRunner) Status(ctx context.Context) ([]StatusEntry, error)
- func (m *MockGitRunner) Upstream(ctx context.Context) (string, error)
- func (m *MockGitRunner) WorkDir() string
- type StatusEntry
Constants ¶
const ( NetworkTimeout = 5 * time.Minute LocalTimeout = 1 * time.Minute )
Every git operation is bounded, on top of the context its caller passes. NetworkTimeout bounds the ones that talk to a remote: Push, PushDryRun and Fetch. LocalTimeout bounds every other one: status, add, commit, merge, rebase, rev-parse and rev-list. An operation that runs past its bound is stopped; its error wraps context.DeadlineExceeded and reads "git <op> timed out after <bound>".
Variables ¶
var ( ErrFileNotFound = errors.New("file not found") ErrPathOutsideOverlay = errors.New("path is outside overlay directory") ErrInvalidPath = errors.New("invalid path") ErrGitCommand = errors.New("git command failed") ErrNoUpstream = errors.New("no upstream configured for the current branch") ErrDetachedHead = errors.New("HEAD is detached; check out a branch first") )
Functions ¶
This section is empty.
Types ¶
type GitExecutor ¶
type GitExecutor interface {
// Status returns the current git status as a list of StatusEntry
Status(ctx context.Context) ([]StatusEntry, error)
// StagedStatus returns only the entries staged in the index (what a commit would include)
StagedStatus(ctx context.Context) ([]StatusEntry, error)
// Add stages files for commit
Add(ctx context.Context, paths ...string) error
// Commit creates a git commit with the specified message and author
Commit(ctx context.Context, message, user, email string) error
// Push pushes commits to the remote repository
Push(ctx context.Context) error
// PushDryRun shows what would be pushed without actually pushing
PushDryRun(ctx context.Context) (string, error)
// Fetch fetches changes from a remote repository
Fetch(ctx context.Context, remote string) error
// Merge merges a branch into the current branch
Merge(ctx context.Context, branch string) error
// MergeFFOnly merges a branch only if it can fast-forward. It refuses
// rather than writing a merge commit when the branches have diverged.
MergeFFOnly(ctx context.Context, branch string) error
// Rebase replays the current branch's commits on top of the given branch
Rebase(ctx context.Context, branch string) error
// CurrentBranch returns the name of the checked-out branch. It reports an
// error on a detached HEAD, which has no branch to pull into.
CurrentBranch(ctx context.Context) (string, error)
// Upstream returns the current branch's configured upstream ref
// (for example "origin/master"), or an error when none is configured.
Upstream(ctx context.Context) (string, error)
// CountRange returns how many commits are reachable from `to` but not
// from `from` — the size of the range `from..to`.
CountRange(ctx context.Context, from, to string) (int, error)
// WorkDir returns the working directory of the git repository
WorkDir() string
}
GitExecutor defines the interface for git operations. This interface allows for mocking git operations in tests.
Every method that runs git takes, first, the context that stops it: when ctx is done the git child is stopped and the method returns an error wrapping ctx.Err().
type GitRunner ¶
type GitRunner struct {
// contains filtered or unexported fields
}
GitRunner executes git commands in a specific working directory
func NewGitRunner ¶
func NewGitRunner(workDir string, opts ...GitRunnerOption) *GitRunner
NewGitRunner creates a new GitRunner for the specified working directory. With no options it uses a no-op reporter and exec.CommandContext, so existing no-arg callers are unaffected.
func (*GitRunner) Add ¶
Add stages files for commit with path validation. Each path follows "--", so a file whose name begins with "-" is staged as a file, never read as an option; bentooScratchExclude keeps bentoo's scratch files out.
func (*GitRunner) CountRange ¶
CountRange returns the number of commits in the range from..to — that is, commits reachable from `to` but not from `from`. "--end-of-options" precedes the range, so neither end is read as an option.
func (*GitRunner) CurrentBranch ¶
CurrentBranch returns the checked-out branch name. A detached HEAD yields the literal "HEAD" from git, which is not a branch anything can be pulled into, so it is reported as an error.
func (*GitRunner) Fetch ¶
Fetch fetches changes from a remote repository. It is a streaming op: git's progress output (which it writes to stderr) is tailed live via a tui.StreamCapture and also captured, so a failing fetch preserves its output in the returned error — the last tui.MaxCapturedBytes of it, which is what Captured keeps. Under the default Noop reporter and exec.CommandContext this is behavior-equivalent to a buffered fetch. It is bounded by NetworkTimeout, and a cancel or a timeout is reported as runCommand reports it (see run).
The remote comes from the overlay config, so "--end-of-options" precedes it: read as an option, a value such as "--upload-pack=<cmd>" would run <cmd>.
func (*GitRunner) Merge ¶
Merge merges a branch into the current branch. If there are conflicts, the error message includes the conflict details from stdout. "--end-of-options" precedes the branch, so a name beginning with "-" is read as a revision, never as an option.
func (*GitRunner) MergeFFOnly ¶
MergeFFOnly merges a branch only when the merge can fast-forward. Where Merge writes a merge commit over diverged history, this refuses and leaves the branch untouched, so the caller can report the divergence instead of burying it in a commit nobody asked for. Git reports "Not possible to fast-forward" on stdout, so both streams reach the caller. The branch follows "--end-of-options", as in Merge.
func (*GitRunner) PushDryRun ¶
PushDryRun shows what would be pushed without actually pushing. It asks the remote, so it is bounded by NetworkTimeout.
func (*GitRunner) Rebase ¶
Rebase replays the current branch's commits on top of branch. As with Merge, conflict details arrive on stdout and are folded into the error, and the branch follows "--end-of-options".
func (*GitRunner) StagedStatus ¶
func (g *GitRunner) StagedStatus(ctx context.Context) ([]StatusEntry, error)
StagedStatus returns only the entries staged in the index, i.e. exactly what a commit would include. It runs git status --porcelain and keeps entries whose index column (X) is set, dropping unstaged-only and untracked files.
func (*GitRunner) Status ¶
func (g *GitRunner) Status(ctx context.Context) ([]StatusEntry, error)
Status returns the current git status as a list of StatusEntry
type GitRunnerOption ¶
type GitRunnerOption func(*GitRunner)
GitRunnerOption configures a GitRunner at construction time.
func WithGitExecCommand ¶
func WithGitExecCommand(fn func(ctx context.Context, name string, arg ...string) *exec.Cmd) GitRunnerOption
WithGitExecCommand overrides the subprocess seam. A nil fn leaves the default (exec.CommandContext) in place. fn receives the bounded context of the run and must build its command with exec.CommandContext on it, so a cancel or a timeout reaches the child.
func WithGitReporter ¶
func WithGitReporter(r tui.Reporter, id string) GitRunnerOption
WithGitReporter sets the reporter and task id used for stage/done/tail events. A nil reporter is normalized to tui.Noop().
type MockGitRunner ¶
type MockGitRunner struct {
StatusFunc func(ctx context.Context) ([]StatusEntry, error)
StagedStatusFunc func(ctx context.Context) ([]StatusEntry, error)
AddFunc func(ctx context.Context, paths ...string) error
CommitFunc func(ctx context.Context, message, user, email string) error
PushFunc func(ctx context.Context) error
PushDryRunFunc func(ctx context.Context) (string, error)
FetchFunc func(ctx context.Context, remote string) error
MergeFunc func(ctx context.Context, branch string) error
MergeFFOnlyFunc func(ctx context.Context, branch string) error
RebaseFunc func(ctx context.Context, branch string) error
CurrentBranchFunc func(ctx context.Context) (string, error)
UpstreamFunc func(ctx context.Context) (string, error)
CountRangeFunc func(ctx context.Context, from, to string) (int, error)
// contains filtered or unexported fields
}
MockGitRunner implements GitExecutor for testing. Each method can be configured with a custom function to control behavior. Each method hands the context it received to that function unchanged, so a test sees the very context the code under test passed, and can stand in for a git child that never answers by blocking on ctx.Done().
func NewMockGitRunner ¶
func NewMockGitRunner(workDir string) *MockGitRunner
NewMockGitRunner creates a new MockGitRunner with the specified working directory
func (*MockGitRunner) Add ¶
func (m *MockGitRunner) Add(ctx context.Context, paths ...string) error
Add stages files for commit
func (*MockGitRunner) Commit ¶
func (m *MockGitRunner) Commit(ctx context.Context, message, user, email string) error
Commit creates a git commit with the specified message and author
func (*MockGitRunner) CountRange ¶
CountRange returns the number of commits in the range from..to
func (*MockGitRunner) CurrentBranch ¶
func (m *MockGitRunner) CurrentBranch(ctx context.Context) (string, error)
CurrentBranch returns the checked-out branch name
func (*MockGitRunner) Fetch ¶
func (m *MockGitRunner) Fetch(ctx context.Context, remote string) error
Fetch fetches changes from a remote repository
func (*MockGitRunner) Merge ¶
func (m *MockGitRunner) Merge(ctx context.Context, branch string) error
Merge merges a branch into the current branch
func (*MockGitRunner) MergeFFOnly ¶
func (m *MockGitRunner) MergeFFOnly(ctx context.Context, branch string) error
MergeFFOnly merges a branch only if it can fast-forward
func (*MockGitRunner) Push ¶
func (m *MockGitRunner) Push(ctx context.Context) error
Push pushes commits to the remote repository
func (*MockGitRunner) PushDryRun ¶
func (m *MockGitRunner) PushDryRun(ctx context.Context) (string, error)
PushDryRun shows what would be pushed without actually pushing
func (*MockGitRunner) Rebase ¶
func (m *MockGitRunner) Rebase(ctx context.Context, branch string) error
Rebase replays the current branch on top of the given branch
func (*MockGitRunner) StagedStatus ¶
func (m *MockGitRunner) StagedStatus(ctx context.Context) ([]StatusEntry, error)
StagedStatus returns only the entries staged in the index
func (*MockGitRunner) Status ¶
func (m *MockGitRunner) Status(ctx context.Context) ([]StatusEntry, error)
Status returns the current git status as a list of StatusEntry
func (*MockGitRunner) Upstream ¶
func (m *MockGitRunner) Upstream(ctx context.Context) (string, error)
Upstream returns the current branch's upstream ref
func (*MockGitRunner) WorkDir ¶
func (m *MockGitRunner) WorkDir() string
WorkDir returns the working directory of the git repository
type StatusEntry ¶
StatusEntry represents a single entry from git status --porcelain
func ParseStagedStatusOutput ¶
func ParseStagedStatusOutput(output string) []StatusEntry
ParseStagedStatusOutput parses git status --porcelain output and keeps only entries staged in the index. In the "XY filename" format, X is the index (staged) status and Y is the worktree status; an entry is staged when X is neither a space (unstaged-only) nor '?' (untracked).
func ParseStatusOutput ¶
func ParseStatusOutput(output string) []StatusEntry
ParseStatusOutput parses git status --porcelain output into StatusEntry slice