Documentation
¶
Overview ¶
Package git provides Git repository operations and utilities for Hitch.
Overview ¶
This package wraps the go-git library with Hitch-specific functionality, providing a higher-level interface for Git operations with enhanced safety, validation, and conflict handling.
Core Types ¶
The main type is Repo, which wraps go-git's Repository:
type Repo struct {
*git.Repository
workdir string
}
Repo provides convenient methods for common Git operations while maintaining access to the underlying go-git functionality.
Key Components ¶
The package consists of several focused components:
- repo.go: Core repository operations (branch, checkout, merge, commit)
- operations.go: Higher-level operations (force-with-lease push)
- validator.go: Repository validation and health checks
- sanitizer.go: Input sanitization for branch names and user input
- conflict_handler.go: Merge conflict detection and handling
Repository Operations ¶
Common repository operations:
// Open a repository
repo, err := git.OpenRepo(".")
if err != nil {
return err
}
// Get current branch
branch, err := repo.CurrentBranch()
// Create and checkout a branch
err = repo.CreateBranch("feature/new", "main")
err = repo.Checkout("feature/new")
// Check for uncommitted changes
hasChanges, err := repo.HasUncommittedChanges("HEAD")
// Merge branches
err = repo.Merge("feature/branch", "Merge feature branch")
Input Sanitization ¶
The InputSanitizer protects against command injection and invalid input:
sanitizer := git.NewInputSanitizer()
// Validate branch names
if !sanitizer.IsValidBranchName("feature/login") {
return errors.New("invalid branch name")
}
// Sanitize environment names
env := sanitizer.SanitizeEnvironmentName("dev-123")
// Validate email addresses
if !sanitizer.IsValidEmail("[email protected]") {
return errors.New("invalid email")
}
The sanitizer blocks:
- Branch names with special characters (, ', ", `, ;, $, etc.)
- Environment names with path traversal attempts (../)
- Invalid email addresses
- Command injection attempts
Repository Validation ¶
The RepositoryValidator performs comprehensive health checks:
validator := git.NewRepositoryValidator(repo)
result := validator.ValidateRepository()
if !result.IsValid {
for _, err := range result.Errors {
fmt.Println("Error:", err)
}
for _, warn := range result.Warnings {
fmt.Println("Warning:", warn)
}
}
Validation checks include:
- Repository exists and is accessible
- .git directory is present and valid
- HEAD reference exists
- Working directory is clean (for operations requiring it)
- No corrupted objects
- Metadata branch (hitch-metadata) is properly configured
Conflict Handling ¶
The ConflictHandler detects and reports merge conflicts:
handler := git.NewConflictHandler(repo)
result := handler.CheckForConflicts("feature/branch", "main")
if result.HasConflicts {
fmt.Println("Conflicts detected:")
for _, file := range result.ConflictingFiles {
fmt.Printf(" - %s\n", file)
}
}
Force-with-Lease ¶
Safe force pushing with lease validation:
err := repo.ForcePushWithLease("dev", "origin/dev", "abc123")
This ensures:
- Remote branch hasn't been updated by others
- Local changes won't overwrite unexpected commits
- Safe force push for environment rebuilds
Branch Operations ¶
Comprehensive branch management:
// Check if branch exists
exists := repo.BranchExists("feature/login")
// Get all branches
branches, err := repo.Branches()
// Delete branch
err = repo.DeleteBranch("feature/old", false) // safe delete
err = repo.DeleteBranch("feature/old", true) // force delete
// Get branch creation time
createdAt, err := repo.BranchCreatedAt("feature/login")
// Check if branch is merged
merged, err := repo.IsBranchMerged("feature/login", "main")
User Information ¶
Extract Git user configuration:
name, err := repo.UserName() email, err := repo.UserEmail()
Working Directory ¶
Access to repository paths:
workdir := repo.WorkingTree() gitDir := filepath.Join(workdir, ".git")
Safety Features ¶
The package implements multiple safety mechanisms:
- Input validation before any git commands
- Sanitization of all user-provided strings
- Pre-flight checks for repository state
- Conflict detection before merges
- Force-with-lease for safe force pushes
- Comprehensive error messages
Error Handling ¶
All functions return descriptive errors:
err := repo.Merge("feature/branch", "Merge message")
if err != nil {
// Error includes context about what failed
return fmt.Errorf("merge failed: %w", err)
}
Testing ¶
The package includes extensive tests:
- sanitizer_test.go: Input sanitization tests
- sanitizer_edge_cases_test.go: Edge cases and attack vectors
- conflict_handler_test.go: Merge conflict detection
- validator_test.go: Repository validation
- force_with_lease_test.go: Safe force push operations
- repo_test.go: Core repository operations
All tests use isolated environments (enforced globally).
Dependencies ¶
External dependencies:
- github.com/go-git/go-git/v5: Pure Go Git implementation
Internal dependencies:
- None (this is a foundational package)
Example: Complete Workflow ¶
// Open repository
repo, err := git.OpenRepo(".")
if err != nil {
return err
}
// Validate repository state
validator := git.NewRepositoryValidator(repo)
if result := validator.ValidateRepository(); !result.IsValid {
return fmt.Errorf("repository validation failed")
}
// Sanitize input
sanitizer := git.NewInputSanitizer()
if !sanitizer.IsValidBranchName(branchName) {
return fmt.Errorf("invalid branch name: %s", branchName)
}
// Check for conflicts
conflictHandler := git.NewConflictHandler(repo)
result := conflictHandler.CheckForConflicts(branchName, "main")
if result.HasConflicts {
return fmt.Errorf("merge conflicts detected")
}
// Perform operation
err = repo.Merge(branchName, "Merge feature branch")
if err != nil {
return err
}
Index ¶
- type ConflictHandler
- func (ch *ConflictHandler) ConfirmAction(message string, defaultValue bool) bool
- func (ch *ConflictHandler) DetectConflicts(sourceBranch, targetBranch string, mergeErr error) *MergeConflict
- func (ch *ConflictHandler) HandleConflict(conflict *MergeConflict) (ConflictResolution, error)
- func (ch *ConflictHandler) IsTerminal() bool
- type ConflictResolution
- type GitRepository
- type InputSanitizer
- func (s *InputSanitizer) SanitizeBranchName(branch string) error
- func (s *InputSanitizer) SanitizeCommitMessage(message string) error
- func (s *InputSanitizer) SanitizeEnvironmentVariable(name, value string) error
- func (s *InputSanitizer) SanitizeFileName(filename string) error
- func (s *InputSanitizer) SanitizeRemoteName(remote string) error
- func (s *InputSanitizer) SanitizeURL(url string) error
- func (s *InputSanitizer) ValidateGitConfigKey(key string) error
- func (s *InputSanitizer) ValidateGitConfigValue(value string) error
- func (s *InputSanitizer) ValidateRef(ref string) error
- type MergeConflict
- type Repo
- func (r *Repo) BranchExists(name string) bool
- func (r *Repo) BranchExistsDetailed(branchName string) (bool, error)
- func (r *Repo) CanMerge(branch string) (bool, error)
- func (r *Repo) Checkout(ref string) error
- func (r *Repo) CreateBranch(name string, fromRef string) error
- func (r *Repo) CurrentBranch() (string, error)
- func (r *Repo) CurrentCommitSHA() (string, error)
- func (r *Repo) DeleteBranch(name string, force bool) error
- func (r *Repo) DeleteRemoteBranch(remoteName string, branchName string) error
- func (r *Repo) DetectForcePush(branchName, oldSHA string) (bool, error)
- func (r *Repo) DiffBranches(branch1, branch2 string) (string, error)
- func (r *Repo) GetBranchCommitSHA(branchName string) (string, error)
- func (r *Repo) GetBranches() ([]string, error)
- func (r *Repo) GetBranchesMatching(pattern string) ([]string, error)
- func (r *Repo) GetCommitsBetween(oldSHA, newSHA string) (int, error)
- func (r *Repo) HasUncommittedChanges(branch string) (bool, error)
- func (r *Repo) IsDetachedHead() bool
- func (r *Repo) IsGitRepository() bool
- func (r *Repo) IsMergeInProgress() bool
- func (r *Repo) Merge(branch string, message string) error
- func (r *Repo) MergeAbort() error
- func (r *Repo) MergeBase(branch1, branch2 string) (string, error)
- func (r *Repo) MergeSquash(branch string, message string) error
- func (r *Repo) Pull(remoteName string, branchName string) error
- func (r *Repo) Push(remoteName string, branchName string, force bool) error
- func (r *Repo) UserEmail() (string, error)
- func (r *Repo) UserName() (string, error)
- func (r *Repo) WorkingTree() string
- type RepositoryLock
- type RepositoryValidator
- type ValidationResult
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ConflictHandler ¶ added in v0.1.21
type ConflictHandler struct {
// contains filtered or unexported fields
}
ConflictHandler manages interactive merge conflict resolution
func NewConflictHandler ¶ added in v0.1.21
func NewConflictHandler(repo *Repo) *ConflictHandler
NewConflictHandler creates a new conflict handler
func (*ConflictHandler) ConfirmAction ¶ added in v0.1.21
func (ch *ConflictHandler) ConfirmAction(message string, defaultValue bool) bool
ConfirmAction asks for user confirmation with a custom message
func (*ConflictHandler) DetectConflicts ¶ added in v0.1.21
func (ch *ConflictHandler) DetectConflicts(sourceBranch, targetBranch string, mergeErr error) *MergeConflict
DetectConflicts analyzes a merge error to extract conflict information
func (*ConflictHandler) HandleConflict ¶ added in v0.1.21
func (ch *ConflictHandler) HandleConflict(conflict *MergeConflict) (ConflictResolution, error)
HandleConflict presents the interactive conflict resolution interface
func (*ConflictHandler) IsTerminal ¶ added in v0.1.21
func (ch *ConflictHandler) IsTerminal() bool
IsTerminal checks if we're running in an interactive terminal
type ConflictResolution ¶ added in v0.1.21
type ConflictResolution int
ConflictResolution represents the user's choice for conflict resolution
const ( ResolutionRollback ConflictResolution = iota ResolutionPause ResolutionManual )
type GitRepository ¶ added in v0.1.21
type GitRepository interface {
// Core repository information
WorkingTree() string
CurrentBranch() (string, error)
IsDetachedHead() bool
CurrentCommitSHA() (string, error)
IsGitRepository() bool
// User configuration
UserName() (string, error)
UserEmail() (string, error)
HasUncommittedChanges(branch string) (bool, error)
}
GitRepository defines the interface for git repository operations
type InputSanitizer ¶ added in v0.1.21
type InputSanitizer struct{}
InputSanitizer validates and sanitizes user inputs for git commands
func NewInputSanitizer ¶ added in v0.1.21
func NewInputSanitizer() *InputSanitizer
NewInputSanitizer creates a new input sanitizer
func (*InputSanitizer) SanitizeBranchName ¶ added in v0.1.21
func (s *InputSanitizer) SanitizeBranchName(branch string) error
SanitizeBranchName validates that a branch name is safe for git operations
func (*InputSanitizer) SanitizeCommitMessage ¶ added in v0.1.21
func (s *InputSanitizer) SanitizeCommitMessage(message string) error
SanitizeCommitMessage validates that a commit message is safe
func (*InputSanitizer) SanitizeEnvironmentVariable ¶ added in v0.1.21
func (s *InputSanitizer) SanitizeEnvironmentVariable(name, value string) error
SanitizeEnvironmentVariable validates that an environment variable name and value are safe
func (*InputSanitizer) SanitizeFileName ¶ added in v0.1.21
func (s *InputSanitizer) SanitizeFileName(filename string) error
SanitizeFileName validates that a filename is safe for git operations
func (*InputSanitizer) SanitizeRemoteName ¶ added in v0.1.21
func (s *InputSanitizer) SanitizeRemoteName(remote string) error
SanitizeRemoteName validates that a remote name is safe
func (*InputSanitizer) SanitizeURL ¶ added in v0.1.21
func (s *InputSanitizer) SanitizeURL(url string) error
SanitizeURL validates that a URL is safe for git operations
func (*InputSanitizer) ValidateGitConfigKey ¶ added in v0.1.21
func (s *InputSanitizer) ValidateGitConfigKey(key string) error
ValidateGitConfigKey validates that a git config key is safe
func (*InputSanitizer) ValidateGitConfigValue ¶ added in v0.1.21
func (s *InputSanitizer) ValidateGitConfigValue(value string) error
ValidateGitConfigValue validates that a git config value is safe
func (*InputSanitizer) ValidateRef ¶ added in v0.1.21
func (s *InputSanitizer) ValidateRef(ref string) error
ValidateRef validates that a git reference is safe
type MergeConflict ¶ added in v0.1.21
type MergeConflict struct {
SourceBranch string
TargetBranch string
ConflictingFiles []string
OriginalError error
IsSquashMerge bool
}
MergeConflict represents information about a merge conflict
type Repo ¶
type Repo struct {
*git.Repository
// contains filtered or unexported fields
}
Repo wraps a git repository with helpful methods
func (*Repo) BranchExists ¶
BranchExists checks if a branch exists (local or remote)
func (*Repo) BranchExistsDetailed ¶ added in v1.1.12
BranchExistsDetailed checks if a branch still exists (different return signature than existing method)
func (*Repo) CanMerge ¶ added in v0.1.21
CanMerge checks if a merge would succeed without actually performing it
func (*Repo) CreateBranch ¶
CreateBranch creates a new branch
func (*Repo) CurrentBranch ¶
CurrentBranch returns the name of the current branch
func (*Repo) CurrentCommitSHA ¶
CurrentCommitSHA returns the SHA of the current commit
func (*Repo) DeleteBranch ¶
DeleteBranch deletes a branch
func (*Repo) DeleteRemoteBranch ¶
DeleteRemoteBranch deletes a remote branch
func (*Repo) DetectForcePush ¶ added in v1.1.12
DetectForcePush checks if a branch has been force-pushed by checking if the old SHA still exists in its history
func (*Repo) DiffBranches ¶ added in v1.1.10
DiffBranches gets the diff between two branches
func (*Repo) GetBranchCommitSHA ¶ added in v1.1.12
GetBranchCommitSHA returns the current HEAD SHA for a specific branch
func (*Repo) GetBranches ¶ added in v1.1.8
GetBranches returns a list of all local branches
func (*Repo) GetBranchesMatching ¶ added in v1.1.8
GetBranchesMatching returns branches that match the given pattern (supports wildcards)
func (*Repo) GetCommitsBetween ¶ added in v1.1.12
GetCommitsBetween returns the number of commits between two SHAs
func (*Repo) HasUncommittedChanges ¶
HasUncommittedChanges checks if a branch has uncommitted changes Note: This requires executing git commands as go-git doesn't support this well
func (*Repo) IsDetachedHead ¶
IsDetachedHead checks if HEAD is in detached state
func (*Repo) IsGitRepository ¶ added in v0.1.21
IsGitRepository checks if the current directory is a git repository
func (*Repo) IsMergeInProgress ¶ added in v0.1.21
IsMergeInProgress checks if there's an ongoing merge operation
func (*Repo) Merge ¶
Merge merges a branch into the current branch with an optional message Note: This uses git command as go-git's merge support is limited
func (*Repo) MergeSquash ¶
MergeSquash squash merges a branch into the current branch
func (*Repo) Push ¶
Push pushes changes to remote Uses force-with-lease for safety when force is specified
func (*Repo) WorkingTree ¶ added in v0.1.7
WorkingTree returns the working directory path of the repository
type RepositoryLock ¶ added in v1.1.10
type RepositoryLock struct {
// contains filtered or unexported fields
}
RepositoryLock provides cross-process synchronization for Git operations
func GetRepositoryLock ¶ added in v1.1.10
func GetRepositoryLock(repoPath string) *RepositoryLock
GetRepositoryLock returns a repository lock for the given path
func NewRepositoryLock ¶ added in v1.1.10
func NewRepositoryLock(repoPath string) *RepositoryLock
NewRepositoryLock creates a new repository lock for the given path
func (*RepositoryLock) Lock ¶ added in v1.1.10
func (rl *RepositoryLock) Lock() error
Lock acquires exclusive access to the repository for Git operations
func (*RepositoryLock) Unlock ¶ added in v1.1.10
func (rl *RepositoryLock) Unlock() error
Unlock releases the repository lock
func (*RepositoryLock) WithLock ¶ added in v1.1.10
func (rl *RepositoryLock) WithLock(fn func() error) error
WithLock executes a function while holding the repository lock
type RepositoryValidator ¶ added in v0.1.21
type RepositoryValidator struct {
// contains filtered or unexported fields
}
RepositoryValidator checks repository health and integrity
func NewRepositoryValidator ¶ added in v0.1.21
func NewRepositoryValidator(repo *Repo) *RepositoryValidator
NewRepositoryValidator creates a new repository validator
func (*RepositoryValidator) ValidateRepository ¶ added in v0.1.21
func (v *RepositoryValidator) ValidateRepository() ValidationResult
ValidateRepository performs comprehensive repository validation
type ValidationResult ¶ added in v0.1.21
ValidationResult contains the result of repository validation