Change current mailmap usage to do matching on both name and email of author/committer.
Signed-off-by: Marius Storm-Olsen <marius@trolltech.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Marius Storm-Olsen committed
Feb 8, 2009 at 15:34 UTC
d20d654fe8923a502527547b17fe284d15d6aec9
5 files changed
+186
-51
Documentation/pretty-formats.txt
+2
@@ -103,6 +103,7 @@ The placeholders are:
103
- '%an': author name
104
- '%aN': author name (respecting .mailmap)
105
- '%ae': author email
106
+- '%aE': author email (respecting .mailmap)
107
- '%ad': author date (format respects --date= option)
108
- '%aD': author date, RFC2822 style
109
- '%ar': author date, relative
@@ -111,6 +112,7 @@ The placeholders are:
112
- '%cn': committer name
113
- '%cN': committer name (respecting .mailmap)
114
- '%ce': committer email
115
+- '%cE': committer email (respecting .mailmap)
116
- '%cd': committer date
117
- '%cD': committer date, RFC2822 style
118
- '%cr': committer date, relative
builtin-blame.c
+31
-19
@@ -1263,11 +1263,12 @@ struct commit_info
1263
* Parse author/committer line in the commit object buffer
1264
*/
1265
static void get_ac_line(const char *inbuf, const char *what,
1266
- int bufsz, char *person, const char **mail,
1266
+ int person_len, char *person,
1267
+ int mail_len, char *mail,
1268
unsigned long *time, const char **tz)
1269
{
1270
int len, tzlen, maillen;
1270
- char *tmp, *endp, *timepos;
1271
+ char *tmp, *endp, *timepos, *mailpos;
1272
1273
tmp = strstr(inbuf, what);
1274
if (!tmp)
@@ -1278,10 +1279,11 @@ static void get_ac_line(const char *inbuf, const char *what,
1279
len = strlen(tmp);
1280
else
1281
len = endp - tmp;
1281
- if (bufsz <= len) {
1282
+ if (person_len <= len) {
1283
error_out:
1284
/* Ugh */
1284
- *mail = *tz = "(unknown)";
1285
+ *tz = "(unknown)";
1286
+ strcpy(mail, *tz);
1287
*time = 0;
1288
return;
1289
}
@@ -1304,9 +1306,10 @@ static void get_ac_line(const char *inbuf, const char *what,
1306
*tmp = 0;
1307
while (*tmp != ' ')
1308
tmp--;
1307
- *mail = tmp + 1;
1309
+ mailpos = tmp + 1;
1310
*tmp = 0;
1311
maillen = timepos - tmp;
1312
+ memcpy(mail, mailpos, maillen);
1313
1314
if (!mailmap.nr)
1315
return;
@@ -1315,20 +1318,23 @@ static void get_ac_line(const char *inbuf, const char *what,
1318
* mailmap expansion may make the name longer.
1319
* make room by pushing stuff down.
1320
*/
1318
- tmp = person + bufsz - (tzlen + 1);
1321
+ tmp = person + person_len - (tzlen + 1);
1322
memmove(tmp, *tz, tzlen);
1323
tmp[tzlen] = 0;
1324
*tz = tmp;
1325
1323
- tmp = tmp - (maillen + 1);
1324
- memmove(tmp, *mail, maillen);
1325
- tmp[maillen] = 0;
1326
- *mail = tmp;
1327
-
1326
/*
1329
- * Now, convert e-mail using mailmap
1327
+ * Now, convert both name and e-mail using mailmap
1328
*/
1331
- map_email(&mailmap, tmp + 1, person, tmp-person-1);
1329
+ if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
1330
+ /* Add a trailing '>' to email, since map_user returns plain emails
1331
+ Note: It already has '<', since we replace from mail+1 */
1332
+ mailpos = memchr(mail, '\0', mail_len);
1333
+ if (mailpos && mailpos-mail < mail_len - 1) {
1334
+ *mailpos = '>';
1335
+ *(mailpos+1) = '\0';
1336
+ }
1337
+ }
1338
}
1339
1340
static void get_commit_info(struct commit *commit,
@@ -1337,8 +1343,10 @@ static void get_commit_info(struct commit *commit,
1343
{
1344
int len;
1345
char *tmp, *endp, *reencoded, *message;
1340
- static char author_buf[1024];
1341
- static char committer_buf[1024];
1346
+ static char author_name[1024];
1347
+ static char author_mail[1024];
1348
+ static char committer_name[1024];
1349
+ static char committer_mail[1024];
1350
static char summary_buf[1024];
1351
1352
/*
@@ -1356,9 +1364,11 @@ static void get_commit_info(struct commit *commit,
1364
}
1365
reencoded = reencode_commit_message(commit, NULL);
1366
message = reencoded ? reencoded : commit->buffer;
1359
- ret->author = author_buf;
1367
+ ret->author = author_name;
1368
+ ret->author_mail = author_mail;
1369
get_ac_line(message, "\nauthor ",
1361
- sizeof(author_buf), author_buf, &ret->author_mail,
1370
+ sizeof(author_name), author_name,
1371
+ sizeof(author_mail), author_mail,
1372
&ret->author_time, &ret->author_tz);
1373
1374
if (!detailed) {
@@ -1366,9 +1376,11 @@ static void get_commit_info(struct commit *commit,
1376
return;
1377
}
1378
1369
- ret->committer = committer_buf;
1379
+ ret->committer = committer_name;
1380
+ ret->committer_mail = committer_mail;
1381
get_ac_line(message, "\ncommitter ",
1371
- sizeof(committer_buf), committer_buf, &ret->committer_mail,
1382
+ sizeof(committer_name), committer_name,
1383
+ sizeof(committer_mail), committer_mail,
1384
&ret->committer_time, &ret->committer_tz);
1385
1386
ret->summary = summary_buf;
builtin-shortlog.c
+17
-5
@@ -40,6 +40,7 @@ static void insert_one_record(struct shortlog *log,
40
char *buffer, *p;
41
struct string_list_item *item;
42
char namebuf[1024];
43
+ char emailbuf[1024];
44
size_t len;
45
const char *eol;
46
const char *boemail, *eoemail;
@@ -51,7 +52,19 @@ static void insert_one_record(struct shortlog *log,
52
eoemail = strchr(boemail, '>');
53
if (!eoemail)
54
return;
54
- if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
55
+
56
+ /* copy author name to namebuf, to support matching on both name and email */
57
+ memcpy(namebuf, author, boemail - author);
58
+ len = boemail - author;
59
+ while(len > 0 && isspace(namebuf[len-1]))
60
+ len--;
61
+ namebuf[len] = 0;
62
+
63
+ /* copy email name to emailbuf, to allow email replacement as well */
64
+ memcpy(emailbuf, boemail+1, eoemail - boemail);
65
+ emailbuf[eoemail - boemail - 1] = 0;
66
+
67
+ if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
68
while (author < boemail && isspace(*author))
69
author++;
70
for (len = 0;
@@ -67,8 +80,8 @@ static void insert_one_record(struct shortlog *log,
80
81
if (log->email) {
82
size_t room = sizeof(namebuf) - len - 1;
70
- int maillen = eoemail - boemail + 1;
71
- snprintf(namebuf + len, room, " %.*s", maillen, boemail);
83
+ int maillen = strlen(emailbuf);
84
+ snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
85
}
86
87
item = string_list_insert(namebuf, &log->list);
@@ -321,6 +334,5 @@ void shortlog_output(struct shortlog *log)
334
335
log->list.strdup_strings = 1;
336
string_list_clear(&log->list, 1);
324
- log->mailmap.strdup_strings = 1;
325
- string_list_clear(&log->mailmap, 1);
337
+ clear_mailmap(&log->mailmap);
338
}
pretty.c
+30
-27
@@ -305,23 +305,14 @@ static char *logmsg_reencode(const struct commit *commit,
305
return out;
306
}
307
308
-static int mailmap_name(struct strbuf *sb, const char *email)
308
+static int mailmap_name(char *email, int email_len, char *name, int name_len)
309
{
310
static struct string_list *mail_map;
311
- char buffer[1024];
312
-
311
if (!mail_map) {
312
mail_map = xcalloc(1, sizeof(*mail_map));
313
read_mailmap(mail_map, NULL);
314
}
317
-
318
- if (!mail_map->nr)
319
- return -1;
320
-
321
- if (!map_email(mail_map, email, buffer, sizeof(buffer)))
322
- return -1;
323
- strbuf_addstr(sb, buffer);
324
- return 0;
315
+ return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
316
}
317
318
static size_t format_person_part(struct strbuf *sb, char part,
@@ -332,6 +323,9 @@ static size_t format_person_part(struct strbuf *sb, char part,
323
int start, end, tz = 0;
324
unsigned long date = 0;
325
char *ep;
326
+ const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
327
+ char person_name[1024];
328
+ char person_mail[1024];
329
330
/* advance 'end' to point to email start delimiter */
331
for (end = 0; end < len && msg[end] != '<'; end++)
@@ -345,25 +339,34 @@ static size_t format_person_part(struct strbuf *sb, char part,
339
if (end >= len - 2)
340
goto skip;
341
342
+ /* Seek for both name and email part */
343
+ name_start = msg;
344
+ name_end = msg+end;
345
+ while (name_end > name_start && isspace(*(name_end-1)))
346
+ name_end--;
347
+ mail_start = msg+end+1;
348
+ mail_end = mail_start;
349
+ while (mail_end < msg_end && *mail_end != '>')
350
+ mail_end++;
351
+ if (mail_end == msg_end)
352
+ goto skip;
353
+ end = mail_end-msg;
354
+
355
+ if (part == 'N' || part == 'E') { /* mailmap lookup */
356
+ strlcpy(person_name, name_start, name_end-name_start+1);
357
+ strlcpy(person_mail, mail_start, mail_end-mail_start+1);
358
+ mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
359
+ name_start = person_name;
360
+ name_end = name_start + strlen(person_name);
361
+ mail_start = person_mail;
362
+ mail_end = mail_start + strlen(person_mail);
363
+ }
364
if (part == 'n' || part == 'N') { /* name */
349
- while (end > 0 && isspace(msg[end - 1]))
350
- end--;
351
- if (part != 'N' || !msg[end] || !msg[end + 1] ||
352
- mailmap_name(sb, msg + end + 2) < 0)
353
- strbuf_add(sb, msg, end);
365
+ strbuf_add(sb, name_start, name_end-name_start);
366
return placeholder_len;
367
}
356
- start = ++end; /* save email start position */
357
-
358
- /* advance 'end' to point to email end delimiter */
359
- for ( ; end < len && msg[end] != '>'; end++)
360
- ; /* do nothing */
361
-
362
- if (end >= len)
363
- goto skip;
364
-
365
- if (part == 'e') { /* email */
366
- strbuf_add(sb, msg + start, end - start);
368
+ if (part == 'e' || part == 'E') { /* email */
369
+ strbuf_add(sb, mail_start, mail_end-mail_start);
370
return placeholder_len;
371
}
372
t/t4203-mailmap.sh
+106
@@ -106,4 +106,110 @@ test_expect_success 'No mailmap files, but configured' '
106
test_cmp expect actual
107
'
108
109
+# Extended mailmap configurations should give us the following output for shortlog
110
+cat >expect <<\EOF
111
+A U Thor <author@example.com> (1):
112
+ initial
113
+
114
+CTO <cto@company.xx> (1):
115
+ seventh
116
+
117
+Other Author <other@author.xx> (2):
118
+ third
119
+ fourth
120
+
121
+Santa Claus <santa.claus@northpole.xx> (2):
122
+ fifth
123
+ sixth
124
+
125
+Some Dude <some@dude.xx> (1):
126
+ second
127
+
128
+EOF
129
+
130
+test_expect_success 'Shortlog output (complex mapping)' '
131
+ echo three >>one &&
132
+ git add one &&
133
+ test_tick &&
134
+ git commit --author "nick2 <bugs@company.xx>" -m third &&
135
+
136
+ echo four >>one &&
137
+ git add one &&
138
+ test_tick &&
139
+ git commit --author "nick2 <nick2@company.xx>" -m fourth &&
140
+
141
+ echo five >>one &&
142
+ git add one &&
143
+ test_tick &&
144
+ git commit --author "santa <me@company.xx>" -m fifth &&
145
+
146
+ echo six >>one &&
147
+ git add one &&
148
+ test_tick &&
149
+ git commit --author "claus <me@company.xx>" -m sixth &&
150
+
151
+ echo seven >>one &&
152
+ git add one &&
153
+ test_tick &&
154
+ git commit --author "CTO <cto@coompany.xx>" -m seventh &&
155
+
156
+ mkdir internal_mailmap &&
157
+ echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
158
+ echo "<cto@company.xx> <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
159
+ echo "Some Dude <some@dude.xx> nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
160
+ echo "Other Author <other@author.xx> nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
161
+ echo "Other Author <other@author.xx> <nick2@company.xx>" >> internal_mailmap/.mailmap &&
162
+ echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
163
+ echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
164
+
165
+ git shortlog -e HEAD >actual &&
166
+ test_cmp expect actual
167
+
168
+'
169
+
170
+# git log with --pretty format which uses the name and email mailmap placemarkers
171
+cat >expect <<\EOF
172
+Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx>
173
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
174
+
175
+Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
176
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
177
+
178
+Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
179
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
180
+
181
+Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
182
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
183
+
184
+Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
185
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
186
+
187
+Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
188
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
189
+
190
+Author A U Thor <author@example.com> maps to A U Thor <author@example.com>
191
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
192
+EOF
193
+
194
+test_expect_success 'Log output (complex mapping)' '
195
+ git log --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
196
+ test_cmp expect actual
197
+'
198
+
199
+# git blame
200
+cat >expect <<\EOF
201
+^3a2fdcb (A U Thor 2005-04-07 15:13:13 -0700 1) one
202
+7de6f99b (Some Dude 2005-04-07 15:13:13 -0700 2) two
203
+5815879d (Other Author 2005-04-07 15:14:13 -0700 3) three
204
+ff859d96 (Other Author 2005-04-07 15:15:13 -0700 4) four
205
+5ab6d4fa (Santa Claus 2005-04-07 15:16:13 -0700 5) five
206
+38a42d8b (Santa Claus 2005-04-07 15:17:13 -0700 6) six
207
+8ddc0386 (CTO 2005-04-07 15:18:13 -0700 7) seven
208
+EOF
209
+
210
+test_expect_success 'Blame output (complex mapping)' '
211
+ git blame one >actual &&
212
+ test_cmp expect actual
213
+'
214
+
215
test_done