# Beads Git Integration: How It Uses Git as Primary Storage

> Discover how Beads Git integration uses Git as primary storage. Learn about its work-tree aware helper for O(1) path lookups and seamless operation.

- Repository: [Gas Town Hall/beads](https://github.com/gastownhall/beads)
- Tags: how-to-guide
- Published: 2026-04-27

---

**Beads treats Git repositories as its primary storage layer through a work-tree-aware helper package that caches a single call to `git rev-parse`, enabling O(1) path lookups and seamless operation across main repositories and linked worktrees.**

The gastownhall/beads project deeply integrates with Git by utilizing the repository as its underlying storage mechanism. Unlike tools that merely execute Git commands, Beads implements a sophisticated caching layer that parses repository structure once and reuses those results throughout the application lifecycle. This Beads Git integration ensures consistent behavior whether you are working in the main repository or across multiple Git worktrees.

## Core Git Integration Architecture

### The Cached Git Context Pattern

At the heart of the integration lies a **singleton cache** implemented in [`internal/git/gitdir.go`](https://github.com/gastownhall/beads/blob/main/internal/git/gitdir.go). When any Git-related function is first invoked, `initGitContext()` executes a single subprocess running:

```bash
git rev-parse --git-dir --git-common-dir --show-toplevel

```

This command returns three critical paths: the `.git` directory, the common directory shared by worktrees, and the repository root. Beads stores these in a `gitContext` struct protected by `sync.Once`, ensuring the expensive subprocess spawns exactly once per command execution. Subsequent lookups read from this in-memory cache, providing **O(1)** access to repository metadata without additional process overhead.

### Worktree-Aware Path Resolution

Beads distinguishes between main repositories and linked worktrees by comparing the absolute paths of `--git-dir` and `--git-common-dir`. When these differ, the helper function `IsWorktree()` returns `true`, signaling that the current working directory resides inside a worktree.

All path-dependent operations—such as `GetGitHooksDir()` and `GetGitRefsDir()`—intentionally use the **common directory** rather than the worktree-specific git-dir. This ensures that hooks, references, and objects remain shared across all worktrees, maintaining consistency when Beads operates on repository-wide data.

## Repository Path Management and Normalization

### Cross-Platform Path Handling

Before caching paths, Beads normalizes them for cross-platform correctness. The code in [`internal/git/gitdir.go`](https://github.com/gastownhall/beads/blob/main/internal/git/gitdir.go) leverages helper functions (defined in [`internal/utils/path.go`](https://github.com/gastownhall/beads/blob/main/internal/utils/path.go)) to convert Windows-style separators, resolve symbolic links, and canonicalize case on macOS and Windows filesystems. This guarantees that string comparisons on paths succeed even when the operating system reports alternate spellings or capitalizations.

### Git Hooks and Configuration Paths

The `GetGitHooksDir()` function respects the `core.hooksPath` configuration variable, expanding `~` to the user's home directory for cross-platform compatibility. If no custom hooks path is defined, it falls back to `<common-dir>/hooks`. This ensures that Beads-installed hooks (such as those for Dolt) remain reachable regardless of which worktree is active.

## Working with Git Worktrees

### Detecting Repository Context from Go

Applications using the Beads library can detect their execution context through the exported API:

```go
package main

import (
    "fmt"
    "github.com/steveyegge/beads/internal/git"
)

func main() {
    // Returns true when the current cwd lives inside a git worktree.
    if git.IsWorktree() {
        fmt.Println("Running inside a git worktree")
    } else {
        fmt.Println("Running in the main repository")
    }

    // Get the absolute path of the repository root (main repo if in a worktree).
    root, err := git.GetMainRepoRoot()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Main repo root: %s\n", root)
}

```

The `GetMainRepoRoot()` function is particularly important for operations requiring repository-wide context, such as exclusive-lock handling or locating the `.beads/` metadata directory, because it returns the root of the *main* repository even when executed from within a nested worktree.

### The bd worktree Command Interface

The CLI command `bd worktree` (implemented in [`cmd/bd/worktree_cmd.go`](https://github.com/gastownhall/beads/blob/main/cmd/bd/worktree_cmd.go)) provides a thin wrapper around native Git worktree operations:

```bash

# List all worktrees that Beads knows about

bd worktree list

# Create a new worktree for a feature branch (Beads will also add it to .gitignore)

bd worktree add feature-login --branch feature/login

# Remove a worktree once the feature is merged

bd worktree remove feature-login

```

These commands utilize an internal helper called `gitCmdInDir()` that constructs secure `exec.CommandContext` instances. Each invocation explicitly sets `GIT_TERMINAL_PROMPT=0` and `GIT_CONFIG_NOSYSTEM=1` to prevent accidental interactive prompts and system configuration leakage, ensuring deterministic behavior in automated environments.

## Accessing Git Hooks Programmatically

When installing or verifying hooks, Beads provides direct access to the resolved hooks directory:

```go
hooksDir, err := git.GetGitHooksDir()
if err != nil {
    log.Fatalf("cannot locate hooks: %v", err)
}
fmt.Printf("Hooks live at: %s\n", hooksDir)

```

This function handles the `~` expansion logic described in lines 28-45 of [`internal/git/gitdir.go`](https://github.com/gastownhall/beads/blob/main/internal/git/gitdir.go), guaranteeing an absolute path that works correctly on Windows, macOS, and Linux.

## Security and Performance Considerations

### Concurrency-Safe Caching

The `sync.Once` primitive guarantees that `initGitContext()` is safe for concurrent reads within a single command execution. The only mechanism to invalidate this cache is the exported `ResetCaches()` function, which the test suite uses to ensure clean state between test cases.

### Secure Git Command Execution

Beyond caching, Beads emphasizes security when spawning Git processes. The helper `gitCmdInDir()` constructs commands with environment variables that disable interactive prompts and ignore system-level Git configuration, preventing potential execution of untrusted hooks or credential helpers during automated Beads operations.

## Summary

- **Single cached call**: Beads executes `git rev-parse` once per command to cache repository paths, enabling O(1) lookups via `sync.Once`.
- **Worktree native**: The integration detects worktrees by comparing `--git-dir` and `--git-common-dir`, ensuring shared resources like hooks remain centralized.
- **Cross-platform paths**: All paths are normalized for Windows, symlinks, and case-insensitive filesystems before caching.
- **Secure by default**: CLI commands use hardened `exec.CommandContext` settings to prevent prompt blocking and config leakage.
- **Repository agnostic**: Higher-level code remains unaware of worktree specifics, using `GetMainRepoRoot()` and `IsWorktree()` to abstract Git complexity.

## Frequently Asked Questions

### Does Beads support Git worktrees?

Yes. Beads fully supports Git worktrees by detecting when `--git-dir` differs from `--git-common-dir`. All path helpers use the common directory for shared resources while exposing functions like `IsWorktree()` and `GetMainRepoRoot()` to let applications adapt their behavior when necessary.

### How does Beads locate Git hooks across different worktrees?

Beads resolves the hooks directory through `GetGitHooksDir()` in [`internal/git/gitdir.go`](https://github.com/gastownhall/beads/blob/main/internal/git/gitdir.go), which checks `core.hooksPath` with proper tilde expansion and falls back to `<common-dir>/hooks`. This ensures hooks are shared across all worktrees rather than duplicated per worktree.

### Why is Beads Git integration considered performant?

The integration optimizes performance by caching repository metadata using `sync.Once`. After the initial call to `git rev-parse`, all subsequent path lookups read from an in-memory struct, eliminating subprocess overhead and providing O(1) access times for repository metadata.

### Is the Git integration safe for concurrent operations?

Yes. The cache initialization is guarded by `sync.Once`, making it safe for concurrent reads within a single process. The only way to reset the cache is through the exported `ResetCaches()` function, which is intended for test isolation rather than production use.