gitdataai/libs/git/commit/query.rs
2026-04-15 09:08:09 +08:00

232 lines
7.7 KiB
Rust

//! Commit querying operations.
use crate::commit::types::*;
use crate::{GitDomain, GitError, GitResult};
impl GitDomain {
pub fn commit_get(&self, oid: &CommitOid) -> GitResult<CommitMeta> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(CommitMeta::from_git2(&commit))
}
pub fn commit_get_prefix(&self, prefix: &str) -> GitResult<CommitMeta> {
let commit = self
.repo()
.find_commit_by_prefix(prefix)
.map_err(|_e| GitError::InvalidOid(format!("prefix: {}", prefix)))?;
Ok(CommitMeta::from_git2(&commit))
}
pub fn commit_exists(&self, oid: &CommitOid) -> bool {
match oid.to_oid() {
Ok(oid) => self.repo.find_commit(oid).is_ok(),
Err(_) => false,
}
}
pub fn commit_is_commit(&self, oid: &CommitOid) -> bool {
match oid.to_oid() {
Ok(oid) => match self.repo.find_object(oid, None) {
Ok(obj) => obj.kind() == Some(git2::ObjectType::Commit),
Err(_) => false,
},
Err(_) => false,
}
}
pub fn commit_message(&self, oid: &CommitOid) -> GitResult<String> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.message().unwrap_or("").to_string())
}
pub fn commit_summary(&self, oid: &CommitOid) -> GitResult<String> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.summary().unwrap_or("").to_string())
}
pub fn commit_short_id(&self, oid: &CommitOid) -> GitResult<String> {
let _ = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
let len = oid.0.len();
if len < 7 {
return Err(GitError::InvalidOid(oid.to_string()));
}
Ok(oid.0[..7].to_string())
}
pub fn commit_author(&self, oid: &CommitOid) -> GitResult<CommitSignature> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(CommitSignature::from_git2(commit.author()))
}
pub fn commit_committer(&self, oid: &CommitOid) -> GitResult<CommitSignature> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(CommitSignature::from_git2(commit.committer()))
}
pub fn commit_time(&self, oid: &CommitOid) -> GitResult<i64> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.time().seconds())
}
pub fn commit_time_offset(&self, oid: &CommitOid) -> GitResult<i32> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.time().offset_minutes())
}
pub fn commit_encoding(&self, oid: &CommitOid) -> GitResult<Option<String>> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.message_encoding().map(String::from))
}
pub fn commit_tree_id(&self, oid: &CommitOid) -> GitResult<CommitOid> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(CommitOid::from_git2(commit.tree_id()))
}
pub fn commit_parent_count(&self, oid: &CommitOid) -> GitResult<usize> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.parent_count())
}
pub fn commit_parent_ids(&self, oid: &CommitOid) -> GitResult<Vec<CommitOid>> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.parent_ids().map(CommitOid::from_git2).collect())
}
pub fn commit_parent(&self, oid: &CommitOid, index: usize) -> GitResult<CommitMeta> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
let parent = commit
.parent(index)
.map_err(|e| GitError::Internal(e.to_string()))?;
Ok(CommitMeta::from_git2(&parent))
}
pub fn commit_first_parent(&self, oid: &CommitOid) -> GitResult<Option<CommitMeta>> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
if commit.parent_count() > 0 {
let parent = commit
.parent(0)
.map_err(|e| GitError::Internal(e.to_string()))?;
Ok(Some(CommitMeta::from_git2(&parent)))
} else {
Ok(None)
}
}
pub fn commit_is_merge(&self, oid: &CommitOid) -> GitResult<bool> {
let commit = self
.repo()
.find_commit(oid.to_oid()?)
.map_err(|_e| GitError::ObjectNotFound(oid.to_string()))?;
Ok(commit.parent_count() > 1)
}
pub fn commit_log(
&self,
rev: Option<&str>,
offset: usize,
limit: usize,
) -> GitResult<Vec<CommitMeta>> {
let mut revwalk = self
.repo()
.revwalk()
.map_err(|e| GitError::Internal(e.to_string()))?;
if let Some(r) = rev {
if r.contains("..") {
revwalk
.push_range(r)
.map_err(|e| GitError::Internal(e.to_string()))?;
} else {
revwalk
.push_ref(r)
.map_err(|e| GitError::Internal(e.to_string()))?;
}
} else {
revwalk
.push_head()
.map_err(|e| GitError::Internal(e.to_string()))?;
}
revwalk
.set_sorting(git2::Sort::TOPOLOGICAL | git2::Sort::TIME)
.map_err(|e| GitError::Internal(e.to_string()))?;
let mut commits = Vec::new();
let target = offset.saturating_add(limit);
for oid_result in revwalk {
let oid = oid_result.map_err(|e| GitError::Internal(e.to_string()))?;
if target > 0 && commits.len() >= target {
break;
}
if let Ok(commit) = self.repo.find_commit(oid) {
commits.push(CommitMeta::from_git2(&commit));
}
}
Ok(commits.into_iter().skip(offset).take(limit).collect())
}
pub fn commit_range(&self, from: &str, to: &str) -> GitResult<Vec<CommitMeta>> {
let range = format!("{}..{}", from, to);
self.commit_log(Some(&range), 0, 0)
}
pub fn commit_count(&self, from: Option<&str>, to: Option<&str>) -> GitResult<usize> {
let rev = match (from, to) {
(Some(f), Some(t)) => Some(format!("{}..{}", f, t)),
(Some(f), None) => Some(f.to_string()),
(None, Some(t)) => Some(t.to_string()),
(None, None) => None,
};
let commits = self.commit_log(rev.as_deref(), 0, 0)?;
Ok(commits.len())
}
pub fn commit_total(&self, rev: Option<&str>) -> GitResult<usize> {
self.commit_count(None, rev)
}
}