{"thread":{"id":"32537","subject":"[PATCH 00/10] Log mailmap optimization","startedAt":"2013-01-05T21:26:36Z","lastAt":"2013-01-07T22:37:39Z","messageCount":14,"participants":["Antoine Pelisse","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":10},"messages":[{"id":"206050","messageId":"1357421206-5014-1-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":null,"subject":"[PATCH 00/10] Log mailmap optimization","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:36Z","receivedAt":"2013-01-05T21:26:36Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"Hi,\n\nHere is a reroll of ap/log-mailmap.\nThe idea is to use another preparatory step:\nAllow case-insensitive and length search in list_lookup\n\nWe can now search for mapping name and email without any copy.\nOf course a copy is then necessary to store the info, but we no\nlonger need any copy to look-up the mapping (useful for replacing or not\nbefore grep).\n\nThanks,\n\nAntoine Pelisse (10):\n  list_lookup: create case and length search\n  Use split_ident_line to parse author and committer\n  mailmap: remove email copy and length limitation\n  mailmap: simplify map_user() interface\n  mailmap: add mailmap structure to rev_info and pp\n  pretty: use mailmap to display username and email\n  log: add --use-mailmap option\n  test: add test for --use-mailmap option\n  log: grep author/committer using mailmap\n  log: add log.mailmap configuration option\n\n Documentation/config.txt  |   4 +\n Documentation/git-log.txt |   5 ++\n builtin/blame.c           | 183 ++++++++++++++++++++++------------------------\n builtin/log.c             |  16 +++-\n builtin/shortlog.c        |  54 ++++----------\n commit.h                  |   1 +\n log-tree.c                |   1 +\n mailmap.c                 |  55 +++++---------\n mailmap.h                 |   4 +-\n pretty.c                  | 114 ++++++++++++++++-------------\n revision.c                |  54 ++++++++++++++\n revision.h                |   1 +\n string-list.c             |  30 ++++++--\n string-list.h             |   2 +\n t/t4203-mailmap.sh        |  56 ++++++++++++++\n 15 files changed, 349 insertions(+), 231 deletions(-)\n\n--\n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206051","messageId":"1357421206-5014-2-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 01/10] list_lookup: create case and length search","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:37Z","receivedAt":"2013-01-05T21:26:37Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"Create a new function to look-up a string in a string_list, but:\n - add a new parameter to ignore case differences\n - add a length parameter to search for a substring\n\nThe idea is to avoid several copies (lowering a string before searching\nit when we just want to ignore case), or copying a substring of a bigger\nstring to search it in the string_list\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n string-list.c | 30 ++++++++++++++++++++++++------\n string-list.h |  2 ++\n 2 files changed, 26 insertions(+), 6 deletions(-)\n\ndiff --git a/string-list.c b/string-list.c\nindex 397e6cf..f06e110 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -4,13 +4,21 @@\n /* if there is no exact match, point to the index where the entry could be\n  * inserted */\n static int get_entry_index(const struct string_list *list, const char *string,\n-\t\tint *exact_match)\n+\t\tint *exact_match, int case_sensitive, size_t n)\n {\n \tint left = -1, right = list->nr;\n \n \twhile (left + 1 < right) {\n+\t\tint compare;\n \t\tint middle = (left + right) / 2;\n-\t\tint compare = strcmp(string, list->items[middle].string);\n+\t\tif (case_sensitive)\n+\t\t\tcompare = strncmp(string, list->items[middle].string, n);\n+\t\telse\n+\t\t\tcompare = strncasecmp(string, list->items[middle].string, n);\n+\t\t/* Make sure our string is not a substring of item string */\n+\t\tif (!compare && n != -1)\n+\t\t\tif (list->items[middle].string[n] != '\\0')\n+\t\t\t\tcompare = -1;\n \t\tif (compare < 0)\n \t\t\tright = middle;\n \t\telse if (compare > 0)\n@@ -29,7 +37,7 @@ static int get_entry_index(const struct string_list *list, const char *string,\n static int add_entry(int insert_at, struct string_list *list, const char *string)\n {\n \tint exact_match = 0;\n-\tint index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match);\n+\tint index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match, 1, -1);\n \n \tif (exact_match)\n \t\treturn -1 - index;\n@@ -70,7 +78,7 @@ struct string_list_item *string_list_insert_at_index(struct string_list *list,\n int string_list_has_string(const struct string_list *list, const char *string)\n {\n \tint exact_match;\n-\tget_entry_index(list, string, &exact_match);\n+\tget_entry_index(list, string, &exact_match, 1, -1);\n \treturn exact_match;\n }\n \n@@ -78,7 +86,7 @@ int string_list_find_insert_index(const struct string_list *list, const char *st\n \t\t\t\t  int negative_existing_index)\n {\n \tint exact_match;\n-\tint index = get_entry_index(list, string, &exact_match);\n+\tint index = get_entry_index(list, string, &exact_match, 1, -1);\n \tif (exact_match)\n \t\tindex = -1 - (negative_existing_index ? index : 0);\n \treturn index;\n@@ -86,7 +94,17 @@ int string_list_find_insert_index(const struct string_list *list, const char *st\n \n struct string_list_item *string_list_lookup(struct string_list *list, const char *string)\n {\n-\tint exact_match, i = get_entry_index(list, string, &exact_match);\n+\tint exact_match, i = get_entry_index(list, string, &exact_match, 1, -1);\n+\tif (!exact_match)\n+\t\treturn NULL;\n+\treturn list->items + i;\n+}\n+\n+struct string_list_item *string_list_lookup_extended(struct string_list *list,\n+    const char *string, int case_sensitive, size_t n)\n+{\n+\tint exact_match, i = get_entry_index(list, string, &exact_match,\n+\t\t\t\t\t     case_sensitive, n);\n \tif (!exact_match)\n \t\treturn NULL;\n \treturn list->items + i;\ndiff --git a/string-list.h b/string-list.h\nindex c50b0d0..4f5ae19 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -62,6 +62,8 @@ struct string_list_item *string_list_insert(struct string_list *list, const char\n struct string_list_item *string_list_insert_at_index(struct string_list *list,\n \t\t\t\t\t\t     int insert_at, const char *string);\n struct string_list_item *string_list_lookup(struct string_list *list, const char *string);\n+struct string_list_item *string_list_lookup_extended(struct string_list *list,\n+    const char *string, int case_sensitive, size_t n);\n \n /*\n  * Remove all but the first of consecutive entries with the same\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206052","messageId":"1357421206-5014-3-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 02/10] Use split_ident_line to parse author and committer","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:38Z","receivedAt":"2013-01-05T21:26:38Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"Currently blame.c::get_acline, pretty.c::pp_user_info() and\nshortlog.c::insert_one_record are parsing author name, email, time and\ntz themselves.\n\nUse ident.c::split_ident_line for better code reuse.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n builtin/blame.c    | 59 +++++++++++++++++++-----------------------------------\n builtin/shortlog.c | 36 +++++++++------------------------\n pretty.c           | 35 +++++++++++++++++++-------------\n 3 files changed, 52 insertions(+), 78 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex cfae569..dd4aff9 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1343,8 +1343,9 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t\t\tint mail_len, char *mail,\n \t\t\tunsigned long *time, const char **tz)\n {\n-\tint len, tzlen, maillen;\n-\tchar *tmp, *endp, *timepos, *mailpos;\n+\tstruct ident_split ident;\n+\tint len, tzlen, maillen, namelen;\n+\tchar *tmp, *endp, *mailpos;\n \n \ttmp = strstr(inbuf, what);\n \tif (!tmp)\n@@ -1355,7 +1356,10 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t\tlen = strlen(tmp);\n \telse\n \t\tlen = endp - tmp;\n-\tif (person_len <= len) {\n+\tif (person_len <= len)\n+\t\tgoto error_out;\n+\n+\tif (split_ident_line(&ident, tmp, len)) {\n \terror_out:\n \t\t/* Ugh */\n \t\t*tz = \"(unknown)\";\n@@ -1364,47 +1368,26 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t\t*time = 0;\n \t\treturn;\n \t}\n-\tmemcpy(person, tmp, len);\n \n-\ttmp = person;\n-\ttmp += len;\n-\t*tmp = 0;\n-\twhile (person < tmp && *tmp != ' ')\n-\t\ttmp--;\n-\tif (tmp <= person)\n-\t\tgoto error_out;\n-\t*tz = tmp+1;\n-\ttzlen = (person+len)-(tmp+1);\n+\tnamelen = ident.name_end - ident.name_begin;\n+\tmemcpy(person, ident.name_begin, namelen);\n+\tperson[namelen] = 0;\n \n-\t*tmp = 0;\n-\twhile (person < tmp && *tmp != ' ')\n-\t\ttmp--;\n-\tif (tmp <= person)\n-\t\tgoto error_out;\n-\t*time = strtoul(tmp, NULL, 10);\n-\ttimepos = tmp;\n+\tmaillen = ident.mail_end - ident.mail_begin + 2;\n+\tmemcpy(mail, ident.mail_begin - 1, maillen);\n+\tmail[maillen] = 0;\n \n-\t*tmp = 0;\n-\twhile (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))\n-\t\ttmp--;\n-\tif (tmp <= person)\n-\t\treturn;\n-\tmailpos = tmp + 1;\n-\t*tmp = 0;\n-\tmaillen = timepos - tmp;\n-\tmemcpy(mail, mailpos, maillen);\n+\t*time = strtoul(ident.date_begin, NULL, 10);\n \n-\tif (!mailmap.nr)\n-\t\treturn;\n+\ttzlen = ident.tz_end - ident.tz_begin;\n \n-\t/*\n-\t * mailmap expansion may make the name longer.\n-\t * make room by pushing stuff down.\n-\t */\n-\ttmp = person + person_len - (tzlen + 1);\n-\tmemmove(tmp, *tz, tzlen);\n+\t/* Place tz at the end of person */\n+\t*tz = tmp = person + person_len - (tzlen + 1);\n+\tmemcpy(tmp, ident.tz_begin, tzlen);\n \ttmp[tzlen] = 0;\n-\t*tz = tmp;\n+\n+\tif (!mailmap.nr)\n+\t\treturn;\n \n \t/*\n \t * Now, convert both name and e-mail using mailmap\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex b316cf3..03c6cd7 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -40,40 +40,24 @@ static void insert_one_record(struct shortlog *log,\n \tchar emailbuf[1024];\n \tsize_t len;\n \tconst char *eol;\n-\tconst char *boemail, *eoemail;\n \tstruct strbuf subject = STRBUF_INIT;\n+\tstruct ident_split ident;\n \n-\tboemail = strchr(author, '<');\n-\tif (!boemail)\n-\t\treturn;\n-\teoemail = strchr(boemail, '>');\n-\tif (!eoemail)\n+\tif (split_ident_line(&ident, author, strlen(author)))\n \t\treturn;\n \n \t/* copy author name to namebuf, to support matching on both name and email */\n-\tmemcpy(namebuf, author, boemail - author);\n-\tlen = boemail - author;\n-\twhile (len > 0 && isspace(namebuf[len-1]))\n-\t\tlen--;\n+\tlen = ident.name_end - ident.name_begin;\n+\tmemcpy(namebuf, ident.name_begin, len);\n \tnamebuf[len] = 0;\n \n \t/* copy email name to emailbuf, to allow email replacement as well */\n-\tmemcpy(emailbuf, boemail+1, eoemail - boemail);\n-\temailbuf[eoemail - boemail - 1] = 0;\n-\n-\tif (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {\n-\t\twhile (author < boemail && isspace(*author))\n-\t\t\tauthor++;\n-\t\tfor (len = 0;\n-\t\t     len < sizeof(namebuf) - 1 && author + len < boemail;\n-\t\t     len++)\n-\t\t\tnamebuf[len] = author[len];\n-\t\twhile (0 < len && isspace(namebuf[len-1]))\n-\t\t\tlen--;\n-\t\tnamebuf[len] = '\\0';\n-\t}\n-\telse\n-\t\tlen = strlen(namebuf);\n+\tlen = ident.mail_end - ident.mail_begin;\n+\tmemcpy(emailbuf, ident.mail_begin, len);\n+\temailbuf[len] = 0;\n+\n+\tmap_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf));\n+\tlen = strlen(namebuf);\n \n \tif (log->email) {\n \t\tsize_t room = sizeof(namebuf) - len - 1;\ndiff --git a/pretty.c b/pretty.c\nindex 5bdc2e7..350d1df 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -387,29 +387,36 @@ void pp_user_info(const struct pretty_print_context *pp,\n \t\t  const char *what, struct strbuf *sb,\n \t\t  const char *line, const char *encoding)\n {\n+\tstruct ident_split ident;\n+\tint linelen, namelen;\n+\tchar *line_end, *date;\n \tint max_length = 78; /* per rfc2822 */\n-\tchar *date;\n-\tint namelen;\n \tunsigned long time;\n \tint tz;\n \n \tif (pp->fmt == CMIT_FMT_ONELINE)\n \t\treturn;\n-\tdate = strchr(line, '>');\n-\tif (!date)\n+\n+\tline_end = strchr(line, '\\n');\n+\tif (!line_end) {\n+\t\tline_end = strchr(line, '\\0');\n+\t\tif (!line_end)\n+\t\t\treturn;\n+\t}\n+\n+\tlinelen = ++line_end - line;\n+\tif (split_ident_line(&ident, line, linelen))\n \t\treturn;\n-\tnamelen = ++date - line;\n-\ttime = strtoul(date, &date, 10);\n+\n+\tnamelen = ident.mail_end - ident.name_begin + 1;\n+\ttime = strtoul(ident.date_begin, &date, 10);\n \ttz = strtol(date, NULL, 10);\n \n \tif (pp->fmt == CMIT_FMT_EMAIL) {\n-\t\tchar *name_tail = strchr(line, '<');\n \t\tint display_name_length;\n-\t\tif (!name_tail)\n-\t\t\treturn;\n-\t\twhile (line < name_tail && isspace(name_tail[-1]))\n-\t\t\tname_tail--;\n-\t\tdisplay_name_length = name_tail - line;\n+\n+\t\tdisplay_name_length = ident.name_end - ident.name_begin;\n+\n \t\tstrbuf_addstr(sb, \"From: \");\n \t\tif (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {\n \t\t\tadd_rfc2047(sb, line, display_name_length,\n@@ -427,10 +434,10 @@ void pp_user_info(const struct pretty_print_context *pp,\n \t\t}\n \t\tif (namelen - display_name_length + last_line_length(sb) > max_length) {\n \t\t\tstrbuf_addch(sb, '\\n');\n-\t\t\tif (!isspace(name_tail[0]))\n+\t\t\tif (!isspace(ident.name_end[0]))\n \t\t\t\tstrbuf_addch(sb, ' ');\n \t\t}\n-\t\tstrbuf_add(sb, name_tail, namelen - display_name_length);\n+\t\tstrbuf_add(sb, ident.name_end, namelen - display_name_length);\n \t\tstrbuf_addch(sb, '\\n');\n \t} else {\n \t\tstrbuf_addf(sb, \"%s: %.*s%.*s\\n\", what,\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206056","messageId":"1357421206-5014-4-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 03/10] mailmap: remove email copy and length limitation","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:39Z","receivedAt":"2013-01-05T21:26:39Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"In map_user(), the new string_list_lookup_extended() allows us to remove\nthe copy of email buffer to lower it.\nThis also removes the limitation on the length of the copy buffer and\nsimplifies the function.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n mailmap.c | 22 ++++++----------------\n 1 file changed, 6 insertions(+), 16 deletions(-)\n\ndiff --git a/mailmap.c b/mailmap.c\nindex ea4b471..9db8a1b 100644\n--- a/mailmap.c\n+++ b/mailmap.c\n@@ -193,8 +193,7 @@ int map_user(struct string_list *map,\n \tchar *end_of_email;\n \tstruct string_list_item *item;\n \tstruct mailmap_entry *me;\n-\tchar buf[1024], *mailbuf;\n-\tint i;\n+\tsize_t maillen;\n \n \t/* figure out space requirement for email */\n \tend_of_email = strchr(email, '>');\n@@ -204,18 +203,11 @@ int map_user(struct string_list *map,\n \t\tif (!end_of_email)\n \t\t\treturn 0;\n \t}\n-\tif (end_of_email - email + 1 < sizeof(buf))\n-\t\tmailbuf = buf;\n-\telse\n-\t\tmailbuf = xmalloc(end_of_email - email + 1);\n-\n-\t/* downcase the email address */\n-\tfor (i = 0; i < end_of_email - email; i++)\n-\t\tmailbuf[i] = tolower(email[i]);\n-\tmailbuf[i] = 0;\n-\n-\tdebug_mm(\"map_user: map '%s' <%s>\\n\", name, mailbuf);\n-\titem = string_list_lookup(map, mailbuf);\n+\n+\tmaillen = end_of_email - email;\n+\n+\tdebug_mm(\"map_user: map '%s' <%.*s>\\n\", name, maillen, email);\n+\titem = string_list_lookup_extended(map, email, 0, maillen);\n \tif (item != NULL) {\n \t\tme = (struct mailmap_entry *)item->util;\n \t\tif (me->namemap.nr) {\n@@ -226,8 +218,6 @@ int map_user(struct string_list *map,\n \t\t\t\titem = subitem;\n \t\t}\n \t}\n-\tif (mailbuf != buf)\n-\t\tfree(mailbuf);\n \tif (item != NULL) {\n \t\tstruct mailmap_info *mi = (struct mailmap_info *)item->util;\n \t\tif (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206058","messageId":"1357421206-5014-5-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 04/10] mailmap: simplify map_user() interface","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:40Z","receivedAt":"2013-01-05T21:26:40Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"The new string_list_lookup_extended() allows us to simplify map_user(),\nmostly to avoid copies of string buffers. It also simplifies caller\nfunctions.\n\nmap_user() directly receive pointers and length from the commit buffer\nas mail and name. If mapping of the user and mail can be done, the\npointer is updated to a new location. Lengths are also updated if\nnecessary.\n\nThe caller of map_user() can then copy the new email and name if\nnecessary.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n builtin/blame.c    | 156 +++++++++++++++++++++++++++--------------------------\n builtin/shortlog.c |  32 +++++------\n mailmap.c          |  43 +++++++--------\n mailmap.h          |   4 +-\n pretty.c           |  35 +++++-------\n 5 files changed, 126 insertions(+), 144 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex dd4aff9..86450e3 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1321,31 +1321,31 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n  * Information on commits, used for output.\n  */\n struct commit_info {\n-\tconst char *author;\n-\tconst char *author_mail;\n+\tstruct strbuf author;\n+\tstruct strbuf author_mail;\n \tunsigned long author_time;\n-\tconst char *author_tz;\n+\tstruct strbuf author_tz;\n \n \t/* filled only when asked for details */\n-\tconst char *committer;\n-\tconst char *committer_mail;\n+\tstruct strbuf committer;\n+\tstruct strbuf committer_mail;\n \tunsigned long committer_time;\n-\tconst char *committer_tz;\n+\tstruct strbuf committer_tz;\n \n-\tconst char *summary;\n+\tstruct strbuf summary;\n };\n \n /*\n  * Parse author/committer line in the commit object buffer\n  */\n static void get_ac_line(const char *inbuf, const char *what,\n-\t\t\tint person_len, char *person,\n-\t\t\tint mail_len, char *mail,\n-\t\t\tunsigned long *time, const char **tz)\n+\tstruct strbuf *name, struct strbuf *mail,\n+\tunsigned long *time, struct strbuf *tz)\n {\n \tstruct ident_split ident;\n-\tint len, tzlen, maillen, namelen;\n-\tchar *tmp, *endp, *mailpos;\n+\tsize_t len, maillen, namelen;\n+\tchar *tmp, *endp;\n+\tconst char *tmpname, *tmpmail;\n \n \ttmp = strstr(inbuf, what);\n \tif (!tmp)\n@@ -1356,51 +1356,61 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t\tlen = strlen(tmp);\n \telse\n \t\tlen = endp - tmp;\n-\tif (person_len <= len)\n-\t\tgoto error_out;\n \n \tif (split_ident_line(&ident, tmp, len)) {\n \terror_out:\n \t\t/* Ugh */\n-\t\t*tz = \"(unknown)\";\n-\t\tstrcpy(person, *tz);\n-\t\tstrcpy(mail, *tz);\n+\t\ttmp = \"(unknown)\";\n+\t\tstrbuf_addstr(name, tmp);\n+\t\tstrbuf_addstr(mail, tmp);\n+\t\tstrbuf_addstr(tz, tmp);\n \t\t*time = 0;\n \t\treturn;\n \t}\n \n \tnamelen = ident.name_end - ident.name_begin;\n-\tmemcpy(person, ident.name_begin, namelen);\n-\tperson[namelen] = 0;\n+\ttmpname = ident.name_begin;\n \n-\tmaillen = ident.mail_end - ident.mail_begin + 2;\n-\tmemcpy(mail, ident.mail_begin - 1, maillen);\n-\tmail[maillen] = 0;\n+\tmaillen = ident.mail_end - ident.mail_begin;\n+\ttmpmail = ident.mail_begin;\n \n \t*time = strtoul(ident.date_begin, NULL, 10);\n \n-\ttzlen = ident.tz_end - ident.tz_begin;\n-\n-\t/* Place tz at the end of person */\n-\t*tz = tmp = person + person_len - (tzlen + 1);\n-\tmemcpy(tmp, ident.tz_begin, tzlen);\n-\ttmp[tzlen] = 0;\n-\n-\tif (!mailmap.nr)\n-\t\treturn;\n+\tlen = ident.tz_end - ident.tz_begin;\n+\tstrbuf_add(tz, ident.tz_begin, len);\n \n \t/*\n \t * Now, convert both name and e-mail using mailmap\n \t */\n-\tif (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {\n-\t\t/* Add a trailing '>' to email, since map_user returns plain emails\n-\t\t   Note: It already has '<', since we replace from mail+1 */\n-\t\tmailpos = memchr(mail, '\\0', mail_len);\n-\t\tif (mailpos && mailpos-mail < mail_len - 1) {\n-\t\t\t*mailpos = '>';\n-\t\t\t*(mailpos+1) = '\\0';\n-\t\t}\n-\t}\n+\tmap_user(&mailmap, &tmpmail, &maillen,\n+\t\t &tmpname, &namelen);\n+\n+\tstrbuf_addf(mail, \"<%.*s>\", (int)maillen, tmpmail);\n+\tstrbuf_add(name, tmpname, namelen);\n+}\n+\n+static void commit_info_init(struct commit_info *ci)\n+{\n+\n+\tstrbuf_init(&ci->author, 0);\n+\tstrbuf_init(&ci->author_mail, 0);\n+\tstrbuf_init(&ci->author_tz, 0);\n+\tstrbuf_init(&ci->committer, 0);\n+\tstrbuf_init(&ci->committer_mail, 0);\n+\tstrbuf_init(&ci->committer_tz, 0);\n+\tstrbuf_init(&ci->summary, 0);\n+}\n+\n+static void commit_info_destroy(struct commit_info *ci)\n+{\n+\n+\tstrbuf_release(&ci->author);\n+\tstrbuf_release(&ci->author_mail);\n+\tstrbuf_release(&ci->author_tz);\n+\tstrbuf_release(&ci->committer);\n+\tstrbuf_release(&ci->committer_mail);\n+\tstrbuf_release(&ci->committer_tz);\n+\tstrbuf_release(&ci->summary);\n }\n \n static void get_commit_info(struct commit *commit,\n@@ -1410,11 +1420,8 @@ static void get_commit_info(struct commit *commit,\n \tint len;\n \tconst char *subject, *encoding;\n \tchar *reencoded, *message;\n-\tstatic char author_name[1024];\n-\tstatic char author_mail[1024];\n-\tstatic char committer_name[1024];\n-\tstatic char committer_mail[1024];\n-\tstatic char summary_buf[1024];\n+\n+\tcommit_info_init(ret);\n \n \t/*\n \t * We've operated without save_commit_buffer, so\n@@ -1432,11 +1439,8 @@ static void get_commit_info(struct commit *commit,\n \tencoding = get_log_output_encoding();\n \treencoded = logmsg_reencode(commit, encoding);\n \tmessage   = reencoded ? reencoded : commit->buffer;\n-\tret->author = author_name;\n-\tret->author_mail = author_mail;\n \tget_ac_line(message, \"\\nauthor \",\n-\t\t    sizeof(author_name), author_name,\n-\t\t    sizeof(author_mail), author_mail,\n+\t\t    &ret->author, &ret->author_mail,\n \t\t    &ret->author_time, &ret->author_tz);\n \n \tif (!detailed) {\n@@ -1444,21 +1448,16 @@ static void get_commit_info(struct commit *commit,\n \t\treturn;\n \t}\n \n-\tret->committer = committer_name;\n-\tret->committer_mail = committer_mail;\n \tget_ac_line(message, \"\\ncommitter \",\n-\t\t    sizeof(committer_name), committer_name,\n-\t\t    sizeof(committer_mail), committer_mail,\n+\t\t    &ret->committer, &ret->committer_mail,\n \t\t    &ret->committer_time, &ret->committer_tz);\n \n-\tret->summary = summary_buf;\n \tlen = find_commit_subject(message, &subject);\n-\tif (len && len < sizeof(summary_buf)) {\n-\t\tmemcpy(summary_buf, subject, len);\n-\t\tsummary_buf[len] = 0;\n-\t} else {\n-\t\tsprintf(summary_buf, \"(%s)\", sha1_to_hex(commit->object.sha1));\n-\t}\n+\tif (len)\n+\t\tstrbuf_add(&ret->summary, subject, len);\n+\telse\n+\t\tstrbuf_addf(&ret->summary, \"(%s)\", sha1_to_hex(commit->object.sha1));\n+\n \tfree(reencoded);\n }\n \n@@ -1487,15 +1486,15 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)\n \n \tsuspect->commit->object.flags |= METAINFO_SHOWN;\n \tget_commit_info(suspect->commit, &ci, 1);\n-\tprintf(\"author %s\\n\", ci.author);\n-\tprintf(\"author-mail %s\\n\", ci.author_mail);\n+\tprintf(\"author %s\\n\", ci.author.buf);\n+\tprintf(\"author-mail %s\\n\", ci.author_mail.buf);\n \tprintf(\"author-time %lu\\n\", ci.author_time);\n-\tprintf(\"author-tz %s\\n\", ci.author_tz);\n-\tprintf(\"committer %s\\n\", ci.committer);\n-\tprintf(\"committer-mail %s\\n\", ci.committer_mail);\n+\tprintf(\"author-tz %s\\n\", ci.author_tz.buf);\n+\tprintf(\"committer %s\\n\", ci.committer.buf);\n+\tprintf(\"committer-mail %s\\n\", ci.committer_mail.buf);\n \tprintf(\"committer-time %lu\\n\", ci.committer_time);\n-\tprintf(\"committer-tz %s\\n\", ci.committer_tz);\n-\tprintf(\"summary %s\\n\", ci.summary);\n+\tprintf(\"committer-tz %s\\n\", ci.committer_tz.buf);\n+\tprintf(\"summary %s\\n\", ci.summary.buf);\n \tif (suspect->commit->object.flags & UNINTERESTING)\n \t\tprintf(\"boundary\\n\");\n \tif (suspect->previous) {\n@@ -1503,6 +1502,9 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)\n \t\tprintf(\"previous %s \", sha1_to_hex(prev->commit->object.sha1));\n \t\twrite_name_quoted(prev->path, stdout, '\\n');\n \t}\n+\n+\tcommit_info_destroy(&ci);\n+\n \treturn 1;\n }\n \n@@ -1689,11 +1691,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\tif (opt & OUTPUT_ANNOTATE_COMPAT) {\n \t\t\tconst char *name;\n \t\t\tif (opt & OUTPUT_SHOW_EMAIL)\n-\t\t\t\tname = ci.author_mail;\n+\t\t\t\tname = ci.author_mail.buf;\n \t\t\telse\n-\t\t\t\tname = ci.author;\n+\t\t\t\tname = ci.author.buf;\n \t\t\tprintf(\"\\t(%10s\\t%10s\\t%d)\", name,\n-\t\t\t       format_time(ci.author_time, ci.author_tz,\n+\t\t\t       format_time(ci.author_time, ci.author_tz.buf,\n \t\t\t\t\t   show_raw_time),\n \t\t\t       ent->lno + 1 + cnt);\n \t\t} else {\n@@ -1712,14 +1714,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\t\t\tconst char *name;\n \t\t\t\tint pad;\n \t\t\t\tif (opt & OUTPUT_SHOW_EMAIL)\n-\t\t\t\t\tname = ci.author_mail;\n+\t\t\t\t\tname = ci.author_mail.buf;\n \t\t\t\telse\n-\t\t\t\t\tname = ci.author;\n+\t\t\t\t\tname = ci.author.buf;\n \t\t\t\tpad = longest_author - utf8_strwidth(name);\n \t\t\t\tprintf(\" (%s%*s %10s\",\n \t\t\t\t       name, pad, \"\",\n \t\t\t\t       format_time(ci.author_time,\n-\t\t\t\t\t\t   ci.author_tz,\n+\t\t\t\t\t\t   ci.author_tz.buf,\n \t\t\t\t\t\t   show_raw_time));\n \t\t\t}\n \t\t\tprintf(\" %*d) \",\n@@ -1734,6 +1736,8 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \n \tif (sb->final_buf_size && cp[-1] != '\\n')\n \t\tputchar('\\n');\n+\n+\tcommit_info_destroy(&ci);\n }\n \n static void output(struct scoreboard *sb, int option)\n@@ -1858,9 +1862,9 @@ static void find_alignment(struct scoreboard *sb, int *option)\n \t\t\tsuspect->commit->object.flags |= METAINFO_SHOWN;\n \t\t\tget_commit_info(suspect->commit, &ci, 1);\n \t\t\tif (*option & OUTPUT_SHOW_EMAIL)\n-\t\t\t\tnum = utf8_strwidth(ci.author_mail);\n+\t\t\t\tnum = utf8_strwidth(ci.author_mail.buf);\n \t\t\telse\n-\t\t\t\tnum = utf8_strwidth(ci.author);\n+\t\t\t\tnum = utf8_strwidth(ci.author.buf);\n \t\t\tif (longest_author < num)\n \t\t\t\tlongest_author = num;\n \t\t}\n@@ -1872,6 +1876,8 @@ static void find_alignment(struct scoreboard *sb, int *option)\n \t\t\tlongest_dst_lines = num;\n \t\tif (largest_score < ent_score(sb, e))\n \t\t\tlargest_score = ent_score(sb, e);\n+\n+\t\tcommit_info_destroy(&ci);\n \t}\n \tmax_orig_digits = decimal_width(longest_src_lines);\n \tmax_digits = decimal_width(longest_dst_lines);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 03c6cd7..1eeed0f 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -36,36 +36,28 @@ static void insert_one_record(struct shortlog *log,\n \tconst char *dot3 = log->common_repo_prefix;\n \tchar *buffer, *p;\n \tstruct string_list_item *item;\n-\tchar namebuf[1024];\n-\tchar emailbuf[1024];\n-\tsize_t len;\n+\tconst char *mailbuf, *namebuf;\n+\tsize_t namelen, maillen;\n \tconst char *eol;\n \tstruct strbuf subject = STRBUF_INIT;\n+\tstruct strbuf namemailbuf = STRBUF_INIT;\n \tstruct ident_split ident;\n \n \tif (split_ident_line(&ident, author, strlen(author)))\n \t\treturn;\n \n-\t/* copy author name to namebuf, to support matching on both name and email */\n-\tlen = ident.name_end - ident.name_begin;\n-\tmemcpy(namebuf, ident.name_begin, len);\n-\tnamebuf[len] = 0;\n+\tnamebuf = ident.name_begin;\n+\tmailbuf = ident.mail_begin;\n+\tnamelen = ident.name_end - ident.name_begin;\n+\tmaillen = ident.mail_end - ident.mail_begin;\n \n-\t/* copy email name to emailbuf, to allow email replacement as well */\n-\tlen = ident.mail_end - ident.mail_begin;\n-\tmemcpy(emailbuf, ident.mail_begin, len);\n-\temailbuf[len] = 0;\n+\tmap_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);\n+\tstrbuf_add(&namemailbuf, namebuf, namelen);\n \n-\tmap_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf));\n-\tlen = strlen(namebuf);\n+\tif (log->email)\n+\t\tstrbuf_addf(&namemailbuf, \" <%.*s>\", (int)maillen, mailbuf);\n \n-\tif (log->email) {\n-\t\tsize_t room = sizeof(namebuf) - len - 1;\n-\t\tint maillen = strlen(emailbuf);\n-\t\tsnprintf(namebuf + len, room, \" <%.*s>\", maillen, emailbuf);\n-\t}\n-\n-\titem = string_list_insert(&log->list, namebuf);\n+\titem = string_list_insert(&log->list, namemailbuf.buf);\n \tif (item->util == NULL)\n \t\titem->util = xcalloc(1, sizeof(struct string_list));\n \ndiff --git a/mailmap.c b/mailmap.c\nindex 9db8a1b..6c5e204 100644\n--- a/mailmap.c\n+++ b/mailmap.c\n@@ -188,49 +188,42 @@ void clear_mailmap(struct string_list *map)\n }\n \n int map_user(struct string_list *map,\n-\t     char *email, int maxlen_email, char *name, int maxlen_name)\n+\t\t\t const char **email, size_t *emaillen,\n+\t\t\t const char **name, size_t *namelen)\n {\n-\tchar *end_of_email;\n \tstruct string_list_item *item;\n \tstruct mailmap_entry *me;\n-\tsize_t maillen;\n-\n-\t/* figure out space requirement for email */\n-\tend_of_email = strchr(email, '>');\n-\tif (!end_of_email) {\n-\t\t/* email passed in might not be wrapped in <>, but end with a \\0 */\n-\t\tend_of_email = memchr(email, '\\0', maxlen_email);\n-\t\tif (!end_of_email)\n-\t\t\treturn 0;\n-\t}\n-\n-\tmaillen = end_of_email - email;\n \n-\tdebug_mm(\"map_user: map '%s' <%.*s>\\n\", name, maillen, email);\n-\titem = string_list_lookup_extended(map, email, 0, maillen);\n+\tdebug_mm(\"map_user: map '%.*s' <%.*s>\\n\",\n+\t\t *name, *namelen, *emaillen, *email);\n+\titem = string_list_lookup_extended(map, *email, 0, *emaillen);\n \tif (item != NULL) {\n \t\tme = (struct mailmap_entry *)item->util;\n \t\tif (me->namemap.nr) {\n \t\t\t/* The item has multiple items, so we'll look up on name too */\n \t\t\t/* If the name is not found, we choose the simple entry      */\n-\t\t\tstruct string_list_item *subitem = string_list_lookup(&me->namemap, name);\n+\t\t\tstruct string_list_item *subitem = string_list_lookup_extended(\n+\t\t\t\t\t&me->namemap, *name, 1, *namelen);\n \t\t\tif (subitem)\n \t\t\t\titem = subitem;\n \t\t}\n \t}\n \tif (item != NULL) {\n \t\tstruct mailmap_info *mi = (struct mailmap_info *)item->util;\n-\t\tif (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {\n+\t\tif (mi->name == NULL && mi->email == NULL) {\n \t\t\tdebug_mm(\"map_user:  -- (no simple mapping)\\n\");\n \t\t\treturn 0;\n \t\t}\n-\t\tif (maxlen_email && mi->email)\n-\t\t\tstrlcpy(email, mi->email, maxlen_email);\n-\t\telse\n-\t\t\t*end_of_email = '\\0';\n-\t\tif (maxlen_name && mi->name)\n-\t\t\tstrlcpy(name, mi->name, maxlen_name);\n-\t\tdebug_mm(\"map_user:  to '%s' <%s>\\n\", name, mi->email ? mi->email : \"\");\n+\t\tif (mi->email) {\n+\t\t\t\t*email = mi->email;\n+\t\t\t\t*emaillen = strlen(*email);\n+\t\t}\n+\t\tif (mi->name) {\n+\t\t\t\t*name = mi->name;\n+\t\t\t\t*namelen = strlen(*name);\n+\t\t}\n+\t\tdebug_mm(\"map_user:  to '%.*s' <.*%s>\\n\", *namelen, *name,\n+\t\t\t\t *emaillen, *email);\n \t\treturn 1;\n \t}\n \tdebug_mm(\"map_user:  --\\n\");\ndiff --git a/mailmap.h b/mailmap.h\nindex d5c3664..ed7c93b 100644\n--- a/mailmap.h\n+++ b/mailmap.h\n@@ -4,7 +4,7 @@\n int read_mailmap(struct string_list *map, char **repo_abbrev);\n void clear_mailmap(struct string_list *map);\n \n-int map_user(struct string_list *mailmap,\n-\t     char *email, int maxlen_email, char *name, int maxlen_name);\n+int map_user(struct string_list *map,\n+\t\t\t const char **email, size_t *emaillen, const char **name, size_t *namelen);\n \n #endif\ndiff --git a/pretty.c b/pretty.c\nindex 350d1df..dffcade 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -593,7 +593,8 @@ char *logmsg_reencode(const struct commit *commit,\n \treturn out;\n }\n \n-static int mailmap_name(char *email, int email_len, char *name, int name_len)\n+static int mailmap_name(const char **email, size_t *email_len,\n+\t\t\tconst char **name, size_t *name_len)\n {\n \tstatic struct string_list *mail_map;\n \tif (!mail_map) {\n@@ -610,36 +611,26 @@ static size_t format_person_part(struct strbuf *sb, char part,\n \tconst int placeholder_len = 2;\n \tint tz;\n \tunsigned long date = 0;\n-\tchar person_name[1024];\n-\tchar person_mail[1024];\n \tstruct ident_split s;\n-\tconst char *name_start, *name_end, *mail_start, *mail_end;\n+\tconst char *name, *mail;\n+\tsize_t maillen, namelen;\n \n \tif (split_ident_line(&s, msg, len) < 0)\n \t\tgoto skip;\n \n-\tname_start = s.name_begin;\n-\tname_end = s.name_end;\n-\tmail_start = s.mail_begin;\n-\tmail_end = s.mail_end;\n-\n-\tif (part == 'N' || part == 'E') { /* mailmap lookup */\n-\t\tsnprintf(person_name, sizeof(person_name), \"%.*s\",\n-\t\t\t (int)(name_end - name_start), name_start);\n-\t\tsnprintf(person_mail, sizeof(person_mail), \"%.*s\",\n-\t\t\t (int)(mail_end - mail_start), mail_start);\n-\t\tmailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));\n-\t\tname_start = person_name;\n-\t\tname_end = name_start + strlen(person_name);\n-\t\tmail_start = person_mail;\n-\t\tmail_end = mail_start +  strlen(person_mail);\n-\t}\n+\tname = s.name_begin;\n+\tnamelen = s.name_end - s.name_begin;\n+\tmail = s.mail_begin;\n+\tmaillen = s.mail_end - s.mail_begin;\n+\n+\tif (part == 'N' || part == 'E') /* mailmap lookup */\n+\t\tmailmap_name(&mail, &maillen, &name, &namelen);\n \tif (part == 'n' || part == 'N') {\t/* name */\n-\t\tstrbuf_add(sb, name_start, name_end-name_start);\n+\t\tstrbuf_add(sb, name, namelen);\n \t\treturn placeholder_len;\n \t}\n \tif (part == 'e' || part == 'E') {\t/* email */\n-\t\tstrbuf_add(sb, mail_start, mail_end-mail_start);\n+\t\tstrbuf_add(sb, mail, maillen);\n \t\treturn placeholder_len;\n \t}\n \n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206057","messageId":"1357421206-5014-6-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 05/10] mailmap: add mailmap structure to rev_info and pp","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:41Z","receivedAt":"2013-01-05T21:26:41Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"the mailmap string_list structure filled with mailmap\ninformation is passed along from rev_info to pretty_print_context\nto provide mailmap information to pretty print each commits\nwith the correct username and email.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n commit.h   | 1 +\n log-tree.c | 1 +\n revision.h | 1 +\n 3 files changed, 3 insertions(+)\n\ndiff --git a/commit.h b/commit.h\nindex b6ad8f3..7f8f987 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -89,6 +89,7 @@ struct pretty_print_context {\n \tchar *notes_message;\n \tstruct reflog_walk_info *reflog_info;\n \tconst char *output_encoding;\n+\tstruct string_list *mailmap;\n };\n \n struct userformat_want {\ndiff --git a/log-tree.c b/log-tree.c\nindex 4f86def..92254fd 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -671,6 +671,7 @@ void show_log(struct rev_info *opt)\n \tctx.preserve_subject = opt->preserve_subject;\n \tctx.reflog_info = opt->reflog_info;\n \tctx.fmt = opt->commit_format;\n+\tctx.mailmap = opt->mailmap;\n \tpretty_print_commit(&ctx, commit, &msgbuf);\n \n \tif (opt->add_signoff)\ndiff --git a/revision.h b/revision.h\nindex 059bfff..83a79f5 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -143,6 +143,7 @@ struct rev_info {\n \tconst char\t*subject_prefix;\n \tint\t\tno_inline;\n \tint\t\tshow_log_size;\n+\tstruct string_list *mailmap;\n \n \t/* Filter by commit log message */\n \tstruct grep_opt\tgrep_filter;\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206055","messageId":"1357421206-5014-7-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 06/10] pretty: use mailmap to display username and email","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:42Z","receivedAt":"2013-01-05T21:26:42Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"Use the mailmap information to display the correct\nusername and email address in all log commands.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n pretty.c | 58 +++++++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 37 insertions(+), 21 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex dffcade..622275c 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -387,9 +387,13 @@ void pp_user_info(const struct pretty_print_context *pp,\n \t\t  const char *what, struct strbuf *sb,\n \t\t  const char *line, const char *encoding)\n {\n+\tstruct strbuf name;\n+\tstruct strbuf mail;\n \tstruct ident_split ident;\n-\tint linelen, namelen;\n+\tint linelen;\n \tchar *line_end, *date;\n+\tconst char *mailbuf, *namebuf;\n+\tsize_t namelen, maillen;\n \tint max_length = 78; /* per rfc2822 */\n \tunsigned long time;\n \tint tz;\n@@ -408,42 +412,54 @@ void pp_user_info(const struct pretty_print_context *pp,\n \tif (split_ident_line(&ident, line, linelen))\n \t\treturn;\n \n-\tnamelen = ident.mail_end - ident.name_begin + 1;\n+\n+\tmailbuf = ident.mail_begin;\n+\tmaillen = ident.mail_end - ident.mail_begin;\n+\tnamebuf = ident.name_begin;\n+\tnamelen = ident.name_end - ident.name_begin;\n+\n+\tif (pp->mailmap)\n+\t\tmap_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);\n+\n+\tstrbuf_init(&mail, 0);\n+\tstrbuf_init(&name, 0);\n+\n+\tstrbuf_add(&mail, mailbuf, maillen);\n+\tstrbuf_add(&name, namebuf, namelen);\n+\n+\tnamelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */\n \ttime = strtoul(ident.date_begin, &date, 10);\n \ttz = strtol(date, NULL, 10);\n \n \tif (pp->fmt == CMIT_FMT_EMAIL) {\n-\t\tint display_name_length;\n-\n-\t\tdisplay_name_length = ident.name_end - ident.name_begin;\n-\n \t\tstrbuf_addstr(sb, \"From: \");\n-\t\tif (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {\n-\t\t\tadd_rfc2047(sb, line, display_name_length,\n-\t\t\t\t\t\tencoding, RFC2047_ADDRESS);\n+\t\tif (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) {\n+\t\t\tadd_rfc2047(sb, name.buf, name.len,\n+\t\t\t\t    encoding, RFC2047_ADDRESS);\n \t\t\tmax_length = 76; /* per rfc2047 */\n-\t\t} else if (needs_rfc822_quoting(line, display_name_length)) {\n+\t\t} else if (needs_rfc822_quoting(name.buf, name.len)) {\n \t\t\tstruct strbuf quoted = STRBUF_INIT;\n-\t\t\tadd_rfc822_quoted(&quoted, line, display_name_length);\n+\t\t\tadd_rfc822_quoted(&quoted, name.buf, name.len);\n \t\t\tstrbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,\n \t\t\t\t\t\t\t-6, 1, max_length);\n \t\t\tstrbuf_release(&quoted);\n \t\t} else {\n-\t\t\tstrbuf_add_wrapped_bytes(sb, line, display_name_length,\n-\t\t\t\t\t\t\t-6, 1, max_length);\n+\t\t\tstrbuf_add_wrapped_bytes(sb, name.buf, name.len,\n+\t\t\t\t\t\t -6, 1, max_length);\n \t\t}\n-\t\tif (namelen - display_name_length + last_line_length(sb) > max_length) {\n+\t\tif (namelen - name.len + last_line_length(sb) > max_length)\n \t\t\tstrbuf_addch(sb, '\\n');\n-\t\t\tif (!isspace(ident.name_end[0]))\n-\t\t\t\tstrbuf_addch(sb, ' ');\n-\t\t}\n-\t\tstrbuf_add(sb, ident.name_end, namelen - display_name_length);\n-\t\tstrbuf_addch(sb, '\\n');\n+\n+\t\tstrbuf_addf(sb, \" <%s>\\n\", mail.buf);\n \t} else {\n-\t\tstrbuf_addf(sb, \"%s: %.*s%.*s\\n\", what,\n+\t\tstrbuf_addf(sb, \"%s: %.*s%s <%s>\\n\", what,\n \t\t\t      (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,\n-\t\t\t      \"    \", namelen, line);\n+\t\t\t      \"    \", name.buf, mail.buf);\n \t}\n+\n+\tstrbuf_release(&mail);\n+\tstrbuf_release(&name);\n+\n \tswitch (pp->fmt) {\n \tcase CMIT_FMT_MEDIUM:\n \t\tstrbuf_addf(sb, \"Date:   %s\\n\", show_date(time, tz, pp->date_mode));\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206062","messageId":"1357421206-5014-8-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 07/10] log: add --use-mailmap option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:43Z","receivedAt":"2013-01-05T21:26:43Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"Add the --use-mailmap option to log commands. It allows\nto display names from mailmap file when displaying logs,\nwhatever the format used.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n Documentation/git-log.txt | 5 +++++\n builtin/log.c             | 9 ++++++++-\n 2 files changed, 13 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-log.txt b/Documentation/git-log.txt\nindex 585dac4..a99be97 100644\n--- a/Documentation/git-log.txt\n+++ b/Documentation/git-log.txt\n@@ -47,6 +47,11 @@ OPTIONS\n \tPrint out the ref name given on the command line by which each\n \tcommit was reached.\n \n+--use-mailmap::\n+\tUse mailmap file to map author and committer names and email\n+\tto canonical real names and email addresses. See\n+\tlinkgit:git-shortlog[1].\n+\n --full-diff::\n \tWithout this flag, \"git log -p <path>...\" shows commits that\n \ttouch the specified paths, and diffs about the same specified\ndiff --git a/builtin/log.c b/builtin/log.c\nindex e7b7db1..d2bd8ce 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -22,6 +22,7 @@\n #include \"branch.h\"\n #include \"streaming.h\"\n #include \"version.h\"\n+#include \"mailmap.h\"\n \n /* Set a default date-time format for git log (\"log.date\" config variable) */\n static const char *default_date_mode = NULL;\n@@ -94,11 +95,12 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \t\t\t struct rev_info *rev, struct setup_revision_opt *opt)\n {\n \tstruct userformat_want w;\n-\tint quiet = 0, source = 0;\n+\tint quiet = 0, source = 0, mailmap = 0;\n \n \tconst struct option builtin_log_options[] = {\n \t\tOPT_BOOLEAN(0, \"quiet\", &quiet, N_(\"suppress diff output\")),\n \t\tOPT_BOOLEAN(0, \"source\", &source, N_(\"show source\")),\n+\t\tOPT_BOOLEAN(0, \"use-mailmap\", &mailmap, N_(\"Use mail map file\")),\n \t\t{ OPTION_CALLBACK, 0, \"decorate\", NULL, NULL, N_(\"decorate options\"),\n \t\t  PARSE_OPT_OPTARG, decorate_callback},\n \t\tOPT_END()\n@@ -136,6 +138,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \tif (source)\n \t\trev->show_source = 1;\n \n+\tif (mailmap) {\n+\t\trev->mailmap = xcalloc(1, sizeof(struct string_list));\n+\t\tread_mailmap(rev->mailmap, NULL);\n+\t}\n+\n \tif (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {\n \t\t/*\n \t\t * \"log --pretty=raw\" is special; ignore UI oriented\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206053","messageId":"1357421206-5014-9-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 08/10] test: add test for --use-mailmap option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:44Z","receivedAt":"2013-01-05T21:26:44Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"The new option '--use-mailmap' can be used to make\nsure that mailmap file is used to convert name\nwhen running log commands.\n\nThe test is simple and checks that the Author line\nis correctly replaced when running log.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n t/t4203-mailmap.sh | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh\nindex 1f182f6..db043dc 100755\n--- a/t/t4203-mailmap.sh\n+++ b/t/t4203-mailmap.sh\n@@ -239,6 +239,20 @@ test_expect_success 'Log output (complex mapping)' '\n \ttest_cmp expect actual\n '\n \n+cat >expect <<\\EOF\n+Author: CTO <cto@company.xx>\n+Author: Santa Claus <santa.claus@northpole.xx>\n+Author: Santa Claus <santa.claus@northpole.xx>\n+Author: Other Author <other@author.xx>\n+Author: Other Author <other@author.xx>\n+Author: Some Dude <some@dude.xx>\n+Author: A U Thor <author@example.com>\n+EOF\n+test_expect_success 'Log output with --use-mailmap' '\n+\tgit log --use-mailmap | grep Author >actual &&\n+\ttest_cmp expect actual\n+'\n+\n # git blame\n cat >expect <<\\EOF\n ^OBJI (A U Thor     DATE 1) one\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206054","messageId":"1357421206-5014-10-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 09/10] log: grep author/committer using mailmap","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:45Z","receivedAt":"2013-01-05T21:26:45Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"Currently you can use mailmap to display log authors and committers\nbut you can't use the mailmap to find commits with mapped values.\n\nThis commit allows you to run:\n\n    git log --use-mailmap --author mapped_name_or_email\n    git log --use-mailmap --committer mapped_name_or_email\n\nOf course it only works if the --use-mailmap option is used.\n\nThe new name and email are copied only when necessary.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n revision.c         | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t4203-mailmap.sh | 18 ++++++++++++++++++\n 2 files changed, 72 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 95d21e6..2cce85a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -13,6 +13,7 @@\n #include \"decorate.h\"\n #include \"log-tree.h\"\n #include \"string-list.h\"\n+#include \"mailmap.h\"\n \n volatile show_early_output_fn_t show_early_output;\n \n@@ -2219,6 +2220,51 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)\n \treturn 0;\n }\n \n+static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap)\n+{\n+\tchar *person, *endp;\n+\tsize_t len, namelen, maillen;\n+\tconst char *name;\n+\tconst char *mail;\n+\tstruct ident_split ident;\n+\n+\tperson = strstr(buf->buf, what);\n+\tif (!person)\n+\t\treturn 0;\n+\n+\tperson += strlen(what);\n+\tendp = strchr(person, '\\n');\n+\tif (!endp)\n+\t\treturn 0;\n+\n+\tlen = endp - person;\n+\n+\tif (split_ident_line(&ident, person, len))\n+\t\treturn 0;\n+\n+\tmail = ident.mail_begin;\n+\tmaillen = ident.mail_end - ident.mail_begin;\n+\tname = ident.name_begin;\n+\tnamelen = ident.name_end - ident.name_begin;\n+\n+\tif (map_user(mailmap, &mail, &maillen, &name, &namelen)) {\n+\t\tstruct strbuf namemail = STRBUF_INIT;\n+\n+\t\tstrbuf_addf(&namemail, \"%.*s <%.*s>\",\n+\t\t\t    (int)namelen, name, (int)maillen, mail);\n+\n+\t\tstrbuf_splice(buf, ident.name_begin - buf->buf,\n+\t\t\t      ident.mail_end - ident.name_begin + 1,\n+\t\t\t      namemail.buf, namemail.len);\n+\n+\t\tstrbuf_release(&namemail);\n+\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int commit_match(struct commit *commit, struct rev_info *opt)\n {\n \tint retval;\n@@ -2237,6 +2283,14 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \tif (buf.len)\n \t\tstrbuf_addstr(&buf, commit->buffer);\n \n+\tif (opt->mailmap) {\n+\t\tif (!buf.len)\n+\t\t\tstrbuf_addstr(&buf, commit->buffer);\n+\n+\t\tcommit_rewrite_person(&buf, \"\\nauthor \", opt->mailmap);\n+\t\tcommit_rewrite_person(&buf, \"\\ncommitter \", opt->mailmap);\n+\t}\n+\n \t/* Append \"fake\" message parts as needed */\n \tif (opt->show_notes) {\n \t\tif (!buf.len)\ndiff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh\nindex db043dc..e16187f 100755\n--- a/t/t4203-mailmap.sh\n+++ b/t/t4203-mailmap.sh\n@@ -248,11 +248,29 @@ Author: Other Author <other@author.xx>\n Author: Some Dude <some@dude.xx>\n Author: A U Thor <author@example.com>\n EOF\n+\n test_expect_success 'Log output with --use-mailmap' '\n \tgit log --use-mailmap | grep Author >actual &&\n \ttest_cmp expect actual\n '\n \n+cat >expect <<\\EOF\n+Author: Santa Claus <santa.claus@northpole.xx>\n+Author: Santa Claus <santa.claus@northpole.xx>\n+EOF\n+\n+test_expect_success 'Grep author with --use-mailmap' '\n+\tgit log --use-mailmap --author Santa | grep Author >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+>expect\n+\n+test_expect_success 'Only grep replaced author with --use-mailmap' '\n+\tgit log --use-mailmap --author \"<cto@coompany.xx>\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n # git blame\n cat >expect <<\\EOF\n ^OBJI (A U Thor     DATE 1) one\n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206059","messageId":"1357421206-5014-11-git-send-email-apelisse@gmail.com","threadId":"32537","inReplyTo":"1357421206-5014-1-git-send-email-apelisse@gmail.com","subject":"[PATCH 10/10] log: add log.mailmap configuration option","fromName":"Antoine Pelisse","fromEmail":"apelisse@gmail.com","sentAt":"2013-01-05T21:26:46Z","receivedAt":"2013-01-05T21:26:46Z","isPatch":true,"sender":{"key":"apelisse@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1929644?v=4"},"body":"Teach \"log.mailmap\" configuration variable to turn \"--use-mailmap\"\noption on to \"git log\", \"git show\" and \"git whatchanged\".\n\nThe \"--no-use-mailmap\" option from the command line can countermand\nthe setting.\n\nSigned-off-by: Antoine Pelisse <apelisse@gmail.com>\n---\n Documentation/config.txt |  4 ++++\n builtin/log.c            |  7 +++++++\n t/t4203-mailmap.sh       | 24 ++++++++++++++++++++++++\n 3 files changed, 35 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex bf8f911..226362a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1509,6 +1509,10 @@ log.showroot::\n \tTools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which\n \tnormally hide the root commit will now show it. True by default.\n \n+log.mailmap::\n+\tIf true, makes linkgit:git-log[1], linkgit:git-show[1], and\n+\tlinkgit:git-whatchanged[1] assume `--use-mailmap`.\n+\n mailmap.file::\n \tThe location of an augmenting mailmap file. The default\n \tmailmap, located in the root of the repository, is loaded\ndiff --git a/builtin/log.c b/builtin/log.c\nindex d2bd8ce..16e6520 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -31,6 +31,7 @@ static int default_abbrev_commit;\n static int default_show_root = 1;\n static int decoration_style;\n static int decoration_given;\n+static int use_mailmap_config;\n static const char *fmt_patch_subject_prefix = \"PATCH\";\n static const char *fmt_pretty;\n \n@@ -106,6 +107,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \t\tOPT_END()\n \t};\n \n+\tmailmap = use_mailmap_config;\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_log_options, builtin_log_usage,\n \t\t\t     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |\n@@ -358,6 +360,11 @@ static int git_log_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!prefixcmp(var, \"color.decorate.\"))\n \t\treturn parse_decorate_color_config(var, 15, value);\n+\tif (!strcmp(var, \"log.mailmap\")) {\n+\t\tuse_mailmap_config = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (grep_config(var, value, cb) < 0)\n \t\treturn -1;\n \treturn git_diff_ui_config(var, value, cb);\ndiff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh\nindex e16187f..7d4d31c 100755\n--- a/t/t4203-mailmap.sh\n+++ b/t/t4203-mailmap.sh\n@@ -255,6 +255,21 @@ test_expect_success 'Log output with --use-mailmap' '\n '\n \n cat >expect <<\\EOF\n+Author: CTO <cto@company.xx>\n+Author: Santa Claus <santa.claus@northpole.xx>\n+Author: Santa Claus <santa.claus@northpole.xx>\n+Author: Other Author <other@author.xx>\n+Author: Other Author <other@author.xx>\n+Author: Some Dude <some@dude.xx>\n+Author: A U Thor <author@example.com>\n+EOF\n+\n+test_expect_success 'Log output with log.mailmap' '\n+\tgit -c log.mailmap=True log | grep Author >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+cat >expect <<\\EOF\n Author: Santa Claus <santa.claus@northpole.xx>\n Author: Santa Claus <santa.claus@northpole.xx>\n EOF\n@@ -263,6 +278,15 @@ test_expect_success 'Grep author with --use-mailmap' '\n \tgit log --use-mailmap --author Santa | grep Author >actual &&\n \ttest_cmp expect actual\n '\n+cat >expect <<\\EOF\n+Author: Santa Claus <santa.claus@northpole.xx>\n+Author: Santa Claus <santa.claus@northpole.xx>\n+EOF\n+\n+test_expect_success 'Grep author with log.mailmap' '\n+\tgit -c log.mailmap=True log --author Santa | grep Author >actual &&\n+\ttest_cmp expect actual\n+'\n \n >expect\n \n-- \n1.7.12.4.3.g2036a08.dirty\n"},{"id":"206064","messageId":"7vmwwnp84p.fsf@alter.siamese.dyndns.org","threadId":"32537","inReplyTo":"1357421206-5014-2-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH 01/10] list_lookup: create case and length search","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-05T22:39:18Z","receivedAt":"2013-01-05T22:39:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> Create a new function to look-up a string in a string_list, but:\n>  - add a new parameter to ignore case differences\n>  - add a length parameter to search for a substring\n>\n> The idea is to avoid several copies (lowering a string before searching\n> it when we just want to ignore case), or copying a substring of a bigger\n> string to search it in the string_list\n>\n> Signed-off-by: Antoine Pelisse <apelisse@gmail.com>\n> ---\n\nI did not read beyond the log message and the function signature of\nthe updated get_entry_index(), but it strikes me insane to build a\nsorted list using case sensitive full string comarison and then to\nlook for an element in that list using a different comparison\ncriteria (e.g. case insensitive comparison) and to expect the\nbisection search to still work.  Shouldn't the codepath that builds\nthe string-list be sorting the list in case insensitive way from the\nbeginning if this were to work correctly?\n\nIt seems to suggest to me that this \"are the keys case sensitive?\"\nbit belongs to the entire struct string_list structure as its\nproperty (similar to the way \"do the keys need to be strdup'ed?\"\nbit), not something you would flip per invocation basis of the\nlookup function.\n\nAlso isn't size_t an unsigned type?  What does it even mean to pass\n\"-1\" to it, and propagate it down to strncmp()?\n\nIf you have a list sorted by a key, and if you want to query it with\na partial prefix of a possibly valid key, I think you shouldn't have\nto add the \"length search\" at all. The existing look up function\nwould return the location in the list that short key would have been\ninserted, which would come before the first entry that your short\nkey is a prefix of, so the caller can iterate the list from there to\nfind all entries.  In other words, if existing list has \"aaa\",\n\"bba\", and \"bbc\", and you want to grab entries that begin with \"bb\",\nyou can ask for \"bb\" to the loop up function, which would say \"the\nkey does not exist in the list, but it would be inserted before this\n'bba' entry\".  Then you can discover that \"bba\" and \"bbc\" both\nmatches the shortened key you have, no?\n"},{"id":"206185","messageId":"7vehhxnco3.fsf@alter.siamese.dyndns.org","threadId":"32537","inReplyTo":"1357421206-5014-5-git-send-email-apelisse@gmail.com","subject":"Re: [PATCH 04/10] mailmap: simplify map_user() interface","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-06T22:56:28Z","receivedAt":"2013-01-06T22:56:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Antoine Pelisse <apelisse@gmail.com> writes:\n\n> diff --git a/builtin/blame.c b/builtin/blame.c\n> index dd4aff9..86450e3 100644\n> --- a/builtin/blame.c\n> +++ b/builtin/blame.c\n> ...\n> @@ -1356,51 +1356,61 @@ static void get_ac_line(const char *inbuf, const char *what,\n>  \t\tlen = strlen(tmp);\n>  \telse\n>  \t\tlen = endp - tmp;\n>  \n>  \tif (split_ident_line(&ident, tmp, len)) {\n>  \terror_out:\n>  \t\t/* Ugh */\n> +\t\ttmp = \"(unknown)\";\n> +\t\tstrbuf_addstr(name, tmp);\n> +\t\tstrbuf_addstr(mail, tmp);\n> +\t\tstrbuf_addstr(tz, tmp);\n>  \t\t*time = 0;\n>  \t\treturn;\n>  \t}\n>  \n>  \tnamelen = ident.name_end - ident.name_begin;\n> +\ttmpname = ident.name_begin;\n>  \n> +\tmaillen = ident.mail_end - ident.mail_begin;\n> +\ttmpmail = ident.mail_begin;\n>  \n>  \t*time = strtoul(ident.date_begin, NULL, 10);\n>  \n> +\tlen = ident.tz_end - ident.tz_begin;\n> +\tstrbuf_add(tz, ident.tz_begin, len);\n>  \n>  \t/*\n>  \t * Now, convert both name and e-mail using mailmap\n>  \t */\n> +\tmap_user(&mailmap, &tmpmail, &maillen,\n> +\t\t &tmpname, &namelen);\n\nI like the general simplification this change gives us, but do we\nstill want to name these variables \"tmp\"-something?  At least to me,\nit makes it look like the variable holds a pointer to a piece of\nmemory that was temporarily allocated.  Calling it \"mail_begin\" or\nsomething might be less confusing.\n\nThe changes to pretty.c (pp_user_info) and shortlog.c\n(insert_one_record) calls these variables mailbuf and namebuf,\nso perhaps these are better names?\n"},{"id":"206250","messageId":"7vpq1ghb64.fsf@alter.siamese.dyndns.org","threadId":"32537","inReplyTo":"7vmwwnp84p.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 01/10] list_lookup: create case and length search","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-07T22:37:39Z","receivedAt":"2013-01-07T22:37:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Antoine Pelisse <apelisse@gmail.com> writes:\n>\n>> Create a new function to look-up a string in a string_list, but:\n>>  - add a new parameter to ignore case differences\n>>  - add a length parameter to search for a substring\n>>\n>> The idea is to avoid several copies (lowering a string before searching\n>> it when we just want to ignore case), or copying a substring of a bigger\n>> string to search it in the string_list\n>>\n>> Signed-off-by: Antoine Pelisse <apelisse@gmail.com>\n>> ---\n>\n> I did not read beyond the log message and the function signature of\n> the updated get_entry_index(), but it strikes me insane to build a\n> sorted list using case sensitive full string comarison and then to\n> look for an element in that list using a different comparison\n> criteria (e.g. case insensitive comparison) and to expect the\n> bisection search to still work.  Shouldn't the codepath that builds\n> the string-list be sorting the list in case insensitive way from the\n> beginning if this were to work correctly?\n>\n> It seems to suggest to me that this \"are the keys case sensitive?\"\n> bit belongs to the entire struct string_list structure as its\n> property (similar to the way \"do the keys need to be strdup'ed?\"\n> bit), not something you would flip per invocation basis of the\n> lookup function.\n>\n> Also isn't size_t an unsigned type?  What does it even mean to pass\n> \"-1\" to it, and propagate it down to strncmp()?\n>\n> If you have a list sorted by a key, and if you want to query it with\n> a partial prefix of a possibly valid key, I think you shouldn't have\n> to add the \"length search\" at all. The existing look up function\n> would return the location in the list that short key would have been\n> inserted, which would come before the first entry that your short\n> key is a prefix of, so the caller can iterate the list from there to\n> find all entries.  In other words, if existing list has \"aaa\",\n> \"bba\", and \"bbc\", and you want to grab entries that begin with \"bb\",\n> you can ask for \"bb\" to the loop up function, which would say \"the\n> key does not exist in the list, but it would be inserted before this\n> 'bba' entry\".  Then you can discover that \"bba\" and \"bbc\" both\n> matches the shortened key you have, no?\n\nIn the above, I mixed up which string is a prefix of which, but the\noverall comment still stands, I think.\n\nYour mailmap string list has \"Ee@eee\" (email part only) stored, while\nthe record from author/committer line has \"NNNN <EE@EEE> TTTTTT sZZZZ\",\nto hold name, timestamp and zone.  You have a pointer email pointing\nat the first \"E\" in that record, with maillen set to 6, and see if\nthe mailmap contains a string that match \"EE@EEE\".  So I mixed up\nthe prefix-ness of these strings.\n\nLet's address the case sensitivety first.  The string_list_lookup()\nand your string_list_lookup_extended() functions both work on a\nstring list that is built by add_mapping() function that asks\nstring_list_find_insert_index() to find an insertion point in the\nsorted string list.  If this is not done using the same case\ninsensitive manner for a string list that is meant to be queried\ncase insensitively, the resulting list is still sorted case\nsensitively, and look-up functions that work by binary search won't\nfind what you are looking for when they try to find the string using\ncase-insensitive comparison.  The change to implement it would\nprobably look like the attached patch.\n\nAssuming that we can leave the case sensitivity behind us that way,\nwe still would need a new helper function\n\n    string_list_lookup_prefix(struct string_list *, const char*, size_t)\n\nso that you can find an existing \"Ee@eee\" entry in the string list\nby email that points at \"EE@EEE> TTTTTT sZZZZ\" with maillen of 6.\n\nI am wondering if we can do that without adding the length-limited\nsort in the low-level get_entry_index() to do so, though.\n\nYou could first ask string_list_find_insert_index() to locate the\ninsertion point for \"EE@EEE> TTTTTT sZZZZ\".  That has to come after\n\"Ee@eee\" if it exists (or it could be \"De@eee\" in which case you\nknow there is no match).\n\n 1. Ask string_list_find_insert_index() to locate \"EE@EEE> TTTTTT...\"\n\n    a. If it says there is an exact match, then that did not match\n       \"EE@EEE\" list (you found \"EE@EEE> TTTTTT sZZZZ\").  You might\n       still find \"EE@EEE\" before that record, though.  Also your\n       key may have been \"EE@EEE\" exactly, in which case you already\n       found what you were looking for.\n\n    b. If it says there is not an exact match, the returned value is\n       pointing at a record that comes after \"EE@EEE> TTTTTT sZZZZ\" if\n       you were to insert it, which definitely is after \"EE@EEE\".\n\n    So in either case, you know what you are looking for must come\n    before string_list_find_insert_index() gave you.  Let's call\n    that position \"i\".\n\n    You still need to be careful that there could be an existing\n    entry whose key is \"EE@EEE\" followed by a byte that sorts before\n    \">\" or \" \" (we do not want to limit this new generic string-list\n    function to operate only on e-mail addresses), though.  So the\n    above \"must come before\" is not \"must come immediately before\".\n\n 2. Then compare map->items[i].string and email using strncasecmp()\n    for maillen bytes, and make sure map->items[].string is exactly\n    maillen bytes long.  If it matches, you found what you were\n    looking for.  Otherwise, decrement \"i\" (e.g. the overlong key\n    string you had was \"EE@EEE> TTTTTT...\" and the record at one\n    before the insertion point was \"EE@EEE\\t\" which sorts before it,\n    but the string-list may have \"EE@EEE\" before that entry), see if\n    the first maillen bytes still match, and continue.  Once you\n    decrement i enough, the first maillen bytes won't match\n    (e.g. you hit \"De@EEE\"), or \"i\" goes negative, and you can stop.\n\nor something like that.  I'll cook up a patch and see how ugly it\nends up with.\n\n-- >8 --\nSubject: [PATCH] string-list: allow case-insensitive string list\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * At the very beginning of mailmap.c::read_mailmap(), you would say\n   something like:\n\n\tint read_mailmap(struct string_list *map, char **repo_ident)\n        {\n        \tmap->strdup_strings = 1;\n       +        map->cmp = strcasecmp;\n                /* each failure returns 1, so >1 means both calls ...\n\n   to make the mailmap string list a case insensitive one.\n\n string-list.c | 17 +++++++++++++----\n string-list.h |  4 ++++\n 2 files changed, 17 insertions(+), 4 deletions(-)\n\ndiff --git a/string-list.c b/string-list.c\nindex 397e6cf..6f3d8cf 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -7,10 +7,11 @@ static int get_entry_index(const struct string_list *list, const char *string,\n \t\tint *exact_match)\n {\n \tint left = -1, right = list->nr;\n+\tcompare_strings_fn cmp = list->cmp ? list->cmp : strcmp;\n \n \twhile (left + 1 < right) {\n \t\tint middle = (left + right) / 2;\n-\t\tint compare = strcmp(string, list->items[middle].string);\n+\t\tint compare = cmp(string, list->items[middle].string);\n \t\tif (compare < 0)\n \t\t\tright = middle;\n \t\telse if (compare > 0)\n@@ -96,8 +97,9 @@ void string_list_remove_duplicates(struct string_list *list, int free_util)\n {\n \tif (list->nr > 1) {\n \t\tint src, dst;\n+\t\tcompare_strings_fn cmp = list->cmp ? list->cmp : strcmp;\n \t\tfor (src = dst = 1; src < list->nr; src++) {\n-\t\t\tif (!strcmp(list->items[dst - 1].string, list->items[src].string)) {\n+\t\t\tif (!cmp(list->items[dst - 1].string, list->items[src].string)) {\n \t\t\t\tif (list->strdup_strings)\n \t\t\t\t\tfree(list->items[src].string);\n \t\t\t\tif (free_util)\n@@ -230,15 +232,20 @@ struct string_list_item *string_list_append(struct string_list *list,\n \t\t\tlist->strdup_strings ? xstrdup(string) : (char *)string);\n }\n \n+/* Yuck */\n+static compare_strings_fn compare_for_qsort;\n+\n+/* Only call this from inside sort_string_list! */\n static int cmp_items(const void *a, const void *b)\n {\n \tconst struct string_list_item *one = a;\n \tconst struct string_list_item *two = b;\n-\treturn strcmp(one->string, two->string);\n+\treturn compare_for_qsort(one->string, two->string);\n }\n \n void sort_string_list(struct string_list *list)\n {\n+\tcompare_for_qsort = list->cmp ? list->cmp : strcmp;\n \tqsort(list->items, list->nr, sizeof(*list->items), cmp_items);\n }\n \n@@ -246,8 +253,10 @@ struct string_list_item *unsorted_string_list_lookup(struct string_list *list,\n \t\t\t\t\t\t     const char *string)\n {\n \tint i;\n+\tcompare_strings_fn cmp = list->cmp ? list->cmp : strcmp;\n+\n \tfor (i = 0; i < list->nr; i++)\n-\t\tif (!strcmp(string, list->items[i].string))\n+\t\tif (!cmp(string, list->items[i].string))\n \t\t\treturn list->items + i;\n \treturn NULL;\n }\ndiff --git a/string-list.h b/string-list.h\nindex c50b0d0..446e79e 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -5,10 +5,14 @@ struct string_list_item {\n \tchar *string;\n \tvoid *util;\n };\n+\n+typedef int (*compare_strings_fn)(const char *, const char *);\n+\n struct string_list {\n \tstruct string_list_item *items;\n \tunsigned int nr, alloc;\n \tunsigned int strdup_strings:1;\n+\tcompare_strings_fn cmp; /* NULL uses strcmp() */\n };\n \n #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }\n-- \n1.8.1.304.gf036638\n"}]}