Merge branch 'ab/mailmap'

Clean-up docs, codepaths and tests around mailmap.

* ab/mailmap: (22 commits)
  shortlog: remove unused(?) "repo-abbrev" feature
  mailmap doc + tests: document and test for case-insensitivity
  mailmap tests: add tests for empty "<>" syntax
  mailmap tests: add tests for whitespace syntax
  mailmap tests: add a test for comment syntax
  mailmap doc + tests: add better examples & test them
  tests: refactor a few tests to use "test_commit --append"
  test-lib functions: add an --append option to test_commit
  test-lib functions: add --author support to test_commit
  test-lib functions: document arguments to test_commit
  test-lib functions: expand "test_commit" comment template
  mailmap: test for silent exiting on missing file/blob
  mailmap tests: get rid of overly complex blame fuzzing
  mailmap tests: add a test for "not a blob" error
  mailmap tests: remove redundant entry in test
  mailmap tests: improve --stdin tests
  mailmap tests: modernize syntax & test idioms
  mailmap tests: use our preferred whitespace syntax
  mailmap doc: start by mentioning the comment syntax
  check-mailmap doc: note config options
  ...
This commit is contained in:
Junio C Hamano 2021-01-25 14:19:19 -08:00
commit 42342b3ee6
22 changed files with 789 additions and 447 deletions

View File

@ -21,6 +21,7 @@ MAN1_TXT += gitweb.txt
MAN5_TXT += gitattributes.txt
MAN5_TXT += githooks.txt
MAN5_TXT += gitignore.txt
MAN5_TXT += gitmailmap.txt
MAN5_TXT += gitmodules.txt
MAN5_TXT += gitrepository-layout.txt
MAN5_TXT += gitweb.conf.txt

View File

@ -226,7 +226,7 @@ commit commentary), a blame viewer will not care.
MAPPING AUTHORS
---------------
include::mailmap.txt[]
See linkgit:gitmailmap[5].
SEE ALSO

View File

@ -36,10 +36,17 @@ name is provided or known to the 'mailmap', ``Name $$<user@host>$$'' is
printed; otherwise only ``$$<user@host>$$'' is printed.
CONFIGURATION
-------------
See `mailmap.file` and `mailmap.blob` in linkgit:git-config[1] for how
to specify a custom `.mailmap` target file or object.
MAPPING AUTHORS
---------------
include::mailmap.txt[]
See linkgit:gitmailmap[5].
GIT

View File

@ -111,11 +111,7 @@ include::rev-list-options.txt[]
MAPPING AUTHORS
---------------
The `.mailmap` feature is used to coalesce together commits by the same
person in the shortlog, where their name and/or email address was
spelled differently.
include::mailmap.txt[]
See linkgit:gitmailmap[5].
GIT
---

View File

