Code

Merge branch 'mm/conflict-advice'
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:42:59 +0000 (14:42 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:42:59 +0000 (14:42 -0800)
* mm/conflict-advice:
  Be more user-friendly when refusing to do something because of conflict.

Conflicts:
Documentation/config.txt
advice.c
advice.h

1  2 
Documentation/config.txt
advice.c
advice.h
builtin-commit.c
builtin-merge.c
builtin-revert.c

diff --combined Documentation/config.txt
index d4332140a01635feab079e394f02d8c927cf37c5,9c27686fab2c813139bb64642e48aad3d5ba6a80..76c7d9c7e53b1e8b5c688870a6d2dba840787b54
@@@ -130,10 -130,10 +130,14 @@@ advice.*:
                Advice shown when linkgit:git-merge[1] refuses to
                merge to avoid overwritting local changes.
                Default: true.
+       resolveConflict::
+               Advices shown by various commands when conflicts
+               prevent the operation from being performed.
+               Default: true.
 +      implicitIdentity::
 +              Advice on how to set your identity configuration when
 +              your information is guessed from the system username and
 +              domain name. Default: true.
  --
  
  core.fileMode::
@@@ -301,24 -301,17 +305,24 @@@ false), while all other repositories ar
  = true).
  
  core.worktree::
 -      Set the path to the working tree.  The value will not be
 -      used in combination with repositories found automatically in
 -      a .git directory (i.e. $GIT_DIR is not set).
 +      Set the path to the root of the work tree.
        This can be overridden by the GIT_WORK_TREE environment
        variable and the '--work-tree' command line option. It can be
 -      a absolute path or relative path to the directory specified by
 -      --git-dir or GIT_DIR.
 -      Note: If --git-dir or GIT_DIR are specified but none of
 +      an absolute path or a relative path to the .git directory,
 +      either specified by --git-dir or GIT_DIR, or automatically
 +      discovered.
 +      If --git-dir or GIT_DIR are specified but none of
        --work-tree, GIT_WORK_TREE and core.worktree is specified,
 -      the current working directory is regarded as the top directory
 -      of your working tree.
 +      the current working directory is regarded as the root of the
 +      work tree.
 ++
 +Note that this variable is honored even when set in a configuration
 +file in a ".git" subdirectory of a directory, and its value differs
 +from the latter directory (e.g. "/path/to/.git/config" has
 +core.worktree set to "/different/path"), which is most likely a
 +misconfiguration.  Running git commands in "/path/to" directory will
 +still use "/different/path" as the root of the work tree and can cause
 +great confusion to the users.
  
  core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
@@@ -506,10 -499,6 +510,10 @@@ notes should be printed
  This setting defaults to "refs/notes/commits", and can be overridden by
  the `GIT_NOTES_REF` environment variable.
  
 +core.sparseCheckout::
 +      Enable "sparse checkout" feature. See section "Sparse checkout" in
 +      linkgit:git-read-tree[1] for more information.
 +
  add.ignore-errors::
        Tells 'git-add' to continue adding files when some files cannot be
        added due to indexing errors. Equivalent to the '--ignore-errors'
@@@ -545,7 -534,7 +549,7 @@@ apply.whitespace:
        as the '--whitespace' option. See linkgit:git-apply[1].
  
  branch.autosetupmerge::
 -      Tells 'git-branch' and 'git-checkout' to setup new branches
 +      Tells 'git-branch' and 'git-checkout' to set up new branches
        so that linkgit:git-pull[1] will appropriately merge from the
        starting point branch. Note that even if this option is not set,
        this behavior can be chosen per-branch using the `--track`
@@@ -720,11 -709,6 +724,11 @@@ color.ui:
        terminal. When more specific variables of color.* are set, they always
        take precedence over this setting. Defaults to false.
  
 +commit.status
 +      A boolean to enable/disable inclusion of status information in the
 +      commit message template when using an editor to prepare the commit
 +      message.  Defaults to true.
 +
  commit.template::
        Specify a file to use as the template for new commit messages.
        "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
