fix: bd sync fails with exit 128 in bare repo worktrees (GH#827)

Two fixes for bare repo worktree setups:

1. fork_protection.go: Use git.GetGitDir() instead of hardcoding .git
   In worktrees, .git is a file containing gitdir path, not a directory.
   Using GetGitDir() handles this correctly.

2. sync_git.go: Simplify gitHasBeadsChanges to use absolute paths
   The previous code used git -C main-repo-root status, but in bare
   repo worktrees GetMainRepoRoot() returns the parent of the bare repo
   which is not a valid working tree. Using absolute paths without -C
   lets git find the repo from cwd, which always works.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
beads/crew/emma
2026-01-01 11:04:48 -08:00
committed by Steve Yegge
parent 95f14fa827
commit 363c5a3819
2 changed files with 15 additions and 21 deletions

View File

@@ -43,8 +43,15 @@ func ensureForkProtection() {
return // Not a fork of beads, user's own project
}
// Get actual git directory (handles worktrees where .git is a file) (GH#827)
gitDir, err := git.GetGitDir()
if err != nil {
debug.Printf("fork protection: failed to get git dir: %v", err)
return
}
// Check if already excluded
excludePath := filepath.Join(gitRoot, ".git", "info", "exclude")
excludePath := filepath.Join(gitDir, "info", "exclude")
if isAlreadyExcluded(excludePath) {
return
}

View File

@@ -89,6 +89,7 @@ func getRepoRootForWorktree(_ context.Context) string {
}
// gitHasBeadsChanges checks if any tracked files in .beads/ have uncommitted changes
// This function is worktree-aware and handles bare repo worktree setups (GH#827).
func gitHasBeadsChanges(ctx context.Context) (bool, error) {
// Get the absolute path to .beads directory
beadsDir := beads.FindBeadsDir()
@@ -96,26 +97,12 @@ func gitHasBeadsChanges(ctx context.Context) (bool, error) {
return false, fmt.Errorf("no .beads directory found")
}
// Get the repository root (handles worktrees properly)
repoRoot := getRepoRootForWorktree(ctx)
if repoRoot == "" {
return false, fmt.Errorf("cannot determine repository root")
}
// Compute relative path from repo root to .beads
relPath, err := filepath.Rel(repoRoot, beadsDir)
if err != nil {
// Fall back to absolute path if relative path fails
statusCmd := exec.CommandContext(ctx, "git", "status", "--porcelain", beadsDir) //nolint:gosec // G204: beadsDir from beads.FindBeadsDir()
statusOutput, err := statusCmd.Output()
if err != nil {
return false, fmt.Errorf("git status failed: %w", err)
}
return len(strings.TrimSpace(string(statusOutput))) > 0, nil
}
// Run git status with relative path from repo root
statusCmd := exec.CommandContext(ctx, "git", "-C", repoRoot, "status", "--porcelain", relPath) //nolint:gosec // G204: paths from internal git helpers
// Run git status with absolute path from current directory.
// This is more robust than using -C with a repo root, because:
// 1. In bare repo worktree setups, GetMainRepoRoot() returns the parent
// of the bare repo, which isn't a valid working tree (GH#827)
// 2. Git will find the repository from cwd, which is always valid
statusCmd := exec.CommandContext(ctx, "git", "status", "--porcelain", beadsDir) //nolint:gosec // G204: beadsDir from beads.FindBeadsDir()
statusOutput, err := statusCmd.Output()
if err != nil {
return false, fmt.Errorf("git status failed: %w", err)