wincred: avoid overwriting configured variables
[gitweb.git] / builtin / commit.c
index d21d07a1a8e9fbc365a4555a0f9e81d0c1f2a7d0..7d711eafa698d933291beaa00b485397c7efb256 100644 (file)
@@ -29,6 +29,8 @@
 #include "gpg-interface.h"
 #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>..."),
@@ -62,8 +64,18 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
 "If you wish to commit it anyway, use:\n"
 "\n"
 "    git commit --allow-empty\n"
+"\n");
+
+static const char empty_cherry_pick_advice_single[] =
+N_("Otherwise, please use 'git reset'\n");
+
+static const char empty_cherry_pick_advice_multi[] =
+N_("If you wish to skip this commit, use:\n"
+"\n"
+"    git reset\n"
 "\n"
-"Otherwise, please use 'git reset'\n");
+"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
+"the remaining commits.\n");
 
 static const char *use_message_buffer;
 static const char commit_editmsg[] = "COMMIT_EDITMSG";
@@ -106,24 +118,29 @@ static enum {
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
+static int sequencer_in_use;
 static int use_editor = 1, include_status = 1;
-static int show_ignored_in_status;
+static int show_ignored_in_status, have_option_m;
 static const char *only_include_assumed;
 static struct strbuf message = STRBUF_INIT;
 
-static enum {
+static enum status_format {
        STATUS_FORMAT_NONE = 0,
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
-       STATUS_FORMAT_PORCELAIN
-} status_format;
+       STATUS_FORMAT_PORCELAIN,
+
+       STATUS_FORMAT_UNSPECIFIED
+} status_format = STATUS_FORMAT_UNSPECIFIED;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
        struct strbuf *buf = opt->value;
-       if (unset)
+       if (unset) {
+               have_option_m = 0;
                strbuf_setlen(buf, 0);
-       else {
+       } else {
+               have_option_m = 1;
                if (buf->len)
                        strbuf_addch(buf, '\n');
                strbuf_addstr(buf, arg);
@@ -136,14 +153,26 @@ static void determine_whence(struct wt_status *s)
 {
        if (file_exists(git_path("MERGE_HEAD")))
                whence = FROM_MERGE;
-       else if (file_exists(git_path("CHERRY_PICK_HEAD")))
+       else if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
                whence = FROM_CHERRY_PICK;
+               if (file_exists(git_path("sequencer")))
+                       sequencer_in_use = 1;
+       }
        else
                whence = FROM_COMMIT;
        if (s)
                s->whence = whence;
 }
 
+static void status_init_config(struct wt_status *s, config_fn_t fn)
+{
+       wt_status_prepare(s);
+       gitmodules_config();
+       git_config(fn, s);
+       determine_whence(s);
+       s->hints = advice_status_hints; /* must come after git_config() */
+}
+
 static void rollback_index_files(void)
 {
        switch (commit_style) {
@@ -183,17 +212,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);
@@ -202,12 +229,12 @@ static int list_paths(struct string_list *list, const char *with_tree,
        }
 
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                struct string_list_item *item;
 
                if (ce->ce_flags & CE_UPDATE)
                        continue;
-               if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
+               if (!ce_path_match(ce, pattern, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@ -279,17 +306,17 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 {
        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) {
@@ -331,9 +358,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * (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) ||
@@ -352,7 +379,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * 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) {
@@ -397,7 +424,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 
        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();
@@ -457,6 +484,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s);
                break;
+       case STATUS_FORMAT_UNSPECIFIED:
+               die("BUG: finalize_deferred_config() should have been called");
+               break;
        case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                wt_status_print(s);
@@ -526,7 +556,6 @@ static void determine_author_info(struct strbuf *author_ident)
                                        (lb - strlen(" ") -
                                         (a + strlen("\nauthor "))));
                email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
-               date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
                len = eol - (rb + strlen("> "));
                date = xmalloc(len + 2);
                *date = '@';
@@ -578,11 +607,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        const char *hook_arg2 = NULL;
        int ident_shown = 0;
        int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+       int old_display_comment_prefix;
 
        /* This checks and barfs if author is badly specified */
        determine_author_info(author_ident);
 
-       if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+       if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL))
                return 0;
 
        if (squash_message) {
@@ -675,6 +705,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        if (s->fp == NULL)
                die_errno(_("could not open '%s'"), git_path(commit_editmsg));
 
+       /* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
+       old_display_comment_prefix = s->display_comment_prefix;
+       s->display_comment_prefix = 1;
+
+       /*
+        * Most hints are counter-productive when the commit has
+        * already started.
+        */
+       s->hints = 0;
+
        if (clean_message_contents)
                stripspace(&sb, 0);
 
@@ -693,7 +733,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                eol = nl - sb.buf;
                        else
                                eol = sb.len;
-                       if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
+                       if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
                                ignore_footer = sb.len - previous;
                                break;
                        }
@@ -702,7 +742,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        previous = eol;
                }
 
-               append_signoff(&sb, ignore_footer);
+               append_signoff(&sb, ignore_footer, 0);
        }
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
@@ -800,11 +840,17 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
         */
        if (!commitable && whence != FROM_MERGE && !allow_empty &&
            !(amend && is_a_merge(current_head))) {
+               s->display_comment_prefix = old_display_comment_prefix;
                run_status(stdout, index_file, prefix, 0, s);
                if (amend)
                        fputs(_(empty_amend_advice), stderr);
-               else if (whence == FROM_CHERRY_PICK)
+               else if (whence == FROM_CHERRY_PICK) {
                        fputs(_(empty_cherry_pick_advice), stderr);
+                       if (!sequencer_in_use)
+                               fputs(_(empty_cherry_pick_advice_single), stderr);
+                       else
+                               fputs(_(empty_cherry_pick_advice_multi), stderr);
+               }
                return 0;
        }
 
