From 9057aeba1742ddecc5a7c7cf5b82821e915c7169 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Sat, 13 Dec 2025 10:13:42 +1100 Subject: [PATCH] fix(gitignore): switch to whitelist approach for .beads/.gitignore (#473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .beads/.gitignore now ignores everything by default and explicitly whitelists tracked files. This fixes confusion about which files to commit when using protected branches workflow. Changes: - Use `*` to ignore all by default, then `!file` to whitelist - Fix config.json -> config.yaml (wrong filename in negation) - Update doctor check to validate new patterns - Update PROTECTED_BRANCHES.md documentation - Simplify git add instructions to just `git add .beads/` Fixes #473 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .beads/.gitignore | 43 +++++++++----------------- cmd/bd/doctor/gitignore.go | 62 +++++++++++++++----------------------- cmd/bd/init_test.go | 20 +++++------- docs/PROTECTED_BRANCHES.md | 41 +++++++++++++------------ 4 files changed, 67 insertions(+), 99 deletions(-) diff --git a/.beads/.gitignore b/.beads/.gitignore index 3f0f4627..6573a6c6 100644 --- a/.beads/.gitignore +++ b/.beads/.gitignore @@ -1,33 +1,20 @@ -# SQLite databases -*.db -*.db?* -*.db-journal -*.db-wal -*.db-shm +# Ignore all .beads/ contents by default (local workspace files) +# Only files explicitly whitelisted below will be tracked in git +* -# Daemon runtime files -daemon.lock -daemon.log -daemon.pid -bd.sock +# === Files tracked in git (shared across clones) === -# Local version tracking (prevents upgrade notification spam after git operations) -# bd-tok: Store version locally instead of in tracked metadata.json -.local_version +# This gitignore file itself +!.gitignore -# Legacy database files -db.sqlite -bd.db - -# Merge artifacts (temporary files from 3-way merge) -beads.base.jsonl -beads.base.meta.json -beads.left.jsonl -beads.left.meta.json -beads.right.jsonl -beads.right.meta.json - -# Keep JSONL exports and config (source of truth for git) +# Issue data in JSONL format (the main data file) !issues.jsonl + +# Repository metadata (database name, JSONL filename) !metadata.json -!config.json + +# Configuration template (sync branch, integrations) +!config.yaml + +# Documentation for contributors +!README.md diff --git a/cmd/bd/doctor/gitignore.go b/cmd/bd/doctor/gitignore.go index c107812c..e3328a74 100644 --- a/cmd/bd/doctor/gitignore.go +++ b/cmd/bd/doctor/gitignore.go @@ -7,52 +7,38 @@ import ( ) // GitignoreTemplate is the canonical .beads/.gitignore content -const GitignoreTemplate = `# SQLite databases -*.db -*.db?* -*.db-journal -*.db-wal -*.db-shm +// Uses whitelist approach: ignore everything by default, explicitly allow tracked files. +// This prevents confusion about which files to commit (fixes GitHub #473). +const GitignoreTemplate = `# Ignore all .beads/ contents by default (local workspace files) +# Only files explicitly whitelisted below will be tracked in git +* -# Daemon runtime files -daemon.lock -daemon.log -daemon.pid -bd.sock +# === Files tracked in git (shared across clones) === -# Local version tracking (prevents upgrade notification spam after git ops) -.local_version +# This gitignore file itself +!.gitignore -# Legacy database files -db.sqlite -bd.db - -# Merge artifacts (temporary files from 3-way merge) -beads.base.jsonl -beads.base.meta.json -beads.left.jsonl -beads.left.meta.json -beads.right.jsonl -beads.right.meta.json - -# Backup directories created by bd reset --backup -.beads-backup-*/ - -# Keep JSONL exports and config (source of truth for git) +# Issue data in JSONL format (the main data file) !issues.jsonl + +# Repository metadata (database name, JSONL filename) !metadata.json -!config.json + +# Configuration template (sync branch, integrations) +!config.yaml + +# Documentation for contributors +!README.md ` // requiredPatterns are patterns that MUST be in .beads/.gitignore +// With the whitelist approach, we check for the blanket ignore and whitelisted files var requiredPatterns = []string{ - "beads.base.jsonl", - "beads.left.jsonl", - "beads.right.jsonl", - "beads.base.meta.json", - "beads.left.meta.json", - "beads.right.meta.json", - "*.db?*", + "*", // Blanket ignore (whitelist approach) + "!.gitignore", // Whitelist the gitignore itself + "!issues.jsonl", + "!metadata.json", + "!config.yaml", // Fixed: was incorrectly !config.json before #473 } // CheckGitignore checks if .beads/.gitignore is up to date @@ -83,7 +69,7 @@ func CheckGitignore() DoctorCheck { return DoctorCheck{ Name: "Gitignore", Status: "warning", - Message: "Outdated .beads/.gitignore (missing merge artifact patterns)", + Message: "Outdated .beads/.gitignore (needs whitelist patterns)", Detail: "Missing: " + strings.Join(missing, ", "), Fix: "Run: bd doctor --fix or bd init (safe to re-run)", } diff --git a/cmd/bd/init_test.go b/cmd/bd/init_test.go index c96d2b85..c91ceabf 100644 --- a/cmd/bd/init_test.go +++ b/cmd/bd/init_test.go @@ -115,21 +115,15 @@ func TestInitCommand(t *testing.T) { if err != nil { t.Errorf(".gitignore file was not created: %v", err) } else { - // Check for essential patterns + // Check for essential patterns (whitelist approach - GitHub #473) gitignoreStr := string(gitignoreContent) expectedPatterns := []string{ - "*.db", - "*.db?*", - "*.db-journal", - "*.db-wal", - "*.db-shm", - "daemon.log", - "daemon.pid", - "bd.sock", - "beads.base.jsonl", - "beads.left.jsonl", - "beads.right.jsonl", - "!issues.jsonl", + "*", // Blanket ignore + "!.gitignore", // Whitelist gitignore itself + "!issues.jsonl", // Whitelist JSONL + "!metadata.json", + "!config.yaml", + "!README.md", } for _, pattern := range expectedPatterns { if !strings.Contains(gitignoreStr, pattern) { diff --git a/docs/PROTECTED_BRANCHES.md b/docs/PROTECTED_BRANCHES.md index acefd8a0..55f8de53 100644 --- a/docs/PROTECTED_BRANCHES.md +++ b/docs/PROTECTED_BRANCHES.md @@ -38,33 +38,32 @@ bd init --branch beads-metadata This creates a `.beads/` directory and configures beads to commit to `beads-metadata` instead of `main`. -**Important:** After initialization, you'll see some untracked files that should be committed to your protected branch: +**Important:** After initialization, commit the beads configuration to your protected branch: ```bash -# Check what files were created -git status - -# Commit the beads configuration to your protected branch -git add .beads/.gitignore .gitattributes +# The .beads/.gitignore uses a whitelist - only tracked files show up +git add .beads/ .gitattributes git commit -m "Initialize beads issue tracker" git push origin main # Or create a PR if required ``` **Files created by `bd init --branch`:** -Files that should be committed to your protected branch (main): -- `.beads/.gitignore` - Tells git what to ignore in .beads/ directory +Files committed to your **protected branch** (main): +- `.beads/.gitignore` - Whitelist of tracked files (everything else ignored) - `.gitattributes` - Configures merge driver for intelligent JSONL conflict resolution -Files that are automatically gitignored (do NOT commit): -- `.beads/beads.db` - SQLite database (local only, regenerated from JSONL) -- `.beads/daemon.lock`, `daemon.log`, `daemon.pid` - Runtime files -- `.beads/beads.left.jsonl`, `beads.right.jsonl` - Temporary merge artifacts - -The sync branch (beads-metadata) will contain: -- `.beads/beads.jsonl` - Issue data in JSONL format (committed automatically by daemon) +Files committed to your **sync branch** (beads-metadata) by the daemon: +- `.beads/issues.jsonl` - Issue data in JSONL format - `.beads/metadata.json` - Metadata about the beads installation -- `.beads/config.yaml` - Configuration template (optional) +- `.beads/config.yaml` - Configuration settings +- `.beads/README.md` - Documentation for contributors + +Files that are **automatically ignored** (local only, never committed): +- `.beads/beads.db` - SQLite database (regenerated from JSONL) +- `.beads/daemon.*` - Runtime files (lock, log, pid, socket) +- `.beads/beads.*.jsonl` - Temporary merge artifacts +- Everything else in `.beads/` not explicitly whitelisted **2. Start the daemon with auto-commit:** @@ -112,17 +111,19 @@ your-project/ **What lives in each branch:** Main branch (protected): -- `.beads/.gitignore` - Tells git what to ignore +- `.beads/.gitignore` - Whitelist of tracked files - `.gitattributes` - Merge driver configuration Sync branch (beads-metadata): -- `.beads/beads.jsonl` - Issue data (committed by daemon) +- `.beads/issues.jsonl` - Issue data (committed by daemon) - `.beads/metadata.json` - Repository metadata -- `.beads/config.yaml` - Configuration template +- `.beads/config.yaml` - Configuration settings +- `.beads/README.md` - Documentation -Not tracked (gitignored): +Not tracked (gitignored via whitelist): - `.beads/beads.db` - SQLite database (local only) - `.beads/daemon.*` - Runtime files +- Everything else not in the whitelist **Key points:** - The worktree is in `.git/beads-worktrees/` (hidden from your workspace)