Documentation
¶
Overview ¶
Package git provides Git operations for the gitbak application.
This package abstracts Git commands and operations used by gitbak, handling repository interaction, branch management, commit creation, and error recovery. It implements automatic Git operations with built-in retry mechanisms and tracking for sequential commit numbering.
Core Components ¶
- Gitbak: Main type that manages a Git repository and performs automatic commits
- CommandExecutor: Interface for executing Git commands
- UserInteractor: Interface for user interaction during Git operations
Features ¶
- Automatic Git operations with configurable intervals
- Sequential commit numbering with the ability to continue from a previous session
- Branch creation and management
- Error handling with configurable retry logic
- Clean session termination with statistics
Usage ¶
Basic usage pattern:
config := git.GitbakConfig{ RepoPath: "/path/to/repo", IntervalMinutes: 5, BranchName: "gitbak-session", CommitPrefix: "[gitbak]", CreateBranch: true, ContinueSession: false, Verbose: true, ShowNoChanges: false, MaxRetries: 3, } gitbak, err := git.NewGitbak(config, logger) if err != nil { // Handle error } ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { // Cancel context when termination is requested // (e.g., signal handling, user input, etc.) }() if err := gitbak.Run(ctx); err != nil { // Handle error } // Show session summary gitbak.PrintSummary()
Error Handling ¶
The package implements sophisticated error recovery with configurable retry mechanisms:
- Consecutive identical errors are counted and compared against MaxRetries
- When errors change or successful operations occur, the error counter resets
- Setting MaxRetries to 0 makes the system retry indefinitely
Implementation Notes ¶
The package uses the command-line Git executable rather than a Go Git library. This ensures compatibility with all Git features and repository configurations.
Concurrency Model ¶
The gitbak instance is not thread-safe and should be accessed from a single goroutine. Different gitbak instances can safely operate on different repositories concurrently, of course - allowing users to run multiple gitbak instances on different repositories without a concern.
Dependencies ¶
This package requires:
- A functional Git installation in the system PATH
- A valid Git repository at the configured path
- Write permissions for the repository
Index ¶
- func IsRepository(path string) (bool, error)
- type CommandExecutor
- type DefaultInteractor
- type ExecExecutor
- func (e *ExecExecutor) Execute(ctx context.Context, cmd *exec.Cmd) error
- func (e *ExecExecutor) ExecuteWithContext(ctx context.Context, name string, args ...string) error
- func (e *ExecExecutor) ExecuteWithContextAndOutput(ctx context.Context, name string, args ...string) (string, error)
- func (e *ExecExecutor) ExecuteWithOutput(ctx context.Context, cmd *exec.Cmd) (string, error)
- type Gitbak
- type GitbakConfig
- type MockCommandExecutor
- func NewAdvancedMockRetryExecutor(failCount int, failWithErr error) *MockCommandExecutor
- func NewMockCommandExecutor() *MockCommandExecutor
- func NewMockRetryExecutor(failCount int, failWithErr error) *MockCommandExecutor
- func NewMockUncommittedChangesExecutor(addError, commitError bool) *MockCommandExecutor
- func (m *MockCommandExecutor) Execute(ctx context.Context, cmd *exec.Cmd) error
- func (m *MockCommandExecutor) ExecuteWithContext(ctx context.Context, name string, args ...string) error
- func (m *MockCommandExecutor) ExecuteWithContextAndOutput(ctx context.Context, name string, args ...string) (string, error)
- func (m *MockCommandExecutor) ExecuteWithOutput(ctx context.Context, cmd *exec.Cmd) (string, error)
- type MockInteractor
- type NonInteractiveInteractor
- type UserInteractor
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsRepository ¶
IsRepository checks if the given path is a git repository Returns true if it is a repository, false otherwise. If path is not a repository due to git exit code 128, returns (false, nil). For other errors (git not found, permission issues, etc), returns (false, err).
Types ¶
type CommandExecutor ¶
type CommandExecutor interface { // Execute runs a command and returns its exit code Execute(ctx context.Context, cmd *exec.Cmd) error // ExecuteWithOutput runs a command and returns its output and exit code ExecuteWithOutput(ctx context.Context, cmd *exec.Cmd) (string, error) // ExecuteWithContext runs a command with context and returns its exit code ExecuteWithContext(ctx context.Context, name string, args ...string) error // ExecuteWithContextAndOutput runs a command with context and returns its output and exit code ExecuteWithContextAndOutput(ctx context.Context, name string, args ...string) (string, error) }
CommandExecutor defines an interface for executing commands
type DefaultInteractor ¶
DefaultInteractor is the standard implementation of UserInteractor that reads from stdin and writes to stdout
func NewDefaultInteractor ¶
func NewDefaultInteractor(logger logger.Logger) *DefaultInteractor
NewDefaultInteractor creates a new DefaultInteractor
func (*DefaultInteractor) PromptYesNo ¶
func (i *DefaultInteractor) PromptYesNo(question string) bool
PromptYesNo asks the user a yes/no question and returns their response
type ExecExecutor ¶
type ExecExecutor struct{}
ExecExecutor is the default implementation of CommandExecutor that delegates to the os/exec package
func NewExecExecutor ¶
func NewExecExecutor() *ExecExecutor
NewExecExecutor creates a new ExecExecutor
func (*ExecExecutor) ExecuteWithContext ¶
ExecuteWithContext implements CommandExecutor.ExecuteWithContext
func (*ExecExecutor) ExecuteWithContextAndOutput ¶
func (e *ExecExecutor) ExecuteWithContextAndOutput(ctx context.Context, name string, args ...string) (string, error)
ExecuteWithContextAndOutput implements CommandExecutor.ExecuteWithContextAndOutput
func (*ExecExecutor) ExecuteWithOutput ¶
ExecuteWithOutput implements CommandExecutor.ExecuteWithOutput
type Gitbak ¶
type Gitbak struct {
// contains filtered or unexported fields
}
Gitbak monitors and auto-commits changes to a git repository. It provides the core functionality for automatically committing changes at regular intervals, with features for branch management, error recovery, and session continuation.
func NewGitbak ¶
func NewGitbak(config GitbakConfig, logger logger.Logger) (*Gitbak, error)
NewGitbak creates a new gitbak instance with default dependencies. This is the primary constructor for creating a gitbak instance with standard components. It validates the configuration and sets up all required dependencies.
Parameters:
- config: The configuration for this gitbak instance
- logger: The logger to use for output messages
Returns:
- A configured gitbak instance ready to run
- An error if the configuration is invalid or initialization fails
Example:
cfg := GitbakConfig{ RepoPath: "/path/to/repo", IntervalMinutes: 5, BranchName: "gitbak-session", CommitPrefix: "[gitbak]", CreateBranch: true, } gitbak, err := NewGitbak(cfg, logger)
func NewGitbakWithDeps ¶
func NewGitbakWithDeps( config GitbakConfig, logger logger.Logger, executor CommandExecutor, interactor UserInteractor, ) (*Gitbak, error)
NewGitbakWithDeps creates a new gitbak instance with custom dependencies
func (*Gitbak) PrintSummary ¶
func (g *Gitbak) PrintSummary()
PrintSummary prints a summary of the gitbak session
func (*Gitbak) RunSingleIteration ¶
RunSingleIteration is an exported version of the internal gitbak logic for testing. It runs a single iteration of the gitbak process without the infinite loop. This function is only available in test builds.
type GitbakConfig ¶
type GitbakConfig struct { // RepoPath specifies the filesystem path to the Git repository. // Can be absolute or relative path. If empty, validation will fail. RepoPath string // IntervalMinutes defines how often (in minutes) gitbak checks for changes. // This can be a fractional value (e.g. 0.5 for 30 seconds). // Must be greater than 0. IntervalMinutes float64 // BranchName specifies the Git branch to use for checkpoint commits. // If CreateBranch is true, this branch will be created. // If CreateBranch is false, this branch must already exist. // If ContinueSession is true, this should be an existing gitbak branch. BranchName string // CommitPrefix is prepended to all commit messages. // Used to identify gitbak commits and extract commit numbers. CommitPrefix string // CreateBranch determines whether to create a new branch or use existing one. // If true, a new branch named BranchName will be created. // If false, gitbak will use the existing branch specified by BranchName. // ContinueSession implicitly sets this to false. CreateBranch bool // ContinueSession enables continuation mode for resuming a previous session. // When true, gitbak finds the last commit number and continues numbering from there. // Requires that previous gitbak commits exist on the specified branch. ContinueSession bool // Verbose controls the amount of informational output. // When true, gitbak provides detailed status updates. // When false, only essential messages are shown. Verbose bool // ShowNoChanges determines whether to report when no changes are detected. // When true, gitbak logs a message at each interval even if nothing changed. // When false, these messages are suppressed. ShowNoChanges bool // NonInteractive disables any prompts and uses default responses. // Useful for running gitbak in automated environments. NonInteractive bool // MaxRetries defines how many consecutive identical errors are allowed before exiting. // If zero, gitbak will retry indefinitely. // Errors of different types or successful operations reset this counter. MaxRetries int }
GitbakConfig contains configuration for a gitbak instance. This struct holds all the settings that control gitbak's behavior, including repository location, commit preferences, output options, and error handling settings.
func (*GitbakConfig) Validate ¶
func (c *GitbakConfig) Validate() error
Validate sanity-checks the config and returns an error if something is wrong. It ensures all required fields have valid values before gitbak starts running. This helps prevent runtime errors by catching configuration issues early.
The following validations are performed:
- RepoPath must not be empty
- IntervalMinutes must be greater than 0
- BranchName must not be empty
- CommitPrefix must not be empty
- MaxRetries must not be negative
Returns nil if the configuration is valid, or an error describing the issue.
type MockCommandExecutor ¶
type MockCommandExecutor struct { // Basic tracking ExitCode int Output string LastCmd *exec.Cmd Commands []*exec.Cmd CallCount int // Function hooks for customizing behavior ExecuteFn func(ctx context.Context, cmd *exec.Cmd) error ExecuteWithOutputFn func(ctx context.Context, cmd *exec.Cmd) (string, error) // Git command tracking (for uncommitted changes tests) AddCalled bool CommitCalled bool AddError bool CommitError bool // Error simulation and signaling (for retry tests) ShouldFailCount int PermanentFailAfter int FailWithErr error CurrentErr error ErrorVariant int ShouldResetErrorMsg bool // Advanced retry test behavior properties // These are used directly by the retry tests UseAdvancedRetryBehavior bool // Channels for signaling (for retry tests) NextFailCall chan struct{} NextSuccessCall chan struct{} }
MockCommandExecutor is a mock of the CommandExecutor interface that can be configured for different test scenarios.
func NewAdvancedMockRetryExecutor ¶
func NewAdvancedMockRetryExecutor(failCount int, failWithErr error) *MockCommandExecutor
NewAdvancedMockRetryExecutor creates a mock executor with advanced retry behavior for complex testing scenarios like message variation and permanent failures
func NewMockCommandExecutor ¶
func NewMockCommandExecutor() *MockCommandExecutor
NewMockCommandExecutor creates a new mock executor with default values
func NewMockRetryExecutor ¶
func NewMockRetryExecutor(failCount int, failWithErr error) *MockCommandExecutor
NewMockRetryExecutor creates a mock executor configured for retry tests
func NewMockUncommittedChangesExecutor ¶
func NewMockUncommittedChangesExecutor(addError, commitError bool) *MockCommandExecutor
NewMockUncommittedChangesExecutor creates a mock for uncommitted changes tests
func (*MockCommandExecutor) ExecuteWithContext ¶
func (m *MockCommandExecutor) ExecuteWithContext(ctx context.Context, name string, args ...string) error
ExecuteWithContext implements the CommandExecutor interface
func (*MockCommandExecutor) ExecuteWithContextAndOutput ¶
func (m *MockCommandExecutor) ExecuteWithContextAndOutput(ctx context.Context, name string, args ...string) (string, error)
ExecuteWithContextAndOutput implements the CommandExecutor interface
func (*MockCommandExecutor) ExecuteWithOutput ¶
ExecuteWithOutput implements the CommandExecutor interface
type MockInteractor ¶
type MockInteractor struct { // Response to return from PromptYesNo PromptYesNoResponse bool // Track the number of times PromptYesNo was called PromptYesNoCalled bool // Store the last prompt that was passed to PromptYesNo LastPrompt string }
MockInteractor is a mock implementation of the UserInteractor interface for testing user interaction scenarios.
func NewMockInteractor ¶
func NewMockInteractor(response bool) *MockInteractor
NewMockInteractor creates a new MockInteractor with default values
func (*MockInteractor) PromptYesNo ¶
func (m *MockInteractor) PromptYesNo(prompt string) bool
PromptYesNo implements the UserInteractor interface
type NonInteractiveInteractor ¶
type NonInteractiveInteractor struct{}
NonInteractiveInteractor always returns default values without prompting
func NewNonInteractiveInteractor ¶
func NewNonInteractiveInteractor() *NonInteractiveInteractor
NewNonInteractiveInteractor creates a new NonInteractiveInteractor
func (*NonInteractiveInteractor) PromptYesNo ¶
func (i *NonInteractiveInteractor) PromptYesNo(question string) bool
PromptYesNo always returns false without prompting
type UserInteractor ¶
type UserInteractor interface { // PromptYesNo asks the user a yes/no question and returns their response PromptYesNo(question string) bool }
UserInteractor defines an interface for interacting with the user