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

plumbing.mddocs/

Plumbing

This document covers low-level Git plumbing operations in go-git, including packfiles, index manipulation, and object encoding.

Overview

The plumbing packages provide low-level access to Git internals:

  • Packfile operations - Reading and writing packfiles
  • Index operations - Staging area manipulation
  • Object encoding - Low-level object read/write
  • Protocol operations - Git wire protocol
  • Format handling - Git file format parsing

Object Types

type ObjectType int8

const (
    InvalidObject   ObjectType = 0
    CommitObject    ObjectType = 1
    TreeObject      ObjectType = 2
    BlobObject      ObjectType = 3
    TagObject       ObjectType = 4
    OFSDeltaObject  ObjectType = 6
    REFDeltaObject  ObjectType = 7
    AnyObject       ObjectType = -127
)

func ParseObjectType(value string) (ObjectType, error)
func (t ObjectType) String() string
func (t ObjectType) Bytes() []byte
func (t ObjectType) Valid() bool
func (t ObjectType) IsDelta() bool

EncodedObject

type EncodedObject interface {
    Hash() Hash
    Type() ObjectType
    SetType(ObjectType)
    Size() int64
    SetSize(int64)
    Reader() (io.ReadCloser, error)
    Writer() (io.WriteCloser, error)
}

Packfile Operations

Scanner

import "github.com/go-git/go-git/v5/plumbing/format/packfile"

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

func NewScanner(r io.Reader) *Scanner

func (s *Scanner) Header() (*Header, error)
func (s *Scanner) NextObjectHeader() (*ObjectHeader, error)
func (s *Scanner) NextObject(w io.Writer) (int64, plumbing.Hash, error)
func (s *Scanner) Close() error

Example:

package main

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

func main() {
    f, err := os.Open("/tmp/repo/.git/objects/pack/pack-*.pack")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    scanner := packfile.NewScanner(f)

    // Read header
    header, err := scanner.Header()
    if err != nil {
        panic(err)
    }

    fmt.Printf("Version: %d, Objects: %d\n", header.Version, header.Objects)

    // Scan objects
    for {
        objHeader, err := scanner.NextObjectHeader()
        if err != nil {
            break
        }

        fmt.Printf("Type: %s, Size: %d\n", objHeader.Type, objHeader.Length)
    }
}

Parser

import "github.com/go-git/go-git/v5/plumbing/format/packfile"

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

func NewParser(scanner Scanner, storage storer.EncodedObjectStorer) *Parser

func (p *Parser) Parse() (plumbing.Hash, error)

Encoder

import "github.com/go-git/go-git/v5/plumbing/format/packfile"

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

func NewEncoder(w io.Writer, storage storer.EncodedObjectStorer, useRefDeltas bool) *Encoder

func (e *Encoder) Encode(refs []*plumbing.Reference, cfg *config.Config) (plumbing.Hash, error)

Index Operations

Index Type

import "github.com/go-git/go-git/v5/plumbing/format/index"

type Index struct {
    Version           uint32
    Entries           []*Entry
    Cache             *Tree
    ResolveUndo       *ResolveUndo
    EndOfIndexEntry   *EndOfIndexEntry
}

func (i *Index) Add(path string, mode filemode.FileMode, hash plumbing.Hash) error
func (i *Index) Remove(path string) error
func (i *Index) Entry(path string) (*Entry, bool)
func (i *Index) Entries() []*Entry
func (i *Index) Decode(r io.Reader) error
func (i *Index) Encode(w io.Writer) error

Entry Type

type Entry struct {
    CreatedAt      time.Time
    ModifiedAt     time.Time
    Dev            uint32
    Inode          uint32
    Mode           filemode.FileMode
    UID            uint32
    GID            uint32
    Size           uint32
    Hash           plumbing.Hash
    Stage          Stage
    Name           string
    IntentToAdd    bool
    SkipWorktree   bool
}

type Stage uint16

