diff --git a/cmd/bd/doctor.go b/cmd/bd/doctor.go index 3ce7f3c1..09bd2c80 100644 --- a/cmd/bd/doctor.go +++ b/cmd/bd/doctor.go @@ -281,6 +281,13 @@ func runDiagnostics(path string) doctorResult { result.OverallOK = false } + // Check git hooks Dolt compatibility (hooks without Dolt check cause errors) + doltHooksCheck := convertWithCategory(doctor.CheckGitHooksDoltCompatibility(path), doctor.CategoryGit) + result.Checks = append(result.Checks, doltHooksCheck) + if doltHooksCheck.Status == statusError { + result.OverallOK = false + } + // If no .beads/, skip remaining checks if installCheck.Status != statusOK { return result diff --git a/cmd/bd/doctor/git.go b/cmd/bd/doctor/git.go index 1771ec20..756bb023 100644 --- a/cmd/bd/doctor/git.go +++ b/cmd/bd/doctor/git.go @@ -13,6 +13,7 @@ import ( _ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" "github.com/steveyegge/beads/cmd/bd/doctor/fix" + "github.com/steveyegge/beads/internal/configfile" "github.com/steveyegge/beads/internal/git" "github.com/steveyegge/beads/internal/storage" "github.com/steveyegge/beads/internal/syncbranch" @@ -760,6 +761,74 @@ func CheckSyncBranchHealth(path string) DoctorCheck { } } +// CheckGitHooksDoltCompatibility checks if installed git hooks are compatible with Dolt backend. +// Hooks installed before Dolt support was added don't have the backend check and will +// fail with confusing errors on git pull/commit. +func CheckGitHooksDoltCompatibility(path string) DoctorCheck { + backend, beadsDir := getBackendAndBeadsDir(path) + + // Only relevant for Dolt backend + if backend != configfile.BackendDolt { + return DoctorCheck{ + Name: "Git Hooks Dolt Compatibility", + Status: StatusOK, + Message: "N/A (not using Dolt backend)", + } + } + + // Check if we're in a git repository + hooksDir, err := git.GetGitHooksDir() + if err != nil { + return DoctorCheck{ + Name: "Git Hooks Dolt Compatibility", + Status: StatusOK, + Message: "N/A (not a git repository)", + } + } + + // Check post-merge hook (most likely to cause issues with Dolt) + postMergePath := filepath.Join(hooksDir, "post-merge") + content, err := os.ReadFile(postMergePath) + if err != nil { + // No hook installed - that's fine + return DoctorCheck{ + Name: "Git Hooks Dolt Compatibility", + Status: StatusOK, + Message: "N/A (no post-merge hook installed)", + } + } + + contentStr := string(content) + + // Check if it's a bd hook + if !strings.Contains(contentStr, bdInlineHookMarker) && !strings.Contains(contentStr, "bd") { + return DoctorCheck{ + Name: "Git Hooks Dolt Compatibility", + Status: StatusOK, + Message: "N/A (not a bd hook)", + } + } + + // Check if it has the Dolt backend skip logic + if strings.Contains(contentStr, `"backend"`) && strings.Contains(contentStr, `"dolt"`) { + return DoctorCheck{ + Name: "Git Hooks Dolt Compatibility", + Status: StatusOK, + Message: "Hooks have Dolt backend check", + } + } + + // Hook exists but lacks Dolt check - this will cause errors + _ = beadsDir // silence unused warning + return DoctorCheck{ + Name: "Git Hooks Dolt Compatibility", + Status: StatusError, + Message: "Git hooks incompatible with Dolt backend", + Detail: "Installed hooks attempt JSONL sync which fails with Dolt. This causes errors on git pull/commit.", + Fix: "Run 'bd hooks install --force' to update hooks for Dolt compatibility", + } +} + // FixGitHooks fixes missing or broken git hooks by calling bd hooks install. func FixGitHooks(path string) error { return fix.GitHooks(path) diff --git a/cmd/bd/migrate_dolt.go b/cmd/bd/migrate_dolt.go index b4409d2b..834c208f 100644 --- a/cmd/bd/migrate_dolt.go +++ b/cmd/bd/migrate_dolt.go @@ -146,10 +146,44 @@ func handleToDoltMigration(dryRun bool, autoYes bool) { printSuccess("Updated metadata.json to use Dolt backend") + // Check if git hooks need updating for Dolt compatibility + if hooksNeedDoltUpdate(beadsDir) { + printWarning("Git hooks need updating for Dolt backend") + if !jsonOutput { + fmt.Println(" The pre-commit and post-merge hooks use JSONL sync which doesn't apply to Dolt.") + fmt.Println(" Run 'bd hooks install --force' to update them.") + } + } + // Final status printFinalStatus("dolt", imported, skipped, backupPath, doltPath, sqlitePath, true) } +// hooksNeedDoltUpdate checks if installed git hooks lack the Dolt backend skip logic. +func hooksNeedDoltUpdate(beadsDir string) bool { + // Find git hooks directory + repoRoot := filepath.Dir(beadsDir) + hooksDir := filepath.Join(repoRoot, ".git", "hooks") + + // Check post-merge hook (most likely to cause issues) + postMergePath := filepath.Join(hooksDir, "post-merge") + content, err := os.ReadFile(postMergePath) + if err != nil { + return false // No hook installed + } + + // Check if it's a bd hook and lacks the Dolt skip logic + contentStr := string(content) + if !strings.Contains(contentStr, "bd") { + return false // Not a bd hook + } + if strings.Contains(contentStr, `"backend"`) && strings.Contains(contentStr, `"dolt"`) { + return false // Already has Dolt check + } + + return true // bd hook without Dolt check +} + // handleToSQLiteMigration migrates from Dolt to SQLite backend (escape hatch). func handleToSQLiteMigration(dryRun bool, autoYes bool) { ctx := context.Background()