Implement auto-resolution of JSONL merge conflicts during bd sync
- Add isInRebase() to detect rebase state - Add hasJSONLConflict() to check for JSONL-only conflicts - Add runGitRebaseContinue() to continue rebase after resolution - Auto-export from DB and resolve conflict when detected - Add comprehensive tests for auto-resolution logic Implements bd-cwmt
This commit is contained in:
@@ -441,3 +441,169 @@ func TestGetSyncBranch_EnvOverridesDB(t *testing.T) {
|
||||
t.Errorf("getSyncBranch() = %q, want %q (env override)", branch, "env-branch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInRebase_NotInRebase(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
originalWd, _ := os.Getwd()
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
// Create a git repo
|
||||
os.Chdir(tmpDir)
|
||||
exec.Command("git", "init").Run()
|
||||
exec.Command("git", "config", "user.email", "test@test.com").Run()
|
||||
exec.Command("git", "config", "user.name", "Test User").Run()
|
||||
|
||||
// Create initial commit
|
||||
os.WriteFile("test.txt", []byte("test"), 0644)
|
||||
exec.Command("git", "add", "test.txt").Run()
|
||||
exec.Command("git", "commit", "-m", "initial").Run()
|
||||
|
||||
// Should not be in rebase
|
||||
if isInRebase() {
|
||||
t.Error("expected false when not in rebase")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInRebase_InRebase(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
originalWd, _ := os.Getwd()
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
// Create a git repo
|
||||
os.Chdir(tmpDir)
|
||||
exec.Command("git", "init").Run()
|
||||
exec.Command("git", "config", "user.email", "test@test.com").Run()
|
||||
exec.Command("git", "config", "user.name", "Test User").Run()
|
||||
|
||||
// Create initial commit
|
||||
os.WriteFile("test.txt", []byte("test"), 0644)
|
||||
exec.Command("git", "add", "test.txt").Run()
|
||||
exec.Command("git", "commit", "-m", "initial").Run()
|
||||
|
||||
// Simulate rebase by creating rebase-merge directory
|
||||
os.MkdirAll(filepath.Join(tmpDir, ".git", "rebase-merge"), 0755)
|
||||
|
||||
// Should detect rebase
|
||||
if !isInRebase() {
|
||||
t.Error("expected true when .git/rebase-merge exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInRebase_InRebaseApply(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
originalWd, _ := os.Getwd()
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
// Create a git repo
|
||||
os.Chdir(tmpDir)
|
||||
exec.Command("git", "init").Run()
|
||||
|
||||
// Simulate non-interactive rebase by creating rebase-apply directory
|
||||
os.MkdirAll(filepath.Join(tmpDir, ".git", "rebase-apply"), 0755)
|
||||
|
||||
// Should detect rebase
|
||||
if !isInRebase() {
|
||||
t.Error("expected true when .git/rebase-apply exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasJSONLConflict_NoConflict(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
originalWd, _ := os.Getwd()
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
// Create a git repo
|
||||
os.Chdir(tmpDir)
|
||||
exec.Command("git", "init").Run()
|
||||
exec.Command("git", "config", "user.email", "test@test.com").Run()
|
||||
exec.Command("git", "config", "user.name", "Test User").Run()
|
||||
|
||||
// Create initial commit
|
||||
os.WriteFile("test.txt", []byte("test"), 0644)
|
||||
exec.Command("git", "add", "test.txt").Run()
|
||||
exec.Command("git", "commit", "-m", "initial").Run()
|
||||
|
||||
// Should not have JSONL conflict
|
||||
if hasJSONLConflict() {
|
||||
t.Error("expected false when no conflicts")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasJSONLConflict_OnlyJSONLConflict(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
originalWd, _ := os.Getwd()
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
// Create a git repo
|
||||
os.Chdir(tmpDir)
|
||||
exec.Command("git", "init", "-b", "main").Run()
|
||||
exec.Command("git", "config", "user.email", "test@test.com").Run()
|
||||
exec.Command("git", "config", "user.name", "Test User").Run()
|
||||
|
||||
// Create initial commit
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
os.MkdirAll(beadsDir, 0755)
|
||||
os.WriteFile(filepath.Join(beadsDir, "beads.jsonl"), []byte(`{"id":"bd-1","title":"original"}`), 0644)
|
||||
exec.Command("git", "add", ".").Run()
|
||||
exec.Command("git", "commit", "-m", "initial").Run()
|
||||
|
||||
// Create a second commit on main (modify same issue)
|
||||
os.WriteFile(filepath.Join(beadsDir, "beads.jsonl"), []byte(`{"id":"bd-1","title":"main-version"}`), 0644)
|
||||
exec.Command("git", "add", ".").Run()
|
||||
exec.Command("git", "commit", "-m", "main change").Run()
|
||||
|
||||
// Create a branch from the first commit
|
||||
exec.Command("git", "checkout", "-b", "feature", "HEAD~1").Run()
|
||||
os.WriteFile(filepath.Join(beadsDir, "beads.jsonl"), []byte(`{"id":"bd-1","title":"feature-version"}`), 0644)
|
||||
exec.Command("git", "add", ".").Run()
|
||||
exec.Command("git", "commit", "-m", "feature change").Run()
|
||||
|
||||
// Attempt rebase onto main (will conflict)
|
||||
exec.Command("git", "rebase", "main").Run()
|
||||
|
||||
// Should detect JSONL conflict during rebase
|
||||
if !hasJSONLConflict() {
|
||||
t.Error("expected true when only beads.jsonl has conflict during rebase")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasJSONLConflict_MultipleConflicts(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
originalWd, _ := os.Getwd()
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
// Create a git repo
|
||||
os.Chdir(tmpDir)
|
||||
exec.Command("git", "init", "-b", "main").Run()
|
||||
exec.Command("git", "config", "user.email", "test@test.com").Run()
|
||||
exec.Command("git", "config", "user.name", "Test User").Run()
|
||||
|
||||
// Create initial commit with beads.jsonl and another file
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
os.MkdirAll(beadsDir, 0755)
|
||||
os.WriteFile(filepath.Join(beadsDir, "beads.jsonl"), []byte(`{"id":"bd-1","title":"original"}`), 0644)
|
||||
os.WriteFile("other.txt", []byte("line1\nline2\nline3"), 0644)
|
||||
exec.Command("git", "add", ".").Run()
|
||||
exec.Command("git", "commit", "-m", "initial").Run()
|
||||
|
||||
// Create a second commit on main (modify both files)
|
||||
os.WriteFile(filepath.Join(beadsDir, "beads.jsonl"), []byte(`{"id":"bd-1","title":"main-version"}`), 0644)
|
||||
os.WriteFile("other.txt", []byte("line1\nmain-version\nline3"), 0644)
|
||||
exec.Command("git", "add", ".").Run()
|
||||
exec.Command("git", "commit", "-m", "main change").Run()
|
||||
|
||||
// Create a branch from the first commit
|
||||
exec.Command("git", "checkout", "-b", "feature", "HEAD~1").Run()
|
||||
os.WriteFile(filepath.Join(beadsDir, "beads.jsonl"), []byte(`{"id":"bd-1","title":"feature-version"}`), 0644)
|
||||
os.WriteFile("other.txt", []byte("line1\nfeature-version\nline3"), 0644)
|
||||
exec.Command("git", "add", ".").Run()
|
||||
exec.Command("git", "commit", "-m", "feature change").Run()
|
||||
|
||||
// Attempt rebase (will conflict on both files)
|
||||
exec.Command("git", "rebase", "main").Run()
|
||||
|
||||
// Should NOT auto-resolve when multiple files conflict
|
||||
if hasJSONLConflict() {
|
||||
t.Error("expected false when multiple files have conflicts (should not auto-resolve)")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user