git

package
v1.1.6 Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package git defines the GitService interface and all types for SSU's git layer.

GitService is the single abstraction through which the Engine and Commands interact with git. Two implementations exist: ExecGit (production, os/exec) and MockGitService (testing, configurable stubs).

Index

Constants

This section is empty.

Variables

View Source
var DefaultBranchPriority = []string{"develop", "master", "main"}

DefaultBranchPriority is the default branch priority chain for detection.

Functions

func DetectBestBranch

func DetectBestBranch(ctx context.Context, svc GitService, dir string, opts BranchDetectOpts) (string, error)

DetectBestBranch implements the smart branch detection algorithm. It follows a strict 5-level priority chain, matching the original bash detect_best_branch() function:

  1. Override: opts.Override (--branch CLI flag)
  2. Feature branch: current branch preserved if it has a remote
  3. Priority chain: first match in opts.PriorityBranches with a remote ref
  4. Remote HEAD: symbolic ref of remote HEAD (e.g. origin/HEAD -> develop)
  5. Final fallback: first available remote branch, or "master"

This is a standalone function operating on the GitService interface so it can be fully unit-tested with MockGitService.

func IsConflict

func IsConflict(err error) bool

IsConflict reports whether err represents a git merge conflict.

func IsTimeout

func IsTimeout(err error) bool

IsTimeout reports whether err is or wraps a context deadline exceeded error.

func ResolveBranchForCheckout added in v1.1.3

func ResolveBranchForCheckout(ctx context.Context, svc GitService, dir string, opts BranchCheckoutOpts) (string, bool, error)

ResolveBranchForCheckout finds the best branch to checkout when HEAD is detached. It queries BranchesPointingAt(HEAD) and picks the best match with priority:

  1. Local feature branch (not in priority list)
  2. Local priority branch (develop > master > main)
  3. Remote feature branch (not in priority list)
  4. Remote priority branch (develop > master > main)

Returns the branch name, whether it's a local branch, and any error. If no branch points at HEAD, returns ("", false, nil).

Types

type BranchCheckoutOpts added in v1.1.3

type BranchCheckoutOpts struct {
	PriorityBranches []string // Default: ["develop", "master", "main"]
	DefaultRemote    string   // Default: "origin"
}

BranchCheckoutOpts configures branch resolution for checkout.

type BranchDetectOpts

type BranchDetectOpts struct {
	Override         string   // --branch CLI flag (highest priority)
	PriorityBranches []string // Default: ["develop", "master", "main"]
	DefaultRemote    string   // Default: "origin"
}

BranchDetectOpts configures the smart branch detection algorithm.

type BranchPointsAtResult added in v1.1.3

type BranchPointsAtResult struct {
	Local  []string       // Local branch names (e.g. "develop", "feature/foo")
	Remote []RemoteBranch // Remote branches (e.g. origin/develop)
}

BranchPointsAtResult holds the branches whose tip equals a given ref.

type BranchResult

type BranchResult struct {
	Name     string // Branch name, or empty if detached
	Detached bool   // True when HEAD is detached
	Stderr   string
}

BranchResult holds the outcome of a current-branch query.

type CheckoutResult

type CheckoutResult struct {
	Branch string
	Stderr string
}

CheckoutResult holds the outcome of a git checkout.

type CommitResult

type CommitResult struct {
	SHA    string // Short SHA of the new commit
	Stderr string
}

CommitResult holds the outcome of a git commit.

type ExecGit

type ExecGit struct {
	GitBin   string        // Path to git binary; defaults to "git" if empty.
	Timeouts TimeoutConfig // Per-operation timeout configuration.
}

ExecGit implements GitService by shelling out to the git binary via os/exec. Every command uses context.WithTimeout for robust timeout handling and sets GIT_TERMINAL_PROMPT=0 to prevent credential prompt hangs.

func NewExecGit

func NewExecGit() *ExecGit

NewExecGit returns an ExecGit with default timeouts and system git.

func (*ExecGit) BranchesPointingAt added in v1.1.3

