git_repository.rs

 1use anyhow::Result;
 2use git2::{Repository as LibGitRepository, RepositoryOpenFlags as LibGitRepositoryOpenFlags};
 3use parking_lot::Mutex;
 4use std::{ffi::OsStr, path::Path, sync::Arc};
 5use util::ResultExt;
 6
 7#[derive(Clone)]
 8pub struct GitRepository {
 9    // Path to folder containing the .git file or directory
10    content_path: Arc<Path>,
11    // Path to the actual .git folder.
12    // Note: if .git is a file, this points to the folder indicated by the .git file
13    git_dir_path: Arc<Path>,
14    scan_id: usize,
15    libgit_repository: Arc<Mutex<LibGitRepository>>,
16}
17
18impl GitRepository {
19    pub fn open(dotgit_path: &Path) -> Option<GitRepository> {
20        LibGitRepository::open(&dotgit_path)
21            .log_err()
22            .and_then(|libgit_repository| {
23                Some(Self {
24                    content_path: libgit_repository.workdir()?.into(),
25                    git_dir_path: dotgit_path.canonicalize().log_err()?.into(),
26                    scan_id: 0,
27                    libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)),
28                })
29            })
30    }
31
32    pub fn manages(&self, path: &Path) -> bool {
33        path.canonicalize()
34            .map(|path| path.starts_with(&self.content_path))
35            .unwrap_or(false)
36    }
37
38    pub fn in_dot_git(&self, path: &Path) -> bool {
39        path.canonicalize()
40            .map(|path| path.starts_with(&self.git_dir_path))
41            .unwrap_or(false)
42    }
43
44    pub fn content_path(&self) -> &Path {
45        self.content_path.as_ref()
46    }
47
48    pub fn git_dir_path(&self) -> &Path {
49        self.git_dir_path.as_ref()
50    }
51
52    pub fn scan_id(&self) -> usize {
53        self.scan_id
54    }
55
56    pub(super) fn set_scan_id(&mut self, scan_id: usize) {
57        self.scan_id = scan_id;
58    }
59
60    pub fn with_repo<F: FnOnce(&mut git2::Repository)>(&mut self, f: F) {
61        let mut git2 = self.libgit_repository.lock();
62        f(&mut git2)
63    }
64
65    pub async fn load_head_text(&self, file_path: &Path) -> Option<String> {
66        fn logic(repo: &LibGitRepository, file_path: &Path) -> Result<Option<String>> {
67            let object = repo
68                .head()?
69                .peel_to_tree()?
70                .get_path(file_path)?
71                .to_object(&repo)?;
72
73            let content = match object.as_blob() {
74                Some(blob) => blob.content().to_owned(),
75                None => return Ok(None),
76            };
77
78            let head_text = String::from_utf8(content.to_owned())?;
79            Ok(Some(head_text))
80        }
81
82        match logic(&self.libgit_repository.lock(), file_path) {
83            Ok(value) => return value,
84            Err(err) => log::error!("Error loading head text: {:?}", err),
85        }
86        None
87    }
88}
89
90impl std::fmt::Debug for GitRepository {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        f.debug_struct("GitRepository")
93            .field("content_path", &self.content_path)
94            .field("git_dir_path", &self.git_dir_path)
95            .field("scan_id", &self.scan_id)
96            .field("libgit_repository", &"LibGitRepository")
97            .finish()
98    }
99}