Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
  • Loading branch information...
commit 577f63e781d2f9b9a6862389b6e9d2ca2467afa2 2 parents 02f55e6 + df874fa
Junio C Hamano authored January 20, 2013
4  Documentation/config.txt
@@ -1525,6 +1525,10 @@ log.showroot::
1525 1525
 	Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
1526 1526
 	normally hide the root commit will now show it. True by default.
1527 1527
 
  1528
+log.mailmap::
  1529
+	If true, makes linkgit:git-log[1], linkgit:git-show[1], and
  1530
+	linkgit:git-whatchanged[1] assume `--use-mailmap`.
  1531
+
1528 1532
 mailmap.file::
1529 1533
 	The location of an augmenting mailmap file. The default
1530 1534
 	mailmap, located in the root of the repository, is loaded
5  Documentation/git-log.txt
@@ -47,6 +47,11 @@ OPTIONS
47 47
 	Print out the ref name given on the command line by which each
48 48
 	commit was reached.
49 49
 
  50
+--use-mailmap::
  51
+	Use mailmap file to map author and committer names and email
  52
+	to canonical real names and email addresses. See
  53
+	linkgit:git-shortlog[1].
  54
+
50 55
 --full-diff::
51 56
 	Without this flag, "git log -p <path>..." shows commits that
52 57
 	touch the specified paths, and diffs about the same specified
183  builtin/blame.c
@@ -1322,30 +1322,31 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
1322 1322
  * Information on commits, used for output.
1323 1323
  */
1324 1324
 struct commit_info {
1325  
-	const char *author;
1326  
-	const char *author_mail;
  1325
+	struct strbuf author;
  1326
+	struct strbuf author_mail;
1327 1327
 	unsigned long author_time;
1328  
-	const char *author_tz;
  1328
+	struct strbuf author_tz;
1329 1329
 
1330 1330
 	/* filled only when asked for details */
1331  
-	const char *committer;
1332  
-	const char *committer_mail;
  1331
+	struct strbuf committer;
  1332
+	struct strbuf committer_mail;
1333 1333
 	unsigned long committer_time;
1334  
-	const char *committer_tz;
  1334
+	struct strbuf committer_tz;
1335 1335
 
1336  
-	const char *summary;
  1336
+	struct strbuf summary;
1337 1337
 };
1338 1338
 
1339 1339
 /*
1340 1340
  * Parse author/committer line in the commit object buffer
1341 1341
  */
1342 1342
 static void get_ac_line(const char *inbuf, const char *what,
1343  
-			int person_len, char *person,
1344  
-			int mail_len, char *mail,
1345  
-			unsigned long *time, const char **tz)
  1343
+	struct strbuf *name, struct strbuf *mail,
  1344
+	unsigned long *time, struct strbuf *tz)
1346 1345
 {
1347  
-	int len, tzlen, maillen;
1348  
-	char *tmp, *endp, *timepos, *mailpos;
  1346
+	struct ident_split ident;
  1347
+	size_t len, maillen, namelen;
  1348
+	char *tmp, *endp;
  1349
+	const char *namebuf, *mailbuf;
1349 1350
 
1350 1351
 	tmp = strstr(inbuf, what);
1351 1352
 	if (!tmp)
@@ -1356,69 +1357,61 @@ static void get_ac_line(const char *inbuf, const char *what,
1356 1357
 		len = strlen(tmp);
1357 1358
 	else
1358 1359
 		len = endp - tmp;
1359  
-	if (person_len <= len) {
  1360
+
  1361
+	if (split_ident_line(&ident, tmp, len)) {
1360 1362
 	error_out:
1361 1363
 		/* Ugh */
1362  
-		*tz = "(unknown)";
1363  
-		strcpy(person, *tz);
1364  
-		strcpy(mail, *tz);
  1364
+		tmp = "(unknown)";
  1365
+		strbuf_addstr(name, tmp);
  1366
+		strbuf_addstr(mail, tmp);
  1367
+		strbuf_addstr(tz, tmp);
1365 1368
 		*time = 0;
1366 1369
 		return;
1367 1370
 	}
1368  
-	memcpy(person, tmp, len);
1369 1371
 
1370  
-	tmp = person;
1371  
-	tmp += len;
1372  
-	*tmp = 0;
1373  
-	while (person < tmp && *tmp != ' ')
1374  
-		tmp--;
1375  
-	if (tmp <= person)
1376  
-		goto error_out;
1377  
-	*tz = tmp+1;
1378  
-	tzlen = (person+len)-(tmp+1);
  1372
+	namelen = ident.name_end - ident.name_begin;
  1373
+	namebuf = ident.name_begin;
1379 1374
 
1380  
-	*tmp = 0;
1381  
-	while (person < tmp && *tmp != ' ')
1382  
-		tmp--;
1383  
-	if (tmp <= person)
1384  
-		goto error_out;
1385  
-	*time = strtoul(tmp, NULL, 10);
1386  
-	timepos = tmp;
  1375
+	maillen = ident.mail_end - ident.mail_begin;
  1376
+	mailbuf = ident.mail_begin;
1387 1377
 
1388  
-	*tmp = 0;
1389  
-	while (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))
1390  
-		tmp--;
1391  
-	if (tmp <= person)
1392  
-		return;
1393  
-	mailpos = tmp + 1;
1394  
-	*tmp = 0;
1395  
-	maillen = timepos - tmp;
1396  
-	memcpy(mail, mailpos, maillen);
  1378
+	*time = strtoul(ident.date_begin, NULL, 10);
1397 1379
 
1398  
-	if (!mailmap.nr)
1399  
-		return;
1400  
-
1401  
-	/*
1402  
-	 * mailmap expansion may make the name longer.
1403  
-	 * make room by pushing stuff down.
1404  
-	 */
1405  
-	tmp = person + person_len - (tzlen + 1);
1406  
-	memmove(tmp, *tz, tzlen);
1407  
-	tmp[tzlen] = 0;
1408  
-	*tz = tmp;
  1380
+	len = ident.tz_end - ident.tz_begin;
  1381
+	strbuf_add(tz, ident.tz_begin, len);
1409 1382
 
1410 1383
 	/*
1411 1384
 	 * Now, convert both name and e-mail using mailmap
1412 1385
 	 */
1413  
-	if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
1414  
-		/* Add a trailing '>' to email, since map_user returns plain emails
1415  
-		   Note: It already has '<', since we replace from mail+1 */
1416  
-		mailpos = memchr(mail, '\0', mail_len);
1417  
-		if (mailpos && mailpos-mail < mail_len - 1) {
1418  
-			*mailpos = '>';
1419  
-			*(mailpos+1) = '\0';
1420  
-		}
1421  
-	}
  1386
+	map_user(&mailmap, &mailbuf, &maillen,
  1387
+		 &namebuf, &namelen);
  1388
+
  1389
+	strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf);
  1390
