Quick Start
This guide will help you get started with nanogit quickly.
Basic Operations
Creating a Client
package main
import (
"context"
"fmt"
"time"
"github.com/grafana/nanogit"
"github.com/grafana/nanogit/options"
)
func main() {
ctx := context.Background()
// Create client with authentication
client, err := nanogit.NewHTTPClient(
"https://github.com/user/repo.git",
options.WithBasicAuth("username", "token"),
)
if err != nil {
panic(err)
}
}Reading Files
// Get the main branch reference
ref, err := client.GetRef(ctx, "refs/heads/main")
if err != nil {
panic(err)
}
// Read a file
content, err := client.GetBlob(ctx, ref.Hash, "README.md")
if err != nil {
panic(err)
}
fmt.Println(string(content))Writing Files
// Create a staged writer
writer, err := client.NewStagedWriter(ctx, ref)
if err != nil {
panic(err)
}
// Create a new file
err = writer.CreateBlob(ctx, "docs/new-feature.md", []byte("# New Feature"))
if err != nil {
panic(err)
}
// Update an existing file
err = writer.UpdateBlob(ctx, "README.md", []byte("Updated content"))
if err != nil {
panic(err)
}
// Commit changes
author := nanogit.Author{
Name: "John Doe",
Email: "john@example.com",
Time: time.Now(),
}
committer := nanogit.Committer{
Name: "Deploy Bot",
Email: "deploy@example.com",
Time: time.Now(),
}
commit, err := writer.Commit(ctx, "Add feature and update docs", author, committer)
if err != nil {
panic(err)
}
// Push to remote
err = writer.Push(ctx)
if err != nil {
panic(err)
}
fmt.Printf("Committed: %s\n", commit.Hash)Cloning a Repository
// Get the commit to clone
ref, err := client.GetRef(ctx, "main")
if err != nil {
panic(err)
}
// Clone with path filtering
result, err := client.Clone(ctx, nanogit.CloneOptions{
Path: "/tmp/my-repo",
Hash: ref.Hash,
IncludePaths: []string{"src/**", "docs/**"},
ExcludePaths: []string{"*.tmp", "node_modules/**"},
BatchSize: 50, // Batch fetch for better performance
Concurrency: 8, // Use 8 concurrent workers
})
if err != nil {
panic(err)
}
fmt.Printf("Cloned %d of %d files to %s\n",
result.FilteredFiles, result.TotalFiles, result.Path)Clone Features:
- Path filtering: Use glob patterns to include/exclude specific files and directories
- Filesystem output: Automatically writes filtered files to specified local path
- Shallow clones: Fetch only the latest commit to minimize bandwidth
- Branch isolation: Clone only specific branches to reduce transfer time
- CI optimized: Perfect for build environments with no persistent storage
Authentication Options
Basic Auth (GitHub Token)
client, err := nanogit.NewHTTPClient(
repo,
options.WithBasicAuth("username", "ghp_token"),
)GitLab Personal Access Token
client, err := nanogit.NewHTTPClient(
repo,
options.WithBasicAuth("oauth2", "gitlab_pat_token"),
)No Authentication (Public Repos)
client, err := nanogit.NewHTTPClient(repo)Overriding Receive-Pack Capabilities
By default nanogit advertises a capability set tuned for compliant servers (see protocol.DefaultReceivePackCapabilities()). Some servers — most notably certain GitLab deployments that wrap report-status in side-band channel 1 — negotiate a response nanogit cannot parse, leaving pushes that appear to succeed on the wire but silently drop the ref update. options.WithReceivePackCapabilities replaces the advertised set entirely:
import (
"github.com/grafana/nanogit"
"github.com/grafana/nanogit/options"
"github.com/grafana/nanogit/protocol"
)
// Drop side-band-64k, keep everything else from the default set.
caps := []protocol.Capability{
protocol.CapReportStatusV2,
protocol.CapQuiet,
protocol.CapObjectFormatSHA1,
protocol.CapAgent("nanogit"),
}
client, err := nanogit.NewHTTPClient(repo,
options.WithBasicAuth("git", token),
options.WithReceivePackCapabilities(caps...),
)The caller passes the full desired list — there is no merge with the defaults. Arbitrary tokens can be spelled as protocol.Capability("foo") when the typed helpers don't cover what the server expects. See Receive-pack capabilities for the default set and guidance on when to override.
If you would rather not enumerate the safe subset by hand, options.WithCapabilityNegotiation() makes the client fetch the server's advertisement once and advertise the intersection on subsequent pushes. See Programmatic negotiation for the trade-offs.
Performance Optimization
Clone Performance
BatchSize - Controls how many blobs to fetch in a single network request:
- Value 0 or 1: Fetches blobs individually (backward compatible, default behavior)
- Values > 1: Enables batch fetching, reducing network round trips by 50-70%
- Recommended value: 20-100 depending on average blob size and network conditions
Concurrency - Controls parallel blob fetching:
- Value 0 or 1: Sequential fetching (backward compatible, default behavior)
- Values > 1: Enables concurrent fetching using worker pools
- Recommended value: 4-10 depending on network conditions and server capacity
- Can improve performance by 2-3x on high-latency networks
Performance Impact: Combined optimization (BatchSize=50, Concurrency=8) can achieve 5-10x speedup compared to default sequential fetching, making it ideal for CI/CD environments and large repository operations.
// Optimized clone for production
result, err := client.Clone(ctx, nanogit.CloneOptions{
Path: "/tmp/repo",
Hash: ref.Hash,
BatchSize: 50, // Fetch 50 blobs per request
Concurrency: 8, // 8 concurrent workers
})Writer Storage Modes
nanogit provides flexible writing modes to optimize memory usage:
// Auto mode (default) - smart memory/disk switching
writer, err := client.NewStagedWriter(ctx, ref)
// Memory mode - maximum performance
writer, err := client.NewStagedWriter(ctx, ref, nanogit.WithMemoryStorage())
// Disk mode - minimal memory usage for bulk operations
writer, err := client.NewStagedWriter(ctx, ref, nanogit.WithDiskStorage())Learn more about Storage Architecture and Performance.
Retry Mechanism
nanogit includes a pluggable retry mechanism, making operations more robust against transient network errors and server issues.
Basic Retry Usage
import "github.com/grafana/nanogit/retry"
ctx := context.Background()
// Enable retries with default exponential backoff
retrier := retry.NewExponentialBackoffRetrier()
ctx = retry.ToContext(ctx, retrier)
// All HTTP operations will now use retry logic
client, err := nanogit.NewHTTPClient(repo)
ref, err := client.GetRef(ctx, "main")Custom Retry Configuration
// Configure retry behavior for production
retrier := retry.NewExponentialBackoffRetrier().
WithMaxAttempts(5). // Retry up to 5 times
WithInitialDelay(200 * time.Millisecond). // Start with 200ms delay
WithMaxDelay(10 * time.Second). // Cap at 10 seconds
WithJitter() // Add random jitter
ctx = retry.ToContext(ctx, retrier)What gets retried:
- Network timeout errors
- 5xx server errors (for GET requests)
- Temporary errors
What does NOT get retried:
- 4xx client errors (bad requests, auth failures)
- Context cancellation
- POST request 5xx errors (request body is consumed and cannot be re-read, so retries are not possible)
Learn more about the Retry Mechanism.
Testing
Unit Testing with Mocks
nanogit provides generated mocks for easy unit testing:
import (
"testing"
"github.com/grafana/nanogit/mocks"
"github.com/stretchr/testify/mock"
)
func TestMyService(t *testing.T) {
// Create a mock client
mockClient := &mocks.FakeClient{}
// Set up expectations
mockClient.GetRefReturns(&nanogit.Ref{
Name: "refs/heads/main",
Hash: hash.FromString("abc123"),
}, nil)
// Use the mock in your tests
service := NewMyService(mockClient)
// ... test your service
}Integration Testing with gittest
For integration tests with a real Git server:
go get github.com/grafana/nanogit/gittest@latestimport "github.com/grafana/nanogit/gittest"
func TestGitOperations(t *testing.T) {
ctx := context.Background()
// Create a Git server
server, err := gittest.NewServer(ctx)
require.NoError(t, err)
defer server.Cleanup()
// Create user and repository
user, err := server.CreateUser(ctx)
require.NoError(t, err)
repo, err := server.CreateRepo(ctx, "myrepo", user)
require.NoError(t, err)
// Create local repository
local, err := gittest.NewLocalRepo(ctx)
require.NoError(t, err)
defer local.Cleanup()
// Initialize with remote
client, err := local.QuickInit(user, repo.AuthURL)
require.NoError(t, err)
// Test your Git operations
err = local.CreateFile("test.txt", "content")
require.NoError(t, err)
_, err = local.Git("add", "test.txt")
require.NoError(t, err)
_, err = local.Git("commit", "-m", "Test commit")
require.NoError(t, err)
_, err = local.Git("push", "origin", "main")
require.NoError(t, err)
// Verify with nanogit client
ref, err := client.GetRef(ctx, "refs/heads/main")
require.NoError(t, err)
require.NotNil(t, ref)
}Learn more in the Testing Guide and gittest documentation.
Next Steps
- API Reference (GoDoc) - Complete API reference with all methods
- Storage Architecture - Pluggable storage and writing modes
- Retry Mechanism - Pluggable retry mechanism for robust operations
- Performance - Performance characteristics and benchmarks
- Architecture Overview - Core design principles
