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:
Junio C Hamano
2013-01-20 17:06:52 -08:00
15 changed files with 390 additions and 226 deletions

106
mailmap.c
View File

@ -235,6 +235,7 @@ int read_mailmap(struct string_list *map, char **repo_abbrev)
int err = 0;
map->strdup_strings = 1;
map->cmp = strcasecmp;
if (!git_mailmap_blob && is_bare_repository())
git_mailmap_blob = "HEAD:.mailmap";
@ -253,60 +254,95 @@ void clear_mailmap(struct string_list *map)
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 mailmap_entry *me;
char buf[1024], *mailbuf;
int i;
/* figure out space requirement for email */
end_of_email = strchr(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);
debug_mm("map_user: map '%.*s' <%.*s>\n",
*name, *namelen, *emaillen, *email);
/* downcase the email address */
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);
item = lookup_prefix(map, *email, *emaillen);
if (item != NULL) {
me = (struct mailmap_entry *)item->util;
if (me->namemap.nr) {
/* The item has multiple items, so we'll look up on name too */
/* 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)
item = subitem;
}
}
if (mailbuf != buf)
free(mailbuf);
if (item != NULL) {
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");
return 0;
}
if (maxlen_email && mi->email)
strlcpy(email, mi->email, maxlen_email);
else
*end_of_email = '\0';
if (maxlen_name && mi->name)
strlcpy(name, mi->name, maxlen_name);
debug_mm("map_user: to '%s' <%s>\n", name, mi->email ? mi->email : "");
if (mi->email) {
*email = mi->email;
*emaillen = strlen(*email);
}
if (mi->name) {
*name = mi->name;
*namelen = strlen(*name);
}
debug_mm("map_user: to '%.*s' <.*%s>\n", *namelen, *name,
*emaillen, *email);
return 1;
}
debug_mm("map_user: --\n");