Code

Merge branch 'jc/index-update-if-able'
authorJunio C Hamano <gitster@pobox.com>
Sun, 27 Mar 2011 03:13:16 +0000 (20:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 27 Mar 2011 03:13:16 +0000 (20:13 -0700)
* jc/index-update-if-able:
  update $GIT_INDEX_FILE when there are racily clean entries
  diff/status: refactor opportunistic index update

1  2 
builtin/commit.c
builtin/diff.c
cache.h
read-cache.c

diff --combined builtin/commit.c
index 3979b823ef4227aac641be48c47581c56423b9d7,0b6ce2fa3757b0d052ee28d716687a70e1c4ca37..54b20497b120bc2bf5ee6e4904eca0ca5d83ff4f
@@@ -45,26 -45,18 +45,26 @@@ static const char implicit_ident_advice
  "    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"
 +"After doing this, you may fix the identity used for this commit with:\n"
  "\n"
 -"    git commit --amend --author='Your Name <you@example.com>'\n";
 +"    git commit --amend --reset-author\n";
  
  static const char empty_amend_advice[] =
  "You asked to amend the most recent commit, but doing so would make\n"
  "it empty. You can repeat your command with --allow-empty, or you can\n"
  "remove the commit entirely with \"git reset HEAD^\".\n";
  
 +static const char empty_cherry_pick_advice[] =
 +"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
 +"If you wish to commit it anyway, use:\n"
 +"\n"
 +"    git commit --allow-empty\n"
 +"\n"
 +"Otherwise, please use 'git reset'\n";
 +
  static unsigned char head_sha1[20];
  
 -static char *use_message_buffer;
 +static const char *use_message_buffer;
  static const char commit_editmsg[] = "COMMIT_EDITMSG";
  static struct lock_file index_lock; /* real index */
  static struct lock_file false_lock; /* used only for partial commits */
@@@ -76,13 -68,8 +76,13 @@@ static enum 
  
  static const char *logfile, *force_author;
  static const char *template_file;
 +/*
 + * The _message variables are commit names from which to take
 + * the commit message and/or authorship.
 + */
 +static const char *author_message, *author_message_buffer;
  static char *edit_message, *use_message;
 -static char *author_name, *author_email, *author_date;
 +static char *fixup_message, *squash_message;
  static int all, edit_flag, also, interactive, only, amend, signoff;
  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
  static int no_post_rewrite, allow_empty_message;
@@@ -101,8 -88,7 +101,8 @@@ static enum 
  } cleanup_mode;
  static char *cleanup_arg;
  
 -static int use_editor = 1, initial_commit, in_merge, include_status = 1;
 +static enum commit_whence whence;
 +static int use_editor = 1, initial_commit, include_status = 1;
  static int show_ignored_in_status;
  static const char *only_include_assumed;
  static struct strbuf message;
