diff --git a/server/src/worktree.rs b/server/src/worktree.rs index 3dc024e..e2b817d 100644 --- a/server/src/worktree.rs +++ b/server/src/worktree.rs @@ -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(())