async_git/plumbing/
repo.rs1use std::env;
2use std::path::{Path, PathBuf};
3use std::str::FromStr;
4
5use tokio::fs;
6
7use crate::errors::{Error, Result};
8use crate::plumbing::{Object, Oid, Ref};
9use crate::util;
10
11pub struct Repository {
12 pub path: PathBuf,
13}
14
15impl Repository {
16 pub async fn open(path: impl AsRef<Path>) -> Result<Self> {
18 let path = path.as_ref().to_path_buf();
19 if !Repository::is_repo(&path).await? {
20 return Err(Error::PathIsNotRepository(path));
21 }
22 Ok(Repository { path })
23 }
24
25 pub async fn head(&self) -> Result<Ref> {
27 let head_path = self.path.join("HEAD");
28 let head_ref = util::file_to_string(head_path).await?;
29 Ok(Ref::parse(head_ref.trim())?)
30 }
31
32 pub async fn is_repo(path: impl AsRef<Path>) -> Result<bool> {
34 let path = path.as_ref();
35
36 if !path.is_dir() {
38 return Ok(false);
39 }
40
41 let head_path = path.join("HEAD");
45 if !head_path.exists() {
46 return Ok(false);
47 }
48 let contents = util::file_to_string(&head_path).await?;
49 if contents.starts_with("ref:") {
50 let rest = contents.trim_start_matches("ref:").trim();
52 if !rest.starts_with("refs/") {
53 return Ok(false);
54 }
55 } else {
56 let contents = contents.trim();
58 if !Oid::from_str(&contents).is_ok() {
59 return Ok(false);
60 }
61 if fs::read_link(&head_path).await.is_ok() {
63 return Ok(false);
64 }
65 }
66
67 let objects_path = match env::var("GIT_OBJECT_DIRECTORY") {
69 Ok(path) => PathBuf::from(path),
70 Err(_) => path.join("objects"),
71 };
72 if !objects_path.exists() {
73 return Ok(false);
74 }
75
76 let refs_path = path.join("refs");
78 if !refs_path.exists() {
79 return Ok(false);
80 }
81
82 Ok(true)
83 }
84
85 pub async fn find() -> Result<Option<PathBuf>> {
87 let mut cwd = env::current_dir()?;
88
89 loop {
94 let dot_git = cwd.join(".git");
95 if dot_git.exists() {
96 if dot_git.is_file() {
97 let contents = util::file_to_string(&dot_git).await?;
98 if contents.starts_with("gitdir:") {
99 let path = PathBuf::from(contents.trim_start_matches("gitdir:").trim());
100 if Repository::is_repo(&path).await? {
101 return Ok(Some(path));
102 }
103 }
104 } else if dot_git.is_dir() {
105 if Repository::is_repo(&dot_git).await? {
106 return Ok(Some(dot_git));
107 }
108 }
109 } else {
110 if Repository::is_repo(&cwd).await? {
111 return Ok(Some(cwd));
112 }
113 }
114
115 if Repository::is_repo(cwd.clone()).await? {
116 return Ok(Some(cwd));
117 }
118
119 cwd = match cwd.parent() {
120 Some(parent) => parent.to_path_buf(),
121 None => return Ok(None),
122 };
123 }
124 }
125
126 pub fn get_object(&self, id: Oid) -> Object {
127 Object {
128 repo_path: self.path.clone(),
129 id,
130 }
131 }
132}