diff --git a/Cargo.lock b/Cargo.lock index f0971a12c7..84197b34df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1224,6 +1224,7 @@ dependencies = [ "gix-hashtable", "gix-index", "gix-lock", + "gix-mailmap", "gix-object", "gix-odb", "gix-pack", @@ -1486,6 +1487,18 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "gix-mailmap" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff80d086d2684d30c5785cc37eba9d2cf817cfb33797ed999db9a359d16ab393" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "thiserror 2.0.12", +] + [[package]] name = "gix-object" version = "0.48.0" diff --git a/asyncgit/Cargo.toml b/asyncgit/Cargo.toml index 7966e82e5c..6f2d5b1027 100644 --- a/asyncgit/Cargo.toml +++ b/asyncgit/Cargo.toml @@ -22,6 +22,7 @@ git2-hooks = { path = "../git2-hooks", version = ">=0.4" } gix = { version = "0.71.0", default-features = false, features = [ "max-performance", "revision", + "mailmap" ] } log = "0.4" # git2 = { path = "../../extern/git2-rs", features = ["vendored-openssl"]} diff --git a/asyncgit/src/error.rs b/asyncgit/src/error.rs index 2113e93d6e..1578ed1e50 100644 --- a/asyncgit/src/error.rs +++ b/asyncgit/src/error.rs @@ -113,6 +113,16 @@ pub enum Error { #[error("gix::revision::walk error: {0}")] GixRevisionWalk(#[from] gix::revision::walk::Error), + /// + #[error("gix::objs::decode::Error error: {0}")] + GixObjsDecode(#[from] gix::objs::decode::Error), + + /// + #[error("gix::object::find::existing::with_conversion::Error error: {0}")] + GixObjectFindExistingWithConversionError( + #[from] gix::object::find::existing::with_conversion::Error, + ), + /// #[error("amend error: config commit.gpgsign=true detected.\ngpg signing is not supported for amending non-last commits")] SignAmendNonLastCommit, diff --git a/asyncgit/src/sync/commits_info.rs b/asyncgit/src/sync/commits_info.rs index 111a2b9bce..ca20f6b4e5 100644 --- a/asyncgit/src/sync/commits_info.rs +++ b/asyncgit/src/sync/commits_info.rs @@ -1,11 +1,8 @@ use std::fmt::Display; use super::RepoPath; -use crate::{ - error::Result, - sync::{commit_details::get_author_of_commit, repository::repo}, -}; -use git2::{Commit, Error, Oid}; +use crate::{error::Result, sync::repository::repo}; +use git2::Oid; use scopetime::scope_time; use unicode_truncate::UnicodeTruncateStr; @@ -84,6 +81,21 @@ impl From for CommitId { } } +impl From for CommitId { + fn from(object_id: gix::ObjectId) -> Self { + #[allow(clippy::expect_used)] + let oid = Oid::from_bytes(object_id.as_bytes()).expect("`Oid::from_bytes(object_id.as_bytes())` is expected to never fail"); + + Self::new(oid) + } +} + +impl From for gix::ObjectId { + fn from(id: CommitId) -> Self { + Self::from_bytes_or_panic(id.0.as_bytes()) + } +} + /// #[derive(Debug, Clone)] pub struct CommitInfo { @@ -105,34 +117,36 @@ pub fn get_commits_info( ) -> Result> { scope_time!("get_commits_info"); - let repo = repo(repo_path)?; - let mailmap = repo.mailmap()?; - - let commits = ids - .iter() - .map(|id| repo.find_commit((*id).into())) - .collect::, Error>>()? - .into_iter(); - - let res = commits - .map(|c: Commit| { - let message = get_message(&c, Some(message_length_limit)); - let author = get_author_of_commit(&c, &mailmap) - .name() - .map_or_else( - || String::from(""), - String::from, - ); - CommitInfo { + let repo: gix::Repository = + gix::ThreadSafeRepository::discover_with_environment_overrides(repo_path.gitpath()) + .map(Into::into)?; + let mailmap = repo.open_mailmap(); + + ids.iter() + .map(|id| -> Result<_> { + let commit = repo.find_commit(*id)?; + let commit_ref = commit.decode()?; + + let message = gix_get_message( + &commit_ref, + Some(message_length_limit), + ); + + let author = commit_ref.author(); + + let author = mailmap.try_resolve(author).map_or_else( + || author.name.into(), + |signature| signature.name, + ); + + Ok(CommitInfo { message, - author, - time: c.time().seconds(), - id: CommitId(c.id()), - } + author: author.to_string(), + time: commit_ref.time().seconds, + id: *id, + }) }) - .collect::>(); - - Ok(res) + .collect() } /// @@ -142,24 +156,35 @@ pub fn get_commit_info( ) -> Result { scope_time!("get_commit_info"); - let repo = repo(repo_path)?; - let mailmap = repo.mailmap()?; + let repo: gix::Repository = + gix::ThreadSafeRepository::discover_with_environment_overrides(repo_path.gitpath()) + .map(Into::into)?; + let mailmap = repo.open_mailmap(); - let commit = repo.find_commit((*commit_id).into())?; - let author = get_author_of_commit(&commit, &mailmap); + let commit = repo.find_commit(*commit_id)?; + let commit_ref = commit.decode()?; + + let message = gix_get_message(&commit_ref, None); + + let author = commit_ref.author(); + + let author = mailmap.try_resolve(author).map_or_else( + || author.name.into(), + |signature| signature.name, + ); Ok(CommitInfo { - message: commit.message().unwrap_or("").into(), - author: author.name().unwrap_or("").into(), - time: commit.time().seconds(), - id: CommitId(commit.id()), + message, + author: author.to_string(), + time: commit_ref.time().seconds, + id: commit.id().detach().into(), }) } /// if `message_limit` is set the message will be /// limited to the first line and truncated to fit pub fn get_message( - c: &Commit, + c: &git2::Commit, message_limit: Option, ) -> String { let msg = String::from_utf8_lossy(c.message_bytes()); @@ -174,6 +199,24 @@ pub fn get_message( ) } +/// if `message_limit` is set the message will be +/// limited to the first line and truncated to fit +pub fn gix_get_message( + commit_ref: &gix::objs::CommitRef, + message_limit: Option, +) -> String { + let message = commit_ref.message.to_string(); + let message = message.trim(); + + message_limit.map_or_else( + || message.to_string(), + |limit| { + let message = message.lines().next().unwrap_or_default(); + message.unicode_truncate(limit).0.to_string() + }, + ) +} + #[cfg(test)] mod tests { use super::get_commits_info; diff --git a/asyncgit/src/sync/logwalker.rs b/asyncgit/src/sync/logwalker.rs index b42447642c..3865368b0d 100644 --- a/asyncgit/src/sync/logwalker.rs +++ b/asyncgit/src/sync/logwalker.rs @@ -161,10 +161,7 @@ impl<'a> LogWalkerWithoutFilter<'a> { let mut count = 0_usize; while let Some(Ok(info)) = self.walk.next() { - let bytes = info.id.as_bytes(); - let commit_id: CommitId = Oid::from_bytes(bytes)?.into(); - - out.push(commit_id); + out.push(info.id.into()); count += 1;