@@@ -738,7 -722,7 +742,7 @@@ diff.autorefreshindex:
        contents in the work tree match the contents in the
        index.  This option defaults to true.  Note that this
        affects only 'git-diff' Porcelain, and not lower level
 -      'diff' commands, such as 'git-diff-files'.
 +      'diff' commands such as 'git-diff-files'.
  
  diff.external::
        If this config variable is set, diff generation is not
@@@ -854,8 -838,8 +858,8 @@@ format.pretty:
  
  format.thread::
        The default threading style for 'git-format-patch'.  Can be
 -      either a boolean value, `shallow` or `deep`.  `shallow`
 -      threading makes every mail a reply to the head of the series,
 +      a boolean value, or `shallow` or `deep`.  `shallow` threading
 +      makes every mail a reply to the head of the series,
        where the head is chosen from the cover letter, the
        `\--in-reply-to`, and the first patch mail, in this order.
        `deep` threading makes every mail a reply to the previous one.
@@@ -888,12 -872,15 +892,12 @@@ gc.autopacklimit:
        default value is 50.  Setting this to 0 disables it.
  
  gc.packrefs::
 -      'git-gc' does not run `git pack-refs` in a bare repository by
 -      default so that older dumb-transport clients can still fetch
 -      from the repository.  Setting this to `true` lets 'git-gc'
 -      to run `git pack-refs`.  Setting this to `false` tells
 -      'git-gc' never to run `git pack-refs`. The default setting is
 -      `notbare`. Enable it only when you know you do not have to
 -      support such clients.  The default setting will change to `true`
 -      at some stage, and setting this to `false` will continue to
 -      prevent `git pack-refs` from being run from 'git-gc'.
 +      Running `git pack-refs` in a repository renders it
 +      unclonable by Git versions prior to 1.5.1.2 over dumb
 +      transports such as HTTP.  This variable determines whether
 +      'git gc' runs `git pack-refs`. This can be set to "nobare"
 +      to enable it within all non-bare repos or it can be set to a
 +      boolean value.  The default is `true`.
  
  gc.pruneexpire::
        When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'.
@@@ -1149,12 -1136,6 +1153,12 @@@ http.maxRequests:
        How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
  
 +http.minSessions::
 +      The number of curl sessions (counted across slots) to be kept across
 +      requests. They will not be ended with curl_easy_cleanup() until
 +      http_cleanup() is invoked. If USE_CURL_MULTI is not defined, this
 +      value will be capped at 1. Defaults to 1.
 +
  http.postBuffer::
        Maximum size in bytes of the buffer used by smart HTTP
        transports when POSTing data to the remote system.
@@@ -1484,10 -1465,6 +1488,10 @@@ remote.<name>.tagopt:
        Setting this value to \--no-tags disables automatic tag following when
        fetching from remote <name>
  
 +remote.<name>.vcs::
 +      Setting this to a value <vcs> will cause git to interact with
 +      the remote with the git-remote-<vcs> helper.
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
diff --combined advice.c
index 8f7de0e9ed70218f1f5355df670c537d1b818c76,3309521f456b0218fa765ba7f3dd7d7a3e94dea2..936d98ba2ba2c597f69188c8e7f9f1abcc7169a8
+++ b/advice.c
@@@ -3,7 -3,7 +3,8 @@@
  int advice_push_nonfastforward = 1;
  int advice_status_hints = 1;
  int advice_commit_before_merge = 1;
+ int advice_resolve_conflict = 1;
 +int advice_implicit_identity = 1;
  
  static struct {
        const char *name;
@@@ -12,7 -12,7 +13,8 @@@
        { "pushnonfastforward", &advice_push_nonfastforward },
        { "statushints", &advice_status_hints },
        { "commitbeforemerge", &advice_commit_before_merge },
+       { "resolveconflict", &advice_resolve_conflict },
 +      { "implicitidentity", &advice_implicit_identity },
  };
  
  int git_default_advice_config(const char *var, const char *value)
  
        return 0;
  }
