Merge branch 'ap/commit-author-mailmap'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:38:33 +0000 (11:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:38:33 +0000 (11:38 -0700)
"git commit --author=$name", when $name is not in the canonical
"A. U. Thor <au.thor@example.xz>" format, looks for a matching name
from existing history, but did not consult mailmap to grab the
preferred author name.

* ap/commit-author-mailmap:
commit: search author pattern against mailmap

1  2 
builtin/commit.c
t/t4203-mailmap.sh
diff --combined builtin/commit.c
index 80d886ab3af371a003ae026c19bd76ea83df9690,a48a7fe59c9abf32ce0ecc5e4ca5126761fc5a75..084d70fd4da5d38196cbde1f692bd2d70c0c3800
@@@ -30,6 -30,7 +30,7 @@@
  #include "column.h"
  #include "sequencer.h"
  #include "notes-utils.h"
+ #include "mailmap.h"
  
  static const char * const builtin_commit_usage[] = {
        N_("git commit [options] [--] <pathspec>..."),
@@@ -202,15 -203,17 +203,15 @@@ static int commit_index_files(void
   * and return the paths that match the given pattern in list.
   */
  static int list_paths(struct string_list *list, const char *with_tree,
 -                    const char *prefix, const char **pattern)
 +                    const char *prefix, const struct pathspec *pattern)
  {
        int i;
        char *m;
  
 -      if (!pattern)
 +      if (!pattern->nr)
                return 0;
  
 -      for (i = 0; pattern[i]; i++)
 -              ;
 -      m = xcalloc(1, i);
 +      m = xcalloc(1, pattern->nr);
  
        if (with_tree) {
                char *max_prefix = common_prefix(pattern);
  
                if (ce->ce_flags & CE_UPDATE)
                        continue;
 -              if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
 +              if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@@ -296,17 -299,17 +297,17 @@@ static char *prepare_index(int argc, co
  {
        int fd;
        struct string_list partial;
 -      const char **pathspec = NULL;
 +      struct pathspec pathspec;
        char *old_index_env = NULL;
        int refresh_flags = REFRESH_QUIET;
  
        if (is_status)
                refresh_flags |= REFRESH_UNMERGED;
 +      parse_pathspec(&pathspec, 0,
 +                     PATHSPEC_PREFER_FULL,
 +                     prefix, argv);
  
 -      if (*argv)
 -              pathspec = get_pathspec(prefix, argv);
 -
 -      if (read_cache_preload(pathspec) < 0)
 +      if (read_cache_preload(&pathspec) < 0)
                die(_("index file corrupt"));
  
        if (interactive) {
         * (A) if all goes well, commit the real index;
         * (B) on failure, rollback the real index.
         */
 -      if (all || (also && pathspec && *pathspec)) {
 +      if (all || (also && pathspec.nr)) {
                fd = hold_locked_index(&index_lock, 1);
 -              add_files_to_cache(also ? prefix : NULL, pathspec, 0);
 +              add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
                if (write_cache(fd, active_cache, active_nr) ||
         * and create commit from the_index.
         * We still need to refresh the index here.
         */
 -      if (!only && (!pathspec || !*pathspec)) {
 +      if (!only && !pathspec.nr) {
                fd = hold_locked_index(&index_lock, 1);
                refresh_cache_or_die(refresh_flags);
                if (active_cache_changed) {
  
        memset(&partial, 0, sizeof(partial));
        partial.strdup_strings = 1;
 -      if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec))
 +      if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
                exit(1);
  
        discard_cache();
@@@ -933,6 -936,7 +934,7 @@@ static const char *find_author_by_nickn
        struct rev_info revs;
        struct commit *commit;
        struct strbuf buf = STRBUF_INIT;
+       struct string_list mailmap = STRING_LIST_INIT_NODUP;
        const char *av[20];
        int ac = 0;
  
        av[++ac] = buf.buf;
        av[++ac] = NULL;
        setup_revisions(ac, av, &revs, NULL);
+       revs.mailmap = &mailmap;
+       read_mailmap(revs.mailmap, NULL);
        prepare_revision_walk(&revs);
        commit = get_revision(&revs);
        if (commit) {
                struct pretty_print_context ctx = {0};
                ctx.date_mode = DATE_NORMAL;
                strbuf_release(&buf);
-               format_commit_message(commit, "%an <%ae>", &buf, &ctx);
+               format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
+               clear_mailmap(&mailmap);
                return strbuf_detach(&buf, NULL);
        }
        die(_("No existing author found with '%s'"), name);
@@@ -1089,7 -1097,7 +1095,7 @@@ static int parse_and_validate_options(i
        if (patch_interactive)
                interactive = 1;
  
 -      if (!!also + !!only + !!all + !!interactive > 1)
 +      if (also + only + all + interactive > 1)
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
        if (argc == 0 && (also || (only && !amend)))
                die(_("No paths with --include/--only does not make sense."));
@@@ -1226,14 -1234,14 +1232,14 @@@ int cmd_status(int argc, const char **a
                OPT_SET_INT(0, "long", &status_format,
                            N_("show status in long format (default)"),
                            STATUS_FORMAT_LONG),
 -              OPT_BOOLEAN('z', "null", &s.null_termination,
 -                          N_("terminate entries with NUL")),
 +              OPT_BOOL('z', "null", &s.null_termination,
 +                       N_("terminate entries with NUL")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
                  N_("mode"),
                  N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
 -              OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
 -                          N_("show ignored files")),
 +              OPT_BOOL(0, "ignored", &show_ignored_in_status,
 +                       N_("show ignored files")),
                { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
                  N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        handle_untracked_files_arg(&s);
        if (show_ignored_in_status)
                s.show_ignored_files = 1;
 -      if (*argv)
 -              s.pathspec = get_pathspec(prefix, argv);
 +      parse_pathspec(&s.pathspec, 0,
 +                     PATHSPEC_PREFER_FULL,
 +                     prefix, argv);
  
 -      read_cache_preload(s.pathspec);
 -      refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
 +      read_cache_preload(&s.pathspec);
 +      refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
  
        fd = hold_locked_index(&index_lock, 0);
        if (0 <= fd)
@@@ -1433,24 -1440,24 +1439,24 @@@ int cmd_commit(int argc, const char **a
                OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
                OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
                OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
 -              OPT_BOOLEAN(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
 -              OPT_BOOLEAN('s', "signoff", &signoff, N_("add Signed-off-by:")),
 +              OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
 +              OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
                OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
 -              OPT_BOOLEAN(0, "status", &include_status, N_("include status in commit message template")),
 +              OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
                { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                /* end commit message options */
  
                OPT_GROUP(N_("Commit contents options")),
 -              OPT_BOOLEAN('a', "all", &all, N_("commit all changed files")),
 -              OPT_BOOLEAN('i', "include", &also, N_("add specified files to index for commit")),
 -              OPT_BOOLEAN(0, "interactive", &interactive, N_("interactively add files")),
 -              OPT_BOOLEAN('p', "patch", &patch_interactive, N_("interactively add changes")),
 -              OPT_BOOLEAN('o', "only", &only, N_("commit only specified files")),
 -              OPT_BOOLEAN('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
 -              OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
 +              OPT_BOOL('a', "all", &all, N_("commit all changed files")),
 +              OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
 +              OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
 +              OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
 +              OPT_BOOL('o', "only", &only, N_("commit only specified files")),
 +              OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
 +              OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
                OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
                OPT_SET_INT(0, "long", &status_format,
                            N_("show status in long format (default)"),
                            STATUS_FORMAT_LONG),
 -              OPT_BOOLEAN('z', "null", &s.null_termination,
 -                          N_("terminate entries with NUL")),
 -              OPT_BOOLEAN(0, "amend", &amend, N_("amend previous commit")),
 -              OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
 +              OPT_BOOL('z', "null", &s.null_termination,
 +                       N_("terminate entries with NUL")),
 +              OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
 +              OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
                /* end commit contents options */
  
 -              { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
 -                N_("ok to record an empty change"),
 -                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 -              { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
 -                N_("ok to record a change with an empty message"),
 -                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 +              OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
 +                              N_("ok to record an empty change")),
 +              OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message,
 +                              N_("ok to record a change with an empty message")),
  
                OPT_END()
        };
diff --combined t/t4203-mailmap.sh
index ce3eace065be81612e1ab2f994909b9352d5a06c,4d715f058c5fba2741eda4e193f096570647f63c..0dd8b65d7cdec2ada739d7e0b524fd3c66dd5ef4
@@@ -202,8 -202,7 +202,8 @@@ test_expect_success 'setup mailmap blo
        Blob Guy <author@example.com>
        Blob Guy <bugs@company.xx>
        EOF
 -      git add just-bugs both &&
 +      printf "Tricky Guy <author@example.com>" >no-newline &&
 +      git add just-bugs both no-newline &&
        git commit -m "my mailmaps" &&
        echo "Repo Guy <author@example.com>" >.mailmap &&
        echo "Internal Guy <author@example.com>" >internal.map
@@@ -287,19 -286,6 +287,19 @@@ test_expect_success 'mailmap.blob defau
        )
  '
  
 +test_expect_success 'mailmap.blob can handle blobs without trailing newline' '
 +      cat >expect <<-\EOF &&
 +      Tricky Guy (1):
 +            initial
 +
 +      nick1 (1):
 +            second
 +
 +      EOF
 +      git -c mailmap.blob=map:no-newline shortlog HEAD >actual &&
 +      test_cmp expect actual
 +'
 +
  test_expect_success 'cleanup after mailmap.blob tests' '
        rm -f .mailmap
  '
@@@ -484,4 -470,15 +484,15 @@@ test_expect_success 'Blame output (comp
        test_cmp expect actual.fuzz
  '
  
+ cat >expect <<\EOF
+ Some Dude <some@dude.xx>
+ EOF
+ test_expect_success 'commit --author honors mailmap' '
+       test_must_fail git commit --author "nick" --allow-empty -meight &&
+       git commit --author "Some Dude" --allow-empty -meight &&
+       git show --pretty=format:"%an <%ae>%n" >actual &&
+       test_cmp expect actual
+ '
  test_done