1use git2::Error;
2use std::path::Path;
3use url::Url;
4
5pub 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 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 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 pub fn open(root: impl AsRef<Path>) -> Result<Self, Error> {
41 let repo = git2::Repository::open(root)?;
42 Ok(Repository { repo })
43 }
44
45 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 pub fn add_path(&self, path: impl AsRef<Path>) -> Result<(), Error> {
61 self.repo.index()?.add_path(path.as_ref())
62 }
63
64 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 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 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}