Skip to main content

changeset_git/repository/
mod.rs

1mod commit;
2mod diff;
3mod files;
4mod remote;
5mod staging;
6mod status;
7mod tag;
8
9use std::path::{Path, PathBuf};
10
11use crate::{GitError, Result};
12
13pub struct Repository {
14    pub(crate) inner: git2::Repository,
15    root: PathBuf,
16}
17
18impl Repository {
19    /// # Errors
20    ///
21    /// Returns [`GitError::NotARepository`] if the path is not inside a git repository.
22    pub fn open(path: &Path) -> Result<Self> {
23        let inner = git2::Repository::discover(path).map_err(|_| GitError::NotARepository {
24            path: path.to_path_buf(),
25        })?;
26
27        let root = inner.workdir().ok_or_else(|| GitError::NotARepository {
28            path: path.to_path_buf(),
29        })?;
30
31        let root = dunce::simplified(root).to_path_buf();
32
33        Ok(Self { inner, root })
34    }
35
36    #[must_use]
37    pub fn root(&self) -> &Path {
38        &self.root
39    }
40
41    pub(crate) fn to_relative_path(&self, path: &Path) -> PathBuf {
42        if path.is_absolute() {
43            let normalized = dunce::simplified(path);
44            normalized
45                .strip_prefix(&self.root)
46                .map_or_else(|_| path.to_path_buf(), Path::to_path_buf)
47        } else {
48            path.to_path_buf()
49        }
50    }
51}
52
53#[cfg(test)]
54pub(crate) mod tests {
55    use super::*;
56    use tempfile::TempDir;
57
58    pub(crate) fn setup_test_repo() -> anyhow::Result<(TempDir, Repository)> {
59        let dir = TempDir::new()?;
60        let repo = git2::Repository::init(dir.path())?;
61
62        let mut config = repo.config()?;
63        config.set_str("user.name", "Test")?;
64        config.set_str("user.email", "test@example.com")?;
65
66        let sig = git2::Signature::now("Test", "test@example.com")?;
67        let tree_id = repo.index()?.write_tree()?;
68        let tree = repo.find_tree(tree_id)?;
69        repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[])?;
70
71        let repository = Repository::open(dir.path())?;
72        Ok((dir, repository))
73    }
74
75    #[test]
76    fn open_repository() -> anyhow::Result<()> {
77        let (dir, repo) = setup_test_repo()?;
78        let expected = dir.path().canonicalize()?;
79        let actual = repo.root().canonicalize()?;
80        assert_eq!(actual, expected);
81        Ok(())
82    }
83
84    #[test]
85    fn open_nonexistent_repository() {
86        let dir = TempDir::new().expect("failed to create temp dir");
87        let result = Repository::open(dir.path());
88        assert!(matches!(result, Err(GitError::NotARepository { .. })));
89    }
90}