git

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2025 License: MIT Imports: 22 Imported by: 0

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:

  1. Thin wrappers over go-git (not reimplementing Git)
  2. Billy filesystem for all I/O operations
  3. Escape hatches via Underlying() methods for advanced use cases
  4. RemoteOperations and WorktreeOperations interfaces enable testing by mocking operations
  5. Organized by operation type (repository, worktree, branch, tag, commit, remote)
  6. 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:

  1. Git must be installed and available in the system PATH
  2. Operations must use the OS filesystem (osfs) - memory filesystems (memfs) are not supported
  3. 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:

Index

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

func BasicAuth(username, password string) Auth

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 Branch

type Branch struct {
	Name     string
	Hash     plumbing.Hash
	IsRemote bool
}

Branch is a simple value type representing a Git branch.

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
	Shared        bool                   // Use git alternates to share objects with source repository
}

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

func (c *Commit) Underlying() *object.Commit

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

type CommitOptions struct {
	Author     string
	Email      string
	Message    string
	AllowEmpty bool
}

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

type PullOptions struct {
	RemoteName string // Default: "origin"
	Auth       Auth
}

PullOptions configures pull operations.

type PushOptions

type PushOptions struct {
	RemoteName string // Default: "origin"
	RefSpecs   []string
	Auth       Auth
	Force      bool
}

PushOptions configures push operations.

type Remote

type Remote struct {
	Name string
	URLs []string
}

Remote is a simple value type representing a Git remote.

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

type RemoteOptions struct {
	Name string
	URL  string
}

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

func (r *Repository) WalkCommits(from, to string) iter.Seq2[Commit, error]

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 Tag

type Tag struct {
	Name    string
	Hash    plumbing.Hash
	Message string // Empty for lightweight tags
}

Tag is a simple value type representing a Git tag.

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

func (w *Worktree) Checkout(ref string) error

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

func (w *Worktree) Lock(reason string) error

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

func (w *Worktree) Path() string

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

func (w *Worktree) Remove() error

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

func (w *Worktree) Underlying() *gogit.Worktree

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

func (*Worktree) Unlock added in v0.4.0

func (w *Worktree) Unlock() error

Unlock unlocks this worktree, allowing it to be pruned.

Returns an error if the worktree is not locked or if a memory filesystem is used (worktrees require OS filesystem).

Example:

err := wt.Unlock()

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.

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.

Jump to

Keyboard shortcuts

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