use std::path::{Path, PathBuf};
use git2::Repository;
use serde::{Deserialize, Serialize};
#[must_use]
pub fn head_sha(root: &Path) -> Option<String> {
let repo = Repository::discover(root).ok()?;
let head = repo.head().ok()?;
let oid = head.target()?;
Some(oid.to_string())
}
#[must_use]
pub fn hooks_dir(root: &Path) -> Option<PathBuf> {
let repo = Repository::discover(root).ok()?;
Some(repo.commondir().join("hooks"))
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommitInfo {
pub sha: String,
pub parent_sha: Option<String>,
pub author_email: Option<String>,
pub message_summary: String,
pub files_changed: u32,
pub insertions: u32,
pub deletions: u32,
}
#[must_use]
pub fn head_commit_info(root: &Path) -> Option<CommitInfo> {
let repo = Repository::discover(root).ok()?;
let head = repo.head().ok()?;
let oid = head.target()?;
let commit = repo.find_commit(oid).ok()?;
let author = commit.author();
let message = commit.message().unwrap_or("");
let message_summary = message.lines().next().unwrap_or("").to_string();
let parent = commit.parent(0).ok();
let parent_sha = parent.as_ref().map(|c| c.id().to_string());
let parent_tree = parent.as_ref().and_then(|p| p.tree().ok());
let head_tree = commit.tree().ok();
let (files_changed, insertions, deletions) = repo
.diff_tree_to_tree(parent_tree.as_ref(), head_tree.as_ref(), None)
.ok()
.and_then(|d| d.stats().ok())
.map_or((0, 0, 0), |s| {
(
u32::try_from(s.files_changed()).unwrap_or(u32::MAX),
u32::try_from(s.insertions()).unwrap_or(u32::MAX),
u32::try_from(s.deletions()).unwrap_or(u32::MAX),
)
});
Some(CommitInfo {
sha: oid.to_string(),
parent_sha,
author_email: author.email().map(str::to_string),
message_summary,
files_changed,
insertions,
deletions,
})
}