use camino::{Utf8Path, Utf8PathBuf}; use git2::{Commit, Index, Repository, Signature, Status}; use crate::WikError; /// based on https://github.com/rust-lang/git2-rs/blob/main/examples/add.rs#L41 pub struct GitContext { path: Utf8PathBuf, repo: Repository, index: Option, } impl GitContext { pub fn open>(path: P) -> Self { let path = path.as_ref(); let repo = Repository::open(&path).unwrap(); Self { path: path.into(), repo, index: None, } } pub fn add(&mut self, path: &Utf8Path) -> Result<(), WikError> { let root = self.path.clone(); if let Ok(stripped) = path.strip_prefix(&root) { return self.add_rel(stripped); } Ok(()) } pub fn add_rel(&mut self, path: &Utf8Path) -> Result<(), WikError> { if self.file_dirty(path.as_std_path()) { self.index() .add_path(path.as_std_path()) .map_err(|e| WikError::GitError { git: e }) } else { Ok(()) } } pub fn commit<'s, S: AsRef>( &mut self, signature: Signature<'s>, msg: S, ) -> Result<(), WikError> { let index = self.index(); let oid = index.write_tree()?; let last_commit = self.head_commit().ok(); let tree = self.repo.find_tree(oid)?; let parents: &[&git2::Commit] = match last_commit.as_ref() { Some(cm) => &[cm], None => &[], }; self.repo.commit( Some("HEAD"), &signature, &signature, msg.as_ref(), &tree, parents, )?; Ok(()) } fn head_commit<'c>(&'c self) -> Result, WikError> { self.repo .head()? .resolve()? .peel(git2::ObjectType::Commit)? .into_commit() .map_err(|_| WikError::GitError { git: git2::Error::from_str("Couldn't find commit!"), }) } fn index(&mut self) -> &mut Index { self.index.get_or_insert_with(|| self.repo.index().unwrap()) } pub fn write_index(&mut self) -> Result<(), WikError> { if let Some(index) = self.index.as_mut() { index.write().map_err(<_>::into) } else { Ok(()) } } fn file_dirty(&self, path: &std::path::Path) -> bool { let status = match self.repo.status_file(path) { Err(_) => return false, Ok(status) => status, }; status.contains(Status::WT_MODIFIED) || status.contains(Status::WT_NEW) } }