fix: Correct git merge driver placeholders from %L/%R to %A/%B

Git merge drivers only support three placeholders:
- %O (ancestor/base)
- %A (current version)
- %B (other branch's version)

The code was incorrectly using %L and %R, which don't exist in git,
causing them to be passed through literally and breaking JSONL merges.

Changes:
- Fixed merge driver config in init.go, merge.go, README.md, docs
- Added detection in bd doctor with clear error messages
- Added auto-fix in bd doctor --fix
- Added proactive warning in bd sync before git pull
- Added reactive error detection after merge failures
- Updated all tests to use correct placeholders

Now users get helpful guidance at every step:
1. bd doctor detects the issue
2. bd doctor --fix auto-corrects it
3. bd sync warns before pulling if misconfigured
4. Error messages suggest bd doctor --fix when merge fails

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-11-21 20:40:18 -05:00
parent ce433bb2e8
commit ddd209e26a
9 changed files with 247 additions and 121 deletions

View File

@@ -8,6 +8,7 @@ import (
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
@@ -192,6 +193,8 @@ func applyFixes(result doctorResult) {
err = fix.DatabaseVersion(result.Path)
case "Schema Compatibility":
err = fix.SchemaCompatibility(result.Path)
case "Git Merge Driver":
err = fix.MergeDriver(result.Path)
default:
fmt.Printf(" ⚠ No automatic fix available for %s\n", check.Name)
fmt.Printf(" Manual fix: %s\n", check.Fix)
@@ -327,6 +330,11 @@ func runDiagnostics(path string) doctorResult {
result.Checks = append(result.Checks, gitignoreCheck)
// Don't fail overall check for gitignore, just warn
// Check 15: Git merge driver configuration
mergeDriverCheck := checkMergeDriver(path)
result.Checks = append(result.Checks, mergeDriverCheck)
// Don't fail overall check for merge driver, just warn
return result
}
@@ -1513,6 +1521,64 @@ func checkSchemaCompatibility(path string) doctorCheck {
}
}
func checkMergeDriver(path string) doctorCheck {
// Check if we're in a git repository
gitDir := filepath.Join(path, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
return doctorCheck{
Name: "Git Merge Driver",
Status: statusOK,
Message: "N/A (not a git repository)",
}
}
// Get current merge driver configuration
cmd := exec.Command("git", "config", "merge.beads.driver")
cmd.Dir = path
output, err := cmd.Output()
if err != nil {
// Merge driver not configured
return doctorCheck{
Name: "Git Merge Driver",
Status: statusWarning,
Message: "Git merge driver not configured",
Fix: "Run 'bd init' to configure the merge driver, or manually: git config merge.beads.driver \"bd merge %A %O %A %B\"",
}
}
currentConfig := strings.TrimSpace(string(output))
correctConfig := "bd merge %A %O %A %B"
// Check if using old incorrect placeholders
if strings.Contains(currentConfig, "%L") || strings.Contains(currentConfig, "%R") {
return doctorCheck{
Name: "Git Merge Driver",
Status: statusError,
Message: fmt.Sprintf("Incorrect merge driver config: %q (uses invalid %%L/%%R placeholders)", currentConfig),
Detail: "Git only supports %O (base), %A (current), %B (other). Using %L/%R causes merge failures.",
Fix: "Run 'bd doctor --fix' to update to correct config, or manually: git config merge.beads.driver \"bd merge %A %O %A %B\"",
}
}
// Check if config is correct
if currentConfig != correctConfig {
return doctorCheck{
Name: "Git Merge Driver",
Status: statusWarning,
Message: fmt.Sprintf("Non-standard merge driver config: %q", currentConfig),
Detail: fmt.Sprintf("Expected: %q", correctConfig),
Fix: fmt.Sprintf("Run 'bd doctor --fix' to update config, or manually: git config merge.beads.driver \"%s\"", correctConfig),
}
}
return doctorCheck{
Name: "Git Merge Driver",
Status: statusOK,
Message: "Correctly configured",
Detail: currentConfig,
}
}
func init() {
rootCmd.AddCommand(doctorCmd)
doctorCmd.Flags().BoolVar(&perfMode, "perf", false, "Run performance diagnostics and generate CPU profile")