Merge branch 'jc/am-i-v-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Thu, 10 Mar 2016 19:13:40 +0000 (11:13 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 10 Mar 2016 19:13:41 +0000 (11:13 -0800)
The "v(iew)" subcommand of the interactive "git am -i" command was
broken in 2.6.0 timeframe when the command was rewritten in C.

* jc/am-i-v-fix:
am -i: fix "v"iew
pager: factor out a helper to prepare a child process to run the pager
pager: lose a separate argv[]

1  2 
builtin/am.c
cache.h
pager.c
diff --combined builtin/am.c
index 95decc6a594a2cf379501a478fd2cdf207cecdc5,56cf26e8b7dd794c34c95524caaf5c86aa3252ba..5668e0c3a5a7e04d266b20e2a5ae60f2a7aa98cd
@@@ -10,7 -10,6 +10,7 @@@
  #include "dir.h"
  #include "run-command.h"
  #include "quote.h"
 +#include "tempfile.h"
  #include "lockfile.h"
  #include "cache-tree.h"
  #include "refs.h"
@@@ -27,7 -26,6 +27,7 @@@
  #include "notes-utils.h"
  #include "rerere.h"
  #include "prompt.h"
 +#include "mailinfo.h"
  
  /**
   * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@@ -100,12 -98,6 +100,12 @@@ enum scissors_type 
        SCISSORS_TRUE        /* pass --scissors to git-mailinfo */
  };
  
 +enum signoff_type {
 +      SIGNOFF_FALSE = 0,
 +      SIGNOFF_TRUE = 1,
 +      SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
 +};
 +
  struct am_state {
        /* state directory path */
        char *dir;
        int interactive;
        int threeway;
        int quiet;
 -      int signoff;
 +      int signoff; /* enum signoff_type */
        int utf8;
        int keep; /* enum keep_type */
        int message_id;
@@@ -195,27 -187,6 +195,27 @@@ static inline const char *am_path(cons
        return mkpath("%s/%s", state->dir, path);
  }
  
 +/**
 + * For convenience to call write_file()
 + */
 +static int write_state_text(const struct am_state *state,
 +                          const char *name, const char *string)
 +{
 +      return write_file(am_path(state, name), "%s", string);
 +}
 +
 +static int write_state_count(const struct am_state *state,
 +                           const char *name, int value)
 +{
 +      return write_file(am_path(state, name), "%d", value);
 +}
 +
 +static int write_state_bool(const struct am_state *state,
 +                          const char *name, int value)
 +{
 +      return write_state_text(state, name, value ? "t" : "f");
 +}
 +
  /**
   * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline
   * at the end.
@@@ -385,7 -356,7 +385,7 @@@ static void write_author_script(const s
        sq_quote_buf(&sb, state->author_date);
        strbuf_addch(&sb, '\n');
  
 -      write_file(am_path(state, "author-script"), 1, "%s", sb.buf);
 +      write_state_text(state, "author-script", sb.buf);
  
        strbuf_release(&sb);
  }
@@@ -1023,10 -994,13 +1023,10 @@@ static void am_setup(struct am_state *s
        if (state->rebasing)
                state->threeway = 1;
  
 -      write_file(am_path(state, "threeway"), 1, state->threeway ? "t" : "f");
 -
 -      write_file(am_path(state, "quiet"), 1, state->quiet ? "t" : "f");
 -
 -      write_file(am_path(state, "sign"), 1, state->signoff ? "t" : "f");
 -
 -      write_file(am_path(state, "utf8"), 1, state->utf8 ? "t" : "f");
 +      write_state_bool(state, "threeway", state->threeway);
 +      write_state_bool(state, "quiet", state->quiet);
 +      write_state_bool(state, "sign", state->signoff);
 +      write_state_bool(state, "utf8", state->utf8);
  
        switch (state->keep) {
        case KEEP_FALSE:
                die("BUG: invalid value for state->keep");
        }
  
 -      write_file(am_path(state, "keep"), 1, "%s", str);
 -
 -      write_file(am_path(state, "messageid"), 1, state->message_id ? "t" : "f");
 +      write_state_text(state, "keep", str);
 +      write_state_bool(state, "messageid", state->message_id);
  
        switch (state->scissors) {
        case SCISSORS_UNSET:
        default:
                die("BUG: invalid value for state->scissors");
        }
 -
 -      write_file(am_path(state, "scissors"), 1, "%s", str);
 +      write_state_text(state, "scissors", str);
  
        sq_quote_argv(&sb, state->git_apply_opts.argv, 0);
 -      write_file(am_path(state, "apply-opt"), 1, "%s", sb.buf);
 +      write_state_text(state, "apply-opt", sb.buf);
  
        if (state->rebasing)
 -              write_file(am_path(state, "rebasing"), 1, "%s", "");
 +              write_state_text(state, "rebasing", "");
        else
 -              write_file(am_path(state, "applying"), 1, "%s", "");
 +              write_state_text(state, "applying", "");
  
        if (!get_sha1("HEAD", curr_head)) {
 -              write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(curr_head));
 +              write_state_text(state, "abort-safety", sha1_to_hex(curr_head));
                if (!state->rebasing)
                        update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
                                        UPDATE_REFS_DIE_ON_ERR);
        } else {
 -              write_file(am_path(state, "abort-safety"), 1, "%s", "");
 +              write_state_text(state, "abort-safety", "");
                if (!state->rebasing)
                        delete_ref("ORIG_HEAD", NULL, 0);
        }
         * session is in progress, they should be written last.
         */
  
 -      write_file(am_path(state, "next"), 1, "%d", state->cur);
 -
 -      write_file(am_path(state, "last"), 1, "%d", state->last);
 +      write_state_count(state, "next", state->cur);
 +      write_state_count(state, "last", state->last);
  
        strbuf_release(&sb);
  }
