From: Junio C Hamano Date: Mon, 21 Jan 2013 01:06:52 +0000 (-0800) Subject: Merge branch 'ap/log-mailmap' X-Git-Tag: v1.8.2-rc0~112 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/577f63e781d2f9b9a6862389b6e9d2ca2467afa2?ds=inline;hp=-c 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 --- 577f63e781d2f9b9a6862389b6e9d2ca2467afa2 diff --combined Documentation/config.txt index ef45c99e53,226362a0ea..5ecb0fccb1 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -140,11 -140,10 +140,11 @@@ advice.*: can tell Git that you do not need help by setting these to 'false': + -- - pushNonFastForward:: + pushUpdateRejected:: Set this variable to 'false' if you want to disable - 'pushNonFFCurrent', 'pushNonFFDefault', and - 'pushNonFFMatching' simultaneously. + 'pushNonFFCurrent', 'pushNonFFDefault', + 'pushNonFFMatching', and 'pushAlreadyExists' + simultaneously. pushNonFFCurrent:: Advice shown when linkgit:git-push[1] fails due to a non-fast-forward update to the current branch. @@@ -159,9 -158,6 +159,9 @@@ 'matching refs' explicitly (i.e. you used ':', or specified a refspec that isn't your current branch) and it resulted in a non-fast-forward error. + pushAlreadyExists:: + Shown when linkgit:git-push[1] rejects an update that + does not qualify for fast-forwarding (e.g., a tag.) statusHints:: Show directions on how to proceed from the current state in the output of linkgit:git-status[1], in @@@ -739,12 -735,6 +739,12 @@@ branch..rebase: it unless you understand the implications (see linkgit:git-rebase[1] for details). +branch..description:: + Branch description, can be edited with + `git branch --edit-description`. Branch description is + automatically added in the format-patch cover letter or + request-pull summary. + browser..cmd:: Specify the command to invoke the specified browser. The specified command is evaluated in shell with the URLs passed @@@ -1361,12 -1351,6 +1361,12 @@@ help.autocorrect: value is 0 - the command will be just shown but not executed. This is the default. +help.htmlpath:: + Specify the path where the HTML documentation resides. File system paths + and URLs are supported. HTML pages will be prefixed with this path when + help is displayed in the 'web' format. This defaults to the documentation + path of your Git installation. + http.proxy:: Override the HTTP proxy, normally configured using the 'http_proxy', 'https_proxy', and 'all_proxy' environment variables (see @@@ -1525,6 -1509,10 +1525,10 @@@ log.showroot: Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which normally hide the root commit will now show it. True by default. + log.mailmap:: + If true, makes linkgit:git-log[1], linkgit:git-show[1], and + linkgit:git-whatchanged[1] assume `--use-mailmap`. + mailmap.file:: The location of an augmenting mailmap file. The default mailmap, located in the root of the repository, is loaded @@@ -1533,14 -1521,6 +1537,14 @@@ subdirectory, or somewhere outside of the repository itself. See linkgit:git-shortlog[1] and linkgit:git-blame[1]. +mailmap.blob:: + Like `mailmap.file`, but consider the value as a reference to a + blob in the repository. If both `mailmap.file` and + `mailmap.blob` are given, both are parsed, with entries from + `mailmap.file` taking precedence. In a bare repository, this + defaults to `HEAD:.mailmap`. In a non-bare repository, it + defaults to empty. + man.viewer:: Specify the programs that may be used to display help in the 'man' format. See linkgit:git-help[1]. @@@ -2019,12 -1999,6 +2023,12 @@@ submodule..update: URL and other values found in the `.gitmodules` file. See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details. +submodule..branch:: + The remote branch name for a submodule, used by `git submodule + update --remote`. Set this option to override the value found in + the `.gitmodules` file. See linkgit:git-submodule[1] and + linkgit:gitmodules[5] for details. + submodule..fetchRecurseSubmodules:: This option can be used to control recursive fetching of this submodule. It can be overridden by using the --[no-]recurse-submodules diff --combined Documentation/git-log.txt index 08a185db7f,a99be97335..22c0d6e4b1 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@@ -47,6 -47,11 +47,11 @@@ OPTION Print out the ref name given on the command line by which each commit was reached. + --use-mailmap:: + Use mailmap file to map author and committer names and email + to canonical real names and email addresses. See + linkgit:git-shortlog[1]. + --full-diff:: Without this flag, "git log -p ..." shows commits that touch the specified paths, and diffs about the same specified @@@ -167,7 -172,7 +172,7 @@@ log.showroot: `git log -p` output would be shown without a diff attached. The default is `true`. -mailmap.file:: +mailmap.*:: See linkgit:git-shortlog[1]. notes.displayRef:: diff --combined builtin/blame.c index bc6c899d30,9d20e25552..b431ba3209 --- a/builtin/blame.c +++ b/builtin/blame.c @@@ -42,7 -42,6 +42,7 @@@ static int blank_boundary static int incremental; static int xdl_opts; static int abbrev = -1; +static int no_whole_file_rename; static enum date_mode blame_date_mode = DATE_ISO8601; static size_t blame_date_width; @@@ -1227,7 -1226,7 +1227,7 @@@ static void pass_blame(struct scoreboar * The first pass looks for unrenamed path to optimize for * common cases, then we look for renames in the second pass. */ - for (pass = 0; pass < 2; pass++) { + for (pass = 0; pass < 2 - no_whole_file_rename; pass++) { struct origin *(*find)(struct scoreboard *, struct commit *, struct origin *); find = pass ? find_rename : find_origin; @@@ -1322,30 -1321,31 +1322,31 @@@ * Information on commits, used for output. */ struct commit_info { - const char *author; - const char *author_mail; + struct strbuf author; + struct strbuf author_mail; unsigned long author_time; - const char *author_tz; + struct strbuf author_tz; /* filled only when asked for details */ - const char *committer; - const char *committer_mail; + struct strbuf committer; + struct strbuf committer_mail; unsigned long committer_time; - const char *committer_tz; + struct strbuf committer_tz; - const char *summary; + struct strbuf summary; }; /* * Parse author/committer line in the commit object buffer */ static void get_ac_line(const char *inbuf, const char *what, - int person_len, char *person, - int mail_len, char *mail, - unsigned long *time, const char **tz) + struct strbuf *name, struct strbuf *mail, + unsigned long *time, struct strbuf *tz) { - int len, tzlen, maillen; - char *tmp, *endp, *timepos, *mailpos; + struct ident_split ident; + size_t len, maillen, namelen; + char *tmp, *endp; + const char *namebuf, *mailbuf; tmp = strstr(inbuf, what); if (!tmp) @@@ -1356,69 -1356,61 +1357,61 @@@ len = strlen(tmp); else len = endp - tmp; - if (person_len <= len) { + + if (split_ident_line(&ident, tmp, len)) { error_out: /* Ugh */ - *tz = "(unknown)"; - strcpy(person, *tz); - strcpy(mail, *tz); + tmp = "(unknown)"; + strbuf_addstr(name, tmp); + strbuf_addstr(mail, tmp); + strbuf_addstr(tz, tmp); *time = 0; return; } - memcpy(person, tmp, len); - - tmp = person; - tmp += len; - *tmp = 0; - while (person < tmp && *tmp != ' ') - tmp--; - if (tmp <= person) - goto error_out; - *tz = tmp+1; - tzlen = (person+len)-(tmp+1); - *tmp = 0; - while (person < tmp && *tmp != ' ') - tmp--; - if (tmp <= person) - goto error_out; - *time = strtoul(tmp, NULL, 10); - timepos = tmp; + namelen = ident.name_end - ident.name_begin; + namebuf = ident.name_begin; - *tmp = 0; - while (person < tmp && !(*tmp == ' ' && tmp[1] == '<')) - tmp--; - if (tmp <= person) - return; - mailpos = tmp + 1; - *tmp = 0; - maillen = timepos - tmp; - memcpy(mail, mailpos, maillen); + maillen = ident.mail_end - ident.mail_begin; + mailbuf = ident.mail_begin; - if (!mailmap.nr) - return; + *time = strtoul(ident.date_begin, NULL, 10); - /* - * mailmap expansion may make the name longer. - * make room by pushing stuff down. - */ - tmp = person + person_len - (tzlen + 1); - memmove(tmp, *tz, tzlen); - tmp[tzlen] = 0; - *tz = tmp; + len = ident.tz_end - ident.tz_begin; + strbuf_add(tz, ident.tz_begin, len); /* * Now, convert both name and e-mail using mailmap */ - if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) { - /* Add a trailing '>' to email, since map_user returns plain emails - Note: It already has '<', since we replace from mail+1 */ - mailpos = memchr(mail, '\0', mail_len); - if (mailpos && mailpos-mail < mail_len - 1) { - *mailpos = '>'; - *(mailpos+1) = '\0'; - } - } + map_user(&mailmap, &mailbuf, &maillen, + &namebuf, &namelen); + + strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf); + strbuf_add(name, namebuf, namelen); + } + + static void commit_info_init(struct commit_info *ci) + { + + strbuf_init(&ci->author, 0); + strbuf_init(&ci->author_mail, 0); + strbuf_init(&ci->author_tz, 0); + strbuf_init(&ci->committer, 0); + strbuf_init(&ci->committer_mail, 0); + strbuf_init(&ci->committer_tz, 0); + strbuf_init(&ci->summary, 0); + } + + static void commit_info_destroy(struct commit_info *ci) + { + + strbuf_release(&ci->author); + strbuf_release(&ci->author_mail); + strbuf_release(&ci->author_tz); + strbuf_release(&ci->committer); + strbuf_release(&ci->committer_mail); + strbuf_release(&ci->committer_tz); + strbuf_release(&ci->summary); } static void get_commit_info(struct commit *commit, @@@ -1428,11 -1420,8 +1421,8 @@@ int len; const char *subject, *encoding; char *reencoded, *message; - static char author_name[1024]; - static char author_mail[1024]; - static char committer_name[1024]; - static char committer_mail[1024]; - static char summary_buf[1024]; + + commit_info_init(ret); /* * We've operated without save_commit_buffer, so @@@ -1450,11 -1439,8 +1440,8 @@@ encoding = get_log_output_encoding(); reencoded = logmsg_reencode(commit, encoding); message = reencoded ? reencoded : commit->buffer; - ret->author = author_name; - ret->author_mail = author_mail; get_ac_line(message, "\nauthor ", - sizeof(author_name), author_name, - sizeof(author_mail), author_mail, + &ret->author, &ret->author_mail, &ret->author_time, &ret->author_tz); if (!detailed) { @@@ -1462,21 -1448,16 +1449,16 @@@ return; } - ret->committer = committer_name; - ret->committer_mail = committer_mail; get_ac_line(message, "\ncommitter ", - sizeof(committer_name), committer_name, - sizeof(committer_mail), committer_mail, + &ret->committer, &ret->committer_mail, &ret->committer_time, &ret->committer_tz); - ret->summary = summary_buf; len = find_commit_subject(message, &subject); - if (len && len < sizeof(summary_buf)) { - memcpy(summary_buf, subject, len); - summary_buf[len] = 0; - } else { - sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1)); - } + if (len) + strbuf_add(&ret->summary, subject, len); + else + strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1)); + free(reencoded); } @@@ -1505,15 -1486,15 +1487,15 @@@ static int emit_one_suspect_detail(stru suspect->commit->object.flags |= METAINFO_SHOWN; get_commit_info(suspect->commit, &ci, 1); - printf("author %s\n", ci.author); - printf("author-mail %s\n", ci.author_mail); + printf("author %s\n", ci.author.buf); + printf("author-mail %s\n", ci.author_mail.buf); printf("author-time %lu\n", ci.author_time); - printf("author-tz %s\n", ci.author_tz); - printf("committer %s\n", ci.committer); - printf("committer-mail %s\n", ci.committer_mail); + printf("author-tz %s\n", ci.author_tz.buf); + printf("committer %s\n", ci.committer.buf); + printf("committer-mail %s\n", ci.committer_mail.buf); printf("committer-time %lu\n", ci.committer_time); - printf("committer-tz %s\n", ci.committer_tz); - printf("summary %s\n", ci.summary); + printf("committer-tz %s\n", ci.committer_tz.buf); + printf("summary %s\n", ci.summary.buf); if (suspect->commit->object.flags & UNINTERESTING) printf("boundary\n"); if (suspect->previous) { @@@ -1521,6 -1502,9 +1503,9 @@@ printf("previous %s ", sha1_to_hex(prev->commit->object.sha1)); write_name_quoted(prev->path, stdout, '\n'); } + + commit_info_destroy(&ci); + return 1; } @@@ -1707,11 -1691,11 +1692,11 @@@ static void emit_other(struct scoreboar if (opt & OUTPUT_ANNOTATE_COMPAT) { const char *name; if (opt & OUTPUT_SHOW_EMAIL) - name = ci.author_mail; + name = ci.author_mail.buf; else - name = ci.author; + name = ci.author.buf; printf("\t(%10s\t%10s\t%d)", name, - format_time(ci.author_time, ci.author_tz, + format_time(ci.author_time, ci.author_tz.buf, show_raw_time), ent->lno + 1 + cnt); } else { @@@ -1730,14 -1714,14 +1715,14 @@@ const char *name; int pad; if (opt & OUTPUT_SHOW_EMAIL) - name = ci.author_mail; + name = ci.author_mail.buf; else - name = ci.author; + name = ci.author.buf; pad = longest_author - utf8_strwidth(name); printf(" (%s%*s %10s", name, pad, "", format_time(ci.author_time, - ci.author_tz, + ci.author_tz.buf, show_raw_time)); } printf(" %*d) ", @@@ -1752,6 -1736,8 +1737,8 @@@ if (sb->final_buf_size && cp[-1] != '\n') putchar('\n'); + + commit_info_destroy(&ci); } static void output(struct scoreboard *sb, int option) @@@ -1876,9 -1862,9 +1863,9 @@@ static void find_alignment(struct score suspect->commit->object.flags |= METAINFO_SHOWN; get_commit_info(suspect->commit, &ci, 1); if (*option & OUTPUT_SHOW_EMAIL) - num = utf8_strwidth(ci.author_mail); + num = utf8_strwidth(ci.author_mail.buf); else - num = utf8_strwidth(ci.author); + num = utf8_strwidth(ci.author.buf); if (longest_author < num) longest_author = num; } @@@ -1890,6 -1876,8 +1877,8 @@@ longest_dst_lines = num; if (largest_score < ent_score(sb, e)) largest_score = ent_score(sb, e); + + commit_info_destroy(&ci); } max_orig_digits = decimal_width(longest_src_lines); max_digits = decimal_width(longest_dst_lines); @@@ -2404,7 -2392,6 +2393,7 @@@ int cmd_blame(int argc, const char **ar init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV); + DIFF_OPT_SET(&revs.diffopt, FOLLOW_RENAMES); save_commit_buffer = 0; dashdash_pos = 0; @@@ -2428,8 -2415,6 +2417,8 @@@ parse_revision_opt(&revs, &ctx, options, blame_opt_usage); } parse_done: + no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES); + DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES); argc = parse_options_end(&ctx); if (0 < abbrev) diff --combined builtin/log.c index 5a4055eb0b,16e6520a5d..8f0b2e84fe --- a/builtin/log.c +++ b/builtin/log.c @@@ -22,6 -22,7 +22,7 @@@ #include "branch.h" #include "streaming.h" #include "version.h" + #include "mailmap.h" /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@@ -30,6 -31,7 +31,7 @@@ static int default_abbrev_commit static int default_show_root = 1; static int decoration_style; static int decoration_given; + static int use_mailmap_config; static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; @@@ -94,16 -96,18 +96,18 @@@ static void cmd_log_init_finish(int arg struct rev_info *rev, struct setup_revision_opt *opt) { struct userformat_want w; - int quiet = 0, source = 0; + int quiet = 0, source = 0, mailmap = 0; const struct option builtin_log_options[] = { OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")), OPT_BOOLEAN(0, "source", &source, N_("show source")), + OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")), { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"), PARSE_OPT_OPTARG, decorate_callback}, OPT_END() }; + mailmap = use_mailmap_config; argc = parse_options(argc, argv, prefix, builtin_log_options, builtin_log_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | @@@ -136,6 -140,11 +140,11 @@@ if (source) rev->show_source = 1; + if (mailmap) { + rev->mailmap = xcalloc(1, sizeof(struct string_list)); + read_mailmap(rev->mailmap, NULL); + } + if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) { /* * "log --pretty=raw" is special; ignore UI oriented @@@ -351,6 -360,11 +360,11 @@@ static int git_log_config(const char *v } if (!prefixcmp(var, "color.decorate.")) return parse_decorate_color_config(var, 15, value); + if (!strcmp(var, "log.mailmap")) { + use_mailmap_config = git_config_bool(var, value); + return 0; + } + if (grep_config(var, value, cb) < 0) return -1; return git_diff_ui_config(var, value, cb); @@@ -678,7 -692,7 +692,7 @@@ static int reopen_stdout(struct commit struct rev_info *rev, int quiet) { struct strbuf filename = STRBUF_INIT; - int suffix_len = strlen(fmt_patch_suffix) + 1; + int suffix_len = strlen(rev->patch_suffix) + 1; if (output_directory) { strbuf_addstr(&filename, output_directory); @@@ -689,12 -703,7 +703,12 @@@ strbuf_addch(&filename, '/'); } - get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename); + if (rev->numbered_files) + strbuf_addf(&filename, "%d", rev->nr); + else if (commit) + fmt_output_commit(&filename, commit, rev); + else + fmt_output_subject(&filename, subject, rev); if (!quiet) fprintf(realstdout, "%s\n", filename.buf + outdir_offset); @@@ -778,6 -787,7 +792,6 @@@ static void add_branch_description(stru } static void make_cover_letter(struct rev_info *rev, int use_stdout, - int numbered, int numbered_files, struct commit *origin, int nr, struct commit **list, struct commit *head, const char *branch_name, @@@ -800,7 -810,7 +814,7 @@@ committer = git_committer_info(0); if (!use_stdout && - reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet)) + reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet)) return; log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, @@@ -1020,9 -1030,8 +1034,9 @@@ static char *find_branch_name(struct re { int i, positive = -1; unsigned char branch_sha1[20]; - struct strbuf buf = STRBUF_INIT; - const char *branch; + const unsigned char *tip_sha1; + const char *ref; + char *full_ref, *branch = NULL; for (i = 0; i < rev->cmdline.nr; i++) { if (rev->cmdline.rev[i].flags & UNINTERESTING) @@@ -1032,27 -1041,18 +1046,27 @@@ else return NULL; } - if (positive < 0) + if (0 <= positive) { + ref = rev->cmdline.rev[positive].name; + tip_sha1 = rev->cmdline.rev[positive].item->sha1; + } else if (!rev->cmdline.nr && rev->pending.nr == 1 && + !strcmp(rev->pending.objects[0].name, "HEAD")) { + /* + * No actual ref from command line, but "HEAD" from + * rev->def was added in setup_revisions() + * e.g. format-patch --cover-letter -12 + */ + ref = "HEAD"; + tip_sha1 = rev->pending.objects[0].item->sha1; + } else { return NULL; - strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name); - branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL); - if (!branch || - prefixcmp(branch, "refs/heads/") || - hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1)) - branch = NULL; - strbuf_release(&buf); - if (branch) - return xstrdup(rev->cmdline.rev[positive].name); - return NULL; + } + if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) && + !prefixcmp(full_ref, "refs/heads/") && + !hashcmp(tip_sha1, branch_sha1)) + branch = xstrdup(full_ref + strlen("refs/heads/")); + free(full_ref); + return branch; } int cmd_format_patch(int argc, const char **argv, const char *prefix) @@@ -1064,7 -1064,7 +1078,7 @@@ int nr = 0, total, i; int use_stdout = 0; int start_number = -1; - int numbered_files = 0; /* _just_ numbers */ + int just_numbers = 0; int ignore_if_in_upstream = 0; int cover_letter = 0; int boundary_count = 0; @@@ -1076,7 -1076,6 +1090,7 @@@ struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; int quiet = 0; + int reroll_count = -1; char *branch_name = NULL; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, @@@ -1090,14 -1089,12 +1104,14 @@@ N_("print patches to standard out")), OPT_BOOLEAN(0, "cover-letter", &cover_letter, N_("generate a cover letter")), - OPT_BOOLEAN(0, "numbered-files", &numbered_files, + OPT_BOOLEAN(0, "numbered-files", &just_numbers, N_("use simple number sequence for output file names")), OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), N_("use instead of '.patch'")), OPT_INTEGER(0, "start-number", &start_number, N_("start numbering patches at instead of 1")), + OPT_INTEGER('v', "reroll-count", &reroll_count, + N_("mark the series as Nth re-roll")), { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"), N_("Use [] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback }, @@@ -1171,14 -1168,6 +1185,14 @@@ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); + if (0 < reroll_count) { + struct strbuf sprefix = STRBUF_INIT; + strbuf_addf(&sprefix, "%s v%d", + rev.subject_prefix, reroll_count); + rev.reroll_count = reroll_count; + rev.subject_prefix = strbuf_detach(&sprefix, NULL); + } + if (do_signoff) { const char *committer; const char *endpos; @@@ -1369,12 -1358,12 +1383,12 @@@ const char *msgid = clean_message_id(in_reply_to); string_list_append(rev.ref_message_ids, msgid); } - rev.numbered_files = numbered_files; + rev.numbered_files = just_numbers; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); - make_cover_letter(&rev, use_stdout, numbered, numbered_files, + make_cover_letter(&rev, use_stdout, origin, nr, list, head, branch_name, quiet); total++; start_number--; @@@ -1421,7 -1410,7 +1435,7 @@@ } if (!use_stdout && - reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet)) + reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet)) die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free(commit->buffer); diff --combined builtin/shortlog.c index 83605143ac,1eeed0ffd8..240bff3efa --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@@ -36,52 -36,28 +36,28 @@@ static void insert_one_record(struct sh const char *dot3 = log->common_repo_prefix; char *buffer, *p; struct string_list_item *item; - char namebuf[1024]; - char emailbuf[1024]; - size_t len; + const char *mailbuf, *namebuf; + size_t namelen, maillen; const char *eol; - const char *boemail, *eoemail; struct strbuf subject = STRBUF_INIT; + struct strbuf namemailbuf = STRBUF_INIT; + struct ident_split ident; - boemail = strchr(author, '<'); - if (!boemail) - return; - eoemail = strchr(boemail, '>'); - if (!eoemail) + if (split_ident_line(&ident, author, strlen(author))) return; - /* copy author name to namebuf, to support matching on both name and email */ - memcpy(namebuf, author, boemail - author); - len = boemail - author; - while (len > 0 && isspace(namebuf[len-1])) - len--; - namebuf[len] = 0; + namebuf = ident.name_begin; + mailbuf = ident.mail_begin; + namelen = ident.name_end - ident.name_begin; + maillen = ident.mail_end - ident.mail_begin; - /* copy email name to emailbuf, to allow email replacement as well */ - memcpy(emailbuf, boemail+1, eoemail - boemail); - emailbuf[eoemail - boemail - 1] = 0; + map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen); + strbuf_add(&namemailbuf, namebuf, namelen); - if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) { - while (author < boemail && isspace(*author)) - author++; - for (len = 0; - len < sizeof(namebuf) - 1 && author + len < boemail; - len++) - namebuf[len] = author[len]; - while (0 < len && isspace(namebuf[len-1])) - len--; - namebuf[len] = '\0'; - } - else - len = strlen(namebuf); - - if (log->email) { - size_t room = sizeof(namebuf) - len - 1; - int maillen = strlen(emailbuf); - snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf); - } + if (log->email) + strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf); - item = string_list_insert(&log->list, namebuf); + item = string_list_insert(&log->list, namemailbuf.buf); if (item->util == NULL) item->util = xcalloc(1, sizeof(struct string_list)); @@@ -306,8 -282,9 +282,8 @@@ parse_done static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s, const struct shortlog *log) { - int col = strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap); - if (col != log->wrap) - strbuf_addch(sb, '\n'); + strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap); + strbuf_addch(sb, '\n'); } void shortlog_output(struct shortlog *log) diff --combined commit.h index 0f469e507d,7f8f9878d8..c16c8a7534 --- a/commit.h +++ b/commit.h @@@ -89,7 -89,7 +89,8 @@@ struct pretty_print_context char *notes_message; struct reflog_walk_info *reflog_info; const char *output_encoding; + struct string_list *mailmap; + int color; }; struct userformat_want { diff --combined log-tree.c index f8487f8a8a,92254fd493..5dc45c4812 --- a/log-tree.c +++ b/log-tree.c @@@ -299,34 -299,26 +299,34 @@@ static unsigned int digits_in_number(un return result; } -void get_patch_filename(struct commit *commit, const char *subject, int nr, - const char *suffix, struct strbuf *buf) +void fmt_output_subject(struct strbuf *filename, + const char *subject, + struct rev_info *info) { - int suffix_len = strlen(suffix) + 1; - int start_len = buf->len; - - strbuf_addf(buf, commit || subject ? "%04d-" : "%d", nr); - if (commit || subject) { - int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len; - struct pretty_print_context ctx = {0}; - - if (subject) - strbuf_addstr(buf, subject); - else if (commit) - format_commit_message(commit, "%f", buf, &ctx); - - if (max_len < buf->len) - strbuf_setlen(buf, max_len); - strbuf_addstr(buf, suffix); - } + const char *suffix = info->patch_suffix; + int nr = info->nr; + int start_len = filename->len; + int max_len = start_len + FORMAT_PATCH_NAME_MAX - (strlen(suffix) + 1); + + if (0 < info->reroll_count) + strbuf_addf(filename, "v%d-", info->reroll_count); + strbuf_addf(filename, "%04d-%s", nr, subject); + + if (max_len < filename->len) + strbuf_setlen(filename, max_len); + strbuf_addstr(filename, suffix); +} + +void fmt_output_commit(struct strbuf *filename, + struct commit *commit, + struct rev_info *info) +{ + struct pretty_print_context ctx = {0}; + struct strbuf subject = STRBUF_INIT; + + format_commit_message(commit, "%f", &subject, &ctx); + fmt_output_subject(filename, subject.buf, info); + strbuf_release(&subject); } void log_write_email_headers(struct rev_info *opt, struct commit *commit, @@@ -395,10 -387,8 +395,10 @@@ mime_boundary_leader, opt->mime_boundary); extra_headers = subject_buffer; - get_patch_filename(opt->numbered_files ? NULL : commit, NULL, - opt->nr, opt->patch_suffix, &filename); + if (opt->numbered_files) + strbuf_addf(&filename, "%d", opt->nr); + else + fmt_output_commit(&filename, commit, opt); snprintf(buffer, sizeof(buffer) - 1, "\n--%s%s\n" "Content-Type: text/x-patch;" @@@ -681,7 -671,7 +681,8 @@@ void show_log(struct rev_info *opt ctx.preserve_subject = opt->preserve_subject; ctx.reflog_info = opt->reflog_info; ctx.fmt = opt->commit_format; + ctx.mailmap = opt->mailmap; + ctx.color = opt->diffopt.use_color; pretty_print_commit(&ctx, commit, &msgbuf); if (opt->add_signoff) diff --combined mailmap.c index b16542febe,743dbdae2f..2a7b36628c --- a/mailmap.c +++ b/mailmap.c @@@ -10,7 -10,6 +10,7 @@@ static inline void debug_mm(const char #endif const char *git_mailmap_file; +const char *git_mailmap_blob; struct mailmap_info { char *name; @@@ -130,119 -129,55 +130,120 @@@ static char *parse_name_and_email(char return (*right == '\0' ? NULL : right); } -static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev) +static void read_mailmap_line(struct string_list *map, char *buffer, + char **repo_abbrev) { - char buffer[1024]; - FILE *f = (filename == NULL ? NULL : fopen(filename, "r")); - - if (f == NULL) - return 1; - while (fgets(buffer, sizeof(buffer), f) != NULL) { - char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL; - if (buffer[0] == '#') { - static const char abbrev[] = "# repo-abbrev:"; - int abblen = sizeof(abbrev) - 1; - int len = strlen(buffer); - - if (!repo_abbrev) - continue; - - if (len && buffer[len - 1] == '\n') - buffer[--len] = 0; - if (!strncmp(buffer, abbrev, abblen)) { - char *cp; - - if (repo_abbrev) - free(*repo_abbrev); - *repo_abbrev = xmalloc(len); - - for (cp = buffer + abblen; isspace(*cp); cp++) - ; /* nothing */ - strcpy(*repo_abbrev, cp); - } - continue; + char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL; + if (buffer[0] == '#') { + static const char abbrev[] = "# repo-abbrev:"; + int abblen = sizeof(abbrev) - 1; + int len = strlen(buffer); + + if (!repo_abbrev) + return; + + if (len && buffer[len - 1] == '\n') + buffer[--len] = 0; + if (!strncmp(buffer, abbrev, abblen)) { + char *cp; + + if (repo_abbrev) + free(*repo_abbrev); + *repo_abbrev = xmalloc(len); + + for (cp = buffer + abblen; isspace(*cp); cp++) + ; /* nothing */ + strcpy(*repo_abbrev, cp); } - if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL) - parse_name_and_email(name2, &name2, &email2, 1); + return; + } + if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL) + parse_name_and_email(name2, &name2, &email2, 1); + + if (email1) + add_mapping(map, name1, email1, name2, email2); +} - if (email1) - add_mapping(map, name1, email1, name2, email2); +static int read_mailmap_file(struct string_list *map, const char *filename, + char **repo_abbrev) +{ + char buffer[1024]; + FILE *f; + + if (!filename) + return 0; + + f = fopen(filename, "r"); + if (!f) { + if (errno == ENOENT) + return 0; + return error("unable to open mailmap at %s: %s", + filename, strerror(errno)); } + + while (fgets(buffer, sizeof(buffer), f) != NULL) + read_mailmap_line(map, buffer, repo_abbrev); fclose(f); return 0; } +static void read_mailmap_buf(struct string_list *map, + const char *buf, unsigned long len, + char **repo_abbrev) +{ + while (len) { + const char *end = strchrnul(buf, '\n'); + unsigned long linelen = end - buf + 1; + char *line = xmemdupz(buf, linelen); + + read_mailmap_line(map, line, repo_abbrev); + + free(line); + buf += linelen; + len -= linelen; + } +} + +static int read_mailmap_blob(struct string_list *map, + const char *name, + char **repo_abbrev) +{ + unsigned char sha1[20]; + char *buf; + unsigned long size; + enum object_type type; + + if (!name) + return 0; + if (get_sha1(name, sha1) < 0) + return 0; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return error("unable to read mailmap object at %s", name); + if (type != OBJ_BLOB) + return error("mailmap is not a blob: %s", name); + + read_mailmap_buf(map, buf, size, repo_abbrev); + + free(buf); + return 0; +} + int read_mailmap(struct string_list *map, char **repo_abbrev) { + int err = 0; + map->strdup_strings = 1; + map->cmp = strcasecmp; - /* each failure returns 1, so >1 means both calls failed */ - return read_single_mailmap(map, ".mailmap", repo_abbrev) + - read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1; + + if (!git_mailmap_blob && is_bare_repository()) + git_mailmap_blob = "HEAD:.mailmap"; + + err |= read_mailmap_file(map, ".mailmap", repo_abbrev); + err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev); + err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev); + return err; } void clear_mailmap(struct string_list *map) @@@ -253,60 -188,95 +254,95 @@@ debug_mm("mailmap: cleared\n"); } + /* + * Look for an entry in map that match string[0:len]; string[len] + * does not have to be NUL (but it could be). + */ + static struct string_list_item *lookup_prefix(struct string_list *map, + const char *string, size_t len) + { + int i = string_list_find_insert_index(map, string, 1); + if (i < 0) { + /* exact match */ + i = -1 - i; + if (!string[len]) + return &map->items[i]; + /* + * that map entry matches exactly to the string, including + * the cruft at the end beyond "len". That is not a match + * with string[0:len] that we are looking for. + */ + } else if (!string[len]) { + /* + * asked with the whole string, and got nothing. No + * matching entry can exist in the map. + */ + return NULL; + } + + /* + * i is at the exact match to an overlong key, or location the + * overlong key would be inserted, which must come after the + * real location of the key if one exists. + */ + while (0 <= --i && i < map->nr) { + int cmp = strncasecmp(map->items[i].string, string, len); + if (cmp < 0) + /* + * "i" points at a key definitely below the prefix; + * the map does not have string[0:len] in it. + */ + break; + else if (!cmp && !map->items[i].string[len]) + /* found it */ + return &map->items[i]; + /* + * otherwise, the string at "i" may be string[0:len] + * followed by a string that sorts later than string[len:]; + * keep trying. + */ + } + return NULL; + } + int map_user(struct string_list *map, - char *email, int maxlen_email, char *name, int maxlen_name) + const char **email, size_t *emaillen, + const char **name, size_t *namelen) { - char *end_of_email; struct string_list_item *item; struct mailmap_entry *me; - char buf[1024], *mailbuf; - int i; - - /* figure out space requirement for email */ - end_of_email = strchr(email, '>'); - if (!end_of_email) { - /* email passed in might not be wrapped in <>, but end with a \0 */ - end_of_email = memchr(email, '\0', maxlen_email); - if (!end_of_email) - return 0; - } - if (end_of_email - email + 1 < sizeof(buf)) - mailbuf = buf; - else - mailbuf = xmalloc(end_of_email - email + 1); - - /* downcase the email address */ - for (i = 0; i < end_of_email - email; i++) - mailbuf[i] = tolower(email[i]); - mailbuf[i] = 0; - - debug_mm("map_user: map '%s' <%s>\n", name, mailbuf); - item = string_list_lookup(map, mailbuf); + + debug_mm("map_user: map '%.*s' <%.*s>\n", + *name, *namelen, *emaillen, *email); + + item = lookup_prefix(map, *email, *emaillen); if (item != NULL) { me = (struct mailmap_entry *)item->util; if (me->namemap.nr) { /* The item has multiple items, so we'll look up on name too */ /* If the name is not found, we choose the simple entry */ - struct string_list_item *subitem = string_list_lookup(&me->namemap, name); + struct string_list_item *subitem; + subitem = lookup_prefix(&me->namemap, *name, *namelen); if (subitem) item = subitem; } } - if (mailbuf != buf) - free(mailbuf); if (item != NULL) { struct mailmap_info *mi = (struct mailmap_info *)item->util; - if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) { + if (mi->name == NULL && mi->email == NULL) { debug_mm("map_user: -- (no simple mapping)\n"); return 0; } - if (maxlen_email && mi->email) - strlcpy(email, mi->email, maxlen_email); - else - *end_of_email = '\0'; - if (maxlen_name && mi->name) - strlcpy(name, mi->name, maxlen_name); - debug_mm("map_user: to '%s' <%s>\n", name, mi->email ? mi->email : ""); + if (mi->email) { + *email = mi->email; + *emaillen = strlen(*email); + } + if (mi->name) { + *name = mi->name; + *namelen = strlen(*name); + } + debug_mm("map_user: to '%.*s' <.*%s>\n", *namelen, *name, + *emaillen, *email); return 1; } debug_mm("map_user: --\n"); diff --combined pretty.c index 01795de687,622275c70b..07fc062865 --- a/pretty.c +++ b/pretty.c @@@ -387,56 -387,79 +387,79 @@@ void pp_user_info(const struct pretty_p const char *what, struct strbuf *sb, const char *line, const char *encoding) { + struct strbuf name; + struct strbuf mail; + struct ident_split ident; + int linelen; + char *line_end, *date; + const char *mailbuf, *namebuf; + size_t namelen, maillen; int max_length = 78; /* per rfc2822 */ - char *date; - int namelen; unsigned long time; int tz; if (pp->fmt == CMIT_FMT_ONELINE) return; - date = strchr(line, '>'); - if (!date) + + line_end = strchr(line, '\n'); + if (!line_end) { + line_end = strchr(line, '\0'); + if (!line_end) + return; + } + + linelen = ++line_end - line; + if (split_ident_line(&ident, line, linelen)) return; - namelen = ++date - line; - time = strtoul(date, &date, 10); + + + mailbuf = ident.mail_begin; + maillen = ident.mail_end - ident.mail_begin; + namebuf = ident.name_begin; + namelen = ident.name_end - ident.name_begin; + + if (pp->mailmap) + map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen); + + strbuf_init(&mail, 0); + strbuf_init(&name, 0); + + strbuf_add(&mail, mailbuf, maillen); + strbuf_add(&name, namebuf, namelen); + + namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */ + time = strtoul(ident.date_begin, &date, 10); tz = strtol(date, NULL, 10); if (pp->fmt == CMIT_FMT_EMAIL) { - char *name_tail = strchr(line, '<'); - int display_name_length; - if (!name_tail) - return; - while (line < name_tail && isspace(name_tail[-1])) - name_tail--; - display_name_length = name_tail - line; strbuf_addstr(sb, "From: "); - if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) { - add_rfc2047(sb, line, display_name_length, - encoding, RFC2047_ADDRESS); + if (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) { + add_rfc2047(sb, name.buf, name.len, + encoding, RFC2047_ADDRESS); max_length = 76; /* per rfc2047 */ - } else if (needs_rfc822_quoting(line, display_name_length)) { + } else if (needs_rfc822_quoting(name.buf, name.len)) { struct strbuf quoted = STRBUF_INIT; - add_rfc822_quoted("ed, line, display_name_length); + add_rfc822_quoted("ed, name.buf, name.len); strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len, -6, 1, max_length); strbuf_release("ed); } else { - strbuf_add_wrapped_bytes(sb, line, display_name_length, - -6, 1, max_length); + strbuf_add_wrapped_bytes(sb, name.buf, name.len, + -6, 1, max_length); } - if (namelen - display_name_length + last_line_length(sb) > max_length) { + if (namelen - name.len + last_line_length(sb) > max_length) strbuf_addch(sb, '\n'); - if (!isspace(name_tail[0])) - strbuf_addch(sb, ' '); - } - strbuf_add(sb, name_tail, namelen - display_name_length); - strbuf_addch(sb, '\n'); + + strbuf_addf(sb, " <%s>\n", mail.buf); } else { - strbuf_addf(sb, "%s: %.*s%.*s\n", what, + strbuf_addf(sb, "%s: %.*s%s <%s>\n", what, (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0, - " ", namelen, line); + " ", name.buf, mail.buf); } + + strbuf_release(&mail); + strbuf_release(&name); + switch (pp->fmt) { case CMIT_FMT_MEDIUM: strbuf_addf(sb, "Date: %s\n", show_date(time, tz, pp->date_mode)); @@@ -567,7 -590,7 +590,7 @@@ char *logmsg_reencode(const struct comm char *encoding; char *out; - if (!*output_encoding) + if (!output_encoding || !*output_encoding) return NULL; encoding = get_header(commit, "encoding"); use_encoding = encoding ? encoding : utf8; @@@ -586,7 -609,8 +609,8 @@@ return out; } - static int mailmap_name(char *email, int email_len, char *name, int name_len) + static int mailmap_name(const char **email, size_t *email_len, + const char **name, size_t *name_len) { static struct string_list *mail_map; if (!mail_map) { @@@ -603,36 -627,26 +627,26 @@@ static size_t format_person_part(struc const int placeholder_len = 2; int tz; unsigned long date = 0; - char person_name[1024]; - char person_mail[1024]; struct ident_split s; - const char *name_start, *name_end, *mail_start, *mail_end; + const char *name, *mail; + size_t maillen, namelen; if (split_ident_line(&s, msg, len) < 0) goto skip; - name_start = s.name_begin; - name_end = s.name_end; - mail_start = s.mail_begin; - mail_end = s.mail_end; - - if (part == 'N' || part == 'E') { /* mailmap lookup */ - snprintf(person_name, sizeof(person_name), "%.*s", - (int)(name_end - name_start), name_start); - snprintf(person_mail, sizeof(person_mail), "%.*s", - (int)(mail_end - mail_start), mail_start); - mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name)); - name_start = person_name; - name_end = name_start + strlen(person_name); - mail_start = person_mail; - mail_end = mail_start + strlen(person_mail); - } + name = s.name_begin; + namelen = s.name_end - s.name_begin; + mail = s.mail_begin; + maillen = s.mail_end - s.mail_begin; + + if (part == 'N' || part == 'E') /* mailmap lookup */ + mailmap_name(&mail, &maillen, &name, &namelen); if (part == 'n' || part == 'N') { /* name */ - strbuf_add(sb, name_start, name_end-name_start); + strbuf_add(sb, name, namelen); return placeholder_len; } if (part == 'e' || part == 'E') { /* email */ - strbuf_add(sb, mail_start, mail_end-mail_start); + strbuf_add(sb, mail, maillen); return placeholder_len; } @@@ -960,19 -974,12 +974,19 @@@ static size_t format_commit_one(struct switch (placeholder[0]) { case 'C': if (placeholder[1] == '(') { - const char *end = strchr(placeholder + 2, ')'); + const char *begin = placeholder + 2; + const char *end = strchr(begin, ')'); char color[COLOR_MAXLEN]; + if (!end) return 0; - color_parse_mem(placeholder + 2, - end - (placeholder + 2), + if (!prefixcmp(begin, "auto,")) { + if (!want_color(c->pretty_ctx->color)) + return end - placeholder + 1; + begin += 5; + } + color_parse_mem(begin, + end - begin, "--pretty format", color); strbuf_addstr(sb, color); return end - placeholder + 1; @@@ -1257,15 -1264,23 +1271,15 @@@ void format_commit_message(const struc const struct pretty_print_context *pretty_ctx) { struct format_commit_context context; - static const char utf8[] = "UTF-8"; const char *output_enc = pretty_ctx->output_encoding; memset(&context, 0, sizeof(context)); context.commit = commit; context.pretty_ctx = pretty_ctx; context.wrap_start = sb->len; - context.message = commit->buffer; - if (output_enc) { - char *enc = get_header(commit, "encoding"); - if (strcmp(enc ? enc : utf8, output_enc)) { - context.message = logmsg_reencode(commit, output_enc); - if (!context.message) - context.message = commit->buffer; - } - free(enc); - } + context.message = logmsg_reencode(commit, output_enc); + if (!context.message) + context.message = commit->buffer; strbuf_expand(sb, format, format_commit_item, &context); rewrap_message_tail(sb, &context, 0, 0, 0); @@@ -1301,7 -1316,7 +1315,7 @@@ static void pp_header(const struct pret continue; } - if (!memcmp(line, "parent ", 7)) { + if (!prefixcmp(line, "parent ")) { if (linelen != 48) die("bad parent line in commit"); continue; @@@ -1325,11 -1340,11 +1339,11 @@@ * FULL shows both authors but not dates. * FULLER shows both authors and dates. */ - if (!memcmp(line, "author ", 7)) { + if (!prefixcmp(line, "author ")) { strbuf_grow(sb, linelen + 80); pp_user_info(pp, "Author", sb, line + 7, encoding); } - if (!memcmp(line, "committer ", 10) && + if (!prefixcmp(line, "committer ") && (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) { strbuf_grow(sb, linelen + 80); pp_user_info(pp, "Commit", sb, line + 10, encoding); diff --combined revision.h index a395c3639a,83a79f5815..5da09ee3ef --- a/revision.h +++ b/revision.h @@@ -135,7 -135,6 +135,7 @@@ struct rev_info const char *mime_boundary; const char *patch_suffix; int numbered_files; + int reroll_count; char *message_id; struct string_list *ref_message_ids; const char *add_signoff; @@@ -144,6 -143,7 +144,7 @@@ const char *subject_prefix; int no_inline; int show_log_size; + struct string_list *mailmap; /* Filter by commit log message */ struct grep_opt grep_filter; diff --combined string-list.c index 480173fe6d,6f3d8cf091..aabb25ef4c --- a/string-list.c +++ b/string-list.c @@@ -7,10 -7,11 +7,11 @@@ static int get_entry_index(const struc int *exact_match) { int left = -1, right = list->nr; + compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; while (left + 1 < right) { int middle = (left + right) / 2; - int compare = strcmp(string, list->items[middle].string); + int compare = cmp(string, list->items[middle].string); if (compare < 0) right = middle; else if (compare > 0) @@@ -96,8 -97,9 +97,9 @@@ void string_list_remove_duplicates(stru { if (list->nr > 1) { int src, dst; + compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; for (src = dst = 1; src < list->nr; src++) { - if (!strcmp(list->items[dst - 1].string, list->items[src].string)) { + if (!cmp(list->items[dst - 1].string, list->items[src].string)) { if (list->strdup_strings) free(list->items[src].string); if (free_util) @@@ -145,6 -147,26 +147,6 @@@ void string_list_remove_empty_items(str filter_string_list(list, free_util, item_is_not_empty, NULL); } -char *string_list_longest_prefix(const struct string_list *prefixes, - const char *string) -{ - int i, max_len = -1; - char *retval = NULL; - - for (i = 0; i < prefixes->nr; i++) { - char *prefix = prefixes->items[i].string; - if (!prefixcmp(string, prefix)) { - int len = strlen(prefix); - if (len > max_len) { - retval = prefix; - max_len = len; - } - } - } - - return retval; -} - void string_list_clear(struct string_list *list, int free_util) { if (list->items) { @@@ -210,15 -232,20 +212,20 @@@ struct string_list_item *string_list_ap list->strdup_strings ? xstrdup(string) : (char *)string); } + /* Yuck */ + static compare_strings_fn compare_for_qsort; + + /* Only call this from inside sort_string_list! */ static int cmp_items(const void *a, const void *b) { const struct string_list_item *one = a; const struct string_list_item *two = b; - return strcmp(one->string, two->string); + return compare_for_qsort(one->string, two->string); } void sort_string_list(struct string_list *list) { + compare_for_qsort = list->cmp ? list->cmp : strcmp; qsort(list->items, list->nr, sizeof(*list->items), cmp_items); } @@@ -226,8 -253,10 +233,10 @@@ struct string_list_item *unsorted_strin const char *string) { int i; + compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; + for (i = 0; i < list->nr; i++) - if (!strcmp(string, list->items[i].string)) + if (!cmp(string, list->items[i].string)) return list->items + i; return NULL; } diff --combined string-list.h index db1284861a,446e79e425..de6769c92d --- a/string-list.h +++ b/string-list.h @@@ -5,10 -5,14 +5,14 @@@ struct string_list_item char *string; void *util; }; + + typedef int (*compare_strings_fn)(const char *, const char *); + struct string_list { struct string_list_item *items; unsigned int nr, alloc; unsigned int strdup_strings:1; + compare_strings_fn cmp; /* NULL uses strcmp() */ }; #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 } @@@ -45,6 -49,15 +49,6 @@@ void filter_string_list(struct string_l */ void string_list_remove_empty_items(struct string_list *list, int free_util); -/* - * Return the longest string in prefixes that is a prefix (in the - * sense of prefixcmp()) of string, or NULL if no such prefix exists. - * This function does not require the string_list to be sorted (it - * does a linear search). - */ -char *string_list_longest_prefix(const struct string_list *prefixes, const char *string); - - /* Use these functions only on sorted lists: */ int string_list_has_string(const struct string_list *list, const char *string); int string_list_find_insert_index(const struct string_list *list, const char *string, diff --combined t/t4203-mailmap.sh index aae30d97b1,7d4d31c830..842b7549ec --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@@ -149,104 -149,6 +149,104 @@@ test_expect_success 'No mailmap files, test_cmp expect actual ' +test_expect_success 'setup mailmap blob tests' ' + git checkout -b map && + test_when_finished "git checkout master" && + cat >just-bugs <<-\EOF && + Blob Guy + EOF + cat >both <<-\EOF && + Blob Guy + Blob Guy + EOF + git add just-bugs both && + git commit -m "my mailmaps" && + echo "Repo Guy " >.mailmap && + echo "Internal Guy " >internal.map +' + +test_expect_success 'mailmap.blob set' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Repo Guy (1): + initial + + EOF + git -c mailmap.blob=map:just-bugs shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob overrides .mailmap' ' + cat >expect <<-\EOF && + Blob Guy (2): + initial + second + + EOF + git -c mailmap.blob=map:both shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.file overrides mailmap.blob' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Internal Guy (1): + initial + + EOF + git \ + -c mailmap.blob=map:both \ + -c mailmap.file=internal.map \ + shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob can be missing' ' + cat >expect <<-\EOF && + Repo Guy (1): + initial + + nick1 (1): + second + + EOF + git -c mailmap.blob=map:nonexistent shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob defaults to off in non-bare repo' ' + git init non-bare && + ( + cd non-bare && + test_commit one .mailmap "Fake Name " && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual && + rm .mailmap && + echo " 1 A U Thor" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' ' + git clone --bare non-bare bare && + ( + cd bare && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'cleanup after mailmap.blob tests' ' + rm -f .mailmap +' + # Extended mailmap configurations should give us the following output for shortlog cat >expect <<\EOF A U Thor (1): @@@ -337,6 -239,62 +337,62 @@@ test_expect_success 'Log output (comple test_cmp expect actual ' + cat >expect <<\EOF + Author: CTO + Author: Santa Claus + Author: Santa Claus + Author: Other Author + Author: Other Author + Author: Some Dude + Author: A U Thor + EOF + + test_expect_success 'Log output with --use-mailmap' ' + git log --use-mailmap | grep Author >actual && + test_cmp expect actual + ' + + cat >expect <<\EOF + Author: CTO + Author: Santa Claus + Author: Santa Claus + Author: Other Author + Author: Other Author + Author: Some Dude + Author: A U Thor + EOF + + test_expect_success 'Log output with log.mailmap' ' + git -c log.mailmap=True log | grep Author >actual && + test_cmp expect actual + ' + + cat >expect <<\EOF + Author: Santa Claus + Author: Santa Claus + EOF + + test_expect_success 'Grep author with --use-mailmap' ' + git log --use-mailmap --author Santa | grep Author >actual && + test_cmp expect actual + ' + cat >expect <<\EOF + Author: Santa Claus + Author: Santa Claus + EOF + + test_expect_success 'Grep author with log.mailmap' ' + git -c log.mailmap=True log --author Santa | grep Author >actual && + test_cmp expect actual + ' + + >expect + + test_expect_success 'Only grep replaced author with --use-mailmap' ' + git log --use-mailmap --author "" >actual && + test_cmp expect actual + ' + # git blame cat >expect <<\EOF ^OBJI (A U Thor DATE 1) one