use crate::error::MyResult;
use crate::git::flags::GitFlags;
use git2::{Repository, RepositoryOpenFlags, Status, StatusEntry, StatusOptions};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
type GitStatusMap = HashMap<PathBuf, Status>;
pub struct GitRepo {
repo: Repository,
root: PathBuf,
statuses: Option<GitStatusMap>,
}
impl GitRepo {
pub fn open_repository<'a, I>(path: &Path, ceiling: I) -> Option<Self> where
I: IntoIterator<Item = &'a PathBuf>,
{
let flags = RepositoryOpenFlags::empty();
if let Ok(repo) = Repository::open_ext(path, flags, ceiling) {
if let Some(root) = repo.workdir() {
let root = root.to_path_buf();
let repo = Self { repo, root, statuses: None };
return Some(repo);
}
}
None
}
pub fn get_root(&self) -> &Path {
&self.root
}
pub fn test_ignored(&self, path: &Path) -> MyResult<bool> {
let path = path.strip_prefix(&self.root).map_err(|e| (e, path))?;
let ignored = self.repo.is_path_ignored(path)?;
Ok(ignored)
}
pub fn test_allowed(&mut self, flags: &GitFlags, path: &Path) -> MyResult<Option<GitFlags>> {
let path = path.strip_prefix(&self.root).map_err(|e| (e, path))?;
let status = self.repo.status_file(path)?;
if status.is_index_new() {
let statuses = self.statuses.get_or_insert_with(|| {
Self::read_statuses(&self.repo)
});
if let Some(status) = statuses.get(path) {
let result = flags.test_allowed(status);
return Ok(result);
}
}
let result = flags.test_allowed(&status);
Ok(result)
}
fn read_statuses(repo: &Repository) -> GitStatusMap {
let mut statuses = GitStatusMap::new();
let mut options = StatusOptions::new();
options.include_unmodified(true);
options.include_untracked(true);
options.include_ignored(true);
options.renames_head_to_index(true);
options.renames_index_to_workdir(true);
options.renames_from_rewrites(true);
if let Ok(entries) = repo.statuses(Some(&mut options)) {
for entry in entries.iter() {
if let Some(path) = Self::read_path(&entry) {
statuses.insert(path.to_path_buf(), entry.status());
}
}
}
statuses
}
fn read_path<'a>(entry: &'a StatusEntry) -> Option<&'a Path> {
let delta = entry.head_to_index()?;
let old = delta.old_file().path()?;
let new = delta.new_file().path()?;
if new != old {
Some(new)
} else {
None
}
}
}