diff mbox series

[PoC] mailmap and trailers

Message ID YW8A5FznqLYs7MqH@coredump.intra.peff.net (mailing list archive)
State New, archived
Headers show
Series [PoC] mailmap and trailers | expand

Commit Message

Jeff King Oct. 19, 2021, 5:31 p.m. UTC
The notion came up at the virtual contrib summit that we mailmap
author/committer names, but not usually idents in trailers. We do for
"git shortlog --group=trailer", but not in say, "git log", which is
where most people will see them.

So here's a proof-of-concept at mapping idents in trailers for git-log.
It does enough that you can try:

  $ git log --format='%(trailers)' >orig
  $ git log --format='%(trailers:mailmap)' >mapped
  $ diff -u orig mapped

which does show the expected differences for git.git (e.g., people's
Signed-off-by trailers getting adjusted for their current emails).

One of the main things I wondered was the speed difference. The first
command runs in about 0.5s for me, and the second in about 3.5s. So
there's a pretty big cost, but perhaps acceptable in the context of
interactive use. I didn't profile much. I'd guess some of the time is
just that parsing and handling individual trailers at all is a bit slow
(so it's on par with %(trailers:unfold), etc).

What's here should work reliably, I think, but is pretty incomplete:

  - it only works for %(trailers). We'd probably want a
    "--mailmap-trailers" options to affect --pretty=medium, etc. That
    code would probably go in pretty.c:pp_remainder()

  - docs/tests conspicuously missing

  - there's some ugliness with the ownership of the mailmap data
    structure itself. I followed the existing pattern there. At the very
    least these two spots should use the _same_ static string_list, but
    probably it should be refactored to put it into pretty_print_context
    or something.

Anyway, here it is. I'm not sure if I'll push it forward more, but
certainly anybody interested is welcome to pick it up and run with it. I
mostly wanted to show how much and where such code would be, and see how
it performed.

---

Comments

Junio C Hamano Oct. 20, 2021, 7:21 p.m. UTC | #1
Jeff King <peff@peff.net> writes:

> What's here should work reliably, I think, but is pretty incomplete:
>
>   - it only works for %(trailers). We'd probably want a
>     "--mailmap-trailers" options to affect --pretty=medium, etc. That
>     code would probably go in pretty.c:pp_remainder()

Yup, that would make it much more useful.

> Anyway, here it is. I'm not sure if I'll push it forward more, but
> certainly anybody interested is welcome to pick it up and run with it. I
> mostly wanted to show how much and where such code would be, and see how
> it performed.

Nice.

Thanks.
diff mbox series

Patch

diff --git a/pretty.c b/pretty.c
index 73b5ead509..0afa70058f 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1167,6 +1167,7 @@  int format_set_trailers_options(struct process_trailer_options *opts,
 	for (;;) {
 		const char *argval;
 		size_t arglen;
+		int bool_arg;
 
 		if (**arg == ')')
 			break;
@@ -1200,6 +1201,15 @@  int format_set_trailers_options(struct process_trailer_options *opts,
 			strbuf_expand(kvsepbuf, fmt, strbuf_expand_literal_cb, NULL);
 			free(fmt);
 			opts->key_value_separator = kvsepbuf;
+		} else if (match_placeholder_bool_arg(*arg, "mailmap", arg, &bool_arg)) {
+			if (bool_arg) {
+				/* yuck but this is how mailmap_name() above does it */
+				static struct string_list mailmap = STRING_LIST_INIT_DUP;
+				read_mailmap(&mailmap);
+				opts->mailmap = &mailmap;
+			} else {
+				opts->mailmap = NULL;
+			}
 		} else if (!match_placeholder_bool_arg(*arg, "only", arg, &opts->only_trailers) &&
 			   !match_placeholder_bool_arg(*arg, "unfold", arg, &opts->unfold) &&
 			   !match_placeholder_bool_arg(*arg, "keyonly", arg, &opts->key_only) &&
diff --git a/trailer.c b/trailer.c
index 7c7cb61a94..743fdd506d 100644
--- a/trailer.c
+++ b/trailer.c
@@ -6,6 +6,8 @@ 
 #include "tempfile.h"
 #include "trailer.h"
 #include "list.h"
+#include "mailmap.h"
+
 /*
  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
  */
@@ -1138,6 +1140,29 @@  void trailer_info_release(struct trailer_info *info)
 	free(info->trailers);
 }
 
+static int mailmap_value(struct string_list *mailmap,
+			 struct strbuf *out, const struct strbuf *in)
+{
+	const char *mailbuf, *namebuf;
+	size_t namelen, maillen;
+	struct ident_split ident;
+
+	if (split_ident_line(&ident, in->buf, in->len))
+		return -1; /* not an ident */
+
+	namebuf = ident.name_begin;
+	namelen = ident.name_end - ident.name_begin;
+	mailbuf = ident.mail_begin;
+	maillen = ident.mail_end - ident.mail_begin;
+
+	map_user(mailmap, &mailbuf, &maillen, &namebuf, &namelen);
+	strbuf_add(out, namebuf, namelen);
+	strbuf_addstr(out, " <");
+	strbuf_add(out, mailbuf, maillen);
+	strbuf_addch(out, '>');
+	return 0;
+}
+
 static void format_trailer_info(struct strbuf *out,
 				const struct trailer_info *info,
 				const struct process_trailer_options *opts)
@@ -1148,7 +1173,7 @@  static void format_trailer_info(struct strbuf *out,
 	/* If we want the whole block untouched, we can take the fast path. */
 	if (!opts->only_trailers && !opts->unfold && !opts->filter &&
 	    !opts->separator && !opts->key_only && !opts->value_only &&
-	    !opts->key_value_separator) {
+	    !opts->key_value_separator && !opts->mailmap) {
 		strbuf_add(out, info->trailer_start,
 			   info->trailer_end - info->trailer_start);
 		return;
@@ -1177,8 +1202,11 @@  static void format_trailer_info(struct strbuf *out,
 					else
 						strbuf_addstr(out, ": ");
 				}
-				if (!opts->key_only)
-					strbuf_addbuf(out, &val);
+				if (!opts->key_only) {
+					if (!opts->mailmap ||
+					    mailmap_value(opts->mailmap, out, &val) < 0)
+						strbuf_addbuf(out, &val);
+				}
 				if (!opts->separator)
 					strbuf_addch(out, '\n');
 			}
diff --git a/trailer.h b/trailer.h
index 795d2fccfd..747d2a6bc6 100644
--- a/trailer.h
+++ b/trailer.h
@@ -73,6 +73,7 @@  struct process_trailer_options {
 	int no_divider;
 	int key_only;
 	int value_only;
+	struct string_list *mailmap;
 	const struct strbuf *separator;
 	const struct strbuf *key_value_separator;
 	int (*filter)(const struct strbuf *, void *);