@@@ -128,18 -114,16 +128,18 @@@ static int opt_parse_m(const struct opt
  }
  
  static struct option builtin_commit_options[] = {
 -      OPT__QUIET(&quiet),
 -      OPT__VERBOSE(&verbose),
 +      OPT__QUIET(&quiet, "suppress summary after successful commit"),
 +      OPT__VERBOSE(&verbose, "show diff in commit message template"),
  
        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_FILENAME('F', "file", &logfile, "read message 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", "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_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
 +      OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
        OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
                    STATUS_FORMAT_SHORT),
        OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
        OPT_SET_INT(0, "porcelain", &status_format,
 -                  "show porcelain output format", STATUS_FORMAT_PORCELAIN),
 +                  "machine-readable output", STATUS_FORMAT_PORCELAIN),
        OPT_BOOLEAN('z', "null", &null_termination,
                    "terminate entries with NUL"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
 -      { 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" },
 +      { 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" },
        /* end commit contents options */
  
        { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
        OPT_END()
  };
  
 +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")))
 +              whence = FROM_CHERRY_PICK;
 +      else
 +              whence = FROM_COMMIT;
 +      if (s)
 +              s->whence = whence;
 +}
 +
 +static const char *whence_s(void)
 +{
 +      char *s = "";
 +
 +      switch (whence) {
 +      case FROM_COMMIT:
 +              break;
 +      case FROM_MERGE:
 +              s = "merge";
 +              break;
 +      case FROM_CHERRY_PICK:
 +              s = "cherry-pick";
 +              break;
 +      }
 +
 +      return s;
 +}
 +
  static void rollback_index_files(void)
  {
        switch (commit_style) {
@@@ -422,8 -376,8 +422,8 @@@ static char *prepare_index(int argc, co
         */
        commit_style = COMMIT_PARTIAL;
  
 -      if (in_merge)
 -              die("cannot do a partial commit during a merge.");
 +      if (whence != FROM_COMMIT)
 +              die("cannot do a partial commit during a %s.", whence_s());
  
        memset(&partial, 0, sizeof(partial));
        partial.strdup_strings = 1;
@@@ -505,7 -459,7 +505,7 @@@ static int is_a_merge(const unsigned ch
  
  static const char sign_off_header[] = "Signed-off-by: ";
  
 -static void determine_author_info(void)
 +static void determine_author_info(struct strbuf *author_ident)
  {
        char *name, *email, *date;
  
        email = getenv("GIT_AUTHOR_EMAIL");
        date = getenv("GIT_AUTHOR_DATE");
  
 -      if (use_message && !renew_authorship) {
 +      if (author_message) {
                const char *a, *lb, *rb, *eol;
  
 -              a = strstr(use_message_buffer, "\nauthor ");
 +              a = strstr(author_message_buffer, "\nauthor ");
                if (!a)
 -                      die("invalid commit: %s", use_message);
 +                      die("invalid commit: %s", author_message);
  
                lb = strchrnul(a + strlen("\nauthor "), '<');
                rb = strchrnul(lb, '>');
                eol = strchrnul(rb, '\n');
                if (!*lb || !*rb || !*eol)
 -                      die("invalid commit: %s", use_message);
 +                      die("invalid commit: %s", author_message);
  
                if (lb == a + strlen("\nauthor "))
                        /* \nauthor <foo@example.com> */
  
        if (force_date)
                date = force_date;
 -
 -      author_name = name;
 -      author_email = email;
 -      author_date = date;
 +      strbuf_addstr(author_ident, fmt_ident(name, email, date,
 +                                            IDENT_ERROR_ON_NO_NAME));
  }
  
  static int ends_rfc2822_footer(struct strbuf *sb)
        return 1;
  }
  
 +static char *cut_ident_timestamp_part(char *string)
 +{
 +      char *ket = strrchr(string, '>');
 +      if (!ket || ket[1] != ' ')
 +              die("Malformed ident string: '%s'", string);
 +      *++ket = '\0';
 +      return ket;
 +}
 +
  static int prepare_to_commit(const char *index_file, const char *prefix,
 -                           struct wt_status *s)
 +                           struct wt_status *s,
 +                           struct strbuf *author_ident)
  {
        struct stat statbuf;
 +      struct strbuf committer_ident = STRBUF_INIT;
        int commitable, saved_color_setting;
        struct strbuf sb = STRBUF_INIT;
        char *buffer;
 -      FILE *fp;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
        int ident_shown = 0;
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
  
 +      if (squash_message) {
 +              /*
 +               * Insert the proper subject line before other commit
 +               * message options add their content.
 +               */
 +              if (use_message && !strcmp(use_message, squash_message))
 +                      strbuf_addstr(&sb, "squash! ");
 +              else {
 +                      struct pretty_print_context ctx = {0};
 +                      struct commit *c;
 +                      c = lookup_commit_reference_by_name(squash_message);
 +                      if (!c)
 +                              die("could not lookup commit %s", squash_message);
 +                      ctx.output_encoding = get_commit_output_encoding();
 +                      format_commit_message(c, "squash! %s\n\n", &sb,
 +                                            &ctx);
 +              }
 +      }
 +
        if (message.len) {
                strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
                strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
                hook_arg1 = "commit";
                hook_arg2 = use_message;
 +      } else if (fixup_message) {
 +              struct pretty_print_context ctx = {0};
 +              struct commit *commit;
 +              commit = lookup_commit_reference_by_name(fixup_message);
 +              if (!commit)
 +                      die("could not lookup commit %s", fixup_message);
 +              ctx.output_encoding = get_commit_output_encoding();
 +              format_commit_message(commit, "fixup! %s\n\n",
 +                                    &sb, &ctx);
 +              hook_arg1 = "message";
        } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
                        die_errno("could not read MERGE_MSG");
                if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
                        die_errno("could not read SQUASH_MSG");
                hook_arg1 = "squash";
 -      } else if (template_file && !stat(template_file, &statbuf)) {
 +      } else if (template_file) {
                if (strbuf_read_file(&sb, template_file, 0) < 0)
                        die_errno("could not read '%s'", template_file);
                hook_arg1 = "template";
        }
  
        /*
 -       * This final case does not modify the template message,
 -       * it just sets the argument to the prepare-commit-msg hook.
 +       * The remaining cases don't modify the template message, but
 +       * just set the argument(s) to the prepare-commit-msg hook.
         */
 -      else if (in_merge)
 +      else if (whence == FROM_MERGE)
                hook_arg1 = "merge";
 +      else if (whence == FROM_CHERRY_PICK) {
 +              hook_arg1 = "commit";
 +              hook_arg2 = "CHERRY_PICK_HEAD";
 +      }
 +
 +      if (squash_message) {
 +              /*
 +               * If squash_commit was used for the commit subject,
 +               * then we're possibly hijacking other commit log options.
 +               * Reset the hook args to tell the real story.
 +               */
 +              hook_arg1 = "message";
 +              hook_arg2 = "";
 +      }
  
 -      fp = fopen(git_path(commit_editmsg), "w");
 -      if (fp == NULL)
 +      s->fp = fopen(git_path(commit_editmsg), "w");
 +      if (s->fp == NULL)
                die_errno("could not open '%s'", git_path(commit_editmsg));
  
        if (cleanup_mode != CLEANUP_NONE)
                strbuf_release(&sob);
        }
  
 -      if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
 +      if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno("could not write commit template");
  
        strbuf_release(&sb);
  
 -      determine_author_info();
 +      /* This checks and barfs if author is badly specified */
 +      determine_author_info(author_ident);
  
        /* This checks if committer ident is explicitly given */
 -      git_committer_info(0);
 +      strbuf_addstr(&committer_ident, git_committer_info(0));
        if (use_editor && include_status) {
 -              char *author_ident;
 -              const char *committer_ident;
 -
 -              if (in_merge)
 -                      fprintf(fp,
 -                              "#\n"
 -                              "# It looks like you may be committing a MERGE.\n"
 -                              "# If this is not correct, please remove the file\n"
 -                              "#      %s\n"
 -                              "# and try again.\n"
 -                              "#\n",
 -                              git_path("MERGE_HEAD"));
 -
 -              fprintf(fp,
 -                      "\n"
 -                      "# Please enter the commit message for your changes.");
 +              char *ai_tmp, *ci_tmp;
 +              if (whence != FROM_COMMIT)
 +                      status_printf_ln(s, GIT_COLOR_NORMAL,
 +                              "\n"
 +                              "It looks like you may be committing a %s.\n"
 +                              "If this is not correct, please remove the file\n"
 +                              "       %s\n"
 +                              "and try again.\n"
 +                              "",
 +                              whence_s(),
 +                              git_path(whence == FROM_MERGE
 +                                       ? "MERGE_HEAD"
 +                                       : "CHERRY_PICK_HEAD"));
 +
 +              fprintf(s->fp, "\n");
 +              status_printf(s, GIT_COLOR_NORMAL,
 +                      "Please enter the commit message for your changes.");
                if (cleanup_mode == CLEANUP_ALL)
 -                      fprintf(fp,
 +                      status_printf_more(s, GIT_COLOR_NORMAL,
                                " Lines starting\n"
 -                              "with '#' will be ignored, and an empty"
 +                              "with '#' will be ignored, and an empty"
                                " message aborts the commit.\n");
                else /* CLEANUP_SPACE, that is. */
 -                      fprintf(fp,
 +                      status_printf_more(s, GIT_COLOR_NORMAL,
                                " Lines starting\n"
 -                              "with '#' will be kept; you may remove them"
 +                              "with '#' will be kept; you may remove them"
                                " yourself if you want to.\n"
 -                              "An empty message aborts the commit.\n");
 +                              "An empty message aborts the commit.\n");
                if (only_include_assumed)
 -                      fprintf(fp, "# %s\n", only_include_assumed);
 +                      status_printf_ln(s, GIT_COLOR_NORMAL,
 +                                      "%s", only_include_assumed);
  
 -              author_ident = xstrdup(fmt_name(author_name, author_email));
 -              committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"),
 -                                         getenv("GIT_COMMITTER_EMAIL"));
 -              if (strcmp(author_ident, committer_ident))
 -                      fprintf(fp,
 +              ai_tmp = cut_ident_timestamp_part(author_ident->buf);
 +              ci_tmp = cut_ident_timestamp_part(committer_ident.buf);
 +              if (strcmp(author_ident->buf, committer_ident.buf))
 +                      status_printf_ln(s, GIT_COLOR_NORMAL,
                                "%s"
 -                              "# Author:    %s\n",
 -                              ident_shown++ ? "" : "#\n",
 -                              author_ident);
 -              free(author_ident);
 +                              "Author:    %s",
 +                              ident_shown++ ? "" : "\n",
 +                              author_ident->buf);
  
                if (!user_ident_sufficiently_given())
 -                      fprintf(fp,
 +                      status_printf_ln(s, GIT_COLOR_NORMAL,
                                "%s"
 -                              "# Committer: %s\n",
 -                              ident_shown++ ? "" : "#\n",
 -                              committer_ident);
 +                              "Committer: %s",
 +                              ident_shown++ ? "" : "\n",
 +                              committer_ident.buf);
  
                if (ident_shown)
 -                      fprintf(fp, "#\n");
 +                      status_printf_ln(s, GIT_COLOR_NORMAL, "");
  
                saved_color_setting = s->use_color;
                s->use_color = 0;
 -              commitable = run_status(fp, index_file, prefix, 1, s);
 +              commitable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
 +
 +              *ai_tmp = ' ';
 +              *ci_tmp = ' ';
        } else {
                unsigned char sha1[20];
                const char *parent = "HEAD";
                else
                        commitable = index_differs_from(parent, 0);
        }
 +      strbuf_release(&committer_ident);
  
 -      fclose(fp);
 +      fclose(s->fp);
  
 -      if (!commitable && !in_merge && !allow_empty &&
 +      /*
 +       * Reject an attempt to record a non-merge empty commit without
 +       * explicit --allow-empty. In the cherry-pick case, it may be
 +       * empty due to conflict resolution, which the user should okay.
 +       */
 +      if (!commitable && whence != FROM_MERGE && !allow_empty &&
            !(amend && is_a_merge(head_sha1))) {
                run_status(stdout, index_file, prefix, 0, s);
                if (amend)
                        fputs(empty_amend_advice, stderr);
 +              else if (whence == FROM_CHERRY_PICK)
 +                      fputs(empty_cherry_pick_advice, stderr);
                return 0;
        }
  
@@@ -956,28 -847,6 +956,28 @@@ static void handle_untracked_files_arg(
                die("Invalid untracked files mode '%s'", untracked_files_arg);
  }
  
 +static const char *read_commit_message(const char *name)
 +{
 +      const char *out_enc, *out;
 +      struct commit *commit;
 +
 +      commit = lookup_commit_reference_by_name(name);
 +      if (!commit)
 +              die("could not lookup commit %s", name);
 +      out_enc = get_commit_output_encoding();
 +      out = logmsg_reencode(commit, out_enc);
 +
 +      /*
 +       * If we failed to reencode the buffer, just copy it
 +       * byte for byte so the user can try to fix it up.
 +       * This also handles the case where input and output
 +       * encodings are identical.
 +       */
 +      if (out == NULL)
 +              out = xstrdup(commit->buffer);
 +      return out;
 +}
 +
  static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[],
                                      const char *prefix,
        if (force_author && renew_authorship)
                die("Using both --reset-author and --author does not make sense");
  
 -      if (logfile || message.len || use_message)
 +      if (logfile || message.len || use_message || fixup_message)
                use_editor = 0;
        if (edit_flag)
                use_editor = 1;
        /* Sanity check options */
        if (amend && initial_commit)
                die("You have nothing to amend.");
 -      if (amend && in_merge)
 -              die("You are in the middle of a merge -- cannot amend.");
 -
 +      if (amend && whence != FROM_COMMIT)
 +              die("You are in the middle of a %s -- cannot amend.", whence_s());
 +      if (fixup_message && squash_message)
 +              die("Options --squash and --fixup cannot be used together");
        if (use_message)
                f++;
        if (edit_message)
                f++;
 +      if (fixup_message)
 +              f++;
        if (logfile)
                f++;
        if (f > 1)
 -              die("Only one of -c/-C/-F can be used.");
 +              die("Only one of -c/-C/-F/--fixup can be used.");
        if (message.len && f > 0)
 -              die("Option -m cannot be combined with -c/-C/-F.");
 +              die("Option -m cannot be combined with -c/-C/-F/--fixup.");
        if (edit_message)
                use_message = edit_message;
 -      if (amend && !use_message)
 +      if (amend && !use_message && !fixup_message)
                use_message = "HEAD";
 -      if (!use_message && renew_authorship)
 +      if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)
                die("--reset-author can be used only with -C, -c or --amend.");
        if (use_message) {
 -              unsigned char sha1[20];
 -              static char utf8[] = "UTF-8";
 -              const char *out_enc;
 -              char *enc, *end;
 -              struct commit *commit;
 -
 -              if (get_sha1(use_message, sha1))
 -                      die("could not lookup commit %s", use_message);
 -              commit = lookup_commit_reference(sha1);
 -              if (!commit || parse_commit(commit))
 -                      die("could not parse commit %s", use_message);
 -
 -              enc = strstr(commit->buffer, "\nencoding");
 -              if (enc) {
 -                      end = strchr(enc + 10, '\n');
 -                      enc = xstrndup(enc + 10, end - (enc + 10));
 -              } else {
 -                      enc = utf8;
 +              use_message_buffer = read_commit_message(use_message);
 +              if (!renew_authorship) {
 +                      author_message = use_message;
 +                      author_message_buffer = use_message_buffer;
                }
 -              out_enc = git_commit_encoding ? git_commit_encoding : utf8;
 -
 -              if (strcmp(out_enc, enc))
 -                      use_message_buffer =
 -                              reencode_string(commit->buffer, out_enc, enc);
 -
 -              /*
 -               * If we failed to reencode the buffer, just copy it
 -               * byte for byte so the user can try to fix it up.
 -               * This also handles the case where input and output
 -               * encodings are identical.
 -               */
 -              if (use_message_buffer == NULL)
 -                      use_message_buffer = xstrdup(commit->buffer);
 -              if (enc != utf8)
 -                      free(enc);
 +      }
 +      if (whence == FROM_CHERRY_PICK && !renew_authorship) {
 +              author_message = "CHERRY_PICK_HEAD";
 +              author_message_buffer = read_commit_message(author_message);
        }
  
        if (!!also + !!only + !!all + !!interactive > 1)
@@@ -1092,8 -984,6 +1092,8 @@@ static int parse_status_slot(const cha
  {
        if (!strcasecmp(var+offset, "header"))
                return WT_STATUS_HEADER;
 +      if (!strcasecmp(var+offset, "branch"))
 +              return WT_STATUS_ONBRANCH;
        if (!strcasecmp(var+offset, "updated")
                || !strcasecmp(var+offset, "added"))
                return WT_STATUS_UPDATED;
@@@ -1158,13 -1048,13 +1158,13 @@@ int cmd_status(int argc, const char **a
        int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
 -              OPT__VERBOSE(&verbose),
 +              OPT__VERBOSE(&verbose, "be verbose"),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
                OPT_BOOLEAN('b', "branch", &status_show_branch,
                            "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
 -                          "show porcelain output format",
 +                          "machine-readable output",
                            STATUS_FORMAT_PORCELAIN),
                OPT_BOOLEAN('z', "null", &null_termination,
                            "terminate entries with NUL"),
                OPT_END(),
        };
  
 +      if (argc == 2 && !strcmp(argv[1], "-h"))
 +              usage_with_options(builtin_status_usage, builtin_status_options);
 +
        if (null_termination && status_format == STATUS_FORMAT_LONG)
                status_format = STATUS_FORMAT_PORCELAIN;
  
        wt_status_prepare(&s);
        gitmodules_config();
        git_config(git_status_config, &s);
 -      in_merge = file_exists(git_path("MERGE_HEAD"));
 +      determine_whence(&s);
        argc = parse_options(argc, argv, prefix,
                             builtin_status_options,
                             builtin_status_usage, 0);
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
  
        fd = hold_locked_index(&index_lock, 0);
-       if (0 <= fd) {
-               if (active_cache_changed &&
-                   !write_cache(fd, active_cache, active_nr))
-                       commit_locked_index(&index_lock);
-               else
-                       rollback_lock_file(&index_lock);
-       }
+       if (0 <= fd)
+               update_index_if_able(&the_index, &index_lock);
  
        s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
 -      s.in_merge = in_merge;
        s.ignore_submodule_arg = ignore_submodule_arg;
        wt_status_collect(&s);
  
@@@ -1286,6 -1169,7 +1281,6 @@@ static void print_summary(const char *p
        get_commit_format(format.buf, &rev);
        rev.always_show_header = 0;
        rev.diffopt.detect_rename = 1;
 -      rev.diffopt.rename_limit = 100;
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
  
@@@ -1357,7 -1241,6 +1352,7 @@@ static int run_rewrite_hook(const unsig
  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;
        unsigned char commit_sha1[20];
        int allow_fast_forward = 1;
        struct wt_status s;
  
 +      if (argc == 2 && !strcmp(argv[1], "-h"))
 +              usage_with_options(builtin_commit_usage, builtin_commit_options);
 +
        wt_status_prepare(&s);
        git_config(git_commit_config, &s);
 -      in_merge = file_exists(git_path("MERGE_HEAD"));
 -      s.in_merge = in_merge;
 +      determine_whence(&s);
  
        if (s.use_color == -1)
                s.use_color = git_use_color_default;
  
        /* Set up everything for writing the commit object.  This includes
           running hooks, writing the trees, and interacting with the user.  */
 -      if (!prepare_to_commit(index_file, prefix, &s)) {
 +      if (!prepare_to_commit(index_file, prefix, &s, &author_ident)) {
                rollback_index_files();
                return 1;
        }
  
                for (c = commit->parents; c; c = c->next)
                        pptr = &commit_list_insert(c->item, pptr)->next;
 -      } else if (in_merge) {
 +      } else if (whence == FROM_MERGE) {
                struct strbuf m = STRBUF_INIT;
                FILE *fp;
  
                        parents = reduce_heads(parents);
        } else {
                if (!reflog_msg)
 -                      reflog_msg = "commit";
 +                      reflog_msg = (whence == FROM_CHERRY_PICK)
 +                                      ? "commit (cherry-pick)"
 +                                      : "commit";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
        }
  
        }
  
        if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
 -                      fmt_ident(author_name, author_email, author_date,
 -                              IDENT_ERROR_ON_NO_NAME))) {
 +                      author_ident.buf)) {
                rollback_index_files();
                die("failed to write commit object");
        }
 +      strbuf_release(&author_ident);
  
        ref_lock = lock_any_ref_for_update("HEAD",
                                           initial_commit ? NULL : head_sha1,
                die("cannot update HEAD ref");
        }
  
 +      unlink(git_path("CHERRY_PICK_HEAD"));
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
        unlink(git_path("MERGE_MODE"));
diff --combined builtin/diff.c
index 4c9deb28ec15d0c2adae795cc2c177b24256e585,bab4bd9f57af2ad663c962fde33fa6934f7518b2..655a013ed05edd369225aa9ee9f39f347d687172
@@@ -22,7 -22,7 +22,7 @@@ struct blobinfo 
  };
  
  static const char builtin_diff_usage[] =
 -"git diff <options> <rev>{0,2} -- <path>*";
 +"git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
  
  static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
@@@ -135,7 -135,7 +135,7 @@@ static int builtin_diff_index(struct re
            revs->max_count != -1 || revs->min_age != -1 ||
            revs->max_age != -1)
                usage(builtin_diff_usage);
 -      if (read_cache_preload(revs->diffopt.paths) < 0) {
 +      if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
                perror("read_cache_preload");
                return -1;
        }
@@@ -197,12 -197,7 +197,7 @@@ static void refresh_index_quietly(void
        discard_cache();
        read_cache();
        refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
-       if (active_cache_changed &&
-           !write_cache(fd, active_cache, active_nr))
-               commit_locked_index(lock_file);
-       rollback_lock_file(lock_file);
+       update_index_if_able(&the_index, lock_file);
  }
  
  static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
                revs->combine_merges = revs->dense_combined_merges = 1;
  
        setup_work_tree();
 -      if (read_cache_preload(revs->diffopt.paths) < 0) {
 +      if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
                perror("read_cache_preload");
                return -1;
        }
@@@ -330,11 -325,8 +325,11 @@@ int cmd_diff(int argc, const char **arg
                        else if (!strcmp(arg, "--cached") ||
                                 !strcmp(arg, "--staged")) {
                                add_head_to_pending(&rev);
 -                              if (!rev.pending.nr)
 -                                      die("No HEAD commit to compare with (yet)");
 +                              if (!rev.pending.nr) {
 +                                      struct tree *tree;
 +                                      tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN);
 +                                      add_pending_object(&rev, &tree->object, "HEAD");
 +                              }
                                break;
                        }
                }
                }
                die("unhandled object '%s' given.", name);
        }
 -      if (rev.prune_data) {
 -              const char **pathspec = rev.prune_data;
 -              while (*pathspec) {
 -                      if (!path)
 -                              path = *pathspec;
 -                      paths++;
 -                      pathspec++;
 -              }
 +      if (rev.prune_data.nr) {
 +              if (!path)
 +                      path = rev.prune_data.items[0].match;
 +              paths += rev.prune_data.nr;
        }
  
        /*
diff --combined cache.h
index abef5b62adb1c43b078189627341d96d4507e67c,9a3cc8ef1a0473287f238f66da3005be96a27ab1..9f06d215790ef98c2138ab26c1a9b4ddafd33c55
+++ b/cache.h
@@@ -5,7 -5,6 +5,7 @@@
  #include "strbuf.h"
  #include "hash.h"
  #include "advice.h"
 +#include "gettext.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
@@@ -171,26 -170,26 +171,26 @@@ struct cache_entry 
   *
   * In-memory only flags
   */
 -#define CE_UPDATE    (0x10000)
 -#define CE_REMOVE    (0x20000)
 -#define CE_UPTODATE  (0x40000)
 -#define CE_ADDED     (0x80000)
 +#define CE_UPDATE            (1 << 16)
 +#define CE_REMOVE            (1 << 17)
 +#define CE_UPTODATE          (1 << 18)
 +#define CE_ADDED             (1 << 19)
  
 -#define CE_HASHED    (0x100000)
 -#define CE_UNHASHED  (0x200000)
 -#define CE_CONFLICTED (0x800000)
 +#define CE_HASHED            (1 << 20)
 +#define CE_UNHASHED          (1 << 21)
 +#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
 +#define CE_CONFLICTED        (1 << 23)
  
 -#define CE_WT_REMOVE (0x400000) /* remove in work directory */
 -
 -#define CE_UNPACKED  (0x1000000)
 +#define CE_UNPACKED          (1 << 24)
 +#define CE_NEW_SKIP_WORKTREE (1 << 25)
  
  /*
   * Extended on-disk flags
   */
 -#define CE_INTENT_TO_ADD 0x20000000
 -#define CE_SKIP_WORKTREE 0x40000000
 +#define CE_INTENT_TO_ADD     (1 << 29)
 +#define CE_SKIP_WORKTREE     (1 << 30)
  /* CE_EXTENDED2 is for future extension */
 -#define CE_EXTENDED2 0x80000000
 +#define CE_EXTENDED2         (1 << 31)
  
  #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
@@@ -278,16 -277,9 +278,16 @@@ static inline int ce_to_dtype(const str
        else
                return DT_UNKNOWN;
  }
 -#define canon_mode(mode) \
 -      (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 -      S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 +static inline unsigned int canon_mode(unsigned int mode)
 +{
 +      if (S_ISREG(mode))
 +              return S_IFREG | ce_permissions(mode);
 +      if (S_ISLNK(mode))
 +              return S_IFLNK;
 +      if (S_ISDIR(mode))
 +              return S_IFDIR;
 +      return S_IFGITLINK;
 +}
  
  #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) flexible_size(cache_entry,len)
@@@ -429,7 -421,7 +429,7 @@@ extern const char **get_pathspec(const 
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
 -extern const char *prefix_path(const char *prefix, int len, const char *path);
 +extern char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix, const char *name);
@@@ -446,7 -438,7 +446,7 @@@ extern int init_db(const char *template
   * at least 'nr' entries; the number of entries currently allocated
   * is 'alloc', using the standard growing factor alloc_nr() macro.
   *
 - * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
 + * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
   */
  #define ALLOC_GROW(x, nr, alloc) \
        do { \
@@@ -501,23 -493,8 +501,23 @@@ extern int index_name_is_other(const st
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
 -extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 +struct pathspec {
 +      const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
 +      int nr;
 +      unsigned int has_wildcard:1;
 +      unsigned int recursive:1;
 +      int max_depth;
 +      struct pathspec_item {
 +              const char *match;
 +              int len;
 +              unsigned int has_wildcard:1;
 +      } *items;
 +};
 +
 +extern int init_pathspec(struct pathspec *, const char **);
 +extern void free_pathspec(struct pathspec *);
 +extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 +extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
 +extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
  
  struct lock_file {
        struct lock_file *next;
@@@ -543,6 -520,7 +543,7 @@@ extern NORETURN void unable_to_lock_ind
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
+ extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern int commit_locked_index(struct lock_file *);
@@@ -556,7 -534,6 +557,7 @@@ extern int trust_executable_bit
  extern int trust_ctime;
  extern int quote_path_fully;
  extern int has_symlinks;
 +extern int minimum_abbrev, default_abbrev;
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
@@@ -587,7 -564,7 +588,7 @@@ extern enum safe_crlf safe_crlf
  enum auto_crlf {
        AUTO_CRLF_FALSE = 0,
        AUTO_CRLF_TRUE = 1,
 -      AUTO_CRLF_INPUT = -1,
 +      AUTO_CRLF_INPUT = -1
  };
  
  extern enum auto_crlf auto_crlf;
@@@ -624,7 -601,7 +625,7 @@@ enum rebase_setup_type 
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 -      PUSH_DEFAULT_TRACKING,
 +      PUSH_DEFAULT_UPSTREAM,
        PUSH_DEFAULT_CURRENT
  };
  
@@@ -692,11 -669,9 +693,11 @@@ static inline void hashclr(unsigned cha
  
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
 -#define EMPTY_TREE_SHA1_BIN \
 +#define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
 +#define EMPTY_TREE_SHA1_BIN \
 +       ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
@@@ -726,7 -701,6 +727,7 @@@ int set_shared_perm(const char *path, i
  #define adjust_shared_perm(path) set_shared_perm((path), 0)
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
 +int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
        return path[0] == '/' || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
 -const char *make_absolute_path(const char *path);
 -const char *make_nonrelative_path(const char *path);
 -const char *make_relative_path(const char *abs, const char *base);
 +const char *real_path(const char *path);
 +const char *absolute_path(const char *path);
 +const char *relative_path(const char *abs, const char *base);
  int normalize_path_copy(char *dst, const char *src);
  int longest_ancestor_length(const char *path, const char *prefix_list);
  char *strip_path_suffix(const char *path, const char *suffix);
@@@ -777,8 -751,8 +778,8 @@@ static inline unsigned int hexval(unsig
  }
  
  /* Convert to/from hex/sha1 representation */
 -#define MINIMUM_ABBREV 4
 -#define DEFAULT_ABBREV 7
 +#define MINIMUM_ABBREV minimum_abbrev
 +#define DEFAULT_ABBREV default_abbrev
  
  struct object_context {
        unsigned char tree[20];
@@@ -879,7 -853,7 +880,7 @@@ struct cache_def 
  
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 -extern int has_symlink_or_noent_leading_path(const char *name, int len);
 +extern int check_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
@@@ -916,8 -890,7 +917,8 @@@ extern struct packed_git 
        time_t mtime;
        int pack_fd;
        unsigned pack_local:1,
 -               pack_keep:1;
 +               pack_keep:1,
 +               do_not_close:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
@@@ -1007,7 -980,6 +1008,7 @@@ extern int git_config_parse_parameter(c
  extern int git_config_parse_environment(void);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
 +extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
@@@ -1017,17 -989,14 +1018,17 @@@ extern int git_config_maybe_bool(const 
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
  extern int git_config_set(const char *, const char *);
 +extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
 -extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
 +extern const char *get_log_output_encoding(void);
 +extern const char *get_commit_output_encoding(void);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1085,14 -1054,8 +1086,14 @@@ extern void alloc_report(void)
  /* trace.c */
  __attribute__((format (printf, 1, 2)))
  extern void trace_printf(const char *format, ...);
 +extern void trace_vprintf(const char *key, const char *format, va_list ap);
  __attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
 +extern void trace_repo_setup(const char *prefix);
 +extern int trace_want(const char *key);
 +extern void trace_strbuf(const char *key, const struct strbuf *buf);
 +
 +void packet_trace_identity(const char *prog);
  
  /* convert.c */
  /* returns 1 if *dst was used */
@@@ -1118,17 -1081,15 +1119,17 @@@ void shift_tree_by(const unsigned char 
  /*
   * whitespace rules.
   * used by both diff and apply
 + * last two digits are tab width
   */
 -#define WS_BLANK_AT_EOL         01
 -#define WS_SPACE_BEFORE_TAB   02
 -#define WS_INDENT_WITH_NON_TAB        04
 -#define WS_CR_AT_EOL           010
 -#define WS_BLANK_AT_EOF        020
 -#define WS_TAB_IN_INDENT       040
 +#define WS_BLANK_AT_EOL         0100
 +#define WS_SPACE_BEFORE_TAB     0200
 +#define WS_INDENT_WITH_NON_TAB  0400
 +#define WS_CR_AT_EOL           01000
 +#define WS_BLANK_AT_EOF        02000
 +#define WS_TAB_IN_INDENT       04000
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 -#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 +#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
 +#define WS_TAB_WIDTH_MASK        077
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
@@@ -1137,7 -1098,6 +1138,7 @@@ extern void ws_check_emit(const char *l
  extern char *whitespace_error_string(unsigned ws);
  extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 +#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
@@@ -1151,7 -1111,6 +1152,7 @@@ const char *split_cmdline_strerror(int 
  /* git.c */
  struct startup_info {
        int have_repository;
 +      const char *prefix;
  };
  extern struct startup_info *startup_info;
  
diff --combined read-cache.c
index 98d526bd48d2a0e764dd0efb4c6efe4965174c9a,971e27705c37b7a1a5708026043dd48a15e7e87f..f38471cac3ac57d8d52429cde2dcc4cf5b92557b
@@@ -92,7 -92,7 +92,7 @@@ static int ce_compare_data(struct cache
  
        if (fd >= 0) {
                unsigned char sha1[20];
 -              if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name))
 +              if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name, 0))
                        match = hashcmp(sha1, ce->sha1);
                /* index_fd() closed the file descriptor already */
        }
@@@ -608,29 -608,6 +608,29 @@@ int add_to_index(struct index_state *is
                ce->ce_mode = ce_mode_from_stat(ent, st_mode);
        }
  
 +      /* When core.ignorecase=true, determine if a directory of the same name but differing
 +       * case already exists within the Git repository.  If it does, ensure the directory
 +       * case of the file being added to the repository matches (is folded into) the existing
 +       * entry's directory case.
 +       */
 +      if (ignore_case) {
 +              const char *startPtr = ce->name;
 +              const char *ptr = startPtr;
 +              while (*ptr) {
 +                      while (*ptr && *ptr != '/')
 +                              ++ptr;
 +                      if (*ptr == '/') {
 +                              struct cache_entry *foundce;
 +                              ++ptr;
 +                              foundce = index_name_exists(&the_index, ce->name, ptr - ce->name, ignore_case);
 +                              if (foundce) {
 +                                      memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
 +                                      startPtr = ptr;
 +                              }
 +                      }
 +              }
 +      }
 +
        alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);
        if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
                /* Nothing changed, really */
@@@ -706,9 -683,30 +706,9 @@@ int ce_same_name(struct cache_entry *a
        return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
  }
  
 -int ce_path_match(const struct cache_entry *ce, const char **pathspec)
 +int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec)
  {
 -      const char *match, *name;
 -      int len;
 -
 -      if (!pathspec)
 -              return 1;
 -
 -      len = ce_namelen(ce);
 -      name = ce->name;
 -      while ((match = *pathspec++) != NULL) {
 -              int matchlen = strlen(match);
 -              if (matchlen > len)
 -                      continue;
 -              if (memcmp(name, match, matchlen))
 -                      continue;
 -              if (matchlen && name[matchlen-1] == '/')
 -                      return 1;
 -              if (name[matchlen] == '/' || !name[matchlen])
 -                      return 1;
 -              if (!matchlen)
 -                      return 1;
 -      }
 -      return 0;
 +      return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL);
  }
  
  /*
@@@ -1083,7 -1081,7 +1083,7 @@@ static struct cache_entry *refresh_cach
  }
  
  static void show_file(const char * fmt, const char * name, int in_porcelain,
 -                    int * first, char *header_msg)
 +                    int * first, const char *header_msg)
  {
        if (in_porcelain && *first && header_msg) {
                printf("%s\n", header_msg);
  }
  
  int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
 -                char *seen, char *header_msg)
 +                char *seen, const char *header_msg)
  {
        int i;
        int has_errors = 0;
@@@ -1547,6 -1545,31 +1547,31 @@@ static int ce_write_entry(git_SHA_CTX *
        return result;
  }
  
+ static int has_racy_timestamp(struct index_state *istate)
+ {
+       int entries = istate->cache_nr;
+       int i;
+       for (i = 0; i < entries; i++) {
+               struct cache_entry *ce = istate->cache[i];
+               if (is_racy_timestamp(istate, ce))
+                       return 1;
+       }
+       return 0;
+ }
+ /*
+  * Opportunisticly update the index but do not complain if we can't
+  */
+ void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
+ {
+       if ((istate->cache_changed || has_racy_timestamp(istate)) &&
+           !write_index(istate, lockfile->fd))
+               commit_locked_index(lockfile);
+       else
+               rollback_lock_file(lockfile);
+ }
  int write_index(struct index_state *istate, int newfd)
  {
        git_SHA_CTX c;