# Quick Start

This guide will help you get started with nanogit quickly.

## Basic Operations

### Creating a Client

```go
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

```go
// 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

```go
// 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

```go
// 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)

```go
client, err := nanogit.NewHTTPClient(
    repo,
    options.WithBasicAuth("username", "ghp_token"),
)
```

### GitLab Personal Access Token

```go
client, err := nanogit.NewHTTPClient(
    repo,
    options.WithBasicAuth("oauth2", "gitlab_pat_token"),
)
```

### No Authentication (Public Repos)

```go
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:

```go
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](server-compatibility.md#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](server-compatibility.md#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.

```go
// 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:

```go
// 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](../architecture/storage.md) and [Performance](../architecture/performance.md).

## Retry Mechanism

nanogit includes a pluggable retry mechanism, making operations more robust against transient network errors and server issues.

### Basic Retry Usage

```go
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

```go
// 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](../architecture/retry.md).

## Testing

### Unit Testing with Mocks

nanogit provides generated mocks for easy unit testing:

```go
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:

```bash
go get github.com/grafana/nanogit/gittest@latest
```

```go
import "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](../testing-guide.md) and [gittest documentation](https://pkg.go.dev/github.com/grafana/nanogit/gittest).

## Next Steps

- **[API Reference (GoDoc)](https://pkg.go.dev/github.com/grafana/nanogit)** - Complete API reference with all methods
- **[Storage Architecture](../architecture/storage.md)** - Pluggable storage and writing modes
- **[Retry Mechanism](../architecture/retry.md)** - Pluggable retry mechanism for robust operations
- **[Performance](../architecture/performance.md)** - Performance characteristics and benchmarks
- **[Architecture Overview](../architecture/overview.md)** - Core design principles
