or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

config.mdindex.mdobjects.mdplumbing.mdreferences.mdremote-operations.mdrepository-operations.mdstorage.mdtransport.mdworktree-operations.md
tile.json

repository-operations.mddocs/

Repository Operations

This document covers all operations for creating, opening, and cloning Git repositories using go-git.

Overview

go-git provides two sets of repository functions:

  1. Standard functions - Work with custom storage and filesystem abstractions (billy.Filesystem)
  2. Plain functions - Convenience functions that work with OS filesystem paths directly

Repository Type

type Repository struct {
    Storer storage.Storer
    // contains filtered or unexported fields
}

The Repository type is the main entry point for all repository operations. It provides methods for:

  • Accessing and manipulating Git objects (commits, trees, blobs, tags)
  • Managing references (branches, tags, HEAD)
  • Working with remotes (fetch, push, pull)
  • Configuration management
  • Worktree access

Creating New Repositories

Init - Create with Custom Storage

Create a new repository with custom storage and filesystem.

func Init(s storage.Storer, worktree billy.Filesystem) (*Repository, error)

Parameters:

  • s - Storage backend (e.g., memory.NewStorage(), filesystem.NewStorage())
  • worktree - Filesystem abstraction for the working directory (nil for bare repositories)

Example:

package main

import (
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/storage/memory"
    "github.com/go-git/go-billy/v5/memfs"
)

func main() {
    // Create in-memory repository
    storer := memory.NewStorage()
    fs := memfs.New()

    r, err := git.Init(storer, fs)
    if err != nil {
        panic(err)
    }

    // Repository is now initialized
    ref, err := r.Head()
    if err != nil {
        // HEAD doesn't exist yet in empty repo
        println("Empty repository")
    }
}

InitWithOptions - Create with Options

Create a new repository with initialization options.

func InitWithOptions(s storage.Storer, worktree billy.Filesystem, opts InitOptions) (*Repository, error)

type InitOptions struct {
    DefaultBranch plumbing.ReferenceName
}

Example:

package main

import (
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/plumbing"
    "github.com/go-git/go-git/v5/storage/memory"
    "github.com/go-git/go-billy/v5/memfs"
)

