Fix sparse checkout leaking to main repo by using git sparse-checkout set

Replace manual git config + file write + read-tree with
`git sparse-checkout set --no-cone` which correctly isolates
sparse checkout config to the worktree without polluting the
shared .git/config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dave
2026-02-23 17:34:45 +00:00
parent 0113d89cbf
commit 3da8e19516

View File

@@ -160,56 +160,31 @@ fn create_worktree_sync(
/// This prevents pipeline file moves (upcoming → current → qa → merge → archived)
/// from being committed on feature branches, which avoids rename/delete merge
/// conflicts when merging back to master.
///
/// Uses `git sparse-checkout set --no-cone` which correctly isolates the
/// config to the worktree without polluting the main checkout's config.
fn configure_sparse_checkout(wt_path: &Path) -> Result<(), String> {
// Enable worktree-specific config so sparse checkout settings don't leak
// to the main checkout or other worktrees.
let _ = Command::new("git")
.args(["config", "extensions.worktreeConfig", "true"])
.current_dir(wt_path)
.output();
// Enable sparse checkout for THIS worktree only (--worktree flag).
// Without --worktree, this writes to the shared .git/config and
// enables sparse checkout on the main checkout too.
// `git sparse-checkout set --no-cone` handles everything:
// - Enables core.sparseCheckout for this worktree only
// - Writes the pattern file to the correct worktree-specific git dir
// - Updates the working tree
//
// The '!' prefix excludes .story_kit/work/ while '/*' includes everything else.
let output = Command::new("git")
.args(["config", "--worktree", "core.sparseCheckout", "true"])
.args([
"sparse-checkout",
"set",
"--no-cone",
"/*",
"!.story_kit/work/",
])
.current_dir(wt_path)
.output()
.map_err(|e| format!("sparse-checkout config: {e}"))?;
.map_err(|e| format!("git sparse-checkout set: {e}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("sparse-checkout config failed: {stderr}"));
}
// Resolve the actual git dir (worktrees use a .git file pointing elsewhere)
let git_dir_output = Command::new("git")
.args(["rev-parse", "--git-dir"])
.current_dir(wt_path)
.output()
.map_err(|e| format!("git rev-parse --git-dir: {e}"))?;
let git_dir = PathBuf::from(
String::from_utf8_lossy(&git_dir_output.stdout).trim().to_string(),
);
// Write sparse-checkout patterns: include everything, exclude .story_kit/work/
let info_dir = git_dir.join("info");
std::fs::create_dir_all(&info_dir)
.map_err(|e| format!("Create sparse-checkout dir: {e}"))?;
std::fs::write(info_dir.join("sparse-checkout"), "/*\n!.story_kit/work/\n")
.map_err(|e| format!("Write sparse-checkout: {e}"))?;
// Re-read the working tree to apply sparse checkout rules
let output = Command::new("git")
.args(["read-tree", "-mu", "HEAD"])
.current_dir(wt_path)
.output()
.map_err(|e| format!("git read-tree: {e}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("git read-tree failed: {stderr}"));
return Err(format!("git sparse-checkout set failed: {stderr}"));
}
Ok(())