Subject: [PATCH v2] pretty: add "%aL"|"%al|%cL|%cl" option to output local-part of email addresses

From: Prarit Bhargava <[email-masked]>
Date: Thu, 24 Oct 2019 08:53:32 -0400

In many projects the number of contributors is low enough that users know
each other and the full email address doesn't need to be displayed.
Displaying only the author's username saves a lot of columns on the screen.
For example displaying "prarit" instead of "[email-masked]" saves 11
columns.

Add a "%aL"|"%al|%cL|%cl" option that output the local-part of an email
address.

Also add tests for "%ae","%an", "%ce", and "%cn".

Changes in v2:
- Changed option to 'L' based on https://www.ietf.org/rfc/rfc2822.txt
  definition of 'local-part' of email addresses.
- added additional information to documentation for %cL and %cl
- added mailmap output test
- modified code to use mailmap output for "L" option
- modified code to check if email address field has '@' symbol
- modified tests based on input from Peff

Signed-off-by: Prarit Bhargava <[email-masked]>
Cc: Junio C Hamano <[email-masked]>
Cc: brian m. carlson <[email-masked]>
Cc: Jeff King <[email-masked]>
---
 Documentation/pretty-formats.txt | 10 ++++++++++
 pretty.c                         |  9 ++++++++-
 t/t4202-log.sh                   | 30 ++++++++++++++++++++++++++++++
 t/test-lib.sh                    | 10 ++++++++--
 4 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index b87e2e83e6d0..9a1f900f114a 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -163,6 +163,11 @@ The placeholders are:
 '%ae':: author email
 '%aE':: author email (respecting .mailmap, see linkgit:git-shortlog[1]
 	or linkgit:git-blame[1])
+'%al':: author local-part (the portion of the email address preceding the '@'
+	symbol)
+'%aL':: author local-part (the portion of the email address preceding the '@'
+	symbol, respecting .mailmap, see linkgit:git-shortlog[1] or
+	linkgit:git-blame[1])
 '%ad':: author date (format respects --date= option)
 '%aD':: author date, RFC2822 style
 '%ar':: author date, relative
@@ -175,6 +180,11 @@ The placeholders are:
 '%ce':: committer email
 '%cE':: committer email (respecting .mailmap, see
 	linkgit:git-shortlog[1] or linkgit:git-blame[1])
+'%cl':: author local-part (the portion of the email address preceding the '@'
+	symbol)
+'%cL':: author local-part (the portion of the email address preceding the '@'
+	symbol, respecting .mailmap, see linkgit:git-shortlog[1] or
+	linkgit:git-blame[1])
 '%cd':: committer date (format respects --date= option)
 '%cD':: committer date, RFC2822 style
 '%cr':: committer date, relative
diff --git a/pretty.c b/pretty.c
index b32f0369531c..93eb6e837071 100644
--- a/pretty.c
+++ b/pretty.c
@@ -696,7 +696,7 @@ static size_t format_person_part(struct strbuf *sb, char part,
 	mail = s.mail_begin;
 	maillen = s.mail_end - s.mail_begin;
 
-	if (part == 'N' || part == 'E') /* mailmap lookup */
+	if (part == 'N' || part == 'E' || part == 'L') /* mailmap lookup */
 		mailmap_name(&mail, &maillen, &name, &namelen);
 	if (part == 'n' || part == 'N') {	/* name */
 		strbuf_add(sb, name, namelen);
@@ -706,6 +706,13 @@ static size_t format_person_part(struct strbuf *sb, char part,
 		strbuf_add(sb, mail, maillen);
 		return placeholder_len;
 	}
+	if (part == 'l' || part == 'L') {	/* local-part */
+		const char *at = memchr(mail, '@', maillen);
+		if (at)
+			maillen = at - mail;
+		strbuf_add(sb, mail, maillen);
+		return placeholder_len;
+	}
 
 	if (!s.date_begin)
 		goto skip;
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index e803ba402e9e..fa6ecf3588b7 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -1729,4 +1729,34 @@ test_expect_success 'log --end-of-options' '
        test_cmp expect actual
 '
 
+test_expect_success 'log pretty %an %ae %al %aN %aE %aL' '
+	git checkout -b anaeal &&
+	test_commit anaeal_test anaeal_test_file &&
+	git log --pretty="%an%n%ae%n%al%n%aN%n%aE%n%aL" -1 > actual &&
+	{
+		echo "${GIT_AUTHOR_NAME}" &&
+		echo "${GIT_AUTHOR_EMAIL}" &&
+		echo "${TEST_AUTHOR_LOCALNAME}"
+		echo "${GIT_AUTHOR_NAME}" &&
+		echo "${GIT_AUTHOR_EMAIL}" &&
+		echo "${TEST_AUTHOR_LOCALNAME}"
+	} > expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log pretty %cn %ce %cl %cN %cE %cL' '
+	git checkout -b cncecl &&
+	test_commit cncecl_test cncecl_test_file &&
+	git log --pretty="%cn%n%ce%n%cl%n%cN%n%cE%n%cL" -1 > actual &&
+	{
+		echo "${GIT_COMMITTER_NAME}" &&
+		echo "${GIT_COMMITTER_EMAIL}" &&
+		echo "${TEST_COMMITTER_LOCALNAME}"
+		echo "${GIT_COMMITTER_NAME}" &&
+		echo "${GIT_COMMITTER_EMAIL}" &&
+		echo "${TEST_COMMITTER_LOCALNAME}"
+	} > expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e06fa02a0eec..5ef0ad8c1a2a 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -404,14 +404,20 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
 unset XDG_CACHE_HOME
 unset XDG_CONFIG_HOME
 unset GITPERLLIB
-GIT_AUTHOR_EMAIL=[email-masked]
+TEST_AUTHOR_LOCALNAME=author
+TEST_AUTHOR_DOMAIN=example.com
+GIT_AUTHOR_EMAIL=${TEST_AUTHOR_LOCALNAME}@${TEST_AUTHOR_DOMAIN}
 GIT_AUTHOR_NAME='A U Thor'
-GIT_COMMITTER_EMAIL=[email-masked]
+TEST_COMMITTER_LOCALNAME=committer
+TEST_COMMITTER_DOMAIN=example.com
+GIT_COMMITTER_EMAIL=${TEST_COMMITTER_LOCALNAME}@${TEST_COMMITTER_DOMAIN}
 GIT_COMMITTER_NAME='C O Mitter'
 GIT_MERGE_VERBOSITY=5
 GIT_MERGE_AUTOEDIT=no
 export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT
+export TEST_AUTHOR_LOCALNAME TEST_AUTHOR_DOMAIN
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
+export TEST_COMMITTER_LOCALNAME TEST_COMMITTER_DOMAIN
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR
 
-- 
2.21.0