Merge branch 'jk/string-list-static-init'
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 Jul 2016 20:38:07 +0000 (13:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Jul 2016 20:38:08 +0000 (13:38 -0700)
Instead of taking advantage of a struct string_list that is
allocated with all NULs happens to be STRING_LIST_INIT_NODUP kind,
initialize them explicitly as such, to document their behaviour
better.

* jk/string-list-static-init:
use string_list initializer consistently
blame,shortlog: don't make local option variables static
interpret-trailers: don't duplicate option strings
parse_opt_string_list: stop allocating new strings

1  2 
builtin/apply.c
builtin/blame.c
builtin/log.c
builtin/remote.c
t/helper/test-parse-options.c
diff --combined builtin/apply.c
index ecb7f1b258cb802c934dc37f8d6af511e7b33ed1,955af4db6cc11321cae85bf320ea25fb1220d4d6..7fc5c3e30745d50a05accb3a83ccfa8871963df6
  #include "ll-merge.h"
  #include "rerere.h"
  
 -/*
 - *  --check turns on checking that the working tree matches the
 - *    files that are being modified, but doesn't apply the patch
 - *  --stat does just a diffstat, and doesn't actually apply
 - *  --numstat does numeric diffstat, and doesn't actually apply
 - *  --index-info shows the old and new index info for paths if available.
 - *  --index updates the cache as well.
 - *  --cached updates only the cache without ever touching the working tree.
 - */
 -static const char *prefix;
 -static int prefix_length = -1;
 -static int newfd = -1;
 -
 -static int unidiff_zero;
 -static int p_value = 1;
 -static int p_value_known;
 -static int check_index;
 -static int update_index;
 -static int cached;
 -static int diffstat;
 -static int numstat;
 -static int summary;
 -static int check;
 -static int apply = 1;
 -static int apply_in_reverse;
 -static int apply_with_reject;
 -static int apply_verbosely;
 -static int allow_overlap;
 -static int no_add;
 -static int threeway;
 -static int unsafe_paths;
 -static const char *fake_ancestor;
 -static int line_termination = '\n';
 -static unsigned int p_context = UINT_MAX;
 -static const char * const apply_usage[] = {
 -      N_("git apply [<options>] [<patch>...]"),
 -      NULL
 -};
 -
 -static enum ws_error_action {
 +enum ws_error_action {
        nowarn_ws_error,
        warn_on_ws_error,
        die_on_ws_error,
        correct_ws_error
 -} ws_error_action = warn_on_ws_error;
 -static int whitespace_error;
 -static int squelch_whitespace_errors = 5;
 -static int applied_after_fixing_ws;
 +};
 +
  
 -static enum ws_ignore {
 +enum ws_ignore {
        ignore_ws_none,
        ignore_ws_change
 -} ws_ignore_action = ignore_ws_none;
 +};
 +
 +/*
 + * We need to keep track of how symlinks in the preimage are
 + * manipulated by the patches.  A patch to add a/b/c where a/b
 + * is a symlink should not be allowed to affect the directory
 + * the symlink points at, but if the same patch removes a/b,
 + * it is perfectly fine, as the patch removes a/b to make room
 + * to create a directory a/b so that a/b/c can be created.
 + *
 + * See also "struct string_list symlink_changes" in "struct
 + * apply_state".
 + */
 +#define SYMLINK_GOES_AWAY 01
 +#define SYMLINK_IN_RESULT 02
  
 +struct apply_state {
 +      const char *prefix;
 +      int prefix_length;
 +
 +      /* These are lock_file related */
 +      struct lock_file *lock_file;
 +      int newfd;
 +
 +      /* These control what gets looked at and modified */
 +      int apply; /* this is not a dry-run */
 +      int cached; /* apply to the index only */
 +      int check; /* preimage must match working tree, don't actually apply */
 +      int check_index; /* preimage must match the indexed version */
 +      int update_index; /* check_index && apply */
 +
 +      /* These control cosmetic aspect of the output */
 +      int diffstat; /* just show a diffstat, and don't actually apply */
 +      int numstat; /* just show a numeric diffstat, and don't actually apply */
 +      int summary; /* just report creation, deletion, etc, and don't actually apply */
 +
 +      /* These boolean parameters control how the apply is done */
 +      int allow_overlap;
 +      int apply_in_reverse;
 +      int apply_with_reject;
 +      int apply_verbosely;
 +      int no_add;
 +      int threeway;
 +      int unidiff_zero;
 +      int unsafe_paths;
 +
 +      /* Other non boolean parameters */
 +      const char *fake_ancestor;
 +      const char *patch_input_file;
 +      int line_termination;
 +      struct strbuf root;
 +      int p_value;
 +      int p_value_known;
 +      unsigned int p_context;
 +
 +      /* Exclude and include path parameters */
 +      struct string_list limit_by_name;
 +      int has_include;
 +
 +      /* Various "current state" */
 +      int linenr; /* current line number */
 +      struct string_list symlink_changes; /* we have to track symlinks */
  
 -static const char *patch_input_file;
 -static struct strbuf root = STRBUF_INIT;
 -static int read_stdin = 1;
 -static int options;
 +      /*
 +       * For "diff-stat" like behaviour, we keep track of the biggest change
 +       * we've seen, and the longest filename. That allows us to do simple
 +       * scaling.
 +       */
 +      int max_change;
 +      int max_len;
  
 -static void parse_whitespace_option(const char *option)
 +      /*
 +       * Records filenames that have been touched, in order to handle
 +       * the case where more than one patches touch the same file.
 +       */
 +      struct string_list fn_table;
 +
 +      /* These control whitespace errors */
 +      enum ws_error_action ws_error_action;
 +      enum ws_ignore ws_ignore_action;
 +      const char *whitespace_option;
 +      int whitespace_error;
 +      int squelch_whitespace_errors;
 +      int applied_after_fixing_ws;
 +};
 +
 +static const char * const apply_usage[] = {
 +      N_("git apply [<options>] [<patch>...]"),
 +      NULL
 +};
 +
 +static void parse_whitespace_option(struct apply_state *state, const char *option)
  {
        if (!option) {
 -              ws_error_action = warn_on_ws_error;
 +              state->ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "warn")) {
 -              ws_error_action = warn_on_ws_error;
 +              state->ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "nowarn")) {
 -              ws_error_action = nowarn_ws_error;
 +              state->ws_error_action = nowarn_ws_error;
                return;
        }
        if (!strcmp(option, "error")) {
 -              ws_error_action = die_on_ws_error;
 +              state->ws_error_action = die_on_ws_error;
                return;
        }
        if (!strcmp(option, "error-all")) {
 -              ws_error_action = die_on_ws_error;
 -              squelch_whitespace_errors = 0;
 +              state->ws_error_action = die_on_ws_error;
 +              state->squelch_whitespace_errors = 0;
                return;
        }
        if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
 -              ws_error_action = correct_ws_error;
 +              state->ws_error_action = correct_ws_error;
                return;
        }
        die(_("unrecognized whitespace option '%s'"), option);
  }
  
 -static void parse_ignorewhitespace_option(const char *option)
 +static void parse_ignorewhitespace_option(struct apply_state *state,
 +                                        const char *option)
  {
        if (!option || !strcmp(option, "no") ||
            !strcmp(option, "false") || !strcmp(option, "never") ||
            !strcmp(option, "none")) {
 -              ws_ignore_action = ignore_ws_none;
 +              state->ws_ignore_action = ignore_ws_none;
                return;
        }
        if (!strcmp(option, "change")) {
 -              ws_ignore_action = ignore_ws_change;
 +              state->ws_ignore_action = ignore_ws_change;
                return;
        }
        die(_("unrecognized whitespace ignore option '%s'"), option);
  }
  
 -static void set_default_whitespace_mode(const char *whitespace_option)
 +static void set_default_whitespace_mode(struct apply_state *state)
  {
 -      if (!whitespace_option && !apply_default_whitespace)
 -              ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
 +      if (!state->whitespace_option && !apply_default_whitespace)
 +              state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error);
  }
  
 -/*
 - * For "diff-stat" like behaviour, we keep track of the biggest change
 - * we've seen, and the longest filename. That allows us to do simple
 - * scaling.
 - */
 -static int max_change, max_len;
 -
 -/*
 - * Various "current state", notably line numbers and what
 - * file (and how) we're patching right now.. The "is_xxxx"
 - * things are flags, where -1 means "don't know yet".
 - */
 -static int linenr = 1;
 -
  /*
   * This represents one "hunk" from a patch, starting with
   * "@@ -oldpos,oldlines +newpos,newlines @@" marker.  The
@@@ -294,6 -265,13 +294,6 @@@ struct image 
        struct line *line;
  };
  
 -/*
 - * Records filenames that have been touched, in order to handle
 - * the case where more than one patches touch the same file.
 - */
 -
 -static struct string_list fn_table = STRING_LIST_INIT_NODUP;
 -
  static uint32_t hash_line(const char *cp, size_t len)
  {
        size_t i;
@@@ -464,7 -442,7 +464,7 @@@ static int is_dev_null(const char *str
  #define TERM_SPACE    1
  #define TERM_TAB      2
  
 -static int name_terminate(const char *name, int namelen, int c, int terminate)
 +static int name_terminate(int c, int terminate)
  {
        if (c == ' ' && !(terminate & TERM_SPACE))
                return 0;
@@@ -491,10 -469,7 +491,10 @@@ static char *squash_slash(char *name
        return name;
  }
  
 -static char *find_name_gnu(const char *line, const char *def, int p_value)
 +static char *find_name_gnu(struct apply_state *state,
 +                         const char *line,
 +                         const char *def,
 +                         int p_value)
  {
        struct strbuf name = STRBUF_INIT;
        char *cp;
        }
  
        strbuf_remove(&name, 0, cp - name.buf);
 -      if (root.len)
 -              strbuf_insert(&name, 0, root.buf, root.len);
 +      if (state->root.len)
 +              strbuf_insert(&name, 0, state->root.buf, state->root.len);
        return squash_slash(strbuf_detach(&name, NULL));
  }
  
@@@ -682,12 -657,8 +682,12 @@@ static size_t diff_timestamp_len(const 
        return line + len - end;
  }
  
 -static char *find_name_common(const char *line, const char *def,
 -                            int p_value, const char *end, int terminate)
 +static char *find_name_common(struct apply_state *state,
 +                            const char *line,
 +                            const char *def,
 +                            int p_value,
 +                            const char *end,
 +                            int terminate)
  {
        int len;
        const char *start = NULL;
                if (!end && isspace(c)) {
                        if (c == '\n')
                                break;
 -                      if (name_terminate(start, line-start, c, terminate))
 +                      if (name_terminate(c, terminate))
                                break;
                }
                line++;
                        return squash_slash(xstrdup(def));
        }
  
 -      if (root.len) {
 -              char *ret = xstrfmt("%s%.*s", root.buf, len, start);
 +      if (state->root.len) {
 +              char *ret = xstrfmt("%s%.*s", state->root.buf, len, start);
                return squash_slash(ret);
        }
  
        return squash_slash(xmemdupz(start, len));
  }
  
 -static char *find_name(const char *line, char *def, int p_value, int terminate)
 +static char *find_name(struct apply_state *state,
 +                     const char *line,
 +                     char *def,
 +                     int p_value,
 +                     int terminate)
  {
        if (*line == '"') {
 -              char *name = find_name_gnu(line, def, p_value);
 +              char *name = find_name_gnu(state, line, def, p_value);
                if (name)
                        return name;
        }
  
 -      return find_name_common(line, def, p_value, NULL, terminate);
 +      return find_name_common(state, line, def, p_value, NULL, terminate);
  }
  
 -static char *find_name_traditional(const char *line, char *def, int p_value)
 +static char *find_name_traditional(struct apply_state *state,
 +                                 const char *line,
 +                                 char *def,
 +                                 int p_value)
  {
        size_t len;
        size_t date_len;
  
        if (*line == '"') {
 -              char *name = find_name_gnu(line, def, p_value);
 +              char *name = find_name_gnu(state, line, def, p_value);
                if (name)
                        return name;
        }
        len = strchrnul(line, '\n') - line;
        date_len = diff_timestamp_len(line, len);
        if (!date_len)
 -              return find_name_common(line, def, p_value, NULL, TERM_TAB);
 +              return find_name_common(state, line, def, p_value, NULL, TERM_TAB);
        len -= date_len;
  
 -      return find_name_common(line, def, p_value, line + len, 0);
 +      return find_name_common(state, line, def, p_value, line + len, 0);
  }
  
  static int count_slashes(const char *cp)
   * Given the string after "--- " or "+++ ", guess the appropriate
   * p_value for the given patch.
   */
 -static int guess_p_value(const char *nameline)
 +static int guess_p_value(struct apply_state *state, const char *nameline)
  {
        char *name, *cp;
        int val = -1;
  
        if (is_dev_null(nameline))
                return -1;
 -      name = find_name_traditional(nameline, NULL, 0);
 +      name = find_name_traditional(state, nameline, NULL, 0);
        if (!name)
                return -1;
        cp = strchr(name, '/');
        if (!cp)
                val = 0;
 -      else if (prefix) {
 +      else if (state->prefix) {
                /*
                 * Does it begin with "a/$our-prefix" and such?  Then this is
                 * very likely to apply to our directory.
                 */
 -              if (!strncmp(name, prefix, prefix_length))
 -                      val = count_slashes(prefix);
 +              if (!strncmp(name, state->prefix, state->prefix_length))
 +                      val = count_slashes(state->prefix);
                else {
                        cp++;
 -                      if (!strncmp(cp, prefix, prefix_length))
 -                              val = count_slashes(prefix) + 1;
 +                      if (!strncmp(cp, state->prefix, state->prefix_length))
 +                              val = count_slashes(state->prefix) + 1;
                }
        }
        free(name);
@@@ -896,39 -860,36 +896,39 @@@ static int has_epoch_timestamp(const ch
   * files, we can happily check the index for a match, but for creating a
   * new file we should try to match whatever "patch" does. I have no idea.
   */
 -static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
 +static void parse_traditional_patch(struct apply_state *state,
 +                                  const char *first,
 +                                  const char *second,
 +                                  struct patch *patch)
  {
        char *name;
  
        first += 4;     /* skip "--- " */
        second += 4;    /* skip "+++ " */
 -      if (!p_value_known) {
 +      if (!state->p_value_known) {
                int p, q;
 -              p = guess_p_value(first);
 -              q = guess_p_value(second);
 +              p = guess_p_value(state, first);
 +              q = guess_p_value(state, second);
                if (p < 0) p = q;
                if (0 <= p && p == q) {
 -                      p_value = p;
 -                      p_value_known = 1;
 +                      state->p_value = p;
 +                      state->p_value_known = 1;
                }
        }
        if (is_dev_null(first)) {
                patch->is_new = 1;
                patch->is_delete = 0;
 -              name = find_name_traditional(second, NULL, p_value);
 +              name = find_name_traditional(state, second, NULL, state->p_value);
                patch->new_name = name;
        } else if (is_dev_null(second)) {
                patch->is_new = 0;
                patch->is_delete = 1;
 -              name = find_name_traditional(first, NULL, p_value);
 +              name = find_name_traditional(state, first, NULL, state->p_value);
                patch->old_name = name;
        } else {
                char *first_name;
 -              first_name = find_name_traditional(first, NULL, p_value);
 -              name = find_name_traditional(second, first_name, p_value);
 +              first_name = find_name_traditional(state, first, NULL, state->p_value);
 +              name = find_name_traditional(state, second, first_name, state->p_value);
                free(first_name);
                if (has_epoch_timestamp(first)) {
                        patch->is_new = 1;
                }
        }
        if (!name)
 -              die(_("unable to find filename in patch at line %d"), linenr);
 +              die(_("unable to find filename in patch at line %d"), state->linenr);
  }
  
 -static int gitdiff_hdrend(const char *line, struct patch *patch)
 +static int gitdiff_hdrend(struct apply_state *state,
 +                        const char *line,
 +                        struct patch *patch)
  {
        return -1;
  }
  #define DIFF_OLD_NAME 0
  #define DIFF_NEW_NAME 1
  
 -static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
 +static void gitdiff_verify_name(struct apply_state *state,
 +                              const char *line,
 +                              int isnull,
 +                              char **name,
 +                              int side)
  {
 -      if (!orig_name && !isnull)
 -              return find_name(line, NULL, p_value, TERM_TAB);
 +      if (!*name && !isnull) {
 +              *name = find_name(state, line, NULL, state->p_value, TERM_TAB);
 +              return;
 +      }
  
 -      if (orig_name) {
 -              int len = strlen(orig_name);
 +      if (*name) {
 +              int len = strlen(*name);
                char *another;
                if (isnull)
                        die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
 -                          orig_name, linenr);
 -              another = find_name(line, NULL, p_value, TERM_TAB);
 -              if (!another || memcmp(another, orig_name, len + 1))
 +                          *name, state->linenr);
 +              another = find_name(state, line, NULL, state->p_value, TERM_TAB);
 +              if (!another || memcmp(another, *name, len + 1))
                        die((side == DIFF_NEW_NAME) ?
                            _("git apply: bad git-diff - inconsistent new filename on line %d") :
 -                          _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
 +                          _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr);
                free(another);
 -              return orig_name;
        } else {
                /* expect "/dev/null" */
                if (memcmp("/dev/null", line, 9) || line[9] != '\n')
 -                      die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
 -              return NULL;
 +                      die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
  }
  
 -static int gitdiff_oldname(const char *line, struct patch *patch)
 +static int gitdiff_oldname(struct apply_state *state,
 +                         const char *line,
 +                         struct patch *patch)
  {
 -      patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
 -                                            DIFF_OLD_NAME);
 +      gitdiff_verify_name(state, line,
 +                          patch->is_new, &patch->old_name,
 +                          DIFF_OLD_NAME);
        return 0;
  }
  
 -static int gitdiff_newname(const char *line, struct patch *patch)
 +static int gitdiff_newname(struct apply_state *state,
 +                         const char *line,
 +                         struct patch *patch)
  {
 -      patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
 -                                            DIFF_NEW_NAME);
 +      gitdiff_verify_name(state, line,
 +                          patch->is_delete, &patch->new_name,
 +                          DIFF_NEW_NAME);
        return 0;
  }
  
 -static int gitdiff_oldmode(const char *line, struct patch *patch)
 +static int gitdiff_oldmode(struct apply_state *state,
 +                         const char *line,
 +                         struct patch *patch)
  {
        patch->old_mode = strtoul(line, NULL, 8);
        return 0;
  }
  
 -static int gitdiff_newmode(const char *line, struct patch *patch)
 +static int gitdiff_newmode(struct apply_state *state,
 +                         const char *line,
 +                         struct patch *patch)
  {
        patch->new_mode = strtoul(line, NULL, 8);
        return 0;
  }
  
 -static int gitdiff_delete(const char *line, struct patch *patch)
 +static int gitdiff_delete(struct apply_state *state,
 +                        const char *line,
 +                        struct patch *patch)
  {
        patch->is_delete = 1;
        free(patch->old_name);
        patch->old_name = xstrdup_or_null(patch->def_name);
 -      return gitdiff_oldmode(line, patch);
 +      return gitdiff_oldmode(state, line, patch);
  }
  
 -static int gitdiff_newfile(const char *line, struct patch *patch)
 +static int gitdiff_newfile(struct apply_state *state,
 +                         const char *line,
 +                         struct patch *patch)
  {
        patch->is_new = 1;
        free(patch->new_name);
        patch->new_name = xstrdup_or_null(patch->def_name);
 -      return gitdiff_newmode(line, patch);
 +      return gitdiff_newmode(state, line, patch);
  }
  
 -static int gitdiff_copysrc(const char *line, struct patch *patch)
 +static int gitdiff_copysrc(struct apply_state *state,
 +                         const char *line,
 +                         struct patch *patch)
  {
        patch->is_copy = 1;
        free(patch->old_name);
 -      patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
 +      patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
  }
  
 -static int gitdiff_copydst(const char *line, struct patch *patch)
 +static int gitdiff_copydst(struct apply_state *state,
 +                         const char *line,
 +                         struct patch *patch)
  {
        patch->is_copy = 1;
        free(patch->new_name);
 -      patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
 +      patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
  }
  
 -static int gitdiff_renamesrc(const char *line, struct patch *patch)
 +static int gitdiff_renamesrc(struct apply_state *state,
 +                           const char *line,
 +                           struct patch *patch)
  {
        patch->is_rename = 1;
        free(patch->old_name);
 -      patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
 +      patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
  }
  
 -static int gitdiff_renamedst(const char *line, struct patch *patch)
 +static int gitdiff_renamedst(struct apply_state *state,
 +                           const char *line,
 +                           struct patch *patch)
  {
        patch->is_rename = 1;
        free(patch->new_name);
 -      patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
 +      patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
  }
  
 -static int gitdiff_similarity(const char *line, struct patch *patch)
 +static int gitdiff_similarity(struct apply_state *state,
 +                            const char *line,
 +                            struct patch *patch)
  {
        unsigned long val = strtoul(line, NULL, 10);
        if (val <= 100)
        return 0;
  }
  
 -static int gitdiff_dissimilarity(const char *line, struct patch *patch)
 +static int gitdiff_dissimilarity(struct apply_state *state,
 +                               const char *line,
 +                               struct patch *patch)
  {
        unsigned long val = strtoul(line, NULL, 10);
        if (val <= 100)
        return 0;
  }
  
 -static int gitdiff_index(const char *line, struct patch *patch)
 +static int gitdiff_index(struct apply_state *state,
 +                       const char *line,
 +                       struct patch *patch)
  {
        /*
         * index line is N hexadecimal, "..", N hexadecimal,
   * This is normal for a diff that doesn't change anything: we'll fall through
   * into the next diff. Tell the parser to break out.
   */
 -static int gitdiff_unrecognized(const char *line, struct patch *patch)
 +static int gitdiff_unrecognized(struct apply_state *state,
 +                              const char *line,
 +                              struct patch *patch)
  {
        return -1;
  }
   * Skip p_value leading components from "line"; as we do not accept
   * absolute paths, return NULL in that case.
   */
 -static const char *skip_tree_prefix(const char *line, int llen)
 +static const char *skip_tree_prefix(struct apply_state *state,
 +                                  const char *line,
 +                                  int llen)
  {
        int nslash;
        int i;
  
 -      if (!p_value)
 +      if (!state->p_value)
                return (llen && line[0] == '/') ? NULL : line;
  
 -      nslash = p_value;
 +      nslash = state->p_value;
        for (i = 0; i < llen; i++) {
                int ch = line[i];
                if (ch == '/' && --nslash <= 0)
   * creation or deletion of an empty file.  In any of these cases,
   * both sides are the same name under a/ and b/ respectively.
   */
 -static char *git_header_name(const char *line, int llen)
 +static char *git_header_name(struct apply_state *state,
 +                           const char *line,
 +                           int llen)
  {
        const char *name;
        const char *second = NULL;
                        goto free_and_fail1;
  
                /* strip the a/b prefix including trailing slash */
 -              cp = skip_tree_prefix(first.buf, first.len);
 +              cp = skip_tree_prefix(state, first.buf, first.len);
                if (!cp)
                        goto free_and_fail1;
                strbuf_remove(&first, 0, cp - first.buf);
                if (*second == '"') {
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail1;
 -                      cp = skip_tree_prefix(sp.buf, sp.len);
 +                      cp = skip_tree_prefix(state, sp.buf, sp.len);
                        if (!cp)
                                goto free_and_fail1;
                        /* They must match, otherwise ignore */
                }
  
                /* unquoted second */
 -              cp = skip_tree_prefix(second, line + llen - second);
 +              cp = skip_tree_prefix(state, second, line + llen - second);
                if (!cp)
                        goto free_and_fail1;
                if (line + llen - cp != first.len ||
        }
  
        /* unquoted first name */
 -      name = skip_tree_prefix(line, llen);
 +      name = skip_tree_prefix(state, line, llen);
        if (!name)
                return NULL;
  
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail2;
  
 -                      np = skip_tree_prefix(sp.buf, sp.len);
 +                      np = skip_tree_prefix(state, sp.buf, sp.len);
                        if (!np)
                                goto free_and_fail2;
  
                         */
                        if (!name[len + 1])
                                return NULL; /* no postimage name */
 -                      second = skip_tree_prefix(name + len + 1,
 +                      second = skip_tree_prefix(state, name + len + 1,
                                                  line_len - (len + 1));
                        if (!second)
                                return NULL;
  }
  
  /* Verify that we recognize the lines following a git header */
 -static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
 +static int parse_git_header(struct apply_state *state,
 +                          const char *line,
 +                          int len,
 +                          unsigned int size,
 +                          struct patch *patch)
  {
        unsigned long offset;
  
         * or removing or adding empty files), so we get
         * the default name from the header.
         */
 -      patch->def_name = git_header_name(line, len);
 -      if (patch->def_name && root.len) {
 -              char *s = xstrfmt("%s%s", root.buf, patch->def_name);
 +      patch->def_name = git_header_name(state, line, len);
 +      if (patch->def_name && state->root.len) {
 +              char *s = xstrfmt("%s%s", state->root.buf, patch->def_name);
                free(patch->def_name);
                patch->def_name = s;
        }
  
        line += len;
        size -= len;
 -      linenr++;
 -      for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
 +      state->linenr++;
 +      for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {
                static const struct opentry {
                        const char *str;
 -                      int (*fn)(const char *, struct patch *);
 +                      int (*fn)(struct apply_state *, const char *, struct patch *);
                } optable[] = {
                        { "@@ -", gitdiff_hdrend },
                        { "--- ", gitdiff_oldname },
                        int oplen = strlen(p->str);
                        if (len < oplen || memcmp(p->str, line, oplen))
                                continue;
 -                      if (p->fn(line + oplen, patch) < 0)
 +                      if (p->fn(state, line + oplen, patch) < 0)
                                return offset;
                        break;
                }
@@@ -1514,11 -1431,7 +1514,11 @@@ static int parse_fragment_header(const 
        return offset;
  }
  
 -static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
 +static int find_header(struct apply_state *state,
 +                     const char *line,
 +                     unsigned long size,
 +                     int *hdrsize,
 +                     struct patch *patch)
  {
        unsigned long offset, len;
  
        patch->is_new = patch->is_delete = -1;
        patch->old_mode = patch->new_mode = 0;
        patch->old_name = patch->new_name = NULL;
 -      for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
 +      for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) {
                unsigned long nextlen;
  
                len = linelen(line, size);
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
                        die(_("patch fragment without header at line %d: %.*s"),
 -                          linenr, (int)len-1, line);
 +                          state->linenr, (int)len-1, line);
                }
  
                if (size < len + 6)
                 * or mode change, so we handle that specially
                 */
                if (!memcmp("diff --git ", line, 11)) {
 -                      int git_hdr_len = parse_git_header(line, len, size, patch);
 +                      int git_hdr_len = parse_git_header(state, line, len, size, patch);
                        if (git_hdr_len <= len)
                                continue;
                        if (!patch->old_name && !patch->new_name) {
                                               "%d leading pathname component (line %d)",
                                               "git diff header lacks filename information when removing "
                                               "%d leading pathname components (line %d)",
 -                                             p_value),
 -                                          p_value, linenr);
 +                                             state->p_value),
 +                                          state->p_value, state->linenr);
                                patch->old_name = xstrdup(patch->def_name);
                                patch->new_name = xstrdup(patch->def_name);
                        }
                        if (!patch->is_delete && !patch->new_name)
                                die("git diff header lacks filename information "
 -                                  "(line %d)", linenr);
 +                                  "(line %d)", state->linenr);
                        patch->is_toplevel_relative = 1;
                        *hdrsize = git_hdr_len;
                        return offset;
                        continue;
  
                /* Ok, we'll consider it a patch */
 -              parse_traditional_patch(line, line+len, patch);
 +              parse_traditional_patch(state, line, line+len, patch);
                *hdrsize = len + nextlen;
 -              linenr += 2;
 +              state->linenr += 2;
                return offset;
        }
        return -1;
  }
  
 -static void record_ws_error(unsigned result, const char *line, int len, int linenr)
 +static void record_ws_error(struct apply_state *state,
 +                          unsigned result,
 +                          const char *line,
 +                          int len,
 +                          int linenr)
  {
        char *err;
  
        if (!result)
                return;
  
 -      whitespace_error++;
 -      if (squelch_whitespace_errors &&
 -          squelch_whitespace_errors < whitespace_error)
 +      state->whitespace_error++;
 +      if (state->squelch_whitespace_errors &&
 +          state->squelch_whitespace_errors < state->whitespace_error)
                return;
  
        err = whitespace_error_string(result);
        fprintf(stderr, "%s:%d: %s.\n%.*s\n",
 -              patch_input_file, linenr, err, len, line);
 +              state->patch_input_file, linenr, err, len, line);
        free(err);
  }
  
 -static void check_whitespace(const char *line, int len, unsigned ws_rule)
 +static void check_whitespace(struct apply_state *state,
 +                           const char *line,
 +                           int len,
 +                           unsigned ws_rule)
  {
        unsigned result = ws_check(line + 1, len - 1, ws_rule);
  
 -      record_ws_error(result, line + 1, len - 2, linenr);
 +      record_ws_error(state, result, line + 1, len - 2, state->linenr);
  }
  
  /*
   * between a "---" that is part of a patch, and a "---" that starts
   * the next patch is to look at the line counts..
   */
 -static int parse_fragment(const char *line, unsigned long size,
 -                        struct patch *patch, struct fragment *fragment)
 +static int parse_fragment(struct apply_state *state,
 +                        const char *line,
 +                        unsigned long size,
 +                        struct patch *patch,
 +                        struct fragment *fragment)
  {
        int added, deleted;
        int len = linelen(line, size), offset;
        /* Parse the thing.. */
        line += len;
        size -= len;
 -      linenr++;
 +      state->linenr++;
        added = deleted = 0;
        for (offset = len;
             0 < size;
 -           offset += len, size -= len, line += len, linenr++) {
 +           offset += len, size -= len, line += len, state->linenr++) {
                if (!oldlines && !newlines)
                        break;
                len = linelen(line, size);
                        if (!deleted && !added)
                                leading++;
                        trailing++;
 -                      if (!apply_in_reverse &&
 -                          ws_error_action == correct_ws_error)
 -                              check_whitespace(line, len, patch->ws_rule);
 +                      if (!state->apply_in_reverse &&
 +                          state->ws_error_action == correct_ws_error)
 +                              check_whitespace(state, line, len, patch->ws_rule);
                        break;
                case '-':
 -                      if (apply_in_reverse &&
 -                          ws_error_action != nowarn_ws_error)
 -                              check_whitespace(line, len, patch->ws_rule);
 +                      if (state->apply_in_reverse &&
 +                          state->ws_error_action != nowarn_ws_error)
 +                              check_whitespace(state, line, len, patch->ws_rule);
                        deleted++;
                        oldlines--;
                        trailing = 0;
                        break;
                case '+':
 -                      if (!apply_in_reverse &&
 -                          ws_error_action != nowarn_ws_error)
 -                              check_whitespace(line, len, patch->ws_rule);
 +                      if (!state->apply_in_reverse &&
 +                          state->ws_error_action != nowarn_ws_error)
 +                              check_whitespace(state, line, len, patch->ws_rule);
                        added++;
                        newlines--;
                        trailing = 0;
   * The (fragment->patch, fragment->size) pair points into the memory given
   * by the caller, not a copy, when we return.
   */
 -static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
 +static int parse_single_patch(struct apply_state *state,
 +                            const char *line,
 +                            unsigned long size,
 +                            struct patch *patch)
  {
        unsigned long offset = 0;
        unsigned long oldlines = 0, newlines = 0, context = 0;
                int len;
  
                fragment = xcalloc(1, sizeof(*fragment));
 -              fragment->linenr = linenr;
 -              len = parse_fragment(line, size, patch, fragment);
 +              fragment->linenr = state->linenr;
 +              len = parse_fragment(state, line, size, patch, fragment);
                if (len <= 0)
 -                      die(_("corrupt patch at line %d"), linenr);
 +                      die(_("corrupt patch at line %d"), state->linenr);
                fragment->patch = line;
                fragment->size = len;
                oldlines += fragment->oldlines;
@@@ -1857,8 -1757,7 +1857,8 @@@ static char *inflate_it(const void *dat
   * points at an allocated memory that the caller must free, so
   * it is marked as "->free_patch = 1".
   */
 -static struct fragment *parse_binary_hunk(char **buf_p,
 +static struct fragment *parse_binary_hunk(struct apply_state *state,
 +                                        char **buf_p,
                                          unsigned long *sz_p,
                                          int *status_p,
                                          int *used_p)
        else
                return NULL;
  
 -      linenr++;
 +      state->linenr++;
        buffer += llen;
        while (1) {
                int byte_length, max_byte_length, newsize;
                llen = linelen(buffer, size);
                used += llen;
 -              linenr++;
 +              state->linenr++;
                if (llen == 1) {
                        /* consume the blank line */
                        buffer++;
        free(data);
        *status_p = -1;
        error(_("corrupt binary patch at line %d: %.*s"),
 -            linenr-1, llen-1, buffer);
 +            state->linenr-1, llen-1, buffer);
        return NULL;
  }
  
   *   -1 in case of error,
   *   the length of the parsed binary patch otherwise
   */
 -static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
 +static int parse_binary(struct apply_state *state,
 +                      char *buffer,
 +                      unsigned long size,
 +                      struct patch *patch)
  {
        /*
         * We have read "GIT binary patch\n"; what follows is a line
        int status;
        int used, used_1;
  
 -      forward = parse_binary_hunk(&buffer, &size, &status, &used);
 +      forward = parse_binary_hunk(state, &buffer, &size, &status, &used);
        if (!forward && !status)
                /* there has to be one hunk (forward hunk) */
 -              return error(_("unrecognized binary patch at line %d"), linenr-1);
 +              return error(_("unrecognized binary patch at line %d"), state->linenr-1);
        if (status)
                /* otherwise we already gave an error message */
                return status;
  
 -      reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
 +      reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1);
        if (reverse)
                used += used_1;
        else if (status) {
        return used;
  }
  
 -static void prefix_one(char **name)
 +static void prefix_one(struct apply_state *state, char **name)
  {
        char *old_name = *name;
        if (!old_name)
                return;
 -      *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
 +      *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name));
        free(old_name);
  }
  
 -static void prefix_patch(struct patch *p)
 +static void prefix_patch(struct apply_state *state, struct patch *p)
  {
 -      if (!prefix || p->is_toplevel_relative)
 +      if (!state->prefix || p->is_toplevel_relative)
                return;
 -      prefix_one(&p->new_name);
 -      prefix_one(&p->old_name);
 +      prefix_one(state, &p->new_name);
 +      prefix_one(state, &p->old_name);
  }
  
  /*
   * include/exclude
   */
  
 -static struct string_list limit_by_name = STRING_LIST_INIT_NODUP;
 -static int has_include;
 -static void add_name_limit(const char *name, int exclude)
 +static void add_name_limit(struct apply_state *state,
 +                         const char *name,
 +                         int exclude)
  {
        struct string_list_item *it;
  
 -      it = string_list_append(&limit_by_name, name);
 +      it = string_list_append(&state->limit_by_name, name);
        it->util = exclude ? NULL : (void *) 1;
  }
  
 -static int use_patch(struct patch *p)
 +static int use_patch(struct apply_state *state, struct patch *p)
  {
        const char *pathname = p->new_name ? p->new_name : p->old_name;
        int i;
  
        /* Paths outside are not touched regardless of "--include" */
 -      if (0 < prefix_length) {
 +      if (0 < state->prefix_length) {
                int pathlen = strlen(pathname);
 -              if (pathlen <= prefix_length ||
 -                  memcmp(prefix, pathname, prefix_length))
 +              if (pathlen <= state->prefix_length ||
 +                  memcmp(state->prefix, pathname, state->prefix_length))
                        return 0;
        }
  
        /* See if it matches any of exclude/include rule */
 -      for (i = 0; i < limit_by_name.nr; i++) {
 -              struct string_list_item *it = &limit_by_name.items[i];
 +      for (i = 0; i < state->limit_by_name.nr; i++) {
 +              struct string_list_item *it = &state->limit_by_name.items[i];
                if (!wildmatch(it->string, pathname, 0, NULL))
                        return (it->util != NULL);
        }
         * not used.  Otherwise, we saw bunch of exclude rules (or none)
         * and such a path is used.
         */
 -      return !has_include;
 +      return !state->has_include;
  }
  
  
   * Return the number of bytes consumed, so that the caller can call us
   * again for the next patch.
   */
 -static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
 +static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch)
  {
        int hdrsize, patchsize;
 -      int offset = find_header(buffer, size, &hdrsize, patch);
 +      int offset = find_header(state, buffer, size, &hdrsize, patch);
  
        if (offset < 0)
                return offset;
  
 -      prefix_patch(patch);
 +      prefix_patch(state, patch);
  
 -      if (!use_patch(patch))
 +      if (!use_patch(state, patch))
                patch->ws_rule = 0;
        else
                patch->ws_rule = whitespace_rule(patch->new_name
                                                 ? patch->new_name
                                                 : patch->old_name);
  
 -      patchsize = parse_single_patch(buffer + offset + hdrsize,
 -                                     size - offset - hdrsize, patch);
 +      patchsize = parse_single_patch(state,
 +                                     buffer + offset + hdrsize,
 +                                     size - offset - hdrsize,
 +                                     patch);
  
        if (!patchsize) {
                static const char git_binary[] = "GIT binary patch\n";
                if (llen == sizeof(git_binary) - 1 &&
                    !memcmp(git_binary, buffer + hd, llen)) {
                        int used;
 -                      linenr++;
 -                      used = parse_binary(buffer + hd + llen,
 +                      state->linenr++;
 +                      used = parse_binary(state, buffer + hd + llen,
                                            size - hd - llen, patch);
                        if (used < 0)
                                return -1;
                                int len = strlen(binhdr[i]);
                                if (len < size - hd &&
                                    !memcmp(binhdr[i], buffer + hd, len)) {
 -                                      linenr++;
 +                                      state->linenr++;
                                        patch->is_binary = 1;
                                        patchsize = llen;
                                        break;
                 * without metadata change.  A binary patch appears
                 * empty to us here.
                 */
 -              if ((apply || check) &&
 +              if ((state->apply || state->check) &&
                    (!patch->is_binary && !metadata_changes(patch)))
 -                      die(_("patch with only garbage at line %d"), linenr);
 +                      die(_("patch with only garbage at line %d"), state->linenr);
        }
  
        return offset + hdrsize + patchsize;
@@@ -2189,7 -2083,7 +2189,7 @@@ static const char pluses[] 
  static const char minuses[]=
  "----------------------------------------------------------------------";
  
 -static void show_stats(struct patch *patch)
 +static void show_stats(struct apply_state *state, struct patch *patch)
  {
        struct strbuf qname = STRBUF_INIT;
        char *cp = patch->new_name ? patch->new_name : patch->old_name;
        /*
         * "scale" the filename
         */
 -      max = max_len;
 +      max = state->max_len;
        if (max > 50)
                max = 50;
  
        /*
         * scale the add/delete
         */
 -      max = max + max_change > 70 ? 70 - max : max_change;
 +      max = max + state->max_change > 70 ? 70 - max : state->max_change;
        add = patch->lines_added;
        del = patch->lines_deleted;
  
 -      if (max_change > 0) {
 -              int total = ((add + del) * max + max_change / 2) / max_change;
 -              add = (add * max + max_change / 2) / max_change;
 +      if (state->max_change > 0) {
 +              int total = ((add + del) * max + state->max_change / 2) / state->max_change;
 +              add = (add * max + state->max_change / 2) / state->max_change;
                del = total - add;
        }
        printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
@@@ -2300,17 -2194,17 +2300,17 @@@ static void update_pre_post_images(stru
        fixed = preimage->buf;
  
        for (i = reduced = ctx = 0; i < postimage->nr; i++) {
 -              size_t len = postimage->line[i].len;
 +              size_t l_len = postimage->line[i].len;
                if (!(postimage->line[i].flag & LINE_COMMON)) {
                        /* an added line -- no counterparts in preimage */
 -                      memmove(new, old, len);
 -                      old += len;
 -                      new += len;
 +                      memmove(new, old, l_len);
 +                      old += l_len;
 +                      new += l_len;
                        continue;
                }
  
                /* a common context -- skip it in the original postimage */
 -              old += len;
 +              old += l_len;
  
                /* and find the corresponding one in the fixed preimage */
                while (ctx < preimage->nr &&
                }
  
                /* and copy it in, while fixing the line length */
 -              len = preimage->line[ctx].len;
 -              memcpy(new, fixed, len);
 -              new += len;
 -              fixed += len;
 -              postimage->line[i].len = len;
 +              l_len = preimage->line[ctx].len;
 +              memcpy(new, fixed, l_len);
 +              new += l_len;
 +              fixed += l_len;
 +              postimage->line[i].len = l_len;
                ctx++;
        }
  
        postimage->nr -= reduced;
  }
  
 -static int match_fragment(struct image *img,
 +static int line_by_line_fuzzy_match(struct image *img,
 +                                  struct image *preimage,
 +                                  struct image *postimage,
 +                                  unsigned long try,
 +                                  int try_lno,
 +                                  int preimage_limit)
 +{
 +      int i;
 +      size_t imgoff = 0;
 +      size_t preoff = 0;
 +      size_t postlen = postimage->len;
 +      size_t extra_chars;
 +      char *buf;
 +      char *preimage_eof;
 +      char *preimage_end;
 +      struct strbuf fixed;
 +      char *fixed_buf;
 +      size_t fixed_len;
 +
 +      for (i = 0; i < preimage_limit; i++) {
 +              size_t prelen = preimage->line[i].len;
 +              size_t imglen = img->line[try_lno+i].len;
 +
 +              if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
 +                                    preimage->buf + preoff, prelen))
 +                      return 0;
 +              if (preimage->line[i].flag & LINE_COMMON)
 +                      postlen += imglen - prelen;
 +              imgoff += imglen;
 +              preoff += prelen;
 +      }
 +
 +      /*
 +       * Ok, the preimage matches with whitespace fuzz.
 +       *
 +       * imgoff now holds the true length of the target that
 +       * matches the preimage before the end of the file.
 +       *
 +       * Count the number of characters in the preimage that fall
 +       * beyond the end of the file and make sure that all of them
 +       * are whitespace characters. (This can only happen if
 +       * we are removing blank lines at the end of the file.)
 +       */
 +      buf = preimage_eof = preimage->buf + preoff;
 +      for ( ; i < preimage->nr; i++)
 +              preoff += preimage->line[i].len;
 +      preimage_end = preimage->buf + preoff;
 +      for ( ; buf < preimage_end; buf++)
 +              if (!isspace(*buf))
 +                      return 0;
 +
 +      /*
 +       * Update the preimage and the common postimage context
 +       * lines to use the same whitespace as the target.
 +       * If whitespace is missing in the target (i.e.
 +       * if the preimage extends beyond the end of the file),
 +       * use the whitespace from the preimage.
 +       */
 +      extra_chars = preimage_end - preimage_eof;
 +      strbuf_init(&fixed, imgoff + extra_chars);
 +      strbuf_add(&fixed, img->buf + try, imgoff);
 +      strbuf_add(&fixed, preimage_eof, extra_chars);
 +      fixed_buf = strbuf_detach(&fixed, &fixed_len);
 +      update_pre_post_images(preimage, postimage,
 +                             fixed_buf, fixed_len, postlen);
 +      return 1;
 +}
 +
 +static int match_fragment(struct apply_state *state,
 +                        struct image *img,
                          struct image *preimage,
                          struct image *postimage,
                          unsigned long try,
                preimage_limit = preimage->nr;
                if (match_end && (preimage->nr + try_lno != img->nr))
                        return 0;
 -      } else if (ws_error_action == correct_ws_error &&
 +      } else if (state->ws_error_action == correct_ws_error &&
                   (ws_rule & WS_BLANK_AT_EOF)) {
                /*
                 * This hunk extends beyond the end of img, and we are
         * fuzzy matching. We collect all the line length information because
         * we need it to adjust whitespace if we match.
         */
 -      if (ws_ignore_action == ignore_ws_change) {
 -              size_t imgoff = 0;
 -              size_t preoff = 0;
 -              size_t postlen = postimage->len;
 -              size_t extra_chars;
 -              char *preimage_eof;
 -              char *preimage_end;
 -              for (i = 0; i < preimage_limit; i++) {
 -                      size_t prelen = preimage->line[i].len;
 -                      size_t imglen = img->line[try_lno+i].len;
 -
 -                      if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
 -                                            preimage->buf + preoff, prelen))
 -                              return 0;
 -                      if (preimage->line[i].flag & LINE_COMMON)
 -                              postlen += imglen - prelen;
 -                      imgoff += imglen;
 -                      preoff += prelen;
 -              }
 +      if (state->ws_ignore_action == ignore_ws_change)
 +              return line_by_line_fuzzy_match(img, preimage, postimage,
 +                                              try, try_lno, preimage_limit);
  
 -              /*
 -               * Ok, the preimage matches with whitespace fuzz.
 -               *
 -               * imgoff now holds the true length of the target that
 -               * matches the preimage before the end of the file.
 -               *
 -               * Count the number of characters in the preimage that fall
 -               * beyond the end of the file and make sure that all of them
 -               * are whitespace characters. (This can only happen if
 -               * we are removing blank lines at the end of the file.)
 -               */
 -              buf = preimage_eof = preimage->buf + preoff;
 -              for ( ; i < preimage->nr; i++)
 -                      preoff += preimage->line[i].len;
 -              preimage_end = preimage->buf + preoff;
 -              for ( ; buf < preimage_end; buf++)
 -                      if (!isspace(*buf))
 -                              return 0;
 -
 -              /*
 -               * Update the preimage and the common postimage context
 -               * lines to use the same whitespace as the target.
 -               * If whitespace is missing in the target (i.e.
 -               * if the preimage extends beyond the end of the file),
 -               * use the whitespace from the preimage.
 -               */
 -              extra_chars = preimage_end - preimage_eof;
 -              strbuf_init(&fixed, imgoff + extra_chars);
 -              strbuf_add(&fixed, img->buf + try, imgoff);
 -              strbuf_add(&fixed, preimage_eof, extra_chars);
 -              fixed_buf = strbuf_detach(&fixed, &fixed_len);
 -              update_pre_post_images(preimage, postimage,
 -                              fixed_buf, fixed_len, postlen);
 -              return 1;
 -      }
 -
 -      if (ws_error_action != correct_ws_error)
 +      if (state->ws_error_action != correct_ws_error)
                return 0;
  
        /*
        return 0;
  }
  
 -static int find_pos(struct image *img,
 +static int find_pos(struct apply_state *state,
 +                  struct image *img,
                    struct image *preimage,
                    struct image *postimage,
                    int line,
        try_lno = line;
  
        for (i = 0; ; i++) {
 -              if (match_fragment(img, preimage, postimage,
 +              if (match_fragment(state, img, preimage, postimage,
                                   try, try_lno, ws_rule,
                                   match_beginning, match_end))
                        return try_lno;
@@@ -2718,8 -2594,7 +2718,8 @@@ static void remove_last_line(struct ima
   * apply at applied_pos (counts in line numbers) in "img".
   * Update "img" to remove "preimage" and replace it with "postimage".
   */
 -static void update_image(struct image *img,
 +static void update_image(struct apply_state *state,
 +                       struct image *img,
                         int applied_pos,
                         struct image *preimage,
                         struct image *postimage)
        memcpy(img->line + applied_pos,
               postimage->line,
               postimage->nr * sizeof(*img->line));
 -      if (!allow_overlap)
 +      if (!state->allow_overlap)
                for (i = 0; i < postimage->nr; i++)
                        img->line[applied_pos + i].flag |= LINE_PATCHED;
        img->nr = nr;
   * postimage) for the hunk.  Find lines that match "preimage" in "img" and
   * replace the part of "img" with "postimage" text.
   */
 -static int apply_one_fragment(struct image *img, struct fragment *frag,
 +static int apply_one_fragment(struct apply_state *state,
 +                            struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule,
                              int nth_fragment)
  {
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
 -              if (apply_in_reverse) {
 +              if (state->apply_in_reverse) {
                        if (first == '-')
                                first = '+';
                        else if (first == '+')
                /* Fall-through for ' ' */
                case '+':
                        /* --no-add does not add new lines */
 -                      if (first == '+' && no_add)
 +                      if (first == '+' && state->no_add)
                                break;
  
                        start = newlines.len;
                        if (first != '+' ||
 -                          !whitespace_error ||
 -                          ws_error_action != correct_ws_error) {
 +                          !state->whitespace_error ||
 +                          state->ws_error_action != correct_ws_error) {
                                strbuf_add(&newlines, patch + 1, plen);
                        }
                        else {
 -                              ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
 +                              ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws);
                        }
                        add_line_info(&postimage, newlines.buf + start, newlines.len - start,
                                      (first == '+' ? 0 : LINE_COMMON));
                        /* Ignore it, we already handled it */
                        break;
                default:
 -                      if (apply_verbosely)
 +                      if (state->apply_verbosely)
                                error(_("invalid start of line: '%c'"), first);
                        applied_pos = -1;
                        goto out;
         * without leading context must match at the beginning.
         */
        match_beginning = (!frag->oldpos ||
 -                         (frag->oldpos == 1 && !unidiff_zero));
 +                         (frag->oldpos == 1 && !state->unidiff_zero));
  
        /*
         * A hunk without trailing lines must match at the end.
         * from the lack of trailing lines if the patch was generated
         * with unidiff without any context.
         */
 -      match_end = !unidiff_zero && !trailing;
 +      match_end = !state->unidiff_zero && !trailing;
  
        pos = frag->newpos ? (frag->newpos - 1) : 0;
        preimage.buf = oldlines;
  
        for (;;) {
  
 -              applied_pos = find_pos(img, &preimage, &postimage, pos,
 +              applied_pos = find_pos(state, img, &preimage, &postimage, pos,
                                       ws_rule, match_beginning, match_end);
  
                if (applied_pos >= 0)
                        break;
  
                /* Am I at my context limits? */
 -              if ((leading <= p_context) && (trailing <= p_context))
 +              if ((leading <= state->p_context) && (trailing <= state->p_context))
                        break;
                if (match_beginning || match_end) {
                        match_beginning = match_end = 0;
                if (new_blank_lines_at_end &&
                    preimage.nr + applied_pos >= img->nr &&
                    (ws_rule & WS_BLANK_AT_EOF) &&
 -                  ws_error_action != nowarn_ws_error) {
 -                      record_ws_error(WS_BLANK_AT_EOF, "+", 1,
 +                  state->ws_error_action != nowarn_ws_error) {
 +                      record_ws_error(state, WS_BLANK_AT_EOF, "+", 1,
                                        found_new_blank_lines_at_end);
 -                      if (ws_error_action == correct_ws_error) {
 +                      if (state->ws_error_action == correct_ws_error) {
                                while (new_blank_lines_at_end--)
                                        remove_last_line(&postimage);
                        }
                         * apply_patch->check_patch_list->check_patch->
                         * apply_data->apply_fragments->apply_one_fragment
                         */
 -                      if (ws_error_action == die_on_ws_error)
 -                              apply = 0;
 +                      if (state->ws_error_action == die_on_ws_error)
 +                              state->apply = 0;
                }
  
 -              if (apply_verbosely && applied_pos != pos) {
 +              if (state->apply_verbosely && applied_pos != pos) {
                        int offset = applied_pos - pos;
 -                      if (apply_in_reverse)
 +                      if (state->apply_in_reverse)
                                offset = 0 - offset;
                        fprintf_ln(stderr,
                                   Q_("Hunk #%d succeeded at %d (offset %d line).",
                        fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
                                             " to apply fragment at %d"),
                                   leading, trailing, applied_pos+1);
 -              update_image(img, applied_pos, &preimage, &postimage);
 +              update_image(state, img, applied_pos, &preimage, &postimage);
        } else {
 -              if (apply_verbosely)
 +              if (state->apply_verbosely)
                        error(_("while searching for:\n%.*s"),
                              (int)(old - oldlines), oldlines);
        }
@@@ -3048,9 -2922,7 +3048,9 @@@ out
        return (applied_pos < 0);
  }
  
 -static int apply_binary_fragment(struct image *img, struct patch *patch)
 +static int apply_binary_fragment(struct apply_state *state,
 +                               struct image *img,
 +                               struct patch *patch)
  {
        struct fragment *fragment = patch->fragments;
        unsigned long len;
                             patch->old_name);
  
        /* Binary patch is irreversible without the optional second hunk */
 -      if (apply_in_reverse) {
 +      if (state->apply_in_reverse) {
                if (!fragment->next)
                        return error("cannot reverse-apply a binary patch "
                                     "without the reverse hunk to '%s'",
   * but the preimage prepared by the caller in "img" is freed here
   * or in the helper function apply_binary_fragment() this calls.
   */
 -static int apply_binary(struct image *img, struct patch *patch)
 +static int apply_binary(struct apply_state *state,
 +                      struct image *img,
 +                      struct patch *patch)
  {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
 -              if (apply_binary_fragment(img, patch))
 +              if (apply_binary_fragment(state, img, patch))
                        return error(_("binary patch does not apply to '%s'"),
                                     name);
  
        return 0;
  }
  
 -static int apply_fragments(struct image *img, struct patch *patch)
 +static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch)
  {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        int nth = 0;
  
        if (patch->is_binary)
 -              return apply_binary(img, patch);
 +              return apply_binary(state, img, patch);
  
        while (frag) {
                nth++;
 -              if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
 +              if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) {
                        error(_("patch failed: %s:%ld"), name, frag->oldpos);
 -                      if (!apply_with_reject)
 +                      if (!state->apply_with_reject)
                                return -1;
                        frag->rejected = 1;
                }
@@@ -3223,14 -3093,14 +3223,14 @@@ static int read_file_or_gitlink(const s
        return read_blob_object(buf, ce->sha1, ce->ce_mode);
  }
  
 -static struct patch *in_fn_table(const char *name)
 +static struct patch *in_fn_table(struct apply_state *state, const char *name)
  {
        struct string_list_item *item;
  
        if (name == NULL)
                return NULL;
  
 -      item = string_list_lookup(&fn_table, name);
 +      item = string_list_lookup(&state->fn_table, name);
        if (item != NULL)
                return (struct patch *)item->util;
  
@@@ -3262,7 -3132,7 +3262,7 @@@ static int was_deleted(struct patch *pa
        return patch == PATH_WAS_DELETED;
  }
  
 -static void add_to_fn_table(struct patch *patch)
 +static void add_to_fn_table(struct apply_state *state, struct patch *patch)
  {
        struct string_list_item *item;
  
         * file creations and copies
         */
        if (patch->new_name != NULL) {
 -              item = string_list_insert(&fn_table, patch->new_name);
 +              item = string_list_insert(&state->fn_table, patch->new_name);
                item->util = patch;
        }
  
         * later chunks shouldn't patch old names
         */
        if ((patch->new_name == NULL) || (patch->is_rename)) {
 -              item = string_list_insert(&fn_table, patch->old_name);
 +              item = string_list_insert(&state->fn_table, patch->old_name);
                item->util = PATH_WAS_DELETED;
        }
  }
  
 -static void prepare_fn_table(struct patch *patch)
 +static void prepare_fn_table(struct apply_state *state, struct patch *patch)
  {
        /*
         * store information about incoming file deletion
        while (patch) {
                if ((patch->new_name == NULL) || (patch->is_rename)) {
                        struct string_list_item *item;
 -                      item = string_list_insert(&fn_table, patch->old_name);
 +                      item = string_list_insert(&state->fn_table, patch->old_name);
                        item->util = PATH_TO_BE_DELETED;
                }
                patch = patch->next;
@@@ -3315,9 -3185,7 +3315,9 @@@ static int checkout_target(struct index
        return 0;
  }
  
 -static struct patch *previous_patch(struct patch *patch, int *gone)
 +static struct patch *previous_patch(struct apply_state *state,
 +                                  struct patch *patch,
 +                                  int *gone)
  {
        struct patch *previous;
  
        if (patch->is_copy || patch->is_rename)
                return NULL; /* "git" patches do not depend on the order */
  
 -      previous = in_fn_table(patch->old_name);
 +      previous = in_fn_table(state, patch->old_name);
        if (!previous)
                return NULL;
  
@@@ -3350,14 -3218,13 +3350,14 @@@ static int verify_index_match(const str
  
  #define SUBMODULE_PATCH_WITHOUT_INDEX 1
  
 -static int load_patch_target(struct strbuf *buf,
 +static int load_patch_target(struct apply_state *state,
 +                           struct strbuf *buf,
                             const struct cache_entry *ce,
                             struct stat *st,
                             const char *name,
                             unsigned expected_mode)
  {
 -      if (cached || check_index) {
 +      if (state->cached || state->check_index) {
                if (read_file_or_gitlink(ce, buf))
                        return error(_("read of %s failed"), name);
        } else if (name) {
   * applying a non-git patch that incrementally updates the tree,
   * we read from the result of a previous diff.
   */
 -static int load_preimage(struct image *image,
 +static int load_preimage(struct apply_state *state,
 +                       struct image *image,
                         struct patch *patch, struct stat *st,
                         const struct cache_entry *ce)
  {
        struct patch *previous;
        int status;
  
 -      previous = previous_patch(patch, &status);
 +      previous = previous_patch(state, patch, &status);
        if (status)
                return error(_("path %s has been renamed/deleted"),
                             patch->old_name);
                /* We have a patched copy in memory; use that. */
                strbuf_add(&buf, previous->result, previous->resultsize);
        } else {
 -              status = load_patch_target(&buf, ce, st,
 +              status = load_patch_target(state, &buf, ce, st,
                                           patch->old_name, patch->old_mode);
                if (status < 0)
                        return status;
@@@ -3461,9 -3327,7 +3461,9 @@@ static int three_way_merge(struct imag
   * the current contents of the new_name.  In no cases other than that
   * this function will be called.
   */
 -static int load_current(struct image *image, struct patch *patch)
 +static int load_current(struct apply_state *state,
 +                      struct image *image,
 +                      struct patch *patch)
  {
        struct strbuf buf = STRBUF_INIT;
        int status, pos;
        if (verify_index_match(ce, &st))
                return error(_("%s: does not match index"), name);
  
 -      status = load_patch_target(&buf, ce, &st, name, mode);
 +      status = load_patch_target(state, &buf, ce, &st, name, mode);
        if (status < 0)
                return status;
        else if (status)
        return 0;
  }
  
 -static int try_threeway(struct image *image, struct patch *patch,
 -                      struct stat *st, const struct cache_entry *ce)
 +static int try_threeway(struct apply_state *state,
 +                      struct image *image,
 +                      struct patch *patch,
 +                      struct stat *st,
 +                      const struct cache_entry *ce)
  {
        unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
        struct strbuf buf = STRBUF_INIT;
        img = strbuf_detach(&buf, &len);
        prepare_image(&tmp_image, img, len, 1);
        /* Apply the patch to get the post image */
 -      if (apply_fragments(&tmp_image, patch) < 0) {
 +      if (apply_fragments(state, &tmp_image, patch) < 0) {
                clear_image(&tmp_image);
                return -1;
        }
  
        /* our_sha1[] is ours */
        if (patch->is_new) {
 -              if (load_current(&tmp_image, patch))
 +              if (load_current(state, &tmp_image, patch))
                        return error("cannot read the current contents of '%s'",
                                     patch->new_name);
        } else {
 -              if (load_preimage(&tmp_image, patch, st, ce))
 +              if (load_preimage(state, &tmp_image, patch, st, ce))
                        return error("cannot read the current contents of '%s'",
                                     patch->old_name);
        }
        return 0;
  }
  
 -static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
 +static int apply_data(struct apply_state *state, struct patch *patch,
 +                    struct stat *st, const struct cache_entry *ce)
  {
        struct image image;
  
 -      if (load_preimage(&image, patch, st, ce) < 0)
 +      if (load_preimage(state, &image, patch, st, ce) < 0)
                return -1;
  
        if (patch->direct_to_threeway ||
 -          apply_fragments(&image, patch) < 0) {
 +          apply_fragments(state, &image, patch) < 0) {
                /* Note: with --reject, apply_fragments() returns 0 */
 -              if (!threeway || try_threeway(&image, patch, st, ce) < 0)
 +              if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0)
                        return -1;
        }
        patch->result = image.buf;
        patch->resultsize = image.len;
 -      add_to_fn_table(patch);
 +      add_to_fn_table(state, patch);
        free(image.line_allocated);
  
        if (0 < patch->is_delete && patch->resultsize)
   * check_patch() separately makes sure (and errors out otherwise) that
   * the path the patch creates does not exist in the current tree.
   */
 -static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
 +static int check_preimage(struct apply_state *state,
 +                        struct patch *patch,
 +                        struct cache_entry **ce,
 +                        struct stat *st)
  {
        const char *old_name = patch->old_name;
        struct patch *previous = NULL;
                return 0;
  
        assert(patch->is_new <= 0);
 -      previous = previous_patch(patch, &status);
 +      previous = previous_patch(state, patch, &status);
  
        if (status)
                return error(_("path %s has been renamed/deleted"), old_name);
        if (previous) {
                st_mode = previous->new_mode;
 -      } else if (!cached) {
 +      } else if (!state->cached) {
                stat_ret = lstat(old_name, st);
                if (stat_ret && errno != ENOENT)
                        return error(_("%s: %s"), old_name, strerror(errno));
        }
  
 -      if (check_index && !previous) {
 +      if (state->check_index && !previous) {
                int pos = cache_name_pos(old_name, strlen(old_name));
                if (pos < 0) {
                        if (patch->is_new < 0)
                        if (checkout_target(&the_index, *ce, st))
                                return -1;
                }
 -              if (!cached && verify_index_match(*ce, st))
 +              if (!state->cached && verify_index_match(*ce, st))
                        return error(_("%s: does not match index"), old_name);
 -              if (cached)
 +              if (state->cached)
                        st_mode = (*ce)->ce_mode;
        } else if (stat_ret < 0) {
                if (patch->is_new < 0)
                return error(_("%s: %s"), old_name, strerror(errno));
        }
  
 -      if (!cached && !previous)
 +      if (!state->cached && !previous)
                st_mode = ce_mode_from_stat(*ce, st->st_mode);
  
        if (patch->is_new < 0)
  #define EXISTS_IN_INDEX 1
  #define EXISTS_IN_WORKTREE 2
  
 -static int check_to_create(const char *new_name, int ok_if_exists)
 +static int check_to_create(struct apply_state *state,
 +                         const char *new_name,
 +                         int ok_if_exists)
  {
        struct stat nst;
  
 -      if (check_index &&
 +      if (state->check_index &&
            cache_name_pos(new_name, strlen(new_name)) >= 0 &&
            !ok_if_exists)
                return EXISTS_IN_INDEX;
 -      if (cached)
 +      if (state->cached)
                return 0;
  
        if (!lstat(new_name, &nst)) {
        return 0;
  }
  
 -/*
 - * We need to keep track of how symlinks in the preimage are
 - * manipulated by the patches.  A patch to add a/b/c where a/b
 - * is a symlink should not be allowed to affect the directory
 - * the symlink points at, but if the same patch removes a/b,
 - * it is perfectly fine, as the patch removes a/b to make room
 - * to create a directory a/b so that a/b/c can be created.
 - */
 -static struct string_list symlink_changes = STRING_LIST_INIT_NODUP;
 -#define SYMLINK_GOES_AWAY 01
 -#define SYMLINK_IN_RESULT 02
 -
 -static uintptr_t register_symlink_changes(const char *path, uintptr_t what)
 +static uintptr_t register_symlink_changes(struct apply_state *state,
 +                                        const char *path,
 +                                        uintptr_t what)
  {
        struct string_list_item *ent;
  
 -      ent = string_list_lookup(&symlink_changes, path);
 +      ent = string_list_lookup(&state->symlink_changes, path);
        if (!ent) {
 -              ent = string_list_insert(&symlink_changes, path);
 +              ent = string_list_insert(&state->symlink_changes, path);
                ent->util = (void *)0;
        }
        ent->util = (void *)(what | ((uintptr_t)ent->util));
        return (uintptr_t)ent->util;
  }
  
 -static uintptr_t check_symlink_changes(const char *path)
 +static uintptr_t check_symlink_changes(struct apply_state *state, const char *path)
  {
        struct string_list_item *ent;
  
 -      ent = string_list_lookup(&symlink_changes, path);
 +      ent = string_list_lookup(&state->symlink_changes, path);
        if (!ent)
                return 0;
        return (uintptr_t)ent->util;
  }
  
 -static void prepare_symlink_changes(struct patch *patch)
 +static void prepare_symlink_changes(struct apply_state *state, struct patch *patch)
  {
        for ( ; patch; patch = patch->next) {
                if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
                    (patch->is_rename || patch->is_delete))
                        /* the symlink at patch->old_name is removed */
 -                      register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY);
 +                      register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY);
  
                if (patch->new_name && S_ISLNK(patch->new_mode))
                        /* the symlink at patch->new_name is created or remains */
 -                      register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT);
 +                      register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT);
        }
  }
  
 -static int path_is_beyond_symlink_1(struct strbuf *name)
 +static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name)
  {
        do {
                unsigned int change;
                if (!name->len)
                        break;
                name->buf[name->len] = '\0';
 -              change = check_symlink_changes(name->buf);
 +              change = check_symlink_changes(state, name->buf);
                if (change & SYMLINK_IN_RESULT)
                        return 1;
                if (change & SYMLINK_GOES_AWAY)
                        continue;
  
                /* otherwise, check the preimage */
 -              if (check_index) {
 +              if (state->check_index) {
                        struct cache_entry *ce;
  
                        ce = cache_file_exists(name->buf, name->len, ignore_case);
        return 0;
  }
  
 -static int path_is_beyond_symlink(const char *name_)
 +static int path_is_beyond_symlink(struct apply_state *state, const char *name_)
  {
        int ret;
        struct strbuf name = STRBUF_INIT;
  
        assert(*name_ != '\0');
        strbuf_addstr(&name, name_);
 -      ret = path_is_beyond_symlink_1(&name);
 +      ret = path_is_beyond_symlink_1(state, &name);
        strbuf_release(&name);
  
        return ret;
@@@ -3828,7 -3693,7 +3828,7 @@@ static void die_on_unsafe_path(struct p
   * Check and apply the patch in-core; leave the result in patch->result
   * for the caller to write it out to the final destination.
   */
 -static int check_patch(struct patch *patch)
 +static int check_patch(struct apply_state *state, struct patch *patch)
  {
        struct stat st;
        const char *old_name = patch->old_name;
  
        patch->rejected = 1; /* we will drop this after we succeed */
  
 -      status = check_preimage(patch, &ce, &st);
 +      status = check_preimage(state, patch, &ce, &st);
        if (status)
                return status;
        old_name = patch->old_name;
         * B and rename from A to B is handled the same way by asking
         * was_deleted().
         */
 -      if ((tpatch = in_fn_table(new_name)) &&
 +      if ((tpatch = in_fn_table(state, new_name)) &&
            (was_deleted(tpatch) || to_be_deleted(tpatch)))
                ok_if_exists = 1;
        else
  
        if (new_name &&
            ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
 -              int err = check_to_create(new_name, ok_if_exists);
 +              int err = check_to_create(state, new_name, ok_if_exists);
  
 -              if (err && threeway) {
 +              if (err && state->threeway) {
                        patch->direct_to_threeway = 1;
                } else switch (err) {
                case 0:
                }
        }
  
 -      if (!unsafe_paths)
 +      if (!state->unsafe_paths)
                die_on_unsafe_path(patch);
  
        /*
         * is not deposited to a path that is beyond a symbolic link
         * here.
         */
 -      if (!patch->is_delete && path_is_beyond_symlink(patch->new_name))
 +      if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name))
                return error(_("affected file '%s' is beyond a symbolic link"),
                             patch->new_name);
  
 -      if (apply_data(patch, &st, ce) < 0)
 +      if (apply_data(state, patch, &st, ce) < 0)
                return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
        return 0;
  }
  
 -static int check_patch_list(struct patch *patch)
 +static int check_patch_list(struct apply_state *state, struct patch *patch)
  {
        int err = 0;
  
 -      prepare_symlink_changes(patch);
 -      prepare_fn_table(patch);
 +      prepare_symlink_changes(state, patch);
 +      prepare_fn_table(state, patch);
        while (patch) {
 -              if (apply_verbosely)
 +              if (state->apply_verbosely)
                        say_patch_name(stderr,
                                       _("Checking patch %s..."), patch);
 -              err |= check_patch(patch);
 +              err |= check_patch(state, patch);
                patch = patch->next;
        }
        return err;
@@@ -4047,7 -3912,7 +4047,7 @@@ static void build_fake_ancestor(struct 
        discard_index(&result);
  }
  
 -static void stat_patch_list(struct patch *patch)
 +static void stat_patch_list(struct apply_state *state, struct patch *patch)
  {
        int files, adds, dels;
  
                files++;
                adds += patch->lines_added;
                dels += patch->lines_deleted;
 -              show_stats(patch);
 +              show_stats(state, patch);
        }
  
        print_stat_summary(stdout, files, adds, dels);
  }
  
 -static void numstat_patch_list(struct patch *patch)
 +static void numstat_patch_list(struct apply_state *state,
 +                             struct patch *patch)
  {
        for ( ; patch; patch = patch->next) {
                const char *name;
                        printf("-\t-\t");
                else
                        printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
 -              write_name_quoted(name, stdout, line_termination);
 +              write_name_quoted(name, stdout, state->line_termination);
        }
  }
  
@@@ -4153,53 -4017,49 +4153,53 @@@ static void summary_patch_list(struct p
        }
  }
  
 -static void patch_stats(struct patch *patch)
 +static void patch_stats(struct apply_state *state, struct patch *patch)
  {
        int lines = patch->lines_added + patch->lines_deleted;
  
 -      if (lines > max_change)
 -              max_change = lines;
 +      if (lines > state->max_change)
 +              state->max_change = lines;
        if (patch->old_name) {
                int len = quote_c_style(patch->old_name, NULL, NULL, 0);
                if (!len)
                        len = strlen(patch->old_name);
 -              if (len > max_len)
 -                      max_len = len;
 +              if (len > state->max_len)
 +                      state->max_len = len;
        }
        if (patch->new_name) {
                int len = quote_c_style(patch->new_name, NULL, NULL, 0);
                if (!len)
                        len = strlen(patch->new_name);
 -              if (len > max_len)
 -                      max_len = len;
 +              if (len > state->max_len)
 +                      state->max_len = len;
        }
  }
  
 -static void remove_file(struct patch *patch, int rmdir_empty)
 +static void remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
  {
 -      if (update_index) {
 +      if (state->update_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
                        die(_("unable to remove %s from index"), patch->old_name);
        }
 -      if (!cached) {
 +      if (!state->cached) {
                if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
                        remove_path(patch->old_name);
                }
        }
  }
  
 -static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
 +static void add_index_file(struct apply_state *state,
 +                         const char *path,
 +                         unsigned mode,
 +                         void *buf,
 +                         unsigned long size)
  {
        struct stat st;
        struct cache_entry *ce;
        int namelen = strlen(path);
        unsigned ce_size = cache_entry_size(namelen);
  
 -      if (!update_index)
 +      if (!state->update_index)
                return;
  
        ce = xcalloc(1, ce_size);
                    get_sha1_hex(s, ce->sha1))
                        die(_("corrupt patch for submodule %s"), path);
        } else {
 -              if (!cached) {
 +              if (!state->cached) {
                        if (lstat(path, &st) < 0)
                                die_errno(_("unable to stat newly created file '%s'"),
                                          path);
@@@ -4266,13 -4126,9 +4266,13 @@@ static int try_create_file(const char *
   * which is true 99% of the time anyway. If they don't,
   * we create them and try again.
   */
 -static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
 +static void create_one_file(struct apply_state *state,
 +                          char *path,
 +                          unsigned mode,
 +                          const char *buf,
 +                          unsigned long size)
  {
 -      if (cached)
 +      if (state->cached)
                return;
        if (!try_create_file(path, mode, buf, size))
                return;
        die_errno(_("unable to write file '%s' mode %o"), path, mode);
  }
  
 -static void add_conflicted_stages_file(struct patch *patch)
 +static void add_conflicted_stages_file(struct apply_state *state,
 +                                     struct patch *patch)
  {
        int stage, namelen;
        unsigned ce_size, mode;
        struct cache_entry *ce;
  
 -      if (!update_index)
 +      if (!state->update_index)
                return;
        namelen = strlen(patch->new_name);
        ce_size = cache_entry_size(namelen);
        }
  }
  
 -static void create_file(struct patch *patch)
 +static void create_file(struct apply_state *state, struct patch *patch)
  {
        char *path = patch->new_name;
        unsigned mode = patch->new_mode;
  
        if (!mode)
                mode = S_IFREG | 0644;
 -      create_one_file(path, mode, buf, size);
 +      create_one_file(state, path, mode, buf, size);
  
        if (patch->conflicted_threeway)
 -              add_conflicted_stages_file(patch);
 +              add_conflicted_stages_file(state, patch);
        else
 -              add_index_file(path, mode, buf, size);
 +              add_index_file(state, path, mode, buf, size);
  }
  
  /* phase zero is to remove, phase one is to create */
 -static void write_out_one_result(struct patch *patch, int phase)
 +static void write_out_one_result(struct apply_state *state,
 +                               struct patch *patch,
 +                               int phase)
  {
        if (patch->is_delete > 0) {
                if (phase == 0)
 -                      remove_file(patch, 1);
 +                      remove_file(state, patch, 1);
                return;
        }
        if (patch->is_new > 0 || patch->is_copy) {
                if (phase == 1)
 -                      create_file(patch);
 +                      create_file(state, patch);
                return;
        }
        /*
         * thing: remove the old, write the new
         */
        if (phase == 0)
 -              remove_file(patch, patch->is_rename);
 +              remove_file(state, patch, patch->is_rename);
        if (phase == 1)
 -              create_file(patch);
 +              create_file(state, patch);
  }
  
 -static int write_out_one_reject(struct patch *patch)
 +static int write_out_one_reject(struct apply_state *state, struct patch *patch)
  {
        FILE *rej;
        char namebuf[PATH_MAX];
        }
  
        if (!cnt) {
 -              if (apply_verbosely)
 +              if (state->apply_verbosely)
                        say_patch_name(stderr,
                                       _("Applied patch %s cleanly."), patch);
                return 0;
        return -1;
  }
  
 -static int write_out_results(struct patch *list)
 +static int write_out_results(struct apply_state *state, struct patch *list)
  {
        int phase;
        int errs = 0;
                        if (l->rejected)
                                errs = 1;
                        else {
 -                              write_out_one_result(l, phase);
 +                              write_out_one_result(state, l, phase);
                                if (phase == 1) {
 -                                      if (write_out_one_reject(l))
 +                                      if (write_out_one_reject(state, l))
                                                errs = 1;
                                        if (l->conflicted_threeway) {
                                                string_list_append(&cpath, l->new_name);
@@@ -4500,17 -4353,14 +4500,17 @@@ static struct lock_file lock_file
  #define INACCURATE_EOF        (1<<0)
  #define RECOUNT               (1<<1)
  
 -static int apply_patch(int fd, const char *filename, int options)
 +static int apply_patch(struct apply_state *state,
 +                     int fd,
 +                     const char *filename,
 +                     int options)
  {
        size_t offset;
        struct strbuf buf = STRBUF_INIT; /* owns the patch text */
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
  
 -      patch_input_file = filename;
 +      state->patch_input_file = filename;
        read_patch_file(&buf, fd);
        offset = 0;
        while (offset < buf.len) {
                patch = xcalloc(1, sizeof(*patch));
                patch->inaccurate_eof = !!(options & INACCURATE_EOF);
                patch->recount =  !!(options & RECOUNT);
 -              nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
 +              nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch);
                if (nr < 0) {
                        free_patch(patch);
                        break;
                }
 -              if (apply_in_reverse)
 +              if (state->apply_in_reverse)
                        reverse_patches(patch);
 -              if (use_patch(patch)) {
 -                      patch_stats(patch);
 +              if (use_patch(state, patch)) {
 +                      patch_stats(state, patch);
                        *listp = patch;
                        listp = &patch->next;
                }
                else {
 -                      if (apply_verbosely)
 +                      if (state->apply_verbosely)
                                say_patch_name(stderr, _("Skipped patch '%s'."), patch);
                        free_patch(patch);
                        skipped_patch++;
        if (!list && !skipped_patch)
                die(_("unrecognized input"));
  
 -      if (whitespace_error && (ws_error_action == die_on_ws_error))
 -              apply = 0;
 +      if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
 +              state->apply = 0;
  
 -      update_index = check_index && apply;
 -      if (update_index && newfd < 0)
 -              newfd = hold_locked_index(&lock_file, 1);
 +      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 (check_index) {
 +      if (state->check_index) {
                if (read_cache() < 0)
                        die(_("unable to read index file"));
        }
  
 -      if ((check || apply) &&
 -          check_patch_list(list) < 0 &&
 -          !apply_with_reject)
 +      if ((state->check || state->apply) &&
 +          check_patch_list(state, list) < 0 &&
 +          !state->apply_with_reject)
                exit(1);
  
 -      if (apply && write_out_results(list)) {
 -              if (apply_with_reject)
 +      if (state->apply && write_out_results(state, list)) {
 +              if (state->apply_with_reject)
                        exit(1);
                /* with --3way, we still need to write the index out */
                return 1;
        }
  
 -      if (fake_ancestor)
 -              build_fake_ancestor(list, fake_ancestor);
 +      if (state->fake_ancestor)
 +              build_fake_ancestor(list, state->fake_ancestor);
  
 -      if (diffstat)
 -              stat_patch_list(list);
 +      if (state->diffstat)
 +              stat_patch_list(state, list);
  
 -      if (numstat)
 -              numstat_patch_list(list);
 +      if (state->numstat)
 +              numstat_patch_list(state, list);
  
 -      if (summary)
 +      if (state->summary)
                summary_patch_list(list);
  
        free_patch_list(list);
        strbuf_release(&buf);
 -      string_list_clear(&fn_table, 0);
 +      string_list_clear(&state->fn_table, 0);
        return 0;
  }
  
@@@ -4596,281 -4446,220 +4596,284 @@@ static void git_apply_config(void
  static int option_parse_exclude(const struct option *opt,
                                const char *arg, int unset)
  {
 -      add_name_limit(arg, 1);
 +      struct apply_state *state = opt->value;
 +      add_name_limit(state, arg, 1);
        return 0;
  }
  
  static int option_parse_include(const struct option *opt,
                                const char *arg, int unset)
  {
 -      add_name_limit(arg, 0);
 -      has_include = 1;
 +      struct apply_state *state = opt->value;
 +      add_name_limit(state, arg, 0);
 +      state->has_include = 1;
        return 0;
  }
  
  static int option_parse_p(const struct option *opt,
 -                        const char *arg, int unset)
 +                        const char *arg,
 +                        int unset)
  {
 -      p_value = atoi(arg);
 -      p_value_known = 1;
 +      struct apply_state *state = opt->value;
 +      state->p_value = atoi(arg);
 +      state->p_value_known = 1;
        return 0;
  }
  
  static int option_parse_space_change(const struct option *opt,
 -                        const char *arg, int unset)
 +                                   const char *arg, int unset)
  {
 +      struct apply_state *state = opt->value;
        if (unset)
 -              ws_ignore_action = ignore_ws_none;
 +              state->ws_ignore_action = ignore_ws_none;
        else
 -              ws_ignore_action = ignore_ws_change;
 +              state->ws_ignore_action = ignore_ws_change;
        return 0;
  }
  
  static int option_parse_whitespace(const struct option *opt,
                                   const char *arg, int unset)
  {
 -      const char **whitespace_option = opt->value;
 -
 -      *whitespace_option = arg;
 -      parse_whitespace_option(arg);
 +      struct apply_state *state = opt->value;
 +      state->whitespace_option = arg;
 +      parse_whitespace_option(state, arg);
        return 0;
  }
  
  static int option_parse_directory(const struct option *opt,
                                  const char *arg, int unset)
  {
 -      strbuf_reset(&root);
 -      strbuf_addstr(&root, arg);
 -      strbuf_complete(&root, '/');
 +      struct apply_state *state = opt->value;
 +      strbuf_reset(&state->root);
 +      strbuf_addstr(&state->root, arg);
 +      strbuf_complete(&state->root, '/');
        return 0;
  }
  
 -int cmd_apply(int argc, const char **argv, const char *prefix_)
 +static void init_apply_state(struct apply_state *state,
 +                           const char *prefix,
 +                           struct lock_file *lock_file)
 +{
 +      memset(state, 0, sizeof(*state));
 +      state->prefix = prefix;
 +      state->prefix_length = state->prefix ? strlen(state->prefix) : 0;
 +      state->lock_file = lock_file;
 +      state->newfd = -1;
 +      state->apply = 1;
 +      state->line_termination = '\n';
 +      state->p_value = 1;
 +      state->p_context = UINT_MAX;
 +      state->squelch_whitespace_errors = 5;
 +      state->ws_error_action = warn_on_ws_error;
 +      state->ws_ignore_action = ignore_ws_none;
 +      state->linenr = 1;
++      string_list_init(&state->fn_table, 0);
++      string_list_init(&state->limit_by_name, 0);
++      string_list_init(&state->symlink_changes, 0);
 +      strbuf_init(&state->root, 0);
 +
 +      git_apply_config();
 +      if (apply_default_whitespace)
 +              parse_whitespace_option(state, apply_default_whitespace);
 +      if (apply_default_ignorewhitespace)
 +              parse_ignorewhitespace_option(state, apply_default_ignorewhitespace);
 +}
 +
 +static void clear_apply_state(struct apply_state *state)
 +{
 +      string_list_clear(&state->limit_by_name, 0);
 +      string_list_clear(&state->symlink_changes, 0);
 +      strbuf_release(&state->root);
 +
 +      /* &state->fn_table is cleared at the end of apply_patch() */
 +}
 +
 +static void 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)
 +              die("--reject and --3way cannot be used together.");
 +      if (state->cached && state->threeway)
 +              die("--cached and --3way cannot be used together.");
 +      if (state->threeway) {
 +              if (is_not_gitdir)
 +                      die(_("--3way outside a repository"));
 +              state->check_index = 1;
 +      }
 +      if (state->apply_with_reject)
 +              state->apply = state->apply_verbosely = 1;
 +      if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
 +              state->apply = 0;
 +      if (state->check_index && is_not_gitdir)
 +              die(_("--index outside a repository"));
 +      if (state->cached) {
 +              if (is_not_gitdir)
 +                      die(_("--cached outside a repository"));
 +              state->check_index = 1;
 +      }
 +      if (state->check_index)
 +              state->unsafe_paths = 0;
 +      if (!state->lock_file)
 +              die("BUG: state->lock_file should not be NULL");
 +}
 +
 +static int apply_all_patches(struct apply_state *state,
 +                           int argc,
 +                           const char **argv,
 +                           int options)
  {
        int i;
        int errs = 0;
 -      int is_not_gitdir = !startup_info->have_repository;
 -      int force_apply = 0;
 +      int read_stdin = 1;
 +
 +      for (i = 0; i < argc; i++) {
 +              const char *arg = argv[i];
 +              int fd;
 +
 +              if (!strcmp(arg, "-")) {
 +                      errs |= apply_patch(state, 0, "<stdin>", options);
 +                      read_stdin = 0;
 +                      continue;
 +              } else if (0 < state->prefix_length)
 +                      arg = prefix_filename(state->prefix,
 +                                            state->prefix_length,
 +                                            arg);
 +
 +              fd = open(arg, O_RDONLY);
 +              if (fd < 0)
 +                      die_errno(_("can't open patch '%s'"), arg);
 +              read_stdin = 0;
 +              set_default_whitespace_mode(state);
 +              errs |= apply_patch(state, fd, arg, options);
 +              close(fd);
 +      }
 +      set_default_whitespace_mode(state);
 +      if (read_stdin)
 +              errs |= apply_patch(state, 0, "<stdin>", options);
 +
 +      if (state->whitespace_error) {
 +              if (state->squelch_whitespace_errors &&
 +                  state->squelch_whitespace_errors < state->whitespace_error) {
 +                      int squelched =
 +                              state->whitespace_error - state->squelch_whitespace_errors;
 +                      warning(Q_("squelched %d whitespace error",
 +                                 "squelched %d whitespace errors",
 +                                 squelched),
 +                              squelched);
 +              }
 +              if (state->ws_error_action == die_on_ws_error)
 +                      die(Q_("%d line adds whitespace errors.",
 +                             "%d lines add whitespace errors.",
 +                             state->whitespace_error),
 +                          state->whitespace_error);
 +              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");
 +              else if (state->whitespace_error)
 +                      warning(Q_("%d line adds whitespace errors.",
 +                                 "%d lines add whitespace errors.",
 +                                 state->whitespace_error),
 +                              state->whitespace_error);
 +      }
 +
 +      if (state->update_index) {
 +              if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK))
 +                      die(_("Unable to write new index file"));
 +              state->newfd = -1;
 +      }
 +
 +      return !!errs;
 +}
  
 -      const char *whitespace_option = NULL;
 +int cmd_apply(int argc, const char **argv, const char *prefix)
 +{
 +      int force_apply = 0;
 +      int options = 0;
 +      int ret;
 +      struct apply_state state;
  
        struct option builtin_apply_options[] = {
 -              { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
 +              { OPTION_CALLBACK, 0, "exclude", &state, N_("path"),
                        N_("don't apply changes matching the given path"),
                        0, option_parse_exclude },
 -              { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
 +              { OPTION_CALLBACK, 0, "include", &state, N_("path"),
                        N_("apply changes matching the given path"),
                        0, option_parse_include },
 -              { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
 +              { OPTION_CALLBACK, 'p', NULL, &state, N_("num"),
                        N_("remove <num> leading slashes from traditional diff paths"),
                        0, option_parse_p },
 -              OPT_BOOL(0, "no-add", &no_add,
 +              OPT_BOOL(0, "no-add", &state.no_add,
                        N_("ignore additions made by the patch")),
 -              OPT_BOOL(0, "stat", &diffstat,
 +              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", &numstat,
 +              OPT_BOOL(0, "numstat", &state.numstat,
                        N_("show number of added and deleted lines in decimal notation")),
 -              OPT_BOOL(0, "summary", &summary,
 +              OPT_BOOL(0, "summary", &state.summary,
                        N_("instead of applying the patch, output a summary for the input")),
 -              OPT_BOOL(0, "check", &check,
 +              OPT_BOOL(0, "check", &state.check,
                        N_("instead of applying the patch, see if the patch is applicable")),
 -              OPT_BOOL(0, "index", &check_index,
 +              OPT_BOOL(0, "index", &state.check_index,
                        N_("make sure the patch is applicable to the current index")),
 -              OPT_BOOL(0, "cached", &cached,
 +              OPT_BOOL(0, "cached", &state.cached,
                        N_("apply a patch without touching the working tree")),
 -              OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
 +              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", &threeway,
 +              OPT_BOOL('3', "3way", &state.threeway,
                         N_( "attempt three-way merge if a patch does not apply")),
 -              OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
 +              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, &line_termination,
 +              OPT_SET_INT('z', NULL, &state.line_termination,
                        N_("paths are separated with NUL character"), '\0'),
 -              OPT_INTEGER('C', NULL, &p_context,
 +              OPT_INTEGER('C', NULL, &state.p_context,
                                N_("ensure at least <n> lines of context match")),
 -              { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
 +              { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"),
                        N_("detect new or modified lines that have whitespace errors"),
                        0, option_parse_whitespace },
 -              { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
 +              { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL,
                        N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
 -              { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
 +              { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL,
                        N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
 -              OPT_BOOL('R', "reverse", &apply_in_reverse,
 +              OPT_BOOL('R', "reverse", &state.apply_in_reverse,
                        N_("apply the patch in reverse")),
 -              OPT_BOOL(0, "unidiff-zero", &unidiff_zero,
 +              OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero,
                        N_("don't expect at least one line of context")),
 -              OPT_BOOL(0, "reject", &apply_with_reject,
 +              OPT_BOOL(0, "reject", &state.apply_with_reject,
                        N_("leave the rejected hunks in corresponding *.rej files")),
 -              OPT_BOOL(0, "allow-overlap", &allow_overlap,
 +              OPT_BOOL(0, "allow-overlap", &state.allow_overlap,
                        N_("allow overlapping hunks")),
 -              OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
 +              OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")),
                OPT_BIT(0, "inaccurate-eof", &options,
                        N_("tolerate incorrectly detected missing new-line at the end of file"),
                        INACCURATE_EOF),
                OPT_BIT(0, "recount", &options,
                        N_("do not trust the line counts in the hunk headers"),
                        RECOUNT),
 -              { OPTION_CALLBACK, 0, "directory", NULL, N_("root"),
 +              { OPTION_CALLBACK, 0, "directory", &state, N_("root"),
                        N_("prepend <root> to all filenames"),
                        0, option_parse_directory },
                OPT_END()
        };
  
 -      prefix = prefix_;
 -      prefix_length = prefix ? strlen(prefix) : 0;
 -      git_apply_config();
 -      if (apply_default_whitespace)
 -              parse_whitespace_option(apply_default_whitespace);
 -      if (apply_default_ignorewhitespace)
 -              parse_ignorewhitespace_option(apply_default_ignorewhitespace);
 +      init_apply_state(&state, prefix, &lock_file);
  
 -      argc = parse_options(argc, argv, prefix, builtin_apply_options,
 +      argc = parse_options(argc, argv, state.prefix, builtin_apply_options,
                        apply_usage, 0);
  
 -      if (apply_with_reject && threeway)
 -              die("--reject and --3way cannot be used together.");
 -      if (cached && threeway)
 -              die("--cached and --3way cannot be used together.");
 -      if (threeway) {
 -              if (is_not_gitdir)
 -                      die(_("--3way outside a repository"));
 -              check_index = 1;
 -      }
 -      if (apply_with_reject)
 -              apply = apply_verbosely = 1;
 -      if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
 -              apply = 0;
 -      if (check_index && is_not_gitdir)
 -              die(_("--index outside a repository"));
 -      if (cached) {
 -              if (is_not_gitdir)
 -                      die(_("--cached outside a repository"));
 -              check_index = 1;
 -      }
 -      if (check_index)
 -              unsafe_paths = 0;
 +      check_apply_state(&state, force_apply);
  
 -      for (i = 0; i < argc; i++) {
 -              const char *arg = argv[i];
 -              int fd;
 +      ret = apply_all_patches(&state, argc, argv, options);
  
 -              if (!strcmp(arg, "-")) {
 -                      errs |= apply_patch(0, "<stdin>", options);
 -                      read_stdin = 0;
 -                      continue;
 -              } else if (0 < prefix_length)
 -                      arg = prefix_filename(prefix, prefix_length, arg);
 +      clear_apply_state(&state);
  
 -              fd = open(arg, O_RDONLY);
 -              if (fd < 0)
 -                      die_errno(_("can't open patch '%s'"), arg);
 -              read_stdin = 0;
 -              set_default_whitespace_mode(whitespace_option);
 -              errs |= apply_patch(fd, arg, options);
 -              close(fd);
 -      }
 -      set_default_whitespace_mode(whitespace_option);
 -      if (read_stdin)
 -              errs |= apply_patch(0, "<stdin>", options);
 -      if (whitespace_error) {
 -              if (squelch_whitespace_errors &&
 -                  squelch_whitespace_errors < whitespace_error) {
 -                      int squelched =
 -                              whitespace_error - squelch_whitespace_errors;
 -                      warning(Q_("squelched %d whitespace error",
 -                                 "squelched %d whitespace errors",
 -                                 squelched),
 -                              squelched);
 -              }
 -              if (ws_error_action == die_on_ws_error)
 -                      die(Q_("%d line adds whitespace errors.",
 -                             "%d lines add whitespace errors.",
 -                             whitespace_error),
 -                          whitespace_error);
 -              if (applied_after_fixing_ws && apply)
 -                      warning("%d line%s applied after"
 -                              " fixing whitespace errors.",
 -                              applied_after_fixing_ws,
 -                              applied_after_fixing_ws == 1 ? "" : "s");
 -              else if (whitespace_error)
 -                      warning(Q_("%d line adds whitespace errors.",
 -                                 "%d lines add whitespace errors.",
 -                                 whitespace_error),
 -                              whitespace_error);
 -      }
 -
 -      if (update_index) {
 -              if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 -                      die(_("Unable to write new index file"));
 -      }
 -
 -      return !!errs;
 +      return ret;
  }
diff --combined builtin/blame.c
index e5deb9c67ff696e378609908d18878717cb5ce43,de701536851662f9d9b3c8e88dd4d80e7e866800..1e214bd4ec2161ff0a9620f6433f43a3407c4c9e
@@@ -56,7 -56,7 +56,7 @@@ static int show_progress
  static struct date_mode blame_date_mode = { DATE_ISO8601 };
  static size_t blame_date_width;
  
- static struct string_list mailmap;
+ static struct string_list mailmap = STRING_LIST_INIT_NODUP;
  
  #ifndef DEBUG
  #define DEBUG 0
@@@ -609,7 -609,7 +609,7 @@@ static struct origin *find_origin(struc
                }
        }
        diff_flush(&diff_opts);
 -      free_pathspec(&diff_opts.pathspec);
 +      clear_pathspec(&diff_opts.pathspec);
        return porigin;
  }
  
@@@ -651,7 -651,7 +651,7 @@@ static struct origin *find_rename(struc
                }
        }
        diff_flush(&diff_opts);
 -      free_pathspec(&diff_opts.pathspec);
 +      clear_pathspec(&diff_opts.pathspec);
        return porigin;
  }
  
@@@ -1343,7 -1343,7 +1343,7 @@@ static void find_copy_in_parent(struct 
        } while (unblamed);
        target->suspects = reverse_blame(leftover, NULL);
        diff_flush(&diff_opts);
 -      free_pathspec(&diff_opts.pathspec);
 +      clear_pathspec(&diff_opts.pathspec);
  }
  
  /*
@@@ -2425,7 -2425,8 +2425,7 @@@ static struct commit *find_single_final
                struct object *obj = revs->pending.objects[i].item;
                if (obj->flags & UNINTERESTING)
                        continue;
 -              while (obj->type == OBJ_TAG)
 -                      obj = deref_tag(obj, NULL, 0);
 +              obj = deref_tag(obj, NULL, 0);
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (found)
@@@ -2460,7 -2461,8 +2460,7 @@@ static char *prepare_initial(struct sco
                struct object *obj = revs->pending.objects[i].item;
                if (!(obj->flags & UNINTERESTING))
                        continue;
 -              while (obj->type == OBJ_TAG)
 -                      obj = deref_tag(obj, NULL, 0);
 +              obj = deref_tag(obj, NULL, 0);
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (sb->final)
@@@ -2520,12 -2522,12 +2520,12 @@@ int cmd_blame(int argc, const char **ar
        enum object_type type;
        struct commit *final_commit = NULL;
  
-       static struct string_list range_list;
-       static int output_option = 0, opt = 0;
-       static int show_stats = 0;
-       static const char *revs_file = NULL;
-       static const char *contents_from = NULL;
-       static const struct option options[] = {
+       struct string_list range_list = STRING_LIST_INIT_NODUP;
+       int output_option = 0, opt = 0;
+       int show_stats = 0;
+       const char *revs_file = NULL;
+       const char *contents_from = NULL;
+       const struct option options[] = {
                OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
                OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
                OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
diff --combined builtin/log.c
index 099f4f7be92c6d4371dcc8cba1322351c6840439,c11bd66f3930818f4bacf88bea26e24bc5fbe245..8eef94f335489eeb91c247251b2dacddbcbb726b
@@@ -674,9 -674,9 +674,9 @@@ static int auto_number = 1
  
  static char *default_attach = NULL;
  
- static struct string_list extra_hdr;
- static struct string_list extra_to;
- static struct string_list extra_cc;
+ static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
+ static struct string_list extra_to = STRING_LIST_INIT_NODUP;
+ static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
  
  static void add_header(const char *value)
  {
  #define THREAD_DEEP 2
  static int thread;
  static int do_signoff;
 +static int base_auto;
  static const char *signature = git_version_string;
  static const char *signature_file;
  static int config_cover_letter;
@@@ -787,10 -786,6 +787,10 @@@ static int git_format_config(const cha
        }
        if (!strcmp(var, "format.outputdirectory"))
                return git_config_string(&config_output_directory, var, value);
 +      if (!strcmp(var, "format.useautobase")) {
 +              base_auto = git_config_bool(var, value);
 +              return 0;
 +      }
  
        return git_log_config(var, value, cb);
  }
@@@ -1196,155 -1191,6 +1196,155 @@@ static int from_callback(const struct o
        return 0;
  }
  
 +struct base_tree_info {
 +      struct object_id base_commit;
 +      int nr_patch_id, alloc_patch_id;
 +      struct object_id *patch_id;
 +};
 +
 +static struct commit *get_base_commit(const char *base_commit,
 +                                    struct commit **list,
 +                                    int total)
 +{
 +      struct commit *base = NULL;
 +      struct commit **rev;
 +      int i = 0, rev_nr = 0;
 +
 +      if (base_commit && strcmp(base_commit, "auto")) {
 +              base = lookup_commit_reference_by_name(base_commit);
 +              if (!base)
 +                      die(_("Unknown commit %s"), base_commit);
 +      } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
 +              struct branch *curr_branch = branch_get(NULL);
 +              const char *upstream = branch_get_upstream(curr_branch, NULL);
 +              if (upstream) {
 +                      struct commit_list *base_list;
 +                      struct commit *commit;
 +                      unsigned char sha1[20];
 +
 +                      if (get_sha1(upstream, sha1))
 +                              die(_("Failed to resolve '%s' as a valid ref."), upstream);
 +                      commit = lookup_commit_or_die(sha1, "upstream base");
 +                      base_list = get_merge_bases_many(commit, total, list);
 +                      /* There should be one and only one merge base. */
 +                      if (!base_list || base_list->next)
 +                              die(_("Could not find exact merge base."));
 +                      base = base_list->item;
 +                      free_commit_list(base_list);
 +              } else {
 +                      die(_("Failed to get upstream, if you want to record base commit automatically,\n"
 +                            "please use git branch --set-upstream-to to track a remote branch.\n"
 +                            "Or you could specify base commit by --base=<base-commit-id> manually."));
 +              }
 +      }
 +
 +      ALLOC_ARRAY(rev, total);
 +      for (i = 0; i < total; i++)
 +              rev[i] = list[i];
 +
 +      rev_nr = total;
 +      /*
 +       * Get merge base through pair-wise computations
 +       * and store it in rev[0].
 +       */
 +      while (rev_nr > 1) {
 +              for (i = 0; i < rev_nr / 2; i++) {
 +                      struct commit_list *merge_base;
 +                      merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
 +                      if (!merge_base || merge_base->next)
 +                              die(_("Failed to find exact merge base"));
 +
 +                      rev[i] = merge_base->item;
 +              }
 +
 +              if (rev_nr % 2)
 +                      rev[i] = rev[2 * i];
 +              rev_nr = (rev_nr + 1) / 2;
 +      }
 +
 +      if (!in_merge_bases(base, rev[0]))
 +              die(_("base commit should be the ancestor of revision list"));
 +
 +      for (i = 0; i < total; i++) {
 +              if (base == list[i])
 +                      die(_("base commit shouldn't be in revision list"));
 +      }
 +
 +      free(rev);
 +      return base;
 +}
 +
 +static void prepare_bases(struct base_tree_info *bases,
 +                        struct commit *base,
 +                        struct commit **list,
 +                        int total)
 +{
 +      struct commit *commit;
 +      struct rev_info revs;
 +      struct diff_options diffopt;
 +      int i;
 +
 +      if (!base)
 +              return;
 +
 +      diff_setup(&diffopt);
 +      DIFF_OPT_SET(&diffopt, RECURSIVE);
 +      diff_setup_done(&diffopt);
 +
 +      oidcpy(&bases->base_commit, &base->object.oid);
 +
 +      init_revisions(&revs, NULL);
 +      revs.max_parents = 1;
 +      revs.topo_order = 1;
 +      for (i = 0; i < total; i++) {
 +              list[i]->object.flags &= ~UNINTERESTING;
 +              add_pending_object(&revs, &list[i]->object, "rev_list");
 +              list[i]->util = (void *)1;
 +      }
 +      base->object.flags |= UNINTERESTING;
 +      add_pending_object(&revs, &base->object, "base");
 +
 +      if (prepare_revision_walk(&revs))
 +              die(_("revision walk setup failed"));
 +      /*
 +       * Traverse the commits list, get prerequisite patch ids
 +       * and stuff them in bases structure.
 +       */
 +      while ((commit = get_revision(&revs)) != NULL) {
 +              unsigned char sha1[20];
 +              struct object_id *patch_id;
 +              if (commit->util)
 +                      continue;
 +              if (commit_patch_id(commit, &diffopt, sha1))
 +                      die(_("cannot get patch id"));
 +              ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
 +              patch_id = bases->patch_id + bases->nr_patch_id;
 +              hashcpy(patch_id->hash, sha1);
 +              bases->nr_patch_id++;
 +      }
 +}
 +
 +static void print_bases(struct base_tree_info *bases)
 +{
 +      int i;
 +
 +      /* Only do this once, either for the cover or for the first one */
 +      if (is_null_oid(&bases->base_commit))
 +              return;
 +
 +      /* Show the base commit */
 +      printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
 +
 +      /* Show the prerequisite patches */
 +      for (i = bases->nr_patch_id - 1; i >= 0; i--)
 +              printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
 +
 +      free(bases->patch_id);
 +      bases->nr_patch_id = 0;
 +      bases->alloc_patch_id = 0;
 +      oidclr(&bases->base_commit);
 +}
 +
  int cmd_format_patch(int argc, const char **argv, const char *prefix)
  {
        struct commit *commit;
        int reroll_count = -1;
        char *branch_name = NULL;
        char *from = NULL;
 +      char *base_commit = NULL;
 +      struct base_tree_info bases;
 +
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            N_("use [PATCH n/m] even with a single patch"),
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, N_("signature"),
                            N_("add a signature")),
 +              OPT_STRING(0, "base", &base_commit, N_("base-commit"),
 +                         N_("add prerequisite tree info to the patch series")),
                OPT_FILENAME(0, "signature-file", &signature_file,
                                N_("add a signature from a file")),
                OPT__QUIET(&quiet, N_("don't print the patch filenames")),
                signature = strbuf_detach(&buf, NULL);
        }
  
 +      memset(&bases, 0, sizeof(bases));
 +      if (base_commit || base_auto) {
 +              struct commit *base = get_base_commit(base_commit, list, nr);
 +              reset_revision_walk();
 +              prepare_bases(&bases, base, list, nr);
 +      }
 +
        if (in_reply_to || thread || cover_letter)
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
                                  origin, nr, list, branch_name, quiet);
 +              print_bases(&bases);
                total++;
                start_number--;
        }
                                       rev.mime_boundary);
                        else
                                print_signature();
 +                      print_bases(&bases);
                }
                if (!use_stdout)
                        fclose(stdout);
diff --combined builtin/remote.c
index d33766be395ad637c4505f5ca9024a9b8331d99d,e1cc55e13c34b806f68fec5ee476dc3345fd2e8d..5ded3018afcf8c44fccac5062aa34df9ba013169
@@@ -247,7 -247,7 +247,7 @@@ struct branch_info 
        enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase;
  };
  
- static struct string_list branch_list;
+ static struct string_list branch_list = STRING_LIST_INIT_NODUP;
  
  static const char *abbrev_ref(const char *name, const char *prefix)
  {
@@@ -1154,8 -1154,6 +1154,8 @@@ static int show(int argc, const char **
                        url_nr = states.remote->url_nr;
                }
                for (i = 0; i < url_nr; i++)
 +                      /* TRANSLATORS: the colon ':' should align with
 +                         the one in "  Fetch URL: %s" translation */
                        printf_ln(_("  Push  URL: %s"), url[i]);
                if (!i)
                        printf_ln(_("  Push  URL: %s"), "(no URL)");
index 8a1235d03e2daab4b9e90d31c5da6708e5c712f2,37a196781dc1a53b925dd68235fe17284978777e..2c63298fab10563bcd8c98c3d0b6d563164e2283
@@@ -7,25 -7,16 +7,25 @@@ static int integer = 0
  static unsigned long magnitude = 0;
  static unsigned long timestamp;
  static int abbrev = 7;
 -static int verbose = 0, dry_run = 0, quiet = 0;
 +static int verbose = -1; /* unspecified */
 +static int dry_run = 0, quiet = 0;
  static char *string = NULL;
  static char *file = NULL;
  static int ambiguous;
- static struct string_list list;
+ static struct string_list list = STRING_LIST_INIT_NODUP;
  
 +static struct {
 +      int called;
 +      const char *arg;
 +      int unset;
 +} length_cb;
 +
  static int length_callback(const struct option *opt, const char *arg, int unset)
  {
 -      printf("Callback: \"%s\", %d\n",
 -              (arg ? arg : "not set"), unset);
 +      length_cb.called = 1;
 +      length_cb.arg = arg;
 +      length_cb.unset = unset;
 +
        if (unset)
                return 1; /* do not support unset */
  
@@@ -39,61 -30,6 +39,61 @@@ static int number_callback(const struc
        return 0;
  }
  
 +static int collect_expect(const struct option *opt, const char *arg, int unset)
 +{
 +      struct string_list *expect;
 +      struct string_list_item *item;
 +      struct strbuf label = STRBUF_INIT;
 +      const char *colon;
 +
 +      if (!arg || unset)
 +              die("malformed --expect option");
 +
 +      expect = (struct string_list *)opt->value;
 +      colon = strchr(arg, ':');
 +      if (!colon)
 +              die("malformed --expect option, lacking a colon");
 +      strbuf_add(&label, arg, colon - arg);
 +      item = string_list_insert(expect, strbuf_detach(&label, NULL));
 +      if (item->util)
 +              die("malformed --expect option, duplicate %s", label.buf);
 +      item->util = (void *)arg;
 +      return 0;
 +}
 +
 +__attribute__((format (printf,3,4)))
 +static void show(struct string_list *expect, int *status, const char *fmt, ...)
 +{
 +      struct string_list_item *item;
 +      struct strbuf buf = STRBUF_INIT;
 +      va_list args;
 +
 +      va_start(args, fmt);
 +      strbuf_vaddf(&buf, fmt, args);
 +      va_end(args);
 +
 +      if (!expect->nr)
 +              printf("%s\n", buf.buf);
 +      else {
 +              char *colon = strchr(buf.buf, ':');
 +              if (!colon)
 +                      die("malformed output format, output lacking colon: %s", fmt);
 +              *colon = '\0';
 +              item = string_list_lookup(expect, buf.buf);
 +              *colon = ':';
 +              if (!item)
 +                      ; /* not among entries being checked */
 +              else {
 +                      if (strcmp((const char *)item->util, buf.buf)) {
 +                              printf("-%s\n", (char *)item->util);
 +                              printf("+%s\n", buf.buf);
 +                              *status = 1;
 +                      }
 +              }
 +      }
 +      strbuf_release(&buf);
 +}
 +
  int main(int argc, char **argv)
  {
        const char *prefix = "prefix/";
                "test-parse-options <options>",
                NULL
        };
 +      struct string_list expect = STRING_LIST_INIT_NODUP;
        struct option options[] = {
                OPT_BOOL(0, "yes", &boolean, "get a boolean"),
                OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
                OPT__VERBOSE(&verbose, "be verbose"),
                OPT__DRY_RUN(&dry_run, "dry run"),
                OPT__QUIET(&quiet, "be quiet"),
 +              OPT_CALLBACK(0, "expect", &expect, "string",
 +                           "expected output in the variable dump",
 +                           collect_expect),
                OPT_END(),
        };
        int i;
 +      int ret = 0;
  
        argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
  
 -      printf("boolean: %d\n", boolean);
 -      printf("integer: %d\n", integer);
 -      printf("magnitude: %lu\n", magnitude);
 -      printf("timestamp: %lu\n", timestamp);
 -      printf("string: %s\n", string ? string : "(not set)");
 -      printf("abbrev: %d\n", abbrev);
 -      printf("verbose: %d\n", verbose);
 -      printf("quiet: %s\n", quiet ? "yes" : "no");
 -      printf("dry run: %s\n", dry_run ? "yes" : "no");
 -      printf("file: %s\n", file ? file : "(not set)");
 +      if (length_cb.called) {
 +              const char *arg = length_cb.arg;
 +              int unset = length_cb.unset;
 +              show(&expect, &ret, "Callback: \"%s\", %d",
 +                   (arg ? arg : "not set"), unset);
 +      }
 +      show(&expect, &ret, "boolean: %d", boolean);
 +      show(&expect, &ret, "integer: %d", integer);
 +      show(&expect, &ret, "magnitude: %lu", magnitude);
 +      show(&expect, &ret, "timestamp: %lu", timestamp);
 +      show(&expect, &ret, "string: %s", string ? string : "(not set)");
 +      show(&expect, &ret, "abbrev: %d", abbrev);
 +      show(&expect, &ret, "verbose: %d", verbose);
 +      show(&expect, &ret, "quiet: %d", quiet);
 +      show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no");
 +      show(&expect, &ret, "file: %s", file ? file : "(not set)");
  
        for (i = 0; i < list.nr; i++)
 -              printf("list: %s\n", list.items[i].string);
 +              show(&expect, &ret, "list: %s", list.items[i].string);
  
        for (i = 0; i < argc; i++)
 -              printf("arg %02d: %s\n", i, argv[i]);
 +              show(&expect, &ret, "arg %02d: %s", i, argv[i]);
  
 -      return 0;
 +      return ret;
  }