func (g *ExecGit) BranchesPointingAt(ctx context.Context, dir, ref string) (BranchPointsAtResult, error)

func (*ExecGit) Checkout

func (g *ExecGit) Checkout(ctx context.Context, dir, branch string) (CheckoutResult, error)

func (*ExecGit) CheckoutNewBranch added in v1.1.6

func (g *ExecGit) CheckoutNewBranch(ctx context.Context, dir, branch, startPoint string) (CheckoutResult, error)

func (*ExecGit) Commit

func (g *ExecGit) Commit(ctx context.Context, dir, message string) (CommitResult, error)

func (*ExecGit) CommitsAhead

func (g *ExecGit) CommitsAhead(ctx context.Context, dir, remoteRef string) (int, error)

func (*ExecGit) CommitsBehind

func (g *ExecGit) CommitsBehind(ctx context.Context, dir, localRef, remoteRef string) (int, error)

func (*ExecGit) CreateTag

func (g *ExecGit) CreateTag(ctx context.Context, dir string, opts TagOpts) error

func (*ExecGit) CurrentBranch

func (g *ExecGit) CurrentBranch(ctx context.Context, dir string) (BranchResult, error)

func (*ExecGit) CurrentSHA

func (g *ExecGit) CurrentSHA(ctx context.Context, dir string) (string, error)

func (*ExecGit) DiffIndex

func (g *ExecGit) DiffIndex(ctx context.Context, dir string) ([]string, error)

func (*ExecGit) Fetch

func (g *ExecGit) Fetch(ctx context.Context, dir string, opts FetchOpts) (FetchResult, error)

func (*ExecGit) HasLocalChanges

func (g *ExecGit) HasLocalChanges(ctx context.Context, dir string) (bool, error)

func (*ExecGit) HasRemoteBranch

func (g *ExecGit) HasRemoteBranch(ctx context.Context, dir, remote, branch string) (bool, error)

func (*ExecGit) IncomingChangelog

func (g *ExecGit) IncomingChangelog(ctx context.Context, dir, remoteRef string, limit int) ([]string, error)

func (*ExecGit) IsAncestor added in v1.1.6

func (g *ExecGit) IsAncestor(ctx context.Context, dir, ancestor, descendant string) (bool, error)

func (*ExecGit) IsDetachedHead

func (g *ExecGit) IsDetachedHead(ctx context.Context, dir string) (bool, error)

func (*ExecGit) IsSubmoduleInitialized

func (g *ExecGit) IsSubmoduleInitialized(rootDir, subPath string) bool

func (*ExecGit) ListTags

func (g *ExecGit) ListTags(ctx context.Context, dir string, limit int) ([]string, error)

func (*ExecGit) Merge

func (g *ExecGit) Merge(ctx context.Context, dir, ref string) (MergeResult, error)

func (*ExecGit) MergeAbort

func (g *ExecGit) MergeAbort(ctx context.Context, dir string) error

func (*ExecGit) Push

func (g *ExecGit) Push(ctx context.Context, dir string, opts PushOpts) (PushResult, error)

func (*ExecGit) RefExists added in v1.1.6

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

func (*ExecGit) RemoteBranches

func (g *ExecGit) RemoteBranches(ctx context.Context, dir string) ([]RemoteBranch, error)

func (*ExecGit) RemovePath added in v1.1.4

func (g *ExecGit) RemovePath(ctx context.Context, rootDir, path string) error

func (*ExecGit) ResetHard

func (g *ExecGit) ResetHard(ctx context.Context, dir, ref string) error

func (*ExecGit) StageFiles

func (g *ExecGit) StageFiles(ctx context.Context, dir string, paths []string) error

func (*ExecGit) Stash

func (g *ExecGit) Stash(ctx context.Context, dir string) (StashResult, error)

func (*ExecGit) StashPop

func (g *ExecGit) StashPop(ctx context.Context, dir string) (StashResult, error)

func (*ExecGit) SubmoduleDeinit added in v1.1.4

