fix: bd sync fails in bare repo worktrees (#785)

The bug: In a bare repo + worktrees setup, jsonlRelPath was calculated
relative to the project root (which contains all worktrees), resulting in
paths like "main/.beads/issues.jsonl". But the sync branch worktree uses
sparse checkout for .beads/*, so files are at ".beads/issues.jsonl".

This caused copyJSONLToMainRepo to look in the wrong location, silently
returning when the file was not found.

Fix: Add normalizeBeadsRelPath() to strip leading path components before
".beads", ensuring correct path resolution in both directions:
- copyJSONLToMainRepo (worktree -> local)
- SyncJSONLToWorktreeWithOptions (local -> worktree)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-29 15:39:07 -08:00
parent 1256352d49
commit 0fa97a4c30
3 changed files with 93 additions and 3 deletions

View File

@@ -181,11 +181,15 @@ func (wm *WorktreeManager) SyncJSONLToWorktree(worktreePath, jsonlRelPath string
// If ForceOverwrite is false (default), the function uses merge logic to prevent
// data loss when a fresh clone syncs with fewer issues than the remote.
func (wm *WorktreeManager) SyncJSONLToWorktreeWithOptions(worktreePath, jsonlRelPath string, opts SyncOptions) error {
// Source: main repo JSONL
// Source: main repo JSONL (use the full path as provided)
srcPath := filepath.Join(wm.repoPath, jsonlRelPath)
// Destination: worktree JSONL
dstPath := filepath.Join(worktreePath, jsonlRelPath)
// GH#785: Handle bare repo worktrees where jsonlRelPath might include the
// worktree name (e.g., "main/.beads/issues.jsonl"). The sync branch uses
// sparse checkout for .beads/* so we normalize to strip leading components.
normalizedRelPath := normalizeBeadsRelPath(jsonlRelPath)
dstPath := filepath.Join(worktreePath, normalizedRelPath)
// Ensure destination directory exists
dstDir := filepath.Dir(dstPath)
@@ -317,6 +321,20 @@ func (wm *WorktreeManager) mergeJSONLFiles(srcData, dstData []byte) ([]byte, err
}
// normalizeBeadsRelPath strips any leading path components before .beads.
// This handles bare repo worktrees where the relative path includes the worktree
// name (e.g., "main/.beads/issues.jsonl" -> ".beads/issues.jsonl").
// GH#785: Fix for sync failing across worktrees in bare repo setup.
func normalizeBeadsRelPath(relPath string) string {
// Use filepath.ToSlash for consistent handling across platforms
normalized := filepath.ToSlash(relPath)
if idx := strings.Index(normalized, ".beads"); idx > 0 {
// Strip leading path components before .beads
return filepath.FromSlash(normalized[idx:])
}
return relPath
}
// isValidWorktree checks if the path is a valid git worktree
func (wm *WorktreeManager) isValidWorktree(worktreePath string) (bool, error) {
cmd := exec.Command("git", "worktree", "list", "--porcelain")