Files
beads/docs/GIT_INTEGRATION.md
Steve Yegge 166d66205c docs: update beads.jsonl references to issues.jsonl
Fixes GH#409 - Documentation inconsistently referred to beads.jsonl
as the canonical filename when issues.jsonl has been the default
since v0.25.1 (bd-6xd).

Updated files:
- docs/ARCHITECTURE.md
- docs/CLI_REFERENCE.md
- docs/PROTECTED_BRANCHES.md
- docs/ADVANCED.md
- docs/GIT_INTEGRATION.md
- docs/MULTI_REPO_AGENTS.md
- docs/MULTI_REPO_MIGRATION.md
- docs/TROUBLESHOOTING.md
- examples/*/README.md
- skills/beads/references/CLI_REFERENCE.md

Note: beads.jsonl is still supported for backward compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 01:08:46 -08:00

629 lines
19 KiB
Markdown

# Git Integration Guide
**For:** AI agents and developers managing bd git workflows
**Version:** 0.21.0+
## Overview
bd integrates deeply with git for issue tracking synchronization. This guide covers merge conflict resolution, intelligent merge drivers, git worktrees, and protected branch workflows.
## Git Worktrees
**🚧 Enhanced Support:** Beads now has comprehensive Git worktree compatibility with shared database architecture. While thoroughly tested internally, real-world usage may reveal additional edge cases.
### How It Works
Git worktrees share the same `.git` directory and `.beads` database:
- All worktrees use the same `.beads/beads.db` file in the main repository
- Database discovery prioritizes main repository location
- Worktree-aware git operations prevent conflicts
- Git hooks automatically adapt to worktree context
### Daemon Mode Limitations
**⚠️ Important:** Daemon mode does NOT work correctly with `git worktree` due to shared database state.
The daemon maintains its own view of the current working directory and git state. When multiple worktrees share the same `.beads` database, the daemon may commit changes intended for one branch to a different branch.
### Solutions for Worktree Users
**1. Use `--no-daemon` flag (recommended):**
```bash
bd --no-daemon ready
bd --no-daemon create "Fix bug" -p 1
bd --no-daemon update bd-42 --status in_progress
```
**2. Disable daemon via environment (entire session):**
```bash
export BEADS_NO_DAEMON=1
bd ready # All commands use direct mode
```
**3. Disable auto-start (less safe, still warns):**
```bash
export BEADS_AUTO_START_DAEMON=false
```
### Automatic Detection & Warnings
bd automatically detects worktrees and shows prominent warnings if daemon mode is active:
```
╔══════════════════════════════════════════════════════════════════════════╗
║ WARNING: Git worktree detected with daemon mode ║
╠══════════════════════════════════════════════════════════════════════════╣
║ Git worktrees share the same .beads directory, which can cause the ║
║ daemon to commit/push to the wrong branch. ║
║ ║
║ Shared database: /path/to/main/.beads ║
║ Worktree git dir: /path/to/shared/.git ║
║ ║
║ RECOMMENDED SOLUTIONS: ║
║ 1. Use --no-daemon flag: bd --no-daemon <command> ║
║ 2. Disable daemon mode: export BEADS_NO_DAEMON=1 ║
╚══════════════════════════════════════════════════════════════════════════╝
```
### Worktree-Aware Features
**Database Discovery:**
- Searches main repository first for `.beads` directory
- Falls back to worktree-local search if needed
- Prevents database duplication across worktrees
**Git Hooks:**
- Pre-commit hook adapts to worktree context
- Automatically stages JSONL in regular repos
- Safely skips staging in worktrees (files outside working tree)
- Post-merge hook works correctly in both contexts
**Sync Operations:**
- Worktree-aware repository root detection
- Proper handling of git directory vs git common directory
- Safe concurrent access to shared database
### Why This Architecture Works
- **Shared Database:** Eliminates data duplication and sync conflicts
- **Priority Search:** Main repository database takes precedence
- **SQLite Locking:** Prevents corruption during concurrent access
- **Git Integration:** Hooks and sync operations adapt to context
- **Clear Warnings:** Users are guided to safe usage patterns
## Handling Merge Conflicts
**With hash-based IDs (v0.20.1+), ID collisions are eliminated!** Different issues get different hash IDs, so most git merges succeed cleanly.
### When Conflicts Occur
Git conflicts in `.beads/issues.jsonl` happen when:
- **Same issue modified on both branches** (different timestamps/fields)
- This is a **same-issue update conflict**, not an ID collision
- Conflicts are rare in practice since hash IDs prevent structural collisions
### Automatic Detection
bd automatically detects conflict markers and shows clear resolution steps:
```bash
# bd import rejects files with conflict markers
bd import -i .beads/issues.jsonl
# Error: JSONL file contains git conflict markers
# Resolve with: git checkout --theirs .beads/issues.jsonl
# Validate for conflicts
bd validate --checks=conflicts
```
Conflict markers detected: `<<<<<<<`, `=======`, `>>>>>>>`
### Resolution Workflow
```bash
# After git merge creates conflict in .beads/issues.jsonl
# Option 1: Accept their version (remote)
git checkout --theirs .beads/issues.jsonl
bd import -i .beads/issues.jsonl
# Option 2: Keep our version (local)
git checkout --ours .beads/issues.jsonl
bd import -i .beads/issues.jsonl
# Option 3: Manual resolution in editor
# Edit .beads/issues.jsonl to remove conflict markers
bd import -i .beads/issues.jsonl
# Commit the merge
git add .beads/issues.jsonl
git commit
```
**Note:** `bd import` automatically handles updates - same ID with different content is a normal update operation. No special flags needed. If you accidentally modified the same issue in both branches, just pick whichever version is more complete.
## Intelligent Merge Driver (Auto-Configured)
**As of v0.21+**, bd automatically configures its own merge driver during `bd init`. This uses the beads-merge algorithm (by @neongreen, vendored into bd) to provide intelligent JSONL merging.
### What It Does
- **Field-level 3-way merging** (not line-by-line)
- **Matches issues by identity** (id + created_at + created_by)
- **Smart field merging:**
- Timestamps → max value
- Dependencies → union
- Status/priority → 3-way merge
- **Conflict markers** only for unresolvable conflicts
- **Auto-configured** during `bd init` (both interactive and `--quiet` modes)
### Auto-Configuration
**Happens automatically during `bd init`:**
```bash
# These are configured automatically:
git config merge.beads.driver "bd merge %A %O %A %B"
git config merge.beads.name "bd JSONL merge driver"
# .gitattributes entry added:
# .beads/issues.jsonl merge=beads
```
### Manual Setup
**If you skipped merge driver with `--skip-merge-driver`:**
```bash
git config merge.beads.driver "bd merge %A %O %A %B"
git config merge.beads.name "bd JSONL merge driver"
echo ".beads/issues.jsonl merge=beads" >> .gitattributes
```
### How It Works
During `git merge`, beads-merge:
1. Parses JSONL from all 3 versions (base, ours, theirs)
2. Matches issues by identity (id + created_at + created_by)
3. Merges fields intelligently per issue
4. Outputs merged JSONL or conflict markers
**Benefits:**
- Prevents spurious conflicts from line renumbering
- Handles timestamp updates gracefully
- Merges dependency/label changes intelligently
- Only conflicts on true semantic conflicts
### Alternative: Standalone beads-merge Binary
**If you prefer the standalone binary (same algorithm):**
```bash
# Install (requires Go 1.24+)
git clone https://github.com/neongreen/mono.git
cd mono/beads-merge
go install
# Configure Git merge driver
git config merge.beads.name "JSONL merge driver for beads"
git config merge.beads.driver "beads-merge %A %O %A %B"
```
### Jujutsu Integration
**For Jujutsu users**, add to `~/.jjconfig.toml`:
```toml
[merge-tools.beads-merge]
program = "beads-merge"
merge-args = ["$output", "$base", "$left", "$right"]
merge-conflict-exit-codes = [1]
```
Then resolve with:
```bash
jj resolve --tool=beads-merge
```
## Protected Branch Workflows
**If your repository uses protected branches** (GitHub, GitLab, etc.), bd can commit to a separate branch instead of `main`:
### Configuration
```bash
# Initialize with separate sync branch
bd init --branch beads-metadata
# Or configure existing setup
bd config set sync.branch beads-metadata
```
### How It Works
- Beads commits issue updates to `beads-metadata` instead of `main`
- Uses git worktrees (lightweight checkouts) in `.git/beads-worktrees/`
- Your main working directory is never affected
- Periodically merge `beads-metadata` back to `main` via pull request
### Daily Workflow (Unchanged for Agents)
```bash
# Agents work normally - no changes needed!
bd create "Fix authentication" -t bug -p 1
bd update bd-a1b2 --status in_progress
bd close bd-a1b2 "Fixed"
```
All changes automatically commit to `beads-metadata` branch (if daemon is running with `--auto-commit`).
### Merging to Main (Humans)
```bash
# Check what's changed
bd sync --status
# Option 1: Create pull request
git push origin beads-metadata
# Then create PR on GitHub/GitLab
# Option 2: Direct merge (if allowed)
bd sync --merge
```
### Benefits
- ✅ Works with protected `main` branches
- ✅ No disruption to agent workflows
- ✅ Platform-agnostic (works on any git platform)
- ✅ Backward compatible (opt-in via config)
See [PROTECTED_BRANCHES.md](PROTECTED_BRANCHES.md) for complete setup guide, troubleshooting, and examples.
## Git Hooks Integration
**STRONGLY RECOMMENDED:** Install git hooks for automatic sync and consistency.
### Installation
```bash
# One-time setup in each beads workspace
./examples/git-hooks/install.sh
```
### What Gets Installed
**pre-commit hook:**
- Flushes pending changes immediately before commit
- Bypasses 30-second debounce
- Guarantees JSONL is current
**post-merge hook:**
- Imports updated JSONL after pull/merge
- Guarantees database sync after remote changes
**pre-push hook:**
- Exports database to JSONL before push
- Prevents stale JSONL from reaching remote
- **Critical for multi-workspace consistency**
**post-checkout hook:**
- Imports updated JSONL after branch switches
- Ensures database reflects checked-out branch state
### Why Hooks Matter
**Without pre-push hook:**
- Database changes committed locally
- Stale JSONL pushed to remote
- Other workspaces diverge from truth
**With pre-push hook:**
- JSONL always reflects database state
- All workspaces stay synchronized
- No manual `bd sync` needed
See [examples/git-hooks/README.md](../examples/git-hooks/README.md) for details.
### Implementation Details
#### Hook Installation (`cmd/bd/hooks.go`)
The `installHooks()` function:
- Writes embedded hook scripts to the `.git/hooks/` directory
- Creates the hooks directory with `os.MkdirAll()` if needed
- Backs up existing hooks with `.backup` extension (unless `--force` flag used)
- Sets execute permissions (0755) on installed hooks
- Supports shared mode via `--shared` flag (installs to `.beads-hooks/` instead)
#### Git Directory Resolution
**Critical for worktree support:** The `getGitDir()` helper uses `git rev-parse --git-dir` to resolve the actual git directory:
```go
// Returns ".git" in normal repos
// Returns "/path/to/shared/.git" in git worktrees
// (where .git is a file containing "gitdir: /path/to/actual/git/dir")
gitDir, err := getGitDir()
```
In **normal repositories**, `.git` is a directory containing the git internals.
In **git worktrees**, `.git` is a file containing `gitdir: /path/to/actual/git/dir`, pointing to the shared git directory.
This difference breaks code that assumes `.git` is always a directory. Using `getGitDir()` ensures hooks work correctly in both cases.
#### Hook Detection (`cmd/bd/init.go`)
The `detectExistingHooks()` function scans for existing hooks and classifies them:
- **bd hooks**: Identified by "bd (beads) pre-commit hook" comment in content
- **pre-commit framework hooks**: Detected by "pre-commit framework" or "pre-commit.com" in content
- **Custom hooks**: Any other existing hook
This classification allows bd to:
- Avoid re-installing already-installed bd hooks
- Support chaining with pre-commit framework hooks
- Warn when overwriting custom hooks
#### Hook Testing
Tests in `hooks_test.go` and `init_hooks_test.go`:
1. Initialize real git repositories via `exec.Command("git", "init")`
2. Call `getGitDir()` to get the actual git directory path
3. Construct hooks path with `filepath.Join(gitDirPath, "hooks")`
4. Create hooks directory if needed with `os.MkdirAll()`
5. Execute hook operations and verify results
This approach ensures tests work correctly in both normal repos and git worktrees, preventing failures when running in worktree environments where `.git` is a file.
## Multi-Workspace Sync Strategies
### Centralized Repository Pattern
```
┌──────────────┐
│ Developer A │────┐
│ (Workspace) │ │
└──────────────┘ │
┌──────────────┐ ┌─────────────────┐
│ Developer B │─▶│ Central Repo │
│ (Workspace) │ │ (.beads/*.jsonl)│
└──────────────┘ └─────────────────┘
┌──────────────┐ │
│ CI/CD │────┘
│ (Workspace) │
└──────────────┘
```
**Best for:**
- Teams working on shared repository
- CI/CD integration
- Multi-agent workflows
**Key points:**
- Each workspace has its own daemon
- Git is the source of truth
- Auto-sync keeps workspaces consistent
### Fork-Based Pattern
```
┌──────────────┐ ┌─────────────────┐
│ OSS Contrib │─────▶│ Planning Repo │
│ (Fork) │ │ (.beads/*.jsonl)│
└──────────────┘ └─────────────────┘
│ PR
┌─────────────────┐
│ Upstream Repo │
│ (no .beads/) │
└─────────────────┘
```
**Best for:**
- Open source contributors
- Solo developers
- Private task tracking on public repos
**Setup:**
```bash
bd init --contributor # Interactive wizard
```
See [MULTI_REPO_MIGRATION.md](MULTI_REPO_MIGRATION.md) for complete guide.
### Team Branch Pattern
```
┌──────────────┐
│ Team Member │────┐
│ (main) │ │
└──────────────┘ │
┌──────────────┐ ┌─────────────────┐
│ Team Member │─▶│ Shared Repo │
│ (main) │ │ (beads-metadata)│
└──────────────┘ └─────────────────┘
```
**Best for:**
- Teams on protected branches
- Managed git workflows
- Review-before-merge policies
**Setup:**
```bash
bd init --team # Interactive wizard
```
See [MULTI_REPO_MIGRATION.md](MULTI_REPO_MIGRATION.md) for complete guide.
## Sync Timing and Control
### Automatic Sync (Default)
**With daemon running:**
- Export to JSONL: 30-second debounce after changes
- Import from JSONL: when file is newer than DB
- Commit/push: configurable via `--auto-commit` / `--auto-push`
**30-second debounce provides transaction window:**
- Multiple changes within 30s get batched
- Single JSONL export/commit for the batch
- Prevents commit spam
### Manual Sync
```bash
# Force immediate sync (bypass debounce)
bd sync
# What it does:
# 1. Export pending changes to JSONL
# 2. Commit to git
# 3. Pull from remote
# 4. Import any updates
# 5. Push to remote
```
**ALWAYS run `bd sync` at end of agent sessions** to ensure changes are committed/pushed.
### Disable Automatic Sync
```bash
# Disable auto-flush (no export until manual sync)
bd --no-auto-flush ready
# Disable auto-import (no import on file changes)
bd --no-auto-import ready
# Disable both (manual sync only)
export BEADS_NO_DAEMON=1 # Direct mode
```
## Git Configuration Best Practices
### Recommended .gitignore
```
# bd database (not tracked - JSONL is source of truth)
.beads/beads.db
.beads/beads.db-*
.beads/bd.sock
.beads/bd.pipe
# bd daemon state
.beads/.exclusive-lock
# Git worktrees (if using protected branches)
.git/beads-worktrees/
```
### Recommended .gitattributes
**IMPORTANT:** The `.gitattributes` file should be **committed to git**, not ignored. It configures merge behavior for the entire team.
```
# Intelligent merge driver for JSONL (auto-configured by bd init)
.beads/issues.jsonl merge=beads
# Treat JSONL as text for diffs
.beads/*.jsonl text diff
```
This file is automatically created by `bd init` and is essential for:
- Preventing spurious merge conflicts in `.beads/issues.jsonl`
- Enabling field-level 3-way merging instead of line-by-line
- Ensuring all team members get intelligent JSONL merging
### Git LFS Considerations
**Do NOT use Git LFS for `.beads/issues.jsonl`:**
- JSONL needs intelligent merge (doesn't work with LFS)
- File size stays reasonable (<1MB per 10K issues)
- Text diffs are valuable for review
## Troubleshooting Git Issues
### Issue: "JSONL file is ahead of database"
**Symptoms:**
```
WARN Database timestamp older than JSONL, importing...
```
**Solutions:**
```bash
# Normal after git pull - auto-import handles it
# If stuck, force import:
bd import -i .beads/issues.jsonl
```
### Issue: "Database is ahead of JSONL"
**Symptoms:**
```
WARN JSONL timestamp older than database, exporting...
```
**Solutions:**
```bash
# Normal after local changes - auto-export handles it
# If stuck, force export:
bd sync
```
### Issue: Merge conflicts every time
**Symptoms:**
- Git merge always creates conflicts in `.beads/issues.jsonl`
- Merge driver not being used
**Solutions:**
```bash
# Check merge driver configured
git config merge.beads.driver
# Reinstall if missing
bd init --skip-db # Only reconfigure git, don't touch database
# Verify .gitattributes
grep "issues.jsonl" .gitattributes
# Expected: .beads/issues.jsonl merge=beads
```
### Issue: Changes not syncing to other workspaces
**Symptoms:**
- Agent A creates issue
- Agent B doesn't see it after `git pull`
**Solutions:**
```bash
# Agent A: Ensure changes were pushed
bd sync
git push
# Agent B: Force import
git pull
bd import -i .beads/issues.jsonl
# Check git hooks installed (prevent future issues)
./examples/git-hooks/install.sh
```
## See Also
- [AGENTS.md](../AGENTS.md) - Main agent workflow guide
- [DAEMON.md](DAEMON.md) - Daemon management and configuration
- [PROTECTED_BRANCHES.md](PROTECTED_BRANCHES.md) - Protected branch workflows
- [MULTI_REPO_MIGRATION.md](MULTI_REPO_MIGRATION.md) - Multi-repo patterns
- [examples/git-hooks/README.md](../examples/git-hooks/README.md) - Git hooks integration