Skip to content

Commit

Permalink
Merge branch 'sa/cat-file-mailmap' into seen
Browse files Browse the repository at this point in the history
"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()
  • Loading branch information
gitster committed Jul 16, 2022
2 parents 845b676 + d0d5639 commit c82b393
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 49 deletions.
6 changes: 6 additions & 0 deletions Documentation/git-cat-file.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ OPTIONS
or to ask for a "blob" with `<object>` being a tag object that
points at it.

--[no-]mailmap::
--[no-]use-mailmap::
Use mailmap file to map author, committer and tagger names
and email addresses to canonical real names and email addresses.
See linkgit:git-shortlog[1].

--textconv::
Show the content as transformed by a textconv filter. In this case,
`<object>` has to be of the form `<tree-ish>:<path>`, or `:<path>` in
Expand Down
43 changes: 42 additions & 1 deletion builtin/cat-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "alias.h"
#include "remote.h"
#include "transport.h"
#include "mailmap.h"

enum batch_mode {
BATCH_MODE_CONTENTS,
Expand All @@ -44,6 +45,22 @@ static const char *force_path;
static struct object_info *remote_object_info;
static struct oid_array object_info_oids = OID_ARRAY_INIT;

static struct string_list mailmap = STRING_LIST_INIT_NODUP;
static int use_mailmap;

static char *replace_idents_using_mailmap(char *, size_t *);

static char *replace_idents_using_mailmap(char *object_buf, size_t *size)
{
struct strbuf sb = STRBUF_INIT;
const char *headers[] = { "author ", "committer ", "tagger ", NULL };

strbuf_attach(&sb, object_buf, *size, *size + 1);
apply_mailmap_to_header(&sb, headers, &mailmap);
*size = sb.len;
return strbuf_detach(&sb, NULL);
}

static int filter_object(const char *path, unsigned mode,
const struct object_id *oid,
char **buf, unsigned long *size)
Expand Down Expand Up @@ -168,6 +185,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (!buf)
die("Cannot read object %s", obj_name);

if (use_mailmap) {
size_t s = size;
buf = replace_idents_using_mailmap(buf, &s);
size = cast_size_t_to_ulong(s);
}

/* otherwise just spit out the data */
break;

Expand Down Expand Up @@ -201,6 +224,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
}
buf = read_object_with_reference(the_repository, &oid,
exp_type_id, &size, NULL);

if (use_mailmap) {
size_t s = size;
buf = replace_idents_using_mailmap(buf, &s);
size = cast_size_t_to_ulong(s);
}
break;
}
default:
Expand Down Expand Up @@ -368,11 +397,18 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
void *contents;

contents = read_object_file(oid, &type, &size);

if (use_mailmap) {
size_t s = size;
contents = replace_idents_using_mailmap(contents, &s);
size = cast_size_t_to_ulong(s);
}

if (!contents)
die("object %s disappeared", oid_to_hex(oid));
if (type != data->type)
die("object %s changed type!?", oid_to_hex(oid));
if (data->info.sizep && size != data->size)
if (data->info.sizep && size != data->size && !use_mailmap)
die("object %s changed size!?", oid_to_hex(oid));

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

if (use_mailmap)
read_mailmap(&mailmap);

/* --batch-all-objects? */
if (opt == 'b')
batch.all_objects = 1;
Expand Down
6 changes: 6 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -1689,6 +1689,12 @@ struct ident_split {
*/
int split_ident_line(struct ident_split *, const char *, int);

/*
* Given a commit or tag object buffer and the commit or tag headers, replaces
* the idents in the headers with their canonical versions using the mailmap mechanism.
*/
void apply_mailmap_to_header(struct strbuf *buf, const char **headers, struct string_list *mailmap);

/*
* Compare split idents for equality or strict ordering. Note that we
* compare only the ident part of the line, ignoring any timestamp.
Expand Down
72 changes: 72 additions & 0 deletions ident.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "cache.h"
#include "config.h"
#include "date.h"
#include "mailmap.h"

static struct strbuf git_default_name = STRBUF_INIT;
static struct strbuf git_default_email = STRBUF_INIT;
Expand Down Expand Up @@ -346,6 +347,77 @@ int split_ident_line(struct ident_split *split, const char *line, int len)
return 0;
}

/*
* Returns the difference between the new and old length of the ident line.
*/
static ssize_t rewrite_ident_line(const char* person, struct strbuf *buf, struct string_list *mailmap)
{
char *endp;
size_t len, namelen, maillen;
const char *name;
const char *mail;
struct ident_split ident;

endp = strchr(person, '\n');
if (!endp)
return 0;

len = endp - person;

if (split_ident_line(&ident, person, len))
return 0;

mail = ident.mail_begin;
maillen = ident.mail_end - ident.mail_begin;
name = ident.name_begin;
namelen = ident.name_end - ident.name_begin;

if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
struct strbuf namemail = STRBUF_INIT;
size_t newlen;

strbuf_addf(&namemail, "%.*s <%.*s>",
(int)namelen, name, (int)maillen, mail);

strbuf_splice(buf, ident.name_begin - buf->buf,
ident.mail_end - ident.name_begin + 1,
namemail.buf, namemail.len);

newlen = namemail.len;

strbuf_release(&namemail);

return newlen - (ident.mail_end - ident.name_begin + 1);
}

return 0;
}