+ void NORETURN die_resolve_conflict(const char *me)
+ {
+       if (advice_resolve_conflict)
+               /*
+                * Message used both when 'git commit' fails and when
+                * other commands doing a merge do.
+                */
+               die("'%s' is not possible because you have unmerged files.\n"
+                   "Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
+                   "appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
+       else
+               die("'%s' is not possible because you have unmerged files.", me);
+ }
diff --combined advice.h
index 728ab90ef17b4ebccff4ccb662108f8c5fded690,acd5fdde3fbb04acaeec19b5ab1bd463c54eb8b6..9b7a3ad1ca126cb882e87d400820043043061f78
+++ b/advice.h
@@@ -1,11 -1,15 +1,16 @@@
  #ifndef ADVICE_H
  #define ADVICE_H
  
+ #include "git-compat-util.h"
  extern int advice_push_nonfastforward;
  extern int advice_status_hints;
  extern int advice_commit_before_merge;
+ extern int advice_resolve_conflict;
 +extern int advice_implicit_identity;
  
  int git_default_advice_config(const char *var, const char *value);
  
+ extern void NORETURN die_resolve_conflict(const char *me);
  #endif /* ADVICE_H */
diff --combined builtin-commit.c
index 42f11c30ca9e099cbed957107c25200efcc6ecd5,c58712c5393245c62edc082a168c690bd7ecb466..fedcda09d02134a1434098016c3b9829d1185c46
@@@ -24,7 -24,6 +24,7 @@@
  #include "string-list.h"
  #include "rerere.h"
  #include "unpack-trees.h"
 +#include "quote.h"
  
  static const char * const builtin_commit_usage[] = {
        "git commit [options] [--] <filepattern>...",
@@@ -36,20 -35,7 +36,20 @@@ static const char * const builtin_statu
        NULL
  };
  
 -static unsigned char head_sha1[20], merge_head_sha1[20];
 +static const char implicit_ident_advice[] =
 +"Your name and email address were configured automatically based\n"
 +"on your username and hostname. Please check that they are accurate.\n"
 +"You can suppress this message by setting them explicitly:\n"
 +"\n"
 +"    git config --global user.name Your Name\n"
 +"    git config --global user.email you@example.com\n"
 +"\n"
 +"If the identity used for this commit is wrong, you can fix it with:\n"
 +"\n"
 +"    git commit --amend --author='Your Name <you@example.com>'\n";
 +
 +static unsigned char head_sha1[20];
 +
  static char *use_message_buffer;
  static const char commit_editmsg[] = "COMMIT_EDITMSG";
  static struct lock_file index_lock; /* real index */
@@@ -66,7 -52,7 +66,7 @@@ static char *edit_message, *use_message
  static char *author_name, *author_email, *author_date;
  static int all, edit_flag, also, interactive, only, amend, signoff;
  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 -static char *untracked_files_arg;
 +static char *untracked_files_arg, *force_date;
  /*
   * The default commit message cleanup mode will remove the lines
   * beginning with # (shell comments) and leading and trailing
@@@ -81,17 -67,10 +81,17 @@@ static enum 
  } cleanup_mode;
  static char *cleanup_arg;
  
 -static int use_editor = 1, initial_commit, in_merge;
 +static int use_editor = 1, initial_commit, in_merge, include_status = 1;
  static const char *only_include_assumed;
  static struct strbuf message;
  
 +static int null_termination;
 +static enum {
 +      STATUS_FORMAT_LONG,
 +      STATUS_FORMAT_SHORT,
 +      STATUS_FORMAT_PORCELAIN,
 +} status_format = STATUS_FORMAT_LONG;
 +
  static int opt_parse_m(const struct option *opt, const char *arg, int unset)
  {
        struct strbuf *buf = opt->value;
  static struct option builtin_commit_options[] = {
        OPT__QUIET(&quiet),
        OPT__VERBOSE(&verbose),
 -      OPT_GROUP("Commit message options"),
  
 +      OPT_GROUP("Commit message options"),
        OPT_FILENAME('F', "file", &logfile, "read log from file"),
        OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
 +      OPT_STRING(0, "date", &force_date, "DATE", "override date for commit"),
        OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
        OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"),
        OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
        OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
 +      OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 +      OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
 +      /* end commit message options */
  
        OPT_GROUP("Commit contents options"),
        OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
        OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
        OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
        OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
 +      OPT_SET_INT(0, "short", &status_format, "show status concisely",
 +                  STATUS_FORMAT_SHORT),
 +      OPT_SET_INT(0, "porcelain", &status_format,
 +                  "show porcelain output format", STATUS_FORMAT_PORCELAIN),
 +      OPT_BOOLEAN('z', "null", &null_termination,
 +                  "terminate entries with NUL"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
 -      OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 +      /* end commit contents options */
  
        OPT_END()
  };
@@@ -197,15 -166,11 +197,15 @@@ static int list_paths(struct string_lis
  
        for (i = 0; i < active_nr; i++) {
                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))
                        continue;
 -              string_list_insert(ce->name, list);
 +              item = string_list_insert(ce->name, list);
 +              if (ce_skip_worktree(ce))
 +                      item->util = item; /* better a valid pointer than a fake one */
        }
  
        return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
