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

objects.mddocs/

Objects

This document covers working with Git objects: commits, trees, blobs, and tags. It includes reading objects, creating objects, iterating through collections, and computing diffs.

Overview

Git has four main object types:

  • Commit - Points to a tree and parent commits, contains metadata and message
  • Tree - Directory listing with entries (files and subdirectories)
  • Blob - File contents
  • Tag - Annotated tag pointing to another object

All objects are stored in the object database and referenced by their SHA-1/SHA-256 hash.

Object Interface

type Object interface {
    ID() plumbing.Hash
    Type() plumbing.ObjectType
    Decode(plumbing.EncodedObject) error
    Encode(plumbing.EncodedObject) error
}

Commit Objects

Commit Type

type Commit struct {
    Hash         plumbing.Hash
    Author       Signature
    Committer    Signature
    MergeTag     string
    PGPSignature string
    Message      string
    TreeHash     plumbing.Hash
    ParentHashes []plumbing.Hash
    Encoding     MessageEncoding
    ExtraHeaders []ExtraHeader
}

type Signature struct {
    Name  string
    Email string
    When  time.Time
}

Get Commit Object

func (r *Repository) CommitObject(h plumbing.Hash) (*object.Commit, error)

Example:

package main

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

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

    // Get specific commit
    hash := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")
    commit, err := r.CommitObject(hash)
    if err != nil {
        panic(err)
    }

    // Access commit fields
    fmt.Println("Author:", commit.Author.Name)
    fmt.Println("Email:", commit.Author.Email)
    fmt.Println("Date:", commit.Author.When)
    fmt.Println("Message:", commit.Message)
    fmt.Println("Tree:", commit.TreeHash)
    fmt.Println("Parents:", commit.ParentHashes)
}

Get HEAD Commit

package main

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

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

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

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

    fmt.Println(commit)
}

Commit Methods

func (c *Commit) Tree() (*Tree, error)
func (c *Commit) Parents() CommitIter
func (c *Commit) NumParents() int
func (c *Commit) Parent(n int) (*Commit, error)
func (c *Commit) File(path string) (*File, error)
func (c *Commit) Files() (*FileIter, error)
func (c *Commit) Patch(to *Commit) (*Patch, error)
func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error)
func (c *Commit) String() string

Get Commit Tree

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    // Get commit's root tree
    tree, err := commit.Tree()
    if err != nil {
        panic(err)
    }

    fmt.Println("Tree hash:", tree.Hash)
    fmt.Println("Tree entries:", len(tree.Entries))
}

Get File from Commit

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    // Get specific file from commit
    file, err := commit.File("README.md")
    if err != nil {
        panic(err)
    }

    // Read file contents
    contents, err := file.Contents()
    if err != nil {
        panic(err)
    }

    fmt.Println("README.md contents:")
    fmt.Println(contents)
}

Iterate Files in Commit

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    // Iterate all files in commit
    files, err := commit.Files()
    if err != nil {
        panic(err)
    }

    err = files.ForEach(func(f *object.File) error {
        fmt.Printf("%s (%s)\n", f.Name, f.Mode)
        return nil
    })
    if err != nil {
        panic(err)
    }

    files.Close()
}

Get Parent Commits

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    // Number of parents
    fmt.Println("Parent count:", commit.NumParents())

    // Get specific parent
    if commit.NumParents() > 0 {
        parent, err := commit.Parent(0)
        if err != nil {
            panic(err)
        }
        fmt.Println("First parent:", parent.Hash)
    }

    // Iterate all parents
    parents := commit.Parents()
    err := parents.ForEach(func(p *object.Commit) error {
        fmt.Println("Parent:", p.Hash, p.Message)
        return nil
    })
    if err != nil {
        panic(err)
    }

    parents.Close()
}

Compute Patch Between Commits

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    // Get parent commit
    parent, _ := commit.Parent(0)

    // Get patch from parent to commit
    patch, err := parent.Patch(commit)
    if err != nil {
        panic(err)
    }

    fmt.Println(patch)

    // Get statistics
    stats := patch.Stats()
    fmt.Println("\nStatistics:")
    for _, stat := range stats {
        fmt.Printf("%s: +%d -%d\n", stat.Name, stat.Addition, stat.Deletion)
    }
}

Commit Iteration

Get Commit History

func (r *Repository) Log(o *LogOptions) (object.CommitIter, error)

type LogOptions struct {
    From       plumbing.Hash
    Order      LogOrder
    FileName   *string
    PathFilter func(string) bool
    All        bool
    Since      *time.Time
    Until      *time.Time
}

type LogOrder int