func (g *ExecGit) SubmoduleDeinit(ctx context.Context, rootDir, subPath string) error

func (*ExecGit) SubmoduleInit added in v1.1.5

func (g *ExecGit) SubmoduleInit(ctx context.Context, rootDir string, paths []string) error

func (*ExecGit) SubmodulePaths

func (g *ExecGit) SubmodulePaths(ctx context.Context, rootDir string) ([]string, error)

func (*ExecGit) SymbolicRef

func (g *ExecGit) SymbolicRef(ctx context.Context, dir, ref string) (string, error)

func (*ExecGit) TrackingBranch

func (g *ExecGit) TrackingBranch(ctx context.Context, dir string) (TrackingInfo, error)

type FetchOpts

type FetchOpts struct {
	Remote string // Empty means all remotes
	Prune  bool
}

FetchOpts configures a git fetch operation.

type FetchResult

type FetchResult struct {
	Stderr string
}

FetchResult holds the outcome of a git fetch.

type GitError

type GitError struct {
	Op     string // Operation name: "fetch", "push", "merge", etc.
	Stderr string // Raw stderr output from git
	Err    error  // Underlying error (exec.ExitError, context.DeadlineExceeded, etc.)
}

GitError wraps a git operation failure with context.

func (*GitError) Error

func (e *GitError) Error() string

Error returns a human-readable error message.

func (*GitError) Unwrap

func (e *GitError) Unwrap() error

Unwrap returns the underlying error for use with errors.Is and errors.As.

type GitService

type GitService interface {
	// Repository discovery
	SubmodulePaths(ctx context.Context, rootDir string) ([]string, error)
	IsSubmoduleInitialized(rootDir, subPath string) bool
	SubmoduleInit(ctx context.Context, rootDir string, paths []string) error

	// Branch and revision queries
	CurrentBranch(ctx context.Context, dir string) (BranchResult, error)
	CurrentSHA(ctx context.Context, dir string) (string, error)
	IsDetachedHead(ctx context.Context, dir string) (bool, error)
	RemoteBranches(ctx context.Context, dir string) ([]RemoteBranch, error)
	CommitsBehind(ctx context.Context, dir, localRef, remoteRef string) (int, error)
	CommitsAhead(ctx context.Context, dir, remoteRef string) (int, error)
	HasRemoteBranch(ctx context.Context, dir, remote, branch string) (bool, error)
	SymbolicRef(ctx context.Context, dir, ref string) (string, error)
	TrackingBranch(ctx context.Context, dir string) (TrackingInfo, error)
	RefExists(ctx context.Context, dir, ref string) (bool, error)
	IsAncestor(ctx context.Context, dir, ancestor, descendant string) (bool, error)

	// Status queries
	HasLocalChanges(ctx context.Context, dir string) (bool, error)
	IncomingChangelog(ctx context.Context, dir, remoteRef string, limit int) ([]string, error)

	// Mutating operations
	Fetch(ctx context.Context, dir string, opts FetchOpts) (FetchResult, error)
	Checkout(ctx context.Context, dir, branch string) (CheckoutResult, error)
	CheckoutNewBranch(ctx context.Context, dir, branch, startPoint string) (CheckoutResult, error)
	Merge(ctx context.Context, dir, ref string) (MergeResult, error)
	Push(ctx context.Context, dir string, opts PushOpts) (PushResult, error)
	Stash(ctx context.Context, dir string) (StashResult, error)
	StashPop(ctx context.Context, dir string) (StashResult, error)
	MergeAbort(ctx context.Context, dir string) error
	ResetHard(ctx context.Context, dir, ref string) error

	// Branch queries
	BranchesPointingAt(ctx context.Context, dir, ref string) (BranchPointsAtResult, error)

	// Submodule removal
	SubmoduleDeinit(ctx context.Context, rootDir, subPath string) error
	RemovePath(ctx context.Context, rootDir, path string) error

	// Root repository operations
	DiffIndex(ctx context.Context, dir string) ([]string, error)
	StageFiles(ctx context.Context, dir string, paths []string) error
	Commit(ctx context.Context, dir, message string) (CommitResult, error)
	ListTags(ctx context.Context, dir string, limit int) ([]string, error)
	CreateTag(ctx context.Context, dir string, opts TagOpts) error
}