@@ -820,8 +866,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                return 0;
        }
 
-       if (run_hook(index_file, "prepare-commit-msg",
-                    git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+       if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
+                           git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
                return 0;
 
        if (use_editor) {
@@ -837,7 +883,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        }
 
        if (!no_verify &&
-           run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+           run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
                return 0;
        }
 
@@ -858,7 +904,7 @@ static int rest_is_empty(struct strbuf *sb, int start)
                        eol = sb->len;
 
                if (strlen(sign_off_header) <= eol - i &&
-                   !prefixcmp(sb->buf + i, sign_off_header)) {
+                   starts_with(sb->buf + i, sign_off_header)) {
                        i = eol;
                        continue;
                }
@@ -909,6 +955,7 @@ static const char *find_author_by_nickname(const char *name)
        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;
 
@@ -919,13 +966,17 @@ static const char *find_author_by_nickname(const char *name)
        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);
@@ -955,7 +1006,43 @@ static const char *read_commit_message(const char *name)
        if (!commit)
                die(_("could not lookup commit %s"), name);
        out_enc = get_commit_output_encoding();
-       return logmsg_reencode(commit, out_enc);
+       return logmsg_reencode(commit, NULL, out_enc);
+}
+
+/*
+ * Enumerate what needs to be propagated when --porcelain
+ * is not in effect here.
+ */
+static struct status_deferred_config {
+       enum status_format status_format;
+       int show_branch;
+} status_deferred_config = {
+       STATUS_FORMAT_UNSPECIFIED,
+       -1 /* unspecified */
+};
+
+static void finalize_deferred_config(struct wt_status *s)
+{
+       int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
+                                  !s->null_termination);
+
+       if (s->null_termination) {
+               if (status_format == STATUS_FORMAT_NONE ||
+                   status_format == STATUS_FORMAT_UNSPECIFIED)
+                       status_format = STATUS_FORMAT_PORCELAIN;
+               else if (status_format == STATUS_FORMAT_LONG)
+                       die(_("--long and -z are incompatible"));
+       }
+
+       if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
+               status_format = status_deferred_config.status_format;
+       if (status_format == STATUS_FORMAT_UNSPECIFIED)
+               status_format = STATUS_FORMAT_NONE;
+
+       if (use_deferred_config && s->show_branch < 0)
+               s->show_branch = status_deferred_config.show_branch;
+       if (s->show_branch < 0)
+               s->show_branch = 0;
 }
 
 static int parse_and_validate_options(int argc, const char *argv[],
@@ -968,6 +1055,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
        int f = 0;
 
        argc = parse_options(argc, argv, prefix, options, usage, 0);
+       finalize_deferred_config(s);
 
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
@@ -975,12 +1063,10 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (force_author && renew_authorship)
                die(_("Using both --reset-author and --author does not make sense"));
 
-       if (logfile || message.len || use_message || fixup_message)
+       if (logfile || have_option_m || use_message || fixup_message)
                use_editor = 0;
        if (0 <= edit_flag)
                use_editor = edit_flag;
-       if (!use_editor)
-               setenv("GIT_EDITOR", ":", 1);
 
        /* Sanity check options */
        if (amend && !current_head)
@@ -1028,14 +1114,14 @@ static int parse_and_validate_options(int argc, const char *argv[],
        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."));
        if (argc == 0 && only && amend)
                only_include_assumed = _("Clever... amending the last one with dirty index.");
        if (argc > 0 && !also && !only)
-               only_include_assumed = _("Explicit paths specified without -i nor -o; assuming --only paths...");
+               only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths...");
        if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
                cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
        else if (!strcmp(cleanup_arg, "verbatim"))
@@ -1052,12 +1138,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (all && argc > 0)
                die(_("Paths with -a does not make sense."));
 
-       if (s->null_termination) {
-               if (status_format == STATUS_FORMAT_NONE)
-                       status_format = STATUS_FORMAT_PORCELAIN;
-               else if (status_format == STATUS_FORMAT_LONG)
-                       die(_("--long and -z are incompatible"));
-       }
        if (status_format != STATUS_FORMAT_NONE)
                dry_run = 1;
 
@@ -1101,7 +1181,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
 {
        struct wt_status *s = cb;
 
-       if (!prefixcmp(k, "column."))
+       if (starts_with(k, "column."))
                return git_column_config(k, v, "status", &s->colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
@@ -1110,11 +1190,26 @@ static int git_status_config(const char *k, const char *v, void *cb)
                        s->submodule_summary = -1;
                return 0;
        }
+       if (!strcmp(k, "status.short")) {
+               if (git_config_bool(k, v))
+                       status_deferred_config.status_format = STATUS_FORMAT_SHORT;
+               else
+                       status_deferred_config.status_format = STATUS_FORMAT_NONE;
+               return 0;
+       }
+       if (!strcmp(k, "status.branch")) {
+               status_deferred_config.show_branch = git_config_bool(k, v);
+               return 0;
+       }
        if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
                s->use_color = git_config_colorbool(k, v);
                return 0;
        }
-       if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+       if (!strcmp(k, "status.displaycommentprefix")) {
+               s->display_comment_prefix = git_config_bool(k, v);
+               return 0;
+       }
+       if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
                if (slot < 0)
                        return 0;
@@ -1152,22 +1247,22 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_SET_INT('s', "short", &status_format,
                            N_("show status concisely"), STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN('b', "branch", &s.show_branch,
-                           N_("show branch information")),
+               OPT_BOOL('b', "branch", &s.show_branch,
+                        N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"),
                            STATUS_FORMAT_PORCELAIN),
                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" },
@@ -1178,30 +1273,22 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_status_usage, builtin_status_options);
 
-       wt_status_prepare(&s);
-       gitmodules_config();
-       git_config(git_status_config, &s);
-       determine_whence(&s);
+       status_init_config(&s, git_status_config);
        argc = parse_options(argc, argv, prefix,
                             builtin_status_options,
                             builtin_status_usage, 0);
        finalize_colopts(&s.colopts, -1);
-
-       if (s.null_termination) {
-               if (status_format == STATUS_FORMAT_NONE)
-                       status_format = STATUS_FORMAT_PORCELAIN;
-               else if (status_format == STATUS_FORMAT_LONG)
-                       die(_("--long and -z are incompatible"));
-       }
+       finalize_deferred_config(&s);
 
        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)
@@ -1221,6 +1308,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s);
                break;