const (
    LogOrderDefault      LogOrder = iota
    LogOrderDFS                   // Depth-first search
    LogOrderDFSPost               // DFS post-order
    LogOrderBSF                   // Breadth-first search
    LogOrderCommitterTime         // Sort by committer time
)

Example - Basic Log:

package main

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

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

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

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

    // Iterate commits
    err = cIter.ForEach(func(c *object.Commit) error {
        fmt.Printf("%s - %s\n", c.Hash[:7], c.Message)
        return nil
    })
    if err != nil {
        panic(err)
    }

    cIter.Close()
}

Example - Log with Time Range:

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()

    // Get commits from last 30 days
    since := time.Now().AddDate(0, 0, -30)

    cIter, err := r.Log(&git.LogOptions{
        From:  ref.Hash(),
        Since: &since,
    })
    if err != nil {
        panic(err)
    }

    err = cIter.ForEach(func(c *object.Commit) error {
        fmt.Printf("%s: %s\n", c.Author.When.Format("2006-01-02"), c.Message)
        return nil
    })
    if err != nil {
        panic(err)
    }

    cIter.Close()
}

Example - Log for Specific File:

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()

    filename := "README.md"
    cIter, err := r.Log(&git.LogOptions{
        From:     ref.Hash(),
        FileName: &filename,
    })
    if err != nil {
        panic(err)
    }

    fmt.Println("Commits affecting README.md:")
    err = cIter.ForEach(func(c *object.Commit) error {
        fmt.Printf("%s - %s\n", c.Hash[:7], c.Message)
        return nil
    })
    if err != nil {
        panic(err)
    }

    cIter.Close()
}

Example - Log with Path Filter:

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()

    // Only commits affecting .go files
    cIter, err := r.Log(&git.LogOptions{
        From: ref.Hash(),
        PathFilter: func(path string) bool {
            return strings.HasSuffix(path, ".go")
        },
    })
    if err != nil {
        panic(err)
    }

    err = cIter.ForEach(func(c *object.Commit) error {
        fmt.Printf("%s - %s\n", c.Hash[:7], c.Message)
        return nil
    })
    if err != nil {
        panic(err)
    }

    cIter.Close()
}

Iterate All Commits

func (r *Repository) CommitObjects() (object.CommitIter, error)

Example:

package main

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

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

    // Iterate all commit objects in repository
    cIter, err := r.CommitObjects()
    if err != nil {
        panic(err)
    }

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

    fmt.Printf("Total commits: %d\n", count)
    cIter.Close()
}

Tree Objects

Tree Type

type Tree struct {
    Entries []TreeEntry
    Hash    plumbing.Hash
}

type TreeEntry struct {
    Name string
    Mode filemode.FileMode
    Hash plumbing.Hash
}

Get Tree Object

func (r *Repository) TreeObject(h plumbing.Hash) (*object.Tree, error)

Example:

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    // Get tree from commit
    tree, err := r.TreeObject(commit.TreeHash)
    if err != nil {
        panic(err)
    }

    // List entries
    for _, entry := range tree.Entries {
        fmt.Printf("%s %s %s\n", entry.Mode, entry.Hash, entry.Name)
    }
}

Tree Methods

func (t *Tree) File(path string) (*File, error)
func (t *Tree) Size(path string) (int64, error)
func (t *Tree) Tree(path string) (*Tree, error)
func (t *Tree) TreeEntryFile(e *TreeEntry) (*File, error)
func (t *Tree) FindEntry(path string) (*TreeEntry, error)
func (t *Tree) Files() *FileIter
func (t *Tree) Diff(ctx context.Context, to *Tree) (Changes, error)

Get File from Tree

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())
    tree, _ := commit.Tree()

    // Get file from tree
    file, err := tree.File("src/main.go")
    if err != nil {
        panic(err)
    }

    contents, _ := file.Contents()
    fmt.Println(contents)
}

Get Subtree

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())
    tree, _ := commit.Tree()

    // Get subdirectory tree
    subtree, err := tree.Tree("src")
    if err != nil {
        panic(err)
    }

    fmt.Println("Files in src/:")
    for _, entry := range subtree.Entries {
        fmt.Println("  ", entry.Name)
    }
}

Compare Trees (Diff)

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())
    parent, _ := commit.Parent(0)

    parentTree, _ := parent.Tree()
    currentTree, _ := commit.Tree()

    // Get changes between trees
    changes, err := parentTree.Diff(context.Background(), currentTree)
    if err != nil {
        panic(err)
    }

    for _, change := range changes {
        action := change.Action()
        name := change.Name()

        fmt.Printf("%s: %s\n", actionString(action), name)
    }
}

func actionString(a object.Action) string {
    switch a {
    case object.Insert:
        return "Add"
    case object.Delete:
        return "Delete"
    case object.Modify:
        return "Modify"
    default:
        return "Unknown"
    }
}

