Merge branch 'jk/mailmap-from-blob'

Allow us to read, and default to read, mailmap files from the tip
of the history in bare repositories.  This will help running tools
like shortlog in server settings.

* jk/mailmap-from-blob:
  mailmap: default mailmap.blob in bare repositories
  mailmap: fix some documentation loose-ends for mailmap.blob
  mailmap: clean up read_mailmap error handling
  mailmap: support reading mailmap from blobs
  mailmap: refactor mailmap parsing for non-file sources
This commit is contained in:
Junio C Hamano 2013-01-05 23:41:42 -08:00
commit 3a3100a889
7 changed files with 214 additions and 38 deletions

View file

@ -1521,6 +1521,14 @@ mailmap.file::
subdirectory, or somewhere outside of the repository itself.
See linkgit:git-shortlog[1] and linkgit:git-blame[1].
mailmap.blob::
Like `mailmap.file`, but consider the value as a reference to a
blob in the repository. If both `mailmap.file` and
`mailmap.blob` are given, both are parsed, with entries from
`mailmap.file` taking precedence. In a bare repository, this
defaults to `HEAD:.mailmap`. In a non-bare repository, it
defaults to empty.
man.viewer::
Specify the programs that may be used to display help in the
'man' format. See linkgit:git-help[1].

View file

@ -167,7 +167,7 @@ log.showroot::
`git log -p` output would be shown without a diff attached.
The default is `true`.
mailmap.file::
mailmap.*::
See linkgit:git-shortlog[1].
notes.displayRef::

View file

@ -1,5 +1,6 @@
If the file `.mailmap` exists at the toplevel of the repository, or at
the location pointed to by the mailmap.file configuration option, it
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.

View file

@ -1164,6 +1164,7 @@ extern int author_ident_sufficiently_given(void);
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
extern const char *git_mailmap_file;
extern const char *git_mailmap_blob;
/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);

View file

@ -839,6 +839,8 @@ static int git_default_mailmap_config(const char *var, const char *value)
{
if (!strcmp(var, "mailmap.file"))
return git_config_string(&git_mailmap_file, var, value);
if (!strcmp(var, "mailmap.blob"))
return git_config_string(&git_mailmap_blob, var, value);
/* Add other config variables here and to Documentation/config.txt. */
return 0;

138
mailmap.c
View file

@ -10,6 +10,7 @@ static inline void debug_mm(const char *format, ...) {}
#endif
const char *git_mailmap_file;
const char *git_mailmap_blob;
struct mailmap_info {
char *name;
@ -129,54 +130,119 @@ static char *parse_name_and_email(char *buffer, char **name,
return (*right == '\0' ? NULL : right);
}
static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
static void read_mailmap_line(struct string_list *map, char *buffer,
char **repo_abbrev)
{
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;
if (repo_abbrev)
free(*repo_abbrev);
*repo_abbrev = xmalloc(len);
for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
strcpy(*repo_abbrev, cp);
}
return;
}
if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
parse_name_and_email(name2, &name2, &email2, 1);
if (email1)
add_mapping(map, name1, email1, name2, email2);
}
static int read_mailmap_file(struct string_list *map, const char *filename,
char **repo_abbrev)
{
char buffer[1024];
FILE *f = (filename == NULL ? NULL : fopen(filename, "r"));
FILE *f;
if (f == NULL)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
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 (!filename)
return 0;
if (!repo_abbrev)
continue;
if (len && buffer[len - 1] == '\n')
buffer[--len] = 0;
if (!strncmp(buffer, abbrev, abblen)) {
char *cp;
if (repo_abbrev)
free(*repo_abbrev);
*repo_abbrev = xmalloc(len);
for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
strcpy(*repo_abbrev, cp);
}
continue;
}
if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
parse_name_and_email(name2, &name2, &email2, 1);
if (email1)
add_mapping(map, name1, email1, name2, email2);
f = fopen(filename, "r");
if (!f) {
if (errno == ENOENT)
return 0;
return error("unable to open mailmap at %s: %s",
filename, strerror(errno));
}
while (fgets(buffer, sizeof(buffer), f) != NULL)
read_mailmap_line(map, buffer, repo_abbrev);
fclose(f);
return 0;
}
static void read_mailmap_buf(struct string_list *map,
const char *buf, unsigned long len,
char **repo_abbrev)
{
while (len) {
const char *end = strchrnul(buf, '\n');
unsigned long linelen = end - buf + 1;
char *line = xmemdupz(buf, linelen);
read_mailmap_line(map, line, repo_abbrev);
free(line);
buf += linelen;
len -= linelen;
}
}
static int read_mailmap_blob(struct string_list *map,
const char *name,
char **repo_abbrev)
{
unsigned char sha1[20];
char *buf;
unsigned long size;
enum object_type type;
if (!name)
return 0;
if (get_sha1(name, sha1) < 0)
return 0;
buf = read_sha1_file(sha1, &type, &size);
if (!buf)
return error("unable to read mailmap object at %s", name);
if (type != OBJ_BLOB)
return error("mailmap is not a blob: %s", name);
read_mailmap_buf(map, buf, size, repo_abbrev);
free(buf);
return 0;
}
int read_mailmap(struct string_list *map, char **repo_abbrev)
{
int err = 0;
map->strdup_strings = 1;
/* each failure returns 1, so >1 means both calls failed */
return read_single_mailmap(map, ".mailmap", repo_abbrev) +
read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1;
if (!git_mailmap_blob && is_bare_repository())
git_mailmap_blob = "HEAD:.mailmap";
err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
return err;
}
void clear_mailmap(struct string_list *map)

