use git2::{Commit, Config, Error, ObjectType, Oid, Repository, Tree};
use git2_ext::ops::{Sign, UserSign};
pub struct GitCommits {
default_branch: String,
}
pub fn find_last_commit(repo: &Repository) -> Result<Commit, Error> {
let obj = repo.head()?.resolve()?.peel(ObjectType::Commit)?;
obj.into_commit()
.map_err(|_| Error::from_str("Couldn't find commit"))
}
impl GitCommits {
pub fn with_default_branch(default_branch: &str) -> GitCommits {
GitCommits {
default_branch: String::from(default_branch),
}
}
pub fn commit(&self, repository: &Repository, message: &str) -> Result<Oid, Error> {
let git_config = Config::open_default()?;
let signature = repository.signature()?;
let user_sign = UserSign::from_config(repository, &git_config).ok();
let signing = user_sign.as_ref().map(|sign| sign as &dyn Sign);
let tree = self.add_paths(repository)?;
let parent_commit = find_last_commit(&repository)?;
git2_ext::ops::commit(
repository,
&signature,
&signature,
message,
&tree,
&[&parent_commit],
signing,
)
.and_then(|commit_id| {
self.update_head(repository, commit_id, message)
})
}
fn add_paths<'a>(&'a self, repository: &'a Repository) -> Result<Tree<'a>, Error> {
let mut index = repository.index()?;
index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
index.write()?;
let oid = index.write_tree()?;
repository.find_tree(oid)
}
fn update_head(
&self,
repository: &Repository,
commit_id: Oid,
message: &str,
) -> Result<Oid, Error> {
let head = repository.head()?;
let branch = head.shorthand().unwrap_or(&self.default_branch);
repository.reference(&format!("refs/heads/{}", branch), commit_id, true, message)?;
Ok(commit_id)
}
}
#[cfg(test)]
mod test {
use crate::repo::commit::GitCommits;
#[test]
fn git_committer_with_default_branch() {
let committer = GitCommits::with_default_branch("develop");
assert_eq!("develop", committer.default_branch)
}
}