Iterate Tree Files

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())
    tree, _ := commit.Tree()

    // Iterate all files recursively
    files := tree.Files()
    err := files.ForEach(func(f *object.File) error {
        fmt.Printf("%s (%d bytes)\n", f.Name, f.Blob.Size)
        return nil
    })
    if err != nil {
        panic(err)
    }

    files.Close()
}

Blob Objects

Blob Type

type Blob struct {
    Hash plumbing.Hash
    Size int64
}

func (b *Blob) Reader() (io.ReadCloser, error)

Get Blob Object

func (r *Repository) BlobObject(h plumbing.Hash) (*object.Blob, error)

Example:

package main

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

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

    // Get specific blob
    hash := plumbing.NewHash("d8ca47fe1cb8d5c7a4e62b9f20c4e30ed5d69ad6")
    blob, err := r.BlobObject(hash)
    if err != nil {
        panic(err)
    }

    fmt.Println("Blob size:", blob.Size, "bytes")

    // Read blob contents
    reader, err := blob.Reader()
    if err != nil {
        panic(err)
    }
    defer reader.Close()

    contents, _ := io.ReadAll(reader)
    fmt.Println(string(contents))
}

Iterate All Blobs

func (r *Repository) BlobObjects() (*object.BlobIter, error)

Example:

package main

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

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

    // Iterate all blobs
    bIter, err := r.BlobObjects()
    if err != nil {
        panic(err)
    }

    var totalSize int64
    err = bIter.ForEach(func(b *object.Blob) error {
        totalSize += b.Size
        return nil
    })
    if err != nil {
        panic(err)
    }

    fmt.Printf("Total blob size: %d bytes\n", totalSize)
    bIter.Close()
}

File Type

The File type combines a blob with its path and mode information.

type File struct {
    Name string
    Mode filemode.FileMode
    Blob Blob
}

func (f *File) Contents() (string, error)
func (f *File) Reader() (io.ReadCloser, error)
func (f *File) Lines() ([]string, error)

Example:

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    file, err := commit.File("README.md")
    if err != nil {
        panic(err)
    }

    fmt.Println("Name:", file.Name)
    fmt.Println("Mode:", file.Mode)
    fmt.Println("Size:", file.Blob.Size)

    // Get contents as string
    contents, _ := file.Contents()
    fmt.Println("\nContents:")
    fmt.Println(contents)

    // Get contents as lines
    lines, _ := file.Lines()
    fmt.Printf("\nLine count: %d\n", len(lines))
}

Tag Objects

Tag Type

type Tag struct {
    Hash         plumbing.Hash
    Name         string
    Tagger       Signature
    Message      string
    PGPSignature string
    TargetType   plumbing.ObjectType
    Target       plumbing.Hash
}

func (t *Tag) Object() (Object, error)

Get Tag Object

func (r *Repository) TagObject(h plumbing.Hash) (*object.Tag, error)

Example:

package main

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

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

    // Get annotated tag
    hash := plumbing.NewHash("abc123...")
    tag, err := r.TagObject(hash)
    if err != nil {
        panic(err)
    }

    fmt.Println("Tag name:", tag.Name)
    fmt.Println("Tagger:", tag.Tagger.Name)
    fmt.Println("Message:", tag.Message)
    fmt.Println("Target:", tag.Target)
    fmt.Println("Target type:", tag.TargetType)
}

Get Tagged Object

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    hash := plumbing.NewHash("abc123...")
    tag, _ := r.TagObject(hash)

    // Get the tagged object
    obj, err := tag.Object()
    if err != nil {
        panic(err)
    }

    // Usually tags point to commits
    if commit, ok := obj.(*object.Commit); ok {
        fmt.Println("Tagged commit:", commit.Hash)
        fmt.Println("Message:", commit.Message)
    }
}

Iterate All Tags

func (r *Repository) TagObjects() (*object.TagIter, error)

Example:

package main

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

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

    // Iterate annotated tags
    tIter, err := r.TagObjects()
    if err != nil {
        panic(err)
    }

    err = tIter.ForEach(func(t *object.Tag) error {
        fmt.Printf("%s: %s\n", t.Name, t.Message)
        return nil
    })
    if err != nil {
        panic(err)
    }

    tIter.Close()
}

Changes and Patches

Change Type

type Change struct {
    From ChangeEntry
    To   ChangeEntry
}

type ChangeEntry struct {
    Name      string
    Tree      plumbing.Hash
    TreeEntry TreeEntry
}

type Action int

const (
    Insert Action = iota
    Delete
    Modify
    Copy
)

