diff --git a/gitoxide-core/src/repository/mailmap.rs b/gitoxide-core/src/repository/mailmap.rs index f9c947d8502..668ccd3c086 100644 --- a/gitoxide-core/src/repository/mailmap.rs +++ b/gitoxide-core/src/repository/mailmap.rs @@ -1,3 +1,5 @@ +use anyhow::bail; +use gix::bstr::{BString, ByteSlice}; use std::io; #[cfg(feature = "serde")] @@ -50,3 +52,59 @@ pub fn entries( Ok(()) } + +pub fn check( + repo: gix::Repository, + format: OutputFormat, + contacts: Vec, + mut out: impl io::Write, + mut err: impl io::Write, +) -> anyhow::Result<()> { + if format != OutputFormat::Human { + bail!("Only human output is supported right now"); + } + if contacts.is_empty() { + bail!("specify at least one contact to run through the mailmap") + } + + let mut mailmap = gix::mailmap::Snapshot::default(); + if let Err(err) = repo.open_mailmap_into(&mut mailmap) { + bail!(err); + } + + let mut buf = Vec::new(); + for contact in contacts { + let actor = match gix::actor::IdentityRef::from_bytes::<()>(&contact) { + Ok(a) => a, + Err(_) => { + let Some(email) = contact + .trim_start() + .strip_prefix(b"<") + .and_then(|rest| rest.trim_end().strip_suffix(b">")) + else { + writeln!(err, "Failed to parse contact '{contact}' - skipping")?; + continue; + }; + gix::actor::IdentityRef { + name: "".into(), + email: email.into(), + } + } + }; + let resolved = mailmap.resolve_cow(gix::actor::SignatureRef { + name: actor.name, + email: actor.email, + time: Default::default(), + }); + let resolved = gix::actor::IdentityRef { + name: resolved.name.as_ref(), + email: resolved.email.as_ref(), + }; + buf.clear(); + resolved.write_to(&mut buf)?; + + out.write_all(&buf)?; + out.write_all(b"\n")?; + } + Ok(()) +} diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index b4c788952f1..e1aa5335e51 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -1216,6 +1216,17 @@ pub fn main() -> Result<()> { core::repository::mailmap::entries(repository(Mode::Lenient)?, format, out, err) }, ), + mailmap::Subcommands::Check { contacts } => prepare_and_run( + "mailmap-check", + trace, + verbose, + progress, + progress_keep_open, + None, + move |_progress, out, err| { + core::repository::mailmap::check(repository(Mode::Lenient)?, format, contacts, out, err) + }, + ), }, Subcommands::Attributes(cmd) => match cmd { attributes::Subcommands::Query { statistics, pathspec } => prepare_and_run( diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index af1374241fb..721b6dd0b89 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -512,10 +512,17 @@ pub mod remote { } pub mod mailmap { + use gix::bstr::BString; + #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Print all entries in configured mailmaps, inform about errors as well. Entries, + /// Print all entries in configured mailmaps, inform about errors as well. + Check { + /// One or more `Name ` or `` to pass through the mailmap. + contacts: Vec, + }, } }