git

package
v1.1.2 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2025 License: MIT Imports: 13 Imported by: 0

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("user@example.com") {
    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 Docker environments with the dockertest build tag.

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

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 operations This provides abstraction for Git repository operations while maintaining testability

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 MergeConflictError

type MergeConflictError struct {
	Branch  string
	Message string
}

MergeConflictError is returned when a merge results in conflicts

func (*MergeConflictError) Error

func (e *MergeConflictError) Error() string

type Repo

type Repo struct {
	*git.Repository
	// contains filtered or unexported fields
}

Repo wraps a git repository with helpful methods

func OpenRepo

func OpenRepo(path string) (*Repo, error)

OpenRepo opens a git repository in the current or specified directory. If path is empty, it opens the repository in the current directory. Returns a Repo wrapper that provides additional helper methods.

func (*Repo) BranchExists

func (r *Repo) BranchExists(name string) bool

BranchExists checks if a branch exists (local or remote)

func (*Repo) CanMerge added in v0.1.21

func (r *Repo) CanMerge(branch string) (bool, error)

CanMerge checks if a merge would succeed without actually performing it

func (*Repo) Checkout

func (r *Repo) Checkout(ref string) error

Checkout checks out a branch or commit

func (*Repo) CreateBranch

func (r *Repo) CreateBranch(name string, fromRef string) error

CreateBranch creates a new branch

func (*Repo) CurrentBranch

func (r *Repo) CurrentBranch() (string, error)

CurrentBranch returns the name of the current branch

func (*Repo) CurrentCommitSHA

func (r *Repo) CurrentCommitSHA() (string, error)

CurrentCommitSHA returns the SHA of the current commit

func (*Repo) DeleteBranch

func (r *Repo) DeleteBranch(name string, force bool) error

DeleteBranch deletes a branch

func (*Repo) DeleteRemoteBranch

func (r *Repo) DeleteRemoteBranch(remoteName string, branchName string) error

DeleteRemoteBranch deletes a branch from remote

func (*Repo) HasUncommittedChanges

func (r *Repo) HasUncommittedChanges(branch string) (bool, error)

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

func (r *Repo) IsDetachedHead() bool

IsDetachedHead checks if HEAD is in detached state

func (*Repo) IsGitRepository added in v0.1.21

func (r *Repo) IsGitRepository() bool

IsGitRepository checks if the current directory is a git repository

func (*Repo) IsMergeInProgress added in v0.1.21

func (r *Repo) IsMergeInProgress() bool

IsMergeInProgress checks if there's an ongoing merge operation

func (*Repo) Merge

func (r *Repo) Merge(branch string, message string) error

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

func (r *Repo) MergeAbort() error

MergeAbort aborts an in-progress merge

func (*Repo) MergeSquash

func (r *Repo) MergeSquash(branch string, message string) error

MergeSquash squash merges a branch into the current branch

func (*Repo) Pull

func (r *Repo) Pull(remoteName string, branchName string) error

Pull pulls changes from remote

func (*Repo) Push

func (r *Repo) Push(remoteName string, branchName string, force bool) error

Push pushes changes to remote Uses force-with-lease for safety when force is specified

func (*Repo) UserEmail

func (r *Repo) UserEmail() (string, error)

UserEmail returns the configured git user email

func (*Repo) UserName

func (r *Repo) UserName() (string, error)

UserName returns the configured git user name

func (*Repo) WorkingTree added in v0.1.7

func (r *Repo) WorkingTree() string

WorkingTree returns the working directory path of the repository

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

type ValidationResult struct {
	IsHealthy bool
	Issues    []string
	Warnings  []string
}

ValidationResult contains the result of repository validation

Jump to

Keyboard shortcuts

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