use super::repo::{git, nearest_repo};
use std::path::Path;
pub struct CommitText {
pub hash: String,
pub author: String,
pub message: String,
}
pub fn commits(root: &Path, range: &str) -> Option<Vec<CommitText>> {
let out = git(root, &["log", "--format=%H%x00%an <%ae>%x00%B%x1e", range])?;
let text = String::from_utf8_lossy(&out).into_owned();
Some(
text.split('\u{1e}')
.filter_map(|rec| {
let rec = rec.trim_start_matches('\n');
let mut parts = rec.splitn(3, '\0');
let hash = parts.next()?.to_string();
if hash.is_empty() {
return None;
}
Some(CommitText {
hash,
author: parts.next()?.to_string(),
message: parts.next().unwrap_or("").to_string(),
})
})
.collect(),
)
}
pub fn last_change(root: &Path, paths: &[String]) -> Option<String> {
let mut args: Vec<String> = vec!["log".into(), "-1".into(), "--format=%cs".into(), "--".into()];
args.extend(paths.iter().map(|p| if p.contains(['*', '?', '[']) { format!(":(glob){p}") } else { p.clone() }));
let refs: Vec<&str> = args.iter().map(String::as_str).collect();
let out = git(root, &refs)?;
let s = String::from_utf8_lossy(&out).trim().to_string();
if s.is_empty() { None } else { Some(s) }
}
pub struct Revision {
pub hash: String,
pub date: String,
pub author: String,
pub path: String,
}
pub fn history(root: &Path, path: &str) -> Option<Vec<Revision>> {
let (repo, rel, prefix) = nearest_repo(root, path);
let out = git(&repo, &["log", "--follow", "--format=%x1e%H%x00%cs%x00%an", "--name-only", "--", &rel])?;
let text = String::from_utf8_lossy(&out).into_owned();
let mut revs: Vec<Revision> = text
.split('\u{1e}')
.filter_map(|rec| {
let mut lines = rec.lines();
let head = lines.next()?;
let mut parts = head.split('\0');
let hash = parts.next()?.to_string();
let date = parts.next()?.to_string();
let author = parts.next().unwrap_or("").to_string();
let path = format!("{prefix}{}", lines.find(|l| !l.trim().is_empty())?.trim());
Some(Revision { hash, date, author, path })
})
.collect();
revs.reverse();
Some(revs)
}