fix(worktree): disable sparse checkout on main repo after worktree creation (GH#886)

Git 2.38+ enables core.sparseCheckout on the main repo as a side effect
of worktree creation, causing confusing git status message:
"You are in a sparse checkout with 100% of tracked files present."

The fix explicitly disables sparse checkout on the main repo after
creating the beads worktree. This doesn't affect the worktree's sparse
checkout functionality since the patterns are already applied during
checkout.

🤖 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/grip
2026-01-04 11:24:12 -08:00
committed by Steve Yegge
parent b3d64d47b3
commit df66ecfe9e
2 changed files with 60 additions and 0 deletions

View File

@@ -94,6 +94,13 @@ func (wm *WorktreeManager) CreateBeadsWorktree(branch, worktreePath string) erro
return fmt.Errorf("failed to checkout branch in worktree: %w\nOutput: %s", err, string(output))
}
// GH#886: Git 2.38+ enables sparse checkout on the main repo as a side effect
// of worktree creation. Explicitly disable it to prevent confusing git status
// message: "You are in a sparse checkout with 100% of tracked files present."
disableSparseCmd := exec.Command("git", "config", "core.sparseCheckout", "false")
disableSparseCmd.Dir = wm.repoPath
_ = disableSparseCmd.Run() // Best effort - don't fail if this doesn't work
return nil
}

View File

@@ -1179,6 +1179,59 @@ func TestCreateBeadsWorktree_MissingButRegistered(t *testing.T) {
}
}
// TestCreateBeadsWorktree_MainRepoSparseCheckoutDisabled tests that creating a worktree
// does not leave core.sparseCheckout enabled on the main repo (GH#886).
// Git 2.38+ enables sparse checkout on the main repo as a side effect of worktree creation,
// which causes confusing "You are in a sparse checkout with 100% of tracked files present"
// message in git status.
func TestCreateBeadsWorktree_MainRepoSparseCheckoutDisabled(t *testing.T) {
repoPath, cleanup := setupTestRepo(t)
defer cleanup()
wm := NewWorktreeManager(repoPath)
worktreePath := filepath.Join(t.TempDir(), "beads-worktree-gh886")
// Verify sparse checkout is not enabled before worktree creation
cmd := exec.Command("git", "config", "--get", "core.sparseCheckout")
cmd.Dir = repoPath
output, _ := cmd.Output()
initialValue := strings.TrimSpace(string(output))
// Empty or "false" are both acceptable initial states
if initialValue == "true" {
t.Log("Note: sparse checkout was already enabled before test")
}
// Create worktree
if err := wm.CreateBeadsWorktree("beads-gh886", worktreePath); err != nil {
t.Fatalf("CreateBeadsWorktree failed: %v", err)
}
// Verify sparse checkout is disabled on main repo after worktree creation
cmd = exec.Command("git", "config", "--get", "core.sparseCheckout")
cmd.Dir = repoPath
output, _ = cmd.Output()
finalValue := strings.TrimSpace(string(output))
// Should be either empty (unset) or "false"
if finalValue == "true" {
t.Errorf("GH#886: Main repo has core.sparseCheckout=true after worktree creation. "+
"This causes confusing git status message. Value should be 'false' or unset, got: %q", finalValue)
}
// Verify that sparse checkout functionality STILL WORKS in the worktree
// (the patterns were applied during checkout, before we disabled the config)
// Check that .beads exists but other.txt does not
beadsDir := filepath.Join(worktreePath, ".beads")
if _, err := os.Stat(beadsDir); os.IsNotExist(err) {
t.Error(".beads directory should exist in worktree (sparse checkout should include it)")
}
otherFile := filepath.Join(worktreePath, "other.txt")
if _, err := os.Stat(otherFile); err == nil {
t.Error("other.txt should NOT exist in worktree (sparse checkout should exclude it)")
}
}
// TestNormalizeBeadsRelPath tests path normalization for bare repo worktrees (GH#785, GH#810)
func TestNormalizeBeadsRelPath(t *testing.T) {
tests := []struct {