Code

Merge branch 'jc/index-update-if-able' into maint
authorJunio C Hamano <gitster@pobox.com>
Sun, 3 Apr 2011 19:33:05 +0000 (12:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 3 Apr 2011 19:33:05 +0000 (12:33 -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 d71e1e0c9c27c4d03cd02b4deaacec67af33917a,0b6ce2fa3757b0d052ee28d716687a70e1c4ca37..6e32166a297d2e8783e9a24dee6398bb0aa28e98
@@@ -45,9 -45,9 +45,9 @@@ 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"
@@@ -69,7 -69,7 +69,7 @@@ static enum 
  static const char *logfile, *force_author;
  static const char *template_file;
  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;
@@@ -114,18 -114,16 +114,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,
@@@ -461,7 -459,7 +461,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;
  
  
        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;
        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";
        else if (in_merge)
                hook_arg1 = "merge";
  
 +      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)
                die_errno("could not open '%s'", git_path(commit_editmsg));
  
        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;
 -
 +              char *ai_tmp, *ci_tmp;
                if (in_merge)
                        fprintf(fp,
                                "#\n"
                if (only_include_assumed)
                        fprintf(fp, "# %s\n", 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))
 +              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))
                        fprintf(fp,
                                "%s"
                                "# Author:    %s\n",
                                ident_shown++ ? "" : "#\n",
 -                              author_ident);
 -              free(author_ident);
 +                              author_ident->buf);
  
                if (!user_ident_sufficiently_given())
                        fprintf(fp,
                                "%s"
                                "# Committer: %s\n",
                                ident_shown++ ? "" : "#\n",
 -                              committer_ident);
 +                              committer_ident.buf);
  
                if (ident_shown)
                        fprintf(fp, "#\n");
                s->use_color = 0;
                commitable = run_status(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);
  
@@@ -914,7 -863,7 +914,7 @@@ static int parse_and_validate_options(i
        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;
                die("You have nothing to amend.");
        if (amend && in_merge)
                die("You are in the middle of a merge -- cannot amend.");
 -
 +      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)
                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))
 +              commit = lookup_commit_reference_by_name(use_message);
 +              if (!commit)
                        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;
 -              }
 -              out_enc = git_commit_encoding ? git_commit_encoding : utf8;
 -
 -              if (strcmp(out_enc, enc))
 -                      use_message_buffer =
 -                              reencode_string(commit->buffer, out_enc, enc);
 +              out_enc = get_commit_output_encoding();
 +              use_message_buffer = logmsg_reencode(commit, out_enc);
  
                /*
                 * If we failed to reencode the buffer, just copy it
                 */
                if (use_message_buffer == NULL)
                        use_message_buffer = xstrdup(commit->buffer);
 -              if (enc != utf8)
 -                      free(enc);
        }
  
        if (!!also + !!only + !!all + !!interactive > 1)
@@@ -1020,8 -984,6 +1020,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;
@@@ -1086,13 -1048,13 +1086,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;
  
        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;
@@@ -1287,7 -1241,6 +1282,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"));
  
        /* 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;
        }
        }
  
        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,
diff --combined builtin/diff.c
index 42822cd5374dbcf0e63c17078a55284c432ddc77,bab4bd9f57af2ad663c962fde33fa6934f7518b2..d4d80c982e99f9a16e4e5228a963d1f2f1836bfb
@@@ -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,
@@@ -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)
@@@ -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;
                        }
                }
diff --combined cache.h
index 50de992c5c1d51fe6ad50c40c152bd63f84869d0,9a3cc8ef1a0473287f238f66da3005be96a27ab1..342b4100f12d5a56262a13290c27298431e82325
+++ b/cache.h
@@@ -170,26 -170,26 +170,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)
  
@@@ -277,16 -277,9 +277,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)
@@@ -428,7 -421,7 +428,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);
@@@ -445,7 -438,7 +445,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 { \
@@@ -527,6 -520,7 +527,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 *);
@@@ -540,7 -534,6 +541,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;
@@@ -571,7 -564,7 +572,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;
@@@ -608,7 -601,7 +609,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
  };
  
@@@ -676,11 -669,9 +677,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);
  
@@@ -760,8 -751,8 +761,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];
@@@ -862,7 -853,7 +863,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);
@@@ -899,8 -890,7 +900,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 */
@@@ -990,7 -980,6 +991,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 *);
@@@ -1000,7 -989,6 +1001,7 @@@ 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);
@@@ -1009,9 -997,6 +1010,9 @@@ extern int git_env_bool(const char *, i
  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)
@@@ -1071,7 -1056,6 +1072,7 @@@ __attribute__((format (printf, 1, 2))
  extern void trace_printf(const char *format, ...);
  __attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
 +extern void trace_repo_setup(const char *prefix);
  
  /* convert.c */
  /* returns 1 if *dst was used */
@@@ -1097,17 -1081,15 +1098,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 *);
@@@ -1116,7 -1098,6 +1117,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);
@@@ -1130,7 -1111,6 +1131,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 4f2e890b01b0c27ef2e49080e1fd34bf67e969c7,971e27705c37b7a1a5708026043dd48a15e7e87f..0480d9455cec042cf128e4d92bbc44a9dcc3fe32
@@@ -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 */
@@@ -1568,6 -1545,31 +1568,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;