void apply_mailmap_to_header(struct strbuf *buf, const char **headers, struct string_list *mailmap)
{
size_t buf_offset = 0;

if (!mailmap)
return;

for (;;) {
const char *person, *line;
size_t i, linelen;

line = buf->buf + buf_offset;
linelen = strchrnul(line, '\n') - line + 1;

if (linelen <= 1)
/* End of header */
return;

buf_offset += linelen;

for (i = 0; headers[i]; i++)
if (skip_prefix(line, headers[i], &person))
buf_offset += rewrite_ident_line(person, buf, mailmap);
}
}

static void ident_env_hint(enum want_ident whose_ident)
{
Expand Down
50 changes: 3 additions & 47 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -3791,51 +3791,6 @@ int rewrite_parents(struct rev_info *revs, struct commit *commit,
return 0;
}

static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap)
{
char *person, *endp;
size_t len, namelen, maillen;
const char *name;
const char *mail;
struct ident_split ident;

person = strstr(buf->buf, what);
if (!person)
return 0;

person += strlen(what);
endp = strchr(person, '\n');
if (!endp)
return 0;

len = endp - person;

if (split_ident_line(&ident, person, len))
return 0;

mail = ident.mail_begin;
maillen = ident.mail_end - ident.mail_begin;
name = ident.name_begin;
namelen = ident.name_end - ident.name_begin;

if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
struct strbuf namemail = STRBUF_INIT;

strbuf_addf(&namemail, "%.*s <%.*s>",
(int)namelen, name, (int)maillen, mail);

strbuf_splice(buf, ident.name_begin - buf->buf,
ident.mail_end - ident.name_begin + 1,
namemail.buf, namemail.len);

strbuf_release(&namemail);

return 1;
}

return 0;
}

static int commit_match(struct commit *commit, struct rev_info *opt)
{
int retval;
Expand Down Expand Up @@ -3868,11 +3823,12 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
strbuf_addstr(&buf, message);

if (opt->grep_filter.header_list && opt->mailmap) {
const char *commit_headers[] = { "author ", "committer ", NULL };

if (!buf.len)
strbuf_addstr(&buf, message);

commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
apply_mailmap_to_header(&buf, commit_headers, opt->mailmap);
}

/* Append "fake" message parts as needed */
Expand Down
56 changes: 55 additions & 1 deletion t/t4203-mailmap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ test_expect_success 'find top-level mailmap from subdir' '
test_cmp expect actual
'

test_expect_success SYMLINKS 'set up symlink tests' '
test_expect_success 'prepare for symlink/--use-mailmap tests' '
git commit --allow-empty -m foo --author="Orig <[email protected]>" &&
echo "New <[email protected]> <[email protected]>" >map &&
rm -f .mailmap
Expand Down Expand Up @@ -963,4 +963,58 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
test_cmp expect actual
'

test_expect_success '--no-use-mailmap disables mailmap in cat-file' '
test_when_finished "rm .mailmap" &&
cat >.mailmap <<-EOF &&
A U Thor <[email protected]> Orig <[email protected]>
EOF
cat >expect <<-EOF &&
author Orig <[email protected]>
EOF
git cat-file --no-use-mailmap commit HEAD >log &&
sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
test_cmp expect actual
'

test_expect_success '--use-mailmap enables mailmap in cat-file' '
test_when_finished "rm .mailmap" &&
cat >.mailmap <<-EOF &&
A U Thor <[email protected]> Orig <[email protected]>
EOF
cat >expect <<-EOF &&
author A U Thor <[email protected]>
EOF
git cat-file --use-mailmap commit HEAD >log &&
sed -n "/^author /s/\([^>]*>\).*/\1/p" log >actual &&
test_cmp expect actual
'

test_expect_success '--no-mailmap disables mailmap in cat-file for annotated tag objects' '
test_when_finished "rm .mailmap" &&
cat >.mailmap <<-EOF &&
Orig <[email protected]> C O Mitter <[email protected]>
EOF
cat >expect <<-EOF &&
tagger C O Mitter <[email protected]>
EOF
git tag -a -m "annotated tag" v1 &&
git cat-file --no-mailmap -p v1 >log &&
sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
test_cmp expect actual
'

test_expect_success '--mailmap enables mailmap in cat-file for annotated tag objects' '
test_when_finished "rm .mailmap" &&
cat >.mailmap <<-EOF &&
Orig <[email protected]> C O Mitter <[email protected]>
EOF
cat >expect <<-EOF &&
tagger Orig <[email protected]>
EOF
git tag -a -m "annotated tag" v2 &&
git cat-file --mailmap -p v2 >log &&
sed -n "/^tagger /s/\([^>]*>\).*/\1/p" log >actual &&
test_cmp expect actual
'

test_done

0 comments on commit c82b393

Please sign in to comment.