View file

@ -149,6 +149,104 @@ test_expect_success 'No mailmap files, but configured' '
test_cmp expect actual
'
test_expect_success 'setup mailmap blob tests' '
git checkout -b map &&
test_when_finished "git checkout master" &&
cat >just-bugs <<-\EOF &&
Blob Guy <bugs@company.xx>
EOF
cat >both <<-\EOF &&
Blob Guy <author@example.com>
Blob Guy <bugs@company.xx>
EOF
git add just-bugs both &&
git commit -m "my mailmaps" &&
echo "Repo Guy <author@example.com>" >.mailmap &&
echo "Internal Guy <author@example.com>" >internal.map
'
test_expect_success 'mailmap.blob set' '
cat >expect <<-\EOF &&
Blob Guy (1):
second
Repo Guy (1):
initial
EOF
git -c mailmap.blob=map:just-bugs shortlog HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'mailmap.blob overrides .mailmap' '
cat >expect <<-\EOF &&
Blob Guy (2):
initial
second
EOF
git -c mailmap.blob=map:both shortlog HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'mailmap.file overrides mailmap.blob' '
cat >expect <<-\EOF &&
Blob Guy (1):
second
Internal Guy (1):
initial
EOF
git \
-c mailmap.blob=map:both \
-c mailmap.file=internal.map \
shortlog HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'mailmap.blob can be missing' '
cat >expect <<-\EOF &&
Repo Guy (1):
initial
nick1 (1):
second
EOF
git -c mailmap.blob=map:nonexistent shortlog HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'mailmap.blob defaults to off in non-bare repo' '
git init non-bare &&
(
cd non-bare &&
test_commit one .mailmap "Fake Name <author@example.com>" &&
echo " 1 Fake Name" >expect &&
git shortlog -ns HEAD >actual &&
test_cmp expect actual &&
rm .mailmap &&
echo " 1 A U Thor" >expect &&
git shortlog -ns HEAD >actual &&
test_cmp expect actual
)
'
test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' '
git clone --bare non-bare bare &&
(
cd bare &&
echo " 1 Fake Name" >expect &&
git shortlog -ns HEAD >actual &&
test_cmp expect actual
)
'
test_expect_success 'cleanup after mailmap.blob tests' '
rm -f .mailmap
'
# Extended mailmap configurations should give us the following output for shortlog
cat >expect <<\EOF
A U Thor <author@example.com> (1):