1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::DOT_GIT_DIR;
use std::{borrow::Cow, ffi::OsStr, path::Path};
pub fn bare(git_dir_candidate: impl AsRef<Path>) -> bool {
let git_dir = git_dir_candidate.as_ref();
!(git_dir.join("index").exists() || (git_dir.file_name() == Some(OsStr::new(DOT_GIT_DIR)) && git_dir.is_file()))
}
pub fn git(git_dir: impl AsRef<Path>) -> Result<crate::repository::Kind, crate::is_git::Error> {
#[derive(Eq, PartialEq)]
enum Kind {
MaybeRepo,
LinkedWorkTreeDir,
WorkTreeGitDir { work_dir: std::path::PathBuf },
}
#[cfg(not(windows))]
fn is_directory(err: &std::io::Error) -> bool {
err.raw_os_error() == Some(21)
}
#[cfg(windows)]
fn is_directory(err: &std::io::Error) -> bool {
err.kind() == std::io::ErrorKind::PermissionDenied
}
let git_dir = git_dir.as_ref();
let (dot_git, common_dir, kind) = match crate::path::from_gitdir_file(git_dir) {
Ok(private_git_dir) => {
let common_dir = private_git_dir.join("commondir");
let common_dir = crate::path::from_plain_file(&common_dir)
.ok_or_else(|| crate::is_git::Error::MissingCommonDir {
missing: common_dir.clone(),
})?
.map_err(|_| crate::is_git::Error::MissingCommonDir { missing: common_dir })?;
let common_dir = private_git_dir.join(common_dir);
(
Cow::Owned(private_git_dir),
Cow::Owned(common_dir),
Kind::LinkedWorkTreeDir,
)
}
Err(crate::path::from_gitdir_file::Error::Io(err)) if is_directory(&err) => {
let common_dir = git_dir.join("commondir");
let worktree_and_common_dir =
crate::path::from_plain_file(common_dir)
.and_then(Result::ok)
.and_then(|cd| {
crate::path::from_plain_file(git_dir.join("gitdir"))
.and_then(Result::ok)
.map(|worktree_gitfile| (crate::path::without_dot_git_dir(worktree_gitfile), cd))
});
match worktree_and_common_dir {
Some((work_dir, common_dir)) => {
let common_dir = git_dir.join(common_dir);
(
Cow::Borrowed(git_dir),
Cow::Owned(common_dir),
Kind::WorkTreeGitDir { work_dir },
)
}
None => (Cow::Borrowed(git_dir), Cow::Borrowed(git_dir), Kind::MaybeRepo),
}
}
Err(err) => return Err(err.into()),
};
{
let object_hash_should_not_matter_here = git_hash::Kind::Sha1;
let refs = git_ref::file::Store::at(
dot_git.as_ref(),
git_ref::store::WriteReflog::Normal,
object_hash_should_not_matter_here,
);
let head = refs.find_loose("HEAD")?;
if head.name.as_bstr() != "HEAD" {
return Err(crate::is_git::Error::MisplacedHead {
name: head.name.into_inner(),
});
}
}
{
let objects_path = common_dir.join("objects");
if !objects_path.is_dir() {
return Err(crate::is_git::Error::MissingObjectsDirectory { missing: objects_path });
}
}
{
let refs_path = common_dir.join("refs");
if !refs_path.is_dir() {
return Err(crate::is_git::Error::MissingRefsDirectory { missing: refs_path });
}
}
Ok(match kind {
Kind::LinkedWorkTreeDir => crate::repository::Kind::WorkTree {
linked_git_dir: Some(dot_git.into_owned()),
},
Kind::WorkTreeGitDir { work_dir } => crate::repository::Kind::WorkTreeGitDir { work_dir },
Kind::MaybeRepo => {
if bare(git_dir) {
crate::repository::Kind::Bare
} else {
crate::repository::Kind::WorkTree { linked_git_dir: None }
}
}
})
}