func main() {
    storer := memory.NewStorage()
    fs := memfs.New()

    // Initialize with 'main' as default branch
    r, err := git.InitWithOptions(storer, fs, git.InitOptions{
        DefaultBranch: plumbing.ReferenceName("refs/heads/main"),
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

PlainInit - Create on Filesystem

Create a new repository directly on the OS filesystem.

func PlainInit(path string, isBare bool) (*Repository, error)

Parameters:

  • path - Directory path for the repository
  • isBare - If true, creates a bare repository (no working directory)

Example:

package main

import (
    "github.com/go-git/go-git/v5"
)

func main() {
    // Create a regular repository with working directory
    r, err := git.PlainInit("/tmp/my-repo", false)
    if err != nil {
        panic(err)
    }

    // Create a bare repository (no working directory)
    bare, err := git.PlainInit("/tmp/bare-repo.git", true)
    if err != nil {
        panic(err)
    }

    _, _ = r, bare
}

PlainInitWithOptions - Create on Filesystem with Options

Create a new repository on the filesystem with options.

func PlainInitWithOptions(path string, opts *PlainInitOptions) (*Repository, error)

type PlainInitOptions struct {
    DefaultBranch plumbing.ReferenceName
    Bare          bool
    InitOptions   InitOptions
}

Example:

package main

import (
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/plumbing"
)

func main() {
    r, err := git.PlainInitWithOptions("/tmp/my-repo", &git.PlainInitOptions{
        DefaultBranch: plumbing.ReferenceName("refs/heads/main"),
        Bare:          false,
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

Opening Existing Repositories

Open - Open with Custom Storage

Open an existing repository with custom storage and filesystem.

func Open(s storage.Storer, worktree billy.Filesystem) (*Repository, error)

Example:

package main

import (
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/storage/filesystem"
    "github.com/go-git/go-billy/v5/osfs"
)

func main() {
    // Open repository using filesystem storage
    fs := osfs.New("/tmp/my-repo")
    storer := filesystem.NewStorage(fs, nil)

    r, err := git.Open(storer, fs)
    if err != nil {
        panic(err)
    }

    // Access repository
    ref, err := r.Head()
    if err != nil {
        panic(err)
    }

    println("HEAD:", ref.Hash().String())
}

PlainOpen - Open from Filesystem Path

Open an existing repository directly from an OS filesystem path.

func PlainOpen(path string) (*Repository, error)

Parameters:

  • path - Path to the repository (directory containing .git or bare repository)

Example:

package main

import (
    "fmt"
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/plumbing/object"
)

func main() {
    // Open repository
    r, err := git.PlainOpen("/path/to/repo")
    if err != nil {
        panic(err)
    }

    // Get HEAD reference
    ref, err := r.Head()
    if err != nil {
        panic(err)
    }

    // Get commit history
    cIter, err := r.Log(&git.LogOptions{From: ref.Hash()})
    if err != nil {
        panic(err)
    }

    // Count commits
    var count int
    err = cIter.ForEach(func(c *object.Commit) error {
        count++
        return nil
    })
    if err != nil {
        panic(err)
    }

    fmt.Printf("Repository has %d commits\n", count)
}

PlainOpenWithOptions - Open with Options

Open an existing repository with options.

func PlainOpenWithOptions(path string, opts *PlainOpenOptions) (*Repository, error)

type PlainOpenOptions struct {
    DetectDotGit          bool
    EnableDotGitCommonDir bool
}

Parameters:

  • DetectDotGit - Automatically detect .git directory in parent directories
  • EnableDotGitCommonDir - Enable support for .git/commondir (worktree support)

Example:

package main

import (
    "github.com/go-git/go-git/v5"
)

func main() {
    // Open repository with automatic .git detection
    r, err := git.PlainOpenWithOptions("/path/to/subdirectory", &git.PlainOpenOptions{
        DetectDotGit:          true,
        EnableDotGitCommonDir: true,
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

Cloning Repositories

Clone - Clone with Custom Storage

Clone a remote repository to custom storage.

func Clone(s storage.Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error)

Example:

package main

import (
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/storage/memory"
    "github.com/go-git/go-billy/v5/memfs"
)

func main() {
    // Clone to in-memory storage
    storer := memory.NewStorage()
    fs := memfs.New()

    r, err := git.Clone(storer, fs, &git.CloneOptions{
        URL: "https://github.com/go-git/go-git",
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

CloneContext - Clone with Context

Clone a repository with context support for cancellation and timeout.

func CloneContext(ctx context.Context, s storage.Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error)

Example:

package main

import (
    "context"
    "time"
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/storage/memory"
    "github.com/go-git/go-billy/v5/memfs"
)

func main() {
    // Clone with 30-second timeout
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    storer := memory.NewStorage()
    fs := memfs.New()

    r, err := git.CloneContext(ctx, storer, fs, &git.CloneOptions{
        URL: "https://github.com/go-git/go-git",
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

PlainClone - Clone to Filesystem

Clone a repository to a filesystem directory.

func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error)

Parameters:

  • path - Destination directory path
  • isBare - If true, creates a bare clone (no working directory)
  • o - Clone options (URL, authentication, progress, etc.)

Example:

package main

import (
    "os"
    "github.com/go-git/go-git/v5"
)

func main() {
    // Clone repository
    r, err := git.PlainClone("/tmp/repo", false, &git.CloneOptions{
        URL:      "https://github.com/go-git/go-git",
        Progress: os.Stdout,
    })
    if err != nil {
        panic(err)
    }

    // Get HEAD
    ref, err := r.Head()
    if err != nil {
        panic(err)
    }

    commit, err := r.CommitObject(ref.Hash())
    if err != nil {
        panic(err)
    }

    println("Cloned to commit:", commit.Hash.String())
}

PlainCloneContext - Clone to Filesystem with Context

Clone a repository with context support.

func PlainCloneContext(ctx context.Context, path string, isBare bool, o *CloneOptions) (*Repository, error)

Example:

package main

import (
    "context"
    "os"
    "time"
    "github.com/go-git/go-git/v5"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    defer cancel()

    r, err := git.PlainCloneContext(ctx, "/tmp/repo", false, &git.CloneOptions{
        URL:      "https://github.com/go-git/go-git",
        Progress: os.Stdout,
        Depth:    1, // Shallow clone
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

Clone Options

type CloneOptions struct {
    URL                   string
    Auth                  transport.AuthMethod
    RemoteName            string
    ReferenceName         plumbing.ReferenceName
    SingleBranch          bool
    Mirror                bool
    NoCheckout            bool
    Depth                 int
    RecurseSubmodules     SubmoduleRescursivity
    Progress              sideband.Progress
    Tags                  TagMode
    InsecureSkipTLS       bool
    ClientCert            []byte
    ClientKey             []byte
    CABundle              []byte
    ProxyOptions          transport.ProxyOptions
    Shared                bool
}

Key Fields:

  • URL - Repository URL (required)
  • Auth - Authentication method (http.BasicAuth, ssh.PublicKeys, etc.)
  • RemoteName - Name for the remote (default: "origin")
  • ReferenceName - Specific branch to checkout (default: HEAD)
  • SingleBranch - Clone only specified branch
  • Mirror - Create a mirror clone
  • NoCheckout - Don't checkout HEAD after clone
  • Depth - Shallow clone depth (0 for full clone)
  • RecurseSubmodules - Submodule recursion level
  • Progress - io.Writer for progress output
  • Tags - Tag fetching mode (TagFollowing, AllTags, NoTags)

Example with Multiple Options:

package main

import (
    "os"
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/plumbing"
    "github.com/go-git/go-git/v5/plumbing/transport/http"
)

func main() {
    r, err := git.PlainClone("/tmp/repo", false, &git.CloneOptions{
        URL: "https://github.com/private/repo",
        Auth: &http.BasicAuth{
            Username: "user",
            Password: "token",
        },
        ReferenceName:     plumbing.ReferenceName("refs/heads/develop"),
        SingleBranch:      true,
        Depth:             50,
        RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
        Progress:          os.Stdout,
        Tags:              git.NoTags,
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

Repository Methods

Configuration

func (r *Repository) Config() (*config.Config, error)
func (r *Repository) SetConfig(cfg *config.Config) error
func (r *Repository) ConfigScoped(scope config.Scope) (*config.Config, error)

Get and set repository configuration.

Example:

package main

import (
    "fmt"
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/config"
)

func main() {
    r, err := git.PlainOpen("/tmp/repo")
    if err != nil {
        panic(err)
    }

    // Get configuration
    cfg, err := r.Config()
    if err != nil {
        panic(err)
    }

    // Modify configuration
    cfg.User.Name = "Jane Doe"
    cfg.User.Email = "jane@example.com"

    // Save configuration
    err = r.SetConfig(cfg)
    if err != nil {
        panic(err)
    }

    fmt.Println("Configuration updated")
}

Remote Management

func (r *Repository) Remote(name string) (*Remote, error)
func (r *Repository) Remotes() ([]*Remote, error)
func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error)
func (r *Repository) DeleteRemote(name string) error

Example:

package main

import (
    "fmt"
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/config"
)

func main() {
    r, err := git.PlainOpen("/tmp/repo")
    if err != nil {
        panic(err)
    }

    // List remotes
    remotes, err := r.Remotes()
    if err != nil {
        panic(err)
    }

    for _, remote := range remotes {
        fmt.Println(remote.Config().Name, remote.Config().URLs)
    }

    // Create new remote
    _, err = r.CreateRemote(&config.RemoteConfig{
        Name: "upstream",
        URLs: []string{"https://github.com/upstream/repo"},
    })
    if err != nil {
        panic(err)
    }
}

Worktree Access

func (r *Repository) Worktree() (*Worktree, error)

Get the repository's worktree for file operations.

Example:

package main

import (
    "github.com/go-git/go-git/v5"
)

func main() {
    r, err := git.PlainOpen("/tmp/repo")
    if err != nil {
        panic(err)
    }

    w, err := r.Worktree()
    if err != nil {
        panic(err)
    }

    // Get status
    status, err := w.Status()
    if err != nil {
        panic(err)
    }

    println("Clean:", status.IsClean())
}

Common Errors

var (
    ErrRepositoryNotExists     = errors.New("repository does not exist")
    ErrRepositoryAlreadyExists = errors.New("repository already exists")
    ErrRepositoryIncomplete    = errors.New("repository is incomplete")
    ErrIsBareRepository        = errors.New("operation not supported on bare repository")
    ErrWorktreeNotProvided     = errors.New("worktree not provided")
)

Error Handling Example:

package main

import (
    "errors"
    "github.com/go-git/go-git/v5"
)

func main() {
    r, err := git.PlainOpen("/nonexistent/repo")
    if err != nil {
        if errors.Is(err, git.ErrRepositoryNotExists) {
            println("Repository doesn't exist")
            // Maybe create it instead
            r, err = git.PlainInit("/nonexistent/repo", false)
        } else {
            panic(err)
        }
    }

    _ = r
}

Best Practices

1. Choose the Right Function

  • Use Plain* functions for simple filesystem operations
  • Use standard functions when you need custom storage (memory, cloud, etc.)
  • Use **Context versions for long-running operations that need cancellation

2. Handle Bare Repositories

package main

import (
    "github.com/go-git/go-git/v5"
)

func main() {
    r, err := git.PlainOpen("/tmp/repo")
    if err != nil {
        panic(err)
    }

    // Check if bare before accessing worktree
    _, err = r.Worktree()
    if err == git.ErrIsBareRepository {
        println("This is a bare repository")
        // Handle accordingly
    }
}

3. Use Shallow Clones for Large Repositories

package main

import (
    "github.com/go-git/go-git/v5"
)

func main() {
    // Clone only the last 10 commits
    r, err := git.PlainClone("/tmp/repo", false, &git.CloneOptions{
        URL:   "https://github.com/large/repo",
        Depth: 10,
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

4. Monitor Clone Progress

package main

import (
    "os"
    "github.com/go-git/go-git/v5"
)

func main() {
    r, err := git.PlainClone("/tmp/repo", false, &git.CloneOptions{
        URL:      "https://github.com/go-git/go-git",
        Progress: os.Stdout, // Print progress to stdout
    })
    if err != nil {
        panic(err)
    }

    _ = r
}

See Also

  • Worktree Operations - Working with files and commits
  • Remote Operations - Fetch, push, and pull
  • Storage - Storage backends and interfaces
  • Configuration - Repository configuration