func (c *Change) Action() Action
func (c *Change) Name() string
func (c *Change) Files() ([]*File, error)
func (c *Change) String() string

Patch Type

type Patch struct {
    // contains filtered or unexported fields
}

func (p *Patch) FilePatches() []FilePatch
func (p *Patch) String() string
func (p *Patch) Stats() FileStats
func (p *Patch) Message() string

Example - Generate Patch:

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())
    parent, _ := commit.Parent(0)

    // Generate patch
    patch, err := parent.Patch(commit)
    if err != nil {
        panic(err)
    }

    // Print unified diff
    fmt.Println(patch)

    // Get file patches
    filePatches := patch.FilePatches()
    fmt.Printf("\n%d files changed\n", len(filePatches))

    // Get statistics
    stats := patch.Stats()
    for _, stat := range stats {
        fmt.Printf("%s: +%d -%d\n", stat.Name, stat.Addition, stat.Deletion)
    }
}

Generic Object Access

Get Any Object

func (r *Repository) Object(t plumbing.ObjectType, h plumbing.Hash) (object.Object, error)

Example:

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    hash := plumbing.NewHash("abc123...")

    // Get object of any type
    obj, err := r.Object(plumbing.AnyObject, hash)
    if err != nil {
        panic(err)
    }

    // Type switch on object
    switch o := obj.(type) {
    case *object.Commit:
        fmt.Println("Commit:", o.Message)
    case *object.Tree:
        fmt.Println("Tree with", len(o.Entries), "entries")
    case *object.Blob:
        fmt.Println("Blob size:", o.Size)
    case *object.Tag:
        fmt.Println("Tag:", o.Name)
    }
}

Iterate All Objects

func (r *Repository) Objects() (*object.ObjectIter, error)

Example:

package main

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

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

    // Iterate all objects
    oIter, err := r.Objects()
    if err != nil {
        panic(err)
    }

    counts := map[plumbing.ObjectType]int{}
    err = oIter.ForEach(func(o object.Object) error {
        counts[o.Type()]++
        return nil
    })
    if err != nil {
        panic(err)
    }

    fmt.Println("Object counts:")
    fmt.Printf("  Commits: %d\n", counts[plumbing.CommitObject])
    fmt.Printf("  Trees:   %d\n", counts[plumbing.TreeObject])
    fmt.Printf("  Blobs:   %d\n", counts[plumbing.BlobObject])
    fmt.Printf("  Tags:    %d\n", counts[plumbing.TagObject])

    oIter.Close()
}

Best Practices

1. Always Close Iterators

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()

    cIter, _ := r.Log(&git.LogOptions{From: ref.Hash()})
    defer cIter.Close() // Always close iterators

    _ = cIter.ForEach(func(c *object.Commit) error {
        // Process commit
        return nil
    })
}

2. Use Context for Long Operations

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())
    parent, _ := commit.Parent(0)

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    patch, err := parent.PatchContext(ctx, commit)
    if err != nil {
        panic(err)
    }

    _ = patch
}

3. Handle Large Files Efficiently

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    ref, _ := r.Head()
    commit, _ := r.CommitObject(ref.Hash())

    file, _ := commit.File("largefile.bin")

    // For large files, use Reader instead of Contents()
    reader, _ := file.Reader()
    defer reader.Close()

    // Process in chunks
    buf := make([]byte, 4096)
    for {
        n, err := reader.Read(buf)
        if err == io.EOF {
            break
        }
        // Process buf[:n]
    }
}

4. Check Object Existence Before Access

package main

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

func main() {
    r, _ := git.PlainOpen("/tmp/repo")
    hash := plumbing.NewHash("abc123...")

    // Get commit, check for error
    commit, err := r.CommitObject(hash)
    if err == plumbing.ErrObjectNotFound {
        println("Commit not found")
        return
    } else if err != nil {
        panic(err)
    }

    _ = commit
}

Common Errors

var (
    ErrObjectNotFound       = errors.New("object not found")
    ErrInvalidType          = errors.New("invalid object type")
    ErrUnsupportedObject    = errors.New("unsupported object type")
    ErrMaxTreeDepth         = errors.New("maximum tree depth exceeded")
    ErrFileNotFound         = errors.New("file not found")
    ErrDirectoryNotFound    = errors.New("directory not found")
    ErrEntryNotFound        = errors.New("entry not found")
    ErrEntriesNotSorted     = errors.New("tree entries not sorted")
    ErrParentNotFound       = errors.New("parent not found")
)

See Also

  • Repository Operations - Creating and opening repositories
  • Worktree Operations - Working with files and commits
  • References - Managing branches and tags
  • Storage - Object storage backends
  • Plumbing - Low-level object operations