1use std::path::{Path, PathBuf};
10
11use crate::error::{Error, Result};
12
13pub(crate) struct Repo {
15 inner: gix::Repository,
16}
17
18impl Repo {
19 pub(crate) fn discover(start: &Path) -> Result<Repo> {
22 match gix::discover(start) {
23 Ok(inner) => Ok(Repo { inner }),
24 Err(_) => Err(Error::NotInRepo),
25 }
26 }
27
28 pub(crate) fn gix(&self) -> &gix::Repository {
30 &self.inner
31 }
32
33 pub(crate) fn is_bare(&self) -> bool {
35 self.inner.workdir().is_none()
36 }
37
38 pub(crate) fn current_workdir(&self) -> Option<PathBuf> {
41 self.inner.workdir().map(Path::to_path_buf)
42 }
43
44 pub(crate) fn git_dir(&self) -> PathBuf {
47 self.inner.git_dir().to_path_buf()
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use crate::testutil::TestRepo;
55
56 #[test]
57 fn discovers_from_root_and_subdir() {
58 let repo = TestRepo::init();
59 let r = Repo::discover(repo.root()).unwrap();
60 assert!(!r.is_bare());
61 assert_eq!(canon(&r.current_workdir().unwrap()), canon(repo.root()));
62
63 let sub = repo.root().join("a/b");
65 std::fs::create_dir_all(&sub).unwrap();
66 let r2 = Repo::discover(&sub).unwrap();
67 assert_eq!(canon(&r2.current_workdir().unwrap()), canon(repo.root()));
68 }
69
70 #[test]
71 fn current_workdir_from_linked_worktree() {
72 let repo = TestRepo::init();
73 repo.add_worktree("feature/x", "../wt-x");
74 let linked = repo.root().parent().unwrap().join("wt-x");
75 let r = Repo::discover(&linked).unwrap();
76 assert_eq!(canon(&r.current_workdir().unwrap()), canon(&linked));
77 }
78
79 #[test]
80 fn not_in_repo_errors() {
81 let dir = tempfile::tempdir().unwrap();
82 assert!(matches!(Repo::discover(dir.path()), Err(Error::NotInRepo)));
83 }
84
85 #[test]
86 fn bare_repo_is_detected() {
87 let repo = TestRepo::init_bare();
88 let r = Repo::discover(repo.root()).unwrap();
89 assert!(r.is_bare());
90 assert!(r.current_workdir().is_none());
91 }
92
93 fn canon(p: &Path) -> PathBuf {
96 std::fs::canonicalize(p).unwrap_or_else(|_| p.to_path_buf())
97 }
98}