const (
    Merged       Stage = 0
    AncestorMode Stage = 1
    OurMode      Stage = 2
    TheirMode    Stage = 3
)

Example:

package main

import (
    "fmt"
    "time"
    "github.com/go-git/go-git/v5/plumbing"
    "github.com/go-git/go-git/v5/plumbing/filemode"
    "github.com/go-git/go-git/v5/plumbing/format/index"
)

func main() {
    idx := &index.Index{
        Version: 2,
    }

    // Add entry
    err := idx.Add("README.md", filemode.Regular, plumbing.NewHash("abc123..."))
    if err != nil {
        panic(err)
    }

    // Get entry
    entry, found := idx.Entry("README.md")
    if found {
        fmt.Println("File:", entry.Name)
        fmt.Println("Mode:", entry.Mode)
        fmt.Println("Hash:", entry.Hash)
    }
}

FileMode

import "github.com/go-git/go-git/v5/plumbing/filemode"

type FileMode uint32

const (
    Empty      FileMode = 0
    Dir        FileMode = 0040000
    Regular    FileMode = 0100644
    Deprecated FileMode = 0100664
    Executable FileMode = 0100755
    Symlink    FileMode = 0120000
    Submodule  FileMode = 0160000
)

func New(mode string) (FileMode, error)
func NewFromOSFileMode(mode os.FileMode) (FileMode, error)

func (m FileMode) Bytes() []byte
func (m FileMode) IsMalformed() bool
func (m FileMode) String() string
func (m FileMode) IsRegular() bool
func (m FileMode) IsFile() bool
func (m FileMode) ToOSFileMode() (os.FileMode, error)

Protocol Operations

AdvRefs

import "github.com/go-git/go-git/v5/plumbing/protocol/packp"

type AdvRefs struct {
    Prefix       [][]byte
    Head         *plumbing.Hash
    Peeled       map[string]plumbing.Hash
    Refs         map[string]plumbing.Hash
    Shallows     []plumbing.Hash
    Capabilities *capability.List
}

func (a *AdvRefs) AllReferences() map[string]plumbing.Hash
func (a *AdvRefs) IsEmpty() bool
func (a *AdvRefs) Decode(r io.Reader) error
func (a *AdvRefs) Encode(w io.Writer) error

UploadPackRequest

type UploadPackRequest struct {
    Wants        []plumbing.Hash
    Haves        []plumbing.Hash
    Shallows     []plumbing.Hash
    Depth        Depth
    Capabilities *capability.List
}

func (r *UploadPackRequest) Decode(rd io.Reader) error
func (r *UploadPackRequest) Encode(w io.Writer) error

ReferenceUpdateRequest

type ReferenceUpdateRequest struct {
    Commands     []*Command
    Capabilities *capability.List
    Shallow      *plumbing.Hash
    Packfile     io.ReadCloser
}

type Command struct {
    Name plumbing.ReferenceName
    Old  plumbing.Hash
    New  plumbing.Hash
}

func (r *ReferenceUpdateRequest) Decode(rd io.Reader) error
func (r *ReferenceUpdateRequest) Encode(w io.Writer) error

Capabilities

import "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"

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

func (l *List) Add(cap Capability) *List
func (l *List) Set(cap Capability, values ...string) *List
func (l *List) Get(cap Capability) *Capability
func (l *List) Supports(cap Capability) bool
func (l *List) Delete(cap Capability)
func (l *List) String() string
func (l *List) Decode(data []byte) error
func (l *List) Encode(w io.Writer) error

type Capability string

