use git2::{Repository, Signature, StashFlags};
use crate::error::{CheckoutError, Result};
fn label(branch: &str, worktree: &str) -> String {
format!("workon-autostash: {} @ {}", branch, worktree)
}
fn parse_label(message: &str) -> Option<(&str, &str)> {
let (_, rest) = message.split_once("workon-autostash: ")?;
rest.rsplit_once(" @ ")
}
pub fn create_labeled_stash(
wt_repo: &mut Repository,
branch: &str,
worktree: &str,
) -> Result<git2::Oid> {
let sig = wt_repo
.signature()
.or_else(|_| Signature::now("workon", "workon@localhost"))
.map_err(CheckoutError::Git)?;
let msg = label(branch, worktree);
wt_repo
.stash_save2(&sig, Some(&msg), Some(StashFlags::INCLUDE_UNTRACKED))
.map_err(CheckoutError::Git)
.map_err(Into::into)
}
pub fn find_labeled_stash(
repo: &mut Repository,
branch: &str,
worktree: &str,
) -> Result<Option<usize>> {
let mut found: Option<usize> = None;
repo.stash_foreach(|index, message, _oid| {
if found.is_none() && parse_label(message) == Some((branch, worktree)) {
found = Some(index);
}
true
})
.map_err(CheckoutError::Git)?;
Ok(found)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StashRestore {
Applied,
Conflict,
NotFound,
}
pub fn apply_labeled_stash(
repo: &mut Repository,
branch: &str,
worktree: &str,
) -> Result<StashRestore> {
let Some(index) = find_labeled_stash(repo, branch, worktree)? else {
return Ok(StashRestore::NotFound);
};
match repo.stash_apply(index, None) {
Ok(()) => {
if repo.index().map_err(CheckoutError::Git)?.has_conflicts() {
return Ok(StashRestore::Conflict);
}
repo.stash_drop(index).map_err(CheckoutError::Git)?;
Ok(StashRestore::Applied)
}
Err(e)
if matches!(
e.code(),
git2::ErrorCode::Conflict | git2::ErrorCode::MergeConflict
) =>
{
Ok(StashRestore::Conflict)
}
Err(e) => Err(CheckoutError::Git(e).into()),
}
}
pub fn list_labeled_for_worktree(repo: &mut Repository, worktree: &str) -> Result<Vec<String>> {
let mut entries = Vec::new();
repo.stash_foreach(|_index, message, _oid| {
if parse_label(message).is_some_and(|(_, wt)| wt == worktree) {
entries.push(message.to_string());
}
true
})
.map_err(CheckoutError::Git)?;
Ok(entries)
}