use std::path::{Path, PathBuf};
pub fn find_git_root(start_dir: &Path) -> Option<PathBuf> {
let mut current = start_dir.to_path_buf();
loop {
if current.join(".git").is_dir() {
return Some(current);
}
if !current.pop() {
return None;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
fn create_temp_dir() -> tempfile::TempDir {
tempfile::tempdir().expect("failed to create temp dir")
}
#[test]
fn finds_git_dir_in_current_directory() {
let tmp = create_temp_dir();
let git_dir = tmp.path().join(".git");
fs::create_dir(&git_dir).unwrap();
let result = find_git_root(tmp.path());
assert_eq!(result, Some(tmp.path().to_path_buf()));
}
#[test]
fn finds_git_dir_in_parent_directory() {
let tmp = create_temp_dir();
let git_dir = tmp.path().join(".git");
fs::create_dir(&git_dir).unwrap();
let child = tmp.path().join("src").join("components");
fs::create_dir_all(&child).unwrap();
let result = find_git_root(&child);
assert_eq!(result, Some(tmp.path().to_path_buf()));
}
#[test]
fn finds_git_dir_in_grandparent_directory() {
let tmp = create_temp_dir();
let git_dir = tmp.path().join(".git");
fs::create_dir(&git_dir).unwrap();
let deep = tmp.path().join("a").join("b").join("c");
fs::create_dir_all(&deep).unwrap();
let result = find_git_root(&deep);
assert_eq!(result, Some(tmp.path().to_path_buf()));
}
#[test]
fn returns_none_when_no_git_dir() {
let tmp = create_temp_dir();
let result = find_git_root(tmp.path());
assert_eq!(result, None);
}
#[test]
fn stops_at_closest_git_root() {
let tmp = create_temp_dir();
fs::create_dir(tmp.path().join(".git")).unwrap();
let inner = tmp.path().join("packages").join("sub");
fs::create_dir_all(&inner).unwrap();
fs::create_dir(inner.join(".git")).unwrap();
let deep = inner.join("src");
fs::create_dir_all(&deep).unwrap();
let result = find_git_root(&deep);
assert_eq!(result, Some(inner.clone()));
}
}