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()
This commit is contained in:
commit
87098a047b
@ -63,6 +63,12 @@ OPTIONS
|
|||||||
or to ask for a "blob" with `<object>` being a tag object that
|
or to ask for a "blob" with `<object>` being a tag object that
|
||||||
points at it.
|
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::
|
--textconv::
|
||||||
Show the content as transformed by a textconv filter. In this case,
|
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
|
`<object>` has to be of the form `<tree-ish>:<path>`, or `:<path>` in
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "packfile.h"
|
#include "packfile.h"
|
||||||
#include "object-store.h"
|
#include "object-store.h"
|
||||||
#include "promisor-remote.h"
|
#include "promisor-remote.h"
|
||||||
|
#include "mailmap.h"
|
||||||
|
|
||||||
enum batch_mode {
|
enum batch_mode {
|
||||||
BATCH_MODE_CONTENTS,
|
BATCH_MODE_CONTENTS,
|
||||||
@ -36,6 +37,22 @@ struct batch_options {
|
|||||||
|
|
||||||
static const char *force_path;
|
static const char *force_path;
|
||||||
|
|
||||||
|
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,
|
static int filter_object(const char *path, unsigned mode,
|
||||||
const struct object_id *oid,
|
const struct object_id *oid,
|
||||||
char **buf, unsigned long *size)
|
char **buf, unsigned long *size)
|
||||||
@ -160,6 +177,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
|
|||||||
if (!buf)
|
if (!buf)
|
||||||
die("Cannot read object %s", obj_name);
|
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 */
|
/* otherwise just spit out the data */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -193,6 +216,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
|
|||||||
}
|
}
|
||||||
buf = read_object_with_reference(the_repository, &oid,
|
buf = read_object_with_reference(the_repository, &oid,
|
||||||
exp_type_id, &size, NULL);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -360,11 +389,18 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
|
|||||||
void *contents;
|
void *contents;
|
||||||
|
|
||||||
contents = read_object_file(oid, &type, &size);
|
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)
|
if (!contents)
|
||||||
die("object %s disappeared", oid_to_hex(oid));
|
die("object %s disappeared", oid_to_hex(oid));
|
||||||
if (type != data->type)
|
if (type != data->type)
|
||||||
die("object %s changed type!?", oid_to_hex(oid));
|
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));
|
die("object %s changed size!?", oid_to_hex(oid));
|
||||||
|
|
||||||
batch_write(opt, contents, size);
|
batch_write(opt, contents, size);
|
||||||
@ -856,6 +892,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
|
|||||||
OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
|
OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
|
||||||
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
|
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
|
||||||
N_("allow -s and -t to work with broken/corrupt objects")),
|
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 */
|
/* Batch mode */
|
||||||
OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
|
OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
|
||||||
OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
|
OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
|
||||||
@ -898,6 +936,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
|
|||||||
opt_cw = (opt == 'c' || opt == 'w');
|
opt_cw = (opt == 'c' || opt == 'w');
|
||||||
opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
|
opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
|
||||||
|
|
||||||
|
if (use_mailmap)
|
||||||
|
read_mailmap(&mailmap);
|
||||||
|
|
||||||
/* --batch-all-objects? */
|
/* --batch-all-objects? */
|
||||||
if (opt == 'b')
|
if (opt == 'b')
|
||||||
batch.all_objects = 1;
|
batch.all_objects = 1;
|
||||||
|
6
cache.h
6
cache.h
@ -1688,6 +1688,12 @@ struct ident_split {
|
|||||||
*/
|
*/
|
||||||
int split_ident_line(struct ident_split *, const char *, int);
|
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 *, const char **, struct string_list *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare split idents for equality or strict ordering. Note that we
|
* Compare split idents for equality or strict ordering. Note that we
|
||||||
* compare only the ident part of the line, ignoring any timestamp.
|
* compare only the ident part of the line, ignoring any timestamp.
|
||||||
|
74
ident.c
74
ident.c
@ -8,6 +8,7 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
#include "mailmap.h"
|
||||||
|
|
||||||
static struct strbuf git_default_name = STRBUF_INIT;
|
static struct strbuf git_default_name = STRBUF_INIT;
|
||||||
static struct strbuf git_default_email = STRBUF_INIT;
|
static struct strbuf git_default_email = STRBUF_INIT;
|
||||||
@ -346,6 +347,79 @@ person_only:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the difference between the new and old length of the ident line.
|
||||||
|
*/
|
||||||
|
static ssize_t rewrite_ident_line(const char *person, size_t len,
|
||||||
|
struct strbuf *buf,
|
||||||
|
struct string_list *mailmap)
|
||||||
|
{
|
||||||
|
size_t namelen, maillen;
|
||||||
|
const char *name;
|
||||||
|
const char *mail;
|
||||||
|
struct ident_split ident;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_mailmap_to_header(struct strbuf *buf, const char **header,
|
||||||
|
struct string_list *mailmap)
|
||||||
|
{
|
||||||
|
size_t buf_offset = 0;
|
||||||
|
|
||||||
|
if (!mailmap)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *person, *line;
|
||||||
|
size_t i;
|
||||||
|
int found_header = 0;
|
||||||
|
|
||||||
|
line = buf->buf + buf_offset;
|
||||||
|
if (!*line || *line == '\n')
|
||||||
|
return; /* End of headers */
|
||||||
|
|
||||||
|
for (i = 0; header[i]; i++)
|
||||||
|
if (skip_prefix(line, header[i], &person)) {
|
||||||
|
const char *endp = strchrnul(person, '\n');
|
||||||
|
found_header = 1;
|
||||||
|
buf_offset += endp - line;
|
||||||
|
buf_offset += rewrite_ident_line(person, endp - person, buf, mailmap);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_header) {
|
||||||
|
buf_offset = strchrnul(line, '\n') - buf->buf;
|
||||||
|
if (buf->buf[buf_offset] == '\n')
|
||||||
|
buf_offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ident_env_hint(enum want_ident whose_ident)
|
static void ident_env_hint(enum want_ident whose_ident)
|
||||||
{
|
{
|
||||||
|
50
revision.c
50
revision.c
@ -3791,51 +3791,6 @@ int rewrite_parents(struct rev_info *revs, struct commit *commit,
|
|||||||
return 0;
|
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)
|
static int commit_match(struct commit *commit, struct rev_info *opt)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
@ -3868,11 +3823,12 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
|
|||||||
strbuf_addstr(&buf, message);
|
strbuf_addstr(&buf, message);
|
||||||
|
|
||||||
if (opt->grep_filter.header_list && opt->mailmap) {
|
if (opt->grep_filter.header_list && opt->mailmap) {
|
||||||
|
const char *commit_headers[] = { "author ", "committer ", NULL };
|
||||||
|
|
||||||
if (!buf.len)
|
if (!buf.len)
|
||||||
strbuf_addstr(&buf, message);
|
strbuf_addstr(&buf, message);
|
||||||
|
|
||||||
commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
|
apply_mailmap_to_header(&buf, commit_headers, opt->mailmap);
|
||||||
commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append "fake" message parts as needed */
|
/* Append "fake" message parts as needed */
|
||||||
|
@ -963,4 +963,63 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
|
|||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'prepare for cat-file --mailmap' '
|
||||||
|
rm -f .mailmap &&
|
||||||
|
git commit --allow-empty -m foo --author="Orig <orig@example.com>"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--no-use-mailmap disables mailmap in cat-file' '
|
||||||
|
test_when_finished "rm .mailmap" &&
|
||||||
|
cat >.mailmap <<-EOF &&
|
||||||
|
A U Thor <author@example.com> Orig <orig@example.com>
|
||||||
|
EOF
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
author Orig <orig@example.com>
|
||||||
|
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 <author@example.com> Orig <orig@example.com>
|
||||||
|
EOF
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
author A U Thor <author@example.com>
|
||||||
|
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 <orig@example.com> C O Mitter <committer@example.com>
|
||||||
|
EOF
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
tagger C O Mitter <committer@example.com>
|
||||||
|
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 <orig@example.com> C O Mitter <committer@example.com>
|
||||||
|
EOF
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
tagger Orig <orig@example.com>
|
||||||
|
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
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user