feat: add Git worktree compatibility (PR #478)
Adds comprehensive Git worktree support for beads issue tracking: Core changes: - New internal/git/gitdir.go package for worktree detection - GetGitDir() returns proper .git location (main repo, not worktree) - Updated all hooks to use git.GetGitDir() instead of local helper - BeadsDir() now prioritizes main repository's .beads directory Features: - Hooks auto-install in main repo when run from worktree - Shared .beads directory across all worktrees - Config option no-install-hooks to disable auto-install - New bd worktree subcommand for diagnostics Documentation: - New docs/WORKTREES.md with setup instructions - Updated CHANGELOG.md and AGENT_INSTRUCTIONS.md Testing: - Updated tests to use exported git.GetGitDir() - Added worktree detection tests Co-authored-by: Claude <noreply@anthropic.com> Closes: #478
This commit is contained in:
@@ -3,7 +3,6 @@ package fix
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -14,9 +13,6 @@ import (
|
||||
// - When .beads is a symlink, Permissions() should return nil without changing anything
|
||||
// - This prevents attempts to chmod symlink targets (which may be read-only like /nix/store)
|
||||
func TestPermissions_SkipsSymlinkedBeadsDir(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping symlink test on Windows - requires elevated privileges")
|
||||
}
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create target .beads directory with wrong permissions
|
||||
@@ -66,9 +62,6 @@ func TestPermissions_SkipsSymlinkedBeadsDir(t *testing.T) {
|
||||
// TestPermissions_SkipsSymlinkedDatabase verifies that chmod is skipped for
|
||||
// symlinked database files, but .beads directory permissions are still fixed.
|
||||
func TestPermissions_SkipsSymlinkedDatabase(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping symlink test on Windows - requires elevated privileges")
|
||||
}
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create real .beads directory with wrong permissions
|
||||
@@ -132,9 +125,6 @@ func TestPermissions_SkipsSymlinkedDatabase(t *testing.T) {
|
||||
// TestPermissions_FixesRegularFiles verifies that permissions ARE fixed for
|
||||
// regular (non-symlinked) files.
|
||||
func TestPermissions_FixesRegularFiles(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping permissions test on Windows - Unix-style permissions don't apply")
|
||||
}
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create .beads directory with wrong permissions
|
||||
|
||||
@@ -7,38 +7,49 @@ import (
|
||||
)
|
||||
|
||||
// GitignoreTemplate is the canonical .beads/.gitignore content
|
||||
// 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
|
||||
*
|
||||
const GitignoreTemplate = `# SQLite databases
|
||||
*.db
|
||||
*.db?*
|
||||
*.db-journal
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
|
||||
# === Files tracked in git (shared across clones) ===
|
||||
# Daemon runtime files
|
||||
daemon.lock
|
||||
daemon.log
|
||||
daemon.pid
|
||||
bd.sock
|
||||
|
||||
# This gitignore file itself
|
||||
!.gitignore
|
||||
# Local version tracking (prevents upgrade notification spam after git ops)
|
||||
.local_version
|
||||
|
||||
# Issue data in JSONL format (the main data file)
|
||||
# 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)
|
||||
!issues.jsonl
|
||||
|
||||
# Repository metadata (database name, JSONL filename)
|
||||
!metadata.json
|
||||
|
||||
# Configuration template (sync branch, integrations)
|
||||
!config.yaml
|
||||
|
||||
# Documentation for contributors
|
||||
!README.md
|
||||
!config.json
|
||||
`
|
||||
|
||||
// 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{
|
||||
"*", // Blanket ignore (whitelist approach)
|
||||
"!.gitignore", // Whitelist the gitignore itself
|
||||
"!issues.jsonl",
|
||||
"!metadata.json",
|
||||
"!config.yaml", // Fixed: was incorrectly !config.json before #473
|
||||
"beads.base.jsonl",
|
||||
"beads.left.jsonl",
|
||||
"beads.right.jsonl",
|
||||
"beads.base.meta.json",
|
||||
"beads.left.meta.json",
|
||||
"beads.right.meta.json",
|
||||
"*.db?*",
|
||||
}
|
||||
|
||||
// CheckGitignore checks if .beads/.gitignore is up to date
|
||||
@@ -69,7 +80,7 @@ func CheckGitignore() DoctorCheck {
|
||||
return DoctorCheck{
|
||||
Name: "Gitignore",
|
||||
Status: "warning",
|
||||
Message: "Outdated .beads/.gitignore (needs whitelist patterns)",
|
||||
Message: "Outdated .beads/.gitignore (missing merge artifact patterns)",
|
||||
Detail: "Missing: " + strings.Join(missing, ", "),
|
||||
Fix: "Run: bd doctor --fix or bd init (safe to re-run)",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user