@ -0,0 +1,123 @@
gitmailmap(5)
=============
NAME
----
gitmailmap - Map author/committer names and/or E-Mail addresses
SYNOPSIS
--------
$GIT_WORK_DIR/.mailmap
DESCRIPTION
-----------
If the file `.mailmap` exists at the toplevel of the repository, or at
the location pointed to by the `mailmap.file` or `mailmap.blob`
configuration options (see linkgit:git-config[1]), it
is used to map author and committer names and email addresses to
canonical real names and email addresses.
SYNTAX
------
The '#' character begins a comment to the end of line, blank lines
are ignored.
In the simple form, each line in the file consists of the canonical
real name of an author, whitespace, and an email address used in the
commit (enclosed by '<' and '>') to map to the name. For example:
--
Proper Name <commit@email.xx>
--
The more complex forms are:
--
<proper@email.xx> <commit@email.xx>
--
which allows mailmap to replace only the email part of a commit, and:
--
Proper Name <proper@email.xx> <commit@email.xx>
--
which allows mailmap to replace both the name and the email of a
commit matching the specified commit email address, and:
--
Proper Name <proper@email.xx> Commit Name <commit@email.xx>
--
which allows mailmap to replace both the name and the email of a
commit matching both the specified commit name and email address.
Both E-Mails and names are matched case-insensitively. For example
this would also match the 'Commit Name <commit@email.xx>' above:
--
Proper Name <proper@email.xx> CoMmIt NaMe <CoMmIt@EmAiL.xX>
--
EXAMPLES
--------
Your history contains commits by two authors, Jane
and Joe, whose names appear in the repository under several forms:
------------
Joe Developer <joe@example.com>
Joe R. Developer <joe@example.com>
Jane Doe <jane@example.com>
Jane Doe <jane@laptop.(none)>
Jane D. <jane@desktop.(none)>
------------
Now suppose that Joe wants his middle name initial used, and Jane
prefers her family name fully spelled out. A `.mailmap` file to
correct the names would look like:
------------
Joe R. Developer <joe@example.com>
Jane Doe <jane@example.com>
Jane Doe <jane@desktop.(none)>
------------
Note that there's no need to map the name for 'jane@laptop.(none)' to
only correct the names. However, leaving the obviously broken
`<jane@laptop.(none)>' and '<jane@desktop.(none)>' E-Mails as-is is
usually not what you want. A `.mailmap` file which also corrects those
is:
------------
Joe R. Developer <joe@example.com>
Jane Doe <jane@example.com> <jane@laptop.(none)>
Jane Doe <jane@example.com> <jane@desktop.(none)>
------------
Finally, let's say that Joe and Jane shared an E-Mail address, but not
a name, e.g. by having these two commits in the history generated by a
bug reporting system. I.e. names appearing in history as:
------------
Joe <bugs@example.com>
Jane <bugs@example.com>
------------
A full `.mailmap` file which also handles those cases (an addition of
two lines to the above example) would be:
------------
Joe R. Developer <joe@example.com>
Jane Doe <jane@example.com> <jane@laptop.(none)>
Jane Doe <jane@example.com> <jane@desktop.(none)>
Joe R. Developer <joe@example.com> Joe <bugs@example.com>
Jane Doe <jane@example.com> Jane <bugs@example.com>
------------
SEE ALSO
--------
linkgit:git-check-mailmap[1]
GIT
---
Part of the linkgit:git[1] suite

View File

@ -1,75 +0,0 @@
If the file `.mailmap` exists at the toplevel of the repository, or at
the location pointed to by the mailmap.file or mailmap.blob
configuration options, it
is used to map author and committer names and email addresses to
canonical real names and email addresses.
In the simple form, each line in the file consists of the canonical
real name of an author, whitespace, and an email address used in the
commit (enclosed by '<' and '>') to map to the name. For example:
--
Proper Name <commit@email.xx>
--
The more complex forms are:
--
<proper@email.xx> <commit@email.xx>
--
which allows mailmap to replace only the email part of a commit, and:
--
Proper Name <proper@email.xx> <commit@email.xx>
--
which allows mailmap to replace both the name and the email of a
commit matching the specified commit email address, and:
--
Proper Name <proper@email.xx> Commit Name <commit@email.xx>
--
which allows mailmap to replace both the name and the email of a
commit matching both the specified commit name and email address.
Example 1: Your history contains commits by two authors, Jane
and Joe, whose names appear in the repository under several forms:
------------
Joe Developer <joe@example.com>
Joe R. Developer <joe@example.com>
Jane Doe <jane@example.com>
Jane Doe <jane@laptop.(none)>
Jane D. <jane@desktop.(none)>
------------
Now suppose that Joe wants his middle name initial used, and Jane
prefers her family name fully spelled out. A proper `.mailmap` file
would look like:
------------
Jane Doe <jane@desktop.(none)>
Joe R. Developer <joe@example.com>
------------
Note how there is no need for an entry for `<jane@laptop.(none)>`, because the
real name of that author is already correct.
Example 2: Your repository contains commits from the following
authors:
------------
nick1 <bugs@company.xx>
nick2 <bugs@company.xx>
nick2 <nick2@company.xx>
santa <me@company.xx>
claus <me@company.xx>
CTO <cto@coompany.xx>
------------
Then you might want a `.mailmap` file that looks like:
------------
<cto@company.xx> <cto@coompany.xx>
Some Dude <some@dude.xx> nick1 <bugs@company.xx>
Other Author <other@author.xx> nick2 <bugs@company.xx>
Other Author <other@author.xx> <nick2@company.xx>
Santa Claus <santa.claus@northpole.xx> <me@company.xx>
------------
Use hash '#' for comments that are either on their own line, or after
the email address.

View File

@ -1151,7 +1151,7 @@ parse_done:
sb.xdl_opts = xdl_opts;
sb.no_whole_file_rename = no_whole_file_rename;
read_mailmap(&mailmap, NULL);
read_mailmap(&mailmap);
sb.found_guilty_entry = &found_guilty_entry;
sb.found_guilty_entry_data = &pi;

View File

@ -47,7 +47,7 @@ int cmd_check_mailmap(int argc, const char **argv, const char *prefix)
if (argc == 0 && !use_stdin)
die(_("no contacts specified"));
read_mailmap(&mailmap, NULL);
read_mailmap(&mailmap);
for (i = 0; i < argc; ++i)
check_mailmap(&mailmap, argv[i]);

View File

@ -1039,7 +1039,7 @@ static const char *find_author_by_nickname(const char *name)
av[++ac] = NULL;
setup_revisions(ac, av, &revs, NULL);
revs.mailmap = &mailmap;
read_mailmap(revs.mailmap, NULL);
read_mailmap(revs.mailmap);
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));

View File

@ -230,7 +230,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (mailmap) {
rev->mailmap = xcalloc(1, sizeof(struct string_list));
read_mailmap(rev->mailmap, NULL);
read_mailmap(rev->mailmap);
}
if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {

View File

@ -61,8 +61,7 @@ static void insert_one_record(struct shortlog *log,
if (log->summary)
item->util = (void *)(UTIL_TO_INT(item) + 1);
else {
const char *dot3 = log->common_repo_prefix;
char *buffer, *p;
char *buffer;
struct strbuf subject = STRBUF_INIT;
const char *eol;
@ -82,17 +81,6 @@ static void insert_one_record(struct shortlog *log,
format_subject(&subject, oneline, " ");
buffer = strbuf_detach(&subject, NULL);
if (dot3) {
int dot3len = strlen(dot3);
if (dot3len > 5) {
while ((p = strstr(buffer, dot3)) != NULL) {
int taillen = strlen(p) - dot3len;
memcpy(p, "/.../", 5);
memmove(p + 5, p + dot3len, taillen + 1);
}
}
}
if (item->util == NULL)
item->util = xcalloc(1, sizeof(struct string_list));
string_list_append(item->util, buffer);
@ -342,7 +330,7 @@ void shortlog_init(struct shortlog *log)
{
memset(log, 0, sizeof(*log));
read_mailmap(&log->mailmap, &log->common_repo_prefix);
read_mailmap(&log->mailmap);
log->list.strdup_strings = 1;
log->wrap = DEFAULT_WRAPLEN;

View File

@ -204,6 +204,7 @@ gitfaq guide
gitglossary guide
githooks guide
gitignore guide
gitmailmap guide
gitmodules guide
gitnamespaces guide
gitremote-helpers guide

View File

@ -143,31 +143,13 @@ static char *parse_name_and_email(char *buffer, char **name,
return (*right == '\0' ? NULL : right);
}
static void read_mailmap_line(struct string_list *map, char *buffer,
char **repo_abbrev)
static void read_mailmap_line(struct string_list *map, char *buffer)
{
char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
if (buffer[0] == '#') {
static const char abbrev[] = "# repo-abbrev:";
int abblen = sizeof(abbrev) - 1;
int len = strlen(buffer);
if (!repo_abbrev)
return;
if (len && buffer[len - 1] == '\n')
buffer[--len] = 0;
if (!strncmp(buffer, abbrev, abblen)) {
char *cp;
free(*repo_abbrev);
for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
*repo_abbrev = xstrdup(cp);
}
if (buffer[0] == '#')
return;
}
if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
parse_name_and_email(name2, &name2, &email2, 1);
@ -175,8 +157,7 @@ static void read_mailmap_line(struct string_list *map, char *buffer,
add_mapping(map, name1, email1, name2, email2);
}
static int read_mailmap_file(struct string_list *map, const char *filename,
char **repo_abbrev)
static int read_mailmap_file(struct string_list *map, const char *filename)
{
char buffer[1024];
FILE *f;
@ -192,13 +173,12 @@ static int read_mailmap_file(struct string_list *map, const char *filename,
}
while (fgets(buffer, sizeof(buffer), f) != NULL)
read_mailmap_line(map, buffer, repo_abbrev);
read_mailmap_line(map, buffer);
fclose(f);
return 0;
}
static void read_mailmap_string(struct string_list *map, char *buf,
char **repo_abbrev)
static void read_mailmap_string(struct string_list *map, char *buf)
{
while (*buf) {
char *end = strchrnul(buf, '\n');
@ -206,14 +186,12 @@ static void read_mailmap_string(struct string_list *map, char *buf,
if (*end)
*end++ = '\0';
read_mailmap_line(map, buf, repo_abbrev);
read_mailmap_line(map, buf);
buf = end;
}
}
static int read_mailmap_blob(struct string_list *map,
const char *name,
char **repo_abbrev)
static int read_mailmap_blob(struct string_list *map, const char *name)
{
struct object_id oid;
char *buf;
@ -231,13 +209,13 @@ static int read_mailmap_blob(struct string_list *map,
if (type != OBJ_BLOB)
return error("mailmap is not a blob: %s", name);
read_mailmap_string(map, buf, repo_abbrev);
read_mailmap_string(map, buf);
free(buf);
return 0;
}
int read_mailmap(struct string_list *map, char **repo_abbrev)
int read_mailmap(struct string_list *map)
{
int err = 0;
@ -247,10 +225,10 @@ int read_mailmap(struct string_list *map, char **repo_abbrev)
if (!git_mailmap_blob && is_bare_repository())
git_mailmap_blob = "HEAD:.mailmap";
err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
err |= read_mailmap_file(map, ".mailmap");
if (startup_info->have_repository)
err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
err |= read_mailmap_blob(map, git_mailmap_blob);
err |= read_mailmap_file(map, git_mailmap_file);
return err;
}

View File

@ -3,7 +3,7 @@
struct string_list;
int read_mailmap(struct string_list *map, char **repo_abbrev);
int read_mailmap(struct string_list *map);
void clear_mailmap(struct string_list *map);
int map_user(struct string_list *map,

View File

@ -679,7 +679,7 @@ static int mailmap_name(const char **email, size_t *email_len,
static struct string_list *mail_map;
if (!mail_map) {
mail_map = xcalloc(1, sizeof(*mail_map));
read_mailmap(mail_map, NULL);
read_mailmap(mail_map);
}
return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
}

View File

@ -23,7 +23,6 @@ struct shortlog {
} groups;
struct string_list trailers;
char *common_repo_prefix;
int email;
struct string_list mailmap;
FILE *file;

View File

@ -4,11 +4,8 @@ test_description='reflog walk shows repeated commits again'
. ./test-lib.sh
test_expect_success 'setup commits' '
test_tick &&
echo content >file && git add file && git commit -m one &&
git tag one &&
echo content >>file && git add file && git commit -m two &&
git tag two
test_commit one file content &&
test_commit --append two file content
'
test_expect_success 'setup reflog with alternating commits' '

View File

@ -8,13 +8,9 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
test_expect_success 'setup' '
echo hello >world &&
git add world &&
git commit -m initial &&
test_commit initial world hello &&
git branch other &&
echo "hello again" >>world &&
git add world &&
git commit -m second
test_commit --append second world "hello again"
'
test_expect_success '"checkout -" does not work initially' '
@ -96,9 +92,7 @@ test_expect_success 'switch to twelfth from the last' '
test_expect_success 'merge base test setup' '
git checkout -b another other &&
echo "hello again" >>world &&
git add world &&
git commit -m third
test_commit --append third world "hello again"
'
test_expect_success 'another...main' '

File diff suppressed because it is too large Load Diff

View File

@ -18,11 +18,8 @@ message_body () {
}
test_expect_success '-C option copies authorship and message' '
echo "Initial" >foo &&
git add foo &&
test_tick &&
git commit -m "Initial Commit" --author Frigate\ \<flying@over.world\> &&
git tag Initial &&
test_commit --author Frigate\ \<flying@over.world\> \
"Initial Commit" foo Initial Initial &&
echo "Test 1" >>foo &&
test_tick &&
git commit -a -C Initial &&

View File

@ -690,21 +690,9 @@ test_expect_success 'grep -C1 hunk mark between files' '
'
test_expect_success 'log grep setup' '
echo a >>file &&
test_tick &&
GIT_AUTHOR_NAME="With * Asterisk" \
GIT_AUTHOR_EMAIL="xyzzy@frotz.com" \
git commit -a -m "second" &&
echo a >>file &&
test_tick &&
git commit -a -m "third" &&
echo a >>file &&
test_tick &&
GIT_AUTHOR_NAME="Night Fall" \
GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
git commit -a -m "fourth"
test_commit --append --author "With * Asterisk <xyzzy@frotz.com>" second file a &&
test_commit --append third file a &&
test_commit --append --author "Night Fall <nitfol@frobozz.com>" fourth file a
'
test_expect_success 'log grep (1)' '

View File

@ -178,19 +178,28 @@ debug () {
GIT_DEBUGGER="${GIT_DEBUGGER}" "$@" <&6 >&5 2>&7
}
# Call test_commit with the arguments
# [-C <directory>] <message> [<file> [<contents> [<tag>]]]"
# Usage: test_commit [options] <message> [<file> [<contents> [<tag>]]]
# -C <dir>:
# Run all git commands in directory <dir>
# --notick
# Do not call test_tick before making a commit
# --append
# Use "echo >>" instead of "echo >" when writing "<contents>" to
# "<file>"
# --signoff
# Invoke "git commit" with --signoff
# --author=<author>
# Invoke "git commit" with --author=<author>
#
# This will commit a file with the given contents and the given commit
# message, and tag the resulting commit with the given tag name.
#
# <file>, <contents>, and <tag> all default to <message>.
#
# If the first argument is "-C", the second argument is used as a path for
# the git invocations.
test_commit () {
notick= &&
append= &&
author= &&
signoff= &&
indir= &&
while test $# != 0
@ -199,6 +208,13 @@ test_commit () {
--notick)
notick=yes
;;
--append)
append=yes
;;
--author)
author="$2"
shift
;;
--signoff)
signoff="$1"
;;
@ -214,13 +230,20 @@ test_commit () {
done &&
indir=${indir:+"$indir"/} &&
file=${2:-"$1.t"} &&
echo "${3-$1}" > "$indir$file" &&
if test -n "$append"
then
echo "${3-$1}" >>"$indir$file"
else
echo "${3-$1}" >"$indir$file"
fi &&
git ${indir:+ -C "$indir"} add "$file" &&
if test -z "$notick"
then
test_tick
fi &&
git ${indir:+ -C "$indir"} commit $signoff -m "$1" &&
git ${indir:+ -C "$indir"} commit \
${author:+ --author "$author"} \
$signoff -m "$1" &&
git ${indir:+ -C "$indir"} tag "${4:-$1}"
}