@@@ -1118,12 -1095,12 +1118,12 @@@ static void am_next(struct am_state *st
        unlink(am_path(state, "original-commit"));
  
        if (!get_sha1("HEAD", head))
 -              write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(head));
 +              write_state_text(state, "abort-safety", sha1_to_hex(head));
        else
 -              write_file(am_path(state, "abort-safety"), 1, "%s", "");
 +              write_state_text(state, "abort-safety", "");
  
        state->cur++;
 -      write_file(am_path(state, "next"), 1, "%d", state->cur);
 +      write_state_count(state, "next", state->cur);
  }
  
  /**
@@@ -1208,45 -1185,6 +1208,45 @@@ static void NORETURN die_user_resolve(c
        exit(128);
  }
  
 +static void am_signoff(struct strbuf *sb)
 +{
 +      char *cp;
 +      struct strbuf mine = STRBUF_INIT;
 +
 +      /* Does it end with our own sign-off? */
 +      strbuf_addf(&mine, "\n%s%s\n",
 +                  sign_off_header,
 +                  fmt_name(getenv("GIT_COMMITTER_NAME"),
 +                           getenv("GIT_COMMITTER_EMAIL")));
 +      if (mine.len < sb->len &&
 +          !strcmp(mine.buf, sb->buf + sb->len - mine.len))
 +              goto exit; /* no need to duplicate */
 +
 +      /* Does it have any Signed-off-by: in the text */
 +      for (cp = sb->buf;
 +           cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
 +           cp = strchr(cp, '\n')) {
 +              if (sb->buf == cp || cp[-1] == '\n')
 +                      break;
 +      }
 +
 +      strbuf_addstr(sb, mine.buf + !!cp);
 +exit:
 +      strbuf_release(&mine);
 +}
 +
 +/**
 + * Appends signoff to the "msg" field of the am_state.
 + */
 +static void am_append_signoff(struct am_state *state)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
 +      am_signoff(&sb);
 +      state->msg = strbuf_detach(&sb, &state->msg_len);
 +}
 +
  /**
   * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
   * state->msg will be set to the patch message. state->author_name,
  static int parse_mail(struct am_state *state, const char *mail)
  {
        FILE *fp;
 -      struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf sb = STRBUF_INIT;
        struct strbuf msg = STRBUF_INIT;
        struct strbuf author_name = STRBUF_INIT;
        struct strbuf author_date = STRBUF_INIT;
        struct strbuf author_email = STRBUF_INIT;
        int ret = 0;
 +      struct mailinfo mi;
  
 -      cp.git_cmd = 1;
 -      cp.in = xopen(mail, O_RDONLY, 0);
 -      cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
 +      setup_mailinfo(&mi);
  
 -      argv_array_push(&cp.args, "mailinfo");
 -      argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
 +      if (state->utf8)
 +              mi.metainfo_charset = get_commit_output_encoding();
 +      else
 +              mi.metainfo_charset = NULL;
  
        switch (state->keep) {
        case KEEP_FALSE:
                break;
        case KEEP_TRUE:
 -              argv_array_push(&cp.args, "-k");
 +              mi.keep_subject = 1;
                break;
        case KEEP_NON_PATCH:
 -              argv_array_push(&cp.args, "-b");
 +              mi.keep_non_patch_brackets_in_subject = 1;
                break;
        default:
                die("BUG: invalid value for state->keep");
        }
  
        if (state->message_id)
 -              argv_array_push(&cp.args, "-m");
 +              mi.add_message_id = 1;
  
        switch (state->scissors) {
        case SCISSORS_UNSET:
                break;
        case SCISSORS_FALSE:
 -              argv_array_push(&cp.args, "--no-scissors");
 +              mi.use_scissors = 0;
                break;
        case SCISSORS_TRUE:
 -              argv_array_push(&cp.args, "--scissors");
 +              mi.use_scissors = 1;
                break;
        default:
                die("BUG: invalid value for state->scissors");
        }
  
 -      argv_array_push(&cp.args, am_path(state, "msg"));
 -      argv_array_push(&cp.args, am_path(state, "patch"));
 -
 -      if (run_command(&cp) < 0)
 +      mi.input = fopen(mail, "r");
 +      if (!mi.input)
 +              die("could not open input");
 +      mi.output = fopen(am_path(state, "info"), "w");
 +      if (!mi.output)
 +              die("could not open output 'info'");
 +      if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
                die("could not parse patch");
  
 -      close(cp.in);
 -      close(cp.out);
 +      fclose(mi.input);
 +      fclose(mi.output);
  
        /* Extract message and author information */
        fp = xfopen(am_path(state, "info"), "r");
        }
  
        strbuf_addstr(&msg, "\n\n");
 -      if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
 -              die_errno(_("could not read '%s'"), am_path(state, "msg"));
 -      stripspace(&msg, 0);
 +      strbuf_addbuf(&msg, &mi.log_message);
 +      strbuf_stripspace(&msg, 0);
  
        if (state->signoff)
 -              append_signoff(&msg, 0, 0);
 +              am_signoff(&msg);
  
        assert(!state->author_name);
        state->author_name = strbuf_detach(&author_name, NULL);
