diff --git a/crates/blue-core/src/repo.rs b/crates/blue-core/src/repo.rs index 7f6745c..bd12f9c 100644 --- a/crates/blue-core/src/repo.rs +++ b/crates/blue-core/src/repo.rs @@ -346,6 +346,13 @@ pub fn create_worktree( branch_name: &str, worktree_path: &Path, ) -> Result<(), RepoError> { + // Derive worktree name from path (directory name = slug, no slashes) + // Git worktree names are stored in .git/worktrees/ and cannot contain slashes + let worktree_name = worktree_path + .file_name() + .and_then(|n| n.to_str()) + .ok_or_else(|| RepoError::Git(git2::Error::from_str("Invalid worktree path")))?; + // Create the branch if it doesn't exist let head = repo.head()?; let head_commit = head.peel_to_commit()?; @@ -358,7 +365,7 @@ pub fn create_worktree( // Create the worktree let reference = branch.into_reference(); repo.worktree( - branch_name, + worktree_name, worktree_path, Some(git2::WorktreeAddOptions::new().reference(Some(&reference))), )?; @@ -366,9 +373,17 @@ pub fn create_worktree( Ok(()) } -/// Remove a worktree -pub fn remove_worktree(repo: &git2::Repository, name: &str) -> Result<(), RepoError> { - let worktree = repo.find_worktree(name)?; +/// Remove a worktree by path +/// +/// Derives the worktree name from the path's directory name. +pub fn remove_worktree(repo: &git2::Repository, worktree_path: &Path) -> Result<(), RepoError> { + // Derive worktree name from path (same as create_worktree) + let worktree_name = worktree_path + .file_name() + .and_then(|n| n.to_str()) + .ok_or_else(|| RepoError::Git(git2::Error::from_str("Invalid worktree path")))?; + + let worktree = repo.find_worktree(worktree_name)?; // Prune the worktree (this removes the worktree but keeps the branch) worktree.prune(Some( diff --git a/crates/blue-mcp/src/handlers/realm.rs b/crates/blue-mcp/src/handlers/realm.rs index 14ac9de..83e41f8 100644 --- a/crates/blue-mcp/src/handlers/realm.rs +++ b/crates/blue-mcp/src/handlers/realm.rs @@ -701,6 +701,13 @@ fn create_git_worktree( return Err("Worktree path already exists".to_string()); } + // Derive worktree name from path (directory name = slug, no slashes) + // Git worktree names are stored in .git/worktrees/ and cannot contain slashes + let worktree_name = worktree_path + .file_name() + .and_then(|n| n.to_str()) + .ok_or("Invalid worktree path")?; + // Get HEAD commit to branch from let head = repo.head().map_err(|e| format!("Failed to get HEAD: {}", e))?; let commit = head.peel_to_commit().map_err(|e| format!("Failed to get commit: {}", e))?; @@ -720,7 +727,7 @@ fn create_git_worktree( // Create the worktree repo.worktree( - branch_name, + worktree_name, worktree_path, Some(git2::WorktreeAddOptions::new().reference(Some(&reference))), ) diff --git a/crates/blue-mcp/src/handlers/worktree.rs b/crates/blue-mcp/src/handlers/worktree.rs index be72c60..e89faab 100644 --- a/crates/blue-mcp/src/handlers/worktree.rs +++ b/crates/blue-mcp/src/handlers/worktree.rs @@ -380,8 +380,9 @@ pub fn handle_cleanup(state: &ProjectState, args: &Value) -> Result Result