Files
beads/examples/git-hooks
Steve Yegge 0b0d9a43d1 Improve bd-my64 fix based on oracle review
Oracle identified a critical race condition in the initial fix:
- Pre-push hook checked for changes but didn't flush first
- Pending 5s-debounced flushes could land after the check
- Result: stale JSONL could still be pushed

Improvements:
1. Pre-push now flushes pending changes FIRST (bd sync --flush-only)
2. Uses git status --porcelain to catch ALL change types:
   - Staged, unstaged, untracked, deleted, renamed, conflicts
3. Handles both beads.jsonl and issues.jsonl (backward compat)
4. Works even without bd installed (git-only check)
5. Pre-commit stages both JSONL files (simpler loop)

This completely eliminates the race condition.
2025-11-06 19:01:28 -08:00
..

bd Git Hooks

This directory contains git hooks that integrate bd (beads) with your git workflow, preventing stale JSONL from being pushed to remote.

The Problem

Two race conditions can occur:

  1. Between operations and commits: Daemon auto-flush (5s debounce) may fire after commit

    • User closes issue via MCP → daemon schedules flush (5 sec delay)
    • User commits code changes → JSONL appears clean
    • Daemon flush fires → JSONL modified after commit
    • Result: dirty working tree showing JSONL changes
  2. Between commits and pushes: Changes made after commit but before push (bd-my64)

    • User commits → pre-commit hook flushes JSONL
    • User adds comments or updates issues
    • User pushes → outdated JSONL is pushed
    • Result: remote has stale JSONL

The Solution

These git hooks ensure bd changes are always synchronized with your commits and pushes:

  • pre-commit - Flushes pending bd changes to JSONL before commit and stages it
  • pre-push - Blocks push if JSONL has uncommitted changes (bd-my64)
  • post-merge - Imports updated JSONL after git pull/merge

Installation

Quick Install

From your repository root:

./examples/git-hooks/install.sh

This will:

  • Copy hooks to .git/hooks/
  • Make them executable
  • Back up any existing hooks

Manual Install

cp examples/git-hooks/pre-commit .git/hooks/pre-commit
cp examples/git-hooks/pre-push .git/hooks/pre-push
cp examples/git-hooks/post-merge .git/hooks/post-merge
chmod +x .git/hooks/pre-commit .git/hooks/pre-push .git/hooks/post-merge

How It Works

pre-commit

Before each commit, the hook runs:

bd sync --flush-only

This:

  1. Exports any pending database changes to .beads/issues.jsonl
  2. Stages the JSONL file if modified
  3. Allows the commit to proceed with clean state

The hook is silent on success, fast (no git operations), and safe (fails commit if flush fails).

pre-push

Before each push, the hook:

bd sync --flush-only  # Flush pending changes (if bd available)
git status --porcelain .beads/*.jsonl  # Check for uncommitted changes

This prevents pushing stale JSONL by:

  1. Flushing pending in-memory changes from daemon's 5s debounce
  2. Checking for uncommitted changes (staged, unstaged, untracked, deleted)
  3. Failing the push with clear error message if changes exist
  4. Instructing user to commit JSONL before pushing again

This solves bd-my64: changes made between commit and push (or pending debounced flushes) are caught before reaching remote.

post-merge

After a git pull or merge, the hook runs:

bd import -i .beads/beads.jsonl

This ensures your local database reflects the merged state. The hook:

  • Only runs if .beads/beads.jsonl exists (also checks issues.jsonl for backward compat)
  • Imports any new issues or updates from the merge
  • Warns on failure but doesn't block the merge

Note: With hash-based IDs (v0.20.1+), ID collisions don't occur - different issues get different hash IDs.

Compatibility

  • Auto-sync: Works alongside bd's automatic 5-second debounce
  • Direct mode: Hooks work in both daemon and --no-daemon mode
  • Worktrees: Safe to use with git worktrees

Benefits

No more dirty working tree after commits
Database always in sync with git
Automatic collision resolution on merge
Fast and silent operation
Optional - manual bd sync still works

Uninstall

Remove the hooks:

rm .git/hooks/pre-commit .git/hooks/pre-push .git/hooks/post-merge

Your backed-up hooks (if any) are in .git/hooks/*.backup-*.

  • See bd-51 for the race condition bug report
  • See AGENTS.md for the full git workflow
  • See examples/ for other integrations