+	strbuf_add(name, namebuf, namelen);
  1391
+}
  1392
+
  1393
+static void commit_info_init(struct commit_info *ci)
  1394
+{
  1395
+
  1396
+	strbuf_init(&ci->author, 0);
  1397
+	strbuf_init(&ci->author_mail, 0);
  1398
+	strbuf_init(&ci->author_tz, 0);
  1399
+	strbuf_init(&ci->committer, 0);
  1400
+	strbuf_init(&ci->committer_mail, 0);
  1401
+	strbuf_init(&ci->committer_tz, 0);
  1402
+	strbuf_init(&ci->summary, 0);
  1403
+}
  1404
+
  1405
+static void commit_info_destroy(struct commit_info *ci)
  1406
+{
  1407
+
  1408
+	strbuf_release(&ci->author);
  1409
+	strbuf_release(&ci->author_mail);
  1410
+	strbuf_release(&ci->author_tz);
  1411
+	strbuf_release(&ci->committer);
  1412
+	strbuf_release(&ci->committer_mail);
  1413
+	strbuf_release(&ci->committer_tz);
  1414
+	strbuf_release(&ci->summary);
1422 1415
 }
1423 1416
 
1424 1417
 static void get_commit_info(struct commit *commit,
@@ -1428,11 +1421,8 @@ static void get_commit_info(struct commit *commit,
1428 1421
 	int len;
1429 1422
 	const char *subject, *encoding;
1430 1423
 	char *reencoded, *message;
1431  
-	static char author_name[1024];
1432  
-	static char author_mail[1024];
1433  
-	static char committer_name[1024];
1434  
-	static char committer_mail[1024];
1435  
-	static char summary_buf[1024];
  1424
+
  1425
+	commit_info_init(ret);
1436 1426
 
1437 1427
 	/*
1438 1428
 	 * We've operated without save_commit_buffer, so
@@ -1450,11 +1440,8 @@ static void get_commit_info(struct commit *commit,
1450 1440
 	encoding = get_log_output_encoding();
1451 1441
 	reencoded = logmsg_reencode(commit, encoding);
1452 1442
 	message   = reencoded ? reencoded : commit->buffer;
1453  
-	ret->author = author_name;
1454  
-	ret->author_mail = author_mail;
1455 1443
 	get_ac_line(message, "\nauthor ",
1456  
-		    sizeof(author_name), author_name,
1457  
-		    sizeof(author_mail), author_mail,
  1444
+		    &ret->author, &ret->author_mail,
1458 1445
 		    &ret->author_time, &ret->author_tz);
1459 1446
 
1460 1447
 	if (!detailed) {
@@ -1462,21 +1449,16 @@ static void get_commit_info(struct commit *commit,
1462 1449
 		return;
1463 1450
 	}
1464 1451
 
1465  
-	ret->committer = committer_name;
1466  
-	ret->committer_mail = committer_mail;
1467 1452
 	get_ac_line(message, "\ncommitter ",
1468  
-		    sizeof(committer_name), committer_name,
1469  
-		    sizeof(committer_mail), committer_mail,
  1453
+		    &ret->committer, &ret->committer_mail,
1470 1454
 		    &ret->committer_time, &ret->committer_tz);
1471 1455
 
1472  
-	ret->summary = summary_buf;
1473 1456
 	len = find_commit_subject(message, &subject);
1474  
-	if (len && len < sizeof(summary_buf)) {
1475  
-		memcpy(summary_buf, subject, len);
1476  
-		summary_buf[len] = 0;
1477  
-	} else {
1478  
-		sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
1479  
-	}
  1457
+	if (len)
  1458
+		strbuf_add(&ret->summary, subject, len);
  1459
+	else
  1460
+		strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
  1461
+
1480 1462
 	free(reencoded);
1481 1463
 }
1482 1464
 
@@ -1505,15 +1487,15 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
1505 1487
 
1506 1488
 	suspect->commit->object.flags |= METAINFO_SHOWN;
1507 1489
 	get_commit_info(suspect->commit, &ci, 1);
1508  
-	printf("author %s\n", ci.author);
1509  
-	printf("author-mail %s\n", ci.author_mail);
  1490
+	printf("author %s\n", ci.author.buf);
  1491
+	printf("author-mail %s\n", ci.author_mail.buf);
1510 1492
 	printf("author-time %lu\n", ci.author_time);
1511  
-	printf("author-tz %s\n", ci.author_tz);
1512  
-	printf("committer %s\n", ci.committer);
1513  
-	printf("committer-mail %s\n", ci.committer_mail);
  1493
+	printf("author-tz %s\n", ci.author_tz.buf);
  1494
+	printf("committer %s\n", ci.committer.buf);
  1495
+	printf("committer-mail %s\n", ci.committer_mail.buf);
1514 1496
 	printf("committer-time %lu\n", ci.committer_time);
1515  
-	printf("committer-tz %s\n", ci.committer_tz);
1516  
-	printf("summary %s\n", ci.summary);
  1497
+	printf("committer-tz %s\n", ci.committer_tz.buf);
  1498
+	printf("summary %s\n", ci.summary.buf);
1517 1499
 	if (suspect->commit->object.flags & UNINTERESTING)
1518 1500
 		printf("boundary\n");
1519 1501
 	if (suspect->previous) {
@@ -1521,6 +1503,9 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
1521 1503
 		printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
1522 1504
 		write_name_quoted(prev->path, stdout, '\n');
1523 1505
 	}
  1506
+
  1507
+	commit_info_destroy(&ci);
  1508
+
1524 1509
 	return 1;
1525 1510
 }
1526 1511
 
@@ -1707,11 +1692,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
1707 1692
 		if (opt & OUTPUT_ANNOTATE_COMPAT) {
1708 1693
 			const char *name;
1709 1694
 			if (opt & OUTPUT_SHOW_EMAIL)
1710  
-				name = ci.author_mail;
  1695
+				name = ci.author_mail.buf;
1711 1696
 			else
1712  
-				name = ci.author;
  1697
+				name = ci.author.buf;
1713 1698
 			printf("\t(%10s\t%10s\t%d)", name,
1714  
-			       format_time(ci.author_time, ci.author_tz,
  1699
+			       format_time(ci.author_time, ci.author_tz.buf,
1715 1700
 					   show_raw_time),
1716 1701
 			       ent->lno + 1 + cnt);
1717 1702
 		} else {
@@ -1730,14 +1715,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
1730 1715
 				const char *name;
1731 1716
 				int pad;
1732 1717
 				if (opt & OUTPUT_SHOW_EMAIL)
1733  
-					name = ci.author_mail;
  1718
+					name = ci.author_mail.buf;
1734 1719
 				else
1735  
-					name = ci.author;
  1720
+					name = ci.author.buf;
1736 1721
 				pad = longest_author - utf8_strwidth(name);
1737 1722
 				printf(" (%s%*s %10s",
1738 1723
 				       name, pad, "",
1739 1724
 				       format_time(ci.author_time,
1740  
-						   ci.author_tz,
  1725
+						   ci.author_tz.buf,
1741 1726
 						   show_raw_time));
1742 1727
 			}
1743 1728
 			printf(" %*d) ",
@@ -1752,6 +1737,8 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
1752 1737
 
1753 1738
 	if (sb->final_buf_size && cp[-1] != '\n')
1754 1739
 		putchar('\n');
  1740
+
  1741
+	commit_info_destroy(&ci);
1755 1742
 }
1756 1743
 
1757 1744
 static void output(struct scoreboard *sb, int option)
@@ -1876,9 +1863,9 @@ static void find_alignment(struct scoreboard *sb, int *option)
1876 1863
 			suspect->commit->object.flags |= METAINFO_SHOWN;
1877 1864
 			get_commit_info(suspect->commit, &ci, 1);
1878 1865
 			if (*option & OUTPUT_SHOW_EMAIL)
1879  
-				num = utf8_strwidth(ci.author_mail);
  1866
+				num = utf8_strwidth(ci.author_mail.buf);
1880 1867
 			else
1881  
-				num = utf8_strwidth(ci.author);
  1868
+				num = utf8_strwidth(ci.author.buf);
1882 1869
 			if (longest_author < num)
1883 1870
 				longest_author = num;
1884 1871
 		}
@@ -1890,6 +1877,8 @@ static void find_alignment(struct scoreboard *sb, int *option)
1890 1877
 			longest_dst_lines = num;
1891 1878
 		if (largest_score < ent_score(sb, e))
1892 1879
 			largest_score = ent_score(sb, e);
  1880
+
  1881
+		commit_info_destroy(&ci);
1893 1882
 	}
1894 1883
 	max_orig_digits = decimal_width(longest_src_lines);
1895 1884
 	max_digits = decimal_width(longest_dst_lines);
16  builtin/log.c
@@ -22,6 +22,7 @@
22 22
 #include "branch.h"
23 23
 #include "streaming.h"
24 24
 #include "version.h"
  25
+#include "mailmap.h"
25 26
 
26 27
 /* Set a default date-time format for git log ("log.date" config variable) */
27 28
 static const char *default_date_mode = NULL;
@@ -30,6 +31,7 @@ static int default_abbrev_commit;
30 31
 static int default_show_root = 1;
31 32
 static int decoration_style;
32 33
 static int decoration_given;
  34
+static int use_mailmap_config;
33 35
 static const char *fmt_patch_subject_prefix = "PATCH";
34 36
 static const char *fmt_pretty;
35 37
 
@@ -94,16 +96,18 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
94 96
 			 struct rev_info *rev, struct setup_revision_opt *opt)
95 97
 {
96 98
 	struct userformat_want w;
97  
-	int quiet = 0, source = 0;
  99
+	int quiet = 0, source = 0, mailmap = 0;
98 100
 
99 101
 	const struct option builtin_log_options[] = {
100 102
 		OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
101 103
 		OPT_BOOLEAN(0, "source", &source, N_("show source")),
  104
+		OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")),
102 105
 		{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
103 106
 		  PARSE_OPT_OPTARG, decorate_callback},
104 107
 		OPT_END()
105 108
 	};
106 109
 
  110
+	mailmap = use_mailmap_config;
107 111
 	argc = parse_options(argc, argv, prefix,
108 112
 			     builtin_log_options, builtin_log_usage,
109 113
 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
@@ -136,6 +140,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
136 140
 	if (source)
137 141
 		rev->show_source = 1;
138 142
 
  143
+	if (mailmap) {
  144
+		rev->mailmap = xcalloc(1, sizeof(struct string_list));
  145
+		read_mailmap(rev->mailmap, NULL);
  146
+	}
  147
+
139 148
 	if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
140 149
 		/*
141 150
 		 * "log --pretty=raw" is special; ignore UI oriented
@@ -351,6 +360,11 @@ static int git_log_config(const char *var, const char *value, void *cb)
351 360
 	}
352 361
 	if (!prefixcmp(var, "color.decorate."))
353 362
 		return parse_decorate_color_config(var, 15, value);
  363
+	if (!strcmp(var, "log.mailmap")) {
  364
+		use_mailmap_config = git_config_bool(var, value);
  365
+		return 0;
  366
+	}
  367
+
354 368
 	if (grep_config(var, value, cb) < 0)
355 369
 		return -1;
356 370
 	return git_diff_ui_config(var, value, cb);
54  builtin/shortlog.c
@@ -36,52 +36,28 @@ static void insert_one_record(struct shortlog *log,
36 36
 	const char *dot3 = log->common_repo_prefix;
37 37
 	char *buffer, *p;
38 38
 	struct string_list_item *item;
39  
-	char namebuf[1024];
40  
-	char emailbuf[1024];
41  
-	size_t len;
  39
+	const char *mailbuf, *namebuf;
  40
+	size_t namelen, maillen;
42 41
 	const char *eol;
43  
-	const char *boemail, *eoemail;
44 42
 	struct strbuf subject = STRBUF_INIT;
  43
+	struct strbuf namemailbuf = STRBUF_INIT;
  44
+	struct ident_split ident;
45 45
 
46  
-	boemail = strchr(author, '<');
47  
-	if (!boemail)
48  
-		return;
49  
-	eoemail = strchr(boemail, '>');
50  
-	if (!eoemail)
  46
+	if (split_ident_line(&ident, author, strlen(author)))
51 47
 		return;
52 48
 
53  
-	/* copy author name to namebuf, to support matching on both name and email */
54  
-	memcpy(namebuf, author, boemail - author);
55  
-	len = boemail - author;
56  
-	while (len > 0 && isspace(namebuf[len-1]))
57  
-		len--;
58  
-	namebuf[len] = 0;
59  
-
60  
-	/* copy email name to emailbuf, to allow email replacement as well */
61  
-	memcpy(emailbuf, boemail+1, eoemail - boemail);
62  
-	emailbuf[eoemail - boemail - 1] = 0;
63  
-
64  
-	if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
65  
-		while (author < boemail && isspace(*author))
66  
-			author++;
67  
-		for (len = 0;
68  
-		     len < sizeof(namebuf) - 1 && author + len < boemail;
69  
-		     len++)
70  
-			namebuf[len] = author[len];
71  
-		while (0 < len && isspace(namebuf[len-1]))
72  
-			len--;
73  
-		namebuf[len] = '\0';
74  
-	}
75  
-	else
76  
-		len = strlen(namebuf);
  49
+	namebuf = ident.name_begin;
  50
+	mailbuf = ident.mail_begin;
  51
+	namelen = ident.name_end - ident.name_begin;
  52
+	maillen = ident.mail_end - ident.mail_begin;
77 53
 
78  
-	if (log->email) {
79  
-		size_t room = sizeof(namebuf) - len - 1;
80  
-		int maillen = strlen(emailbuf);
81  
-		snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
82  
-	}
  54
+	map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
  55
+	strbuf_add(&namemailbuf, namebuf, namelen);
  56
+
  57
+	if (log->email)
  58
+		strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf);
83 59
 
84  
-	item = string_list_insert(&log->list, namebuf);
  60
+	item = string_list_insert(&log->list, namemailbuf.buf);
85 61
 	if (item->util == NULL)
86 62
 		item->util = xcalloc(1, sizeof(struct string_list));
87 63
 
1  commit.h
@@ -89,6 +89,7 @@ struct pretty_print_context {
89 89
 	char *notes_message;
90 90
 	struct reflog_walk_info *reflog_info;
91 91
 	const char *output_encoding;
  92
+	struct string_list *mailmap;
92 93
 	int color;
93 94
 };
94 95
 
1  log-tree.c
@@ -681,6 +681,7 @@ void show_log(struct rev_info *opt)
681 681
 	ctx.preserve_subject = opt->preserve_subject;
682 682
 	ctx.reflog_info = opt->reflog_info;
683 683
 	ctx.fmt = opt->commit_format;
  684
+	ctx.mailmap = opt->mailmap;
684 685
 	ctx.color = opt->diffopt.use_color;
685 686
 	pretty_print_commit(&ctx, commit, &msgbuf);
686 687
 
108  mailmap.c
@@ -235,6 +235,7 @@ int read_mailmap(struct string_list *map, char **repo_abbrev)
235 235
 	int err = 0;
236 236
 
237 237
 	map->strdup_strings = 1;
  238
+	map->cmp = strcasecmp;
238 239
 
239 240
 	if (!git_mailmap_blob && is_bare_repository())
240 241
 		git_mailmap_blob = "HEAD:.mailmap";
@@ -253,60 +254,95 @@ void clear_mailmap(struct string_list *map)
253 254
 	debug_mm("mailmap: cleared\n");
254 255
 }
