Skip to main content

git_claw/workflow/
spike.rs

1//! Spike worktree drop and abandonment orchestration.
2
3use crate::cli::output::print_success;
4use crate::infra::git::{resolve_git_common_dir, resolve_toplevel};
5use crate::infra::lock::SlotLock;
6use crate::infra::registry::SlotRegistry;
7use crate::infra::worktree::{delete_branch, remove_worktree};
8use crate::workflow::error::WorkflowError;
9use std::path::{Path, PathBuf};
10
11pub struct DropSpikeOptions<'a> {
12    pub name: &'a str,
13    pub repo_root: Option<&'a Path>,
14}
15
16/// Drops a spike worktree without merging, force-removes the worktree and branch, and frees the slot.
17pub fn drop_spike(options: DropSpikeOptions<'_>) -> Result<(), WorkflowError> {
18    let toplevel = resolve_toplevel(options.repo_root)?;
19    let common_dir = resolve_git_common_dir(Some(&toplevel))?;
20
21    let registry_path = common_dir.join("claw/slots.json");
22    let lock_path = common_dir.join("claw/slots.lock");
23
24    let _lock = SlotLock::acquire(&lock_path)?;
25    let mut registry = SlotRegistry::load_or_empty(&registry_path)?;
26
27    let slot = registry
28        .find_by_name(options.name)
29        .cloned()
30        .ok_or_else(|| WorkflowError::WorktreeNotFound(options.name.to_string()))?;
31
32    let worktree_path = PathBuf::from(&slot.path);
33
34    // Force remove worktree directory
35    let _ = remove_worktree(&toplevel, &worktree_path, true);
36
37    // Force delete the branch without merging (-D)
38    let _ = delete_branch(&toplevel, &slot.branch, true);
39
40    // Release Slot ID in registry
41    registry.remove_by_id(slot.id);
42    registry.save_atomic(&registry_path)?;
43
44    print_success(format!(
45        "Spike worktree '{}' (Slot {}) and branch '{}' dropped successfully without merging",
46        slot.name, slot.id, slot.branch
47    ));
48
49    Ok(())
50}