1use std::{borrow::Cow, ffi::OsStr, path::Path};
2
3use crate::path::RepositoryKind;
4use crate::DOT_GIT_DIR;
5
6pub fn bare(git_dir_candidate: &Path) -> bool {
10 !(git_dir_candidate.join("index").exists() || (git_dir_candidate.file_name() == Some(OsStr::new(DOT_GIT_DIR))))
11}
12
13#[deprecated = "use path::repository_kind() instead"]
15pub fn submodule_git_dir(git_dir: &Path) -> bool {
16 crate::path::repository_kind(git_dir).is_some_and(|kind| matches!(kind, RepositoryKind::Submodule))
17}
18
19pub fn git(git_dir: &Path) -> Result<crate::repository::Kind, crate::is_git::Error> {
31 let git_dir_metadata = git_dir.metadata().map_err(|err| crate::is_git::Error::Metadata {
32 source: err,
33 path: git_dir.into(),
34 })?;
35 let cwd = gix_fs::current_dir(false)?;
37 git_with_metadata(git_dir, git_dir_metadata, &cwd)
38}
39
40pub(crate) fn git_with_metadata(
41 git_dir: &Path,
42 git_dir_metadata: std::fs::Metadata,
43 cwd: &Path,
44) -> Result<crate::repository::Kind, crate::is_git::Error> {
45 #[derive(Eq, PartialEq)]
46 enum Kind {
47 MaybeRepo,
48 Submodule,
49 LinkedWorkTreeDir,
50 WorkTreeGitDir { work_dir: std::path::PathBuf },
51 }
52
53 let dot_git = if git_dir_metadata.is_file() {
54 let private_git_dir = crate::path::from_gitdir_file(git_dir)?;
55 Cow::Owned(private_git_dir)
56 } else {
57 Cow::Borrowed(git_dir)
58 };
59
60 {
61 if !dot_git.join("HEAD").exists() {
63 return Err(crate::is_git::Error::MissingHead);
64 }
65 let refs = gix_ref::file::Store::at(dot_git.as_ref().into(), Default::default());
69 match refs.find_loose("HEAD") {
70 Ok(head) => {
71 if head.name.as_bstr() != "HEAD" {
72 return Err(crate::is_git::Error::MisplacedHead {
73 name: head.name.into_inner(),
74 });
75 }
76 }
77 Err(gix_ref::file::find::existing::Error::Find(gix_ref::file::find::Error::ReferenceCreation {
78 source: _,
79 relative_path,
80 })) if relative_path == Path::new("HEAD") => {
81 }
83 Err(err) => {
84 return Err(err.into());
85 }
86 }
87 }
88
89 let (common_dir, kind) = if git_dir_metadata.is_file() {
90 let common_dir = dot_git.join("commondir");
91 match crate::path::from_plain_file(&common_dir) {
92 Some(Err(err)) => {
93 return Err(crate::is_git::Error::MissingCommonDir {
94 missing: common_dir,
95 source: err,
96 })
97 }
98 Some(Ok(common_dir)) => {
99 let common_dir = dot_git.join(common_dir);
100 (Cow::Owned(common_dir), Kind::LinkedWorkTreeDir)
101 }
102 None => (dot_git.clone(), Kind::Submodule),
103 }
104 } else {
105 let common_dir = dot_git.join("commondir");
106 let worktree_and_common_dir = crate::path::from_plain_file(&common_dir)
107 .and_then(Result::ok)
108 .and_then(|cd| {
109 crate::path::from_plain_file(&dot_git.join("gitdir"))
110 .and_then(Result::ok)
111 .map(|worktree_gitfile| (crate::path::without_dot_git_dir(worktree_gitfile), cd))
112 });
113 match worktree_and_common_dir {
114 Some((work_dir, common_dir)) => {
115 let common_dir = dot_git.join(common_dir);
116 (Cow::Owned(common_dir), Kind::WorkTreeGitDir { work_dir })
117 }
118 None => (dot_git.clone(), Kind::MaybeRepo),
119 }
120 };
121
122 {
123 let objects_path = common_dir.join("objects");
124 if !objects_path.is_dir() {
125 return Err(crate::is_git::Error::MissingObjectsDirectory { missing: objects_path });
126 }
127 }
128 {
129 let refs_path = common_dir.join("refs");
130 if !refs_path.is_dir() {
131 return Err(crate::is_git::Error::MissingRefsDirectory { missing: refs_path });
132 }
133 }
134 Ok(match kind {
135 Kind::LinkedWorkTreeDir => crate::repository::Kind::WorkTree {
136 linked_git_dir: Some(dot_git.into_owned()),
137 },
138 Kind::WorkTreeGitDir { work_dir } => crate::repository::Kind::WorkTreeGitDir { work_dir },
139 Kind::Submodule => crate::repository::Kind::Submodule {
140 git_dir: dot_git.into_owned(),
141 },
142 Kind::MaybeRepo => {
143 let conformed_git_dir = if git_dir == Path::new(".") {
144 gix_path::realpath_opts(git_dir, cwd, gix_path::realpath::MAX_SYMLINKS)
145 .map(Cow::Owned)
146 .unwrap_or(Cow::Borrowed(git_dir))
147 } else {
148 gix_path::normalize(git_dir.into(), cwd).unwrap_or(Cow::Borrowed(git_dir))
149 };
150 if bare(conformed_git_dir.as_ref()) || conformed_git_dir.extension() == Some(OsStr::new("git")) {
151 crate::repository::Kind::PossiblyBare
152 } else if crate::path::repository_kind(conformed_git_dir.as_ref())
153 .is_some_and(|kind| matches!(kind, RepositoryKind::Submodule))
154 {
155 crate::repository::Kind::SubmoduleGitDir
156 } else if conformed_git_dir.file_name() == Some(OsStr::new(DOT_GIT_DIR)) {
157 crate::repository::Kind::WorkTree { linked_git_dir: None }
158 } else {
159 crate::repository::Kind::PossiblyBare
160 }
161 }
162 })
163}