builtin/apply: move 'diffstat' global into 'struct apply_state'
[gitweb.git] / builtin / apply.c
index 7bab466fe2dd6e8d7735cd79eaa19befa96f37e6..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 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)
 {
@@ -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,8 +873,8 @@ 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) {
                        state_p_value = p;
@@ -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;
 
@@ -1508,7 +1517,7 @@ 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;
                state_linenr += 2;
                return offset;
@@ -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;
@@ -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;
@@ -1669,7 +1684,7 @@ static int parse_single_patch(const char *line, unsigned long size, struct patch
 
                fragment = xcalloc(1, sizeof(*fragment));
                fragment->linenr = state_linenr;
-               len = parse_fragment(line, size, patch, fragment);
+               len = parse_fragment(state, line, size, patch, fragment);
                if (len <= 0)
                        die(_("corrupt patch at line %d"), state_linenr);
                fragment->patch = line;
@@ -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";
@@ -2043,7 +2060,7 @@ 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"), state_linenr);
        }
@@ -2610,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)
@@ -2675,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;
@@ -2686,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)
 {
@@ -2731,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 == '+')
@@ -2787,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;
@@ -2828,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.
@@ -2836,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;
@@ -2902,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).",
@@ -2922,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);
        }
@@ -2938,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;
@@ -2951,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'",
@@ -2984,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];
@@ -3045,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);
 
@@ -3059,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;
@@ -3068,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;
                }
@@ -3234,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) {
@@ -3266,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)
 {
@@ -3284,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;
@@ -3343,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;
@@ -3370,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)
@@ -3380,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;
@@ -3407,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;
        }
@@ -3417,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);
        }
@@ -3451,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;
@@ -3486,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;
@@ -3503,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)
@@ -3521,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)
@@ -3531,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)
@@ -3559,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)) {
@@ -3639,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;
@@ -3660,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);
@@ -3675,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;
@@ -3709,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;
@@ -3722,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;
@@ -3749,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;
@@ -3804,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;
@@ -4055,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);
@@ -4090,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);
@@ -4142,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;
@@ -4185,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);
@@ -4212,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;
@@ -4221,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;
        }
        /*
@@ -4247,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];
@@ -4267,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;
@@ -4323,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;
@@ -4336,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);
@@ -4369,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 */
@@ -4386,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++;
@@ -4413,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;
@@ -4437,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)
@@ -4511,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;
 
@@ -4532,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"),
@@ -4540,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")),
@@ -4568,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),
@@ -4589,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++) {
@@ -4628,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) {
@@ -4672,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;
 }