use std::path::Path;
use anyhow::{Result, bail};
use crate::output::{ResetMode, ResetOutput};
use crate::stash::dirty_paths;
use crate::{GitModule, TIMEOUT_LOCAL, git_cmd};
impl GitModule {
pub async fn reset(
&self,
working_dir: &Path,
mode: ResetMode,
target: &str,
force: bool,
) -> Result<ResetOutput> {
self.ensure_session_scope(working_dir)?;
if matches!(mode, ResetMode::Hard) && !force {
self.ensure_no_stash_in_flight(working_dir).await?;
}
let previous_head = git_cmd(working_dir, &["rev-parse", "HEAD"], TIMEOUT_LOCAL).await?;
let flag = match mode {
ResetMode::Soft => "--soft",
ResetMode::Mixed => "--mixed",
ResetMode::Hard => "--hard",
};
git_cmd(working_dir, &["reset", flag, target], TIMEOUT_LOCAL).await?;
let current_head = git_cmd(working_dir, &["rev-parse", "HEAD"], TIMEOUT_LOCAL).await?;
Ok(ResetOutput {
mode,
target: target.to_string(),
previous_head,
current_head,
})
}
async fn ensure_no_stash_in_flight(&self, working_dir: &Path) -> Result<()> {
let stashes = self.stash_list().await?.stashes;
if stashes.is_empty() {
return Ok(());
}
let (staged, unstaged) = dirty_paths(working_dir).await?;
if staged.is_empty() && unstaged.is_empty() {
return Ok(());
}
bail!(
"reset --hard refused: {} stash entr(y|ies) exist and the working tree is dirty \
(staged: [{}], unstaged: [{}]) — this looks like an applied stash. Use \
git_stash_abort to undo the apply (the entry survives), or pass force=true if \
the reset is intended.",
stashes.len(),
staged.join(", "),
unstaged.join(", "),
)
}
}