Merge branch 'tg/add-chmod+x-fix'
authorJunio C Hamano <gitster@pobox.com>
Mon, 26 Sep 2016 23:09:20 +0000 (16:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 26 Sep 2016 23:09:20 +0000 (16:09 -0700)
"git add --chmod=+x <pathspec>" added recently only toggled the
executable bit for paths that are either new or modified. This has
been corrected to flip the executable bit for all paths that match
the given pathspec.

* tg/add-chmod+x-fix:
t3700-add: do not check working tree file mode without POSIXPERM
t3700-add: create subdirectory gently
add: modify already added files when --chmod is given
read-cache: introduce chmod_index_entry
update-index: add test for chmod flags

1  2 
builtin/checkout.c
builtin/commit.c
builtin/update-index.c
cache.h
read-cache.c
diff --combined builtin/checkout.c
index efbda8e63bcd8b388d5003cd85d8fde65169ec27,3398c61e9a64ab686bf59fb027277f3de578b5fb..f4b24a432a5df8e25891134b1632b3e5c6d7f0d2
@@@ -76,7 -76,7 +76,7 @@@ static int update_some(const unsigned c
  
        len = base->len + strlen(pathname);
        ce = xcalloc(1, cache_entry_size(len));
 -      hashcpy(ce->sha1, sha1);
 +      hashcpy(ce->oid.hash, sha1);
        memcpy(ce->name, base->buf, base->len);
        memcpy(ce->name + base->len, pathname, len - base->len);
        ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
@@@ -92,7 -92,7 +92,7 @@@
        if (pos >= 0) {
                struct cache_entry *old = active_cache[pos];
                if (ce->ce_mode == old->ce_mode &&
 -                  !hashcmp(ce->sha1, old->sha1)) {
 +                  !oidcmp(&ce->oid, &old->oid)) {
                        old->ce_flags |= CE_UPDATE;
                        free(ce);
                        return 0;
@@@ -154,8 -154,8 +154,8 @@@ static int check_stages(unsigned stages
        return 0;
  }
  
 -static int checkout_stage(int stage, struct cache_entry *ce, int pos,
 -                        struct checkout *state)
 +static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
 +                        const struct checkout *state)
  {
        while (pos < active_nr &&
               !strcmp(active_cache[pos]->name, ce->name)) {
                return error(_("path '%s' does not have their version"), ce->name);
  }
  
 -static int checkout_merged(int pos, struct checkout *state)
 +static int checkout_merged(int pos, const struct checkout *state)
  {
        struct cache_entry *ce = active_cache[pos];
        const char *path = ce->name;
        mmfile_t ancestor, ours, theirs;
        int status;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        mmbuffer_t result_buf;
 -      unsigned char threeway[3][20];
 +      struct object_id threeway[3];
        unsigned mode = 0;
  
        memset(threeway, 0, sizeof(threeway));
                stage = ce_stage(ce);
                if (!stage || strcmp(path, ce->name))
                        break;
 -              hashcpy(threeway[stage - 1], ce->sha1);
 +              oidcpy(&threeway[stage - 1], &ce->oid);
                if (stage == 2)
                        mode = create_ce_mode(ce->ce_mode);
                pos++;
                ce = active_cache[pos];
        }
 -      if (is_null_sha1(threeway[1]) || is_null_sha1(threeway[2]))
 +      if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
                return error(_("path '%s' does not have necessary versions"), path);
  
 -      read_mmblob(&ancestor, threeway[0]);
 -      read_mmblob(&ours, threeway[1]);
 -      read_mmblob(&theirs, threeway[2]);
 +      read_mmblob(&ancestor, &threeway[0]);
 +      read_mmblob(&ours, &threeway[1]);
 +      read_mmblob(&theirs, &threeway[2]);
  
        /*
         * NEEDSWORK: re-create conflicts from merges with
         * object database even when it may contain conflicts).
         */
        if (write_sha1_file(result_buf.ptr, result_buf.size,
 -                          blob_type, sha1))
 +                          blob_type, oid.hash))
                die(_("Unable to add merge result for '%s'"), path);
 -      ce = make_cache_entry(mode, sha1, path, 2, 0);
 +      ce = make_cache_entry(mode, oid.hash, path, 2, 0);
        if (!ce)
                die(_("make_cache_entry failed for path '%s'"), path);
        status = checkout_entry(ce, state, NULL);
@@@ -241,7 -241,7 +241,7 @@@ static int checkout_paths(const struct 
        int pos;
        struct checkout state;
        static char *ps_matched;
 -      unsigned char rev[20];
 +      struct object_id rev;
        struct commit *head;
        int errs = 0;
        struct lock_file *lock_file;
  
        hold_locked_index(lock_file, 1);
        if (read_cache_preload(&opts->pathspec) < 0)
 -              return error(_("corrupt index file"));
 +              return error(_("index file corrupt"));
  
        if (opts->source_tree)
                read_tree_some(opts->source_tree, &opts->pathspec);
        if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
 -      read_ref_full("HEAD", 0, rev, NULL);
 -      head = lookup_commit_reference_gently(rev, 1);
 +      read_ref_full("HEAD", 0, rev.hash, NULL);
 +      head = lookup_commit_reference_gently(rev.hash, 1);
  
        errs |= post_checkout_hook(head, head, 0);
        return errs;
@@@ -470,7 -470,7 +470,7 @@@ static int merge_working_tree(const str
  
        hold_locked_index(lock_file, 1);
        if (read_cache_preload(NULL) < 0)
 -              return error(_("corrupt index file"));
 +              return error(_("index file corrupt"));
  
        resolve_undo_clear();
        if (opts->force) {
                         * entries in the index.
                         */
  
-                       add_files_to_cache(NULL, NULL, 0, 0);
+                       add_files_to_cache(NULL, NULL, 0);
                        /*
                         * NEEDSWORK: carrying over local changes
                         * when branches have different end-of-line
                        o.ancestor = old->name;
                        o.branch1 = new->name;
                        o.branch2 = "local";
 -                      merge_trees(&o, new->commit->tree, work,
 +                      ret = merge_trees(&o, new->commit->tree, work,
                                old->commit->tree, &result);
 +                      if (ret < 0)
 +                              exit(128);
                        ret = reset_tree(new->commit->tree, opts, 0,
                                         writeout_error);
 +                      strbuf_release(&o.obuf);
                        if (ret)
                                return ret;
                }
@@@ -658,8 -655,7 +658,8 @@@ static void update_refs_for_switch(cons
                update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL,
                           REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
 -                      if (old->path && advice_detached_head)
 +                      if (old->path &&
 +                          advice_detached_head && !opts->force_detach)
                                detach_advice(new->name);
                        describe_detached_head(_("HEAD is now at"), new->commit);
                }
@@@ -707,7 -703,8 +707,7 @@@ static int add_pending_uninteresting_re
  static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
  {
        strbuf_addstr(sb, "  ");
 -      strbuf_addstr(sb,
 -              find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
 +      strbuf_add_unique_abbrev(sb, commit->object.oid.hash, DEFAULT_ABBREV);
        strbuf_addch(sb, ' ');
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
@@@ -808,11 -805,11 +808,11 @@@ static int switch_branches(const struc
        int ret = 0;
        struct branch_info old;
        void *path_to_free;
 -      unsigned char rev[20];
 +      struct object_id rev;
        int flag, writeout_error = 0;
        memset(&old, 0, sizeof(old));
 -      old.path = path_to_free = resolve_refdup("HEAD", 0, rev, &flag);
 -      old.commit = lookup_commit_reference_gently(rev, 1);
 +      old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
 +      old.commit = lookup_commit_reference_gently(rev.hash, 1);
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
  
@@@ -860,7 -857,7 +860,7 @@@ static int git_checkout_config(const ch
  struct tracking_name_data {
        /* const */ char *src_ref;
        char *dst_ref;
 -      unsigned char *dst_sha1;
 +      struct object_id *dst_oid;
        int unique;
  };
  
@@@ -871,7 -868,7 +871,7 @@@ static int check_tracking_name(struct r
        memset(&query, 0, sizeof(struct refspec));
        query.src = cb->src_ref;
        if (remote_find_tracking(remote, &query) ||
 -          get_sha1(query.dst, cb->dst_sha1)) {
 +          get_oid(query.dst, cb->dst_oid)) {
                free(query.dst);
                return 0;
        }
        return 0;
  }
  
 -static const char *unique_tracking_name(const char *name, unsigned char *sha1)
 +static const char *unique_tracking_name(const char *name, struct object_id *oid)
  {
        struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
        char src_ref[PATH_MAX];
        snprintf(src_ref, PATH_MAX, "refs/heads/%s", name);
        cb_data.src_ref = src_ref;
 -      cb_data.dst_sha1 = sha1;
 +      cb_data.dst_oid = oid;
        for_each_remote(check_tracking_name, &cb_data);
        if (cb_data.unique)
                return cb_data.dst_ref;
@@@ -902,12 -899,12 +902,12 @@@ static int parse_branchname_arg(int arg
                                int dwim_new_local_branch_ok,
                                struct branch_info *new,
                                struct checkout_opts *opts,
 -                              unsigned char rev[20])
 +                              struct object_id *rev)
  {
        struct tree **source_tree = &opts->source_tree;
        const char **new_branch = &opts->new_branch;
        int argcount = 0;
 -      unsigned char branch_rev[20];
 +      struct object_id branch_rev;
        const char *arg;
        int dash_dash_pos;
        int has_dash_dash = 0;
        if (!strcmp(arg, "-"))
                arg = "@{-1}";
  
 -      if (get_sha1_mb(arg, rev)) {
 +      if (get_oid_mb(arg, rev)) {
                /*
                 * Either case (3) or (4), with <something> not being
                 * a commit, or an attempt to use case (1) with an
                int recover_with_dwim = dwim_new_local_branch_ok;
  
                if (!has_dash_dash &&
 -                  (check_filename(NULL, arg) || !no_wildcard(arg)))
 +                  (check_filename(opts->prefix, arg) || !no_wildcard(arg)))
                        recover_with_dwim = 0;
                /*
                 * Accept "git checkout foo" and "git checkout foo --"
        setup_branch_path(new);
  
        if (!check_refname_format(new->path, 0) &&
 -          !read_ref(new->path, branch_rev))
 -              hashcpy(rev, branch_rev);
 +          !read_ref(new->path, branch_rev.hash))
 +              oidcpy(rev, &branch_rev);
        else
                new->path = NULL; /* not an existing branch */
  
 -      new->commit = lookup_commit_reference_gently(rev, 1);
 +      new->commit = lookup_commit_reference_gently(rev->hash, 1);
        if (!new->commit) {
                /* not a commit */
 -              *source_tree = parse_tree_indirect(rev);
 +              *source_tree = parse_tree_indirect(rev->hash);
        } else {
                parse_commit_or_die(new->commit);
                *source_tree = new->commit->tree;
  
        if (!*source_tree)                   /* case (1): want a tree */
                die(_("reference is not a tree: %s"), arg);
 -      if (!has_dash_dash) {/* case (3).(d) -> (1) */
 +      if (!has_dash_dash) {   /* case (3).(d) -> (1) */
                /*
                 * Do not complain the most common case
                 *      git checkout branch
                 * it would be extremely annoying.
                 */
                if (argc)
 -                      verify_non_filename(NULL, arg);
 +                      verify_non_filename(opts->prefix, arg);
        } else {
                argcount++;
                argv++;
@@@ -1108,9 -1105,9 +1108,9 @@@ static int checkout_branch(struct check
  
        if (new->path && !opts->force_detach && !opts->new_branch &&
            !opts->ignore_other_worktrees) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                int flag;
 -              char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
 +              char *head_ref = resolve_refdup("HEAD", 0, oid.hash, &flag);
                if (head_ref &&
                    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
                        die_if_checked_out(new->path, 1);
        }
  
        if (!new->commit && opts->new_branch) {
 -              unsigned char rev[20];
 +              struct object_id rev;
                int flag;
  
 -              if (!read_ref_full("HEAD", 0, rev, &flag) &&
 -                  (flag & REF_ISSYMREF) && is_null_sha1(rev))
 +              if (!read_ref_full("HEAD", 0, rev.hash, &flag) &&
 +                  (flag & REF_ISSYMREF) && is_null_oid(&rev))
                        return switch_unborn_to_new_branch(opts);
        }
        return switch_branches(opts, new);
@@@ -1141,7 -1138,7 +1141,7 @@@ int cmd_checkout(int argc, const char *
                OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
                           N_("create/reset and checkout a branch")),
                OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
 -              OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
 +              OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
                OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
         * remote branches, erroring out for invalid or ambiguous cases.
         */
        if (argc) {
 -              unsigned char rev[20];
 +              struct object_id rev;
                int dwim_ok =
                        !opts.patch_mode &&
                        dwim_new_local_branch &&
                        opts.track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts.new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
 -                                           &new, &opts, rev);
 +                                           &new, &opts, &rev);
                argv += n;
                argc -= n;
        }
diff --combined builtin/commit.c
index bb9f79b6ef4b5cd01cafac551bb6665a433334aa,443ff9196d16a9b6fc2d1d8b22393b1533faeb8f..1cba3b75c8dc121cf86558aa96726ea99f28eba3
@@@ -92,9 -92,8 +92,9 @@@ N_("If you wish to skip this commit, us
  "Then \"git cherry-pick --continue\" will resume cherry-picking\n"
  "the remaining commits.\n");
  
 +static GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
 +
  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 */
  static enum {
@@@ -142,24 -141,14 +142,24 @@@ static int show_ignored_in_status, have
  static const char *only_include_assumed;
  static struct strbuf message = STRBUF_INIT;
  
 -static enum status_format {
 -      STATUS_FORMAT_NONE = 0,
 -      STATUS_FORMAT_LONG,
 -      STATUS_FORMAT_SHORT,
 -      STATUS_FORMAT_PORCELAIN,
 +static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
  
 -      STATUS_FORMAT_UNSPECIFIED
 -} status_format = STATUS_FORMAT_UNSPECIFIED;
 +static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 +{
 +      enum wt_status_format *value = (enum wt_status_format *)opt->value;
 +      if (unset)
 +              *value = STATUS_FORMAT_NONE;
 +      else if (!arg)
 +              *value = STATUS_FORMAT_PORCELAIN;
 +      else if (!strcmp(arg, "v1") || !strcmp(arg, "1"))
 +              *value = STATUS_FORMAT_PORCELAIN;
 +      else if (!strcmp(arg, "v2") || !strcmp(arg, "2"))
 +              *value = STATUS_FORMAT_PORCELAIN_V2;
 +      else
 +              die("unsupported porcelain version '%s'", arg);
 +
 +      return 0;
 +}
  
  static int opt_parse_m(const struct option *opt, const char *arg, int unset)
  {
@@@ -397,7 -386,7 +397,7 @@@ static const char *prepare_index(int ar
         */
        if (all || (also && pathspec.nr)) {
                hold_locked_index(&index_lock, 1);
-               add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
+               add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
                if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
@@@ -510,13 -499,24 +510,13 @@@ static int run_status(FILE *fp, const c
        s->fp = fp;
        s->nowarn = nowarn;
        s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
 +      if (!s->is_initial)
 +              hashcpy(s->sha1_commit, sha1);
 +      s->status_format = status_format;
 +      s->ignore_submodule_arg = ignore_submodule_arg;
  
        wt_status_collect(s);
 -
 -      switch (status_format) {
 -      case STATUS_FORMAT_SHORT:
 -              wt_shortstatus_print(s);
 -              break;
 -      case STATUS_FORMAT_PORCELAIN:
 -              wt_porcelain_print(s);
 -              break;
 -      case STATUS_FORMAT_UNSPECIFIED:
 -              die("BUG: finalize_deferred_config() should have been called");
 -              break;
 -      case STATUS_FORMAT_NONE:
 -      case STATUS_FORMAT_LONG:
 -              wt_status_print(s);
 -              break;
 -      }
 +      wt_status_print(s);
  
        return s->commitable;
  }
@@@ -714,7 -714,7 +714,7 @@@ static int prepare_to_commit(const cha
                char *buffer;
                buffer = strstr(use_message_buffer, "\n\n");
                if (buffer)
 -                      strbuf_addstr(&sb, buffer + 2);
 +                      strbuf_addstr(&sb, skip_blank_lines(buffer + 2));
                hook_arg1 = "commit";
                hook_arg2 = use_message;
        } else if (fixup_message) {
                hook_arg2 = "";
        }
  
 -      s->fp = fopen_for_writing(git_path(commit_editmsg));
 +      s->fp = fopen_for_writing(git_path_commit_editmsg());
        if (s->fp == NULL)
 -              die_errno(_("could not open '%s'"), git_path(commit_editmsg));
 +              die_errno(_("could not open '%s'"), git_path_commit_editmsg());
  
        /* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
        old_display_comment_prefix = s->display_comment_prefix;
        }
  
        if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
 -                          git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
 +                          git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL))
                return 0;
  
        if (use_editor) {
                const char *env[2] = { NULL };
                env[0] =  index;
                snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
 -              if (launch_editor(git_path(commit_editmsg), NULL, env)) {
 +              if (launch_editor(git_path_commit_editmsg(), NULL, env)) {
                        fprintf(stderr,
                        _("Please supply the message using either -m or -F option.\n"));
                        exit(1);
        }
  
        if (!no_verify &&
 -          run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
 +          run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) {
                return 0;
        }
  
@@@ -1098,7 -1098,7 +1098,7 @@@ static const char *read_commit_message(
   * is not in effect here.
   */
  static struct status_deferred_config {
 -      enum status_format status_format;
 +      enum wt_status_format status_format;
        int show_branch;
  } status_deferred_config = {
        STATUS_FORMAT_UNSPECIFIED,
  static void finalize_deferred_config(struct wt_status *s)
  {
        int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
 +                                 status_format != STATUS_FORMAT_PORCELAIN_V2 &&
                                   !s->null_termination);
  
        if (s->null_termination) {
@@@ -1336,9 -1335,9 +1336,9 @@@ int cmd_status(int argc, const char **a
                            N_("show status concisely"), STATUS_FORMAT_SHORT),
                OPT_BOOL('b', "branch", &s.show_branch,
                         N_("show branch information")),
 -              OPT_SET_INT(0, "porcelain", &status_format,
 -                          N_("machine-readable output"),
 -                          STATUS_FORMAT_PORCELAIN),
 +              { OPTION_CALLBACK, 0, "porcelain", &status_format,
 +                N_("version"), N_("machine-readable output"),
 +                PARSE_OPT_OPTARG, opt_parse_porcelain },
                OPT_SET_INT(0, "long", &status_format,
                            N_("show status in long format (default)"),
                            STATUS_FORMAT_LONG),
        fd = hold_locked_index(&index_lock, 0);
  
        s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
 +      if (!s.is_initial)
 +              hashcpy(s.sha1_commit, sha1);
 +
        s.ignore_submodule_arg = ignore_submodule_arg;
 +      s.status_format = status_format;
 +      s.verbose = verbose;
 +
        wt_status_collect(&s);
  
        if (0 <= fd)
        if (s.relative_paths)
                s.prefix = prefix;
  
 -      switch (status_format) {
 -      case STATUS_FORMAT_SHORT:
 -              wt_shortstatus_print(&s);
 -              break;
 -      case STATUS_FORMAT_PORCELAIN:
 -              wt_porcelain_print(&s);
 -              break;
 -      case STATUS_FORMAT_UNSPECIFIED:
 -              die("BUG: finalize_deferred_config() should have been called");
 -              break;
 -      case STATUS_FORMAT_NONE:
 -      case STATUS_FORMAT_LONG:
 -              s.verbose = verbose;
 -              s.ignore_submodule_arg = ignore_submodule_arg;
 -              wt_status_print(&s);
 -              break;
 -      }
 +      wt_status_print(&s);
        return 0;
  }
  
@@@ -1607,7 -1616,7 +1607,7 @@@ int cmd_commit(int argc, const char **a
                OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
                OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
                OPT_BOOL('o', "only", &only, N_("commit only specified files")),
 -              OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
 +              OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
                OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
  
        /* Finally, get the commit message */
        strbuf_reset(&sb);
 -      if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
 +      if (strbuf_read_file(&sb, git_path_commit_editmsg(), 0) < 0) {
                int saved_errno = errno;
                rollback_index_files();
                die(_("could not read commit message: %s"), strerror(saved_errno));
diff --combined builtin/update-index.c
index 73f6b3e1bec362fe8ec6ed78dee0edbb5155403c,8ef21fedc842c996c07d629be72e5df810d8540d..f3f07e7f1cb2d952144bf98969b481dc331155bf
@@@ -275,7 -275,7 +275,7 @@@ static int add_one_path(const struct ca
        fill_stat_cache_info(ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
  
 -      if (index_path(ce->sha1, path, st,
 +      if (index_path(ce->oid.hash, path, st,
                       info_only ? 0 : HASH_WRITE_OBJECT)) {
                free(ce);
                return -1;
   */
  static int process_directory(const char *path, int len, struct stat *st)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int pos = cache_name_pos(path, len);
  
        /* Exact match: file or existing gitlink */
                if (S_ISGITLINK(ce->ce_mode)) {
  
                        /* Do nothing to the index if there is no HEAD! */
 -                      if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
 +                      if (resolve_gitlink_ref(path, "HEAD", oid.hash) < 0)
                                return 0;
  
                        return add_one_path(ce, path, len, st);
        }
  
        /* No match - should we add it as a gitlink? */
 -      if (!resolve_gitlink_ref(path, "HEAD", sha1))
 +      if (!resolve_gitlink_ref(path, "HEAD", oid.hash))
                return add_one_path(NULL, path, len, st);
  
        /* Error out. */
@@@ -390,7 -390,7 +390,7 @@@ static int process_path(const char *pat
        return add_one_path(ce, path, len, &st);
  }
  
 -static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
 +static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
                         const char *path, int stage)
  {
        int size, len, option;
        size = cache_entry_size(len);
        ce = xcalloc(1, size);
  
 -      hashcpy(ce->sha1, sha1);
 +      oidcpy(&ce->oid, oid);
        memcpy(ce->name, path, len);
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
        return 0;
  }
  
- static void chmod_path(int flip, const char *path)
+ static void chmod_path(char flip, const char *path)
  {
        int pos;
        struct cache_entry *ce;
-       unsigned int mode;
  
        pos = cache_name_pos(path, strlen(path));
        if (pos < 0)
                goto fail;
        ce = active_cache[pos];
-       mode = ce->ce_mode;
-       if (!S_ISREG(mode))
+       if (chmod_cache_entry(ce, flip) < 0)
                goto fail;
-       switch (flip) {
-       case '+':
-               ce->ce_mode |= 0111; break;
-       case '-':
-               ce->ce_mode &= ~0111; break;
-       default:
-               goto fail;
-       }
-       cache_tree_invalidate_path(&the_index, path);
-       ce->ce_flags |= CE_UPDATE_IN_BASE;
-       active_cache_changed |= CE_ENTRY_CHANGED;
        report("chmod %cx '%s'", flip, path);
        return;
   fail:
@@@ -487,7 -475,7 +475,7 @@@ static void read_index_info(int nul_ter
        while (getline_fn(&buf, stdin) != EOF) {
                char *ptr, *tab;
                char *path_name;
 -              unsigned char sha1[20];
 +              struct object_id oid;
                unsigned int mode;
                unsigned long ul;
                int stage;
                mode = ul;
  
                tab = strchr(ptr, '\t');
 -              if (!tab || tab - ptr < 41)
 +              if (!tab || tab - ptr < GIT_SHA1_HEXSZ + 1)
                        goto bad_line;
  
                if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') {
                        ptr = tab + 1; /* point at the head of path */
                }
  
 -              if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
 +              if (get_oid_hex(tab - GIT_SHA1_HEXSZ, &oid) ||
 +                      tab[-(GIT_SHA1_HEXSZ + 1)] != ' ')
                        goto bad_line;
  
                path_name = ptr;
                         * ptr[-1] points at tab,
                         * ptr[-41] is at the beginning of sha1
                         */
 -                      ptr[-42] = ptr[-1] = 0;
 -                      if (add_cacheinfo(mode, sha1, path_name, stage))
 +                      ptr[-(GIT_SHA1_HEXSZ + 2)] = ptr[-1] = 0;
 +                      if (add_cacheinfo(mode, &oid, path_name, stage))
                                die("git update-index: unable to update %s",
                                    path_name);
                }
@@@ -577,19 -564,19 +565,19 @@@ static const char * const update_index_
        NULL
  };
  
 -static unsigned char head_sha1[20];
 -static unsigned char merge_head_sha1[20];
 +static struct object_id head_oid;
 +static struct object_id merge_head_oid;
  
  static struct cache_entry *read_one_ent(const char *which,
 -                                      unsigned char *ent, const char *path,
 +                                      struct object_id *ent, const char *path,
                                        int namelen, int stage)
  {
        unsigned mode;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int size;
        struct cache_entry *ce;
  
 -      if (get_tree_entry(ent, path, sha1, &mode)) {
 +      if (get_tree_entry(ent->hash, path, oid.hash, &mode)) {
                if (which)
                        error("%s: not in %s branch.", path, which);
                return NULL;
        size = cache_entry_size(namelen);
        ce = xcalloc(1, size);
  
 -      hashcpy(ce->sha1, sha1);
 +      oidcpy(&ce->oid, &oid);
        memcpy(ce->name, path, namelen);
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = namelen;
@@@ -652,14 -639,14 +640,14 @@@ static int unresolve_one(const char *pa
         * stuff HEAD version in stage #2,
         * stuff MERGE_HEAD version in stage #3.
         */
 -      ce_2 = read_one_ent("our", head_sha1, path, namelen, 2);
 -      ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3);
 +      ce_2 = read_one_ent("our", &head_oid, path, namelen, 2);
 +      ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3);
  
        if (!ce_2 || !ce_3) {
                ret = -1;
                goto free_return;
        }
 -      if (!hashcmp(ce_2->sha1, ce_3->sha1) &&
 +      if (!oidcmp(&ce_2->oid, &ce_3->oid) &&
            ce_2->ce_mode == ce_3->ce_mode) {
                fprintf(stderr, "%s: identical in both, skipping.\n",
                        path);
  
  static void read_head_pointers(void)
  {
 -      if (read_ref("HEAD", head_sha1))
 +      if (read_ref("HEAD", head_oid.hash))
                die("No HEAD -- no initial commit yet?");
 -      if (read_ref("MERGE_HEAD", merge_head_sha1)) {
 +      if (read_ref("MERGE_HEAD", merge_head_oid.hash)) {
                fprintf(stderr, "Not in the middle of a merge.\n");
                exit(0);
        }
@@@ -726,7 -713,7 +714,7 @@@ static int do_reupdate(int ac, const ch
                       PATHSPEC_PREFER_CWD,
                       prefix, av + 1);
  
 -      if (read_ref("HEAD", head_sha1))
 +      if (read_ref("HEAD", head_oid.hash))
                /* If there is no HEAD, that means it is an initial
                 * commit.  Update everything in the index.
                 */
                if (ce_stage(ce) || !ce_path_match(ce, &pathspec, NULL))
                        continue;
                if (has_head)
 -                      old = read_one_ent(NULL, head_sha1,
 +                      old = read_one_ent(NULL, &head_oid,
                                           ce->name, ce_namelen(ce), 0);
                if (old && ce->ce_mode == old->ce_mode &&
 -                  !hashcmp(ce->sha1, old->sha1)) {
 +                  !oidcmp(&ce->oid, &old->oid)) {
                        free(old);
                        continue; /* unchanged */
                }
                if (save_nr != active_nr)
                        goto redo;
        }
 -      free_pathspec(&pathspec);
 +      clear_pathspec(&pathspec);
        return 0;
  }
  
@@@ -808,7 -795,7 +796,7 @@@ static int resolve_undo_clear_callback(
  
  static int parse_new_style_cacheinfo(const char *arg,
                                     unsigned int *mode,
 -                                   unsigned char sha1[],
 +                                   struct object_id *oid,
                                     const char **path)
  {
        unsigned long ul;
                return -1; /* not a new-style cacheinfo */
        *mode = ul;
        endp++;
 -      if (get_sha1_hex(endp, sha1) || endp[40] != ',')
 +      if (get_oid_hex(endp, oid) || endp[GIT_SHA1_HEXSZ] != ',')
                return -1;
 -      *path = endp + 41;
 +      *path = endp + GIT_SHA1_HEXSZ + 1;
        return 0;
  }
  
  static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
                                const struct option *opt, int unset)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        unsigned int mode;
        const char *path;
  
 -      if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, sha1, &path)) {
 -              if (add_cacheinfo(mode, sha1, path, 0))
 +      if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, &oid, &path)) {
 +              if (add_cacheinfo(mode, &oid, path, 0))
                        die("git update-index: --cacheinfo cannot add %s", path);
                ctx->argv++;
                ctx->argc--;
        if (ctx->argc <= 3)
                return error("option 'cacheinfo' expects <mode>,<sha1>,<path>");
        if (strtoul_ui(*++ctx->argv, 8, &mode) ||
 -          get_sha1_hex(*++ctx->argv, sha1) ||
 -          add_cacheinfo(mode, sha1, *++ctx->argv, 0))
 +          get_oid_hex(*++ctx->argv, &oid) ||
 +          add_cacheinfo(mode, &oid, *++ctx->argv, 0))
                die("git update-index: --cacheinfo cannot add %s", *ctx->argv);
        ctx->argc -= 3;
        return 0;
@@@ -1128,9 -1115,9 +1116,9 @@@ int cmd_update_index(int argc, const ch
                break;
        case UC_DISABLE:
                if (git_config_get_untracked_cache() == 1)
 -                      warning("core.untrackedCache is set to true; "
 -                              "remove or change it, if you really want to "
 -                              "disable the untracked cache");
 +                      warning(_("core.untrackedCache is set to true; "
 +                                "remove or change it, if you really want to "
 +                                "disable the untracked cache"));
                remove_untracked_cache(&the_index);
                report(_("Untracked cache disabled"));
                break;
        case UC_ENABLE:
        case UC_FORCE:
                if (git_config_get_untracked_cache() == 0)
 -                      warning("core.untrackedCache is set to false; "
 -                              "remove or change it, if you really want to "
 -                              "enable the untracked cache");
 +                      warning(_("core.untrackedCache is set to false; "
 +                                "remove or change it, if you really want to "
 +                                "enable the untracked cache"));
                add_untracked_cache(&the_index);
                report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
                break;
        default:
 -              die("Bug: bad untracked_cache value: %d", untracked_cache);
 +              die("BUG: bad untracked_cache value: %d", untracked_cache);
        }
  
        if (active_cache_changed) {
diff --combined cache.h
index d0494c85338b9063b3abe6e2081c921617ca0203,009432f9a8b1c30895a488aebdc234c17813ba69..1a56f81f3357eb6bed624309b22ae0795bd8c0ac
+++ b/cache.h
@@@ -173,7 -173,7 +173,7 @@@ struct cache_entry 
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned int index;     /* for link extension */
 -      unsigned char sha1[20];
 +      struct object_id oid;
        char name[FLEX_ARRAY]; /* more */
  };
  
@@@ -367,8 -367,9 +367,9 @@@ extern void free_name_hash(struct index
  #define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
  #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
- #define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
- #define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
+ #define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
+ #define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
+ #define chmod_cache_entry(ce, flip) chmod_index_entry(&the_index, (ce), (flip))
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@@ -453,12 -454,6 +454,12 @@@ static inline enum object_type object_t
   */
  extern const char * const local_repo_env[];
  
 +/*
 + * Returns true iff we have a configured git repository (either via
 + * setup_git_directory, or in the environment via $GIT_DIR).
 + */
 +int have_git_dir(void);
 +
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
  extern int is_inside_git_dir(void);
@@@ -587,9 -582,10 +588,10 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
- extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
- extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
+ extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
+ extern int add_file_to_index(struct index_state *, const char *path, int flags);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
+ extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
  extern int index_name_is_other(const struct index_state *, const char *, int);
@@@ -638,7 -634,6 +640,7 @@@ extern void fill_stat_cache_info(struc
  #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 struct pathspec *pathspec, char *seen, const char *header_msg);
 +extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
  
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
@@@ -671,15 -666,8 +673,15 @@@ extern size_t delta_base_cache_limit
  extern unsigned long big_file_threshold;
  extern unsigned long pack_size_limit_cfg;
  
 +/*
 + * Accessors for the core.sharedrepository config which lazy-load the value
 + * from the config (if not already set). The "reset" function can be
 + * used to unset "set" or cached value, meaning that the value will be loaded
 + * fresh from the config file on the next call to get_shared_repository().
 + */
  void set_shared_repository(int value);
  int get_shared_repository(void);
 +void reset_shared_repository(void);
  
  /*
   * Do replace refs need to be checked this run?  This variable is
@@@ -832,8 -820,8 +834,8 @@@ extern void strbuf_git_common_path(stru
        __attribute__((format (printf, 2, 3)));
  extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 -extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 -                                    const char *fmt, ...)
 +extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 +                                   const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
@@@ -966,39 -954,22 +968,39 @@@ static inline void oidclr(struct object
  #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)
 +extern const struct object_id empty_tree_oid;
 +#define EMPTY_TREE_SHA1_BIN (empty_tree_oid.hash)
  
  #define EMPTY_BLOB_SHA1_HEX \
        "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
  #define EMPTY_BLOB_SHA1_BIN_LITERAL \
        "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
        "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 -#define EMPTY_BLOB_SHA1_BIN \
 -      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +extern const struct object_id empty_blob_oid;
 +#define EMPTY_BLOB_SHA1_BIN (empty_blob_oid.hash)
 +
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
        return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
  }
  
 +static inline int is_empty_blob_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1034,11 -1005,6 +1036,11 @@@ int adjust_shared_perm(const char *path
   * directory while we were working.  To be robust against this kind of
   * race, callers might want to try invoking the function again when it
   * returns SCLD_VANISHED.
 + *
 + * safe_create_leading_directories() temporarily changes path while it
 + * is working but restores it before returning.
 + * safe_create_leading_directories_const() doesn't modify path, even
 + * temporarily.
   */
  enum scld_error {
        SCLD_OK = 0,
@@@ -1169,16 -1135,6 +1171,16 @@@ static inline unsigned int hexval(unsig
        return hexval_table[c];
  }
  
 +/*
 + * Convert two consecutive hexadecimal digits into a char.  Return a
 + * negative value on error.  Don't run over the end of short strings.
 + */
 +static inline int hex2chr(const char *s)
 +{
 +      int val = hexval(s[0]);
 +      return (val < 0) ? val : (val << 4) | hexval(s[1]);
 +}
 +
  /* Convert to/from hex/sha1 representation */
  #define MINIMUM_ABBREV minimum_abbrev
  #define DEFAULT_ABBREV default_abbrev
@@@ -1239,12 -1195,11 +1241,12 @@@ extern int get_oid_hex(const char *hex
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
  extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +extern char *oid_to_hex_r(char *out, const struct object_id *oid);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
  
  extern int interpret_branch_name(const char *str, int len, struct strbuf *);
 -extern int get_sha1_mb(const char *str, unsigned char *sha1);
 +extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
  
@@@ -1270,8 -1225,7 +1272,8 @@@ struct date_mode 
                DATE_ISO8601_STRICT,
                DATE_RFC2822,
                DATE_STRFTIME,
 -              DATE_RAW
 +              DATE_RAW,
 +              DATE_UNIX
        } type;
        const char *strftime_fmt;
        int local;
@@@ -1310,7 -1264,6 +1312,7 @@@ extern const char *ident_default_email(
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
  extern int git_ident_config(const char *, const char *, void *);
 +extern void reset_ident_date(void);
  
  struct ident_split {
        const char *name_begin;
@@@ -1384,7 -1337,6 +1386,7 @@@ extern struct alternate_object_databas
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
  extern void read_info_alternates(const char * relative_base, int depth);
 +extern char *compute_alternate_path(const char *path, struct strbuf *err);
  extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern int foreach_alt_odb(alt_odb_fn, void*);
@@@ -1420,13 -1372,6 +1422,13 @@@ extern struct packed_git 
        char pack_name[FLEX_ARRAY]; /* more */
  } *packed_git;
  
 +/*
 + * A most-recently-used ordered version of the packed_git list, which can
 + * be iterated instead of packed_git (and marked via mru_mark).
 + */
 +struct mru;
 +extern struct mru *packed_git_mru;
 +
  struct pack_entry {
        off_t offset;
        unsigned char sha1[20];
@@@ -1466,6 -1411,7 +1468,6 @@@ extern unsigned char *use_pack(struct p
  extern void close_pack_windows(struct packed_git *);
  extern void close_all_packs(void);
  extern void unuse_pack(struct pack_window **);
 -extern void free_pack_by_name(const char *);
  extern void clear_delta_base_cache(void);
  extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
  
@@@ -1564,7 -1510,7 +1566,7 @@@ struct object_info 
        /* Request */
        enum object_type *typep;
        unsigned long *sizep;
 -      unsigned long *disk_sizep;
 +      off_t *disk_sizep;
        unsigned char *delta_base_sha1;
        struct strbuf *typename;
  
@@@ -1615,18 -1561,10 +1617,18 @@@ struct git_config_source 
        const char *blob;
  };
  
 +enum config_origin_type {
 +      CONFIG_ORIGIN_BLOB,
 +      CONFIG_ORIGIN_FILE,
 +      CONFIG_ORIGIN_STDIN,
 +      CONFIG_ORIGIN_SUBMODULE_BLOB,
 +      CONFIG_ORIGIN_CMDLINE
 +};
 +
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
 -extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
 +extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
@@@ -1668,16 -1606,6 +1670,16 @@@ extern const char *get_log_output_encod
  extern const char *get_commit_output_encoding(void);
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +
 +enum config_scope {
 +      CONFIG_SCOPE_UNKNOWN = 0,
 +      CONFIG_SCOPE_SYSTEM,
 +      CONFIG_SCOPE_GLOBAL,
 +      CONFIG_SCOPE_REPO,
 +      CONFIG_SCOPE_CMDLINE,
 +};
 +
 +extern enum config_scope current_config_scope(void);
  extern const char *current_config_origin_type(void);
  extern const char *current_config_name(void);
  
@@@ -1770,8 -1698,6 +1772,8 @@@ extern int ignore_untracked_cache_confi
  struct key_value_info {
        const char *filename;
        int linenr;
 +      enum config_origin_type origin_type;
 +      enum config_scope scope;
  };
  
  extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
@@@ -1797,6 -1723,8 +1799,6 @@@ extern int copy_file(const char *dst, c
  extern int copy_file_with_time(const char *dst, const char *src, int mode);
  
  extern void write_or_die(int fd, const void *buf, size_t count);
 -extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 -extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
  extern void fsync_or_die(int fd, const char *);
  
  extern ssize_t read_in_full(int fd, void *buf, size_t count);
@@@ -1808,24 -1736,12 +1810,24 @@@ static inline ssize_t write_str_in_full
        return write_in_full(fd, str, strlen(str));
  }
  
 -extern int write_file(const char *path, const char *fmt, ...);
 -extern int write_file_gently(const char *path, const char *fmt, ...);
 +/**
 + * Open (and truncate) the file at path, write the contents of buf to it,
 + * and close it. Dies if any errors are encountered.
 + */
 +extern void write_file_buf(const char *path, const char *buf, size_t len);
 +
 +/**
 + * Like write_file_buf(), but format the contents into a buffer first.
 + * Additionally, write_file() will append a newline if one is not already
 + * present, making it convenient to write text files:
 + *
 + *   write_file(path, "counter: %d", ctr);
 + */
 +__attribute__((format (printf, 2, 3)))
 +extern void write_file(const char *path, const char *fmt, ...);
  
  /* pager.c */
  extern void setup_pager(void);
 -extern const char *pager_program;
  extern int pager_in_use(void);
  extern int pager_use_color;
  extern int term_columns(void);
@@@ -1858,7 -1774,7 +1860,7 @@@ void packet_trace_identity(const char *
   * return 0 if success, 1 - if addition of a file failed and
   * ADD_FILES_IGNORE_ERRORS was specified in flags
   */
- int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
+ int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
diff --combined read-cache.c
index 31eddec5f751b2c8d8abda0698869bca133b606a,c2b2e970bc0388b8984fbb7883fedb6c94faeb27..38d67faf708d7b88f208f10dad23f893dd16f587
@@@ -19,6 -19,9 +19,6 @@@
  #include "split-index.h"
  #include "utf8.h"
  
 -static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
 -                                             unsigned int options);
 -
  /* Mask for the name length in ce_flags in the on-disk index */
  
  #define CE_NAMEMASK  (0x0fff)
@@@ -161,7 -164,7 +161,7 @@@ static int ce_compare_data(const struc
        if (fd >= 0) {
                unsigned char sha1[20];
                if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
 -                      match = hashcmp(sha1, ce->sha1);
 +                      match = hashcmp(sha1, ce->oid.hash);
                /* index_fd() closed the file descriptor already */
        }
        return match;
@@@ -178,7 -181,7 +178,7 @@@ static int ce_compare_link(const struc
        if (strbuf_readlink(&sb, ce->name, expected_size))
                return -1;
  
 -      buffer = read_sha1_file(ce->sha1, &type, &size);
 +      buffer = read_sha1_file(ce->oid.hash, &type, &size);
        if (buffer) {
                if (size == sb.len)
                        match = memcmp(buffer, sb.buf, size);
@@@ -202,7 -205,7 +202,7 @@@ static int ce_compare_gitlink(const str
         */
        if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
                return 0;
 -      return hashcmp(sha1, ce->sha1);
 +      return hashcmp(sha1, ce->oid.hash);
  }
  
  static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
@@@ -262,7 -265,7 +262,7 @@@ static int ce_match_stat_basic(const st
  
        /* Racily smudged entry? */
        if (!ce->ce_stat_data.sd_size) {
 -              if (!is_empty_blob_sha1(ce->sha1))
 +              if (!is_empty_blob_sha1(ce->oid.hash))
                        changed |= DATA_CHANGED;
        }
  
@@@ -624,10 -627,10 +624,10 @@@ void set_object_name_for_intent_to_add_
        unsigned char sha1[20];
        if (write_sha1_file("", 0, blob_type, sha1))
                die("cannot create an empty blob in the object database");
 -      hashcpy(ce->sha1, sha1);
 +      hashcpy(ce->oid.hash, sha1);
  }
  
- int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, int force_mode)
+ int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
  {
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
        else
                ce->ce_flags |= CE_INTENT_TO_ADD;
  
-       if (S_ISREG(st_mode) && force_mode)
-               ce->ce_mode = create_ce_mode(force_mode);
-       else if (trust_executable_bit && has_symlinks)
+       if (trust_executable_bit && has_symlinks) {
                ce->ce_mode = create_ce_mode(st_mode);
-       else {
+       else {
                /* If there is an existing entry, pick the mode bits and type
                 * from it, otherwise assume unexecutable regular file.
                 */
                return 0;
        }
        if (!intent_only) {
 -              if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT)) {
 +              if (index_path(ce->oid.hash, path, st, HASH_WRITE_OBJECT)) {
                        free(ce);
                        return error("unable to index file %s", path);
                }
        /* It was suspected to be racily clean, but it turns out to be Ok */
        was_same = (alias &&
                    !ce_stage(alias) &&
 -                  !hashcmp(alias->sha1, ce->sha1) &&
 +                  !oidcmp(&alias->oid, &ce->oid) &&
                    ce->ce_mode == alias->ce_mode);
  
        if (pretend)
        return 0;
  }
  
- int add_file_to_index(struct index_state *istate, const char *path,
-       int flags, int force_mode)
+ int add_file_to_index(struct index_state *istate, const char *path, int flags)
  {
        struct stat st;
        if (lstat(path, &st))
                die_errno("unable to stat '%s'", path);
-       return add_to_index(istate, path, &st, flags, force_mode);
+       return add_to_index(istate, path, &st, flags);
  }
  
  struct cache_entry *make_cache_entry(unsigned int mode,
        size = cache_entry_size(len);
        ce = xcalloc(1, size);
  
 -      hashcpy(ce->sha1, sha1);
 +      hashcpy(ce->oid.hash, sha1);
        memcpy(ce->name, path, len);
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
        return ret;
  }
  
+ /*
+  * Chmod an index entry with either +x or -x.
+  *
+  * Returns -1 if the chmod for the particular cache entry failed (if it's
+  * not a regular file), -2 if an invalid flip argument is passed in, 0
+  * otherwise.
+  */
+ int chmod_index_entry(struct index_state *istate, struct cache_entry *ce,
+                     char flip)
+ {
+       if (!S_ISREG(ce->ce_mode))
+               return -1;
+       switch (flip) {
+       case '+':
+               ce->ce_mode |= 0111;
+               break;
+       case '-':
+               ce->ce_mode &= ~0111;
+               break;
+       default:
+               return -2;
+       }
+       cache_tree_invalidate_path(istate, ce->name);
+       ce->ce_flags |= CE_UPDATE_IN_BASE;
+       istate->cache_changed |= CE_ENTRY_CHANGED;
+       return 0;
+ }
  int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
  {
        int len = ce_namelen(a);
@@@ -1254,7 -1284,7 +1281,7 @@@ int refresh_index(struct index_state *i
        return has_errors;
  }
  
 -static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
 +struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
                                               unsigned int options)
  {
        return refresh_cache_ent(&the_index, ce, options, NULL, NULL);
@@@ -1424,7 -1454,7 +1451,7 @@@ static struct cache_entry *cache_entry_
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        ce->index = 0;
 -      hashcpy(ce->sha1, ondisk->sha1);
 +      hashcpy(ce->oid.hash, ondisk->sha1);
        memcpy(ce->name, name, len);
        ce->name[len] = '\0';
        return ce;
@@@ -1849,7 -1879,7 +1876,7 @@@ static char *copy_cache_entry_to_ondisk
        ondisk->uid  = htonl(ce->ce_stat_data.sd_uid);
        ondisk->gid  = htonl(ce->ce_stat_data.sd_gid);
        ondisk->size = htonl(ce->ce_stat_data.sd_size);
 -      hashcpy(ondisk->sha1, ce->sha1);
 +      hashcpy(ondisk->sha1, ce->oid.hash);
  
        flags = ce->ce_flags & ~CE_NAMEMASK;
        flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
@@@ -2038,7 -2068,7 +2065,7 @@@ static int do_write_index(struct index_
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
 -              if (is_null_sha1(ce->sha1)) {
 +              if (is_null_oid(&ce->oid)) {
                        static const char msg[] = "cache entry has null sha1: %s";
                        static int allow = -1;
  
@@@ -2285,7 -2315,7 +2312,7 @@@ void *read_blob_data_from_index(struct 
        }
        if (pos < 0)
                return NULL;
 -      data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
 +      data = read_sha1_file(istate->cache[pos]->oid.hash, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return NULL;