Git merge drivers only support three placeholders: - %O (ancestor/base) - %A (current version) - %B (other branch's version) The code was incorrectly using %L and %R, which don't exist in git, causing them to be passed through literally and breaking JSONL merges. Changes: - Fixed merge driver config in init.go, merge.go, README.md, docs - Added detection in bd doctor with clear error messages - Added auto-fix in bd doctor --fix - Added proactive warning in bd sync before git pull - Added reactive error detection after merge failures - Updated all tests to use correct placeholders Now users get helpful guidance at every step: 1. bd doctor detects the issue 2. bd doctor --fix auto-corrects it 3. bd sync warns before pulling if misconfigured 4. Error messages suggest bd doctor --fix when merge fails 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
535 lines
14 KiB
Markdown
535 lines
14 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
|
|
|
|
**⚠️ Important Limitation:** Daemon mode does NOT work correctly with `git worktree`.
|
|
|
|
### The Problem
|
|
|
|
Git worktrees share the same `.git` directory and `.beads` database:
|
|
- All worktrees use the same `.beads/beads.db` file
|
|
- Daemon doesn't know which branch each worktree has checked out
|
|
- Can commit/push changes to the wrong branch
|
|
- Leads to confusion and incorrect git history
|
|
|
|
### What You Lose Without Daemon Mode
|
|
|
|
- **Auto-sync** - No automatic commit/push of changes (use `bd sync` manually)
|
|
- **MCP server** - beads-mcp requires daemon for multi-repo support
|
|
- **Background watching** - No automatic detection of remote changes
|
|
|
|
### 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
|
|
|
|
bd automatically detects worktrees and shows prominent warning if daemon mode is active. The `--no-daemon` mode works correctly since it operates directly on the database without shared state.
|
|
|
|
### Why It Matters
|
|
|
|
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.
|
|
|
|
## 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/beads.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/beads.jsonl
|
|
# Error: JSONL file contains git conflict markers
|
|
# Resolve with: git checkout --theirs .beads/beads.jsonl
|
|
|
|
# Validate for conflicts
|
|
bd validate --checks=conflicts
|
|
```
|
|
|
|
Conflict markers detected: `<<<<<<<`, `=======`, `>>>>>>>`
|
|
|
|
### Resolution Workflow
|
|
|
|
```bash
|
|
# After git merge creates conflict in .beads/beads.jsonl
|
|
|
|
# Option 1: Accept their version (remote)
|
|
git checkout --theirs .beads/beads.jsonl
|
|
bd import -i .beads/beads.jsonl
|
|
|
|
# Option 2: Keep our version (local)
|
|
git checkout --ours .beads/beads.jsonl
|
|
bd import -i .beads/beads.jsonl
|
|
|
|
# Option 3: Manual resolution in editor
|
|
# Edit .beads/beads.jsonl to remove conflict markers
|
|
bd import -i .beads/beads.jsonl
|
|
|
|
# Commit the merge
|
|
git add .beads/beads.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/beads.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/beads.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.21+)
|
|
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**
|
|
|
|
### 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.
|
|
|
|
## 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/beads.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/beads.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/beads.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/beads.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/beads.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 "beads.jsonl" .gitattributes
|
|
# Expected: .beads/beads.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/beads.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
|