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