GitService abstracts all git operations SSU needs. Implementations: ExecGit (production), MockGitService (testing).

type MergeResult

type MergeResult struct {
	Success  bool
	Conflict bool // True if merge failed due to conflict
	Stderr   string
}

MergeResult holds the outcome of a git merge.

type MockGitService

type MockGitService struct {
	SubmodulePathsFn         func(ctx context.Context, rootDir string) ([]string, error)
	IsSubmoduleInitializedFn func(rootDir, subPath string) bool
	SubmoduleInitFn          func(ctx context.Context, rootDir string, paths []string) error
	CurrentBranchFn          func(ctx context.Context, dir string) (BranchResult, error)
	CurrentSHAFn             func(ctx context.Context, dir string) (string, error)
	IsDetachedHeadFn         func(ctx context.Context, dir string) (bool, error)
	RemoteBranchesFn         func(ctx context.Context, dir string) ([]RemoteBranch, error)
	CommitsBehindFn          func(ctx context.Context, dir, localRef, remoteRef string) (int, error)
	CommitsAheadFn           func(ctx context.Context, dir, remoteRef string) (int, error)
	HasRemoteBranchFn        func(ctx context.Context, dir, remote, branch string) (bool, error)
	SymbolicRefFn            func(ctx context.Context, dir, ref string) (string, error)
	TrackingBranchFn         func(ctx context.Context, dir string) (TrackingInfo, error)
	RefExistsFn              func(ctx context.Context, dir, ref string) (bool, error)
	IsAncestorFn             func(ctx context.Context, dir, ancestor, descendant string) (bool, error)
	HasLocalChangesFn        func(ctx context.Context, dir string) (bool, error)
	IncomingChangelogFn      func(ctx context.Context, dir, remoteRef string, limit int) ([]string, error)
	FetchFn                  func(ctx context.Context, dir string, opts FetchOpts) (FetchResult, error)
	CheckoutFn               func(ctx context.Context, dir, branch string) (CheckoutResult, error)
	CheckoutNewBranchFn      func(ctx context.Context, dir, branch, startPoint string) (CheckoutResult, error)
	MergeFn                  func(ctx context.Context, dir, ref string) (MergeResult, error)
	PushFn                   func(ctx context.Context, dir string, opts PushOpts) (PushResult, error)
	StashFn                  func(ctx context.Context, dir string) (StashResult, error)
	StashPopFn               func(ctx context.Context, dir string) (StashResult, error)
	MergeAbortFn             func(ctx context.Context, dir string) error
	ResetHardFn              func(ctx context.Context, dir, ref string) error
	DiffIndexFn              func(ctx context.Context, dir string) ([]string, error)
	StageFilesFn             func(ctx context.Context, dir string, paths []string) error
	CommitFn                 func(ctx context.Context, dir, message string) (CommitResult, error)
	ListTagsFn               func(ctx context.Context, dir string, limit int) ([]string, error)
	CreateTagFn              func(ctx context.Context, dir string, opts TagOpts) error
	BranchesPointingAtFn     func(ctx context.Context, dir, ref string) (BranchPointsAtResult, error)
	SubmoduleDeinitFn        func(ctx context.Context, rootDir, subPath string) error
	RemovePathFn             func(ctx context.Context, rootDir, path string) error
}

MockGitService is a test double for GitService. Each method delegates to its corresponding Fn field if non-nil, otherwise returns a sensible default. This allows tests to override only the methods they care about while leaving everything else functional with safe zero-value behavior.

func (*MockGitService) BranchesPointingAt added in v1.1.3

func (m *MockGitService) BranchesPointingAt(ctx context.Context, dir, ref string) (BranchPointsAtResult, error)

func (*MockGitService) Checkout

