builtin/apply: move 'diffstat' global into 'struct apply_state'
[gitweb.git] / builtin / apply.c
index 8e4da2e1bdaf02590289f54750a9602281f4a365..d940125ee20d02e52a16065832d3d681d3e44f61 100644 (file)
 #include "ll-merge.h"
 #include "rerere.h"
 
+struct apply_state {
+       const char *prefix;
+       int prefix_length;
+
+       /* These control what gets looked at and modified */
+       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 */
+
+       /* These boolean parameters control how the apply is done */
+       int allow_overlap;
+       int apply_in_reverse;
+       int apply_with_reject;
+       int apply_verbosely;
+       int unidiff_zero;
+};
+
 /*
- *  --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 state_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;
@@ -78,8 +82,6 @@ static enum ws_ignore {
 
 static const char *patch_input_file;
 static struct strbuf root = STRBUF_INIT;
-static int read_stdin = 1;
-static int options;
 
 static void parse_whitespace_option(const char *option)
 {
@@ -144,7 +146,7 @@ static int max_change, max_len;
  * 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;
+static int state_linenr = 1;
 
 /*
  * This represents one "hunk" from a patch, starting with
@@ -750,7 +752,7 @@ 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;
@@ -763,17 +765,17 @@ static int guess_p_value(const char *nameline)
        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);
@@ -860,7 +862,10 @@ static int has_epoch_timestamp(const char *nameline)
  * 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;
 
@@ -868,28 +873,28 @@ static void parse_traditional_patch(const char *first, const char *second, struc
        second += 4;    /* skip "+++ " */
        if (!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;
+                       state_p_value = p;
                        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(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(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(first, NULL, state_p_value);
+               name = find_name_traditional(second, first_name, state_p_value);
                free(first_name);
                if (has_epoch_timestamp(first)) {
                        patch->is_new = 1;
@@ -905,7 +910,7 @@ static void parse_traditional_patch(const char *first, const char *second, struc
                }
        }
        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)
@@ -925,43 +930,43 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
 #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(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(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(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)
 {
-       patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
-                                             DIFF_OLD_NAME);
+       gitdiff_verify_name(line, patch->is_new, &patch->old_name,
+                           DIFF_OLD_NAME);
        return 0;
 }
 
 static int gitdiff_newname(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(line, patch->is_delete, &patch->new_name,
+                           DIFF_NEW_NAME);
        return 0;
 }
 
@@ -997,7 +1002,7 @@ static int gitdiff_copysrc(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(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
        return 0;
 }
 
@@ -1005,7 +1010,7 @@ static int gitdiff_copydst(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(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
        return 0;
 }
 
@@ -1013,7 +1018,7 @@ static int gitdiff_renamesrc(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(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
        return 0;
 }
 
@@ -1021,7 +1026,7 @@ static int gitdiff_renamedst(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(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
        return 0;
 }
 
@@ -1092,10 +1097,10 @@ static const char *skip_tree_prefix(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)
@@ -1272,8 +1277,8 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
 
        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 *);
@@ -1431,7 +1436,11 @@ static int parse_fragment_header(const char *line, int len, struct fragment *fra
        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;
 
@@ -1440,7 +1449,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
        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);
@@ -1461,7 +1470,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
                        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)
@@ -1481,14 +1490,14 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
                                               "%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;
@@ -1508,9 +1517,9 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
                        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;
@@ -1538,7 +1547,7 @@ static void check_whitespace(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(result, line + 1, len - 2, state_linenr);
 }
 
 /*
@@ -1547,8 +1556,11 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule)
  * 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;
@@ -1568,11 +1580,11 @@ static int parse_fragment(const char *line, unsigned long size,
        /* 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);
@@ -1588,12 +1600,12 @@ static int parse_fragment(const char *line, unsigned long size,
                        if (!deleted && !added)
                                leading++;
                        trailing++;
-                       if (!apply_in_reverse &&
+                       if (!state->apply_in_reverse &&
                            ws_error_action == correct_ws_error)
                                check_whitespace(line, len, patch->ws_rule);
                        break;
                case '-':
-                       if (apply_in_reverse &&
+                       if (state->apply_in_reverse &&
                            ws_error_action != nowarn_ws_error)
                                check_whitespace(line, len, patch->ws_rule);
                        deleted++;
@@ -1601,7 +1613,7 @@ static int parse_fragment(const char *line, unsigned long size,
                        trailing = 0;
                        break;
                case '+':
-                       if (!apply_in_reverse &&
+                       if (!state->apply_in_reverse &&
                            ws_error_action != nowarn_ws_error)
                                check_whitespace(line, len, patch->ws_rule);
                        added++;
@@ -1657,7 +1669,10 @@ static int parse_fragment(const char *line, unsigned long size,
  * 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;
@@ -1668,10 +1683,10 @@ static int parse_single_patch(const char *line, unsigned long size, struct patch
                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;
@@ -1799,13 +1814,13 @@ static struct fragment *parse_binary_hunk(char **buf_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++;
@@ -1859,7 +1874,7 @@ static struct fragment *parse_binary_hunk(char **buf_p,
        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;
 }
 
@@ -1892,7 +1907,7 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
        forward = parse_binary_hunk(&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;
@@ -1915,21 +1930,21 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
        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);
 }
 
 /*
@@ -1946,16 +1961,16 @@ static void add_name_limit(const char *name, int exclude)
        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;
        }
 
@@ -1982,25 +1997,27 @@ static int use_patch(struct patch *p)
  * 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";
@@ -2010,7 +2027,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                if (llen == sizeof(git_binary) - 1 &&
                    !memcmp(git_binary, buffer + hd, llen)) {
                        int used;
-                       linenr++;
+                       state_linenr++;
                        used = parse_binary(buffer + hd + llen,
                                            size - hd - llen, patch);
                        if (used < 0)
@@ -2031,7 +2048,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                                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;
@@ -2043,9 +2060,9 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                 * without metadata change.  A binary patch appears
                 * empty to us here.
                 */
-               if ((apply || check) &&
+               if ((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;
@@ -2194,17 +2211,17 @@ static void update_pre_post_images(struct image *preimage,
        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 &&
@@ -2223,11 +2240,11 @@ static void update_pre_post_images(struct image *preimage,
                }
 
                /* 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++;
        }
 
@@ -2242,6 +2259,74 @@ static void update_pre_post_images(struct image *preimage,
        postimage->nr -= reduced;
 }
 
+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 image *img,
                          struct image *preimage,
                          struct image *postimage,
@@ -2331,61 +2416,9 @@ static int match_fragment(struct image *img,
         * 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;
-               }
-
-               /*
-                * 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_ignore_action == ignore_ws_change)
+               return line_by_line_fuzzy_match(img, preimage, postimage,
+                                               try, try_lno, preimage_limit);
 
        if (ws_error_action != correct_ws_error)
                return 0;
@@ -2594,7 +2627,8 @@ static void remove_last_line(struct image *img)
  * 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)
@@ -2659,7 +2693,7 @@ static void update_image(struct image *img,
        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;
@@ -2670,7 +2704,8 @@ static void update_image(struct image *img,
  * 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)
 {
@@ -2715,7 +2750,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
-               if (apply_in_reverse) {
+               if (state->apply_in_reverse) {
                        if (first == '-')
                                first = '+';
                        else if (first == '+')
@@ -2771,7 +2806,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        /* 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;
@@ -2812,7 +2847,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
         * 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.
@@ -2820,7 +2855,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
         * 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;
@@ -2886,9 +2921,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                                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).",
@@ -2906,9 +2941,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        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);
        }
@@ -2922,7 +2957,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        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;
@@ -2935,7 +2972,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
                             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'",
@@ -2968,7 +3005,9 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
  * 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];
@@ -3029,7 +3068,7 @@ static int apply_binary(struct image *img, struct patch *patch)
                 * 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);
 
@@ -3043,7 +3082,7 @@ static int apply_binary(struct image *img, struct patch *patch)
        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;
@@ -3052,13 +3091,13 @@ static int apply_fragments(struct image *img, struct patch *patch)
        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;
                }
@@ -3218,13 +3257,14 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)
 
 #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) {
@@ -3250,7 +3290,8 @@ static int load_patch_target(struct strbuf *buf,
  * 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)
 {
@@ -3268,7 +3309,7 @@ static int load_preimage(struct image *image,
                /* 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;
@@ -3327,7 +3368,9 @@ static int three_way_merge(struct image *image,
  * 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;
@@ -3354,7 +3397,7 @@ static int load_current(struct image *image, struct patch *patch)
        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)
@@ -3364,8 +3407,11 @@ static int load_current(struct image *image, struct patch *patch)
        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;
@@ -3391,7 +3437,7 @@ static int try_threeway(struct image *image, struct patch *patch,
        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;
        }
@@ -3401,11 +3447,11 @@ static int try_threeway(struct image *image, struct patch *patch,
 
        /* 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);
        }
@@ -3435,17 +3481,18 @@ static int try_threeway(struct image *image, struct patch *patch,
        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 (!threeway || try_threeway(state, &image, patch, st, ce) < 0)
                        return -1;
        }
        patch->result = image.buf;
@@ -3470,7 +3517,10 @@ static int apply_data(struct patch *patch, struct stat *st, const struct cache_e
  * 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;
@@ -3487,13 +3537,13 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                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)
@@ -3505,9 +3555,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                        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)
@@ -3515,7 +3565,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                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)
@@ -3543,15 +3593,17 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
 #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)) {
@@ -3623,7 +3675,7 @@ static void prepare_symlink_changes(struct patch *patch)
        }
 }
 
-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;
@@ -3644,7 +3696,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
                        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);
@@ -3659,14 +3711,14 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
        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;
@@ -3693,7 +3745,7 @@ static void die_on_unsafe_path(struct patch *patch)
  * 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;
@@ -3706,7 +3758,7 @@ static int check_patch(struct patch *patch)
 
        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;
@@ -3733,7 +3785,7 @@ static int check_patch(struct patch *patch)
 
        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) {
                        patch->direct_to_threeway = 1;
@@ -3788,27 +3840,27 @@ static int check_patch(struct patch *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);
        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;
@@ -4039,27 +4091,31 @@ static void patch_stats(struct patch *patch)
        }
 }
 
-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);
@@ -4074,7 +4130,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
                    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);
@@ -4126,9 +4182,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
  * 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;
@@ -4169,13 +4229,14 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
        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);
@@ -4196,7 +4257,7 @@ static void add_conflicted_stages_file(struct patch *patch)
        }
 }
 
-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;
@@ -4205,25 +4266,27 @@ static void create_file(struct patch *patch)
 
        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;
        }
        /*
@@ -4231,12 +4294,12 @@ static void write_out_one_result(struct patch *patch, int phase)
         * 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];
@@ -4251,7 +4314,7 @@ static int write_out_one_reject(struct patch *patch)
        }
 
        if (!cnt) {
-               if (apply_verbosely)
+               if (state->apply_verbosely)
                        say_patch_name(stderr,
                                       _("Applied patch %s cleanly."), patch);
                return 0;
@@ -4307,7 +4370,7 @@ static int write_out_one_reject(struct patch *patch)
        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;
@@ -4320,9 +4383,9 @@ static int write_out_results(struct patch *list)
                        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);
@@ -4353,7 +4416,10 @@ 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 */
@@ -4370,20 +4436,20 @@ static int apply_patch(int fd, const char *filename, int options)
                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)) {
+               if (use_patch(state, patch)) {
                        patch_stats(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++;
@@ -4397,22 +4463,22 @@ static int apply_patch(int fd, const char *filename, int options)
        if (whitespace_error && (ws_error_action == die_on_ws_error))
                apply = 0;
 
-       update_index = check_index && apply;
-       if (update_index && newfd < 0)
+       state->update_index = state->check_index && apply;
+       if (state->update_index && newfd < 0)
                newfd = hold_locked_index(&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 || apply) &&
+           check_patch_list(state, list) < 0 &&
+           !state->apply_with_reject)
                exit(1);
 
-       if (apply && write_out_results(list)) {
-               if (apply_with_reject)
+       if (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;
@@ -4421,7 +4487,7 @@ static int apply_patch(int fd, const char *filename, int options)
        if (fake_ancestor)
                build_fake_ancestor(list, fake_ancestor);
 
-       if (diffstat)
+       if (state->diffstat)
                stat_patch_list(list);
 
        if (numstat)
@@ -4461,7 +4527,7 @@ static int option_parse_include(const struct option *opt,
 static int option_parse_p(const struct option *opt,
                          const char *arg, int unset)
 {
-       p_value = atoi(arg);
+       state_p_value = atoi(arg);
        p_value_known = 1;
        return 0;
 }
@@ -4495,12 +4561,33 @@ static int option_parse_directory(const struct option *opt,
        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)
+{
+       memset(state, 0, sizeof(*state));
+       state->prefix = prefix;
+       state->prefix_length = state->prefix ? strlen(state->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);
+}
+
+static void clear_apply_state(struct apply_state *state)
+{
+       /* empty for now */
+}
+
+int cmd_apply(int argc, const char **argv, const char *prefix)
 {
        int i;
        int errs = 0;
        int is_not_gitdir = !startup_info->have_repository;
        int force_apply = 0;
+       int options = 0;
+       int read_stdin = 1;
+       struct apply_state state;
 
        const char *whitespace_option = NULL;
 
@@ -4516,7 +4603,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                        0, option_parse_p },
                OPT_BOOL(0, "no-add", &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"),
@@ -4524,11 +4611,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                        N_("show number of added and deleted lines in decimal notation")),
                OPT_BOOL(0, "summary", &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,
                        N_("accept a patch that touches outside the working area")),
@@ -4552,15 +4639,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, 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),
@@ -4573,38 +4660,32 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                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);
 
-       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)
+       if (state.apply_with_reject && threeway)
                die("--reject and --3way cannot be used together.");
-       if (cached && threeway)
+       if (state.cached && threeway)
                die("--cached and --3way cannot be used together.");
        if (threeway) {
                if (is_not_gitdir)
                        die(_("--3way outside a repository"));
-               check_index = 1;
+               state.check_index = 1;
        }
-       if (apply_with_reject)
-               apply = apply_verbosely = 1;
-       if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
+       if (state.apply_with_reject)
+               apply = state.apply_verbosely = 1;
+       if (!force_apply && (state.diffstat || numstat || summary || state.check || fake_ancestor))
                apply = 0;
-       if (check_index && is_not_gitdir)
+       if (state.check_index && is_not_gitdir)
                die(_("--index outside a repository"));
-       if (cached) {
+       if (state.cached) {
                if (is_not_gitdir)
                        die(_("--cached outside a repository"));
-               check_index = 1;
+               state.check_index = 1;
        }
-       if (check_index)
+       if (state.check_index)
                unsafe_paths = 0;
 
        for (i = 0; i < argc; i++) {
@@ -4612,23 +4693,25 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       errs |= apply_patch(0, "<stdin>", options);
+                       errs |= apply_patch(&state, 0, "<stdin>", options);
                        read_stdin = 0;
                        continue;
-               } else if (0 < prefix_length)
-                       arg = prefix_filename(prefix, prefix_length, arg);
+               } 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(whitespace_option);
-               errs |= apply_patch(fd, arg, options);
+               errs |= apply_patch(&state, fd, arg, options);
                close(fd);
        }
        set_default_whitespace_mode(whitespace_option);
        if (read_stdin)
-               errs |= apply_patch(0, "<stdin>", options);
+               errs |= apply_patch(&state, 0, "<stdin>", options);
        if (whitespace_error) {
                if (squelch_whitespace_errors &&
                    squelch_whitespace_errors < whitespace_error) {
@@ -4656,10 +4739,12 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                                whitespace_error);
        }
 
-       if (update_index) {
+       if (state.update_index) {
                if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
 
+       clear_apply_state(&state);
+
        return !!errs;
 }