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
|
||||
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
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "packfile.h"
|
||||
#include "object-store.h"
|
||||
#include "promisor-remote.h"
|
||||
#include "mailmap.h"
|
||||
|
||||
enum batch_mode {
|
||||
BATCH_MODE_CONTENTS,
|
||||
@ -36,6 +37,22 @@ struct batch_options {
|
||||
|
||||
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,
|
||||
const struct object_id *oid,
|
||||
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)
|
||||
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;
|
||||
|
||||
@ -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,
|
||||
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:
|
||||
@ -360,11 +389,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);
|
||||
@ -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_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"),
|
||||
@ -898,6 +936,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;
|
||||
|
6
cache.h
6
cache.h
@ -1688,6 +1688,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 *, const char **, struct string_list *);
|
||||
|
||||
/*
|
||||
* Compare split idents for equality or strict ordering. Note that we
|
||||
* 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 "config.h"
|
||||
#include "date.h"
|
||||
#include "mailmap.h"
|
||||
|
||||
static struct strbuf git_default_name = STRBUF_INIT;
|
||||
static struct strbuf git_default_email = STRBUF_INIT;
|
||||
@ -346,6 +347,79 @@ person_only:
|
||||
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)
|
||||
{
|
||||
|
50
revision.c
50
revision.c
@ -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;
|
||||
@ -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 */
|
||||
|
@ -963,4 +963,63 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user