+       case STATUS_FORMAT_UNSPECIFIED:
+               die("BUG: finalize_deferred_config() should have been called");
+               break;
        case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                s.verbose = verbose;
@@ -1246,7 +1336,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
        commit = lookup_commit(sha1);
        if (!commit)
                die(_("couldn't look up newly created commit"));
-       if (!commit || parse_commit(commit))
+       if (parse_commit(commit))
                die(_("could not parse newly created commit"));
 
        strbuf_addstr(&format, "format:%h] %s");
@@ -1285,7 +1375,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
 
        head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
        printf("[%s%s ",
-               !prefixcmp(head, "refs/heads/") ?
+               starts_with(head, "refs/heads/") ?
                        head + 11 :
                        !strcmp(head, "HEAD") ?
                                _("detached HEAD") :
@@ -1353,6 +1443,29 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
        return finish_command(&proc);
 }
 
+int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
+{
+       const char *hook_env[3] =  { NULL };
+       char index[PATH_MAX];
+       va_list args;
+       int ret;
+
+       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+       hook_env[0] = index;
+
+       /*
+        * Let the hook know that no editor will be launched.
+        */
+       if (!editor_is_used)
+               hook_env[1] = "GIT_EDITOR=:";
+
+       va_start(args, name);
+       ret = run_hook_ve(hook_env, name, args);
+       va_end(args);
+
+       return ret;
+}
+
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
        static struct wt_status s;
