From df66ecfe9e1f624e239ed8f751599bbfbaa0ede2 Mon Sep 17 00:00:00 2001 From: beads/crew/grip Date: Sun, 4 Jan 2026 11:24:12 -0800 Subject: [PATCH] fix(worktree): disable sparse checkout on main repo after worktree creation (GH#886) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- internal/git/worktree.go | 7 +++++ internal/git/worktree_test.go | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/internal/git/worktree.go b/internal/git/worktree.go index d5f0164c..1cf2ee09 100644 --- a/internal/git/worktree.go +++ b/internal/git/worktree.go @@ -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 } diff --git a/internal/git/worktree_test.go b/internal/git/worktree_test.go index 39c6ba2f..6172e9e7 100644 --- a/internal/git/worktree_test.go +++ b/internal/git/worktree_test.go @@ -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 {