fix(sync): restore .beads/ from branch after sync when using sync.branch

When sync.branch is configured, the main branch's .beads/ directory was
showing as modified after every sync, even though the data was correctly
synced to the sync branch worktree.

This happened because:
1. Export writes to main's .beads/
2. Files are copied to worktree and committed there
3. But main's .beads/ now differs from what's committed on main

Fix: After sync completes, restore .beads/ from HEAD to keep the working
directory clean. The actual beads data lives on the sync branch; the main
branch's .beads/ is just a snapshot that should match what's committed.
This commit is contained in:
Steve Yegge
2025-12-01 20:43:35 -08:00
parent d91f92802a
commit 9483ab053e

View File

@@ -607,6 +607,17 @@ Use --merge to merge the sync branch back to main branch.`,
fmt.Fprintf(os.Stderr, "Warning: auto-compact deletions failed: %v\n", err)
}
// When using sync.branch, restore .beads/ from current branch to keep
// working directory clean. The actual beads data lives on the sync branch,
// and the main branch's .beads/ should match what's committed there.
// This prevents "modified .beads/" showing in git status after sync.
if useSyncBranch {
if err := restoreBeadsDirFromBranch(ctx); err != nil {
// Non-fatal - just means git status will show modified files
debug.Logf("sync: failed to restore .beads/ from branch: %v", err)
}
}
fmt.Println("\n✓ Sync complete")
}
},
@@ -926,6 +937,25 @@ func gitPush(ctx context.Context) error {
return nil
}
// restoreBeadsDirFromBranch restores .beads/ directory from the current branch's committed state.
// This is used after sync when sync.branch is configured to keep the working directory clean.
// The actual beads data lives on the sync branch; the main branch's .beads/ is just a snapshot.
func restoreBeadsDirFromBranch(ctx context.Context) error {
beadsDir := findBeadsDir()
if beadsDir == "" {
return fmt.Errorf("no .beads directory found")
}
// Restore .beads/ from HEAD (current branch's committed state)
// Using -- to ensure .beads/ is treated as a path, not a branch name
cmd := exec.CommandContext(ctx, "git", "checkout", "HEAD", "--", beadsDir)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("git checkout failed: %w\n%s", err, output)
}
return nil
}
// getDefaultBranch returns the default branch name (main or master) for origin remote
// Checks remote HEAD first, then falls back to checking if main/master exist
func getDefaultBranch(ctx context.Context) string {