use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
pub(crate) type SharedMatcher = Arc<RwLock<BaselineMatcherInner>>;
#[derive(Debug, Clone)]
pub(crate) struct BaselineMatcherInner {
head_file: PathBuf,
branch_ref: Option<PathBuf>,
packed_refs: PathBuf,
}
impl BaselineMatcherInner {
pub(crate) fn new(
git_dir: &Path,
common_git_dir: &Path,
current_branch_ref: Option<&str>,
) -> Self {
let head_file = canonicalize_or_self(&git_dir.join("HEAD"));
let branch_ref = current_branch_ref.map(|r| {
let mut p = common_git_dir.to_path_buf();
for segment in r.split('/') {
p.push(segment);
}
canonicalize_or_self(&p)
});
let packed_refs = canonicalize_or_self(&common_git_dir.join("packed-refs"));
Self {
head_file,
branch_ref,
packed_refs,
}
}
pub(crate) fn matches(&self, path: &Path) -> bool {
let p = canonicalize_or_self(path);
p == self.head_file
|| self.branch_ref.as_ref().is_some_and(|r| p == *r)
|| p == self.packed_refs
}
}
pub(crate) fn canonicalize_or_self(p: &Path) -> PathBuf {
if let Ok(canonical) = p.canonicalize() {
return canonical;
}
let mut missing_tail = Vec::new();
let mut cursor = p;
while let Some(parent) = cursor.parent() {
let Some(name) = cursor.file_name() else {
break;
};
missing_tail.push(name.to_os_string());
if let Ok(mut canonical_parent) = parent.canonicalize() {
for segment in missing_tail.iter().rev() {
canonical_parent.push(segment);
}
return canonical_parent;
}
cursor = parent;
}
p.to_path_buf()
}