Help us shape the future of Go! Take the 2026 Go Developer Survey

git

package
v0.37.1 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: MIT Imports: 13 Imported by: 0

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

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

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

func (g *GitRunner) Add(ctx context.Context, paths ...string) error

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) Commit

func (g *GitRunner) Commit(ctx context.Context, message, user, email string) error

Commit creates a git commit with the specified message and author

func (*GitRunner) CountRange

func (g *GitRunner) CountRange(ctx context.Context, from, to string) (int, error)

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

func (g *GitRunner) CurrentBranch(ctx context.Context) (string, error)

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

func (g *GitRunner) Fetch(ctx context.Context, remote string) error

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

func (g *GitRunner) Merge(ctx context.Context, branch string) error

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

func (g *GitRunner) MergeFFOnly(ctx context.Context, branch string) error

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) Push

func (g *GitRunner) Push(ctx context.Context) error

Push pushes commits to the remote repository, bounded by NetworkTimeout.

func (*GitRunner) PushDryRun

func (g *GitRunner) PushDryRun(ctx context.Context) (string, error)

PushDryRun shows what would be pushed without actually pushing. It asks the remote, so it is bounded by NetworkTimeout.

func (*GitRunner) Rebase

func (g *GitRunner) Rebase(ctx context.Context, branch string) error

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

func (*GitRunner) Upstream

func (g *GitRunner) Upstream(ctx context.Context) (string, error)

Upstream returns the current branch's upstream ref, such as "origin/master". A branch with no upstream yields ErrNoUpstream rather than git's raw "no upstream configured" text. A cancel or a timeout is not a missing upstream and is returned as it is.

func (*GitRunner) WorkDir

func (g *GitRunner) WorkDir() string

WorkDir returns the working directory of the GitRunner

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

func (m *MockGitRunner) CountRange(ctx context.Context, from, to string) (int, error)

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

type StatusEntry struct {
	Status   string // A, M, D, R, ??
	FilePath string
}

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

Jump to

Keyboard shortcuts

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