@@@ -1369,7 -1305,6 +1369,7 @@@ finish
        strbuf_release(&author_email);
        strbuf_release(&author_name);
        strbuf_release(&sb);
 +      clear_mailinfo(&mi);
        return ret;
  }
  
@@@ -1441,7 -1376,7 +1441,7 @@@ static void get_commit_info(struct am_s
        assert(!state->msg);
        msg = strstr(buffer, "\n\n");
        if (!msg)
 -              die(_("unable to parse commit %s"), sha1_to_hex(commit->object.sha1));
 +              die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
        state->msg = xstrdup(msg + 2);
        state->msg_len = strlen(state->msg);
  }
@@@ -1526,7 -1461,8 +1526,7 @@@ static int parse_mail_rebase(struct am_
        write_commit_patch(state, commit);
  
        hashcpy(state->orig_commit, commit_sha1);
 -      write_file(am_path(state, "original-commit"), 1, "%s",
 -                      sha1_to_hex(commit_sha1));
 +      write_state_text(state, "original-commit", sha1_to_hex(commit_sha1));
  
        return 0;
  }
@@@ -1593,38 -1529,6 +1593,38 @@@ static int build_fake_ancestor(const st
        return 0;
  }
  
 +/**
 + * Do the three-way merge using fake ancestor, his tree constructed
 + * from the fake ancestor and the postimage of the patch, and our
 + * state.
 + */
 +static int run_fallback_merge_recursive(const struct am_state *state,
 +                                      unsigned char *orig_tree,
 +                                      unsigned char *our_tree,
 +                                      unsigned char *his_tree)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      int status;
 +
 +      cp.git_cmd = 1;
 +
 +      argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
 +                       sha1_to_hex(his_tree), linelen(state->msg), state->msg);
 +      if (state->quiet)
 +              argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
 +
 +      argv_array_push(&cp.args, "merge-recursive");
 +      argv_array_push(&cp.args, sha1_to_hex(orig_tree));
 +      argv_array_push(&cp.args, "--");
 +      argv_array_push(&cp.args, sha1_to_hex(our_tree));
 +      argv_array_push(&cp.args, sha1_to_hex(his_tree));
 +
 +      status = run_command(&cp) ? (-1) : 0;
 +      discard_cache();
 +      read_cache();
 +      return status;
 +}
 +
  /**
   * Attempt a threeway merge, using index_path as the temporary index.
   */
