crate_index/index/
git.rs

1use git2::Error;
2use std::path::Path;
3use url::Url;
4
5/// Representation of a git repository on the host filesystem
6pub struct Repository {
7    repo: git2::Repository,
8}
9
10pub(crate) struct Identity<'a> {
11    pub username: &'a str,
12    pub email: &'a str,
13}
14
15impl Repository {
16    /// Initialise a new git repository at the given path.
17    pub fn init(root: impl AsRef<Path>) -> Result<Self, Error> {
18        let repo = git2::Repository::init(root)?;
19
20        Ok(Repository { repo })
21    }
22
23    /// Commit the current tree state as an "Initial commit"
24    pub fn create_initial_commit(&self) -> Result<(), Error> {
25        let signature = self.repo.signature()?;
26        let oid = self.repo.index()?.write_tree()?;
27        let tree = self.repo.find_tree(oid)?;
28        self.repo.commit(
29            Some("HEAD"),
30            &signature,
31            &signature,
32            "Initial commit",
33            &tree,
34            &[],
35        )?;
36        Ok(())
37    }
38
39    /// Open an existing repository
40    pub fn open(root: impl AsRef<Path>) -> Result<Self, Error> {
41        let repo = git2::Repository::open(root)?;
42        Ok(Repository { repo })
43    }
44
45    /// Add a remote to the repository
46    pub(crate) fn add_origin(&self, remote: &Url) -> Result<(), Error> {
47        self.repo.remote("origin", remote.as_str())?;
48        Ok(())
49    }
50
51    pub(crate) fn set_username(&self, username: impl AsRef<str>) -> Result<(), Error> {
52        self.repo.config()?.set_str("user.name", username.as_ref())
53    }
54
55    pub(crate) fn set_email(&self, email: impl AsRef<str>) -> Result<(), Error> {
56        self.repo.config()?.set_str("user.email", email.as_ref())
57    }
58
59    /// Add a file to the repository by relative path
60    pub fn add_path(&self, path: impl AsRef<Path>) -> Result<(), Error> {
61        self.repo.index()?.add_path(path.as_ref())
62    }
63
64    /// Add every file in the tree to the repository.
65    ///
66    /// everything that matches '*', that is.
67    pub fn add_all(&self) -> Result<(), Error> {
68        self.repo
69            .index()?
70            .add_all(&["*"], git2::IndexAddOption::DEFAULT, None)
71    }
72
73    fn find_last_commit(&self) -> Result<git2::Commit, Error> {
74        self.repo.head()?.peel_to_commit()
75    }
76
77    /// Commit all staged changes
78    pub fn commit(&self, message: impl AsRef<str>) -> Result<(), Error> {
79        let mut index = self.repo.index()?;
80        let oid = index.write_tree()?;
81        let signature = self.repo.signature()?;
82        let parent_commit = self.find_last_commit()?;
83        let tree = self.repo.find_tree(oid)?;
84        self.repo.commit(
85            Some("HEAD"),
86            &signature,
87            &signature,
88            message.as_ref(),
89            &tree,
90            &[&parent_commit],
91        )?;
92        Ok(())
93    }
94
95    /// Push all commits to the configured remotes
96    pub fn push(&self) -> Result<(), Error> {
97        let refspecs: Vec<String> = Vec::new();
98
99        for remote_name in self.repo.remotes()?.into_iter().filter_map(|x| x) {
100            self.repo.find_remote(&remote_name)?.push(&refspecs, None)?;
101        }
102
103        Ok(())
104    }
105}