255 256
 
  257
+/*
  258
+ * Look for an entry in map that match string[0:len]; string[len]
  259
+ * does not have to be NUL (but it could be).
  260
+ */
  261
+static struct string_list_item *lookup_prefix(struct string_list *map,
  262
+					      const char *string, size_t len)
  263
+{
  264
+	int i = string_list_find_insert_index(map, string, 1);
  265
+	if (i < 0) {
  266
+		/* exact match */
  267
+		i = -1 - i;
  268
+		if (!string[len])
  269
+			return &map->items[i];
  270
+		/*
  271
+		 * that map entry matches exactly to the string, including
  272
+		 * the cruft at the end beyond "len".  That is not a match
  273
+		 * with string[0:len] that we are looking for.
  274
+		 */
  275
+	} else if (!string[len]) {
  276
+		/*
  277
+		 * asked with the whole string, and got nothing.  No
  278
+		 * matching entry can exist in the map.
  279
+		 */
  280
+		return NULL;
  281
+	}
  282
+
  283
+	/*
  284
+	 * i is at the exact match to an overlong key, or location the
  285
+	 * overlong key would be inserted, which must come after the
  286
+	 * real location of the key if one exists.
  287
+	 */
  288
+	while (0 <= --i && i < map->nr) {
  289
+		int cmp = strncasecmp(map->items[i].string, string, len);
  290
+		if (cmp < 0)
  291
+			/*
  292
+			 * "i" points at a key definitely below the prefix;
  293
+			 * the map does not have string[0:len] in it.
  294
+			 */
  295
+			break;
  296
+		else if (!cmp && !map->items[i].string[len])
  297
+			/* found it */
  298
+			return &map->items[i];
  299
+		/*
  300
+		 * otherwise, the string at "i" may be string[0:len]
  301
+		 * followed by a string that sorts later than string[len:];
  302
+		 * keep trying.
  303
+		 */
  304
+	}
  305
+	return NULL;
  306
+}
  307
