use chrono::{DateTime, TimeZone, Utc};
use git2::Repository;
use std::path::Path;
pub struct GitRepo {
repo: Repository,
}
impl GitRepo {
pub fn open(path: &Path) -> Option<Self> {
Repository::discover(path).ok().map(|repo| Self { repo })
}
pub fn last_modified(&self, path: &Path) -> Option<DateTime<Utc>> {
let relative_path = self.make_relative(path)?;
let mut revwalk = self.repo.revwalk().ok()?;
revwalk.push_head().ok()?;
revwalk.set_sorting(git2::Sort::TIME).ok()?;
for oid in revwalk.flatten() {
let commit = self.repo.find_commit(oid).ok()?;
let tree = commit.tree().ok()?;
if tree.get_path(Path::new(&relative_path)).is_ok() {
let time = commit.time();
let datetime = Utc.timestamp_opt(time.seconds(), 0).single()?;
return Some(datetime);
}
}
None
}
fn make_relative(&self, path: &Path) -> Option<String> {
let workdir = self.repo.workdir()?;
let abs_path = if path.is_absolute() {
path.to_path_buf()
} else {
std::env::current_dir().ok()?.join(path)
};
abs_path
.strip_prefix(workdir)
.ok()
.map(|p| p.to_string_lossy().to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_open_non_git_directory() {
let repo = GitRepo::open(Path::new("/tmp"));
let _ = repo;
}
}