func (m *MockGitService) Checkout(ctx context.Context, dir, branch string) (CheckoutResult, error)

func (*MockGitService) CheckoutNewBranch added in v1.1.6

func (m *MockGitService) CheckoutNewBranch(ctx context.Context, dir, branch, startPoint string) (CheckoutResult, error)

func (*MockGitService) Commit

func (m *MockGitService) Commit(ctx context.Context, dir, message string) (CommitResult, error)

func (*MockGitService) CommitsAhead

func (m *MockGitService) CommitsAhead(ctx context.Context, dir, remoteRef string) (int, error)

func (*MockGitService) CommitsBehind

func (m *MockGitService) CommitsBehind(ctx context.Context, dir, localRef, remoteRef string) (int, error)

func (*MockGitService) CreateTag

func (m *MockGitService) CreateTag(ctx context.Context, dir string, opts TagOpts) error

func (*MockGitService) CurrentBranch

func (m *MockGitService) CurrentBranch(ctx context.Context, dir string) (BranchResult, error)

func (*MockGitService) CurrentSHA

func (m *MockGitService) CurrentSHA(ctx context.Context, dir string) (string, error)

func (*MockGitService) DiffIndex

func (m *MockGitService) DiffIndex(ctx context.Context, dir string) ([]string, error)

func (*MockGitService) Fetch

func (m *MockGitService) Fetch(ctx context.Context, dir string, opts FetchOpts) (FetchResult, error)

func (*MockGitService) HasLocalChanges

func (m *MockGitService) HasLocalChanges(ctx context.Context, dir string) (bool, error)

func (*MockGitService) HasRemoteBranch

func (m *MockGitService) HasRemoteBranch(ctx context.Context, dir, remote, branch string) (bool, error)

func (*MockGitService) IncomingChangelog

func (m *MockGitService) IncomingChangelog(ctx context.Context, dir, remoteRef string, limit int) ([]string, error)

func (*MockGitService) IsAncestor added in v1.1.6

func (m *MockGitService) IsAncestor(ctx context.Context, dir, ancestor, descendant string) (bool, error)

func (*MockGitService) IsDetachedHead

func (m *MockGitService) IsDetachedHead(ctx context.Context, dir string) (bool, error)

func (*MockGitService) IsSubmoduleInitialized

func (m *MockGitService) IsSubmoduleInitialized(rootDir, subPath string) bool

func (*MockGitService) ListTags

func (m *MockGitService) ListTags(ctx context.Context, dir string, limit int) ([]string, error)

func (*MockGitService) Merge

func (m *MockGitService) Merge(ctx context.Context, dir, ref string) (MergeResult, error)

func (*MockGitService) MergeAbort

func (m *MockGitService) MergeAbort(ctx context.Context, dir string) error

func (*MockGitService) Push

func (m *MockGitService) Push(ctx context.Context, dir string, opts PushOpts) (PushResult, error)

func (*MockGitService) RefExists added in v1.1.6

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

func (*MockGitService) RemoteBranches

func (m *MockGitService) RemoteBranches(ctx context.Context, dir string) ([]RemoteBranch, error)

func (*MockGitService) RemovePath added in v1.1.4

func (m *MockGitService) RemovePath(ctx context.Context, rootDir, path string) error

func (*MockGitService) ResetHard

func (m *MockGitService) ResetHard(ctx context.Context, dir, ref string) error

func (*MockGitService) StageFiles

func (m *MockGitService) StageFiles(ctx context.Context, dir string, paths []string) error

func (*MockGitService) Stash

func (m *MockGitService) Stash(ctx context.Context, dir string) (StashResult, error)

func (*MockGitService) StashPop

func (m *MockGitService) StashPop(ctx context.Context, dir string) (StashResult, error)

func (*MockGitService) SubmoduleDeinit added in v1.1.4

func (m *MockGitService) SubmoduleDeinit(ctx context.Context, rootDir, subPath string) error

func (*MockGitService) SubmoduleInit added in v1.1.5

