blob: 18ccc1f15f30016fd10e6e5b628ba9ee048ea138 [file] [log] [blame] [view]
# Git Interface for Go
A comprehensive Go interface for Git operations that provides a clean, type-safe API for interacting with Git repositories.
## Features
- **Complete Git Operations**: Supports all major Git operations including repository management, commits, branches, remotes, tags, and more
- **Type-Safe API**: Strongly typed interfaces and structs for better code safety
- **Context Support**: All operations support context for cancellation and timeouts
- **Error Handling**: Comprehensive error handling with detailed error messages
- **Flexible Configuration**: Configurable timeouts and environment variables
- **Easy to Use**: Simple and intuitive API design
## Installation
The Git interface is part of the `github.com/iomodo/staff` module. No additional dependencies are required beyond the standard library.
## Quick Start
```go
package main
import (
"context"
"fmt"
"log"
"github.com/iomodo/staff/git"
)
func main() {
ctx := context.Background()
// Create a Git instance
git := git.DefaultGit("/path/to/your/repo")
// Open an existing repository
if err := git.Open(ctx, "/path/to/your/repo"); err != nil {
log.Fatalf("Failed to open repository: %v", err)
}
// Get repository status
status, err := git.Status(ctx)
if err != nil {
log.Fatalf("Failed to get status: %v", err)
}
fmt.Printf("Current branch: %s\n", status.Branch)
fmt.Printf("Repository is clean: %t\n", status.IsClean)
}
```
## Core Interface
### GitInterface
The main interface that defines all Git operations:
```go
type GitInterface interface {
// Repository operations
Init(ctx context.Context, path string) error
Clone(ctx context.Context, url, path string) error
IsRepository(ctx context.Context, path string) (bool, error)
// Status and information
Status(ctx context.Context) (*Status, error)
Log(ctx context.Context, options LogOptions) ([]Commit, error)
Show(ctx context.Context, ref string) (*Commit, error)
// Branch operations
ListBranches(ctx context.Context) ([]Branch, error)
CreateBranch(ctx context.Context, name string, startPoint string) error
Checkout(ctx context.Context, ref string) error
DeleteBranch(ctx context.Context, name string, force bool) error
GetCurrentBranch(ctx context.Context) (string, error)
// Commit operations
Add(ctx context.Context, paths []string) error
AddAll(ctx context.Context) error
Commit(ctx context.Context, message string, options CommitOptions) error
// Remote operations
ListRemotes(ctx context.Context) ([]Remote, error)
AddRemote(ctx context.Context, name, url string) error
RemoveRemote(ctx context.Context, name string) error
Fetch(ctx context.Context, remote string, options FetchOptions) error
Pull(ctx context.Context, remote, branch string) error
Push(ctx context.Context, remote, branch string, options PushOptions) error
// Merge operations
Merge(ctx context.Context, ref string, options MergeOptions) error
MergeBase(ctx context.Context, ref1, ref2 string) (string, error)
// Configuration
GetConfig(ctx context.Context, key string) (string, error)
SetConfig(ctx context.Context, key, value string) error
GetUserConfig(ctx context.Context) (*UserConfig, error)
SetUserConfig(ctx context.Context, config UserConfig) error
}
```
## Data Structures
### Status
Represents the current state of the repository:
```go
type Status struct {
Branch string
IsClean bool
Staged []FileStatus
Unstaged []FileStatus
Untracked []string
Conflicts []string
}
```
### Commit
Represents a Git commit:
```go
type Commit struct {
Hash string
Author Author
Committer Author
Message string
Parents []string
Timestamp time.Time
Files []CommitFile
}
```
### Branch
Represents a Git branch:
```go
type Branch struct {
Name string
IsCurrent bool
IsRemote bool
Commit string
Message string
}
```
## Usage Examples
### Repository Management
```go
// Initialize a new repository
git := git.DefaultGit("/path/to/new/repo")
if err := git.Init(ctx, "/path/to/new/repo"); err != nil {
log.Fatal(err)
}
// Clone a repository
if err := git.Clone(ctx, "https://github.com/user/repo.git", "/path/to/clone"); err != nil {
log.Fatal(err)
}
```
### Basic Git Workflow
```go
// Stage files
if err := git.Add(ctx, []string{"file1.txt", "file2.txt"}); err != nil {
log.Fatal(err)
}
// Or stage all changes
if err := git.AddAll(ctx); err != nil {
log.Fatal(err)
}
// Commit changes
commitOptions := git.CommitOptions{
AllowEmpty: false,
}
if err := git.Commit(ctx, "Add new feature", commitOptions); err != nil {
log.Fatal(err)
}
```
### Branch Operations
```go
// List all branches
branches, err := git.ListBranches(ctx)
if err != nil {
log.Fatal(err)
}
for _, branch := range branches {
fmt.Printf("Branch: %s (current: %t)\n", branch.Name, branch.IsCurrent)
}
// Create a new branch
if err := git.CreateBranch(ctx, "feature/new-feature", ""); err != nil {
log.Fatal(err)
}
// Switch to a branch
if err := git.Checkout(ctx, "feature/new-feature"); err != nil {
log.Fatal(err)
}
// Get current branch
currentBranch, err := git.GetCurrentBranch(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Current branch: %s\n", currentBranch)
```
### Remote Operations
```go
// Add a remote
if err := git.AddRemote(ctx, "origin", "https://github.com/user/repo.git"); err != nil {
log.Fatal(err)
}
// List remotes
remotes, err := git.ListRemotes(ctx)
if err != nil {
log.Fatal(err)
}
for _, remote := range remotes {
fmt.Printf("Remote: %s -> %s\n", remote.Name, remote.URL)
}
// Fetch from remote
fetchOptions := git.FetchOptions{
All: true,
Tags: true,
}
if err := git.Fetch(ctx, "", fetchOptions); err != nil {
log.Fatal(err)
}
// Push to remote
pushOptions := git.PushOptions{
SetUpstream: true,
}
if err := git.Push(ctx, "origin", "main", pushOptions); err != nil {
log.Fatal(err)
}
```
### Configuration
```go
// Get user configuration
userConfig, err := git.GetUserConfig(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s <%s>\n", userConfig.Name, userConfig.Email)
// Set user configuration
newConfig := git.UserConfig{
Name: "John Doe",
Email: "john@example.com",
}
if err := git.SetUserConfig(ctx, newConfig); err != nil {
log.Fatal(err)
}
// Get/set custom config
value, err := git.GetConfig(ctx, "core.editor")
if err != nil {
log.Fatal(err)
}
if err := git.SetConfig(ctx, "core.editor", "vim"); err != nil {
log.Fatal(err)
}
```
## Error Handling
The Git interface provides detailed error information through the `GitError` type:
```go
if err := git.Commit(ctx, "Invalid commit", git.CommitOptions{}); err != nil {
if gitErr, ok := err.(*git.GitError); ok {
fmt.Printf("Git command '%s' failed: %v\n", gitErr.Command, gitErr.Err)
fmt.Printf("Output: %s\n", gitErr.Output)
}
}
```
## Configuration
You can customize the Git instance with specific configuration:
```go
config := git.GitConfig{
Timeout: 60 * time.Second,
Env: map[string]string{
"GIT_AUTHOR_NAME": "Custom Author",
"GIT_AUTHOR_EMAIL": "author@example.com",
},
}
git := git.NewGit("/path/to/repo", config)
```
## Thread Safety
The Git interface is not thread-safe. If you need to use it from multiple goroutines, you should either:
1. Use separate Git instances for each goroutine
2. Use a mutex to synchronize access
3. Use channels to serialize operations
## Requirements
- Go 1.24.4 or later
- Git installed and available in PATH
- Appropriate permissions to access the repository
## License
This code is part of the `github.com/iomodo/staff` project and follows the same license terms.