use std::collections::BTreeMap;
use std::path::Path;
use git2::Repository;
use crate::git_changes::Lines;
pub(crate) fn add_untracked(
repo: &Repository,
files: &mut BTreeMap<String, Lines>,
) -> Result<(), String> {
for rel in untracked_rel_paths(repo) {
if !files.contains_key(&rel) {
files.insert(rel, Lines::All);
}
}
Ok(())
}
fn untracked_rel_paths(repo: &Repository) -> Vec<String> {
let Some(root) = repo.workdir() else {
return Vec::new();
};
let tracked = index_paths(repo);
untracked_walker(root)
.filter_map(Result::ok)
.filter_map(|e| untracked_rel_of(&e, root, &tracked))
.collect()
}
fn untracked_rel_of(
entry: &ignore::DirEntry,
root: &Path,
tracked: &std::collections::HashSet<Vec<u8>>,
) -> Option<String> {
if !entry.file_type()?.is_file() {
return None;
}
let rel = normalize(entry.path().strip_prefix(root).ok()?.to_str()?);
(!tracked.contains(rel.as_bytes())).then_some(rel)
}
fn untracked_walker(root: &Path) -> ignore::Walk {
let mut builder = ignore::WalkBuilder::new(root);
builder
.standard_filters(true)
.hidden(false)
.filter_entry(|e| e.file_name() != std::ffi::OsStr::new(".git"));
builder.build()
}
fn index_paths(repo: &Repository) -> std::collections::HashSet<Vec<u8>> {
repo.index()
.map(|idx| idx.iter().map(|e| e.path.to_vec()).collect())
.unwrap_or_default()
}
fn normalize(path: &str) -> String {
path.replace('\\', "/")
}