@@@ -218,10 -183,6 +218,10 @@@ static void add_remove_files(struct str
                struct stat st;
                struct string_list_item *p = &(list->items[i]);
  
 +              /* p->util is skip-worktree */
 +              if (p->util)
 +                      continue;
 +
                if (!lstat(p->string, &st)) {
                        if (add_to_cache(p->string, &st, 0))
                                die("updating files failed");
@@@ -258,6 -219,16 +258,16 @@@ static void create_base_index(void
                exit(128); /* We've already reported the error, finish dying */
  }
  
+ static void refresh_cache_or_die(int refresh_flags)
+ {
+       /*
+        * refresh_flags contains REFRESH_QUIET, so the only errors
+        * are for unmerged entries.
+        */
+       if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN))
+               die_resolve_conflict("commit");
+ }
  static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
  {
        int fd;
        if (all || (also && pathspec && *pathspec)) {
                int fd = hold_locked_index(&index_lock, 1);
                add_files_to_cache(also ? prefix : NULL, pathspec, 0);
-               refresh_cache(refresh_flags);
+               refresh_cache_or_die(refresh_flags);
                if (write_cache(fd, active_cache, active_nr) ||
                    close_lock_file(&index_lock))
                        die("unable to write new_index file");
         */
        if (!pathspec || !*pathspec) {
                fd = hold_locked_index(&index_lock, 1);
-               refresh_cache(refresh_flags);
+               refresh_cache_or_die(refresh_flags);
                if (write_cache(fd, active_cache, active_nr) ||
                    commit_locked_index(&index_lock))
                        die("unable to write new_index file");
         */
        commit_style = COMMIT_PARTIAL;
  
 -      if (file_exists(git_path("MERGE_HEAD")))
 +      if (in_merge)
                die("cannot do a partial commit during a merge.");
  
        memset(&partial, 0, sizeof(partial));
  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
                      struct wt_status *s)
  {
 +      unsigned char sha1[20];
 +
        if (s->relative_paths)
                s->prefix = prefix;
  
        s->index_file = index_file;
        s->fp = fp;
        s->nowarn = nowarn;
 +      s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
  
 -      wt_status_print(s);
 +      wt_status_collect(s);
 +
 +      switch (status_format) {
 +      case STATUS_FORMAT_SHORT:
 +              wt_shortstatus_print(s, null_termination);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN:
 +              wt_porcelain_print(s, null_termination);
 +              break;
 +      case STATUS_FORMAT_LONG:
 +              wt_status_print(s);
 +              break;
 +      }
  
        return s->commitable;
  }
@@@ -464,9 -420,6 +474,9 @@@ static void determine_author_info(void
                email = xstrndup(lb + 2, rb - (lb + 2));
        }
  
 +      if (force_date)
 +              date = force_date;
 +
        author_name = name;
        author_email = email;
        author_date = date;
@@@ -604,7 -557,7 +614,7 @@@ static int prepare_to_commit(const cha
  
        /* This checks if committer ident is explicitly given */
        git_committer_info(0);
 -      if (use_editor) {
 +      if (use_editor && include_status) {
                char *author_ident;
                const char *committer_ident;
  
                                author_ident);
                free(author_ident);
  
 -              if (!user_ident_explicitly_given)
 +              if (!user_ident_sufficiently_given())
                        fprintf(fp,
                                "%s"
                                "# Committer: %s\n",
@@@ -792,21 -745,6 +802,21 @@@ static const char *find_author_by_nickn
        die("No existing author found with '%s'", name);
  }
  
 +
 +static void handle_untracked_files_arg(struct wt_status *s)
 +{
 +      if (!untracked_files_arg)
 +              ; /* default already initialized */
 +      else if (!strcmp(untracked_files_arg, "no"))
 +              s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
 +      else if (!strcmp(untracked_files_arg, "normal"))
 +              s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 +      else if (!strcmp(untracked_files_arg, "all"))
 +              s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 +      else
 +              die("Invalid untracked files mode '%s'", untracked_files_arg);
 +}
 +
  static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[],
                                      const char *prefix,
        if (get_sha1("HEAD", head_sha1))
                initial_commit = 1;
  
 -      if (!get_sha1("MERGE_HEAD", merge_head_sha1))
 -              in_merge = 1;
 -
        /* Sanity check options */
        if (amend && initial_commit)
                die("You have nothing to amend.");
        else
                die("Invalid cleanup mode %s", cleanup_arg);
  
 -      if (!untracked_files_arg)
 -              ; /* default already initialized */
 -      else if (!strcmp(untracked_files_arg, "no"))
 -              s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
 -      else if (!strcmp(untracked_files_arg, "normal"))
 -              s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 -      else if (!strcmp(untracked_files_arg, "all"))
 -              s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 -      else
 -              die("Invalid untracked files mode '%s'", untracked_files_arg);
 +      handle_untracked_files_arg(s);
  
        if (all && argc > 0)
                die("Paths with -a does not make sense.");
        else if (interactive && argc > 0)
                die("Paths with --interactive does not make sense.");
  
 +      if (null_termination && status_format == STATUS_FORMAT_LONG)
 +              status_format = STATUS_FORMAT_PORCELAIN;
 +      if (status_format != STATUS_FORMAT_LONG)
 +              dry_run = 1;
 +
        return argc;
  }
  
@@@ -1005,75 -950,26 +1015,75 @@@ static int git_status_config(const cha
  int cmd_status(int argc, const char **argv, const char *prefix)
  {
        struct wt_status s;
 +      unsigned char sha1[20];
 +      static struct option builtin_status_options[] = {
 +              OPT__VERBOSE(&verbose),
 +              OPT_SET_INT('s', "short", &status_format,
 +                          "show status concisely", STATUS_FORMAT_SHORT),
 +              OPT_SET_INT(0, "porcelain", &status_format,
 +                          "show porcelain output format",
 +                          STATUS_FORMAT_PORCELAIN),
 +              OPT_BOOLEAN('z', "null", &null_termination,
 +                          "terminate entries with NUL"),
 +              { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
 +                "mode",
 +                "show untracked files, optional modes: all, normal, no. (Default: all)",
 +                PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
 +              OPT_END(),
 +      };
 +
 +      if (null_termination && status_format == STATUS_FORMAT_LONG)
 +              status_format = STATUS_FORMAT_PORCELAIN;
  
        wt_status_prepare(&s);
        git_config(git_status_config, &s);
 +      in_merge = file_exists(git_path("MERGE_HEAD"));
 +      argc = parse_options(argc, argv, prefix,
 +                           builtin_status_options,
 +                           builtin_status_usage, 0);
 +      handle_untracked_files_arg(&s);
 +
 +      if (*argv)
 +              s.pathspec = get_pathspec(prefix, argv);
 +
 +      read_cache();
 +      refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
 +      s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
 +      s.in_merge = in_merge;
 +      wt_status_collect(&s);
 +
 +      if (s.relative_paths)
 +              s.prefix = prefix;
        if (s.use_color == -1)
                s.use_color = git_use_color_default;
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
  
 -      argc = parse_and_validate_options(argc, argv, builtin_status_usage,
 -                                        prefix, &s);
 -      return dry_run_commit(argc, argv, prefix, &s);
 +      switch (status_format) {
 +      case STATUS_FORMAT_SHORT:
 +              wt_shortstatus_print(&s, null_termination);
 +              break;
 +      case STATUS_FORMAT_PORCELAIN:
 +              wt_porcelain_print(&s, null_termination);
 +              break;
 +      case STATUS_FORMAT_LONG:
 +              s.verbose = verbose;
 +              wt_status_print(&s);
 +              break;
 +      }
 +      return 0;
  }
  
  static void print_summary(const char *prefix, const unsigned char *sha1)
  {
        struct rev_info rev;
        struct commit *commit;
 -      static const char *format = "format:%h] %s";
 +      struct strbuf format = STRBUF_INIT;
        unsigned char junk_sha1[20];
        const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
 +      struct pretty_print_context pctx = {0};
 +      struct strbuf author_ident = STRBUF_INIT;
 +      struct strbuf committer_ident = STRBUF_INIT;
  
        commit = lookup_commit(sha1);
        if (!commit)
        if (!commit || parse_commit(commit))
                die("could not parse newly created commit");
  
 +      strbuf_addstr(&format, "format:%h] %s");
 +
 +      format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
 +      format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
 +      if (strbuf_cmp(&author_ident, &committer_ident)) {
 +              strbuf_addstr(&format, "\n Author: ");
 +              strbuf_addbuf_percentquote(&format, &author_ident);
 +      }
 +      if (!user_ident_sufficiently_given()) {
 +              strbuf_addstr(&format, "\n Committer: ");
 +              strbuf_addbuf_percentquote(&format, &committer_ident);
 +              if (advice_implicit_identity) {
 +                      strbuf_addch(&format, '\n');
 +                      strbuf_addstr(&format, implicit_ident_advice);
 +              }
 +      }
 +      strbuf_release(&author_ident);
 +      strbuf_release(&committer_ident);
 +
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
  
  
        rev.verbose_header = 1;
        rev.show_root_diff = 1;
 -      get_commit_format(format, &rev);
 +      get_commit_format(format.buf, &rev);
        rev.always_show_header = 0;
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 100;
                struct pretty_print_context ctx = {0};
                struct strbuf buf = STRBUF_INIT;
                ctx.date_mode = DATE_NORMAL;
 -              format_commit_message(commit, format + 7, &buf, &ctx);
 +              format_commit_message(commit, format.buf + 7, &buf, &ctx);
                printf("%s\n", buf.buf);
                strbuf_release(&buf);
        }
 +      strbuf_release(&format);
  }
  
  static int git_commit_config(const char *k, const char *v, void *cb)
  
        if (!strcmp(k, "commit.template"))
                return git_config_pathname(&template_file, k, v);
 +      if (!strcmp(k, "commit.status")) {
 +              include_status = git_config_bool(k, v);
 +              return 0;
 +      }
  
        return git_status_config(k, v, s);
  }
@@@ -1164,11 -1036,10 +1174,11 @@@ int cmd_commit(int argc, const char **a
  
        wt_status_prepare(&s);
        git_config(git_commit_config, &s);
 +      in_merge = file_exists(git_path("MERGE_HEAD"));
 +      s.in_merge = in_merge;
  
        if (s.use_color == -1)
                s.use_color = git_use_color_default;
 -
        argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
                                          prefix, &s);
        if (dry_run) {
                     "new_index file. Check that disk is not full or quota is\n"
                     "not exceeded, and then \"git reset HEAD\" to recover.");
  
 -      rerere();
 +      rerere(0);
        run_hook(get_index_file(), "post-commit", NULL);
        if (!quiet)
                print_summary(prefix, commit_sha1);
diff --combined builtin-merge.c
index 82e2a0491a0f3f148ff65851d44481d2809f64de,79a35c305b2e8a93c34f21b764498733446c8419..6f1311414b6ba6c956cdc75a4afc0ed361a0f92d
@@@ -52,7 -52,6 +52,7 @@@ static struct strategy **use_strategies
  static size_t use_strategies_nr, use_strategies_alloc;
  static const char *branch;
  static int verbosity;
 +static int allow_rerere_auto;
  
  static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@@ -171,7 -170,6 +171,7 @@@ static struct option builtin_merge_opti
                "allow fast-forward (default)"),
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
                "abort if fast-forward is not possible"),
 +      OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
                "merge strategy to use", option_parse_strategy),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