@@@ -1632,6 -1536,10 +1632,6 @@@ static int fall_back_threeway(const str
  {
        unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
                      our_tree[GIT_SHA1_RAWSZ];
 -      const unsigned char *bases[1] = {orig_tree};
 -      struct merge_options o;
 -      struct commit *result;
 -      char *his_tree_name;
  
        if (get_sha1("HEAD", our_tree) < 0)
                hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
  
                init_revisions(&rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
 -              diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1);
 +              diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
                add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
                diff_setup_done(&rev_info.diffopt);
                run_diff_index(&rev_info, 1);
         * changes.
         */
  
 -      init_merge_options(&o);
 -
 -      o.branch1 = "HEAD";
 -      his_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
 -      o.branch2 = his_tree_name;
 -
 -      if (state->quiet)
 -              o.verbosity = 0;
 -
 -      if (merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result)) {
 +      if (run_fallback_merge_recursive(state, orig_tree, our_tree, his_tree)) {
                rerere(state->allow_rerere_autoupdate);
 -              free(his_tree_name);
                return error(_("Failed to merge in the changes."));
        }
  
 -      free(his_tree_name);
        return 0;
  }
  
@@@ -1821,7 -1740,7 +1821,7 @@@ static int do_interactive(struct am_sta
  
                        if (!pager)
                                pager = "cat";
-                       argv_array_push(&cp.args, pager);
+                       prepare_pager_args(&cp, pager);
                        argv_array_push(&cp.args, am_path(state, "patch"));
                        run_command(&cp);
                }
@@@ -1845,7 -1764,7 +1845,7 @@@ static void am_run(struct am_state *sta
        refresh_and_write_cache();
  
        if (index_has_changes(&sb)) {
 -              write_file(am_path(state, "dirtyindex"), 1, "t");
 +              write_state_bool(state, "dirtyindex", 1);
                die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf);
        }
  
  
                if (resume) {
                        validate_resume_state(state);
 -                      resume = 0;
                } else {
                        int skip;
  
  
  next:
                am_next(state);
 +
 +              if (resume)
 +                      am_load(state);
 +              resume = 0;
        }
  
        if (!is_empty_file(am_path(state, "rewritten"))) {
         */
        if (!state->rebasing) {
                am_destroy(state);
 +              close_all_packs();
                run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        }
  }
@@@ -1980,7 -1895,6 +1980,7 @@@ static void am_resolve(struct am_state 
  
  next:
        am_next(state);
 +      am_load(state);
        am_run(state, 0);
  }
  
@@@ -2025,49 -1939,16 +2025,49 @@@ static int fast_forward_to(struct tree 
        return 0;
  }
  
 +/**
 + * Merges a tree into the index. The index's stat info will take precedence
 + * over the merged tree's. Returns 0 on success, -1 on failure.
 + */
 +static int merge_tree(struct tree *tree)
 +{
 +      struct lock_file *lock_file;
 +      struct unpack_trees_options opts;
 +      struct tree_desc t[1];
 +
 +      if (parse_tree(tree))
 +              return -1;
 +
 +      lock_file = xcalloc(1, sizeof(struct lock_file));
 +      hold_locked_index(lock_file, 1);
 +
 +      memset(&opts, 0, sizeof(opts));
 +      opts.head_idx = 1;
 +      opts.src_index = &the_index;
 +      opts.dst_index = &the_index;
 +      opts.merge = 1;
 +      opts.fn = oneway_merge;
 +      init_tree_desc(&t[0], tree->buffer, tree->size);
 +
 +      if (unpack_trees(1, t, &opts)) {
 +              rollback_lock_file(lock_file);
 +              return -1;
 +      }
 +
 +      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +              die(_("unable to write new index file"));
 +
 +      return 0;
 +}
 +
  /**
   * Clean the index without touching entries that are not modified between
   * `head` and `remote`.
   */
  static int clean_index(const unsigned char *head, const unsigned char *remote)
  {
 -      struct lock_file *lock_file;
        struct tree *head_tree, *remote_tree, *index_tree;
        unsigned char index[GIT_SHA1_RAWSZ];
 -      struct pathspec pathspec;
  
        head_tree = parse_tree_indirect(head);
        if (!head_tree)
        if (fast_forward_to(index_tree, remote_tree, 0))
                return -1;
  
 -      memset(&pathspec, 0, sizeof(pathspec));
 -
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, 1);
 -
 -      if (read_tree(remote_tree, 0, &pathspec)) {
 -              rollback_lock_file(lock_file);
 +      if (merge_tree(remote_tree))
                return -1;
 -      }
 -
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 -              die(_("unable to write new index file"));
  
        remove_branch_state();
  
  static void am_rerere_clear(void)
  {
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
 -      int fd = setup_rerere(&merge_rr, 0);
 -
 -      if (fd < 0)
 -              return;
 -
        rerere_clear(&merge_rr);
        string_list_clear(&merge_rr, 1);
  }
@@@ -2126,7 -2022,6 +2126,7 @@@ static void am_skip(struct am_state *st
                die(_("failed to clean index"));
  
        am_next(state);
 +      am_load(state);
        am_run(state, 0);
  }
  
@@@ -2230,17 -2125,6 +2230,17 @@@ enum resume_mode 
        RESUME_ABORT
  };
  
 +static int git_am_config(const char *k, const char *v, void *cb)
 +{
 +      int status;
 +
 +      status = git_gpg_config(k, v, NULL);
 +      if (status)
 +              return status;
 +
 +      return git_default_config(k, v, NULL);
 +}
 +
  int cmd_am(int argc, const char **argv, const char *prefix)
  {
        struct am_state state;
        int keep_cr = -1;
        int patch_format = PATCH_FORMAT_UNKNOWN;
        enum resume_mode resume = RESUME_FALSE;
 +      int in_progress;
  
        const char * const usage[] = {
 -              N_("git am [options] [(<mbox>|<Maildir>)...]"),
 -              N_("git am [options] (--continue | --skip | --abort)"),
 +              N_("git am [<options>] [(<mbox>|<Maildir>)...]"),
 +              N_("git am [<options>] (--continue | --skip | --abort)"),
                NULL
        };
  
                OPT_BOOL('i', "interactive", &state.interactive,
                        N_("run interactively")),
                OPT_HIDDEN_BOOL('b', "binary", &binary,
 -                      N_("(historical option -- no-op")),
 +                      N_("historical option -- no-op")),
                OPT_BOOL('3', "3way", &state.threeway,
                        N_("allow fall back on 3way merging if needed")),
                OPT__QUIET(&state.quiet, N_("be quiet")),
 -              OPT_BOOL('s', "signoff", &state.signoff,
 -                      N_("add a Signed-off-by line to the commit message")),
 +              OPT_SET_INT('s', "signoff", &state.signoff,
 +                      N_("add a Signed-off-by line to the commit message"),
 +                      SIGNOFF_EXPLICIT),
                OPT_BOOL('u', "utf8", &state.utf8,
                        N_("recode into utf8 (default)")),
                OPT_SET_INT('k', "keep", &state.keep,
                OPT_END()
        };
  
 -      git_config(git_default_config, NULL);
 +      git_config(git_am_config, NULL);
  
        am_state_init(&state, git_path("rebase-apply"));
  
 +      in_progress = am_in_progress(&state);
 +      if (in_progress)
 +              am_load(&state);
 +
        argc = parse_options(argc, argv, prefix, options, usage, 0);
  
        if (binary >= 0)
        if (read_index_preload(&the_index, NULL) < 0)
                die(_("failed to read the index"));
  
 -      if (am_in_progress(&state)) {
 +      if (in_progress) {
                /*
                 * Catch user error to feed us patches when there is a session
                 * in progress:
                if (resume == RESUME_FALSE)
                        resume = RESUME_APPLY;
  
 -              am_load(&state);
 +              if (state.signoff == SIGNOFF_EXPLICIT)
 +                      am_append_signoff(&state);
        } else {
                struct argv_array paths = ARGV_ARRAY_INIT;
                int i;
diff --combined cache.h
index e2e64d188efdcfef25a5a9523b7324166a8863ca,a839accbd65b146bcb7d3d439128e44fc409266c..1f145c2c6a9bcefe21caf1e681267b4e86911136
+++ b/cache.h
  #include "string-list.h"
  
  #include SHA1_HEADER
 -#ifndef git_SHA_CTX
 -#define git_SHA_CTX   SHA_CTX
 -#define git_SHA1_Init SHA1_Init
 -#define git_SHA1_Update       SHA1_Update
 -#define git_SHA1_Final        SHA1_Final
 +#ifndef platform_SHA_CTX
 +/*
 + * platform's underlying implementation of SHA-1; could be OpenSSL,
 + * blk_SHA, Apple CommonCrypto, etc...  Note that including
 + * SHA1_HEADER may have already defined platform_SHA_CTX for our
 + * own implementations like block-sha1 and ppc-sha1, so we list
 + * the default for OpenSSL compatible SHA-1 implementations here.
 + */
 +#define platform_SHA_CTX      SHA_CTX
 +#define platform_SHA1_Init    SHA1_Init
 +#define platform_SHA1_Update  SHA1_Update
 +#define platform_SHA1_Final           SHA1_Final
 +#endif
 +
 +#define git_SHA_CTX           platform_SHA_CTX
 +#define git_SHA1_Init         platform_SHA1_Init
 +#define git_SHA1_Update               platform_SHA1_Update
 +#define git_SHA1_Final                platform_SHA1_Final
 +
 +#ifdef SHA1_MAX_BLOCK_SIZE
 +#include "compat/sha1-chunked.h"
 +#undef git_SHA1_Update
 +#define git_SHA1_Update               git_SHA1_Update_Chunked
  #endif
  
  #include <zlib.h>
@@@ -214,7 -196,7 +214,7 @@@ struct cache_entry 
  #define CE_INTENT_TO_ADD     (1 << 29)
  #define CE_SKIP_WORKTREE     (1 << 30)
  /* CE_EXTENDED2 is for future extension */
 -#define CE_EXTENDED2         (1 << 31)
 +#define CE_EXTENDED2         (1U << 31)
  
  #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
  #error "CE_EXTENDED_FLAGS out of range"
  #endif
  
+ /* Forward structure decls */
  struct pathspec;
+ struct child_process;
  
  /*
   * Copy the sha1 and stat state of a cache entry from one to
@@@ -259,7 -243,6 +261,7 @@@ static inline unsigned create_ce_flags(
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 +#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
  static inline unsigned int create_ce_mode(unsigned int mode)
@@@ -457,35 -440,16 +459,35 @@@ extern char *git_work_tree_cfg
  extern int is_inside_work_tree(void);
  extern const char *get_git_dir(void);
  extern const char *get_git_common_dir(void);
 -extern int is_git_directory(const char *path);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
 +extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
  extern int get_common_dir(struct strbuf *sb, const char *gitdir);
  extern const char *get_git_namespace(void);
  extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
  
 +/*
 + * Return true if the given path is a git directory; note that this _just_
 + * looks at the directory itself. If you want to know whether "foo/.git"
 + * is a repository, you must feed that path, not just "foo".
 + */
 +extern int is_git_directory(const char *path);
 +
 +/*
 + * Return 1 if the given path is the root of a git repository or
 + * submodule, else 0. Will not return 1 for bare repositories with the
 + * exception of creating a bare repository in "foo/.git" and calling
 + * is_git_repository("foo").
 + *
 + * If we run into read errors, we err on the side of saying "yes, it is",
 + * as we usually consider sub-repos precious, and would prefer to err on the
 + * side of not disrupting or deleting them.
 + */
 +extern int is_nonbare_repository_dir(struct strbuf *path);
 +
  #define READ_GITFILE_ERR_STAT_FAILED 1
  #define READ_GITFILE_ERR_NOT_A_FILE 2
  #define READ_GITFILE_ERR_OPEN_FAILED 3
@@@ -558,8 -522,7 +560,8 @@@ extern int write_locked_index(struct in
  extern int discard_index(struct index_state *);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
 -extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
 +extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
 +extern void adjust_dirname_case(struct index_state *istate, char *name);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
@@@ -735,15 -698,8 +737,15 @@@ extern char *notes_ref_name
  
  extern int grafts_replace_parents;
  
 +/*
 + * GIT_REPO_VERSION is the version we write by default. The
 + * _READ variant is the highest number we know how to
 + * handle.
 + */
  #define GIT_REPO_VERSION 0
 +#define GIT_REPO_VERSION_READ 1
  extern int repository_format_version;
 +extern int repository_format_precious_objects;
  extern int check_repository_format(void);
  
  #define MTIME_CHANGED 0x0001
  #define DATA_CHANGED    0x0020
  #define TYPE_CHANGED    0x0040
  
 +/*
 + * Return a statically allocated filename, either generically (mkpath), in
 + * the repository directory (git_path), or in a submodule's repository
 + * directory (git_path_submodule). In all cases, note that the result
 + * may be overwritten by another call to _any_ of the functions. Consider
 + * using the safer "dup" or "strbuf" formats below (in some cases, the
 + * unsafe versions have already been removed).
 + */
 +extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +
  extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __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, ...)
 +      __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
  extern char *mkpathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 -
 -/* Return a statically allocated filename matching the sha1 signature */
 -extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 -extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 -extern const char *git_path_submodule(const char *path, const char *fmt, ...)
 +extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 +
  extern void report_linked_checkout_garbage(void);
  
 +/*
 + * You can define a static memoized git path like:
 + *
 + *    static GIT_PATH_FUNC(git_path_foo, "FOO");
 + *
 + * or use one of the global ones below.
 + */
 +#define GIT_PATH_FUNC(func, filename) \
 +      const char *func(void) \
 +      { \
 +              static char *ret; \
 +              if (!ret) \
 +                      ret = git_pathdup(filename); \
 +              return ret; \
 +      }
 +
 +const char *git_path_cherry_pick_head(void);
 +const char *git_path_revert_head(void);
 +const char *git_path_squash_msg(void);
 +const char *git_path_merge_msg(void);
 +const char *git_path_merge_rr(void);
 +const char *git_path_merge_mode(void);
 +const char *git_path_merge_head(void);
 +const char *git_path_fetch_head(void);
 +const char *git_path_shallow(void);
 +
  /*
   * Return the name of the file in the local object database that would
   * be used to store a loose object with the specified sha1.  The
@@@ -831,26 -748,8 +833,26 @@@ extern char *sha1_pack_name(const unsig
   */
  extern char *sha1_pack_index_name(const unsigned char *sha1);
  
 -extern const char *find_unique_abbrev(const unsigned char *sha1, int);
 +/*
 + * Return an abbreviated sha1 unique within this repository's object database.
 + * The result will be at least `len` characters long, and will be NUL
 + * terminated.
 + *
 + * The non-`_r` version returns a static buffer which will be overwritten by
 + * subsequent calls.
 + *
 + * The `_r` variant writes to a buffer supplied by the caller, which must be at
 + * least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes
 + * written (excluding the NUL terminator).
 + *
 + * Note that while this version avoids the static buffer, it is not fully
 + * reentrant, as it calls into other non-reentrant git code.
 + */
 +extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
 +extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
 +
  extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
 +extern const struct object_id null_oid;
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
@@@ -1048,7 -947,7 +1050,7 @@@ extern int do_check_packed_object_crc
  
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
 -extern int move_temp_to_file(const char *tmpfile, const char *filename);
 +extern int finalize_object_file(const char *tmpfile, const char *filename);
  
  extern int has_sha1_pack(const unsigned char *sha1);
  
@@@ -1068,9 -967,6 +1070,9 @@@ static inline int has_sha1_file(const u
        return has_sha1_file_with_flags(sha1, 0);
  }
  
 +/* Same as the above, except for struct object_id. */
 +extern int has_object_file(const struct object_id *oid);
 +
  /*
   * Return true iff an alternate object database has a loose object
   * with the specified name.  This function does not respect replace
@@@ -1134,18 -1030,6 +1136,18 @@@ extern int for_each_abbrev(const char *
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
 +/*
 + * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
 + * and writes the NUL-terminated output to the buffer `out`, which must be at
 + * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
 + * convenience.
 + *
 + * The non-`_r` variant returns a static buffer, but uses a ring of 4
 + * buffers, making it safe to make multiple calls for a single statement, like:
 + *
 + *   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 *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 */
  
@@@ -1172,6 -1056,7 +1174,6 @@@ struct date_mode 
                DATE_NORMAL = 0,
                DATE_RELATIVE,
                DATE_SHORT,
 -              DATE_LOCAL,
                DATE_ISO8601,
                DATE_ISO8601_STRICT,
                DATE_RFC2822,
                DATE_RAW
        } type;
        const char *strftime_fmt;
 +      int local;
  };
  
  /*
@@@ -1330,11 -1214,8 +1332,11 @@@ struct pack_entry 
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
 -/* A hook for count-objects to report invalid files in pack directory */
 -extern void (*report_garbage)(const char *desc, const char *path);
 +/* A hook to report invalid files in pack directory */
 +#define PACKDIR_FILE_PACK 1
 +#define PACKDIR_FILE_IDX 2
 +#define PACKDIR_FILE_GARBAGE 4
 +extern void (*report_garbage)(unsigned seen_bits, const char *path);
  
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
@@@ -1359,11 -1240,10 +1361,11 @@@ extern void close_pack_index(struct pac
  
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  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 *, int, int);
 +extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
  
  /*
   * Return the SHA-1 of the nth object within the specified packfile.
@@@ -1485,7 -1365,7 +1487,7 @@@ extern int update_server_info(int)
  /* git_config_parse_key() returns these negated: */
  #define CONFIG_INVALID_KEY 1
  #define CONFIG_NO_SECTION_OR_NAME 2
 -/* git_config_set(), git_config_set_multivar() return the above or these: */
 +/* git_config_set_gently(), git_config_set_multivar_gently() return the above or these: */
  #define CONFIG_NO_LOCK -1
  #define CONFIG_INVALID_FILE 3
  #define CONFIG_NO_WRITE 4
