cat-file: add mailmap support

git-cat-file is used by tools like GitLab to get commit tag contents that are then displayed to users. This content which has author, committer or tagger information, could benefit from passing through the mailmap mechanism before being sent or displayed. This patch adds --[no-]use-mailmap command line option to the git cat-file command. It also adds --[no-]mailmap option as an alias to --[no-]use-mailmap. This patch also introduces new test cases to test the mailmap mechanism in git cat-file command. Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: John Cai <johncai86@gmail.com> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Siddharth Asthana <siddharthasthana31@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Siddharth Asthana committed Jul 19, 2022 at 01:21 UTC ec031da9f97a2545601304b5ac1e93fee09425b4
3 files changed +107 -1
Documentation/git-cat-file.txt
+6
@@ -63,6 +63,12 @@ OPTIONS
63 or to ask for a "blob" with `<object>` being a tag object that
64 points at it.
65
66 +--[no-]mailmap::
67 +--[no-]use-mailmap::
68 + Use mailmap file to map author, committer and tagger names
69 + and email addresses to canonical real names and email addresses.
70 + See linkgit:git-shortlog[1].
71 +
72 --textconv::
73 Show the content as transformed by a textconv filter. In this case,
74 `<object>` has to be of the form `<tree-ish>:<path>`, or `:<path>` in
builtin/cat-file.c
+42 -1
@@ -16,6 +16,7 @@
16 #include "packfile.h"
17 #include "object-store.h"
18 #include "promisor-remote.h"
19 +#include "mailmap.h"
20
21 enum batch_mode {
22 BATCH_MODE_CONTENTS,
@@ -36,6 +37,22 @@ struct batch_options {
37
38 static const char *force_path;
39
40 +static struct string_list mailmap = STRING_LIST_INIT_NODUP;
41 +static int use_mailmap;
42 +
43 +static char *replace_idents_using_mailmap(char *, size_t *);
44 +
45 +static char *replace_idents_using_mailmap(char *object_buf, size_t *size)
46 +{
47 + struct strbuf sb = STRBUF_INIT;
48 + const char *headers[] = { "author ", "committer ", "tagger ", NULL };
49 +
50 + strbuf_attach(&sb, object_buf, *size, *size + 1);
51 + apply_mailmap_to_header(&sb, headers, &mailmap);
52 + *size = sb.len;
53 + return strbuf_detach(&sb, NULL);
54 +}
55 +
56 static int filter_object(const char *path, unsigned mode,
57 const struct object_id *oid,
58 char **buf, unsigned long *size)
@@ -152,6 +169,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
169 if (!buf)
170 die("Cannot read object %s", obj_name);
171
172 + if (use_mailmap) {
173 + size_t s = size;
174 + buf = replace_idents_using_mailmap(buf, &s);
175 + size = cast_size_t_to_ulong(s);
176 + }
177 +
178 /* otherwise just spit out the data */
179 break;
180
@@ -183,6 +206,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
206 }
207 buf = read_object_with_reference(the_repository, &oid,
208 exp_type_id, &size, NULL);
209 +
210 + if (use_mailmap) {
211 + size_t s = size;
212 + buf = replace_idents_using_mailmap(buf, &s);
213 + size = cast_size_t_to_ulong(s);
214 + }
215 break;
216 }
217 default:
@@ -348,11 +377,18 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
377 void *contents;
378
379 contents = read_object_file(oid, &type, &size);
380 +
381 + if (use_mailmap) {
382 + size_t s = size;
383 + contents = replace_idents_using_mailmap(contents, &s);
384 + size = cast_size_t_to_ulong(s);
385 + }
386 +
387 if (!contents)
388 die("object %s disappeared", oid_to_hex(oid));
389 if (type != data->type)
390 die("object %s changed type!?", oid_to_hex(oid));
355 - if (data->info.sizep && size != data->size)
391 + if (data->info.sizep && size != data->size && !use_mailmap)
392 die("object %s changed size!?", oid_to_hex(oid));
393
394 batch_write(opt, contents, size);
@@ -843,6 +879,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
879 OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
880 OPT_BOOL(0, "allow-unknown-type", &unknown_type,
881 N_("allow -s and -t to work with broken/corrupt objects")),
882 + OPT_BOOL(0, "use-mailmap", &use_mailmap, N_("use mail map file")),
883 + OPT_ALIAS(0, "mailmap", "use-mailmap"),
884 /* Batch mode */
885 OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
886 OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
@@ -885,6 +923,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
923 opt_cw = (opt == 'c' || opt == 'w');
924 opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
925
926 + if (use_mailmap)
927 + read_mailmap(&mailmap);
928 +
929 /* --batch-all-objects? */
930 if (opt == 'b')
931 batch.all_objects = 1;
t/t4203-mailmap.sh
+59
@@ -963,4 +963,63 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
963 test_cmp expect actual
964 '
965
966 +test_expect_success 'prepare for cat-file --mailmap' '
967 + rm -f .mailmap &&
968 + git commit --allow-empty -m foo --author="Orig <orig@example.com>"
969 +'
970 +
971 +test_expect_success '--no-use-mailmap disables mailmap in cat-file' '
972 + test_when_finished "rm .mailmap" &&
973 + cat >.mailmap <<-EOF &&
974 + A U Thor <author@example.com> Orig <orig@example.com>
975 + EOF
976 + cat >expect <<-EOF &&
977 + author Orig <orig@example.com>
978 + EOF
979 + git cat-file --no-use-mailmap commit HEAD >log &&
980 + sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
981 + test_cmp expect actual
982 +'
983 +
984 +test_expect_success '--use-mailmap enables mailmap in cat-file' '
985 + test_when_finished "rm .mailmap" &&
986 + cat >.mailmap <<-EOF &&
987 + A U Thor <author@example.com> Orig <orig@example.com>
988 + EOF
989 + cat >expect <<-EOF &&
990 + author A U Thor <author@example.com>
991 + EOF
992 + git cat-file --use-mailmap commit HEAD >log &&
993 + sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
994 + test_cmp expect actual
995 +'
996 +
997 +test_expect_success '--no-mailmap disables mailmap in cat-file for annotated tag objects' '
998 + test_when_finished "rm .mailmap" &&
999 + cat >.mailmap <<-EOF &&
1000 + Orig <orig@example.com> C O Mitter <committer@example.com>
1001 + EOF
1002 + cat >expect <<-EOF &&
1003 + tagger C O Mitter <committer@example.com>
1004 + EOF
1005 + git tag -a -m "annotated tag" v1 &&
1006 + git cat-file --no-mailmap -p v1 >log &&
1007 + sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
1008 + test_cmp expect actual
1009 +'
1010 +
1011 +test_expect_success '--mailmap enables mailmap in cat-file for annotated tag objects' '
1012 + test_when_finished "rm .mailmap" &&
1013 + cat >.mailmap <<-EOF &&
1014 + Orig <orig@example.com> C O Mitter <committer@example.com>
1015 + EOF
1016 + cat >expect <<-EOF &&
1017 + tagger Orig <orig@example.com>
1018 + EOF
1019 + git tag -a -m "annotated tag" v2 &&
1020 + git cat-file --mailmap -p v2 >log &&
1021 + sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
1022 + test_cmp expect actual
1023 +'
1024 +
1025 test_done