@@@ -792,7 -790,7 +792,7 @@@ static int suggest_conflicts(void
                }
        }
        fclose(fp);
 -      rerere();
 +      rerere(allow_rerere_auto);
        printf("Automatic merge failed; "
                        "fix conflicts and then commit the result.\n");
        return 1;
@@@ -849,11 -847,20 +849,20 @@@ int cmd_merge(int argc, const char **ar
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list **remotes = &remoteheads;
  
-       if (file_exists(git_path("MERGE_HEAD")))
-               die("You have not concluded your merge. (MERGE_HEAD exists)");
-       if (read_cache_unmerged())
-               die("You are in the middle of a conflicted merge."
-                               " (index unmerged)");
+       if (read_cache_unmerged()) {
+               die_resolve_conflict("merge");
+       }
+       if (file_exists(git_path("MERGE_HEAD"))) {
+               /*
+                * There is no unmerged entry, don't advise 'git
+                * add/rm <file>', just 'git commit'.
+                */
+               if (advice_resolve_conflict)
+                       die("You have not concluded your merge (MERGE_HEAD exists).\n"
+                           "Please, commit your changes before you can merge.");
+               else
+                       die("You have not concluded your merge (MERGE_HEAD exists).");
+       }
  
        /*
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
diff --combined builtin-revert.c
index 857ca2eefa9719029faf48a143ee4afa846c77a6,d14dde3fbfd5d6b659b56743b8ccb5d54eab9524..8ac86f09434d1dad830c924de8cadcef6705e1e5
@@@ -38,7 -38,6 +38,7 @@@ static const char * const cherry_pick_u
  static int edit, no_replay, no_commit, mainline, signoff;
  static enum { REVERT, CHERRY_PICK } action;
  static struct commit *commit;
 +static int allow_rerere_auto;
  
  static const char *me;
  
@@@ -58,7 -57,6 +58,7 @@@ static void parse_args(int argc, const 
                OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
                OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
 +              OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
                OPT_END(),
        };
  
@@@ -235,6 -233,19 +235,19 @@@ static struct tree *empty_tree(void
        return tree;
  }
  
+ static NORETURN void die_dirty_index(const char *me)
+ {
+       if (read_cache_unmerged()) {
+               die_resolve_conflict(me);
+       } else {
+               if (advice_commit_before_merge)
+                       die("Your local changes would be overwritten by %s.\n"
+                           "Please, commit your changes or stash them to proceed.", me);
+               else
+                       die("Your local changes would be overwritten by %s.\n", me);
+       }
+ }
  static int revert_or_cherry_pick(int argc, const char **argv)
  {
        unsigned char head[20];
                if (get_sha1("HEAD", head))
                        die ("You do not have a valid HEAD");
                if (index_differs_from("HEAD", 0))
-                       die ("Dirty index: cannot %s", me);
+                       die_dirty_index(me);
        }
        discard_cache();
  
                        die ("Error wrapping up %s", defmsg);
                fprintf(stderr, "Automatic %s failed.%s\n",
                        me, help_msg(commit->object.sha1));
 -              rerere();
 +              rerere(allow_rerere_auto);
                exit(1);
        }
        if (commit_lock_file(&msg_file) < 0)