Merge branch 'ap/log-mailmap'
Teach commands in the "log" family to optionally pay attention to the mailmap. * ap/log-mailmap: log --use-mailmap: optimize for cases without --author/--committer search log: add log.mailmap configuration option log: grep author/committer using mailmap test: add test for --use-mailmap option log: add --use-mailmap option pretty: use mailmap to display username and email mailmap: add mailmap structure to rev_info and pp mailmap: simplify map_user() interface mailmap: remove email copy and length limitation Use split_ident_line to parse author and committer string-list: allow case-insensitive string list
This commit is contained in:
commit
577f63e781
@ -1525,6 +1525,10 @@ log.showroot::
|
|||||||
Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
|
Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
|
||||||
normally hide the root commit will now show it. True by default.
|
normally hide the root commit will now show it. True by default.
|
||||||
|
|
||||||
|
log.mailmap::
|
||||||
|
If true, makes linkgit:git-log[1], linkgit:git-show[1], and
|
||||||
|
linkgit:git-whatchanged[1] assume `--use-mailmap`.
|
||||||
|
|
||||||
mailmap.file::
|
mailmap.file::
|
||||||
The location of an augmenting mailmap file. The default
|
The location of an augmenting mailmap file. The default
|
||||||
mailmap, located in the root of the repository, is loaded
|
mailmap, located in the root of the repository, is loaded
|
||||||
|
@ -47,6 +47,11 @@ OPTIONS
|
|||||||
Print out the ref name given on the command line by which each
|
Print out the ref name given on the command line by which each
|
||||||
commit was reached.
|
commit was reached.
|
||||||
|
|
||||||
|
--use-mailmap::
|
||||||
|
Use mailmap file to map author and committer names and email
|
||||||
|
to canonical real names and email addresses. See
|
||||||
|
linkgit:git-shortlog[1].
|
||||||
|
|
||||||
--full-diff::
|
--full-diff::
|
||||||
Without this flag, "git log -p <path>..." shows commits that
|
Without this flag, "git log -p <path>..." shows commits that
|
||||||
touch the specified paths, and diffs about the same specified
|
touch the specified paths, and diffs about the same specified
|
||||||
|
183
builtin/blame.c
183
builtin/blame.c
@ -1322,30 +1322,31 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
|||||||
* Information on commits, used for output.
|
* Information on commits, used for output.
|
||||||
*/
|
*/
|
||||||
struct commit_info {
|
struct commit_info {
|
||||||
const char *author;
|
struct strbuf author;
|
||||||
const char *author_mail;
|
struct strbuf author_mail;
|
||||||
unsigned long author_time;
|
unsigned long author_time;
|
||||||
const char *author_tz;
|
struct strbuf author_tz;
|
||||||
|
|
||||||
/* filled only when asked for details */
|
/* filled only when asked for details */
|
||||||
const char *committer;
|
struct strbuf committer;
|
||||||
const char *committer_mail;
|
struct strbuf committer_mail;
|
||||||
unsigned long committer_time;
|
unsigned long committer_time;
|
||||||
const char *committer_tz;
|
struct strbuf committer_tz;
|
||||||
|
|
||||||
const char *summary;
|
struct strbuf summary;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse author/committer line in the commit object buffer
|
* Parse author/committer line in the commit object buffer
|
||||||
*/
|
*/
|
||||||
static void get_ac_line(const char *inbuf, const char *what,
|
static void get_ac_line(const char *inbuf, const char *what,
|
||||||
int person_len, char *person,
|
struct strbuf *name, struct strbuf *mail,
|
||||||
int mail_len, char *mail,
|
unsigned long *time, struct strbuf *tz)
|
||||||
unsigned long *time, const char **tz)
|
|
||||||
{
|
{
|
||||||
int len, tzlen, maillen;
|
struct ident_split ident;
|
||||||
char *tmp, *endp, *timepos, *mailpos;
|
size_t len, maillen, namelen;
|
||||||
|
char *tmp, *endp;
|
||||||
|
const char *namebuf, *mailbuf;
|
||||||
|
|
||||||
tmp = strstr(inbuf, what);
|
tmp = strstr(inbuf, what);
|
||||||
if (!tmp)
|
if (!tmp)
|
||||||
@ -1356,69 +1357,61 @@ static void get_ac_line(const char *inbuf, const char *what,
|
|||||||
len = strlen(tmp);
|
len = strlen(tmp);
|
||||||
else
|
else
|
||||||
len = endp - tmp;
|
len = endp - tmp;
|
||||||
if (person_len <= len) {
|
|
||||||
|
if (split_ident_line(&ident, tmp, len)) {
|
||||||
error_out:
|
error_out:
|
||||||
/* Ugh */
|
/* Ugh */
|
||||||
*tz = "(unknown)";
|
tmp = "(unknown)";
|
||||||
strcpy(person, *tz);
|
strbuf_addstr(name, tmp);
|
||||||
strcpy(mail, *tz);
|
strbuf_addstr(mail, tmp);
|
||||||
|
strbuf_addstr(tz, tmp);
|
||||||
*time = 0;
|
*time = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(person, tmp, len);
|
|
||||||
|
|
||||||
tmp = person;
|
namelen = ident.name_end - ident.name_begin;
|
||||||
tmp += len;
|
namebuf = ident.name_begin;
|
||||||
*tmp = 0;
|
|
||||||
while (person < tmp && *tmp != ' ')
|
|
||||||
tmp--;
|
|
||||||
if (tmp <= person)
|
|
||||||
goto error_out;
|
|
||||||
*tz = tmp+1;
|
|
||||||
tzlen = (person+len)-(tmp+1);
|
|
||||||
|
|
||||||
*tmp = 0;
|
maillen = ident.mail_end - ident.mail_begin;
|
||||||
while (person < tmp && *tmp != ' ')
|
mailbuf = ident.mail_begin;
|
||||||
tmp--;
|
|
||||||
if (tmp <= person)
|
|
||||||
goto error_out;
|
|
||||||
*time = strtoul(tmp, NULL, 10);
|
|
||||||
timepos = tmp;
|
|
||||||
|
|
||||||
*tmp = 0;
|
*time = strtoul(ident.date_begin, NULL, 10);
|
||||||
while (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))
|
|
||||||
tmp--;
|
|
||||||
if (tmp <= person)
|
|
||||||
return;
|
|
||||||
mailpos = tmp + 1;
|
|
||||||
*tmp = 0;
|
|
||||||
maillen = timepos - tmp;
|
|
||||||
memcpy(mail, mailpos, maillen);
|
|
||||||
|
|
||||||
if (!mailmap.nr)
|
len = ident.tz_end - ident.tz_begin;
|
||||||
return;
|
strbuf_add(tz, ident.tz_begin, len);
|
||||||
|
|
||||||
/*
|
|
||||||
* mailmap expansion may make the name longer.
|
|
||||||
* make room by pushing stuff down.
|
|
||||||
*/
|
|
||||||
tmp = person + person_len - (tzlen + 1);
|
|
||||||
memmove(tmp, *tz, tzlen);
|
|
||||||
tmp[tzlen] = 0;
|
|
||||||
*tz = tmp;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now, convert both name and e-mail using mailmap
|
* Now, convert both name and e-mail using mailmap
|
||||||
*/
|
*/
|
||||||
if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
|
map_user(&mailmap, &mailbuf, &maillen,
|
||||||
/* Add a trailing '>' to email, since map_user returns plain emails
|
&namebuf, &namelen);
|
||||||
Note: It already has '<', since we replace from mail+1 */
|
|
||||||
mailpos = memchr(mail, '\0', mail_len);
|
strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf);
|
||||||
if (mailpos && mailpos-mail < mail_len - 1) {
|
strbuf_add(name, namebuf, namelen);
|
||||||
*mailpos = '>';
|
}
|
||||||
*(mailpos+1) = '\0';
|
|
||||||
}
|
static void commit_info_init(struct commit_info *ci)
|
||||||
}
|
{
|
||||||
|
|
||||||
|
strbuf_init(&ci->author, 0);
|
||||||
|
strbuf_init(&ci->author_mail, 0);
|
||||||
|
strbuf_init(&ci->author_tz, 0);
|
||||||
|
strbuf_init(&ci->committer, 0);
|
||||||
|
strbuf_init(&ci->committer_mail, 0);
|
||||||
|
strbuf_init(&ci->committer_tz, 0);
|
||||||
|
strbuf_init(&ci->summary, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void commit_info_destroy(struct commit_info *ci)
|
||||||
|
{
|
||||||
|
|
||||||
|
strbuf_release(&ci->author);
|
||||||
|
strbuf_release(&ci->author_mail);
|
||||||
|
strbuf_release(&ci->author_tz);
|
||||||
|
strbuf_release(&ci->committer);
|
||||||
|
strbuf_release(&ci->committer_mail);
|
||||||
|
strbuf_release(&ci->committer_tz);
|
||||||
|
strbuf_release(&ci->summary);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_commit_info(struct commit *commit,
|
static void get_commit_info(struct commit *commit,
|
||||||
@ -1428,11 +1421,8 @@ static void get_commit_info(struct commit *commit,
|
|||||||
int len;
|
int len;
|
||||||
const char *subject, *encoding;
|
const char *subject, *encoding;
|
||||||
char *reencoded, *message;
|
char *reencoded, *message;
|
||||||
static char author_name[1024];
|
|
||||||
static char author_mail[1024];
|
commit_info_init(ret);
|
||||||
static char committer_name[1024];
|
|
||||||
static char committer_mail[1024];
|
|
||||||
static char summary_buf[1024];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We've operated without save_commit_buffer, so
|
* We've operated without save_commit_buffer, so
|
||||||
@ -1450,11 +1440,8 @@ static void get_commit_info(struct commit *commit,
|
|||||||
encoding = get_log_output_encoding();
|
encoding = get_log_output_encoding();
|
||||||
reencoded = logmsg_reencode(commit, encoding);
|
reencoded = logmsg_reencode(commit, encoding);
|
||||||
message = reencoded ? reencoded : commit->buffer;
|
message = reencoded ? reencoded : commit->buffer;
|
||||||
ret->author = author_name;
|
|
||||||
ret->author_mail = author_mail;
|
|
||||||
get_ac_line(message, "\nauthor ",
|
get_ac_line(message, "\nauthor ",
|
||||||
sizeof(author_name), author_name,
|
&ret->author, &ret->author_mail,
|
||||||
sizeof(author_mail), author_mail,
|
|
||||||
&ret->author_time, &ret->author_tz);
|
&ret->author_time, &ret->author_tz);
|
||||||
|
|
||||||
if (!detailed) {
|
if (!detailed) {
|
||||||
@ -1462,21 +1449,16 @@ static void get_commit_info(struct commit *commit,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->committer = committer_name;
|
|
||||||
ret->committer_mail = committer_mail;
|
|
||||||
get_ac_line(message, "\ncommitter ",
|
get_ac_line(message, "\ncommitter ",
|
||||||
sizeof(committer_name), committer_name,
|
&ret->committer, &ret->committer_mail,
|
||||||
sizeof(committer_mail), committer_mail,
|
|
||||||
&ret->committer_time, &ret->committer_tz);
|
&ret->committer_time, &ret->committer_tz);
|
||||||
|
|
||||||
ret->summary = summary_buf;
|
|
||||||
len = find_commit_subject(message, &subject);
|
len = find_commit_subject(message, &subject);
|
||||||
if (len && len < sizeof(summary_buf)) {
|
if (len)
|
||||||
memcpy(summary_buf, subject, len);
|
strbuf_add(&ret->summary, subject, len);
|
||||||
summary_buf[len] = 0;
|
else
|
||||||
} else {
|
strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
|
||||||
sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
|
|
||||||
}
|
|
||||||
free(reencoded);
|
free(reencoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1505,15 +1487,15 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
|
|||||||
|
|
||||||
suspect->commit->object.flags |= METAINFO_SHOWN;
|
suspect->commit->object.flags |= METAINFO_SHOWN;
|
||||||
get_commit_info(suspect->commit, &ci, 1);
|
get_commit_info(suspect->commit, &ci, 1);
|
||||||
printf("author %s\n", ci.author);
|
printf("author %s\n", ci.author.buf);
|
||||||
printf("author-mail %s\n", ci.author_mail);
|
printf("author-mail %s\n", ci.author_mail.buf);
|
||||||
printf("author-time %lu\n", ci.author_time);
|
printf("author-time %lu\n", ci.author_time);
|
||||||
printf("author-tz %s\n", ci.author_tz);
|
printf("author-tz %s\n", ci.author_tz.buf);
|
||||||
printf("committer %s\n", ci.committer);
|
printf("committer %s\n", ci.committer.buf);
|
||||||
printf("committer-mail %s\n", ci.committer_mail);
|
printf("committer-mail %s\n", ci.committer_mail.buf);
|
||||||
printf("committer-time %lu\n", ci.committer_time);
|
printf("committer-time %lu\n", ci.committer_time);
|
||||||
printf("committer-tz %s\n", ci.committer_tz);
|
printf("committer-tz %s\n", ci.committer_tz.buf);
|
||||||
printf("summary %s\n", ci.summary);
|
printf("summary %s\n", ci.summary.buf);
|
||||||
if (suspect->commit->object.flags & UNINTERESTING)
|
if (suspect->commit->object.flags & UNINTERESTING)
|
||||||
printf("boundary\n");
|
printf("boundary\n");
|
||||||
if (suspect->previous) {
|
if (suspect->previous) {
|
||||||
@ -1521,6 +1503,9 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
|
|||||||
printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
|
printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
|
||||||
write_name_quoted(prev->path, stdout, '\n');
|
write_name_quoted(prev->path, stdout, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commit_info_destroy(&ci);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1707,11 +1692,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
|
|||||||
if (opt & OUTPUT_ANNOTATE_COMPAT) {
|
if (opt & OUTPUT_ANNOTATE_COMPAT) {
|
||||||
const char *name;
|
const char *name;
|
||||||
if (opt & OUTPUT_SHOW_EMAIL)
|
if (opt & OUTPUT_SHOW_EMAIL)
|
||||||
name = ci.author_mail;
|
name = ci.author_mail.buf;
|
||||||
else
|
else
|
||||||
name = ci.author;
|
name = ci.author.buf;
|
||||||
printf("\t(%10s\t%10s\t%d)", name,
|
printf("\t(%10s\t%10s\t%d)", name,
|
||||||
format_time(ci.author_time, ci.author_tz,
|
format_time(ci.author_time, ci.author_tz.buf,
|
||||||
show_raw_time),
|
show_raw_time),
|
||||||
ent->lno + 1 + cnt);
|
ent->lno + 1 + cnt);
|
||||||
} else {
|
} else {
|
||||||
@ -1730,14 +1715,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
|
|||||||
const char *name;
|
const char *name;
|
||||||
int pad;
|
int pad;
|
||||||
if (opt & OUTPUT_SHOW_EMAIL)
|
if (opt & OUTPUT_SHOW_EMAIL)
|
||||||
name = ci.author_mail;
|
name = ci.author_mail.buf;
|
||||||
else
|
else
|
||||||
name = ci.author;
|
name = ci.author.buf;
|
||||||
pad = longest_author - utf8_strwidth(name);
|
pad = longest_author - utf8_strwidth(name);
|
||||||
printf(" (%s%*s %10s",
|
printf(" (%s%*s %10s",
|
||||||
name, pad, "",
|
name, pad, "",
|
||||||
format_time(ci.author_time,
|
format_time(ci.author_time,
|
||||||
ci.author_tz,
|
ci.author_tz.buf,
|
||||||
show_raw_time));
|
show_raw_time));
|
||||||
}
|
}
|
||||||
printf(" %*d) ",
|
printf(" %*d) ",
|
||||||
@ -1752,6 +1737,8 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
|
|||||||
|
|
||||||
if (sb->final_buf_size && cp[-1] != '\n')
|
if (sb->final_buf_size && cp[-1] != '\n')
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
|
commit_info_destroy(&ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output(struct scoreboard *sb, int option)
|
static void output(struct scoreboard *sb, int option)
|
||||||
@ -1876,9 +1863,9 @@ static void find_alignment(struct scoreboard *sb, int *option)
|
|||||||
suspect->commit->object.flags |= METAINFO_SHOWN;
|
suspect->commit->object.flags |= METAINFO_SHOWN;
|
||||||
get_commit_info(suspect->commit, &ci, 1);
|
get_commit_info(suspect->commit, &ci, 1);
|
||||||
if (*option & OUTPUT_SHOW_EMAIL)
|
if (*option & OUTPUT_SHOW_EMAIL)
|
||||||
num = utf8_strwidth(ci.author_mail);
|
num = utf8_strwidth(ci.author_mail.buf);
|
||||||
else
|
else
|
||||||
num = utf8_strwidth(ci.author);
|
num = utf8_strwidth(ci.author.buf);
|
||||||
if (longest_author < num)
|
if (longest_author < num)
|
||||||
longest_author = num;
|
longest_author = num;
|
||||||
}
|
}
|
||||||
@ -1890,6 +1877,8 @@ static void find_alignment(struct scoreboard *sb, int *option)
|
|||||||
longest_dst_lines = num;
|
longest_dst_lines = num;
|
||||||
if (largest_score < ent_score(sb, e))
|
if (largest_score < ent_score(sb, e))
|
||||||
largest_score = ent_score(sb, e);
|
largest_score = ent_score(sb, e);
|
||||||
|
|
||||||
|
commit_info_destroy(&ci);
|
||||||
}
|
}
|
||||||
max_orig_digits = decimal_width(longest_src_lines);
|
max_orig_digits = decimal_width(longest_src_lines);
|
||||||
max_digits = decimal_width(longest_dst_lines);
|
max_digits = decimal_width(longest_dst_lines);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "branch.h"
|
#include "branch.h"
|
||||||
#include "streaming.h"
|
#include "streaming.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "mailmap.h"
|
||||||
|
|
||||||
/* Set a default date-time format for git log ("log.date" config variable) */
|
/* Set a default date-time format for git log ("log.date" config variable) */
|
||||||
static const char *default_date_mode = NULL;
|
static const char *default_date_mode = NULL;
|
||||||
@ -30,6 +31,7 @@ static int default_abbrev_commit;
|
|||||||
static int default_show_root = 1;
|
static int default_show_root = 1;
|
||||||
static int decoration_style;
|
static int decoration_style;
|
||||||
static int decoration_given;
|
static int decoration_given;
|
||||||
|
static int use_mailmap_config;
|
||||||
static const char *fmt_patch_subject_prefix = "PATCH";
|
static const char *fmt_patch_subject_prefix = "PATCH";
|
||||||
static const char *fmt_pretty;
|
static const char *fmt_pretty;
|
||||||
|
|
||||||
@ -94,16 +96,18 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
|
|||||||
struct rev_info *rev, struct setup_revision_opt *opt)
|
struct rev_info *rev, struct setup_revision_opt *opt)
|
||||||
{
|
{
|
||||||
struct userformat_want w;
|
struct userformat_want w;
|
||||||
int quiet = 0, source = 0;
|
int quiet = 0, source = 0, mailmap = 0;
|
||||||
|
|
||||||
const struct option builtin_log_options[] = {
|
const struct option builtin_log_options[] = {
|
||||||
OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
|
OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
|
||||||
OPT_BOOLEAN(0, "source", &source, N_("show source")),
|
OPT_BOOLEAN(0, "source", &source, N_("show source")),
|
||||||
|
OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")),
|
||||||
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
|
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
|
||||||
PARSE_OPT_OPTARG, decorate_callback},
|
PARSE_OPT_OPTARG, decorate_callback},
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mailmap = use_mailmap_config;
|
||||||
argc = parse_options(argc, argv, prefix,
|
argc = parse_options(argc, argv, prefix,
|
||||||
builtin_log_options, builtin_log_usage,
|
builtin_log_options, builtin_log_usage,
|
||||||
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
|
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
|
||||||
@ -136,6 +140,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
|
|||||||
if (source)
|
if (source)
|
||||||
rev->show_source = 1;
|
rev->show_source = 1;
|
||||||
|
|
||||||
|
if (mailmap) {
|
||||||
|
rev->mailmap = xcalloc(1, sizeof(struct string_list));
|
||||||
|
read_mailmap(rev->mailmap, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
|
if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
|
||||||
/*
|
/*
|
||||||
* "log --pretty=raw" is special; ignore UI oriented
|
* "log --pretty=raw" is special; ignore UI oriented
|
||||||
@ -351,6 +360,11 @@ static int git_log_config(const char *var, const char *value, void *cb)
|
|||||||
}
|
}
|
||||||
if (!prefixcmp(var, "color.decorate."))
|
if (!prefixcmp(var, "color.decorate."))
|
||||||
return parse_decorate_color_config(var, 15, value);
|
return parse_decorate_color_config(var, 15, value);
|
||||||
|
if (!strcmp(var, "log.mailmap")) {
|
||||||
|
use_mailmap_config = git_config_bool(var, value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (grep_config(var, value, cb) < 0)
|
if (grep_config(var, value, cb) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return git_diff_ui_config(var, value, cb);
|
return git_diff_ui_config(var, value, cb);
|
||||||
|
@ -36,52 +36,28 @@ static void insert_one_record(struct shortlog *log,
|
|||||||
const char *dot3 = log->common_repo_prefix;
|
const char *dot3 = log->common_repo_prefix;
|
||||||
char *buffer, *p;
|
char *buffer, *p;
|
||||||
struct string_list_item *item;
|
struct string_list_item *item;
|
||||||
char namebuf[1024];
|
const char *mailbuf, *namebuf;
|
||||||
char emailbuf[1024];
|
size_t namelen, maillen;
|
||||||
size_t len;
|
|
||||||
const char *eol;
|
const char *eol;
|
||||||
const char *boemail, *eoemail;
|
|
||||||
struct strbuf subject = STRBUF_INIT;
|
struct strbuf subject = STRBUF_INIT;
|
||||||
|
struct strbuf namemailbuf = STRBUF_INIT;
|
||||||
|
struct ident_split ident;
|
||||||
|
|
||||||
boemail = strchr(author, '<');
|
if (split_ident_line(&ident, author, strlen(author)))
|
||||||
if (!boemail)
|
|
||||||
return;
|
|
||||||
eoemail = strchr(boemail, '>');
|
|
||||||
if (!eoemail)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* copy author name to namebuf, to support matching on both name and email */
|
namebuf = ident.name_begin;
|
||||||
memcpy(namebuf, author, boemail - author);
|
mailbuf = ident.mail_begin;
|
||||||
len = boemail - author;
|
namelen = ident.name_end - ident.name_begin;
|
||||||
while (len > 0 && isspace(namebuf[len-1]))
|
maillen = ident.mail_end - ident.mail_begin;
|
||||||
len--;
|
|
||||||
namebuf[len] = 0;
|
|
||||||
|
|
||||||
/* copy email name to emailbuf, to allow email replacement as well */
|
map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
|
||||||
memcpy(emailbuf, boemail+1, eoemail - boemail);
|
strbuf_add(&namemailbuf, namebuf, namelen);
|
||||||
emailbuf[eoemail - boemail - 1] = 0;
|
|
||||||
|
|
||||||
if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
|
if (log->email)
|
||||||
while (author < boemail && isspace(*author))
|
strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf);
|
||||||
author++;
|
|
||||||
for (len = 0;
|
|
||||||
len < sizeof(namebuf) - 1 && author + len < boemail;
|
|
||||||
len++)
|
|
||||||
namebuf[len] = author[len];
|
|
||||||
while (0 < len && isspace(namebuf[len-1]))
|
|
||||||
len--;
|
|
||||||
namebuf[len] = '\0';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
len = strlen(namebuf);
|
|
||||||
|
|
||||||
if (log->email) {
|
item = string_list_insert(&log->list, namemailbuf.buf);
|
||||||
size_t room = sizeof(namebuf) - len - 1;
|
|
||||||
int maillen = strlen(emailbuf);
|
|
||||||
snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
item = string_list_insert(&log->list, namebuf);
|
|
||||||
if (item->util == NULL)
|
if (item->util == NULL)
|
||||||
item->util = xcalloc(1, sizeof(struct string_list));
|
item->util = xcalloc(1, sizeof(struct string_list));
|
||||||
|
|
||||||
|
1
commit.h
1
commit.h
@ -89,6 +89,7 @@ struct pretty_print_context {
|
|||||||
char *notes_message;
|
char *notes_message;
|
||||||
struct reflog_walk_info *reflog_info;
|
struct reflog_walk_info *reflog_info;
|
||||||
const char *output_encoding;
|
const char *output_encoding;
|
||||||
|
struct string_list *mailmap;
|
||||||
int color;
|
int color;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -681,6 +681,7 @@ void show_log(struct rev_info *opt)
|
|||||||
ctx.preserve_subject = opt->preserve_subject;
|
ctx.preserve_subject = opt->preserve_subject;
|
||||||
ctx.reflog_info = opt->reflog_info;
|
ctx.reflog_info = opt->reflog_info;
|
||||||
ctx.fmt = opt->commit_format;
|
ctx.fmt = opt->commit_format;
|
||||||
|
ctx.mailmap = opt->mailmap;
|
||||||
ctx.color = opt->diffopt.use_color;
|
ctx.color = opt->diffopt.use_color;
|
||||||
pretty_print_commit(&ctx, commit, &msgbuf);
|
pretty_print_commit(&ctx, commit, &msgbuf);
|
||||||
|
|
||||||
|
106
mailmap.c
106
mailmap.c
@ -235,6 +235,7 @@ int read_mailmap(struct string_list *map, char **repo_abbrev)
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
map->strdup_strings = 1;
|
map->strdup_strings = 1;
|
||||||
|
map->cmp = strcasecmp;
|
||||||
|
|
||||||
if (!git_mailmap_blob && is_bare_repository())
|
if (!git_mailmap_blob && is_bare_repository())
|
||||||
git_mailmap_blob = "HEAD:.mailmap";
|
git_mailmap_blob = "HEAD:.mailmap";
|
||||||
@ -253,60 +254,95 @@ void clear_mailmap(struct string_list *map)
|
|||||||
debug_mm("mailmap: cleared\n");
|
debug_mm("mailmap: cleared\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int map_user(struct string_list *map,
|
/*
|
||||||
char *email, int maxlen_email, char *name, int maxlen_name)
|
* Look for an entry in map that match string[0:len]; string[len]
|
||||||
|
* does not have to be NUL (but it could be).
|
||||||
|
*/
|
||||||
|
static struct string_list_item *lookup_prefix(struct string_list *map,
|
||||||
|
const char *string, size_t len)
|
||||||
|
{
|
||||||
|
int i = string_list_find_insert_index(map, string, 1);
|
||||||
|
if (i < 0) {
|
||||||
|
/* exact match */
|
||||||
|
i = -1 - i;
|
||||||
|
if (!string[len])
|
||||||
|
return &map->items[i];
|
||||||
|
/*
|
||||||
|
* that map entry matches exactly to the string, including
|
||||||
|
* the cruft at the end beyond "len". That is not a match
|
||||||
|
* with string[0:len] that we are looking for.
|
||||||
|
*/
|
||||||
|
} else if (!string[len]) {
|
||||||
|
/*
|
||||||
|
* asked with the whole string, and got nothing. No
|
||||||
|
* matching entry can exist in the map.
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* i is at the exact match to an overlong key, or location the
|
||||||
|
* overlong key would be inserted, which must come after the
|
||||||
|
* real location of the key if one exists.
|
||||||
|
*/
|
||||||
|
while (0 <= --i && i < map->nr) {
|
||||||
|
int cmp = strncasecmp(map->items[i].string, string, len);
|
||||||
|
if (cmp < 0)
|
||||||
|
/*
|
||||||
|
* "i" points at a key definitely below the prefix;
|
||||||
|
* the map does not have string[0:len] in it.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
else if (!cmp && !map->items[i].string[len])
|
||||||
|
/* found it */
|
||||||
|
return &map->items[i];
|
||||||
|
/*
|
||||||
|
* otherwise, the string at "i" may be string[0:len]
|
||||||
|
* followed by a string that sorts later than string[len:];
|
||||||
|
* keep trying.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_user(struct string_list *map,
|
||||||
|
const char **email, size_t *emaillen,
|
||||||
|
const char **name, size_t *namelen)
|
||||||
{
|
{
|
||||||
char *end_of_email;
|
|
||||||
struct string_list_item *item;
|
struct string_list_item *item;
|
||||||
struct mailmap_entry *me;
|
struct mailmap_entry *me;
|
||||||
char buf[1024], *mailbuf;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* figure out space requirement for email */
|
debug_mm("map_user: map '%.*s' <%.*s>\n",
|
||||||
end_of_email = strchr(email, '>');
|
*name, *namelen, *emaillen, *email);
|
||||||
if (!end_of_email) {
|
|
||||||
/* email passed in might not be wrapped in <>, but end with a \0 */
|
|
||||||
end_of_email = memchr(email, '\0', maxlen_email);
|
|
||||||
if (!end_of_email)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (end_of_email - email + 1 < sizeof(buf))
|
|
||||||
mailbuf = buf;
|
|
||||||
else
|
|
||||||
mailbuf = xmalloc(end_of_email - email + 1);
|
|
||||||
|
|
||||||
/* downcase the email address */
|
item = lookup_prefix(map, *email, *emaillen);
|
||||||
for (i = 0; i < end_of_email - email; i++)
|
|
||||||
mailbuf[i] = tolower(email[i]);
|
|
||||||
mailbuf[i] = 0;
|
|
||||||
|
|
||||||
debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
|
|
||||||
item = string_list_lookup(map, mailbuf);
|
|
||||||
if (item != NULL) {
|
if (item != NULL) {
|
||||||
me = (struct mailmap_entry *)item->util;
|
me = (struct mailmap_entry *)item->util;
|
||||||
if (me->namemap.nr) {
|
if (me->namemap.nr) {
|
||||||
/* The item has multiple items, so we'll look up on name too */
|
/* The item has multiple items, so we'll look up on name too */
|
||||||
/* If the name is not found, we choose the simple entry */
|
/* If the name is not found, we choose the simple entry */
|
||||||
struct string_list_item *subitem = string_list_lookup(&me->namemap, name);
|
struct string_list_item *subitem;
|
||||||
|
subitem = lookup_prefix(&me->namemap, *name, *namelen);
|
||||||
if (subitem)
|
if (subitem)
|
||||||
item = subitem;
|
item = subitem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mailbuf != buf)
|
|
||||||
free(mailbuf);
|
|
||||||
if (item != NULL) {
|
if (item != NULL) {
|
||||||
struct mailmap_info *mi = (struct mailmap_info *)item->util;
|
struct mailmap_info *mi = (struct mailmap_info *)item->util;
|
||||||
if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {
|
if (mi->name == NULL && mi->email == NULL) {
|
||||||
debug_mm("map_user: -- (no simple mapping)\n");
|
debug_mm("map_user: -- (no simple mapping)\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (maxlen_email && mi->email)
|
if (mi->email) {
|
||||||
strlcpy(email, mi->email, maxlen_email);
|
*email = mi->email;
|
||||||
else
|
*emaillen = strlen(*email);
|
||||||
*end_of_email = '\0';
|
}
|
||||||
if (maxlen_name && mi->name)
|
if (mi->name) {
|
||||||
strlcpy(name, mi->name, maxlen_name);
|
*name = mi->name;
|
||||||
debug_mm("map_user: to '%s' <%s>\n", name, mi->email ? mi->email : "");
|
*namelen = strlen(*name);
|
||||||
|
}
|
||||||
|
debug_mm("map_user: to '%.*s' <.*%s>\n", *namelen, *name,
|
||||||
|
*emaillen, *email);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
debug_mm("map_user: --\n");
|
debug_mm("map_user: --\n");
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
int read_mailmap(struct string_list *map, char **repo_abbrev);
|
int read_mailmap(struct string_list *map, char **repo_abbrev);
|
||||||
void clear_mailmap(struct string_list *map);
|
void clear_mailmap(struct string_list *map);
|
||||||
|
|
||||||
int map_user(struct string_list *mailmap,
|
int map_user(struct string_list *map,
|
||||||
char *email, int maxlen_email, char *name, int maxlen_name);
|
const char **email, size_t *emaillen, const char **name, size_t *namelen);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
112
pretty.c
112
pretty.c
@ -387,56 +387,79 @@ void pp_user_info(const struct pretty_print_context *pp,
|
|||||||
const char *what, struct strbuf *sb,
|
const char *what, struct strbuf *sb,
|
||||||
const char *line, const char *encoding)
|
const char *line, const char *encoding)
|
||||||
{
|
{
|
||||||
|
struct strbuf name;
|
||||||
|
struct strbuf mail;
|
||||||
|
struct ident_split ident;
|
||||||
|
int linelen;
|
||||||
|
char *line_end, *date;
|
||||||
|
const char *mailbuf, *namebuf;
|
||||||
|
size_t namelen, maillen;
|
||||||
int max_length = 78; /* per rfc2822 */
|
int max_length = 78; /* per rfc2822 */
|
||||||
char *date;
|
|
||||||
int namelen;
|
|
||||||
unsigned long time;
|
unsigned long time;
|
||||||
int tz;
|
int tz;
|
||||||
|
|
||||||
if (pp->fmt == CMIT_FMT_ONELINE)
|
if (pp->fmt == CMIT_FMT_ONELINE)
|
||||||
return;
|
return;
|
||||||
date = strchr(line, '>');
|
|
||||||
if (!date)
|
line_end = strchr(line, '\n');
|
||||||
|
if (!line_end) {
|
||||||
|
line_end = strchr(line, '\0');
|
||||||
|
if (!line_end)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
linelen = ++line_end - line;
|
||||||
|
if (split_ident_line(&ident, line, linelen))
|
||||||
return;
|
return;
|
||||||
namelen = ++date - line;
|
|
||||||
time = strtoul(date, &date, 10);
|
|
||||||
|
mailbuf = ident.mail_begin;
|
||||||
|
maillen = ident.mail_end - ident.mail_begin;
|
||||||
|
namebuf = ident.name_begin;
|
||||||
|
namelen = ident.name_end - ident.name_begin;
|
||||||
|
|
||||||
|
if (pp->mailmap)
|
||||||
|
map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
|
||||||
|
|
||||||
|
strbuf_init(&mail, 0);
|
||||||
|
strbuf_init(&name, 0);
|
||||||
|
|
||||||
|
strbuf_add(&mail, mailbuf, maillen);
|
||||||
|
strbuf_add(&name, namebuf, namelen);
|
||||||
|
|
||||||
|
namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */
|
||||||
|
time = strtoul(ident.date_begin, &date, 10);
|
||||||
tz = strtol(date, NULL, 10);
|
tz = strtol(date, NULL, 10);
|
||||||
|
|
||||||
if (pp->fmt == CMIT_FMT_EMAIL) {
|
if (pp->fmt == CMIT_FMT_EMAIL) {
|
||||||
char *name_tail = strchr(line, '<');
|
|
||||||
int display_name_length;
|
|
||||||
if (!name_tail)
|
|
||||||
return;
|
|
||||||
while (line < name_tail && isspace(name_tail[-1]))
|
|
||||||
name_tail--;
|
|
||||||
display_name_length = name_tail - line;
|
|
||||||
strbuf_addstr(sb, "From: ");
|
strbuf_addstr(sb, "From: ");
|
||||||
if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {
|
if (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) {
|
||||||
add_rfc2047(sb, line, display_name_length,
|
add_rfc2047(sb, name.buf, name.len,
|
||||||
encoding, RFC2047_ADDRESS);
|
encoding, RFC2047_ADDRESS);
|
||||||
max_length = 76; /* per rfc2047 */
|
max_length = 76; /* per rfc2047 */
|
||||||
} else if (needs_rfc822_quoting(line, display_name_length)) {
|
} else if (needs_rfc822_quoting(name.buf, name.len)) {
|
||||||
struct strbuf quoted = STRBUF_INIT;
|
struct strbuf quoted = STRBUF_INIT;
|
||||||
add_rfc822_quoted("ed, line, display_name_length);
|
add_rfc822_quoted("ed, name.buf, name.len);
|
||||||
strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
|
strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
|
||||||
-6, 1, max_length);
|
-6, 1, max_length);
|
||||||
strbuf_release("ed);
|
strbuf_release("ed);
|
||||||
} else {
|
} else {
|
||||||
strbuf_add_wrapped_bytes(sb, line, display_name_length,
|
strbuf_add_wrapped_bytes(sb, name.buf, name.len,
|
||||||
-6, 1, max_length);
|
-6, 1, max_length);
|
||||||
}
|
}
|
||||||
if (namelen - display_name_length + last_line_length(sb) > max_length) {
|
if (namelen - name.len + last_line_length(sb) > max_length)
|
||||||
strbuf_addch(sb, '\n');
|
strbuf_addch(sb, '\n');
|
||||||
if (!isspace(name_tail[0]))
|
|
||||||
strbuf_addch(sb, ' ');
|
strbuf_addf(sb, " <%s>\n", mail.buf);
|
||||||
}
|
|
||||||
strbuf_add(sb, name_tail, namelen - display_name_length);
|
|
||||||
strbuf_addch(sb, '\n');
|
|
||||||
} else {
|
} else {
|
||||||
strbuf_addf(sb, "%s: %.*s%.*s\n", what,
|
strbuf_addf(sb, "%s: %.*s%s <%s>\n", what,
|
||||||
(pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
|
(pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
|
||||||
" ", namelen, line);
|
" ", name.buf, mail.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strbuf_release(&mail);
|
||||||
|
strbuf_release(&name);
|
||||||
|
|
||||||
switch (pp->fmt) {
|
switch (pp->fmt) {
|
||||||
case CMIT_FMT_MEDIUM:
|
case CMIT_FMT_MEDIUM:
|
||||||
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, pp->date_mode));
|
strbuf_addf(sb, "Date: %s\n", show_date(time, tz, pp->date_mode));
|
||||||
@ -586,7 +609,8 @@ char *logmsg_reencode(const struct commit *commit,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mailmap_name(char *email, int email_len, char *name, int name_len)
|
static int mailmap_name(const char **email, size_t *email_len,
|
||||||
|
const char **name, size_t *name_len)
|
||||||
{
|
{
|
||||||
static struct string_list *mail_map;
|
static struct string_list *mail_map;
|
||||||
if (!mail_map) {
|
if (!mail_map) {
|
||||||
@ -603,36 +627,26 @@ static size_t format_person_part(struct strbuf *sb, char part,
|
|||||||
const int placeholder_len = 2;
|
const int placeholder_len = 2;
|
||||||
int tz;
|
int tz;
|
||||||
unsigned long date = 0;
|
unsigned long date = 0;
|
||||||
char person_name[1024];
|
|
||||||
char person_mail[1024];
|
|
||||||
struct ident_split s;
|
struct ident_split s;
|
||||||
const char *name_start, *name_end, *mail_start, *mail_end;
|
const char *name, *mail;
|
||||||
|
size_t maillen, namelen;
|
||||||
|
|
||||||
if (split_ident_line(&s, msg, len) < 0)
|
if (split_ident_line(&s, msg, len) < 0)
|
||||||
goto skip;
|
goto skip;
|
||||||
|
|
||||||
name_start = s.name_begin;
|
name = s.name_begin;
|
||||||
name_end = s.name_end;
|
namelen = s.name_end - s.name_begin;
|
||||||
mail_start = s.mail_begin;
|
mail = s.mail_begin;
|
||||||
mail_end = s.mail_end;
|
maillen = s.mail_end - s.mail_begin;
|
||||||
|
|
||||||
if (part == 'N' || part == 'E') { /* mailmap lookup */
|
if (part == 'N' || part == 'E') /* mailmap lookup */
|
||||||
snprintf(person_name, sizeof(person_name), "%.*s",
|
mailmap_name(&mail, &maillen, &name, &namelen);
|
||||||
(int)(name_end - name_start), name_start);
|
|
||||||
snprintf(person_mail, sizeof(person_mail), "%.*s",
|
|
||||||
(int)(mail_end - mail_start), mail_start);
|
|
||||||
mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
|
|
||||||
name_start = person_name;
|
|
||||||
name_end = name_start + strlen(person_name);
|
|
||||||
mail_start = person_mail;
|
|
||||||
mail_end = mail_start + strlen(person_mail);
|
|
||||||
}
|
|
||||||
if (part == 'n' || part == 'N') { /* name */
|
if (part == 'n' || part == 'N') { /* name */
|
||||||
strbuf_add(sb, name_start, name_end-name_start);
|
strbuf_add(sb, name, namelen);
|
||||||
return placeholder_len;
|
return placeholder_len;
|
||||||
}
|
}
|
||||||
if (part == 'e' || part == 'E') { /* email */
|
if (part == 'e' || part == 'E') { /* email */
|
||||||
strbuf_add(sb, mail_start, mail_end-mail_start);
|
strbuf_add(sb, mail, maillen);
|
||||||
return placeholder_len;
|
return placeholder_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
54
revision.c
54
revision.c
@ -13,6 +13,7 @@
|
|||||||
#include "decorate.h"
|
#include "decorate.h"
|
||||||
#include "log-tree.h"
|
#include "log-tree.h"
|
||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
|
#include "mailmap.h"
|
||||||
|
|
||||||
volatile show_early_output_fn_t show_early_output;
|
volatile show_early_output_fn_t show_early_output;
|
||||||
|
|
||||||
@ -2219,6 +2220,51 @@ static 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;
|
||||||
@ -2237,6 +2283,14 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
|
|||||||
if (buf.len)
|
if (buf.len)
|
||||||
strbuf_addstr(&buf, commit->buffer);
|
strbuf_addstr(&buf, commit->buffer);
|
||||||
|
|
||||||
|
if (opt->grep_filter.header_list && opt->mailmap) {
|
||||||
|
if (!buf.len)
|
||||||
|
strbuf_addstr(&buf, commit->buffer);
|
||||||
|
|
||||||
|
commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
|
||||||
|
commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
|
||||||
|
}
|
||||||
|
|
||||||
/* Append "fake" message parts as needed */
|
/* Append "fake" message parts as needed */
|
||||||
if (opt->show_notes) {
|
if (opt->show_notes) {
|
||||||
if (!buf.len)
|
if (!buf.len)
|
||||||
|
@ -144,6 +144,7 @@ struct rev_info {
|
|||||||
const char *subject_prefix;
|
const char *subject_prefix;
|
||||||
int no_inline;
|
int no_inline;
|
||||||
int show_log_size;
|
int show_log_size;
|
||||||
|
struct string_list *mailmap;
|
||||||
|
|
||||||
/* Filter by commit log message */
|
/* Filter by commit log message */
|
||||||
struct grep_opt grep_filter;
|
struct grep_opt grep_filter;
|
||||||
|
@ -7,10 +7,11 @@ static int get_entry_index(const struct string_list *list, const char *string,
|
|||||||
int *exact_match)
|
int *exact_match)
|
||||||
{
|
{
|
||||||
int left = -1, right = list->nr;
|
int left = -1, right = list->nr;
|
||||||
|
compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
|
||||||
|
|
||||||
while (left + 1 < right) {
|
while (left + 1 < right) {
|
||||||
int middle = (left + right) / 2;
|
int middle = (left + right) / 2;
|
||||||
int compare = strcmp(string, list->items[middle].string);
|
int compare = cmp(string, list->items[middle].string);
|
||||||
if (compare < 0)
|
if (compare < 0)
|
||||||
right = middle;
|
right = middle;
|
||||||
else if (compare > 0)
|
else if (compare > 0)
|
||||||
@ -96,8 +97,9 @@ void string_list_remove_duplicates(struct string_list *list, int free_util)
|
|||||||
{
|
{
|
||||||
if (list->nr > 1) {
|
if (list->nr > 1) {
|
||||||
int src, dst;
|
int src, dst;
|
||||||
|
compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
|
||||||
for (src = dst = 1; src < list->nr; src++) {
|
for (src = dst = 1; src < list->nr; src++) {
|
||||||
if (!strcmp(list->items[dst - 1].string, list->items[src].string)) {
|
if (!cmp(list->items[dst - 1].string, list->items[src].string)) {
|
||||||
if (list->strdup_strings)
|
if (list->strdup_strings)
|
||||||
free(list->items[src].string);
|
free(list->items[src].string);
|
||||||
if (free_util)
|
if (free_util)
|
||||||
@ -210,15 +212,20 @@ struct string_list_item *string_list_append(struct string_list *list,
|
|||||||
list->strdup_strings ? xstrdup(string) : (char *)string);
|
list->strdup_strings ? xstrdup(string) : (char *)string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Yuck */
|
||||||
|
static compare_strings_fn compare_for_qsort;
|
||||||
|
|
||||||
|
/* Only call this from inside sort_string_list! */
|
||||||
static int cmp_items(const void *a, const void *b)
|
static int cmp_items(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
const struct string_list_item *one = a;
|
const struct string_list_item *one = a;
|
||||||
const struct string_list_item *two = b;
|
const struct string_list_item *two = b;
|
||||||
return strcmp(one->string, two->string);
|
return compare_for_qsort(one->string, two->string);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sort_string_list(struct string_list *list)
|
void sort_string_list(struct string_list *list)
|
||||||
{
|
{
|
||||||
|
compare_for_qsort = list->cmp ? list->cmp : strcmp;
|
||||||
qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
|
qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,8 +233,10 @@ struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
|
|||||||
const char *string)
|
const char *string)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
|
||||||
|
|
||||||
for (i = 0; i < list->nr; i++)
|
for (i = 0; i < list->nr; i++)
|
||||||
if (!strcmp(string, list->items[i].string))
|
if (!cmp(string, list->items[i].string))
|
||||||
return list->items + i;
|
return list->items + i;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,14 @@ struct string_list_item {
|
|||||||
char *string;
|
char *string;
|
||||||
void *util;
|
void *util;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef int (*compare_strings_fn)(const char *, const char *);
|
||||||
|
|
||||||
struct string_list {
|
struct string_list {
|
||||||
struct string_list_item *items;
|
struct string_list_item *items;
|
||||||
unsigned int nr, alloc;
|
unsigned int nr, alloc;
|
||||||
unsigned int strdup_strings:1;
|
unsigned int strdup_strings:1;
|
||||||
|
compare_strings_fn cmp; /* NULL uses strcmp() */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
|
#define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
|
||||||
|
@ -337,6 +337,62 @@ test_expect_success 'Log output (complex mapping)' '
|
|||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
cat >expect <<\EOF
|
||||||
|
Author: CTO <cto@company.xx>
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
Author: Other Author <other@author.xx>
|
||||||
|
Author: Other Author <other@author.xx>
|
||||||
|
Author: Some Dude <some@dude.xx>
|
||||||
|
Author: A U Thor <author@example.com>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Log output with --use-mailmap' '
|
||||||
|
git log --use-mailmap | grep Author >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expect <<\EOF
|
||||||
|
Author: CTO <cto@company.xx>
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
Author: Other Author <other@author.xx>
|
||||||
|
Author: Other Author <other@author.xx>
|
||||||
|
Author: Some Dude <some@dude.xx>
|
||||||
|
Author: A U Thor <author@example.com>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Log output with log.mailmap' '
|
||||||
|
git -c log.mailmap=True log | grep Author >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expect <<\EOF
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Grep author with --use-mailmap' '
|
||||||
|
git log --use-mailmap --author Santa | grep Author >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
cat >expect <<\EOF
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
Author: Santa Claus <santa.claus@northpole.xx>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'Grep author with log.mailmap' '
|
||||||
|
git -c log.mailmap=True log --author Santa | grep Author >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
>expect
|
||||||
|
|
||||||
|
test_expect_success 'Only grep replaced author with --use-mailmap' '
|
||||||
|
git log --use-mailmap --author "<cto@coompany.xx>" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
# git blame
|
# git blame
|
||||||
cat >expect <<\EOF
|
cat >expect <<\EOF
|
||||||
^OBJI (A U Thor DATE 1) one
|
^OBJI (A U Thor DATE 1) one
|
||||||
|
Loading…
Reference in New Issue
Block a user