const (
    MultiACK         Capability = "multi_ack"
    MultiACKDetailed Capability = "multi_ack_detailed"
    NoDone           Capability = "no-done"
    ThinPack         Capability = "thin-pack"
    SideBand         Capability = "side-band"
    SideBand64k      Capability = "side-band-64k"
    OFSDelta         Capability = "ofs-delta"
    Shallow          Capability = "shallow"
    DeepenSince      Capability = "deepen-since"
    DeepenNot        Capability = "deepen-not"
    DeepenRelative   Capability = "deepen-relative"
    NoProgress       Capability = "no-progress"
    IncludeTag       Capability = "include-tag"
    ReportStatus     Capability = "report-status"
    ReportStatusV2   Capability = "report-status-v2"
    DeleteRefs       Capability = "delete-refs"
    Quiet            Capability = "quiet"
    Atomic           Capability = "atomic"
    PushOptions      Capability = "push-options"
    Agent            Capability = "agent"
    SymRef           Capability = "symref"
    Filter           Capability = "filter"
)

Hash Computation

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

type Hasher interface {
    io.Writer
    Sum() Hash
}

func NewHasher(t ObjectType, size int64) Hasher
func ComputeHash(t ObjectType, content []byte) Hash

Example:

package main

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

func main() {
    // Compute hash for blob content
    content := []byte("Hello, World!")
    hash := plumbing.ComputeHash(plumbing.BlobObject, content)

    fmt.Println("Blob hash:", hash.String())

    // Using hasher
    hasher := plumbing.NewHasher(plumbing.BlobObject, int64(len(content)))
    hasher.Write(content)
    hash2 := hasher.Sum()

    fmt.Println("Hasher result:", hash2.String())
}

Binary Utilities

import "github.com/go-git/go-git/v5/utils/binary"

func Read(r io.Reader, data interface{}) error
func Write(w io.Writer, data interface{}) error

func ReadUint8(r io.Reader) (uint8, error)
func ReadUint16(r io.Reader) (uint16, error)
func ReadUint32(r io.Reader) (uint32, error)
func ReadUint64(r io.Reader) (uint64, error)
func ReadVariableWidthInt(r io.Reader) (int64, error)

func WriteUint8(w io.Writer, value uint8) error
func WriteUint16(w io.Writer, value uint16) error
func WriteUint32(w io.Writer, value uint32) error
func WriteUint64(w io.Writer, value uint64) error
func WriteVariableWidthInt(w io.Writer, value int64) error

Diff Utilities

import "github.com/go-git/go-git/v5/utils/diff"

func Do(a, b string) (string, error)
func DoWithColor(e UnifiedEncoder, from, to *File, color color.Color) error

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

func (e *UnifiedEncoder) Encode(w io.Writer) error

Merkletrie

import "github.com/go-git/go-git/v5/utils/merkletrie"

type Change struct {
    From noder.Noder
    To   noder.Noder
}

type Action int

const (
    Insert Action = iota
    Delete
    Modify
)

func (c *Change) Action() Action
func (c *Change) Files() (from, to noder.Path)
func (c *Change) String() string

func DiffTree(from, to noder.Noder) ([]*Change, error)

Best Practices

1. Use High-Level APIs When Possible

The plumbing APIs are powerful but complex. Use porcelain APIs for common operations.

2. Handle Binary Data Carefully

When working with packfiles and object encoding, ensure proper error handling for corrupted data.

3. Close Readers/Writers

Always close resources:

package main

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

func main() {
    f, err := os.Open("pack.pack")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    scanner := packfile.NewScanner(f)
    defer scanner.Close()

    // Use scanner...
}

Common Use Cases

Read Packfile Contents

package main

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

func main() {
    f, _ := os.Open("/tmp/repo/.git/objects/pack/pack-*.pack")
    defer f.Close()

    scanner := packfile.NewScanner(f)
    defer scanner.Close()

    header, _ := scanner.Header()
    fmt.Printf("Objects: %d\n", header.Objects)
}

Manipulate Index

package main

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

func main() {
    idx := &index.Index{Version: 2}

    // Add file
    idx.Add("main.go", filemode.Regular, plumbing.NewHash("abc123..."))

    // Remove file
    idx.Remove("old.go")

    // Check entries
    for _, entry := range idx.Entries() {
        println(entry.Name)
    }
}

See Also

  • Objects - High-level object operations
  • Storage - Storage backends
  • References - Reference management