use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::Path;
use crate::runtime_api::workspace::run_git;
use super::nudge::LongHorizonSessionState;
#[must_use]
pub fn git_counts_as_progress(
session: &LongHorizonSessionState,
current: Option<&String>,
last_nudge: Option<&String>,
) -> bool {
let (Some(cur), Some(prev)) = (current, last_nudge) else {
return false;
};
if cur == prev {
return false;
}
if let Some(suppress) = session.suppress_git_progress_baseline.as_ref()
&& cur == suppress
{
return false;
}
true
}
#[must_use]
pub fn workspace_change_signature(workspace: &Path) -> Option<String> {
let inside = run_git(workspace, &["rev-parse", "--is-inside-work-tree"])?;
if inside.trim() != "true" {
return None;
}
let porcelain = run_git(workspace, &["status", "--porcelain=v1"])?;
let mut hasher = DefaultHasher::new();
porcelain.hash(&mut hasher);
Some(format!("{:016x}", hasher.finish()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::long_horizon::nudge::LongHorizonSessionState;
use std::process::Command;
fn git(dir: &Path, args: &[&str]) -> bool {
Command::new("git")
.args(args)
.current_dir(dir)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn git_available() -> bool {
Command::new("git").arg("--version").output().is_ok()
}
#[test]
fn git_progress_suppressed_at_gate_baseline() {
let mut session = LongHorizonSessionState::default();
session.suppress_git_progress_baseline = Some("aaa".to_string());
assert!(!git_counts_as_progress(
&session,
Some(&"aaa".to_string()),
Some(&"bbb".to_string()),
));
assert!(git_counts_as_progress(
&session,
Some(&"ccc".to_string()),
Some(&"bbb".to_string()),
));
}
#[test]
fn signature_changes_when_worktree_changes() {
if !git_available() {
return;
}
let dir = std::env::temp_dir().join(format!(
"lht-progress-{}-{:?}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
let _ = std::fs::create_dir_all(&dir);
assert!(git(&dir, &["init"]), "git init");
let clean = workspace_change_signature(&dir);
assert!(clean.is_some(), "git repo should yield a signature");
std::fs::write(dir.join("a.txt"), b"hello").expect("write file");
let dirty = workspace_change_signature(&dir);
assert!(dirty.is_some());
assert_ne!(clean, dirty, "worktree change must shift the signature");
let _ = std::fs::remove_dir_all(&dir);
}
}