Skip to content

Commit 87098a0

Browse files
committed
Merge branch 'sa/cat-file-mailmap'
"git cat-file" learned an option to use the mailmap when showing commit and tag objects. * sa/cat-file-mailmap: cat-file: add mailmap support ident: rename commit_rewrite_person() to apply_mailmap_to_header() ident: move commit_rewrite_person() to ident.c revision: improve commit_rewrite_person()
2 parents 8e56aff + ec031da commit 87098a0

File tree

6 files changed

+190
-48
lines changed

6 files changed

+190
-48
lines changed

Documentation/git-cat-file.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ OPTIONS
6363
or to ask for a "blob" with `<object>` being a tag object that
6464
points at it.
6565

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+
6672
--textconv::
6773
Show the content as transformed by a textconv filter. In this case,
6874
`<object>` has to be of the form `<tree-ish>:<path>`, or `:<path>` in

builtin/cat-file.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "packfile.h"
1717
#include "object-store.h"
1818
#include "promisor-remote.h"
19+
#include "mailmap.h"
1920

2021
enum batch_mode {
2122
BATCH_MODE_CONTENTS,
@@ -36,6 +37,22 @@ struct batch_options {
3637

3738
static const char *force_path;
3839

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+
3956
static int filter_object(const char *path, unsigned mode,
4057
const struct object_id *oid,
4158
char **buf, unsigned long *size)
@@ -160,6 +177,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
160177
if (!buf)
161178
die("Cannot read object %s", obj_name);
162179

180+
if (use_mailmap) {
181+
size_t s = size;
182+
buf = replace_idents_using_mailmap(buf, &s);
183+
size = cast_size_t_to_ulong(s);
184+
}
185+
163186
/* otherwise just spit out the data */
164187
break;
165188

@@ -193,6 +216,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
193216
}
194217
buf = read_object_with_reference(the_repository, &oid,
195218
exp_type_id, &size, NULL);
219+
220+
if (use_mailmap) {
221+
size_t s = size;
222+
buf = replace_idents_using_mailmap(buf, &s);
223+
size = cast_size_t_to_ulong(s);
224+
}
196225
break;
197226
}
198227
default:
@@ -360,11 +389,18 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
360389
void *contents;
361390

362391
contents = read_object_file(oid, &type, &size);
392+
393+
if (use_mailmap) {
394+
size_t s = size;
395+
contents = replace_idents_using_mailmap(contents, &s);
396+
size = cast_size_t_to_ulong(s);
397+
}
398+
363399
if (!contents)
364400
die("object %s disappeared", oid_to_hex(oid));
365401
if (type != data->type)
366402
die("object %s changed type!?", oid_to_hex(oid));
367-
if (data->info.sizep && size != data->size)
403+
if (data->info.sizep && size != data->size && !use_mailmap)
368404
die("object %s changed size!?", oid_to_hex(oid));
369405

370406
batch_write(opt, contents, size);
@@ -856,6 +892,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
856892
OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
857893
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
858894
N_("allow -s and -t to work with broken/corrupt objects")),
895+
OPT_BOOL(0, "use-mailmap", &use_mailmap, N_("use mail map file")),
896+
OPT_ALIAS(0, "mailmap", "use-mailmap"),
859897
/* Batch mode */
860898
OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
861899
OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
@@ -898,6 +936,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
898936
opt_cw = (opt == 'c' || opt == 'w');
899937
opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
900938

939+
if (use_mailmap)
940+
read_mailmap(&mailmap);
941+
901942
/* --batch-all-objects? */
902943
if (opt == 'b')
903944
batch.all_objects = 1;

