- Remove standalone beads-merge binary section (no longer maintained) - Update Jujutsu config to use `bd merge` instead of `beads-merge` - Fix config path to standard `~/.config/jj/config.toml` - Add note that tool only works for .beads/issues.jsonl Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
616 lines
19 KiB
Markdown
616 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
|
|
|
|
### Jujutsu Integration
|
|
|
|
**For Jujutsu users**, add to `~/.config/jj/config.toml`:
|
|
|
|
```toml
|
|
[merge-tools.beads-merge]
|
|
program = "bd"
|
|
merge-args = ["merge", "$output", "$base", "$left", "$right"]
|
|
merge-conflict-exit-codes = [1]
|
|
```
|
|
|
|
Then resolve with:
|
|
```bash
|
|
jj resolve --tool=beads-merge .beads/issues.jsonl
|
|
```
|
|
|
|
**Note:** This only works for `.beads/issues.jsonl` since `bd merge` is a specialized JSONL merge tool.
|
|
|
|
## 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-sync
|
|
|
|
# Or configure existing setup
|
|
bd config set sync.branch beads-sync
|
|
```
|
|
|
|
### How It Works
|
|
|
|
- Beads commits issue updates to `beads-sync` instead of `main`
|
|
- Uses git worktrees (lightweight checkouts) in `.git/beads-worktrees/`
|
|
- Your main working directory is never affected
|
|
- Periodically merge `beads-sync` 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-sync` 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-sync
|
|
# 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-sync)│
|
|
└──────────────┘ └─────────────────┘
|
|
```
|
|
|
|
**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
|