@@ -1369,45 +1482,43 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                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_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
+               OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
                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()
        };
@@ -1415,7 +1526,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
-       char *nl, *p;
+       char *nl;
        unsigned char sha1[20];
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
@@ -1427,17 +1538,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_commit_usage, builtin_commit_options);
 
-       wt_status_prepare(&s);
-       gitmodules_config();
-       git_config(git_commit_config, &s);
-       determine_whence(&s);
+       status_init_config(&s, git_commit_config);
+       status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
        s.colopts = 0;
 
        if (get_sha1("HEAD", sha1))
                current_head = NULL;
        else {
                current_head = lookup_commit_or_die(sha1, "HEAD");
-               if (!current_head || parse_commit(current_head))
+               if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
        argc = parse_and_validate_options(argc, argv, builtin_commit_options,
@@ -1513,11 +1622,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        /* Truncate the message just before the diff, if any. */
-       if (verbose) {
-               p = strstr(sb.buf, "\ndiff --git ");
-               if (p != NULL)
-                       strbuf_setlen(&sb, p - sb.buf + 1);
-       }
+       if (verbose)
+               wt_status_truncate_message_at_cut_line(&sb);
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
@@ -1552,7 +1658,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                                           !current_head
                                           ? NULL
                                           : current_head->object.sha1,
-                                          0);
+                                          0, NULL);
 
        nl = strchr(sb.buf, '\n');
        if (nl)
@@ -1584,14 +1690,14 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                     "not exceeded, and then \"git reset HEAD\" to recover."));
 
        rerere(0);
-       run_hook(get_index_file(), "post-commit", NULL);
+       run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
        if (amend && !no_post_rewrite) {
                struct notes_rewrite_cfg *cfg;
                cfg = init_copy_notes_for_rewrite("amend");
                if (cfg) {
                        /* we are amending, so current_head is not NULL */
                        copy_note_for_rewrite(cfg, current_head->object.sha1, sha1);
-                       finish_copy_notes_for_rewrite(cfg);
+                       finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
                }
                run_rewrite_hook(current_head->object.sha1, sha1);
        }