cache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,12 @@ struct ident_split {
16881688
*/
16891689
int split_ident_line(struct ident_split *, const char *, int);
16901690

1691+
/*
1692+
* Given a commit or tag object buffer and the commit or tag headers, replaces
1693+
* the idents in the headers with their canonical versions using the mailmap mechanism.
1694+
*/
1695+
void apply_mailmap_to_header(struct strbuf *, const char **, struct string_list *);
1696+
16911697
/*
16921698
* Compare split idents for equality or strict ordering. Note that we
16931699
* compare only the ident part of the line, ignoring any timestamp.

ident.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "cache.h"
99
#include "config.h"
1010
#include "date.h"
11+
#include "mailmap.h"
1112

1213
static struct strbuf git_default_name = STRBUF_INIT;
1314
static struct strbuf git_default_email = STRBUF_INIT;
@@ -346,6 +347,79 @@ int split_ident_line(struct ident_split *split, const char *line, int len)
346347
return 0;
347348
}
348349

350+
/*
351+
* Returns the difference between the new and old length of the ident line.
352+
*/
353+
static ssize_t rewrite_ident_line(const char *person, size_t len,
354+
struct strbuf *buf,
355+
struct string_list *mailmap)
356+
{
357+
size_t namelen, maillen;
358+
const char *name;
359+
const char *mail;
360+
struct ident_split ident;
361+
362+
if (split_ident_line(&ident, person, len))
363+
return 0;
364+
365+
mail = ident.mail_begin;
366+
maillen = ident.mail_end - ident.mail_begin;
367+
name = ident.name_begin;
368+
namelen = ident.name_end - ident.name_begin;
369+
370+
if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
371+
struct strbuf namemail = STRBUF_INIT;
372+
size_t newlen;
373+
374+
strbuf_addf(&namemail, "%.*s <%.*s>",
375+
(int)namelen, name, (int)maillen, mail);
376+
377+
strbuf_splice(buf, ident.name_begin - buf->buf,
378+
ident.mail_end - ident.name_begin + 1,
379+
namemail.buf, namemail.len);
380+
newlen = namemail.len;
381+
382+
strbuf_release(&namemail);
383+
384+
return newlen - (ident.mail_end - ident.name_begin);
385+
}
386+
387+
return 0;
388+
}
389+
390+
void apply_mailmap_to_header(struct strbuf *buf, const char **header,
391+
struct string_list *mailmap)
392+
{
393+
size_t buf_offset = 0;
394+
395+
if (!mailmap)
396+
return;
397+
398+
for (;;) {
399+
const char *person, *line;
400+
size_t i;
401+
int found_header = 0;
402+
403+
line = buf->buf + buf_offset;
404+
if (!*line || *line == '\n')
405+
return; /* End of headers */
406+
407+
for (i = 0; header[i]; i++)
408+
if (skip_prefix(line, header[i], &person)) {
409+
const char *endp = strchrnul(person, '\n');
410+
found_header = 1;
411+
buf_offset += endp - line;
412+
buf_offset += rewrite_ident_line(person, endp - person, buf, mailmap);
413+
break;
414+
}
415+
416+
if (!found_header) {
417+
buf_offset = strchrnul(line, '\n') - buf->buf;
418+
if (buf->buf[buf_offset] == '\n')
419+
buf_offset++;
420+
}
421+
}
422+
}
349423

350424
static void ident_env_hint(enum want_ident whose_ident)
351425
{

revision.c

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,51 +3791,6 @@ int rewrite_parents(struct rev_info *revs, struct commit *commit,
37913791
return 0;
37923792
}
37933793

3794-
static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap)
3795-
{
3796-
char *person, *endp;
3797-
size_t len, namelen, maillen;
3798-
const char *name;
3799-
const char *mail;
3800-
struct ident_split ident;
3801-
3802-
person = strstr(buf->buf, what);
3803-
if (!person)
3804-
return 0;
3805-
3806-
person += strlen(what);
3807-
endp = strchr(person, '\n');
3808-
if (!endp)
3809-
return 0;
3810-
3811-
len = endp - person;
3812-
3813-
if (split_ident_line(&ident, person, len))
3814-
return 0;
3815-
3816-
mail = ident.mail_begin;
3817-
maillen = ident.mail_end - ident.mail_begin;
3818-
name = ident.name_begin;
3819-
namelen = ident.name_end - ident.name_begin;
3820-
3821-
if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
3822-
struct strbuf namemail = STRBUF_INIT;
3823-
3824-
strbuf_addf(&namemail, "%.*s <%.*s>",
3825-
(int)namelen, name, (int)maillen, mail);
3826-
3827-
strbuf_splice(buf, ident.name_begin - buf->buf,
3828-
ident.mail_end - ident.name_begin + 1,
3829-
namemail.buf, namemail.len);
3830-
3831-
strbuf_release(&namemail);
3832-
3833-
return 1;
3834-
}
3835-
3836-
return 0;
3837-
}
3838-
38393794
static int commit_match(struct commit *commit, struct rev_info *opt)
38403795
{
38413796
int retval;
@@ -3868,11 +3823,12 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
38683823
strbuf_addstr(&buf, message);
38693824

38703825
if (opt->grep_filter.header_list && opt->mailmap) {
3826+
const char *commit_headers[] = { "author ", "committer ", NULL };
3827+
38713828
if (!buf.len)
38723829
strbuf_addstr(&buf, message);
38733830

3874-
commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
3875-
commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
3831+
apply_mailmap_to_header(&buf, commit_headers, opt->mailmap);
38763832
}
38773833

38783834
/* Append "fake" message parts as needed */

t/t4203-mailmap.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,4 +963,63 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
963963
test_cmp expect actual
964964
'
965965

966+
test_expect_success 'prepare for cat-file --mailmap' '
967+
rm -f .mailmap &&
968+
git commit --allow-empty -m foo --author="Orig <[email protected]>"
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+
975+
EOF
976+
cat >expect <<-EOF &&
977+
author Orig <[email protected]>
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+
988+
EOF
989+
cat >expect <<-EOF &&
990+
author A U Thor <[email protected]>
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+
1001+
EOF
1002+
cat >expect <<-EOF &&
1003+
tagger C O Mitter <[email protected]>
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+
1015+
EOF
1016+
cat >expect <<-EOF &&
1017+
tagger Orig <[email protected]>
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+
9661025
test_done

0 commit comments

Comments
 (0)