use std::path::Path;
use super::converge::{hash_bytes, Baseline, Entry, EntryKind};
use super::scan;
pub fn from_head(workdir: &Path) -> Result<Baseline, String> {
let repo = crate::git_backend::open_repo(workdir)
.map_err(|e| format!("cannot open {}: {e}", workdir.display()))?;
let Ok(head) = repo.head_commit() else {
return Ok(Baseline::new());
};
let tree = head
.tree()
.map_err(|e| format!("cannot read the commit's tree: {e}"))?;
let mut baseline = Baseline::new();
let mut walk = |relative: String, entry: gix::object::tree::EntryRef<'_, '_>| -> Result<(), String> {
let mode = entry.mode();
if mode.is_commit() {
return Ok(());
}
if scan::is_refused(Path::new(&relative)) {
return Ok(());
}
let object = entry
.object()
.map_err(|e| format!("cannot read {relative}: {e}"))?;
let bytes = object.data.clone();
let kind = if mode.is_link() {
EntryKind::Symlink
} else {
EntryKind::File
};
let hash = hash_bytes(&bytes);
baseline.insert(
relative,
Entry {
kind,
hash,
size: bytes.len() as u64,
executable: mode.is_executable(),
},
);
Ok(())
};
let mut stack: Vec<(String, gix::Tree<'_>)> = vec![(String::new(), tree)];
while let Some((prefix, tree)) = stack.pop() {
for entry in tree.iter() {
let entry = entry.map_err(|e| format!("cannot read a tree entry: {e}"))?;
let name = entry.filename().to_string();
let relative = if prefix.is_empty() {
name.clone()
} else {
format!("{prefix}/{name}")
};
if entry.mode().is_tree() {
if scan::is_refused(Path::new(&relative)) {
continue;
}
let object = entry
.object()
.map_err(|e| format!("cannot read {relative}: {e}"))?;
let subtree = object
.try_into_tree()
.map_err(|e| format!("{relative} is not a tree: {e}"))?;
stack.push((relative, subtree));
} else {
walk(relative, entry)?;
}
}
}
Ok(baseline)
}
#[cfg(test)]
#[path = "seed_tests.rs"]
mod tests;