use std::path::{Path, PathBuf};
fn main() {
let hash = git_short_hash().unwrap_or_else(|| "unknown".to_string());
println!("cargo:rustc-env=ZNIPPY_GIT_HASH={hash}");
println!("cargo:rerun-if-changed=build.rs");
}
fn git_short_hash() -> Option<String> {
let (gitdir, commondir) = resolve_git_dirs()?;
println!("cargo:rerun-if-changed={}", gitdir.join("HEAD").display());
let head = std::fs::read_to_string(gitdir.join("HEAD")).ok()?;
let head = head.trim();
let full = if let Some(ref_name) = head.strip_prefix("ref: ") {
resolve_ref(ref_name.trim(), &gitdir, &commondir)?
} else {
head.to_string()
};
let full = full.trim();
if full.len() < 7 || !full.bytes().all(|b| b.is_ascii_hexdigit()) {
return None;
}
Some(full[..7].to_string())
}
fn resolve_git_dirs() -> Option<(PathBuf, PathBuf)> {
let manifest = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR")?);
let mut dir: Option<&Path> = Some(manifest.as_path());
let mut dot_git = None;
while let Some(d) = dir {
let candidate = d.join(".git");
if candidate.exists() {
dot_git = Some(candidate);
break;
}
dir = d.parent();
}
let dot_git = dot_git?;
let meta = std::fs::metadata(&dot_git).ok()?;
let gitdir = if meta.is_dir() {
dot_git
} else {
println!("cargo:rerun-if-changed={}", dot_git.display());
let contents = std::fs::read_to_string(&dot_git).ok()?;
let rel = contents.trim().strip_prefix("gitdir:")?.trim();
let p = PathBuf::from(rel);
if p.is_absolute() {
p
} else {
dot_git.parent()?.join(p)
}
};
let commondir = match std::fs::read_to_string(gitdir.join("commondir")) {
Ok(rel) => {
let rel = rel.trim();
let p = PathBuf::from(rel);
if p.is_absolute() { p } else { gitdir.join(p) }
}
Err(_) => gitdir.clone(),
};
Some((gitdir, commondir))
}
fn resolve_ref(ref_name: &str, gitdir: &Path, commondir: &Path) -> Option<String> {
for base in [gitdir, commondir] {
let loose = base.join(ref_name);
println!("cargo:rerun-if-changed={}", loose.display());
if let Ok(s) = std::fs::read_to_string(&loose) {
let s = s.trim();
if !s.is_empty() && !s.starts_with("ref: ") {
return Some(s.to_string());
}
}
}
let packed = commondir.join("packed-refs");
println!("cargo:rerun-if-changed={}", packed.display());
let packed = std::fs::read_to_string(&packed).ok()?;
for line in packed.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') || line.starts_with('^') {
continue;
}
if let Some((sha, name)) = line.split_once(' ') {
if name.trim() == ref_name {
return Some(sha.trim().to_string());
}
}
}
None
}