Initial upload: GitPulse - Developer Productivity Analyzer CLI tool
This commit is contained in:
145
src/git/commit.rs
Normal file
145
src/git/commit.rs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
use git2;
|
||||||
|
|
||||||
|
pub struct Commit<'a> {
|
||||||
|
commit: git2::Commit<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Commit<'a> {
|
||||||
|
pub fn new(commit: git2::Commit<'a>) -> Self {
|
||||||
|
Self { commit }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> git2::Oid {
|
||||||
|
self.commit.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hex(&self) -> String {
|
||||||
|
self.id().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn short_id(&self, len: usize) -> String {
|
||||||
|
self.id().to_string()[..len.min(40)].to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> Option<&str> {
|
||||||
|
self.commit.message()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_short(&self, max_len: usize) -> String {
|
||||||
|
let msg = self.commit.message().unwrap_or("");
|
||||||
|
if msg.len() > max_len {
|
||||||
|
format!("{}...", &msg[..max_len - 3])
|
||||||
|
} else {
|
||||||
|
msg.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn summary(&self) -> Option<&str> {
|
||||||
|
self.commit.summary()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time(&self) -> DateTime<Utc> {
|
||||||
|
let time = self.commit.time();
|
||||||
|
let naive = chrono::NaiveDateTime::from_timestamp_opt(time.seconds(), 0).unwrap();
|
||||||
|
Utc.from_utc_datetime(&naive)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn author(&self) -> &git2::Signature {
|
||||||
|
self.commit.author()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn author_name(&self) -> String {
|
||||||
|
self.commit.author().name().unwrap_or("Unknown").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn author_email(&self) -> String {
|
||||||
|
self.commit.author().email().unwrap_or("Unknown").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn committer(&self) -> &git2::Signature {
|
||||||
|
self.commit.committer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn committer_name(&self) -> String {
|
||||||
|
self.commit.committer().name().unwrap_or("Unknown").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn committer_email(&self) -> String {
|
||||||
|
self.commit.committer().email().unwrap_or("Unknown").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent_count(&self) -> usize {
|
||||||
|
self.commit.parent_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parents(&self) -> Vec<git2::Commit<'a>> {
|
||||||
|
self.commit.parents().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_merge(&self) -> bool {
|
||||||
|
self.commit.parent_count() > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_initial_commit(&self) -> bool {
|
||||||
|
self.commit.parent_count() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tree(&self) -> Result<git2::Tree> {
|
||||||
|
self.commit.tree().with_context(|| "Failed to get tree")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diff_to_parent(&self) -> Result<Option<git2::Diff>> {
|
||||||
|
if self.is_initial_commit() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let parent = self.commit.parent(0)?;
|
||||||
|
let parent_tree = parent.tree()?;
|
||||||
|
let commit_tree = self.commit.tree()?;
|
||||||
|
let repo = self.commit.repository();
|
||||||
|
let diff = repo.diff_tree_to_tree(Some(&parent_tree), Some(&commit_tree), None)?;
|
||||||
|
Ok(Some(diff))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw(&self) -> &git2::Commit<'a> {
|
||||||
|
&self.commit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommitIterator<'a> {
|
||||||
|
repo: &'a git2::Repository,
|
||||||
|
revwalk: git2::Revwalk<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CommitIterator<'a> {
|
||||||
|
pub fn new(repo: &'a git2::Repository) -> Result<Self> {
|
||||||
|
let mut revwalk = repo.revwalk()?;
|
||||||
|
revwalk.set_sorting(git2::Sort::TIME)?;
|
||||||
|
revwalk.push_head()?;
|
||||||
|
Ok(Self { repo, revwalk })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_sorting(repo: &'a git2::Repository, sorting: git2::Sort) -> Result<Self> {
|
||||||
|
let mut revwalk = repo.revwalk()?;
|
||||||
|
revwalk.set_sorting(sorting)?;
|
||||||
|
revwalk.push_head()?;
|
||||||
|
Ok(Self { repo, revwalk })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for CommitIterator<'a> {
|
||||||
|
type Item = Result<Commit<'a>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.revwalk
|
||||||
|
.next()
|
||||||
|
.map(|oid| match oid {
|
||||||
|
Ok(oid) => match self.repo.find_commit(oid) {
|
||||||
|
Ok(commit) => Ok(Commit::new(commit)),
|
||||||
|
Err(e) => Err(anyhow::anyhow!("Failed to find commit {}: {}", oid, e)),
|
||||||
|
},
|
||||||
|
Err(e) => Err(anyhow::anyhow!("Revwalk error: {}", e)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user