use super::repo::{git, split_nul};
use std::collections::HashMap;
use std::path::Path;
pub fn staged(root: &Path) -> Option<Vec<String>> {
git(root, &["diff", "--cached", "--name-only", "-z", "--diff-filter=ACMR"]).map(split_nul)
}
pub fn changed_since(root: &Path, base: &str) -> Option<Vec<String>> {
let mut files = split_nul(git(root, &["diff", "--name-only", "-z", "--diff-filter=ACMR", base])?);
if let Some(untracked) = git(root, &["ls-files", "-z", "--others", "--exclude-standard"]) {
files.extend(split_nul(untracked));
}
files.sort();
files.dedup();
Some(files)
}
pub fn changed_in_range(root: &Path, range: &str) -> Option<Vec<String>> {
git(root, &["diff", "--name-only", "-z", "--diff-filter=ACMR", range]).map(split_nul)
}
pub fn added_lines(root: &Path, base: Option<&str>) -> Option<HashMap<String, Vec<usize>>> {
let mut args = vec!["diff", "-U0", "--no-color", "--no-ext-diff", "--diff-filter=ACMR"];
match base {
Some(b) => args.push(b),
None => args.push("--cached"),
}
let out = git(root, &args)?;
let mut map: HashMap<String, Vec<usize>> = HashMap::new();
let mut current: Option<String> = None;
for line in String::from_utf8_lossy(&out).lines() {
if let Some(p) = line.strip_prefix("+++ ") {
current = p.strip_prefix("b/").map(str::to_string);
} else if let (Some(p), Some(h)) = (¤t, line.strip_prefix("@@ ")) {
let added = h.split(' ').find(|s| s.starts_with('+')).unwrap_or("+0,0");
let (start, count) = added[1..].split_once(',').unwrap_or((&added[1..], "1"));
let (start, count): (usize, usize) = (start.parse().unwrap_or(0), count.parse().unwrap_or(0));
map.entry(p.clone()).or_default().extend(start..start + count);
}
}
Some(map)
}
pub fn show(root: &Path, rev: &str, path: &str) -> Option<String> {
let spec = format!("{rev}:{path}");
git(root, &["show", &spec]).map(|b| String::from_utf8_lossy(&b).into_owned())
}
pub fn show_staged(root: &Path, path: &str) -> Option<String> {
let spec = format!(":{path}");
git(root, &["show", &spec]).map(|b| String::from_utf8_lossy(&b).into_owned())
}