use std::path::Path;
use super::driver::Git;
use super::{DirtProbe, owned};
use crate::session_manager::DirtyWorktree;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct Snapshot {
dirty_files: usize,
unpushed: usize,
head: String,
}
pub(super) fn first(dirt: Option<&DirtyWorktree>, listed_head: &str) -> Snapshot {
Snapshot {
dirty_files: dirt.map_or(0, |d| d.dirty_files),
unpushed: dirt.map_or(0, |d| d.unpushed_commits),
head: listed_head.trim().to_ascii_lowercase(),
}
}
pub(super) fn again<T: Git>(
git: &T,
probe_dirt: DirtProbe<'_>,
path: &Path,
) -> Result<Snapshot, String> {
let head = match git.run(path, &owned(&["rev-parse", "HEAD"])) {
Ok(out) if out.success && !out.stdout.trim().is_empty() => {
out.stdout.trim().to_ascii_lowercase()
}
Ok(out) if out.success => {
return Err(format!(
"`git rev-parse HEAD` in {} named no commit",
path.display()
));
}
Ok(out) => {
return Err(format!(
"`git rev-parse HEAD` in {} failed: {}",
path.display(),
out.stderr.trim()
));
}
Err(e) => {
return Err(format!(
"`git rev-parse HEAD` in {} could not be run: {e:#}",
path.display()
));
}
};
let dirt = probe_dirt(path);
Ok(Snapshot {
dirty_files: dirt.as_ref().map_or(0, |d| d.dirty_files),
unpushed: dirt.as_ref().map_or(0, |d| d.unpushed_commits),
head,
})
}
pub(super) fn drift(before: &Snapshot, after: &Snapshot) -> Option<String> {
if before.head.is_empty() {
return Some(
"the worktree listing reported no HEAD commit, so whether the tree changed while \
cleanup was checking it cannot be told"
.to_string(),
);
}
let mut changed = Vec::new();
if after.dirty_files != before.dirty_files {
changed.push(format!(
"uncommitted/untracked files went from {} to {}",
before.dirty_files, after.dirty_files
));
}
if after.unpushed != before.unpushed {
changed.push(format!(
"unpushed commits went from {} to {}",
before.unpushed, after.unpushed
));
}
if after.head != before.head {
changed.push(format!("HEAD moved from {} to {}", before.head, after.head));
}
if changed.is_empty() {
return None;
}
Some(changed.join(", "))
}