A comprehensive Go interface for Git operations that provides a clean, type-safe API for interacting with Git repositories.
The Git interface is part of the github.com/iomodo/staff module. No additional dependencies are required beyond the standard library.
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) }
The main interface that defines all Git operations:
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 }
Represents the current state of the repository:
type Status struct {
Branch string
IsClean bool
Staged []FileStatus
Unstaged []FileStatus
Untracked []string
Conflicts []string
}
Represents a Git commit:
type Commit struct {
Hash string
Author Author
Committer Author
Message string
Parents []string
Timestamp time.Time
Files []CommitFile
}
Represents a Git branch:
type Branch struct {
Name string
IsCurrent bool
IsRemote bool
Commit string
Message string
}
// 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) }
// 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) }
// 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)
// 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) }
// 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) }
The Git interface provides detailed error information through the GitError type:
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)
}
}
You can customize the Git instance with specific configuration:
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)
The Git interface is not thread-safe. If you need to use it from multiple goroutines, you should either:
This code is part of the github.com/iomodo/staff project and follows the same license terms.