1use std::path::Path;
2use std::path::PathBuf;
3
4use git2::Repository;
5use rootcause::option_ext::OptionExt as _;
6use rootcause::prelude::ResultExt as _;
7
8pub fn discover(path: &Path) -> rootcause::Result<Repository> {
14 Ok(Repository::discover(path)
15 .context("error discovering repo")
16 .attach_with(|| format!("path={}", path.display()))?)
17}
18
19pub fn get_root(repo: &Repository) -> PathBuf {
25 if let Some(workdir) = repo.workdir() {
26 return workdir.to_path_buf();
27 }
28 repo.commondir()
30 .components()
31 .filter(|c| c.as_os_str() != ".git")
32 .collect()
33}
34
35pub fn get_relative_path_to_repo(path: &Path, repo: &Repository) -> rootcause::Result<PathBuf> {
41 let repo_workdir = repo
42 .workdir()
43 .context("error getting repository working directory")
44 .attach_with(|| format!("repo={:?}", repo.path().display()))?;
45 Ok(Path::new("/").join(path.strip_prefix(repo_workdir)?))
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn discover_when_path_is_inside_repo_returns_repo() {
54 let (_temp_dir, repo) = crate::tests::init_test_repo(None);
55 let workdir = repo.workdir().unwrap();
56 assert2::assert!(let Ok(_repo) = discover(workdir));
57 }
58
59 #[test]
60 fn discover_when_path_is_not_a_repo_returns_error() {
61 let temp_dir = tempfile::TempDir::new().unwrap();
62 assert2::assert!(let Err(err) = discover(temp_dir.path()));
63 assert!(err.to_string().contains("error discovering repo"));
64 }
65
66 #[test]
67 fn get_root_returns_workdir() {
68 let (_temp_dir, repo) = crate::tests::init_test_repo(None);
69 let root = get_root(&repo);
70 pretty_assertions::assert_eq!(root, repo.workdir().unwrap());
71 }
72
73 #[test]
74 fn get_root_in_worktree_returns_worktree_path() {
75 let (temp_dir, repo) = crate::tests::init_test_repo(None);
76
77 let wt_dir = temp_dir.path().join("my_worktree");
78 repo.worktree("my_worktree", &wt_dir, None).unwrap();
79
80 let wt_repo = Repository::open(&wt_dir).unwrap();
81 let root = get_root(&wt_repo);
82 pretty_assertions::assert_eq!(root, wt_dir.canonicalize().unwrap());
83 }
84
85 #[test]
86 fn get_relative_path_to_repo_when_path_inside_repo_returns_rooted_relative() {
87 let (_temp_dir, repo) = crate::tests::init_test_repo(None);
88 let workdir = repo.workdir().unwrap();
89 let file_path = workdir.join("src").join("main.rs");
90 assert2::assert!(let Ok(rel) = get_relative_path_to_repo(&file_path, &repo));
91 pretty_assertions::assert_eq!(rel, PathBuf::from("/src/main.rs"));
92 }
93
94 #[test]
95 fn get_relative_path_to_repo_when_path_outside_repo_returns_error() {
96 let (_temp_dir, repo) = crate::tests::init_test_repo(None);
97 let outside_path = Path::new("/completely/different/path");
98 assert2::assert!(let Err(_err) = get_relative_path_to_repo(outside_path, &repo));
99 }
100}