Skip to content

Commit d2388d8

Browse files
committed
feat: gix repository mailmap entries (#366)
1 parent 2a01f47 commit d2388d8

File tree

6 files changed

+75
-14
lines changed

6 files changed

+75
-14
lines changed

git-mailmap/src/snapshot.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ enum EncodedString {
3232
}
3333

3434
impl EncodedString {
35+
fn as_bstr(&self) -> &BStr {
36+
match self {
37+
EncodedString::Utf8(v) => v.as_str().into(),
38+
EncodedString::Unknown(v) => v.as_bstr(),
39+
}
40+
}
3541
fn cmp_ref(&self, other: EncodedStringRef<'_>) -> Ordering {
3642
match (self, other) {
3743
(EncodedString::Utf8(a), EncodedStringRef::Utf8(b)) => {
@@ -206,6 +212,34 @@ impl Snapshot {
206212
self
207213
}
208214

215+
/// Transform our acceleration structure into a list of entries.
216+
///
217+
/// Note that the order is different from how they were obtained initially, and are explicitly ordered by
218+
/// (old_email, old_name).
219+
pub fn entries(&self) -> Vec<crate::Entry<'_>> {
220+
let mut out = Vec::with_capacity(self.entries_by_old_email.len());
221+
for entry in &self.entries_by_old_email {
222+
if entry.new_email.is_some() || entry.new_name.is_some() {
223+
out.push(crate::Entry {
224+
new_name: entry.new_name.as_ref().map(|b| b.as_bstr()),
225+
new_email: entry.new_email.as_ref().map(|b| b.as_bstr()),
226+
old_name: None,
227+
old_email: entry.old_email.as_bstr(),
228+
});
229+
}
230+
231+
for name_entry in &entry.entries_by_old_name {
232+
out.push(crate::Entry {
233+
new_name: name_entry.new_name.as_ref().map(|b| b.as_bstr()),
234+
new_email: name_entry.new_email.as_ref().map(|b| b.as_bstr()),
235+
old_name: name_entry.old_name.as_bstr().into(),
236+
old_email: entry.old_email.as_bstr(),
237+
});
238+
}
239+
}
240+
out
241+
}
242+
209243
/// Try to resolve `signature` by its contained email and name and provide resolved/mapped names as reference.
210244
/// Return `None` if no such mapping was found.
211245
///

git-mailmap/tests/snapshot/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use git_testtools::fixture_bytes;
33

44
#[test]
55
fn try_resolve() {
6-
let snapshot = snapshot();
6+
let snapshot = Snapshot::from_bytes(&fixture_bytes("typical.txt"));
77
assert_eq!(
88
snapshot.try_resolve(&signature("Foo", "[email protected]").to_ref()),
99
Some(signature("Joe R. Developer", "[email protected]")),
@@ -48,6 +48,8 @@ fn try_resolve() {
4848
"matched email, unmatched name"
4949
);
5050
assert_eq!(snapshot.resolve(&sig.to_ref()), sig);
51+
52+
assert_eq!(snapshot.entries().len(), 5);
5153
}
5254

5355
#[test]
@@ -76,6 +78,8 @@ fn non_name_and_name_mappings_will_not_clash() {
7678
Some(signature("other-new-name", "other-new-email")),
7779
"it can match by email and name as well"
7880
);
81+
82+
assert_eq!(snapshot.entries().len(), 2);
7983
}
8084
}
8185

@@ -105,10 +109,8 @@ fn overwrite_entries() {
105109
Some(signature("unchanged", "new-d-email-overwritten")),
106110
"email by email"
107111
);
108-
}
109112

110-
fn snapshot() -> Snapshot {
111-
Snapshot::from_bytes(&fixture_bytes("typical.txt"))
113+
assert_eq!(snapshot.entries().len(), 4);
112114
}
113115

114116
fn signature(name: &str, email: &str) -> git_actor::Signature {

git-repository/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ one-stop-shop = [ "local", "local-time-support" ]
4141
#! ### Other
4242

4343
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
44-
serde1 = ["git-pack/serde1", "git-object/serde1", "git-protocol/serde1", "git-transport/serde1", "git-ref/serde1", "git-odb/serde1", "git-index/serde1"]
44+
serde1 = ["git-pack/serde1", "git-object/serde1", "git-protocol/serde1", "git-transport/serde1", "git-ref/serde1", "git-odb/serde1", "git-index/serde1", "git-mailmap/serde1"]
4545
## Activate other features that maximize performance, like usage of threads, `zlib-ng` and access to caching in object databases.
4646
## **Note** that
4747
max-performance = ["git-features/parallel", "git-features/zlib-ng-compat", "git-pack/pack-cache-lru-static", "git-pack/pack-cache-lru-dynamic"]

git-repository/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,11 @@ pub mod mailmap {
247247

248248
///
249249
pub mod load {
250-
/// The error returned by [`crate::Repository::load_mailmap()`].
250+
/// The error returned by [`crate::Repository::load_mailmap_into()`].
251251
#[derive(Debug, thiserror::Error)]
252252
#[allow(missing_docs)]
253253
pub enum Error {
254-
#[error("A mailmap file could not be loaded from disk")]
254+
#[error("The mailmap file declared in `mailmap.file` could not be read")]
255255
Io(#[from] std::io::Error),
256256
#[error("The configured mailmap.blob could not be parsed")]
257257
BlobSpec(#[from] git_hash::decode::Error),

git-repository/src/repository/snapshots.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,7 @@ impl crate::Repository {
9696
{
9797
buf.clear();
9898
std::io::copy(&mut file, &mut buf)
99-
.map_err(|e| {
100-
if e.kind() != std::io::ErrorKind::NotFound {
101-
err.get_or_insert(e.into());
102-
}
103-
})
99+
.map_err(|e| err.get_or_insert(e.into()))
104100
.ok();
105101
target.merge(git_mailmap::parse_ignore_errors(&buf));
106102
}
Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
use crate::OutputFormat;
22
use git_repository as git;
3+
use git_repository::bstr::ByteSlice;
4+
use git_repository::mailmap::Entry;
35
use std::io;
46
use std::path::PathBuf;
57

8+
#[cfg(feature = "serde1")]
9+
#[cfg_attr(feature = "serde1", derive(serde::Serialize))]
10+
struct JsonEntry {
11+
new_name: Option<String>,
12+
new_email: Option<String>,
13+
old_name: Option<String>,
14+
old_email: String,
15+
}
16+
17+
#[cfg(feature = "serde1")]
18+
impl<'a> From<Entry<'a>> for JsonEntry {
19+
fn from(v: Entry<'a>) -> Self {
20+
JsonEntry {
21+
new_name: v.new_name().map(|s| s.to_str_lossy().into_owned()),
22+
new_email: v.new_email().map(|s| s.to_str_lossy().into_owned()),
23+
old_name: v.old_name().map(|s| s.to_str_lossy().into_owned()),
24+
old_email: v.old_email().to_str_lossy().into_owned(),
25+
}
26+
}
27+
}
28+
629
pub fn entries(
730
repository: PathBuf,
831
format: OutputFormat,
9-
mut out: impl io::Write,
32+
out: impl io::Write,
1033
mut err: impl io::Write,
1134
) -> anyhow::Result<()> {
1235
if format == OutputFormat::Human {
@@ -16,8 +39,14 @@ pub fn entries(
1639
let repo = git::open(repository)?.apply_environment();
1740
let mut mailmap = git::mailmap::Snapshot::default();
1841
if let Err(e) = repo.load_mailmap_into(&mut mailmap) {
19-
writeln!(err, "Error while loading mailmap: {}", e).ok();
42+
writeln!(err, "Error while loading mailmap, the first error is: {}", e).ok();
2043
}
2144

45+
#[cfg(feature = "serde1")]
46+
serde_json::to_writer_pretty(
47+
out,
48+
&mailmap.entries().into_iter().map(JsonEntry::from).collect::<Vec<_>>(),
49+
)?;
50+
2251
Ok(())
2352
}

0 commit comments

Comments
 (0)