Documentation
¶
Overview ¶
Package git provides a clean, type-safe wrapper around go-git for Git repository operations.
This library uses fs/billy for all filesystem operations, wraps go-git types with enhanced platform types while providing escape hatches, and organizes functionality into focused, maintainable files.
The library targets Worker Service caching workflows (clone → update → worktree creation) while supporting bootstrap CLI and discovery needs. It prioritizes simplicity in common operations while providing full access to underlying go-git when needed.
Architecture ¶
The library is built on several key principles:
- Thin wrappers over go-git (not reimplementing Git)
- Billy filesystem for all I/O operations
- Escape hatches via Underlying() methods for advanced use cases
- RemoteOperations and WorktreeOperations interfaces enable testing by mocking operations
- Organized by operation type (repository, worktree, branch, tag, commit, remote)
- Worktree operations use git CLI for true linked worktree support
Core Types ¶
Repository wraps go-git with platform conventions and provides methods for all Git operations.
Worktree represents a Git worktree with path tracking and operations. This library provides full support for linked worktrees via git CLI commands, allowing multiple branches to be checked out simultaneously in separate working directories.
Commit, Branch, Tag, and Remote are value types for representing Git objects.
Billy Filesystem Requirement ¶
All repository operations use the go-billy filesystem abstraction. By default, operations use the local OS filesystem (osfs), but you can provide custom filesystems for testing (memfs) or other specialized use cases.
The filesystem is scoped to the repository path, so all file operations are relative to the repository root.
Worktree Support via Git CLI ¶
Worktree operations (CreateWorktree, ListWorktrees, Remove, Lock, Unlock, PruneWorktrees) use git CLI commands to provide true linked worktree support. This requires:
- Git must be installed and available in the system PATH
- Operations must use the OS filesystem (osfs) - memory filesystems (memfs) are not supported
- The WorktreeOperations interface can be mocked for testing without git CLI dependency
When a memory filesystem is detected, worktree operations will return an error explaining that worktrees require the OS filesystem.
Factory Functions ¶
Init initializes a new Git repository at the specified path.
Clone clones a repository from a remote URL.
Open opens an existing repository from a path.
All factory functions accept RepositoryOption arguments for customization (filesystem, authentication, depth, etc.).
Authentication ¶
The library provides helper functions for creating authentication:
// SSH key authentication
auth, err := git.SSHKeyFile("git", "/home/user/.ssh/id_rsa", "")
// SSH key from memory
auth, err := git.SSHKeyAuth("git", pemBytes, "")
// Basic authentication (HTTPS)
auth := git.BasicAuth("username", "password")
// No authentication (public repositories)
auth := git.EmptyAuth()
// Use with operations
repo, err := git.Clone(ctx, "https://github.com/org/repo", git.WithAuth(auth))
Escape Hatches ¶
All wrapper types provide Underlying() methods to access the raw go-git objects for advanced operations not covered by this library:
gogitRepo := repo.Underlying() // Access go-git repository fs := repo.Filesystem() // Access billy filesystem gogitCommit := commit.Underlying() // Access go-git commit gogitWorktree := worktree.Underlying() // Access go-git worktree
This allows you to use the full power of go-git when needed while still benefiting from the simplified API for common operations.
RemoteOperations Interface ¶
The RemoteOperations interface abstracts network operations (Clone, Fetch, Push) to enable testing without actual network calls. Tests can provide a mock implementation using WithRemoteOperations() option.
The default implementation uses go-git's network operations. Custom implementations can be used for testing, proxying, or adding custom behavior around network operations.
Usage Examples ¶
## Example 1: Initialize New Repository
package main
import (
"github.com/jmgilman/go/git"
)
func main() {
// Initialize new repository
repo, err := git.Init("/path/to/repos/my-repo")
if err != nil {
panic(err)
}
// Create initial commit
hash, err := repo.CreateCommit(git.CommitOptions{
Author: "User",
Email: "user@example.com",
Message: "Initial commit",
AllowEmpty: true,
})
if err != nil {
panic(err)
}
println("Initialized repository with commit:", hash)
}
## Example 2: Clone and Create Worktree
package main
import (
"context"
"github.com/go-git/go-git/v5/plumbing"
"github.com/jmgilman/go/git"
)
func main() {
ctx := context.Background()
// Clone repository (shallow)
repo, err := git.Clone(ctx, "https://github.com/org/repo",
git.WithDepth(1),
git.WithSingleBranch())
if err != nil {
panic(err)
}
// Create linked worktree at specific commit
wt, err := repo.CreateWorktree("/tmp/worktree", git.WorktreeOptions{
Hash: plumbing.NewHash("abc123..."),
})
if err != nil {
panic(err)
}
defer wt.Remove()
// Create worktree with new branch
wt2, err := repo.CreateWorktree("/tmp/feature", git.WorktreeOptions{
Branch: plumbing.NewBranchReferenceName("main"),
CreateBranch: "new-feature",
})
if err != nil {
panic(err)
}
// Lock worktree to prevent pruning
err = wt2.Lock("Working on feature")
// Work with worktrees...
println("Worktree created at:", wt.Path())
}
## Example 3: Update Cached Repository
func updateCache(ctx context.Context, repoPath string) error {
// Open existing repository
repo, err := git.Open(repoPath)
if err != nil {
return err
}
// Fetch updates
auth := git.BasicAuth("user", "token")
err = repo.Fetch(ctx, git.FetchOptions{
Auth: auth,
})
if err != nil {
return err
}
return nil
}
## Example 4: Walk Commits Between Tags
func listChanges(repo *git.Repository, from, to string) error {
for commit, err := range repo.WalkCommits(from, to) {
if err != nil {
return err
}
fmt.Printf("%s: %s (%s)\n",
commit.Hash[:7],
commit.Message,
commit.Author,
)
}
return nil
}
## Example 5: Create Commit (GitOps)
func updateReleasePointer(ctx context.Context, repo *git.Repository, auth git.Auth) error {
// Modify files using billy filesystem
fs := repo.Filesystem()
file, err := fs.Create("release-pointer.yaml")
if err != nil {
return err
}
// ... write content ...
file.Close()
// Stage all changes
wt, err := repo.Underlying().Worktree()
if err != nil {
return err
}
_, err = wt.Add("release-pointer.yaml")
if err != nil {
return err
}
// Create commit
hash, err := repo.CreateCommit(git.CommitOptions{
Author: "Platform Bot",
Email: "bot@platform",
Message: "Update release pointer",
})
if err != nil {
return err
}
// Push to remote (needs ctx for network operation)
err = repo.Push(ctx, git.PushOptions{
RemoteName: "origin",
RefSpecs: []string{"refs/heads/main"},
Auth: auth,
})
return err
}
## Example 6: Using Escape Hatches
func advancedOperation(repo *git.Repository) error {
// Get underlying go-git repository for advanced operations
gogitRepo := repo.Underlying()
// Use go-git API directly for operations not wrapped
// by our library
storer := gogitRepo.Storer
// ... advanced go-git operations ...
return nil
}
Error Handling ¶
The library wraps go-git errors with platform error types from the errors library. Common error types include:
- ErrNotFound: Repository, reference, tag, or branch not found
- ErrAlreadyExists: Branch, tag, or remote already exists
- ErrAuthenticationFailed: Authentication failure
- ErrNetwork: Network or connectivity issues
- ErrInvalidInput: Invalid reference or bad parameters
- ErrConflict: Dirty worktree or merge conflicts
Context and Cancellation ¶
Network operations (Clone, Fetch, Push, Pull) accept a context.Context parameter for cancellation and timeout control:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() repo, err := git.Clone(ctx, "https://github.com/org/repo")
Local operations (branch, tag, commit operations) do not require context as they operate purely on the local filesystem.
Testing ¶
The library provides a testutil sub-package with helpers for testing:
import "github.com/jmgilman/go/git/testutil" // Create in-memory repository repo, fs, err := testutil.NewMemoryRepo() // Create test commits hash, err := testutil.CreateTestCommit(repo, "Test commit") // Create test files err := testutil.CreateTestFile(fs, "test.txt", "content")
For mocking network and worktree operations in tests:
mockRemoteOps := &mockRemoteOps{...}
mockWorktreeOps := &mockWorktreeOps{...}
repo, err := git.Clone(ctx, "https://github.com/org/repo",
git.WithRemoteOperations(mockRemoteOps),
git.WithWorktreeOperations(mockWorktreeOps))
References ¶
For advanced operations not covered by this library, refer to:
- go-git documentation: https://pkg.go.dev/github.com/go-git/go-git/v5
- go-billy documentation: https://pkg.go.dev/github.com/go-git/go-billy/v5
Index ¶
- type Auth
- type Branch
- type CloneOptions
- type Commit
- type CommitOptions
- type FetchOptions
- type PullOptions
- type PushOptions
- type Remote
- type RemoteOperations
- type RemoteOptions
- type Repository
- func Clone(ctx context.Context, url string, opts ...RepositoryOption) (*Repository, error)
- func CloneWithOptions(ctx context.Context, fs billy.Filesystem, opts CloneOptions) (*Repository, error)
- func Init(path string, opts ...RepositoryOption) (*Repository, error)
- func Open(path string, opts ...RepositoryOption) (*Repository, error)
- func (r *Repository) AddRemote(opts RemoteOptions) error
- func (r *Repository) CheckoutBranch(name string) error
- func (r *Repository) CreateBranch(name string, ref string) error
- func (r *Repository) CreateBranchFromRemote(localName, remoteBranch string) error
- func (r *Repository) CreateCommit(opts CommitOptions) (string, error)
- func (r *Repository) CreateLightweightTag(name string, ref string) error
- func (r *Repository) CreateTag(name string, ref string, message string) error
- func (r *Repository) CreateWorktree(path string, opts WorktreeOptions) (*Worktree, error)
- func (r *Repository) CurrentBranch() (string, error)
- func (r *Repository) DeleteBranch(name string, force bool) error
- func (r *Repository) DeleteTag(name string) error
- func (r *Repository) Fetch(ctx context.Context, opts FetchOptions) error
- func (r *Repository) Filesystem() billy.Filesystem
- func (r *Repository) GetCommit(ref string) (*Commit, error)
- func (r *Repository) ListBranches() ([]Branch, error)
- func (r *Repository) ListRemotes() ([]Remote, error)
- func (r *Repository) ListTags() ([]Tag, error)
- func (r *Repository) ListWorktrees() ([]*Worktree, error)
- func (r *Repository) PruneWorktrees() error
- func (r *Repository) Pull(ctx context.Context, opts PullOptions) error
- func (r *Repository) Push(ctx context.Context, opts PushOptions) error
- func (r *Repository) RemoveRemote(name string) error
- func (r *Repository) Underlying() *gogit.Repository
- func (r *Repository) WalkCommits(from, to string) iter.Seq2[Commit, error]
- type RepositoryOption
- func WithAuth(auth Auth) RepositoryOption
- func WithBare() RepositoryOption
- func WithDepth(depth int) RepositoryOption
- func WithFilesystem(fs billy.Filesystem) RepositoryOption
- func WithReferenceName(ref plumbing.ReferenceName) RepositoryOption
- func WithRemoteOperations(ops RemoteOperations) RepositoryOption
- func WithShared() RepositoryOption
- func WithSingleBranch() RepositoryOption
- func WithWorktreeOperations(ops WorktreeOperations) RepositoryOption
- type SSHKeyOption
- type Tag
- type Worktree
- type WorktreeInfo
- type WorktreeOperations
- type WorktreeOptions
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Auth ¶
type Auth interface {
}
Auth is an interface for authentication methods. It is satisfied by go-git's transport.AuthMethod.
func BasicAuth ¶
BasicAuth creates HTTP basic authentication. This is commonly used with personal access tokens for HTTPS Git operations.
Parameters:
- username: username or token name
- password: password or personal access token
Returns an Auth object that can be used with Clone, Fetch, Push operations.
Example:
auth := git.BasicAuth("myuser", "ghp_mytoken")
func EmptyAuth ¶
func EmptyAuth() Auth
EmptyAuth returns nil authentication for public repositories. go-git interprets nil Auth as "no authentication needed".
This function exists for explicit documentation purposes - you can also just pass nil as the Auth field in options structs.
Example:
repo, err := git.Clone(ctx, fs, git.CloneOptions{
URL: "https://github.com/public/repo",
Auth: git.EmptyAuth(),
})
func SSHKeyAuth ¶
func SSHKeyAuth(user string, pemBytes []byte, opts ...SSHKeyOption) (Auth, error)
SSHKeyAuth creates SSH authentication from PEM-encoded key bytes. It handles both password-protected and unprotected private keys.
Parameters:
- user: SSH username (typically "git" for Git hosting services)
- pemBytes: PEM-encoded private key bytes
- opts: optional configuration (use WithSSHPassword for encrypted keys)
Returns an Auth object that can be used with Clone, Fetch, Push operations. Errors are wrapped with context for easier debugging.
Example (unencrypted key):
keyBytes, _ := os.ReadFile("~/.ssh/id_rsa")
auth, err := git.SSHKeyAuth("git", keyBytes)
if err != nil {
return err
}
Example (encrypted key):
auth, err := git.SSHKeyAuth("git", keyBytes, git.WithSSHPassword("mypassphrase"))
func SSHKeyFile ¶
func SSHKeyFile(user string, keyPath string, opts ...SSHKeyOption) (Auth, error)
SSHKeyFile creates SSH authentication by reading a key from a file. This is a convenience wrapper around SSHKeyAuth that handles file I/O.
Parameters:
- user: SSH username (typically "git" for Git hosting services)
- keyPath: path to PEM-encoded private key file
- opts: optional configuration (use WithSSHPassword for encrypted keys)
Returns an Auth object that can be used with Clone, Fetch, Push operations. File read errors and key parsing errors are wrapped with context.
Example (unencrypted key):
auth, err := git.SSHKeyFile("git", "~/.ssh/id_rsa")
if err != nil {
return err
}
Example (encrypted key):
auth, err := git.SSHKeyFile("git", "~/.ssh/id_rsa", git.WithSSHPassword("mypassphrase"))
type CloneOptions ¶
type CloneOptions struct {
URL string
Auth Auth
Depth int // 0 for full clone, >0 for shallow clone
SingleBranch bool // Clone only a single branch
ReferenceName plumbing.ReferenceName // Branch or tag to clone
}
CloneOptions configures repository cloning operations.
type Commit ¶
type Commit struct {
Hash string
Author string
Email string
Message string
Timestamp time.Time
// contains filtered or unexported fields
}
Commit is a value type containing formatted commit information. It includes an escape hatch to the underlying go-git commit object for advanced operations.
func (*Commit) Underlying ¶
Underlying returns the underlying go-git commit object for advanced operations not covered by this wrapper. This escape hatch allows direct access to go-git's full commit API when needed.
The returned *object.Commit can be used for any go-git commit operation, such as accessing the tree, parent commits, or other low-level details.
Example:
commit, _ := repo.GetCommit("HEAD")
gogitCommit := commit.Underlying()
tree, _ := gogitCommit.Tree()
// Use go-git tree operations...
type CommitOptions ¶
CommitOptions configures commit creation.
type FetchOptions ¶
type FetchOptions struct {
RemoteName string // Default: "origin"
Auth Auth
Depth int // For deepening shallow clones
}
FetchOptions configures fetch operations.
type PullOptions ¶
PullOptions configures pull operations.
type PushOptions ¶
type PushOptions struct {
RemoteName string // Default: "origin"
RefSpecs []string
Auth Auth
Force bool
}
PushOptions configures push operations.
type RemoteOperations ¶
type RemoteOperations interface {
// Clone clones a remote repository to the local filesystem.
Clone(ctx context.Context, fs billy.Filesystem, opts CloneOptions) (*Repository, error)
// Fetch downloads objects and refs from the remote repository.
Fetch(ctx context.Context, repo *Repository, opts FetchOptions) error
// Push uploads objects and refs to the remote repository.
Push(ctx context.Context, repo *Repository, opts PushOptions) error
}
RemoteOperations defines the interface for Git remote network operations. This interface allows for testing by enabling mock implementations that don't require actual network access.
The default implementation (defaultRemoteOps) delegates to go-git's network operations. Tests can replace the package-level remoteOps variable with a mock implementation to avoid network calls.
type RemoteOptions ¶
RemoteOptions configures remote management.
type Repository ¶
type Repository struct {
// contains filtered or unexported fields
}
Repository wraps a go-git repository with platform conventions. It stores both the underlying go-git repository and a billy filesystem for all I/O operations, providing escape hatches for advanced use cases.
func Clone ¶
func Clone(ctx context.Context, url string, opts ...RepositoryOption) (*Repository, error)
Clone clones a remote repository to the local filesystem.
By default, Clone creates a full clone using the local filesystem rooted at a generated path based on the repository name, and uses the internal RemoteOperations implementation for network operations.
The behavior can be customized using RepositoryOption functions to set authentication, shallow clone depth, filesystem location, and mock network operations for testing.
Returns the cloned Repository or an error if cloning fails. Common errors include ErrNotFound if the remote repository doesn't exist, ErrUnauthorized for authentication failures, or network errors.
Examples:
// Clone a public repository
repo, err := git.Clone(ctx, "https://github.com/org/repo")
// Clone with authentication
auth, _ := git.SSHKeyFile("git", "~/.ssh/id_rsa")
repo, err := git.Clone(ctx, "git@github.com:org/repo.git", git.WithAuth(auth))
// Shallow clone (depth=1)
repo, err := git.Clone(ctx, "https://github.com/org/repo",
git.WithDepth(1),
git.WithSingleBranch())
// Clone with custom filesystem (for testing)
repo, err := git.Clone(ctx, "https://github.com/org/repo",
git.WithFilesystem(memfs.New()),
git.WithRemoteOperations(mockOps))
func CloneWithOptions ¶
func CloneWithOptions(ctx context.Context, fs billy.Filesystem, opts CloneOptions) (*Repository, error)
CloneWithOptions is a legacy function that clones using CloneOptions. Deprecated: Use Clone with functional options instead:
git.Clone(ctx, url, git.WithAuth(auth), git.WithFilesystem(fs))
func Init ¶
func Init(path string, opts ...RepositoryOption) (*Repository, error)
Init creates a new Git repository at the specified path.
By default, Init creates a standard (non-bare) repository using the local filesystem rooted at the specified path. This behavior can be customized using RepositoryOption functions.
Returns the initialized Repository or an error if initialization fails. Common errors include ErrAlreadyExists if a repository already exists at the specified path, or filesystem errors if the path cannot be created.
Examples:
// Create a standard repository
repo, err := git.Init("/path/to/repo")
// Create a bare repository
repo, err := git.Init("/path/to/repo.git", git.WithBare())
// Create repository with custom filesystem (for testing)
repo, err := git.Init("/path/to/repo", git.WithFilesystem(memfs.New()))
func Open ¶
func Open(path string, opts ...RepositoryOption) (*Repository, error)
Open opens an existing Git repository at the specified path.
By default, Open uses the local filesystem rooted at the specified path. This behavior can be customized using RepositoryOption functions.
Returns the opened Repository or an error if opening fails. Common errors include ErrNotFound if no repository exists at the path, or filesystem errors if the path cannot be accessed.
Examples:
// Open a repository from the local filesystem
repo, err := git.Open("/path/to/repo")
// Open with custom filesystem (for testing)
repo, err := git.Open("/path/to/repo", git.WithFilesystem(memfs.New()))
func (*Repository) AddRemote ¶
func (r *Repository) AddRemote(opts RemoteOptions) error
AddRemote adds a new remote to the repository configuration. The remote name must be unique within the repository.
Parameters:
- opts: remote options including name and URL
Returns an error if the remote already exists (ErrAlreadyExists) or if the configuration is invalid (ErrInvalidInput).
Example:
err := repo.AddRemote(git.RemoteOptions{
Name: "upstream",
URL: "https://github.com/upstream/repo",
})
func (*Repository) CheckoutBranch ¶
func (r *Repository) CheckoutBranch(name string) error
CheckoutBranch switches the working tree to the specified branch.
This updates HEAD to point to the branch and updates the working tree to match the branch's commit. Any uncommitted changes may cause conflicts.
Returns ErrNotFound if the branch doesn't exist, ErrConflict if there are uncommitted changes that would be overwritten, or other errors for checkout failures.
Note: This operation requires a working tree and will fail for bare repositories.
Examples:
// Checkout an existing branch
err := repo.CheckoutBranch("main")
// Switch to a different branch
err := repo.CheckoutBranch("develop")
func (*Repository) CreateBranch ¶
func (r *Repository) CreateBranch(name string, ref string) error
CreateBranch creates a new local branch from the specified reference (commit, tag, or branch).
The ref parameter can be:
- A commit hash (e.g., "abc123...")
- A branch name (e.g., "main", "develop")
- A tag name (e.g., "v1.0.0")
- A reference name (e.g., "refs/heads/main")
The branch is created but not checked out. Use CheckoutBranch to switch to the new branch.
Returns ErrAlreadyExists if a branch with the given name already exists, ErrNotFound if the specified reference doesn't exist, or ErrInvalidInput for invalid parameters.
Examples:
// Create branch from HEAD
err := repo.CreateBranch("feature-branch", "HEAD")
// Create branch from specific commit
err := repo.CreateBranch("hotfix", "abc123def456")
// Create branch from another branch
err := repo.CreateBranch("new-feature", "develop")
func (*Repository) CreateBranchFromRemote ¶
func (r *Repository) CreateBranchFromRemote(localName, remoteBranch string) error
CreateBranchFromRemote creates a local branch that tracks a remote branch.
This is a convenience function that simplifies the common workflow of creating a local branch from a remote tracking branch. The remoteBranch parameter should be in the format "remote/branch" (e.g., "origin/main", "upstream/develop").
The created local branch will be configured to track the remote branch for pull and push operations.
Returns ErrAlreadyExists if the local branch already exists, ErrNotFound if the remote branch doesn't exist, or ErrInvalidInput for invalid parameters.
Examples:
// Create local "main" tracking "origin/main"
err := repo.CreateBranchFromRemote("main", "origin/main")
// Create local "develop" tracking "upstream/develop"
err := repo.CreateBranchFromRemote("develop", "upstream/develop")
func (*Repository) CreateCommit ¶
func (r *Repository) CreateCommit(opts CommitOptions) (string, error)
CreateCommit creates a new commit with the specified options.
By default, CreateCommit will fail if there are no changes to commit (clean working tree). Use the AllowEmpty option to create commits without changes, which is useful for triggers, markers, or testing.
The commit is created on the current HEAD. To commit to a different branch, first checkout that branch using CheckoutBranch.
Returns the commit hash as a string, or an error if the commit fails. Common errors include ErrConflict for a clean working tree without AllowEmpty, or ErrInvalidInput for missing author/email/message.
Examples:
// Create a commit with changes
hash, err := repo.CreateCommit(git.CommitOptions{
Author: "John Doe",
Email: "john@example.com",
Message: "Add new feature",
})
// Create an empty commit (e.g., for triggering CI)
hash, err := repo.CreateCommit(git.CommitOptions{
Author: "Bot",
Email: "bot@example.com",
Message: "Trigger rebuild",
AllowEmpty: true,
})
func (*Repository) CreateLightweightTag ¶
func (r *Repository) CreateLightweightTag(name string, ref string) error
CreateLightweightTag creates a lightweight tag at the specified reference.
A lightweight tag is simply a reference to a commit, similar to a branch but immutable. Unlike annotated tags, lightweight tags don't have a message or tagger information. They are useful for temporary or local markers.
The ref parameter can be:
- A commit hash (e.g., "abc123...")
- A branch name (e.g., "main")
- Another tag name
- "HEAD" for the current commit
Returns ErrAlreadyExists if a tag with the given name already exists, ErrNotFound if the specified reference doesn't exist, or ErrInvalidInput for invalid parameters.
Examples:
// Tag the current HEAD with a lightweight tag
err := repo.CreateLightweightTag("build-123", "HEAD")
// Tag a specific commit
err := repo.CreateLightweightTag("tested", "abc123")
func (*Repository) CreateTag ¶
func (r *Repository) CreateTag(name string, ref string, message string) error
CreateTag creates an annotated tag with a message at the specified reference.
An annotated tag is a full object in the Git database with its own hash, containing a message, tagger information, and a timestamp. This is the recommended tag type for releases and other significant milestones.
The ref parameter can be:
- A commit hash (e.g., "abc123...")
- A branch name (e.g., "main")
- Another tag name
- "HEAD" for the current commit
Returns ErrAlreadyExists if a tag with the given name already exists, ErrNotFound if the specified reference doesn't exist, or ErrInvalidInput for invalid parameters.
Examples:
// Tag the current HEAD
err := repo.CreateTag("v1.0.0", "HEAD", "Release version 1.0.0")
// Tag a specific commit
err := repo.CreateTag("v1.0.1", "abc123", "Hotfix release")
// Tag a branch
err := repo.CreateTag("release-candidate", "develop", "RC for testing")
func (*Repository) CreateWorktree ¶
func (r *Repository) CreateWorktree(path string, opts WorktreeOptions) (*Worktree, error)
CreateWorktree creates a new linked worktree at the specified path.
A worktree is a separate working directory connected to the same repository. This allows multiple branches to be checked out simultaneously without affecting each other. This implementation uses git CLI commands to create true linked worktrees.
The worktree can be created at a specific commit (using opts.Hash) or branch (using opts.Branch). At least one of these options must be provided.
Additional options:
- CreateBranch: Create a new branch when adding the worktree
- Force: Force creation even if the worktree path already exists
- Detach: Detach HEAD at the named commit
Returns the created Worktree or an error if creation fails. Common errors include ErrInvalidInput if neither Hash nor Branch are provided, ErrAlreadyExists if the worktree already exists, ErrNotFound if the reference doesn't exist, or ErrConflict if a memory filesystem is used (worktrees require OS filesystem).
Examples:
// Create worktree at a specific commit
wt, err := repo.CreateWorktree("/tmp/worktree", git.WorktreeOptions{
Hash: plumbing.NewHash("abc123..."),
})
// Create worktree on a branch
wt, err := repo.CreateWorktree("/tmp/worktree", git.WorktreeOptions{
Branch: plumbing.NewBranchReferenceName("feature"),
})
// Create worktree with a new branch
wt, err := repo.CreateWorktree("/tmp/worktree", git.WorktreeOptions{
Branch: plumbing.NewBranchReferenceName("main"),
CreateBranch: "new-feature",
})
func (*Repository) CurrentBranch ¶ added in v0.3.0
func (r *Repository) CurrentBranch() (string, error)
CurrentBranch returns the name of the currently checked out branch.
Returns an empty string if HEAD is detached (not pointing to a branch).
Examples:
branch, err := repo.CurrentBranch()
if err != nil {
return err
}
if branch == "" {
fmt.Println("HEAD is detached")
} else {
fmt.Printf("Current branch: %s\n", branch)
}
func (*Repository) DeleteBranch ¶
func (r *Repository) DeleteBranch(name string, force bool) error
DeleteBranch deletes a local branch from the repository.
The force parameter determines whether to delete the branch even if it has unmerged changes. If force is false and the branch has commits that are not merged into the current branch or other branches, the operation will fail.
Returns ErrNotFound if the branch doesn't exist, or ErrConflict if the branch has unmerged changes and force is false.
Note: This only deletes local branches (refs/heads/*). To delete remote branches, use push operations with appropriate refspecs.
Examples:
// Delete a merged branch
err := repo.DeleteBranch("feature-branch", false)
// Force delete an unmerged branch
err := repo.DeleteBranch("experimental", true)
func (*Repository) DeleteTag ¶
func (r *Repository) DeleteTag(name string) error
DeleteTag removes a tag from the repository.
This deletes both annotated and lightweight tags. For annotated tags, only the reference is removed; the tag object remains in the Git object database but becomes unreachable (will be garbage collected eventually).
Returns ErrNotFound if the tag doesn't exist.
Examples:
// Delete a tag
err := repo.DeleteTag("v1.0.0")
// Delete a lightweight tag
err := repo.DeleteTag("build-123")
func (*Repository) Fetch ¶
func (r *Repository) Fetch(ctx context.Context, opts FetchOptions) error
Fetch downloads objects and refs from the remote repository. It updates remote-tracking branches but doesn't modify the working tree.
The fetch operation uses the package-level remoteOps interface, which allows tests to mock network operations.
Parameters:
- ctx: context for cancellation and timeout
- opts: fetch options including remote name, authentication, depth, etc.
Returns an error if fetching fails. Common errors include ErrNotFound if the remote doesn't exist, ErrUnauthorized for authentication failures, or network errors.
Example:
err := repo.Fetch(ctx, git.FetchOptions{
RemoteName: "origin",
Auth: auth,
})
func (*Repository) Filesystem ¶
func (r *Repository) Filesystem() billy.Filesystem
Filesystem returns the billy.Filesystem associated with this repository. This filesystem can be used for file operations within the repository's working tree or for accessing repository data.
For standard (non-bare) repositories, this returns a filesystem scoped to the working tree. For bare repositories, it returns a filesystem scoped to the repository directory itself.
func (*Repository) GetCommit ¶
func (r *Repository) GetCommit(ref string) (*Commit, error)
GetCommit retrieves a single commit by reference.
The ref parameter can be:
- A commit hash (e.g., "abc123...")
- A branch name (e.g., "main")
- A tag name (e.g., "v1.0.0")
- "HEAD" for the current commit
Returns ErrNotFound if the reference doesn't exist or doesn't point to a commit.
Examples:
// Get commit by hash
commit, err := repo.GetCommit("abc123")
// Get current HEAD commit
commit, err := repo.GetCommit("HEAD")
// Get commit for a tag
commit, err := repo.GetCommit("v1.0.0")
func (*Repository) ListBranches ¶
func (r *Repository) ListBranches() ([]Branch, error)
ListBranches returns all branches in the repository, including both local and remote branches.
Local branches are those in refs/heads/, remote branches are those in refs/remotes/. The Branch.IsRemote field indicates whether each branch is remote or local.
Examples:
branches, err := repo.ListBranches()
for _, branch := range branches {
if branch.IsRemote {
fmt.Printf("Remote: %s (%s)\n", branch.Name, branch.Hash)
} else {
fmt.Printf("Local: %s (%s)\n", branch.Name, branch.Hash)
}
}
func (*Repository) ListRemotes ¶
func (r *Repository) ListRemotes() ([]Remote, error)
ListRemotes returns all configured remotes for this repository. Each remote includes its name and configured URLs.
Example:
remotes, err := repo.ListRemotes()
for _, remote := range remotes {
fmt.Printf("Remote %s: %v\n", remote.Name, remote.URLs)
}
func (*Repository) ListTags ¶
func (r *Repository) ListTags() ([]Tag, error)
ListTags returns all tags in the repository, including both annotated and lightweight tags.
Annotated tags will have the Message field populated with the tag message. Lightweight tags will have an empty Message field.
The Hash field contains the hash of the tagged commit for lightweight tags, or the hash of the tag object itself for annotated tags.
Examples:
tags, err := repo.ListTags()
for _, tag := range tags {
if tag.Message != "" {
fmt.Printf("Annotated: %s (%s): %s\n", tag.Name, tag.Hash, tag.Message)
} else {
fmt.Printf("Lightweight: %s (%s)\n", tag.Name, tag.Hash)
}
}
func (*Repository) ListWorktrees ¶
func (r *Repository) ListWorktrees() ([]*Worktree, error)
ListWorktrees returns all worktrees associated with this repository.
This method uses git CLI commands to list all worktrees, including the main worktree and all linked worktrees. Each worktree is opened and returned as a Worktree object.
Returns a slice of Worktree pointers or an error if listing fails. Common errors include ErrConflict if a memory filesystem is used (worktrees require OS filesystem).
Example:
worktrees, err := repo.ListWorktrees()
if err != nil {
return err
}
for _, wt := range worktrees {
fmt.Printf("Worktree at: %s\n", wt.Path())
}
func (*Repository) PruneWorktrees ¶ added in v0.4.0
func (r *Repository) PruneWorktrees() error
PruneWorktrees removes stale worktree administrative data.
This method cleans up worktree metadata for worktrees that have been manually deleted from the filesystem. It's useful for maintaining repository health when worktrees are removed without using Remove().
Returns an error if a memory filesystem is used (worktrees require OS filesystem).
Example:
err := repo.PruneWorktrees()
func (*Repository) Pull ¶
func (r *Repository) Pull(ctx context.Context, opts PullOptions) error
Pull is a convenience method that combines Fetch with a working tree update. It fetches changes from the remote and updates the current branch's working tree to match the remote tracking branch.
This operation is equivalent to: Fetch + merge/rebase of the remote tracking branch into the current branch. The implementation uses go-git's Pull which performs a fetch followed by a merge.
Parameters:
- ctx: context for cancellation and timeout
- opts: pull options including remote name and authentication
Returns an error if pulling fails. Common errors include ErrNotFound if the remote doesn't exist, ErrUnauthorized for authentication failures, ErrConflict for merge conflicts, or network errors.
Note: This method requires a non-bare repository with a working tree.
Example:
err := repo.Pull(ctx, git.PullOptions{
RemoteName: "origin",
Auth: auth,
})
func (*Repository) Push ¶
func (r *Repository) Push(ctx context.Context, opts PushOptions) error
Push uploads objects and refs to the remote repository. It updates the remote branches with local changes.
The push operation uses the package-level remoteOps interface, which allows tests to mock network operations.
Parameters:
- ctx: context for cancellation and timeout
- opts: push options including remote name, authentication, refspecs, etc.
Returns an error if pushing fails. Common errors include ErrUnauthorized for authentication failures, ErrConflict for non-fast-forward updates (unless Force is true), or network errors.
Example:
err := repo.Push(ctx, git.PushOptions{
RemoteName: "origin",
RefSpecs: []string{"refs/heads/main:refs/heads/main"},
Auth: auth,
})
func (*Repository) RemoveRemote ¶
func (r *Repository) RemoveRemote(name string) error
RemoveRemote removes a remote from the repository configuration.
Parameters:
- name: name of the remote to remove
Returns an error if the remote doesn't exist (ErrNotFound).
Example:
err := repo.RemoveRemote("upstream")
func (*Repository) Underlying ¶
func (r *Repository) Underlying() *gogit.Repository
Underlying returns the underlying go-git Repository for advanced operations not covered by this wrapper. This escape hatch allows direct access to go-git's full API when needed.
The returned *gogit.Repository can be used for any go-git operation. Changes made through the underlying repository will be reflected in this wrapper.
func (*Repository) WalkCommits ¶
WalkCommits walks the commit history starting from a reference and returns commits in reverse chronological order (newest to oldest).
The iterator yields commits one at a time, enabling true streaming without loading all commits into memory. This is especially efficient for limiting results with early termination using 'break'.
The from parameter specifies where to stop (exclusive). When empty, walks all ancestors from 'to'.
The to parameter is required and can be:
- A commit hash (e.g., "abc123...")
- A branch name (e.g., "main", "develop")
- A tag name (e.g., "v1.0.0")
- "HEAD" for the current commit
The iterator yields (Commit, error) pairs. Check for errors on each iteration. The walk includes the commit pointed to by 'to' but excludes the commit pointed to by 'from'. This matches the behavior of "git log from..to".
Examples:
// Get last 10 commits (newest first)
count := 0
for commit, err := range repo.WalkCommits("", "HEAD") {
if err != nil { return err }
if count >= 10 { break }
fmt.Println(commit.Message)
count++
}
// Walk all commits from HEAD
for commit, err := range repo.WalkCommits("", "HEAD") {
if err != nil { return err }
// process commit (newest→oldest order)
}
// Walk between two refs
for commit, err := range repo.WalkCommits("v1.0.0", "v2.0.0") {
if err != nil { return err }
// commits from v2.0.0 back to (but not including) v1.0.0
}
type RepositoryOption ¶
type RepositoryOption func(*repositoryOptions)
RepositoryOption configures repository creation operations (Init, Open, Clone). Options can be used to customize filesystem, remote operations, and clone behavior.
func WithAuth ¶
func WithAuth(auth Auth) RepositoryOption
WithAuth sets authentication for Clone operations.
Example:
auth, _ := git.SSHKeyFile("git", "~/.ssh/id_rsa")
repo, err := git.Clone(ctx, "git@github.com:org/repo.git", git.WithAuth(auth))
func WithBare ¶
func WithBare() RepositoryOption
WithBare creates a bare repository (no working tree). Only applicable to Init operations.
Example:
repo, err := git.Init(ctx, "/path/to/repo.git", git.WithBare())
func WithDepth ¶
func WithDepth(depth int) RepositoryOption
WithDepth sets the depth for shallow clones. A depth of 0 (default) performs a full clone.
Example:
repo, err := git.Clone(ctx, "https://github.com/org/repo", git.WithDepth(1))
func WithFilesystem ¶
func WithFilesystem(fs billy.Filesystem) RepositoryOption
WithFilesystem sets the billy filesystem to use for repository operations. If not provided, defaults to osfs.New(path) rooted at the repository path.
Example:
repo, err := git.Init(ctx, "/path/to/repo", git.WithFilesystem(memfs.New()))
func WithReferenceName ¶
func WithReferenceName(ref plumbing.ReferenceName) RepositoryOption
WithReferenceName sets the specific branch or tag to clone.
Example:
repo, err := git.Clone(ctx, "https://github.com/org/repo",
git.WithReferenceName(plumbing.NewBranchReferenceName("develop")))
func WithRemoteOperations ¶
func WithRemoteOperations(ops RemoteOperations) RepositoryOption
WithRemoteOperations sets the RemoteOperations implementation to use for network operations (Clone, Fetch, Push). If not provided, defaults to the internal implementation that uses go-git's network operations.
This option is primarily useful for testing, allowing consumers to mock network operations without actual network calls.
Example:
repo, err := git.Clone(ctx, "https://github.com/org/repo",
git.WithRemoteOperations(mockOps))
func WithShared ¶ added in v0.4.0
func WithShared() RepositoryOption
WithShared enables git alternates to share objects with the source repository. This is useful when cloning from a local repository to avoid duplicating the object database. The cloned repository will reference the source's objects via .git/objects/info/alternates.
This option only applies to Clone operations from local file paths. When cloning from remote URLs, this option is ignored.
Example:
// Clone from local bare repo with shared objects repo, err := git.Clone(ctx, "/path/to/bare.git", git.WithShared())
func WithSingleBranch ¶
func WithSingleBranch() RepositoryOption
WithSingleBranch limits the clone to a single branch.
Example:
repo, err := git.Clone(ctx, "https://github.com/org/repo", git.WithSingleBranch())
func WithWorktreeOperations ¶ added in v0.4.0
func WithWorktreeOperations(ops WorktreeOperations) RepositoryOption
WithWorktreeOperations sets the WorktreeOperations implementation to use for worktree operations (add, list, remove, lock, unlock, prune). If not provided, defaults to the git CLI implementation via the exec module.
This option is primarily useful for testing, allowing consumers to mock worktree operations without actual git CLI calls.
Example:
repo, err := git.Open("/path/to/repo",
git.WithWorktreeOperations(mockOps))
type SSHKeyOption ¶
type SSHKeyOption func(*sshKeyOptions)
SSHKeyOption configures SSH key authentication.
func WithSSHPassword ¶
func WithSSHPassword(password string) SSHKeyOption
WithSSHPassword sets the password for encrypted SSH keys.
type Worktree ¶
type Worktree struct {
// contains filtered or unexported fields
}
Worktree wraps a go-git worktree with path tracking and a back-reference to its parent Repository.
func (*Worktree) Checkout ¶
Checkout updates the worktree to the specified reference.
The ref parameter can be a commit hash, branch name, or tag name. This operation updates the working directory to match the specified reference.
When checking out a branch by name, the HEAD will be set to that branch. When checking out by hash or tag, HEAD will be detached.
Returns an error if checkout fails. Common errors include ErrNotFound if the reference doesn't exist, ErrConflict if there are uncommitted changes that would be overwritten, or filesystem errors.
Examples:
// Checkout a branch
err := wt.Checkout("main")
// Checkout a specific commit
err := wt.Checkout("abc123...")
// Checkout a tag
err := wt.Checkout("v1.0.0")
func (*Worktree) Lock ¶ added in v0.4.0
Lock locks this worktree to prevent it from being pruned.
Locked worktrees cannot be removed by 'git worktree prune' and are protected from automatic cleanup. The optional reason parameter explains why the worktree is locked.
Returns an error if the worktree is already locked or if a memory filesystem is used (worktrees require OS filesystem).
Example:
err := wt.Lock("Working on critical fix")
func (*Worktree) Path ¶
Path returns the filesystem path of this worktree.
This is the absolute path to the worktree's working directory where files can be accessed and modified.
Example:
wt, err := repo.CreateWorktree("/tmp/worktree", opts)
if err != nil {
return err
}
fmt.Printf("Worktree created at: %s\n", wt.Path())
func (*Worktree) Remove ¶
Remove removes this worktree from the repository.
This operation performs safety checks to ensure no uncommitted changes would be lost. The worktree must be clean (no uncommitted changes) before it can be removed. This method uses git CLI commands to remove linked worktrees.
Returns an error if the worktree has uncommitted changes (ErrConflict), if the worktree is locked, or if a memory filesystem is used (worktrees require OS filesystem).
Example:
wt, err := repo.CreateWorktree("/tmp/worktree", opts)
if err != nil {
return err
}
defer wt.Remove()
// ... use worktree ...
func (*Worktree) Underlying ¶
Underlying returns the underlying go-git Worktree for advanced operations not covered by this wrapper. This escape hatch allows direct access to go-git's full worktree API when needed.
The returned *gogit.Worktree can be used for any go-git worktree operation. Changes made through the underlying worktree will be reflected in this wrapper.
Example:
// Access go-git's worktree for advanced operations gogitWt := wt.Underlying() status, err := gogitWt.Status()
type WorktreeInfo ¶ added in v0.4.0
type WorktreeInfo struct {
// Path is the absolute filesystem path to the worktree
Path string
// Head is the commit hash that the worktree is currently at
Head string
// Branch is the name of the checked out branch (empty if detached HEAD)
Branch string
// IsLocked indicates if the worktree is locked
IsLocked bool
// Reason contains the lock reason if the worktree is locked
Reason string
}
WorktreeInfo contains information about a worktree returned by List.
type WorktreeOperations ¶ added in v0.4.0
type WorktreeOperations interface {
// Add creates a new worktree at the specified path.
// The ref parameter specifies which commit/branch to checkout in the worktree.
// Options control creation behavior (force, detach, create branch).
Add(ctx context.Context, path string, ref string, opts WorktreeOptions) error
// List returns information about all worktrees associated with the repository.
// This includes the main worktree and all linked worktrees.
List(ctx context.Context) ([]WorktreeInfo, error)
// Remove deletes a worktree.
// If force is true, the worktree is removed even if it has uncommitted changes.
Remove(ctx context.Context, path string, force bool) error
// Lock locks a worktree to prevent it from being pruned.
// The reason parameter is optional and explains why the worktree is locked.
Lock(ctx context.Context, path string, reason string) error
// Unlock unlocks a worktree, allowing it to be pruned.
Unlock(ctx context.Context, path string) error
// Prune removes stale worktree administrative data.
// This cleans up worktree metadata for worktrees that have been manually deleted.
Prune(ctx context.Context) error
}
WorktreeOperations defines operations for managing git worktrees. This interface abstracts the underlying implementation (git CLI) to enable testing and follows the same pattern as RemoteOperations for consistency.
All operations require a real OS filesystem and will return an error if used with memory-based filesystems (like memfs), since they shell out to the git CLI.
type WorktreeOptions ¶
type WorktreeOptions struct {
Hash plumbing.Hash
Branch plumbing.ReferenceName
CreateBranch string // Create a new branch with this name when adding worktree
Force bool // Force creation even if worktree path already exists
Detach bool // Detach HEAD at named commit
}
WorktreeOptions configures worktree creation.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cache provides efficient caching of Git repositories with two-tier storage.
|
Package cache provides efficient caching of Git repositories with two-tier storage. |
|
Package testutil provides in-memory testing utilities for the git package.
|
Package testutil provides in-memory testing utilities for the git package. |