+
256 308
 int map_user(struct string_list *map,
257  
-	     char *email, int maxlen_email, char *name, int maxlen_name)
  309
+			 const char **email, size_t *emaillen,
  310
+			 const char **name, size_t *namelen)
258 311
 {
259  
-	char *end_of_email;
260 312
 	struct string_list_item *item;
261 313
 	struct mailmap_entry *me;
262  
-	char buf[1024], *mailbuf;
263  
-	int i;
264  
-
265  
-	/* figure out space requirement for email */
266  
-	end_of_email = strchr(email, '>');
267  
-	if (!end_of_email) {
268  
-		/* email passed in might not be wrapped in <>, but end with a \0 */
269  
-		end_of_email = memchr(email, '\0', maxlen_email);
270  
-		if (!end_of_email)
271  
-			return 0;
272  
-	}
273  
-	if (end_of_email - email + 1 < sizeof(buf))
274  
-		mailbuf = buf;
275  
-	else
276  
-		mailbuf = xmalloc(end_of_email - email + 1);
277  
-
278  
-	/* downcase the email address */
279  
-	for (i = 0; i < end_of_email - email; i++)
280  
-		mailbuf[i] = tolower(email[i]);
281  
-	mailbuf[i] = 0;
282  
-
283  
-	debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
284  
-	item = string_list_lookup(map, mailbuf);
  314
+
  315
+	debug_mm("map_user: map '%.*s' <%.*s>\n",
  316
+		 *name, *namelen, *emaillen, *email);
  317
+
  318
+	item = lookup_prefix(map, *email, *emaillen);
285 319
 	if (item != NULL) {
286 320
 		me = (struct mailmap_entry *)item->util;
287 321
 		if (me->namemap.nr) {
288 322
 			/* The item has multiple items, so we'll look up on name too */
289 323
 			/* If the name is not found, we choose the simple entry      */
290  
-			struct string_list_item *subitem = string_list_lookup(&me->namemap, name);
  324
+			struct string_list_item *subitem;
  325
+			subitem = lookup_prefix(&me->namemap, *name, *namelen);
291 326
 			if (subitem)
292 327
 				item = subitem;
293 328
 		}
294 329
 	}
295  
-	if (mailbuf != buf)
296  
-		free(mailbuf);
297 330
 	if (item != NULL) {
298 331
 		struct mailmap_info *mi = (struct mailmap_info *)item->util;
299  
-		if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {
  332
+		if (mi->name == NULL && mi->email == NULL) {
300 333
 			debug_mm("map_user:  -- (no simple mapping)\n");
301 334
 			return 0;
302 335
 		}
303  
-		if (maxlen_email && mi->email)
304  
-			strlcpy(email, mi->email, maxlen_email);
305  
-		else
306  
-			*end_of_email = '\0';
307  
-		if (maxlen_name && mi->name)
308  
-			strlcpy(name, mi->name, maxlen_name);
309  
-		debug_mm("map_user:  to '%s' <%s>\n", name, mi->email ? mi->email : "");
  336
+		if (mi->email) {
  337
+				*email = mi->email;
  338
+				*emaillen = strlen(*email);
  339
+		}
  340
+		if (mi->name) {
  341
+				*name = mi->name;
  342
+				*namelen = strlen(*name);
  343
+		}
  344
+		debug_mm("map_user:  to '%.*s' <.*%s>\n", *namelen, *name,
  345
+				 *emaillen, *email);
310 346
 		return 1;
311 347
 	}
312 348
 	debug_mm("map_user:  --\n");
4  mailmap.h
@@ -4,7 +4,7 @@
4 4
 int read_mailmap(struct string_list *map, char **repo_abbrev);
5 5
 void clear_mailmap(struct string_list *map);
6 6
 
7  
-int map_user(struct string_list *mailmap,
8  
-	     char *email, int maxlen_email, char *name, int maxlen_name);
  7
+int map_user(struct string_list *map,
  8
+			 const char **email, size_t *emaillen, const char **name, size_t *namelen);
9 9
 
10 10
 #endif
114  pretty.c
@@ -387,56 +387,79 @@ void pp_user_info(const struct pretty_print_context *pp,
387 387
 		  const char *what, struct strbuf *sb,
388 388
 		  const char *line, const char *encoding)
389 389
 {
  390
+	struct strbuf name;
  391
+	struct strbuf mail;
  392
+	struct ident_split ident;
  393
+	int linelen;
  394
+	char *line_end, *date;
  395
+	const char *mailbuf, *namebuf;
  396
+	size_t namelen, maillen;
390 397
 	int max_length = 78; /* per rfc2822 */
391  
-	char *date;
392  
-	int namelen;
393 398
 	unsigned long time;
394 399
 	int tz;
395 400
 
396 401
 	if (pp->fmt == CMIT_FMT_ONELINE)
397 402
 		return;
398  
-	date = strchr(line, '>');
399  
-	if (!date)
  403
+
  404
+	line_end = strchr(line, '\n');
  405
+	if (!line_end) {
  406
+		line_end = strchr(line, '\0');
  407
+		if (!line_end)
  408
+			return;
  409
+	}
  410
+
  411
+	linelen = ++line_end - line;
  412
+	if (split_ident_line(&ident, line, linelen))
400 413
 		return;
401  
-	namelen = ++date - line;
402  
-	time = strtoul(date, &date, 10);
  414
+
  415
+
  416
+	mailbuf = ident.mail_begin;
  417
+	maillen = ident.mail_end - ident.mail_begin;
  418
+	namebuf = ident.name_begin;
  419
+	namelen = ident.name_end - ident.name_begin;
  420
+
  421
+	if (pp->mailmap)
  422
+		map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
  423
+
  424
+	strbuf_init(&mail, 0);
  425
+	strbuf_init(&name, 0);
  426
+
  427
+	strbuf_add(&mail, mailbuf, maillen);
  428
+	strbuf_add(&name, namebuf, namelen);
  429
+
  430
+	namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */
  431
+	time = strtoul(ident.date_begin, &date, 10);
403 432
 	tz = strtol(date, NULL, 10);
404 433
 
405 434
 	if (pp->fmt == CMIT_FMT_EMAIL) {
406  
-		char *name_tail = strchr(line, '<');
407  
-		int display_name_length;
408  
-		if (!name_tail)
409  
-			return;
410  
-		while (line < name_tail && isspace(name_tail[-1]))
411  
-			name_tail--;
412  
-		display_name_length = name_tail - line;
413 435
 		strbuf_addstr(sb, "From: ");
414  
-		if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {
415  
-			add_rfc2047(sb, line, display_name_length,
416  
-						encoding, RFC2047_ADDRESS);
  436
+		if (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) {
  437
+			add_rfc2047(sb, name.buf, name.len,
  438
+				    encoding, RFC2047_ADDRESS);
417 439
 			max_length = 76; /* per rfc2047 */
418  
-		} else if (needs_rfc822_quoting(line, display_name_length)) {
  440
+		} else if (needs_rfc822_quoting(name.buf, name.len)) {
419 441
 			struct strbuf quoted = STRBUF_INIT;
420  
-			add_rfc822_quoted(&quoted, line, display_name_length);
  442
+			add_rfc822_quoted(&quoted, name.buf, name.len);
421 443
 			strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
422 444
 							-6, 1, max_length);
423 445
 			strbuf_release(&quoted);
424 446
 		} else {
425  
-			strbuf_add_wrapped_bytes(sb, line, display_name_length,
426  
-							-6, 1, max_length);
  447
+			strbuf_add_wrapped_bytes(sb, name.buf, name.len,
  448
+						 -6, 1, max_length);
427 449
 		}
428  
-		if (namelen - display_name_length + last_line_length(sb) > max_length) {
  450
+		if (namelen - name.len + last_line_length(sb) > max_length)
429 451
 			strbuf_addch(sb, '\n');
430  
-			if (!isspace(name_tail[0]))
431  
-				strbuf_addch(sb, ' ');
432  
-		}
433  
-		strbuf_add(sb, name_tail, namelen - display_name_length);
434  
-		strbuf_addch(sb, '\n');
  452
+
  453
+		strbuf_addf(sb, " <%s>\n", mail.buf);
435 454
 	} else {
436  
-		strbuf_addf(sb, "%s: %.*s%.*s\n", what,
  455
+		strbuf_addf(sb, "%s: %.*s%s <%s>\n", what,
437 456
 			      (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
438  
-			      "    ", namelen, line);
  457
+			      "    ", name.buf, mail.buf);
439 458
 	}
  459
+
  460
+	strbuf_release(&mail);
  461
+	strbuf_release(&name);
  462
+
440 463
 	switch (pp->fmt) {
441 464
 	case CMIT_FMT_MEDIUM:
442 465
 		strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
@@ -586,7 +609,8 @@ char *logmsg_reencode(const struct commit *commit,
586 609
 	return out;
587 610
 }
588 611
 
589  
-static int mailmap_name(char *email, int email_len, char *name, int name_len)
  612
+static int mailmap_name(const char **email, size_t *email_len,
  613
+			const char **name, size_t *name_len)
590 614
 {
591 615
 	static struct string_list *mail_map;
592 616
 	if (!mail_map) {
@@ -603,36 +627,26 @@ static size_t format_person_part(struct strbuf *sb, char part,
603 627
 	const int placeholder_len = 2;
604 628
 	int tz;
605 629
 	unsigned long date = 0;
606  
-	char person_name[1024];
607  
-	char person_mail[1024];
608 630
 	struct ident_split s;
609  
-	const char *name_start, *name_end, *mail_start, *mail_end;
  631
+	const char *name, *mail;
  632
+	size_t maillen, namelen;
610 633
 
611 634
 	if (split_ident_line(&s, msg, len) < 0)
612 635
 		goto skip;
613 636
 
614  
-	name_start = s.name_begin;
615  
-	name_end = s.name_end;
616  
-	mail_start = s.mail_begin;
617  
-	mail_end = s.mail_end;
618  
-
619  
-	if (part == 'N' || part == 'E') { /* mailmap lookup */
620  
-		snprintf(person_name, sizeof(person_name), "%.*s",
621  
-			 (int)(name_end - name_start), name_start);
622  
-		snprintf(person_mail, sizeof(person_mail), "%.*s",
623  
-			 (int)(mail_end - mail_start), mail_start);
624  
-		mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
625  
-		name_start = person_name;
626  
-		name_end = name_start + strlen(person_name);
627  
-		mail_start = person_mail;
628  
-		mail_end = mail_start +  strlen(person_mail);
629  
-	}
  637
+	name = s.name_begin;
  638
+	namelen = s.name_end - s.name_begin;
  639
+	mail = s.mail_begin;
  640
+	maillen = s.mail_end - s.mail_begin;
  641
+
  642
+	if (part == 'N' || part == 'E') /* mailmap lookup */
  643
+		mailmap_name(&mail, &maillen, &name, &namelen);
630 644
 	if (part == 'n' || part == 'N') {	/* name */
631  
-		strbuf_add(sb, name_start, name_end-name_start);
  645
+		strbuf_add(sb, name, namelen);
632 646
 		return placeholder_len;
633 647
 	}
634 648
 	if (part == 'e' || part == 'E') {	/* email */
635  
-		strbuf_add(sb, mail_start, mail_end-mail_start);
  649
+		strbuf_add(sb, mail, maillen);
636 650
 		return placeholder_len;
637 651
 	}
638 652
 
54  revision.c
@@ -13,6 +13,7 @@
13 13
 #include "decorate.h"
14 14
 #include "log-tree.h"
15 15
 #include "string-list.h"
  16
+#include "mailmap.h"
16 17
 
17 18
 volatile show_early_output_fn_t show_early_output;
18 19
 
@@ -2219,6 +2220,51 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
2219 2220
 	return 0;
2220 2221
 }
2221 2222
 
  2223
+static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap)
  2224
+{
  2225
+	char *person, *endp;
  2226
+	size_t len, namelen, maillen;
  2227
+	const char *name;
  2228
+	const char *mail;
  2229
+	struct ident_split ident;
  2230
+
  2231
+	person = strstr(buf->buf, what);
  2232
+	if (!person)
  2233
+		return 0;
  2234
+
  2235
+	person += strlen(what);
  2236
+	endp = strchr(person, '\n');
  2237
+	if (!endp)
  2238
+		return 0;
  2239
+
  2240
+	len = endp - person;
  2241
+
  2242
+	if (split_ident_line(&ident, person, len))
  2243
+		return 0;
  2244
+
  2245
+	mail = ident.mail_begin;
  2246
+	maillen = ident.mail_end - ident.mail_begin;
  2247
+	name = ident.name_begin;
  2248
+	namelen = ident.name_end - ident.name_begin;
  2249
+
  2250
+	if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
  2251
+		struct strbuf namemail = STRBUF_INIT;
  2252
+
  2253
+		strbuf_addf(&namemail, "%.*s <%.*s>",
  2254
+			    (int)namelen, name, (int)maillen, mail);
  2255
+
  2256
+		strbuf_splice(buf, ident.name_begin - buf->buf,
  2257
+			      ident.mail_end - ident.name_begin + 1,
  2258
+			      namemail.buf, namemail.len);
  2259
+
  2260
+		strbuf_release(&namemail);
  2261
+
  2262
+		return 1;
  2263
+	}
  2264
+
  2265
+	return 0;
  2266
+}
  2267
+
2222 2268
 static int commit_match(struct commit *commit, struct rev_info *opt)
2223 2269
 {
2224 2270
 	int retval;
@@ -2237,6 +2283,14 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
2237 2283
 	if (buf.len)
2238 2284
 		strbuf_addstr(&buf, commit->buffer);
2239 2285
 
  2286
+	if (opt->grep_filter.header_list && opt->mailmap) {
  2287
+		if (!buf.len)
  2288
+			strbuf_addstr(&buf, commit->buffer);
  2289
+
  2290
+		commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
  2291
+		commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
  2292
+	}
  2293
+
2240 2294
 	/* Append "fake" message parts as needed */
2241 2295
 	if (opt->show_notes) {
2242 2296
 		if (!buf.len)
1  revision.h
@@ -144,6 +144,7 @@ struct rev_info {
144 144
 	const char	*subject_prefix;
145 145
 	int		no_inline;
146 146
 	int		show_log_size;
  147
+	struct string_list *mailmap;
147 148
 
148 149
 	/* Filter by commit log message */
149 150
 	struct grep_opt	grep_filter;
17  string-list.c
@@ -7,10 +7,11 @@ static int get_entry_index(const struct string_list *list, const char *string,
7 7
 		int *exact_match)
8 8
 {
9 9
 	int left = -1, right = list->nr;
  10
+	compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
10 11
 
11 12
 	while (left + 1 < right) {
12 13
 		int middle = (left + right) / 2;
13  
-		int compare = strcmp(string, list->items[middle].string);
  14
+		int compare = cmp(string, list->items[middle].string);
14 15
 		if (compare < 0)
15 16
 			right = middle;
16 17
 		else if (compare > 0)
@@ -96,8 +97,9 @@ void string_list_remove_duplicates(struct string_list *list, int free_util)
96 97
 {
97 98
 	if (list->nr > 1) {
98 99
 		int src, dst;
  100
+		compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
99 101
 		for (src = dst = 1; src < list->nr; src++) {
100  
-			if (!strcmp(list->items[dst - 1].string, list->items[src].string)) {
  102
+			if (!cmp(list->items[dst - 1].string, list->items[src].string)) {
101 103
 				if (list->strdup_strings)
102 104
 					free(list->items[src].string);
103 105
 				if (free_util)
@@ -210,15 +212,20 @@ struct string_list_item *string_list_append(struct string_list *list,
210 212
 			list->strdup_strings ? xstrdup(string) : (char *)string);
211 213
 }
212 214
 
  215
+/* Yuck */
  216
+static compare_strings_fn compare_for_qsort;
  217
+
  218
+/* Only call this from inside sort_string_list! */
213 219
 static int cmp_items(const void *a, const void *b)
214 220
 {
215 221
 	const struct string_list_item *one = a;
216 222
 	const struct string_list_item *two = b;
217  
-	return strcmp(one->string, two->string);
  223
+	return compare_for_qsort(one->string, two->string);
218 224
 }
219 225
 
220 226
 void sort_string_list(struct string_list *list)
221 227
 {
  228
+	compare_for_qsort = list->cmp ? list->cmp : strcmp;
222 229
 	qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
223 230
 }
224 231
 
@@ -226,8 +233,10 @@ struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
226 233
 						     const char *string)
227 234
 {
228 235
 	int i;
  236
+	compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
  237
+
229 238
 	for (i = 0; i < list->nr; i++)
230  
-		if (!strcmp(string, list->items[i].string))
  239
+		if (!cmp(string, list->items[i].string))
231 240
 			return list->items + i;
232 241
 	return NULL;
233 242
 }
4  string-list.h
@@ -5,10 +5,14 @@ struct string_list_item {
5 5
 	char *string;
6 6
 	void *util;
7 7
 };
  8
+
  9
+typedef int (*compare_strings_fn)(const char *, const char *);
  10
+
8 11
 struct string_list {
9 12
 	struct string_list_item *items;
10 13
 	unsigned int nr, alloc;
11 14
 	unsigned int strdup_strings:1;
  15
+	compare_strings_fn cmp; /* NULL uses strcmp() */
12 16
 };
13 17
 
14 18
 #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
56  t/t4203-mailmap.sh
@@ -337,6 +337,62 @@ test_expect_success 'Log output (complex mapping)' '
337 337
 	test_cmp expect actual
338 338
 '
339 339
 
  340
+cat >expect <<\EOF
  341
+Author: CTO <cto@company.xx>
  342
+Author: Santa Claus <santa.claus@northpole.xx>
  343
+Author: Santa Claus <santa.claus@northpole.xx>
  344
+Author: Other Author <other@author.xx>
  345
+Author: Other Author <other@author.xx>
  346
+Author: Some Dude <some@dude.xx>
  347
+Author: A U Thor <author@example.com>
  348
+EOF
  349
+
  350
+test_expect_success 'Log output with --use-mailmap' '
  351
+	git log --use-mailmap | grep Author >actual &&
  352
+	test_cmp expect actual
  353
+'
  354
+
  355
+cat >expect <<\EOF
  356
+Author: CTO <cto@company.xx>
  357
+Author: Santa Claus <santa.claus@northpole.xx>
  358
+Author: Santa Claus <santa.claus@northpole.xx>
  359
+Author: Other Author <other@author.xx>
  360
+Author: Other Author <other@author.xx>
  361
+Author: Some Dude <some@dude.xx>
  362
+Author: A U Thor <author@example.com>
  363
+EOF
  364
+
  365
+test_expect_success 'Log output with log.mailmap' '
  366
+	git -c log.mailmap=True log | grep Author >actual &&
  367
+	test_cmp expect actual
  368
+'
  369
+
  370
+cat >expect <<\EOF
  371
+Author: Santa Claus <santa.claus@northpole.xx>
  372
+Author: Santa Claus <santa.claus@northpole.xx>
  373
+EOF
  374
+
  375
+test_expect_success 'Grep author with --use-mailmap' '
  376
+	git log --use-mailmap --author Santa | grep Author >actual &&
  377
+	test_cmp expect actual
  378
+'
  379
+cat >expect <<\EOF
  380
+Author: Santa Claus <santa.claus@northpole.xx>
  381
+Author: Santa Claus <santa.claus@northpole.xx>
  382
+EOF
  383
+
  384
+test_expect_success 'Grep author with log.mailmap' '
  385
+	git -c log.mailmap=True log --author Santa | grep Author >actual &&
  386
+	test_cmp expect actual
  387
+'
  388
+
  389
+>expect
  390
+
  391
+test_expect_success 'Only grep replaced author with --use-mailmap' '
  392
+	git log --use-mailmap --author "<cto@coompany.xx>" >actual &&
  393
+	test_cmp expect actual
  394
+'
  395
+
340 396
 # git blame
341 397
 cat >expect <<\EOF
342 398
 ^OBJI (A U Thor     DATE 1) one

0 notes on commit 577f63e

Please sign in to comment.
Something went wrong with that request. Please try again.