use std::path::{Path, PathBuf};
pub fn current_branch(cwd: &Path) -> Option<String> {
let head_path = find_head(cwd)?;
let raw = std::fs::read_to_string(&head_path).ok()?;
parse_head(&raw)
}
pub(crate) fn parse_head(raw: &str) -> Option<String> {
let trimmed = raw.trim();
if let Some(rest) = trimmed.strip_prefix("ref: refs/heads/") {
return Some(rest.to_string());
}
if let Some(rest) = trimmed.strip_prefix("ref: ") {
return rest.rsplit('/').next().map(String::from);
}
if trimmed.len() >= 7 && trimmed.chars().all(|c| c.is_ascii_hexdigit()) {
return Some(trimmed[..7].to_string());
}
None
}
fn find_head(cwd: &Path) -> Option<PathBuf> {
let mut dir = cwd;
loop {
let git_path = dir.join(".git");
if git_path.is_dir() {
let head = git_path.join("HEAD");
if head.is_file() {
return Some(head);
}
} else if git_path.is_file()
&& let Ok(contents) = std::fs::read_to_string(&git_path)
&& let Some(rest) = contents.trim().strip_prefix("gitdir: ")
{
let gitdir = Path::new(rest);
let resolved = if gitdir.is_absolute() {
gitdir.to_path_buf()
} else {
dir.join(gitdir)
};
let head = resolved.join("HEAD");
if head.is_file() {
return Some(head);
}
}
match dir.parent() {
Some(p) => dir = p,
None => return None,
}
}
}