use std::collections::HashMap;
use console::style;
use crate::error::{CwError, Result};
use crate::git;
use crate::messages;
pub fn stash_save(message: Option<&str>) -> Result<()> {
let cwd = std::env::current_dir()?;
let branch = git::get_current_branch(Some(&cwd))?;
let stash_msg = match message {
Some(m) => format!("[{}] {}", branch, m),
None => format!("[{}] WIP", branch),
};
let status = git::git_command(&["status", "--porcelain"], Some(&cwd), false, true)?;
if status.returncode == 0 && status.stdout.trim().is_empty() {
println!("{} No changes to stash\n", style("!").yellow());
return Ok(());
}
println!(
"{}",
style(format!("Stashing changes in {}...", branch)).yellow()
);
git::git_command(
&["stash", "push", "--include-untracked", "-m", &stash_msg],
Some(&cwd),
true,
false,
)?;
println!(
"{} Stashed changes: {}\n",
style("*").green().bold(),
stash_msg
);
Ok(())
}
pub fn stash_list() -> Result<()> {
let repo = git::get_repo_root(None)?;
let result = git::git_command(&["stash", "list"], Some(&repo), false, true)?;
if result.stdout.trim().is_empty() {
println!("{}\n", style("No stashes found").yellow());
return Ok(());
}
println!("\n{}\n", style("Stashes by worktree:").cyan().bold());
let mut by_branch: HashMap<String, Vec<(String, String)>> = HashMap::new();
for line in result.stdout.trim().lines() {
let parts: Vec<&str> = line.splitn(3, ':').collect();
if parts.len() < 3 {
continue;
}
let stash_ref = parts[0].trim();
let stash_info = parts[1].trim();
let stash_msg = parts[2].trim();
let branch_name = if stash_msg.starts_with('[') {
if let Some(end) = stash_msg.find(']') {
stash_msg[1..end].to_string()
} else {
"unknown".to_string()
}
} else if stash_info.contains("On ") {
stash_info
.split("On ")
.nth(1)
.unwrap_or("unknown")
.trim()
.to_string()
} else if stash_info.contains("WIP on ") {
stash_info
.split("WIP on ")
.nth(1)
.unwrap_or("unknown")
.trim()
.to_string()
} else {
"unknown".to_string()
};
by_branch
.entry(branch_name)
.or_default()
.push((stash_ref.to_string(), stash_msg.to_string()));
}
let mut branches: Vec<_> = by_branch.keys().cloned().collect();
branches.sort();
for branch in branches {
println!("{}:", style(&branch).green().bold());
for (stash_ref, msg) in &by_branch[&branch] {
println!(" {}: {}", stash_ref, msg);
}
println!();
}
Ok(())
}
pub fn stash_apply(target_branch: &str, stash_ref: &str) -> Result<()> {
let repo = git::get_repo_root(None)?;
let wt_path = git::find_worktree_by_branch(&repo, target_branch)?
.or(git::find_worktree_by_branch(
&repo,
&format!("refs/heads/{}", target_branch),
)?)
.ok_or_else(|| CwError::WorktreeNotFound(messages::worktree_not_found(target_branch)))?;
let verify = git::git_command(&["stash", "list"], Some(&repo), false, true)?;
if !verify.stdout.contains(stash_ref) {
return Err(CwError::Git(messages::stash_not_found(stash_ref)));
}
println!(
"\n{}",
style(format!("Applying {} to {}...", stash_ref, target_branch)).yellow()
);
match git::git_command(&["stash", "apply", stash_ref], Some(&wt_path), false, true) {
Ok(r) if r.returncode == 0 => {
println!(
"{} Stash applied to {}\n",
style("*").green().bold(),
target_branch
);
println!(
"{}\n",
style(format!("Worktree path: {}", wt_path.display())).dim()
);
}
_ => {
println!("{} Failed to apply stash\n", style("x").red().bold());
println!(
"{}\n",
style("Tip: There may be conflicts. Check the worktree and resolve manually.")
.yellow()
);
}
}
Ok(())
}