diff --git a/CHANGELOG.md b/CHANGELOG.md index b972a53c8a..2ab47d09e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * use OSC52 copying in case other methods fail [[@naseschwarz](https://github.com/naseschwarz)] ([#2366](https://github.com/gitui-org/gitui/issues/2366)) * push: respect `branch.*.merge` when push default is upstream [[@vlad-anger](https://github.com/vlad-anger)] ([#2542](https://github.com/gitui-org/gitui/pull/2542)) * set the terminal title to `gitui ({repo_path})` [[@acuteenvy](https://github.com/acuteenvy)] ([#2462](https://github.com/gitui-org/gitui/issues/2462)) +* respect `.mailmap` [[@acuteenvy](https://github.com/acuteenvy)] ([#2406](https://github.com/gitui-org/gitui/issues/2406)) ## [0.27.0] - 2024-01-14 diff --git a/asyncgit/src/sync/commit_details.rs b/asyncgit/src/sync/commit_details.rs index b70bd52fd6..086272110c 100644 --- a/asyncgit/src/sync/commit_details.rs +++ b/asyncgit/src/sync/commit_details.rs @@ -87,6 +87,42 @@ impl CommitDetails { } } +/// Get the author of a commit. +pub fn get_author_of_commit<'a>( + commit: &'a git2::Commit<'a>, + mailmap: &git2::Mailmap, +) -> git2::Signature<'a> { + match commit.author_with_mailmap(mailmap) { + Ok(author) => author, + Err(e) => { + log::error!( + "Couldn't get author with mailmap for {} (message: {:?}): {e}", + commit.id(), + commit.message(), + ); + commit.author() + } + } +} + +/// Get the committer of a commit. +pub fn get_committer_of_commit<'a>( + commit: &'a git2::Commit<'a>, + mailmap: &git2::Mailmap, +) -> git2::Signature<'a> { + match commit.committer_with_mailmap(mailmap) { + Ok(committer) => committer, + Err(e) => { + log::error!( + "Couldn't get committer with mailmap for {} (message: {:?}): {e}", + commit.id(), + commit.message(), + ); + commit.committer() + } + } +} + /// pub fn get_commit_details( repo_path: &RepoPath, @@ -95,11 +131,17 @@ pub fn get_commit_details( scope_time!("get_commit_details"); let repo = repo(repo_path)?; + let mailmap = repo.mailmap()?; let commit = repo.find_commit(id.into())?; - let author = CommitSignature::from(&commit.author()); - let committer = CommitSignature::from(&commit.committer()); + let author = CommitSignature::from(&get_author_of_commit( + &commit, &mailmap, + )); + let committer = CommitSignature::from(&get_committer_of_commit( + &commit, &mailmap, + )); + let committer = if author == committer { None } else { diff --git a/asyncgit/src/sync/commit_filter.rs b/asyncgit/src/sync/commit_filter.rs index e8c7f36771..f4b3e8a6c6 100644 --- a/asyncgit/src/sync/commit_filter.rs +++ b/asyncgit/src/sync/commit_filter.rs @@ -1,4 +1,7 @@ -use super::{commit_files::get_commit_diff, CommitId}; +use super::{ + commit_details::get_author_of_commit, + commit_files::get_commit_diff, CommitId, +}; use crate::error::Result; use bitflags::bitflags; use fuzzy_matcher::FuzzyMatcher; @@ -159,6 +162,7 @@ pub fn filter_commit_by_search( move |repo: &Repository, commit_id: &CommitId| -> Result { + let mailmap = repo.mailmap()?; let commit = repo.find_commit((*commit_id).into())?; let msg_summary_match = filter @@ -199,16 +203,15 @@ pub fn filter_commit_by_search( .fields .contains(SearchFields::AUTHORS) .then(|| { - let name_match = commit - .author() - .name() - .is_some_and(|name| filter.match_text(name)); - let mail_match = commit - .author() - .email() - .is_some_and(|name| filter.match_text(name)); - - name_match || mail_match + let author = + get_author_of_commit(&commit, &mailmap); + [author.email(), author.name()].iter().any( + |opt_haystack| { + opt_haystack.is_some_and(|haystack| { + filter.match_text(haystack) + }) + }, + ) }) .unwrap_or_default(); diff --git a/asyncgit/src/sync/commits_info.rs b/asyncgit/src/sync/commits_info.rs index f168df1efb..df426249e7 100644 --- a/asyncgit/src/sync/commits_info.rs +++ b/asyncgit/src/sync/commits_info.rs @@ -1,7 +1,10 @@ use std::fmt::Display; use super::RepoPath; -use crate::{error::Result, sync::repository::repo}; +use crate::{ + error::Result, + sync::{commit_details::get_author_of_commit, repository::repo}, +}; use git2::{Commit, Error, Oid}; use scopetime::scope_time; use unicode_truncate::UnicodeTruncateStr; @@ -91,6 +94,7 @@ pub fn get_commits_info( scope_time!("get_commits_info"); let repo = repo(repo_path)?; + let mailmap = repo.mailmap()?; let commits = ids .iter() @@ -101,10 +105,12 @@ pub fn get_commits_info( let res = commits .map(|c: Commit| { let message = get_message(&c, Some(message_length_limit)); - let author = c.author().name().map_or_else( - || String::from(""), - String::from, - ); + let author = get_author_of_commit(&c, &mailmap) + .name() + .map_or_else( + || String::from(""), + String::from, + ); CommitInfo { message, author, @@ -125,9 +131,10 @@ pub fn get_commit_info( scope_time!("get_commit_info"); let repo = repo(repo_path)?; + let mailmap = repo.mailmap()?; let commit = repo.find_commit((*commit_id).into())?; - let author = commit.author(); + let author = get_author_of_commit(&commit, &mailmap); Ok(CommitInfo { message: commit.message().unwrap_or("").into(), @@ -189,6 +196,12 @@ mod tests { assert_eq!(res[0].author.as_str(), "name"); assert_eq!(res[1].message.as_str(), "commit1"); + File::create(root.join(".mailmap"))? + .write_all(b"new name ")?; + let res = get_commits_info(repo_path, &[c2], 50).unwrap(); + + assert_eq!(res[0].author.as_str(), "new name"); + Ok(()) }