Adds Dolt auto-commit functionality for write commands and sets explicit commit authors. Includes fix for race condition in commandDidWrite (converted to atomic.Bool). Original PR: #1267 by @coffeegoddd Co-authored-by: Dustin Brown <dustin@dolthub.com>
19 KiB
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.dbfile 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):
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):
export BEADS_NO_DAEMON=1
bd ready # All commands use direct mode
3. Disable auto-start (less safe, still warns):
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
.beadsdirectory - 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:
# 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
# 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--quietmodes)
Auto-Configuration
Happens automatically during bd init:
# 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:
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:
- Parses JSONL from all 3 versions (base, ours, theirs)
- Matches issues by identity (id + created_at + created_by)
- Merges fields intelligently per issue
- 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:
[merge-tools.beads-merge]
program = "bd"
merge-args = ["merge", "$output", "$base", "$left", "$right"]
merge-conflict-exit-codes = [1]
Then resolve with:
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
# 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-syncinstead ofmain - Uses git worktrees (lightweight checkouts) in
.git/beads-worktrees/ - Your main working directory is never affected
- Periodically merge
beads-syncback tomainvia pull request
Daily Workflow (Unchanged for Agents)
# 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)
# 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
mainbranches - ✅ No disruption to agent workflows
- ✅ Platform-agnostic (works on any git platform)
- ✅ Backward compatible (opt-in via config)
See PROTECTED_BRANCHES.md for complete setup guide, troubleshooting, and examples.
Git Hooks Integration
STRONGLY RECOMMENDED: Install git hooks for automatic sync and consistency.
Installation
# 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 syncneeded
See 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
.backupextension (unless--forceflag used) - Sets execute permissions (0755) on installed hooks
- Supports shared mode via
--sharedflag (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:
// 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:
- Initialize real git repositories via
exec.Command("git", "init") - Call
getGitDir()to get the actual git directory path - Construct hooks path with
filepath.Join(gitDirPath, "hooks") - Create hooks directory if needed with
os.MkdirAll() - 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:
bd init --contributor # Interactive wizard
See 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:
bd init --team # Interactive wizard
See MULTI_REPO_MIGRATION.md for complete guide.
Sync Timing and Control
Automatic Sync (Default)
With daemon running (SQLite backend):
- 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
Note: --auto-commit here refers to git commits (typically to the sync branch).
For the Dolt backend, use dolt.auto-commit / --dolt-auto-commit to control Dolt history commits.
30-second debounce provides transaction window:
- Multiple changes within 30s get batched
- Single JSONL export/commit for the batch
- Prevents commit spam
Manual Sync
# 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
# 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:
# 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:
# 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:
# 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:
# 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 - Main agent workflow guide
- DAEMON.md - Daemon management and configuration
- PROTECTED_BRANCHES.md - Protected branch workflows
- MULTI_REPO_MIGRATION.md - Multi-repo patterns
- examples/git-hooks/README.md - Git hooks integration