use std::fmt;
use std::path::Path;
use std::process::Command;
pub mod commit;
mod error;
pub mod id;
pub mod versions;
pub use error::{Error, Result};
pub struct Repository {
inner: git2::Repository,
}
impl Repository {
pub fn open<P>(path: P) -> Result<Self>
where
P: AsRef<Path>,
{
Ok(Self {
inner: git2::Repository::discover(path)?,
})
}
pub fn add<S>(&self, spec: S) -> Result
where
S: AsRef<str>,
{
let mut index = self.inner.index()?;
index.add_all([spec.as_ref()], git2::IndexAddOption::DEFAULT, None)?;
index.write()?;
Ok(())
}
pub fn commit<M>(&self, message: M) -> Result
where
M: AsRef<str>,
{
let status = Command::new("git")
.current_dir(self.path())
.args([
"commit",
"--cleanup=verbatim", "--signoff", "--no-verify", "--message",
message.as_ref(),
])
.status()?;
if !status.success() {
return Err(Error::Status(status));
}
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn branch<N>(&self, name: N) -> Result
where
N: AsRef<str>,
{
let commit = self.inner.head()?.peel_to_commit()?;
let branch = self.inner.branch(name.as_ref(), &commit, false)?;
let name = branch.get().name().expect("invariant");
self.inner.set_head(name)?;
self.inner.checkout_head(None)?;
Ok(())
}
pub fn is_clean(&self) -> Result<bool> {
let mut options = git2::StatusOptions::new();
options
.include_ignored(false)
.include_untracked(true)
.recurse_untracked_dirs(true);
let statuses = self.inner.statuses(Some(&mut options))?;
Ok(statuses.is_empty())
}
pub fn on_default_branch(&self) -> Result<bool> {
let opt = self.inner.head()?;
Ok(opt
.shorthand()
.filter(|name| ["master", "main"].contains(name))
.is_some())
}
}
#[allow(clippy::must_use_candidate)]
impl Repository {
#[allow(clippy::missing_panics_doc)]
#[inline]
pub fn path(&self) -> &Path {
self.inner.path().parent().expect("invariant")
}
}
impl fmt::Debug for Repository {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Repository")
.field("path", &self.path())
.finish()
}
}