Merge branch 'rs/apply-avoid-over-reading'
authorJunio C Hamano <gitster@pobox.com>
Fri, 7 Jul 2017 01:14:45 +0000 (18:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 7 Jul 2017 01:14:45 +0000 (18:14 -0700)
Code clean-up to fix possible buffer over-reading.

* rs/apply-avoid-over-reading:
apply: use starts_with() in gitdiff_verify_name()

1  2 
apply.c
diff --combined apply.c
index c442b8932823ac0bc104d094a2b372a974ee8217,4024e70c54408dee28b0f9f41d11115476925ba1..946be4d2f553813a3df7b513c816f002591fd89f
+++ b/apply.c
@@@ -8,7 -8,6 +8,7 @@@
   */
  
  #include "cache.h"
 +#include "config.h"
  #include "blob.h"
  #include "delta.h"
  #include "diff.h"
@@@ -28,7 -27,7 +28,7 @@@ static void git_apply_config(void
        git_config(git_default_config, NULL);
  }
  
 -int parse_whitespace_option(struct apply_state *state, const char *option)
 +static int parse_whitespace_option(struct apply_state *state, const char *option)
  {
        if (!option) {
                state->ws_error_action = warn_on_ws_error;
@@@ -58,8 -57,8 +58,8 @@@
        return error(_("unrecognized whitespace option '%s'"), option);
  }
  
 -int parse_ignorewhitespace_option(struct apply_state *state,
 -                                const char *option)
 +static int parse_ignorewhitespace_option(struct apply_state *state,
 +                                               const char *option)
  {
        if (!option || !strcmp(option, "no") ||
            !strcmp(option, "false") || !strcmp(option, "never") ||
@@@ -113,29 -112,21 +113,29 @@@ void clear_apply_state(struct apply_sta
        /* &state->fn_table is cleared at the end of apply_patch() */
  }
  
 +static void mute_routine(const char *msg, va_list params)
 +{
 +      /* do nothing */
 +}
 +
  int check_apply_state(struct apply_state *state, int force_apply)
  {
        int is_not_gitdir = !startup_info->have_repository;
  
        if (state->apply_with_reject && state->threeway)
 -              return error("--reject and --3way cannot be used together.");
 +              return error(_("--reject and --3way cannot be used together."));
        if (state->cached && state->threeway)
 -              return error("--cached and --3way cannot be used together.");
 +              return error(_("--cached and --3way cannot be used together."));
        if (state->threeway) {
                if (is_not_gitdir)
                        return error(_("--3way outside a repository"));
                state->check_index = 1;
        }
 -      if (state->apply_with_reject)
 -              state->apply = state->apply_verbosely = 1;
 +      if (state->apply_with_reject) {
 +              state->apply = 1;
 +              if (state->apply_verbosity == verbosity_normal)
 +                      state->apply_verbosity = verbosity_verbose;
 +      }
        if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
                state->apply = 0;
        if (state->check_index && is_not_gitdir)
        if (!state->lock_file)
                return error("BUG: state->lock_file should not be NULL");
  
 +      if (state->apply_verbosity <= verbosity_silent) {
 +              state->saved_error_routine = get_error_routine();
 +              state->saved_warn_routine = get_warn_routine();
 +              set_error_routine(mute_routine);
 +              set_warn_routine(mute_routine);
 +      }
 +
        return 0;
  }
  
@@@ -211,7 -195,6 +211,7 @@@ struct patch 
        unsigned ws_rule;
        int lines_added, lines_deleted;
        int score;
 +      int extension_linenr; /* first line specifying delete/new/rename/copy */
        unsigned int is_toplevel_relative:1;
        unsigned int inaccurate_eof:1;
        unsigned int is_binary:1;
@@@ -764,6 -747,17 +764,6 @@@ static char *find_name_traditional(stru
        return find_name_common(state, line, def, p_value, line + len, 0);
  }
  
 -static int count_slashes(const char *cp)
 -{
 -      int cnt = 0;
 -      char ch;
 -
 -      while ((ch = *cp++))
 -              if (ch == '/')
 -                      cnt++;
 -      return cnt;
 -}
 -
  /*
   * Given the string after "--- " or "+++ ", guess the appropriate
   * p_value for the given patch.
@@@ -976,8 -970,7 +976,7 @@@ static int gitdiff_verify_name(struct a
                }
                free(another);
        } else {
-               /* expect "/dev/null" */
-               if (memcmp("/dev/null", line, 9) || line[9] != '\n')
+               if (!starts_with(line, "/dev/null\n"))
                        return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
  
@@@ -1002,27 -995,20 +1001,27 @@@ static int gitdiff_newname(struct apply
                                   DIFF_NEW_NAME);
  }
  
 +static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
 +{
 +      char *end;
 +      *mode = strtoul(line, &end, 8);
 +      if (end == line || !isspace(*end))
 +              return error(_("invalid mode on line %d: %s"), linenr, line);
 +      return 0;
 +}
 +
  static int gitdiff_oldmode(struct apply_state *state,
                           const char *line,
                           struct patch *patch)
  {
 -      patch->old_mode = strtoul(line, NULL, 8);
 -      return 0;
 +      return parse_mode_line(line, state->linenr, &patch->old_mode);
  }
  
  static int gitdiff_newmode(struct apply_state *state,
                           const char *line,
                           struct patch *patch)
  {
 -      patch->new_mode = strtoul(line, NULL, 8);
 -      return 0;
 +      return parse_mode_line(line, state->linenr, &patch->new_mode);
  }
  
  static int gitdiff_delete(struct apply_state *state,
@@@ -1136,7 -1122,7 +1135,7 @@@ static int gitdiff_index(struct apply_s
        memcpy(patch->new_sha1_prefix, line, len);
        patch->new_sha1_prefix[len] = 0;
        if (*ptr == ' ')
 -              patch->old_mode = strtoul(ptr+1, NULL, 8);
 +              return gitdiff_oldmode(state, ptr + 1, patch);
        return 0;
  }
  
@@@ -1320,18 -1306,6 +1319,18 @@@ static char *git_header_name(struct app
        }
  }
  
 +static int check_header_line(struct apply_state *state, struct patch *patch)
 +{
 +      int extensions = (patch->is_delete == 1) + (patch->is_new == 1) +
 +                       (patch->is_rename == 1) + (patch->is_copy == 1);
 +      if (extensions > 1)
 +              return error(_("inconsistent header lines %d and %d"),
 +                           patch->extension_linenr, state->linenr);
 +      if (extensions && !patch->extension_linenr)
 +              patch->extension_linenr = state->linenr;
 +      return 0;
 +}
 +
  /* Verify that we recognize the lines following a git header */
  static int parse_git_header(struct apply_state *state,
                            const char *line,
                        res = p->fn(state, line + oplen, patch);
                        if (res < 0)
                                return -1;
 +                      if (check_header_line(state, patch))
 +                              return -1;
                        if (res > 0)
                                return offset;
                        break;
@@@ -1597,10 -1569,9 +1596,10 @@@ static int find_header(struct apply_sta
                                patch->old_name = xstrdup(patch->def_name);
                                patch->new_name = xstrdup(patch->def_name);
                        }
 -                      if (!patch->is_delete && !patch->new_name) {
 -                              error("git diff header lacks filename information "
 -                                           "(line %d)", state->linenr);
 +                      if ((!patch->new_name && !patch->is_delete) ||
 +                          (!patch->old_name && !patch->is_new)) {
 +                              error(_("git diff header lacks filename information "
 +                                           "(line %d)"), state->linenr);
                                return -128;
                        }
                        patch->is_toplevel_relative = 1;
@@@ -1648,9 -1619,8 +1647,9 @@@ static void record_ws_error(struct appl
                return;
  
        err = whitespace_error_string(result);
 -      fprintf(stderr, "%s:%d: %s.\n%.*s\n",
 -              state->patch_input_file, linenr, err, len, line);
 +      if (state->apply_verbosity > verbosity_silent)
 +              fprintf(stderr, "%s:%d: %s.\n%.*s\n",
 +                      state->patch_input_file, linenr, err, len, line);
        free(err);
  }
  
@@@ -1845,7 -1815,7 +1844,7 @@@ static int parse_single_patch(struct ap
                return error(_("new file %s depends on old contents"), patch->new_name);
        if (0 < patch->is_delete && newlines)
                return error(_("deleted file %s still has contents"), patch->old_name);
 -      if (!patch->is_delete && !newlines && context)
 +      if (!patch->is_delete && !newlines && context && state->apply_verbosity > verbosity_silent)
                fprintf_ln(stderr,
                           _("** warning: "
                             "file %s becomes empty but is not deleted"),
@@@ -2059,7 -2029,7 +2058,7 @@@ static void prefix_one(struct apply_sta
        char *old_name = *name;
        if (!old_name)
                return;
 -      *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name));
 +      *name = prefix_filename(state->prefix, *name);
        free(old_name);
  }
  
@@@ -2200,20 -2170,29 +2199,20 @@@ static int parse_chunk(struct apply_sta
        return offset + hdrsize + patchsize;
  }
  
 -#define swap(a,b) myswap((a),(b),sizeof(a))
 -
 -#define myswap(a, b, size) do {               \
 -      unsigned char mytmp[size];      \
 -      memcpy(mytmp, &a, size);                \
 -      memcpy(&a, &b, size);           \
 -      memcpy(&b, mytmp, size);                \
 -} while (0)
 -
  static void reverse_patches(struct patch *p)
  {
        for (; p; p = p->next) {
                struct fragment *frag = p->fragments;
  
 -              swap(p->new_name, p->old_name);
 -              swap(p->new_mode, p->old_mode);
 -              swap(p->is_new, p->is_delete);
 -              swap(p->lines_added, p->lines_deleted);
 -              swap(p->old_sha1_prefix, p->new_sha1_prefix);
 +              SWAP(p->new_name, p->old_name);
 +              SWAP(p->new_mode, p->old_mode);
 +              SWAP(p->is_new, p->is_delete);
 +              SWAP(p->lines_added, p->lines_deleted);
 +              SWAP(p->old_sha1_prefix, p->new_sha1_prefix);
  
                for (; frag; frag = frag->next) {
 -                      swap(frag->newpos, frag->oldpos);
 -                      swap(frag->newlines, frag->oldlines);
 +                      SWAP(frag->newpos, frag->oldpos);
 +                      SWAP(frag->newlines, frag->oldlines);
                }
        }
  }
@@@ -2280,7 -2259,7 +2279,7 @@@ static int read_old_data(struct stat *s
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
                        return error(_("unable to open or read %s"), path);
 -              convert_to_git(path, buf->buf, buf->len, buf, 0);
 +              convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0);
                return 0;
        default:
                return -1;
@@@ -2931,7 -2910,7 +2930,7 @@@ static int apply_one_fragment(struct ap
                        /* Ignore it, we already handled it */
                        break;
                default:
 -                      if (state->apply_verbosely)
 +                      if (state->apply_verbosity > verbosity_normal)
                                error(_("invalid start of line: '%c'"), first);
                        applied_pos = -1;
                        goto out;
                                state->apply = 0;
                }
  
 -              if (state->apply_verbosely && applied_pos != pos) {
 +              if (state->apply_verbosity > verbosity_normal && applied_pos != pos) {
                        int offset = applied_pos - pos;
                        if (state->apply_in_reverse)
                                offset = 0 - offset;
                 * Warn if it was necessary to reduce the number
                 * of context lines.
                 */
 -              if ((leading != frag->leading) ||
 -                  (trailing != frag->trailing))
 +              if ((leading != frag->leading ||
 +                   trailing != frag->trailing) && state->apply_verbosity > verbosity_silent)
                        fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
                                             " to apply fragment at %d"),
                                   leading, trailing, applied_pos+1);
                update_image(state, img, applied_pos, &preimage, &postimage);
        } else {
 -              if (state->apply_verbosely)
 +              if (state->apply_verbosity > verbosity_normal)
                        error(_("while searching for:\n%.*s"),
                              (int)(old - oldlines), oldlines);
        }
@@@ -3099,8 -3078,8 +3098,8 @@@ static int apply_binary_fragment(struc
        /* Binary patch is irreversible without the optional second hunk */
        if (state->apply_in_reverse) {
                if (!fragment->next)
 -                      return error("cannot reverse-apply a binary patch "
 -                                   "without the reverse hunk to '%s'",
 +                      return error(_("cannot reverse-apply a binary patch "
 +                                     "without the reverse hunk to '%s'"),
                                     patch->new_name
                                     ? patch->new_name : patch->old_name);
                fragment = fragment->next;
@@@ -3135,7 -3114,7 +3134,7 @@@ static int apply_binary(struct apply_st
                        struct patch *patch)
  {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        /*
         * For safety, we require patch index line to contain
         */
        if (strlen(patch->old_sha1_prefix) != 40 ||
            strlen(patch->new_sha1_prefix) != 40 ||
 -          get_sha1_hex(patch->old_sha1_prefix, sha1) ||
 -          get_sha1_hex(patch->new_sha1_prefix, sha1))
 -              return error("cannot apply binary patch to '%s' "
 -                           "without full index line", name);
 +          get_oid_hex(patch->old_sha1_prefix, &oid) ||
 +          get_oid_hex(patch->new_sha1_prefix, &oid))
 +              return error(_("cannot apply binary patch to '%s' "
 +                             "without full index line"), name);
  
        if (patch->old_name) {
                /*
                 * See if the old one matches what the patch
                 * applies to.
                 */
 -              hash_sha1_file(img->buf, img->len, blob_type, sha1);
 -              if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
 -                      return error("the patch applies to '%s' (%s), "
 -                                   "which does not match the "
 -                                   "current contents.",
 -                                   name, sha1_to_hex(sha1));
 +              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
 +                      return error(_("the patch applies to '%s' (%s), "
 +                                     "which does not match the "
 +                                     "current contents."),
 +                                   name, oid_to_hex(&oid));
        }
        else {
                /* Otherwise, the old one must be empty. */
                if (img->len)
 -                      return error("the patch applies to an empty "
 -                                   "'%s' but it is not empty", name);
 +                      return error(_("the patch applies to an empty "
 +                                     "'%s' but it is not empty"), name);
        }
  
 -      get_sha1_hex(patch->new_sha1_prefix, sha1);
 -      if (is_null_sha1(sha1)) {
 +      get_oid_hex(patch->new_sha1_prefix, &oid);
 +      if (is_null_oid(&oid)) {
                clear_image(img);
                return 0; /* deletion patch */
        }
  
 -      if (has_sha1_file(sha1)) {
 +      if (has_sha1_file(oid.hash)) {
                /* We already have the postimage */
                enum object_type type;
                unsigned long size;
                char *result;
  
 -              result = read_sha1_file(sha1, &type, &size);
 +              result = read_sha1_file(oid.hash, &type, &size);
                if (!result)
 -                      return error("the necessary postimage %s for "
 -                                   "'%s' cannot be read",
 +                      return error(_("the necessary postimage %s for "
 +                                     "'%s' cannot be read"),
                                     patch->new_sha1_prefix, name);
                clear_image(img);
                img->buf = result;
                                     name);
  
                /* verify that the result matches */
 -              hash_sha1_file(img->buf, img->len, blob_type, sha1);
 -              if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
 +              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
 -                              name, patch->new_sha1_prefix, sha1_to_hex(sha1));
 +                              name, patch->new_sha1_prefix, oid_to_hex(&oid));
        }
  
        return 0;
@@@ -3231,17 -3210,17 +3230,17 @@@ static int apply_fragments(struct apply
        return 0;
  }
  
 -static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode)
 +static int read_blob_object(struct strbuf *buf, const struct object_id *oid, unsigned mode)
  {
        if (S_ISGITLINK(mode)) {
                strbuf_grow(buf, 100);
 -              strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1));
 +              strbuf_addf(buf, "Subproject commit %s\n", oid_to_hex(oid));
        } else {
                enum object_type type;
                unsigned long sz;
                char *result;
  
 -              result = read_sha1_file(sha1, &type, &sz);
 +              result = read_sha1_file(oid->hash, &type, &sz);
                if (!result)
                        return -1;
                /* XXX read_sha1_file NUL-terminates */
@@@ -3254,7 -3233,7 +3253,7 @@@ static int read_file_or_gitlink(const s
  {
        if (!ce)
                return 0;
 -      return read_blob_object(buf, ce->sha1, ce->ce_mode);
 +      return read_blob_object(buf, &ce->oid, ce->ce_mode);
  }
  
  static struct patch *in_fn_table(struct apply_state *state, const char *name)
@@@ -3338,8 -3317,10 +3337,8 @@@ static void prepare_fn_table(struct app
  static int checkout_target(struct index_state *istate,
                           struct cache_entry *ce, struct stat *st)
  {
 -      struct checkout costate;
 +      struct checkout costate = CHECKOUT_INIT;
  
 -      memset(&costate, 0, sizeof(costate));
 -      costate.base_dir = "";
        costate.refresh_cache = 1;
        costate.istate = istate;
        if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
@@@ -3459,9 -3440,9 +3458,9 @@@ static int load_preimage(struct apply_s
  
  static int three_way_merge(struct image *image,
                           char *path,
 -                         const unsigned char *base,
 -                         const unsigned char *ours,
 -                         const unsigned char *theirs)
 +                         const struct object_id *base,
 +                         const struct object_id *ours,
 +                         const struct object_id *theirs)
  {
        mmfile_t base_file, our_file, their_file;
        mmbuffer_t result = { NULL };
@@@ -3515,7 -3496,7 +3514,7 @@@ static int load_current(struct apply_st
        ce = active_cache[pos];
        if (lstat(name, &st)) {
                if (errno != ENOENT)
 -                      return error(_("%s: %s"), name, strerror(errno));
 +                      return error_errno("%s", name);
                if (checkout_target(&the_index, ce, &st))
                        return -1;
        }
@@@ -3538,7 -3519,7 +3537,7 @@@ static int try_threeway(struct apply_st
                        struct stat *st,
                        const struct cache_entry *ce)
  {
 -      unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
 +      struct object_id pre_oid, post_oid, our_oid;
        struct strbuf buf = STRBUF_INIT;
        size_t len;
        int status;
  
        /* Preimage the patch was prepared for */
        if (patch->is_new)
 -              write_sha1_file("", 0, blob_type, pre_sha1);
 -      else if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
 -               read_blob_object(&buf, pre_sha1, patch->old_mode))
 -              return error("repository lacks the necessary blob to fall back on 3-way merge.");
 +              write_sha1_file("", 0, blob_type, pre_oid.hash);
 +      else if (get_sha1(patch->old_sha1_prefix, pre_oid.hash) ||
 +               read_blob_object(&buf, &pre_oid, patch->old_mode))
 +              return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
  
 -      fprintf(stderr, "Falling back to three-way merge...\n");
 +      if (state->apply_verbosity > verbosity_silent)
 +              fprintf(stderr, _("Falling back to three-way merge...\n"));
  
        img = strbuf_detach(&buf, &len);
        prepare_image(&tmp_image, img, len, 1);
                clear_image(&tmp_image);
                return -1;
        }
 -      /* post_sha1[] is theirs */
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1);
 +      /* post_oid is theirs */
 +      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_oid.hash);
        clear_image(&tmp_image);
  
 -      /* our_sha1[] is ours */
 +      /* our_oid is ours */
        if (patch->is_new) {
                if (load_current(state, &tmp_image, patch))
 -                      return error("cannot read the current contents of '%s'",
 +                      return error(_("cannot read the current contents of '%s'"),
                                     patch->new_name);
        } else {
                if (load_preimage(state, &tmp_image, patch, st, ce))
 -                      return error("cannot read the current contents of '%s'",
 +                      return error(_("cannot read the current contents of '%s'"),
                                     patch->old_name);
        }
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);
 +      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_oid.hash);
        clear_image(&tmp_image);
  
        /* in-core three-way merge between post and our using pre as base */
        status = three_way_merge(image, patch->new_name,
 -                               pre_sha1, our_sha1, post_sha1);
 +                               &pre_oid, &our_oid, &post_oid);
        if (status < 0) {
 -              fprintf(stderr, "Failed to fall back on three-way merge...\n");
 +              if (state->apply_verbosity > verbosity_silent)
 +                      fprintf(stderr,
 +                              _("Failed to fall back on three-way merge...\n"));
                return status;
        }
  
                if (patch->is_new)
                        oidclr(&patch->threeway_stage[0]);
                else
 -                      hashcpy(patch->threeway_stage[0].hash, pre_sha1);
 -              hashcpy(patch->threeway_stage[1].hash, our_sha1);
 -              hashcpy(patch->threeway_stage[2].hash, post_sha1);
 -              fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
 +                      oidcpy(&patch->threeway_stage[0], &pre_oid);
 +              oidcpy(&patch->threeway_stage[1], &our_oid);
 +              oidcpy(&patch->threeway_stage[2], &post_oid);
 +              if (state->apply_verbosity > verbosity_silent)
 +                      fprintf(stderr,
 +                              _("Applied patch to '%s' with conflicts.\n"),
 +                              patch->new_name);
        } else {
 -              fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
 +              if (state->apply_verbosity > verbosity_silent)
 +                      fprintf(stderr,
 +                              _("Applied patch to '%s' cleanly.\n"),
 +                              patch->new_name);
        }
        return 0;
  }
@@@ -3674,7 -3646,7 +3673,7 @@@ static int check_preimage(struct apply_
        } else if (!state->cached) {
                stat_ret = lstat(old_name, st);
                if (stat_ret && errno != ENOENT)
 -                      return error(_("%s: %s"), old_name, strerror(errno));
 +                      return error_errno("%s", old_name);
        }
  
        if (state->check_index && !previous) {
        } else if (stat_ret < 0) {
                if (patch->is_new < 0)
                        goto is_new;
 -              return error(_("%s: %s"), old_name, strerror(errno));
 +              return error_errno("%s", old_name);
        }
  
        if (!state->cached && !previous)
   is_new:
        patch->is_new = 1;
        patch->is_delete = 0;
 -      free(patch->old_name);
 -      patch->old_name = NULL;
 +      FREE_AND_NULL(patch->old_name);
        return 0;
  }
  
@@@ -3753,8 -3726,8 +3752,8 @@@ static int check_to_create(struct apply
                        return 0;
  
                return EXISTS_IN_WORKTREE;
 -      } else if ((errno != ENOENT) && (errno != ENOTDIR)) {
 -              return error("%s: %s", new_name, strerror(errno));
 +      } else if (!is_missing_file_error(errno)) {
 +              return error_errno("%s", new_name);
        }
        return 0;
  }
@@@ -3982,7 -3955,7 +3981,7 @@@ static int check_patch_list(struct appl
        prepare_fn_table(state, patch);
        while (patch) {
                int res;
 -              if (state->apply_verbosely)
 +              if (state->apply_verbosity > verbosity_normal)
                        say_patch_name(stderr,
                                       _("Checking patch %s..."), patch);
                res = check_patch(state, patch);
        return err;
  }
  
 -/* This function tries to read the sha1 from the current index */
 -static int get_current_sha1(const char *path, unsigned char *sha1)
 +static int read_apply_cache(struct apply_state *state)
 +{
 +      if (state->index_file)
 +              return read_cache_from(state->index_file);
 +      else
 +              return read_cache();
 +}
 +
 +/* This function tries to read the object name from the current index */
 +static int get_current_oid(struct apply_state *state, const char *path,
 +                         struct object_id *oid)
  {
        int pos;
  
 -      if (read_cache() < 0)
 +      if (read_apply_cache(state) < 0)
                return -1;
        pos = cache_name_pos(path, strlen(path));
        if (pos < 0)
                return -1;
 -      hashcpy(sha1, active_cache[pos]->sha1);
 +      oidcpy(oid, &active_cache[pos]->oid);
        return 0;
  }
  
 -static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20])
 +static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
  {
        /*
         * A usable gitlink patch has only one fragment (hunk) that looks like:
            (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
            starts_with(++preimage, heading) &&
            /* does it record full SHA-1? */
 -          !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
 -          preimage[sizeof(heading) + 40 - 1] == '\n' &&
 +          !get_oid_hex(preimage + sizeof(heading) - 1, oid) &&
 +          preimage[sizeof(heading) + GIT_SHA1_HEXSZ - 1] == '\n' &&
            /* does the abbreviated name on the index line agree with it? */
            starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
                return 0; /* it all looks fine */
  
        /* we may have full object name on the index line */
 -      return get_sha1_hex(p->old_sha1_prefix, sha1);
 +      return get_oid_hex(p->old_sha1_prefix, oid);
  }
  
  /* Build an index that contains the just the files needed for a 3way merge */
 -static int build_fake_ancestor(struct patch *list, const char *filename)
 +static int build_fake_ancestor(struct apply_state *state, struct patch *list)
  {
        struct patch *patch;
        struct index_state result = { NULL };
         * worth showing the new sha1 prefix, but until then...
         */
        for (patch = list; patch; patch = patch->next) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                struct cache_entry *ce;
                const char *name;
  
                        continue;
  
                if (S_ISGITLINK(patch->old_mode)) {
 -                      if (!preimage_sha1_in_gitlink_patch(patch, sha1))
 +                      if (!preimage_oid_in_gitlink_patch(patch, &oid))
                                ; /* ok, the textual part looks sane */
                        else
 -                              return error("sha1 information is lacking or "
 -                                           "useless for submodule %s", name);
 -              } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) {
 +                              return error(_("sha1 information is lacking or "
 +                                             "useless for submodule %s"), name);
 +              } else if (!get_sha1_blob(patch->old_sha1_prefix, oid.hash)) {
                        ; /* ok */
                } else if (!patch->lines_added && !patch->lines_deleted) {
                        /* mode-only change: update the current */
 -                      if (get_current_sha1(patch->old_name, sha1))
 -                              return error("mode change for %s, which is not "
 -                                           "in current HEAD", name);
 +                      if (get_current_oid(state, patch->old_name, &oid))
 +                              return error(_("mode change for %s, which is not "
 +                                             "in current HEAD"), name);
                } else
 -                      return error("sha1 information is lacking or useless "
 -                                   "(%s).", name);
 +                      return error(_("sha1 information is lacking or useless "
 +                                     "(%s)."), name);
  
 -              ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0);
 +              ce = make_cache_entry(patch->old_mode, oid.hash, name, 0, 0);
                if (!ce)
                        return error(_("make_cache_entry failed for path '%s'"),
                                     name);
                if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) {
                        free(ce);
 -                      return error("Could not add %s to temporary index",
 +                      return error(_("could not add %s to temporary index"),
                                     name);
                }
        }
  
 -      hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
 +      hold_lock_file_for_update(&lock, state->fake_ancestor, LOCK_DIE_ON_ERROR);
        res = write_locked_index(&result, &lock, COMMIT_LOCK);
        discard_index(&result);
  
        if (res)
 -              return error("Could not write temporary index to %s", filename);
 +              return error(_("could not write temporary index to %s"),
 +                           state->fake_ancestor);
  
        return 0;
  }