func (m *MockGitService) SubmoduleInit(ctx context.Context, rootDir string, paths []string) error

func (*MockGitService) SubmodulePaths

func (m *MockGitService) SubmodulePaths(ctx context.Context, rootDir string) ([]string, error)

func (*MockGitService) SymbolicRef

func (m *MockGitService) SymbolicRef(ctx context.Context, dir, ref string) (string, error)

func (*MockGitService) TrackingBranch

func (m *MockGitService) TrackingBranch(ctx context.Context, dir string) (TrackingInfo, error)

type PushOpts

type PushOpts struct {
	Remote      string
	Branch      string
	SetUpstream bool
}

PushOpts configures a git push operation.

type PushResult

type PushResult struct {
	Remote      string // Which remote was pushed to
	Branch      string // Which branch was pushed
	NewTracking bool   // True if -u was used to set up tracking
	Stderr      string
}

PushResult holds the outcome of a git push.

type RemoteBranch

type RemoteBranch struct {
	Remote string // e.g. "origin"
	Branch string // e.g. "develop"
}

RemoteBranch represents a branch on a remote. This is a data type, not an operation result, so it does not carry Stderr.

type SemVer

type SemVer struct {
	Major      int
	Minor      int
	Patch      int
	Prerelease string // e.g. "beta.1" (empty if none)
}

SemVer represents a parsed semantic version (major.minor.patch with optional prerelease).

func LatestSemVer

func LatestSemVer(tags []string) *SemVer

LatestSemVer finds the highest semver tag from a list of tag strings. Non-semver tags are ignored. Returns nil if no valid semver tags found.

func ParseSemVer

func ParseSemVer(s string) *SemVer

ParseSemVer parses a version string like "v1.2.3" or "v1.2.3-beta.1". Returns nil if the string is not a valid semver.

func (SemVer) IncrementPatch

func (v SemVer) IncrementPatch() SemVer

IncrementPatch returns a new SemVer with the patch version bumped and prerelease cleared.

func (SemVer) Less

func (v SemVer) Less(other SemVer) bool

Less reports whether v is less than other using semver ordering. Prerelease versions are less than the same version without prerelease.

func (SemVer) String

func (v SemVer) String() string

String returns the v-prefixed version string (e.g. "v1.2.4").

type StashResult

type StashResult struct {
	Created bool // For Stash: true if a stash was created; for StashPop: true if pop was applied
	Stderr  string
}

StashResult holds the outcome of a stash or stash pop operation.

type SubmoduleStatus

type SubmoduleStatus string

SubmoduleStatus represents the state of a submodule.

const (
	StatusPending  SubmoduleStatus = "pending"
	StatusCurrent  SubmoduleStatus = "current"
	StatusModified SubmoduleStatus = "modified"
	StatusAhead    SubmoduleStatus = "ahead"
	StatusConflict SubmoduleStatus = "conflict"
	StatusMissing  SubmoduleStatus = "missing"
	StatusSkipped  SubmoduleStatus = "skipped"
	StatusError    SubmoduleStatus = "error"
)

type TagOpts

type TagOpts struct {
	Name    string // Tag name (e.g. "v1.2.3")
	Message string // Annotation message
}

TagOpts configures a git tag creation.

type TimeoutConfig

type TimeoutConfig struct {
	Fetch   time.Duration // Default 120s -- network bound.
	Push    time.Duration // Default 60s -- network bound.
	Merge   time.Duration // Default 30s -- local operation.
	Default time.Duration // Default 30s -- for queries like rev-parse, branch -r.
}

TimeoutConfig holds per-operation timeout durations.

func DefaultTimeouts

func DefaultTimeouts() TimeoutConfig

DefaultTimeouts returns a TimeoutConfig with sensible defaults.

type TrackingInfo

type TrackingInfo struct {
	Remote string // e.g. "origin"
	Branch string // e.g. "develop"
	Set    bool   // False if no tracking branch is configured
	Stderr string
}

TrackingInfo describes the upstream tracking branch for a local branch.

Jump to

Keyboard shortcuts

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