From 1c8dd49f170efb59d1ecc5c7f5e3067a06d97231 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Sun, 23 Nov 2025 19:56:20 -0800 Subject: [PATCH] fix: Auto-repair stale merge driver configs with invalid placeholders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Old bd versions (<0.24.0) installed merge driver with invalid %L/%R placeholders. Git only supports %O (base), %A (current), %B (other). Changes: - mergeDriverInstalled() now detects %L/%R and returns false to trigger repair - bd init automatically fixes stale configs during initialization - bd doctor --fix also repairs stale configs - Added comprehensive test coverage for auto-repair Fixes: bd-3sz0 Epic: bd-tbz3 (all sub-issues now complete) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cmd/bd/init.go | 10 +++++++- cmd/bd/init_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/cmd/bd/init.go b/cmd/bd/init.go index 38da4b7d..f715f749 100644 --- a/cmd/bd/init.go +++ b/cmd/bd/init.go @@ -752,7 +752,7 @@ exit 0 return nil } -// mergeDriverInstalled checks if bd merge driver is configured +// mergeDriverInstalled checks if bd merge driver is configured correctly func mergeDriverInstalled() bool { // Check git config for merge driver cmd := exec.Command("git", "config", "merge.beads.driver") @@ -761,6 +761,14 @@ func mergeDriverInstalled() bool { return false } + // Check if using old invalid placeholders (%L/%R from versions <0.24.0) + // Git only supports %O (base), %A (current), %B (other) + driverConfig := strings.TrimSpace(string(output)) + if strings.Contains(driverConfig, "%L") || strings.Contains(driverConfig, "%R") { + // Stale config with invalid placeholders - needs repair + return false + } + // Check if .gitattributes has the merge driver configured gitattributesPath := ".gitattributes" content, err := os.ReadFile(gitattributesPath) diff --git a/cmd/bd/init_test.go b/cmd/bd/init_test.go index 3ca7f675..5f3fc2e4 100644 --- a/cmd/bd/init_test.go +++ b/cmd/bd/init_test.go @@ -791,6 +791,63 @@ func TestInitMergeDriverAutoConfiguration(t *testing.T) { t.Errorf("Expected merge.beads.name to contain 'bd', got %q", name) } }) + + t.Run("auto-repair stale merge driver with invalid placeholders", func(t *testing.T) { + // Reset global state + origDBPath := dbPath + defer func() { dbPath = origDBPath }() + dbPath = "" + + tmpDir := t.TempDir() + originalWd, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get working directory: %v", err) + } + defer os.Chdir(originalWd) + + if err := os.Chdir(tmpDir); err != nil { + t.Fatalf("Failed to change to temp directory: %v", err) + } + + // Initialize git repo + if err := runCommandInDir(tmpDir, "git", "init"); err != nil { + t.Fatalf("Failed to init git: %v", err) + } + + // Configure stale merge driver with old invalid placeholders (%L/%R) + // This simulates a user who initialized with bd version <0.24.0 + if err := runCommandInDir(tmpDir, "git", "config", "merge.beads.driver", "bd merge %L %R"); err != nil { + t.Fatalf("Failed to set stale git config: %v", err) + } + + // Create .gitattributes with merge driver + gitattrsPath := filepath.Join(tmpDir, ".gitattributes") + if err := os.WriteFile(gitattrsPath, []byte(".beads/beads.jsonl merge=beads\n"), 0644); err != nil { + t.Fatalf("Failed to create .gitattributes: %v", err) + } + + // Run bd init - should detect stale config and repair it + rootCmd.SetArgs([]string{"init", "--prefix", "test", "--quiet"}) + if err := rootCmd.Execute(); err != nil { + t.Fatalf("Init failed: %v", err) + } + + // Verify merge driver was updated to correct placeholders + driver, err := runCommandInDirWithOutput(tmpDir, "git", "config", "merge.beads.driver") + if err != nil { + t.Fatalf("Failed to get merge.beads.driver: %v", err) + } + driver = strings.TrimSpace(driver) + expected := "bd merge %A %O %A %B" + if driver != expected { + t.Errorf("Expected merge driver to be repaired to %q, got %q", expected, driver) + } + + // Verify it no longer contains invalid placeholders + if strings.Contains(driver, "%L") || strings.Contains(driver, "%R") { + t.Errorf("Merge driver should not contain invalid %%L or %%R placeholders, got %q", driver) + } + }) } // TestReadFirstIssueFromJSONL_ValidFile verifies reading first issue from valid JSONL