Skip to content

Commit

Permalink
Merge branch 'fix-mailmap'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Jun 23, 2024
2 parents 78b8e41 + 3e08fa3 commit f107014
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 5 deletions.
58 changes: 58 additions & 0 deletions gitoxide-core/src/repository/mailmap.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use anyhow::bail;
use gix::bstr::{BString, ByteSlice};
use std::io;

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -50,3 +52,59 @@ pub fn entries(

Ok(())
}

pub fn check(
repo: gix::Repository,
format: OutputFormat,
contacts: Vec<BString>,
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(())
}
19 changes: 17 additions & 2 deletions gix-mailmap/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,37 @@ impl<'a> Entry<'a> {
/// Constructors indicating what kind of mapping is created.
///
/// Only these combinations of values are valid.
#[allow(missing_docs)]
impl<'a> Entry<'a> {
/// An entry that changes the name by an email.
pub fn change_name_by_email(proper_name: impl Into<&'a BStr>, commit_email: impl Into<&'a BStr>) -> Self {
Entry {
new_name: Some(proper_name.into()),
old_email: commit_email.into(),
..Default::default()
}
}
/// An entry that changes the email by an email.
pub fn change_email_by_email(proper_email: impl Into<&'a BStr>, commit_email: impl Into<&'a BStr>) -> Self {
Entry {
new_email: Some(proper_email.into()),
old_email: commit_email.into(),
..Default::default()
}
}
/// An entry that changes the email by a name and email.
pub fn change_email_by_name_and_email(
proper_email: impl Into<&'a BStr>,
commit_name: impl Into<&'a BStr>,
commit_email: impl Into<&'a BStr>,
) -> Self {
Entry {
new_email: Some(proper_email.into()),
old_email: commit_email.into(),
old_name: Some(commit_name.into()),
..Default::default()
}
}
/// An entry that changes a name and the email by an email.
pub fn change_name_and_email_by_email(
proper_name: impl Into<&'a BStr>,
proper_email: impl Into<&'a BStr>,
Expand All @@ -53,7 +68,7 @@ impl<'a> Entry<'a> {
..Default::default()
}
}

/// An entry that changes a name and email by a name and email.
pub fn change_name_and_email_by_name_and_email(
proper_name: impl Into<&'a BStr>,
proper_email: impl Into<&'a BStr>,
Expand Down
3 changes: 3 additions & 0 deletions gix-mailmap/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ fn parse_line(line: &BStr, line_number: usize) -> Result<Entry<'_>, Error> {
(Some(proper_name), Some(proper_email), Some(commit_name), Some(commit_email)) => {
Entry::change_name_and_email_by_name_and_email(proper_name, proper_email, commit_name, commit_email)
}
(None, Some(proper_email), Some(commit_name), Some(commit_email)) => {
Entry::change_email_by_name_and_email(proper_email, commit_name, commit_email)
}
_ => {
return Err(Error::Malformed {
line_number,
Expand Down
1 change: 1 addition & 0 deletions gix-mailmap/tests/fixtures/typical.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Joe R. Developer <joe@example.com> Joe <bugs@example.com>
Jane Doe <jane@example.com> <jane@laptop.(none)>
Jane Doe <jane@example.com> <jane@desktop.(none)>
Jane Doe <jane@example.com> Jane <bugs@example.com>
<jane@example.com> Jane <Jane@ipad.(none)>
5 changes: 3 additions & 2 deletions gix-mailmap/tests/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fn a_typical_mailmap() {
Entry::change_name_and_email_by_email("Jane Doe", "jane@example.com", "jane@laptop.(none)"),
Entry::change_name_and_email_by_email("Jane Doe", "jane@example.com", "jane@desktop.(none)"),
Entry::change_name_and_email_by_name_and_email("Jane Doe", "jane@example.com", "Jane", "bugs@example.com"),
Entry::change_email_by_name_and_email("jane@example.com", "Jane", "Jane@ipad.(none)"),
]
);
}
Expand Down Expand Up @@ -76,8 +77,8 @@ fn valid_entries() {
Entry::change_name_and_email_by_email("proper name", "proper email", "commit-email")
);
assert_eq!(
line(" proper name <proper email>\tcommit name\t<commit-email>\t"),
Entry::change_name_and_email_by_name_and_email("proper name", "proper email", "commit name", "commit-email")
line("<proper-email> commit name <commit-email>"),
Entry::change_email_by_name_and_email("proper-email", "commit name", "commit-email")
);
}

Expand Down
7 changes: 6 additions & 1 deletion gix-mailmap/tests/snapshot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ fn try_resolve() {
Some(signature("Jane Doe", "jane@example.com")),
"name and email can be mapped specifically, case insensitive matching of name"
);
assert_eq!(
snapshot.resolve(signature("janE", "jane@ipad.(none)").to_ref()),
signature("janE", "jane@example.com"),
"an email can be mapped by name and email specifically, both match case-insensitively"
);

let sig = signature("Jane", "other@example.com");
assert_eq!(snapshot.try_resolve(sig.to_ref()), None, "unmatched email");
Expand All @@ -49,7 +54,7 @@ fn try_resolve() {
);
assert_eq!(snapshot.resolve(sig.to_ref()), sig);

assert_eq!(snapshot.entries().len(), 5);
assert_eq!(snapshot.entries().len(), 6);
}

#[test]
Expand Down
11 changes: 11 additions & 0 deletions src/plumbing/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
7 changes: 7 additions & 0 deletions src/plumbing/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 the canonical form of contacts according to the configured mailmaps.
Check {
/// One or more `Name <email>` or `<email>` to pass through the mailmap.
contacts: Vec<BString>,
},
}
}

Expand Down

0 comments on commit f107014

Please sign in to comment.