@@@ -4275,21 -4238,21 +4274,21 @@@ static int add_index_file(struct apply_
                const char *s;
  
                if (!skip_prefix(buf, "Subproject commit ", &s) ||
 -                  get_sha1_hex(s, ce->sha1)) {
 +                  get_oid_hex(s, &ce->oid)) {
                        free(ce);
 -                      return error(_("corrupt patch for submodule %s"), path);
 +                     return error(_("corrupt patch for submodule %s"), path);
                }
        } else {
                if (!state->cached) {
                        if (lstat(path, &st) < 0) {
                                free(ce);
 -                              return error(_("unable to stat newly "
 -                                             "created file '%s': %s"),
 -                                           path, strerror(errno));
 +                              return error_errno(_("unable to stat newly "
 +                                                   "created file '%s'"),
 +                                                 path);
                        }
                        fill_stat_cache_info(ce, &st);
                }
 -              if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) {
 +              if (write_sha1_file(buf, size, blob_type, ce->oid.hash) < 0) {
                        free(ce);
                        return error(_("unable to create backing store "
                                       "for newly created file %s"), path);
@@@ -4438,7 -4401,7 +4437,7 @@@ static int add_conflicted_stages_file(s
                ce->ce_mode = create_ce_mode(mode);
                ce->ce_flags = create_ce_flags(stage);
                ce->ce_namelen = namelen;
 -              hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash);
 +              oidcpy(&ce->oid, &patch->threeway_stage[stage - 1]);
                if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) {
                        free(ce);
                        return error(_("unable to add cache entry for %s"),
@@@ -4508,7 -4471,7 +4507,7 @@@ static int write_out_one_reject(struct 
        }
  
        if (!cnt) {
 -              if (state->apply_verbosely)
 +              if (state->apply_verbosity > verbosity_normal)
                        say_patch_name(stderr,
                                       _("Applied patch %s cleanly."), patch);
                return 0;
                            "Applying patch %%s with %d rejects...",
                            cnt),
                    cnt);
 -      say_patch_name(stderr, sb.buf, patch);
 +      if (state->apply_verbosity > verbosity_silent)
 +              say_patch_name(stderr, sb.buf, patch);
        strbuf_release(&sb);
  
        cnt = strlen(patch->new_name);
  
        rej = fopen(namebuf, "w");
        if (!rej)
 -              return error(_("cannot open %s: %s"), namebuf, strerror(errno));
 +              return error_errno(_("cannot open %s"), namebuf);
  
        /* Normal git tools never deal with .rej, so do not pretend
         * this is a git patch by saying --git or giving extended
             frag;
             cnt++, frag = frag->next) {
                if (!frag->rejected) {
 -                      fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
 +                      if (state->apply_verbosity > verbosity_silent)
 +                              fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
                        continue;
                }
 -              fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
 +              if (state->apply_verbosity > verbosity_silent)
 +                      fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
                fprintf(rej, "%.*s", frag->size, frag->patch);
                if (frag->patch[frag->size-1] != '\n')
                        fputc('\n', rej);
@@@ -4607,10 -4567,8 +4606,10 @@@ static int write_out_results(struct app
                struct string_list_item *item;
  
                string_list_sort(&cpath);
 -              for_each_string_list_item(item, &cpath)
 -                      fprintf(stderr, "U %s\n", item->string);
 +              if (state->apply_verbosity > verbosity_silent) {
 +                      for_each_string_list_item(item, &cpath)
 +                              fprintf(stderr, "U %s\n", item->string);
 +              }
                string_list_clear(&cpath, 0);
  
                rerere(0);
@@@ -4667,7 -4625,7 +4666,7 @@@ static int apply_patch(struct apply_sta
                        listp = &patch->next;
                }
                else {
 -                      if (state->apply_verbosely)
 +                      if (state->apply_verbosity > verbosity_normal)
                                say_patch_name(stderr, _("Skipped patch '%s'."), patch);
                        free_patch(patch);
                        skipped_patch++;
                state->apply = 0;
  
        state->update_index = state->check_index && state->apply;
 -      if (state->update_index && state->newfd < 0)
 -              state->newfd = hold_locked_index(state->lock_file, 1);
 +      if (state->update_index && state->newfd < 0) {
 +              if (state->index_file)
 +                      state->newfd = hold_lock_file_for_update(state->lock_file,
 +                                                               state->index_file,
 +                                                               LOCK_DIE_ON_ERROR);
 +              else
 +                      state->newfd = hold_locked_index(state->lock_file, LOCK_DIE_ON_ERROR);
 +      }
  
 -      if (state->check_index && read_cache() < 0) {
 +      if (state->check_index && read_apply_cache(state) < 0) {
                error(_("unable to read index file"));
                res = -128;
                goto end;
        }
  
        if (state->fake_ancestor &&
 -          build_fake_ancestor(list, state->fake_ancestor)) {
 +          build_fake_ancestor(state, list)) {
                res = -128;
                goto end;
        }
  
 -      if (state->diffstat)
 +      if (state->diffstat && state->apply_verbosity > verbosity_silent)
                stat_patch_list(state, list);
  
 -      if (state->numstat)
 +      if (state->numstat && state->apply_verbosity > verbosity_silent)
                numstat_patch_list(state, list);
  
 -      if (state->summary)
 +      if (state->summary && state->apply_verbosity > verbosity_silent)
                summary_patch_list(list);
  
  end:
        return res;
  }
  
 -int apply_option_parse_exclude(const struct option *opt,
 -                             const char *arg, int unset)
 +static int apply_option_parse_exclude(const struct option *opt,
 +                                    const char *arg, int unset)
  {
        struct apply_state *state = opt->value;
        add_name_limit(state, arg, 1);
        return 0;
  }
  
 -int apply_option_parse_include(const struct option *opt,
 -                             const char *arg, int unset)
 +static int apply_option_parse_include(const struct option *opt,
 +                                    const char *arg, int unset)
  {
        struct apply_state *state = opt->value;
        add_name_limit(state, arg, 0);
        return 0;
  }
  
 -int apply_option_parse_p(const struct option *opt,
 -                       const char *arg,
 -                       int unset)
 +static int apply_option_parse_p(const struct option *opt,
 +                              const char *arg,
 +                              int unset)
  {
        struct apply_state *state = opt->value;
        state->p_value = atoi(arg);
        return 0;
  }
  
 -int apply_option_parse_space_change(const struct option *opt,
 -                                  const char *arg, int unset)
 +static int apply_option_parse_space_change(const struct option *opt,
 +                                         const char *arg, int unset)
  {
        struct apply_state *state = opt->value;
        if (unset)
        return 0;
  }
  
 -int apply_option_parse_whitespace(const struct option *opt,
 -                                const char *arg, int unset)
 +static int apply_option_parse_whitespace(const struct option *opt,
 +                                       const char *arg, int unset)
  {
        struct apply_state *state = opt->value;
        state->whitespace_option = arg;
        return 0;
  }
  
 -int apply_option_parse_directory(const struct option *opt,
 -                               const char *arg, int unset)
 +static int apply_option_parse_directory(const struct option *opt,
 +                                      const char *arg, int unset)
  {
        struct apply_state *state = opt->value;
        strbuf_reset(&state->root);
@@@ -4817,7 -4769,6 +4816,7 @@@ int apply_all_patches(struct apply_stat
  
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
 +              char *to_free = NULL;
                int fd;
  
                if (!strcmp(arg, "-")) {
                        errs |= res;
                        read_stdin = 0;
                        continue;
 -              } else if (0 < state->prefix_length)
 -                      arg = prefix_filename(state->prefix,
 -                                            state->prefix_length,
 -                                            arg);
 +              } else
 +                      arg = to_free = prefix_filename(state->prefix, arg);
  
                fd = open(arg, O_RDONLY);
                if (fd < 0) {
                        error(_("can't open patch '%s': %s"), arg, strerror(errno));
                        res = -128;
 +                      free(to_free);
                        goto end;
                }
                read_stdin = 0;
                set_default_whitespace_mode(state);
                res = apply_patch(state, fd, arg, options);
                close(fd);
 +              free(to_free);
                if (res < 0)
                        goto end;
                errs |= res;
                        goto end;
                }
                if (state->applied_after_fixing_ws && state->apply)
 -                      warning("%d line%s applied after"
 -                              " fixing whitespace errors.",
 -                              state->applied_after_fixing_ws,
 -                              state->applied_after_fixing_ws == 1 ? "" : "s");
 +                      warning(Q_("%d line applied after"
 +                                 " fixing whitespace errors.",
 +                                 "%d lines applied after"
 +                                 " fixing whitespace errors.",
 +                                 state->applied_after_fixing_ws),
 +                              state->applied_after_fixing_ws);
                else if (state->whitespace_error)
                        warning(Q_("%d line adds whitespace errors.",
                                   "%d lines add whitespace errors.",
                state->newfd = -1;
        }
  
 -      return !!errs;
 +      res = !!errs;
  
  end:
        if (state->newfd >= 0) {
                state->newfd = -1;
        }
  
 +      if (state->apply_verbosity <= verbosity_silent) {
 +              set_error_routine(state->saved_error_routine);
 +              set_warn_routine(state->saved_warn_routine);
 +      }
 +
 +      if (res > -1)
 +              return res;
        return (res == -1 ? 1 : 128);
  }
 +
 +int apply_parse_options(int argc, const char **argv,
 +                      struct apply_state *state,
 +                      int *force_apply, int *options,
 +                      const char * const *apply_usage)
 +{
 +      struct option builtin_apply_options[] = {
 +              { OPTION_CALLBACK, 0, "exclude", state, N_("path"),
 +                      N_("don't apply changes matching the given path"),
 +                      0, apply_option_parse_exclude },
 +              { OPTION_CALLBACK, 0, "include", state, N_("path"),
 +                      N_("apply changes matching the given path"),
 +                      0, apply_option_parse_include },
 +              { OPTION_CALLBACK, 'p', NULL, state, N_("num"),
 +                      N_("remove <num> leading slashes from traditional diff paths"),
 +                      0, apply_option_parse_p },
 +              OPT_BOOL(0, "no-add", &state->no_add,
 +                      N_("ignore additions made by the patch")),
 +              OPT_BOOL(0, "stat", &state->diffstat,
 +                      N_("instead of applying the patch, output diffstat for the input")),
 +              OPT_NOOP_NOARG(0, "allow-binary-replacement"),
 +              OPT_NOOP_NOARG(0, "binary"),
 +              OPT_BOOL(0, "numstat", &state->numstat,
 +                      N_("show number of added and deleted lines in decimal notation")),
 +              OPT_BOOL(0, "summary", &state->summary,
 +                      N_("instead of applying the patch, output a summary for the input")),
 +              OPT_BOOL(0, "check", &state->check,
 +                      N_("instead of applying the patch, see if the patch is applicable")),
 +              OPT_BOOL(0, "index", &state->check_index,
 +                      N_("make sure the patch is applicable to the current index")),
 +              OPT_BOOL(0, "cached", &state->cached,
 +                      N_("apply a patch without touching the working tree")),
 +              OPT_BOOL(0, "unsafe-paths", &state->unsafe_paths,
 +                      N_("accept a patch that touches outside the working area")),
 +              OPT_BOOL(0, "apply", force_apply,
 +                      N_("also apply the patch (use with --stat/--summary/--check)")),
 +              OPT_BOOL('3', "3way", &state->threeway,
 +                       N_( "attempt three-way merge if a patch does not apply")),
 +              OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor,
 +                      N_("build a temporary index based on embedded index information")),
 +              /* Think twice before adding "--nul" synonym to this */
 +              OPT_SET_INT('z', NULL, &state->line_termination,
 +                      N_("paths are separated with NUL character"), '\0'),
 +              OPT_INTEGER('C', NULL, &state->p_context,
 +                              N_("ensure at least <n> lines of context match")),
 +              { OPTION_CALLBACK, 0, "whitespace", state, N_("action"),
 +                      N_("detect new or modified lines that have whitespace errors"),
 +                      0, apply_option_parse_whitespace },
 +              { OPTION_CALLBACK, 0, "ignore-space-change", state, NULL,
 +                      N_("ignore changes in whitespace when finding context"),
 +                      PARSE_OPT_NOARG, apply_option_parse_space_change },
 +              { OPTION_CALLBACK, 0, "ignore-whitespace", state, NULL,
 +                      N_("ignore changes in whitespace when finding context"),
 +                      PARSE_OPT_NOARG, apply_option_parse_space_change },
 +              OPT_BOOL('R', "reverse", &state->apply_in_reverse,
 +                      N_("apply the patch in reverse")),
 +              OPT_BOOL(0, "unidiff-zero", &state->unidiff_zero,
 +                      N_("don't expect at least one line of context")),
 +              OPT_BOOL(0, "reject", &state->apply_with_reject,
 +                      N_("leave the rejected hunks in corresponding *.rej files")),
 +              OPT_BOOL(0, "allow-overlap", &state->allow_overlap,
 +                      N_("allow overlapping hunks")),
 +              OPT__VERBOSE(&state->apply_verbosity, N_("be verbose")),
 +              OPT_BIT(0, "inaccurate-eof", options,
 +                      N_("tolerate incorrectly detected missing new-line at the end of file"),
 +                      APPLY_OPT_INACCURATE_EOF),
 +              OPT_BIT(0, "recount", options,
 +                      N_("do not trust the line counts in the hunk headers"),
 +                      APPLY_OPT_RECOUNT),
 +              { OPTION_CALLBACK, 0, "directory", state, N_("root"),
 +                      N_("prepend <root> to all filenames"),
 +                      0, apply_option_parse_directory },
 +              OPT_END()
 +      };
 +
 +      return parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0);
 +}