Skip to content

Commit 5d9ca63

Browse files
committed
Merge branch 'sa/cat-file-mailmap' into seen
"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 8515876 + 976f8a0 commit 5d9ca63

File tree

6 files changed

+191
-48
lines changed

6 files changed

+191
-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
@@ -19,6 +19,7 @@
1919
#include "alias.h"
2020
#include "remote.h"
2121
#include "transport.h"
22+
#include "mailmap.h"
2223

2324
enum batch_mode {
2425
BATCH_MODE_CONTENTS,
@@ -44,6 +45,22 @@ static const char *force_path;
4445
static struct object_info *remote_object_info;
4546
static struct oid_array object_info_oids = OID_ARRAY_INIT;
4647

48+
static struct string_list mailmap = STRING_LIST_INIT_NODUP;
49+
static int use_mailmap;
50+
51+
static char *replace_idents_using_mailmap(char *, size_t *);
52+
53+
static char *replace_idents_using_mailmap(char *object_buf, size_t *size)
54+
{
55+
struct strbuf sb = STRBUF_INIT;
56+
const char *headers[] = { "author ", "committer ", "tagger ", NULL };
57+
58+
strbuf_attach(&sb, object_buf, *size, *size + 1);
59+
apply_mailmap_to_header(&sb, headers, &mailmap);
60+
*size = sb.len;
61+
return strbuf_detach(&sb, NULL);
62+
}
63+
4764
static int filter_object(const char *path, unsigned mode,
4865
const struct object_id *oid,
4966
char **buf, unsigned long *size)
@@ -168,6 +185,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
168185
if (!buf)
169186
die("Cannot read object %s", obj_name);
170187

188+
if (use_mailmap) {
189+
size_t s = size;
190+
buf = replace_idents_using_mailmap(buf, &s);
191+
size = cast_size_t_to_ulong(s);
192+
}
193+
171194
/* otherwise just spit out the data */
172195
break;
173196

@@ -201,6 +224,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
201224
}
202225
buf = read_object_with_reference(the_repository, &oid,
203226
exp_type_id, &size, NULL);
227+
228+
if (use_mailmap) {
229+
size_t s = size;
230+
buf = replace_idents_using_mailmap(buf, &s);
231+
size = cast_size_t_to_ulong(s);
232+
}
204233
break;
205234
}
206235
default:
@@ -368,11 +397,18 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
368397
void *contents;
369398

370399
contents = read_object_file(oid, &type, &size);
400+
401+
if (use_mailmap) {
402+
size_t s = size;
403+
contents = replace_idents_using_mailmap(contents, &s);
404+
size = cast_size_t_to_ulong(s);
405+
}
406+
371407
if (!contents)
372408
die("object %s disappeared", oid_to_hex(oid));
373409
if (type != data->type)
374410
die("object %s changed type!?", oid_to_hex(oid));
375-
if (data->info.sizep && size != data->size)
411+
if (data->info.sizep && size != data->size && !use_mailmap)
376412
die("object %s changed size!?", oid_to_hex(oid));
377413

378414
batch_write(opt, contents, size);
@@ -979,6 +1015,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
9791015
OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
9801016
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
9811017
N_("allow -s and -t to work with broken/corrupt objects")),
1018+
OPT_BOOL(0, "use-mailmap", &use_mailmap, N_("use mail map file")),
1019+
OPT_ALIAS(0, "mailmap", "use-mailmap"),
9821020
/* Batch mode */
9831021
OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
9841022
OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
@@ -1021,6 +1059,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
10211059
opt_cw = (opt == 'c' || opt == 'w');
10221060
opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
10231061

1062+
if (use_mailmap)
1063+
read_mailmap(&mailmap);
1064+
10241065
/* --batch-all-objects? */
10251066
if (opt == 'b')
10261067
batch.all_objects = 1;

cache.h

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

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

ident.c

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

350425
static void ident_env_hint(enum want_ident whose_ident)
351426
{

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)