@@@ -1514,7 -1394,6 +1516,7 @@@ extern int git_config_with_options(conf
                                   int respect_includes);
  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_parse_maybe_bool(const char *);
  extern int git_config_int(const char *, const char *);
  extern int64_t git_config_int64(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
@@@ -1523,16 -1402,11 +1525,16 @@@ extern int git_config_bool(const char *
  extern int git_config_maybe_bool(const char *, const char *);
  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_in_file(const char *, const char *, const char *);
 -extern int git_config_set(const char *, const char *);
 +extern int git_config_set_in_file_gently(const char *, const char *, const char *);
 +extern void git_config_set_in_file(const char *, const char *, const char *);
 +extern int git_config_set_gently(const char *, const char *);
 +extern void 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_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
 +extern int git_config_key_is_valid(const char *key);
 +extern int git_config_set_multivar_gently(const char *, const char *, const char *, int);
 +extern void git_config_set_multivar(const char *, const char *, const char *, int);
 +extern int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
 +extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern int git_config_rename_section_in_file(const char *, const char *, const char *);
  extern const char *git_etc_gitconfig(void);
@@@ -1667,9 -1541,8 +1669,9 @@@ static inline ssize_t write_str_in_full
  {
        return write_in_full(fd, str, strlen(str));
  }
 -__attribute__((format (printf, 3, 4)))
 -extern int write_file(const char *path, int fatal, const char *fmt, ...);
 +
 +extern int write_file(const char *path, const char *fmt, ...);
 +extern int write_file_gently(const char *path, const char *fmt, ...);
  
  /* pager.c */
  extern void setup_pager(void);
@@@ -1679,6 -1552,7 +1681,7 @@@ extern int pager_use_color
  extern int term_columns(void);
  extern int decimal_width(uintmax_t);
  extern int check_pager_config(const char *cmd);
+ extern void prepare_pager_args(struct child_process *, const char *pager);
  
  extern const char *editor_program;
  extern const char *askpass_program;
@@@ -1795,12 -1669,4 +1798,12 @@@ void stat_validity_update(struct stat_v
  int versioncmp(const char *s1, const char *s2);
  void sleep_millisec(int millisec);
  
 +/*
 + * Create a directory and (if share is nonzero) adjust its permissions
 + * according to the shared_repository setting. Only use this for
 + * directories under $GIT_DIR.  Don't use it for working tree
 + * directories.
 + */
 +void safe_create_dir(const char *dir, int share);
 +
  #endif /* CACHE_H */
diff --combined pager.c
index e425070528f4f9fbf0953c9056d1ea43686e176f,cb28207c48b2b9adafc5a874ea9f5afc9a55842c..4bc048148e043eabf1315bbcbae8ea4c6363b330
+++ b/pager.c
   * something different on Windows.
   */
  
- static const char *pager_argv[] = { NULL, NULL };
  static struct child_process pager_process = CHILD_PROCESS_INIT;
  
 -static void wait_for_pager(void)
 +static void wait_for_pager(int in_signal)
  {
 -      fflush(stdout);
 -      fflush(stderr);
 +      if (!in_signal) {
 +              fflush(stdout);
 +              fflush(stderr);
 +      }
        /* signal EOF to pager */
        close(1);
        close(2);
 -      finish_command(&pager_process);
 +      if (in_signal)
 +              finish_command_in_signal(&pager_process);
 +      else
 +              finish_command(&pager_process);
 +}
 +
 +static void wait_for_pager_atexit(void)
 +{
 +      wait_for_pager(0);
  }
  
  static void wait_for_pager_signal(int signo)
  {
 -      wait_for_pager();
 +      wait_for_pager(1);
        sigchain_pop(signo);
        raise(signo);
  }
@@@ -64,6 -53,16 +63,16 @@@ const char *git_pager(int stdout_is_tty
        return pager;
  }
  
+ void prepare_pager_args(struct child_process *pager_process, const char *pager)
+ {
+       argv_array_push(&pager_process->args, pager);
+       pager_process->use_shell = 1;
+       if (!getenv("LESS"))
+               argv_array_push(&pager_process->env_array, "LESS=FRX");
+       if (!getenv("LV"))
+               argv_array_push(&pager_process->env_array, "LV=-c");
+ }
  void setup_pager(void)
  {
        const char *pager = git_pager(isatty(1));
        setenv("GIT_PAGER_IN_USE", "true", 1);
  
        /* spawn the pager */
-       pager_argv[0] = pager;
-       pager_process.use_shell = 1;
-       pager_process.argv = pager_argv;
+       prepare_pager_args(&pager_process, pager);
        pager_process.in = -1;
-       if (!getenv("LESS"))
-               argv_array_push(&pager_process.env_array, "LESS=FRX");
-       if (!getenv("LV"))
-               argv_array_push(&pager_process.env_array, "LV=-c");
        argv_array_push(&pager_process.env_array, "GIT_PAGER_IN_USE");
        if (start_command(&pager_process))
                return;
  
        /* this makes sure that the parent terminates after the pager */
        sigchain_push_common(wait_for_pager_signal);
 -      atexit(wait_for_pager);
 +      atexit(wait_for_pager_atexit);
  }
  
  int pager_in_use(void)
@@@ -160,8 -153,7 +163,8 @@@ int check_pager_config(const char *cmd
        struct strbuf key = STRBUF_INIT;
        const char *value = NULL;
        strbuf_addf(&key, "pager.%s", cmd);
 -      if (!git_config_get_value(key.buf, &value)) {
 +      if (git_config_key_is_valid(key.buf) &&
 +          !git_config_get_value(key.buf, &value)) {
                int b = git_config_maybe_bool(key.buf, value);
                if (b >= 0)
                        want = b;