builtin/apply: move 'p_value_known' global into 'struct apply_state'
[gitweb.git] / builtin / apply.c
index f174a42a557dffa78a386ce8795698d54830f657..e1b68d4cb75da74f2d567133534514d19cde9302 100644 (file)
@@ -26,6 +26,7 @@ struct apply_state {
        int prefix_length;
 
        /* These control what gets looked at and modified */
+       int apply; /* this is not a dry-run */
        int cached; /* apply to the index only */
        int check; /* preimage must match working tree, don't actually apply */
        int check_index; /* preimage must match the indexed version */
@@ -41,23 +42,26 @@ struct apply_state {
        int apply_in_reverse;
        int apply_with_reject;
        int apply_verbosely;
+       int no_add;
+       int threeway;
        int unidiff_zero;
+       int unsafe_paths;
+
+       /* Other non boolean parameters */
+       const char *fake_ancestor;
+       const char *patch_input_file;
+       int line_termination;
+       int p_value;
+       int p_value_known;
+       unsigned int p_context;
+
+       /* Exclude and include path parameters */
+       struct string_list limit_by_name;
+       int has_include;
 };
 
-/*
- *  --index-info shows the old and new index info for paths if available.
- */
 static int newfd = -1;
 
-static int state_p_value = 1;
-static int p_value_known;
-static int apply = 1;
-static int no_add;
-static int threeway;
-static int unsafe_paths;
-static const char *fake_ancestor;
-static int line_termination = '\n';
-static unsigned int p_context = UINT_MAX;
 static const char * const apply_usage[] = {
        N_("git apply [<options>] [<patch>...]"),
        NULL
@@ -79,7 +83,6 @@ static enum ws_ignore {
 } ws_ignore_action = ignore_ws_none;
 
 
-static const char *patch_input_file;
 static struct strbuf root = STRBUF_INIT;
 
 static void parse_whitespace_option(const char *option)
@@ -127,10 +130,11 @@ static void parse_ignorewhitespace_option(const char *option)
        die(_("unrecognized whitespace ignore option '%s'"), option);
 }
 
-static void set_default_whitespace_mode(const char *whitespace_option)
+static void set_default_whitespace_mode(struct apply_state *state,
+                                       const char *whitespace_option)
 {
        if (!whitespace_option && !apply_default_whitespace)
-               ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
+               ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error);
 }
 
 /*
@@ -870,30 +874,30 @@ static void parse_traditional_patch(struct apply_state *state,
 
        first += 4;     /* skip "--- " */
        second += 4;    /* skip "+++ " */
-       if (!p_value_known) {
+       if (!state->p_value_known) {
                int p, q;
                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;
-                       p_value_known = 1;
+                       state->p_value = p;
+                       state->p_value_known = 1;
                }
        }
        if (is_dev_null(first)) {
                patch->is_new = 1;
                patch->is_delete = 0;
-               name = find_name_traditional(second, NULL, state_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, state_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, state_p_value);
-               name = find_name_traditional(second, first_name, state_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;
@@ -912,7 +916,9 @@ static void parse_traditional_patch(struct apply_state *state,
                die(_("unable to find filename in patch at line %d"), state_linenr);
 }
 
-static int gitdiff_hdrend(const char *line, struct patch *patch)
+static int gitdiff_hdrend(struct apply_state *state,
+                         const char *line,
+                         struct patch *patch)
 {
        return -1;
 }
@@ -929,10 +935,14 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
 #define DIFF_OLD_NAME 0
 #define DIFF_NEW_NAME 1
 
-static void gitdiff_verify_name(const char *line, int isnull, char **name, int side)
+static void gitdiff_verify_name(struct apply_state *state,
+                               const char *line,
+                               int isnull,
+                               char **name,
+                               int side)
 {
        if (!*name && !isnull) {
-               *name = find_name(line, NULL, state_p_value, TERM_TAB);
+               *name = find_name(line, NULL, state->p_value, TERM_TAB);
                return;
        }
 
@@ -942,7 +952,7 @@ static void gitdiff_verify_name(const char *line, int isnull, char **name, int s
                if (isnull)
                        die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
                            *name, state_linenr);
-               another = find_name(line, NULL, state_p_value, TERM_TAB);
+               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") :
@@ -955,81 +965,105 @@ static void gitdiff_verify_name(const char *line, int isnull, char **name, int s
        }
 }
 
-static int gitdiff_oldname(const char *line, struct patch *patch)
+static int gitdiff_oldname(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
-       gitdiff_verify_name(line, patch->is_new, &patch->old_name,
+       gitdiff_verify_name(state, line,
+                           patch->is_new, &patch->old_name,
                            DIFF_OLD_NAME);
        return 0;
 }
 
-static int gitdiff_newname(const char *line, struct patch *patch)
+static int gitdiff_newname(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
-       gitdiff_verify_name(line, patch->is_delete, &patch->new_name,
+       gitdiff_verify_name(state, line,
+                           patch->is_delete, &patch->new_name,
                            DIFF_NEW_NAME);
        return 0;
 }
 
-static int gitdiff_oldmode(const char *line, struct patch *patch)
+static int gitdiff_oldmode(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->old_mode = strtoul(line, NULL, 8);
        return 0;
 }
 
-static int gitdiff_newmode(const char *line, struct patch *patch)
+static int gitdiff_newmode(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->new_mode = strtoul(line, NULL, 8);
        return 0;
 }
 
-static int gitdiff_delete(const char *line, struct patch *patch)
+static int gitdiff_delete(struct apply_state *state,
+                         const char *line,
+                         struct patch *patch)
 {
        patch->is_delete = 1;
        free(patch->old_name);
        patch->old_name = xstrdup_or_null(patch->def_name);
-       return gitdiff_oldmode(line, patch);
+       return gitdiff_oldmode(state, line, patch);
 }
 
-static int gitdiff_newfile(const char *line, struct patch *patch)
+static int gitdiff_newfile(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->is_new = 1;
        free(patch->new_name);
        patch->new_name = xstrdup_or_null(patch->def_name);
-       return gitdiff_newmode(line, patch);
+       return gitdiff_newmode(state, line, patch);
 }
 
-static int gitdiff_copysrc(const char *line, struct patch *patch)
+static int gitdiff_copysrc(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->is_copy = 1;
        free(patch->old_name);
-       patch->old_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
+       patch->old_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_copydst(const char *line, struct patch *patch)
+static int gitdiff_copydst(struct apply_state *state,
+                          const char *line,
+                          struct patch *patch)
 {
        patch->is_copy = 1;
        free(patch->new_name);
-       patch->new_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
+       patch->new_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_renamesrc(const char *line, struct patch *patch)
+static int gitdiff_renamesrc(struct apply_state *state,
+                            const char *line,
+                            struct patch *patch)
 {
        patch->is_rename = 1;
        free(patch->old_name);
-       patch->old_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
+       patch->old_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_renamedst(const char *line, struct patch *patch)
+static int gitdiff_renamedst(struct apply_state *state,
+                            const char *line,
+                            struct patch *patch)
 {
        patch->is_rename = 1;
        free(patch->new_name);
-       patch->new_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0);
+       patch->new_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
        return 0;
 }
 
-static int gitdiff_similarity(const char *line, struct patch *patch)
+static int gitdiff_similarity(struct apply_state *state,
+                             const char *line,
+                             struct patch *patch)
 {
        unsigned long val = strtoul(line, NULL, 10);
        if (val <= 100)
@@ -1037,7 +1071,9 @@ static int gitdiff_similarity(const char *line, struct patch *patch)
        return 0;
 }
 
-static int gitdiff_dissimilarity(const char *line, struct patch *patch)
+static int gitdiff_dissimilarity(struct apply_state *state,
+                                const char *line,
+                                struct patch *patch)
 {
        unsigned long val = strtoul(line, NULL, 10);
        if (val <= 100)
@@ -1045,7 +1081,9 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
        return 0;
 }
 
-static int gitdiff_index(const char *line, struct patch *patch)
+static int gitdiff_index(struct apply_state *state,
+                        const char *line,
+                        struct patch *patch)
 {
        /*
         * index line is N hexadecimal, "..", N hexadecimal,
@@ -1082,7 +1120,9 @@ static int gitdiff_index(const char *line, struct patch *patch)
  * This is normal for a diff that doesn't change anything: we'll fall through
  * into the next diff. Tell the parser to break out.
  */
-static int gitdiff_unrecognized(const char *line, struct patch *patch)
+static int gitdiff_unrecognized(struct apply_state *state,
+                               const char *line,
+                               struct patch *patch)
 {
        return -1;
 }
@@ -1091,15 +1131,17 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
  * Skip p_value leading components from "line"; as we do not accept
  * absolute paths, return NULL in that case.
  */
-static const char *skip_tree_prefix(const char *line, int llen)
+static const char *skip_tree_prefix(struct apply_state *state,
+                                   const char *line,
+                                   int llen)
 {
        int nslash;
        int i;
 
-       if (!state_p_value)
+       if (!state->p_value)
                return (llen && line[0] == '/') ? NULL : line;
 
-       nslash = state_p_value;
+       nslash = state->p_value;
        for (i = 0; i < llen; i++) {
                int ch = line[i];
                if (ch == '/' && --nslash <= 0)
@@ -1116,7 +1158,9 @@ static const char *skip_tree_prefix(const char *line, int llen)
  * creation or deletion of an empty file.  In any of these cases,
  * both sides are the same name under a/ and b/ respectively.
  */
-static char *git_header_name(const char *line, int llen)
+static char *git_header_name(struct apply_state *state,
+                            const char *line,
+                            int llen)
 {
        const char *name;
        const char *second = NULL;
@@ -1134,7 +1178,7 @@ static char *git_header_name(const char *line, int llen)
                        goto free_and_fail1;
 
                /* strip the a/b prefix including trailing slash */
-               cp = skip_tree_prefix(first.buf, first.len);
+               cp = skip_tree_prefix(state, first.buf, first.len);
                if (!cp)
                        goto free_and_fail1;
                strbuf_remove(&first, 0, cp - first.buf);
@@ -1151,7 +1195,7 @@ static char *git_header_name(const char *line, int llen)
                if (*second == '"') {
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail1;
-                       cp = skip_tree_prefix(sp.buf, sp.len);
+                       cp = skip_tree_prefix(state, sp.buf, sp.len);
                        if (!cp)
                                goto free_and_fail1;
                        /* They must match, otherwise ignore */
@@ -1162,7 +1206,7 @@ static char *git_header_name(const char *line, int llen)
                }
 
                /* unquoted second */
-               cp = skip_tree_prefix(second, line + llen - second);
+               cp = skip_tree_prefix(state, second, line + llen - second);
                if (!cp)
                        goto free_and_fail1;
                if (line + llen - cp != first.len ||
@@ -1177,7 +1221,7 @@ static char *git_header_name(const char *line, int llen)
        }
 
        /* unquoted first name */
-       name = skip_tree_prefix(line, llen);
+       name = skip_tree_prefix(state, line, llen);
        if (!name)
                return NULL;
 
@@ -1193,7 +1237,7 @@ static char *git_header_name(const char *line, int llen)
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail2;
 
-                       np = skip_tree_prefix(sp.buf, sp.len);
+                       np = skip_tree_prefix(state, sp.buf, sp.len);
                        if (!np)
                                goto free_and_fail2;
 
@@ -1237,7 +1281,7 @@ static char *git_header_name(const char *line, int llen)
                         */
                        if (!name[len + 1])
                                return NULL; /* no postimage name */
-                       second = skip_tree_prefix(name + len + 1,
+                       second = skip_tree_prefix(state, name + len + 1,
                                                  line_len - (len + 1));
                        if (!second)
                                return NULL;
@@ -1253,7 +1297,11 @@ static char *git_header_name(const char *line, int llen)
 }
 
 /* Verify that we recognize the lines following a git header */
-static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
+static int parse_git_header(struct apply_state *state,
+                           const char *line,
+                           int len,
+                           unsigned int size,
+                           struct patch *patch)
 {
        unsigned long offset;
 
@@ -1267,7 +1315,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
         * or removing or adding empty files), so we get
         * the default name from the header.
         */
-       patch->def_name = git_header_name(line, len);
+       patch->def_name = git_header_name(state, line, len);
        if (patch->def_name && root.len) {
                char *s = xstrfmt("%s%s", root.buf, patch->def_name);
                free(patch->def_name);
@@ -1280,7 +1328,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
        for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state_linenr++) {
                static const struct opentry {
                        const char *str;
-                       int (*fn)(const char *, struct patch *);
+                       int (*fn)(struct apply_state *, const char *, struct patch *);
                } optable[] = {
                        { "@@ -", gitdiff_hdrend },
                        { "--- ", gitdiff_oldname },
@@ -1310,7 +1358,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
                        int oplen = strlen(p->str);
                        if (len < oplen || memcmp(p->str, line, oplen))
                                continue;
-                       if (p->fn(line + oplen, patch) < 0)
+                       if (p->fn(state, line + oplen, patch) < 0)
                                return offset;
                        break;
                }
@@ -1480,7 +1528,7 @@ static int find_header(struct apply_state *state,
                 * or mode change, so we handle that specially
                 */
                if (!memcmp("diff --git ", line, 11)) {
-                       int git_hdr_len = parse_git_header(line, len, size, patch);
+                       int git_hdr_len = parse_git_header(state, line, len, size, patch);
                        if (git_hdr_len <= len)
                                continue;
                        if (!patch->old_name && !patch->new_name) {
@@ -1489,8 +1537,8 @@ static int find_header(struct apply_state *state,
                                               "%d leading pathname component (line %d)",
                                               "git diff header lacks filename information when removing "
                                               "%d leading pathname components (line %d)",
-                                              state_p_value),
-                                           state_p_value, state_linenr);
+                                              state->p_value),
+                                           state->p_value, state_linenr);
                                patch->old_name = xstrdup(patch->def_name);
                                patch->new_name = xstrdup(patch->def_name);
                        }
@@ -1524,7 +1572,11 @@ static int find_header(struct apply_state *state,
        return -1;
 }
 
-static void record_ws_error(unsigned result, const char *line, int len, int linenr)
+static void record_ws_error(struct apply_state *state,
+                           unsigned result,
+                           const char *line,
+                           int len,
+                           int linenr)
 {
        char *err;
 
@@ -1538,15 +1590,18 @@ static void record_ws_error(unsigned result, const char *line, int len, int line
 
        err = whitespace_error_string(result);
        fprintf(stderr, "%s:%d: %s.\n%.*s\n",
-               patch_input_file, linenr, err, len, line);
+               state->patch_input_file, linenr, err, len, line);
        free(err);
 }
 
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
+static void check_whitespace(struct apply_state *state,
+                            const char *line,
+                            int len,
+                            unsigned ws_rule)
 {
        unsigned result = ws_check(line + 1, len - 1, ws_rule);
 
-       record_ws_error(result, line + 1, len - 2, state_linenr);
+       record_ws_error(state, result, line + 1, len - 2, state_linenr);
 }
 
 /*
@@ -1601,12 +1656,12 @@ static int parse_fragment(struct apply_state *state,
                        trailing++;
                        if (!state->apply_in_reverse &&
                            ws_error_action == correct_ws_error)
-                               check_whitespace(line, len, patch->ws_rule);
+                               check_whitespace(state, line, len, patch->ws_rule);
                        break;
                case '-':
                        if (state->apply_in_reverse &&
                            ws_error_action != nowarn_ws_error)
-                               check_whitespace(line, len, patch->ws_rule);
+                               check_whitespace(state, line, len, patch->ws_rule);
                        deleted++;
                        oldlines--;
                        trailing = 0;
@@ -1614,7 +1669,7 @@ static int parse_fragment(struct apply_state *state,
                case '+':
                        if (!state->apply_in_reverse &&
                            ws_error_action != nowarn_ws_error)
-                               check_whitespace(line, len, patch->ws_rule);
+                               check_whitespace(state, line, len, patch->ws_rule);
                        added++;
                        newlines--;
                        trailing = 0;
@@ -1950,13 +2005,13 @@ static void prefix_patch(struct apply_state *state, struct patch *p)
  * include/exclude
  */
 
-static struct string_list limit_by_name;
-static int has_include;
-static void add_name_limit(const char *name, int exclude)
+static void add_name_limit(struct apply_state *state,
+                          const char *name,
+                          int exclude)
 {
        struct string_list_item *it;
 
-       it = string_list_append(&limit_by_name, name);
+       it = string_list_append(&state->limit_by_name, name);
        it->util = exclude ? NULL : (void *) 1;
 }
 
@@ -1974,8 +2029,8 @@ static int use_patch(struct apply_state *state, struct patch *p)
        }
 
        /* See if it matches any of exclude/include rule */
-       for (i = 0; i < limit_by_name.nr; i++) {
-               struct string_list_item *it = &limit_by_name.items[i];
+       for (i = 0; i < state->limit_by_name.nr; i++) {
+               struct string_list_item *it = &state->limit_by_name.items[i];
                if (!wildmatch(it->string, pathname, 0, NULL))
                        return (it->util != NULL);
        }
@@ -1985,7 +2040,7 @@ static int use_patch(struct apply_state *state, struct patch *p)
         * not used.  Otherwise, we saw bunch of exclude rules (or none)
         * and such a path is used.
         */
-       return !has_include;
+       return !state->has_include;
 }
 
 
@@ -2059,7 +2114,7 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si
                 * without metadata change.  A binary patch appears
                 * empty to us here.
                 */
-               if ((apply || state->check) &&
+               if ((state->apply || state->check) &&
                    (!patch->is_binary && !metadata_changes(patch)))
                        die(_("patch with only garbage at line %d"), state_linenr);
        }
@@ -2782,7 +2837,7 @@ static int apply_one_fragment(struct apply_state *state,
                /* Fall-through for ' ' */
                case '+':
                        /* --no-add does not add new lines */
-                       if (first == '+' && no_add)
+                       if (first == '+' && state->no_add)
                                break;
 
                        start = newlines.len;
@@ -2873,7 +2928,7 @@ static int apply_one_fragment(struct apply_state *state,
                        break;
 
                /* Am I at my context limits? */
-               if ((leading <= p_context) && (trailing <= p_context))
+               if ((leading <= state->p_context) && (trailing <= state->p_context))
                        break;
                if (match_beginning || match_end) {
                        match_beginning = match_end = 0;
@@ -2903,7 +2958,7 @@ static int apply_one_fragment(struct apply_state *state,
                    preimage.nr + applied_pos >= img->nr &&
                    (ws_rule & WS_BLANK_AT_EOF) &&
                    ws_error_action != nowarn_ws_error) {
-                       record_ws_error(WS_BLANK_AT_EOF, "+", 1,
+                       record_ws_error(state, WS_BLANK_AT_EOF, "+", 1,
                                        found_new_blank_lines_at_end);
                        if (ws_error_action == correct_ws_error) {
                                while (new_blank_lines_at_end--)
@@ -2917,7 +2972,7 @@ static int apply_one_fragment(struct apply_state *state,
                         * apply_data->apply_fragments->apply_one_fragment
                         */
                        if (ws_error_action == die_on_ws_error)
-                               apply = 0;
+                               state->apply = 0;
                }
 
                if (state->apply_verbosely && applied_pos != pos) {
@@ -3491,7 +3546,7 @@ static int apply_data(struct apply_state *state, struct patch *patch,
        if (patch->direct_to_threeway ||
            apply_fragments(state, &image, patch) < 0) {
                /* Note: with --reject, apply_fragments() returns 0 */
-               if (!threeway || try_threeway(state, &image, patch, st, ce) < 0)
+               if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0)
                        return -1;
        }
        patch->result = image.buf;
@@ -3786,7 +3841,7 @@ static int check_patch(struct apply_state *state, struct patch *patch)
            ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
                int err = check_to_create(state, new_name, ok_if_exists);
 
-               if (err && threeway) {
+               if (err && state->threeway) {
                        patch->direct_to_threeway = 1;
                } else switch (err) {
                case 0:
@@ -3827,7 +3882,7 @@ static int check_patch(struct apply_state *state, struct patch *patch)
                }
        }
 
-       if (!unsafe_paths)
+       if (!state->unsafe_paths)
                die_on_unsafe_path(patch);
 
        /*
@@ -3977,7 +4032,8 @@ static void stat_patch_list(struct patch *patch)
        print_stat_summary(stdout, files, adds, dels);
 }
 
-static void numstat_patch_list(struct patch *patch)
+static void numstat_patch_list(struct apply_state *state,
+                              struct patch *patch)
 {
        for ( ; patch; patch = patch->next) {
                const char *name;
@@ -3986,7 +4042,7 @@ static void numstat_patch_list(struct patch *patch)
                        printf("-\t-\t");
                else
                        printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
-               write_name_quoted(name, stdout, line_termination);
+               write_name_quoted(name, stdout, state->line_termination);
        }
 }
 
@@ -4425,7 +4481,7 @@ static int apply_patch(struct apply_state *state,
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
 
-       patch_input_file = filename;
+       state->patch_input_file = filename;
        read_patch_file(&buf, fd);
        offset = 0;
        while (offset < buf.len) {
@@ -4460,9 +4516,9 @@ static int apply_patch(struct apply_state *state,
                die(_("unrecognized input"));
 
        if (whitespace_error && (ws_error_action == die_on_ws_error))
-               apply = 0;
+               state->apply = 0;
 
-       state->update_index = state->check_index && apply;
+       state->update_index = state->check_index && state->apply;
        if (state->update_index && newfd < 0)
                newfd = hold_locked_index(&lock_file, 1);
 
@@ -4471,26 +4527,26 @@ static int apply_patch(struct apply_state *state,
                        die(_("unable to read index file"));
        }
 
-       if ((state->check || apply) &&
+       if ((state->check || state->apply) &&
            check_patch_list(state, list) < 0 &&
            !state->apply_with_reject)
                exit(1);
 
-       if (apply && write_out_results(state, list)) {
+       if (state->apply && write_out_results(state, list)) {
                if (state->apply_with_reject)
                        exit(1);
                /* with --3way, we still need to write the index out */
                return 1;
        }
 
-       if (fake_ancestor)
-               build_fake_ancestor(list, fake_ancestor);
+       if (state->fake_ancestor)
+               build_fake_ancestor(list, state->fake_ancestor);
 
        if (state->diffstat)
                stat_patch_list(list);
 
        if (state->numstat)
-               numstat_patch_list(list);
+               numstat_patch_list(state, list);
 
        if (state->summary)
                summary_patch_list(list);
@@ -4511,23 +4567,27 @@ static void git_apply_config(void)
 static int option_parse_exclude(const struct option *opt,
                                const char *arg, int unset)
 {
-       add_name_limit(arg, 1);
+       struct apply_state *state = opt->value;
+       add_name_limit(state, arg, 1);
        return 0;
 }
 
 static int option_parse_include(const struct option *opt,
                                const char *arg, int unset)
 {
-       add_name_limit(arg, 0);
-       has_include = 1;
+       struct apply_state *state = opt->value;
+       add_name_limit(state, arg, 0);
+       state->has_include = 1;
        return 0;
 }
 
 static int option_parse_p(const struct option *opt,
-                         const char *arg, int unset)
+                         const char *arg,
+                         int unset)
 {
-       state_p_value = atoi(arg);
-       p_value_known = 1;
+       struct apply_state *state = opt->value;
+       state->p_value = atoi(arg);
+       state->p_value_known = 1;
        return 0;
 }
 
@@ -4565,6 +4625,10 @@ 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;
+       state->apply = 1;
+       state->line_termination = '\n';
+       state->p_value = 1;
+       state->p_context = UINT_MAX;
 
        git_apply_config();
        if (apply_default_whitespace)
@@ -4575,7 +4639,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix)
 
 static void clear_apply_state(struct apply_state *state)
 {
-       /* empty for now */
+       string_list_clear(&state->limit_by_name, 0);
 }
 
 int cmd_apply(int argc, const char **argv, const char *prefix)
@@ -4591,16 +4655,16 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
        const char *whitespace_option = NULL;
 
        struct option builtin_apply_options[] = {
-               { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
+               { OPTION_CALLBACK, 0, "exclude", &state, N_("path"),
                        N_("don't apply changes matching the given path"),
                        0, option_parse_exclude },
-               { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
+               { OPTION_CALLBACK, 0, "include", &state, N_("path"),
                        N_("apply changes matching the given path"),
                        0, option_parse_include },
-               { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
+               { OPTION_CALLBACK, 'p', NULL, &state, N_("num"),
                        N_("remove <num> leading slashes from traditional diff paths"),
                        0, option_parse_p },
-               OPT_BOOL(0, "no-add", &no_add,
+               OPT_BOOL(0, "no-add", &state.no_add,
                        N_("ignore additions made by the patch")),
                OPT_BOOL(0, "stat", &state.diffstat,
                        N_("instead of applying the patch, output diffstat for the input")),
@@ -4616,18 +4680,18 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        N_("make sure the patch is applicable to the current index")),
                OPT_BOOL(0, "cached", &state.cached,
                        N_("apply a patch without touching the working tree")),
-               OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
+               OPT_BOOL(0, "unsafe-paths", &state.unsafe_paths,
                        N_("accept a patch that touches outside the working area")),
                OPT_BOOL(0, "apply", &force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
-               OPT_BOOL('3', "3way", &threeway,
+               OPT_BOOL('3', "3way", &state.threeway,
                         N_( "attempt three-way merge if a patch does not apply")),
-               OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
+               OPT_FILENAME(0, "build-fake-ancestor", &state.fake_ancestor,
                        N_("build a temporary index based on embedded index information")),
                /* Think twice before adding "--nul" synonym to this */
-               OPT_SET_INT('z', NULL, &line_termination,
+               OPT_SET_INT('z', NULL, &state.line_termination,
                        N_("paths are separated with NUL character"), '\0'),
-               OPT_INTEGER('C', NULL, &p_context,
+               OPT_INTEGER('C', NULL, &state.p_context,
                                N_("ensure at least <n> lines of context match")),
                { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
                        N_("detect new or modified lines that have whitespace errors"),
@@ -4664,19 +4728,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, state.prefix, builtin_apply_options,
                        apply_usage, 0);
 
-       if (state.apply_with_reject && threeway)
+       if (state.apply_with_reject && state.threeway)
                die("--reject and --3way cannot be used together.");
-       if (state.cached && threeway)
+       if (state.cached && state.threeway)
                die("--cached and --3way cannot be used together.");
-       if (threeway) {
+       if (state.threeway) {
                if (is_not_gitdir)
                        die(_("--3way outside a repository"));
                state.check_index = 1;
        }
        if (state.apply_with_reject)
-               apply = state.apply_verbosely = 1;
-       if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || fake_ancestor))
-               apply = 0;
+               state.apply = state.apply_verbosely = 1;
+       if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || state.fake_ancestor))
+               state.apply = 0;
        if (state.check_index && is_not_gitdir)
                die(_("--index outside a repository"));
        if (state.cached) {
@@ -4685,7 +4749,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                state.check_index = 1;
        }
        if (state.check_index)
-               unsafe_paths = 0;
+               state.unsafe_paths = 0;
 
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
@@ -4704,11 +4768,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                if (fd < 0)
                        die_errno(_("can't open patch '%s'"), arg);
                read_stdin = 0;
-               set_default_whitespace_mode(whitespace_option);
+               set_default_whitespace_mode(&state, whitespace_option);
                errs |= apply_patch(&state, fd, arg, options);
                close(fd);
        }
-       set_default_whitespace_mode(whitespace_option);
+       set_default_whitespace_mode(&state, whitespace_option);
        if (read_stdin)
                errs |= apply_patch(&state, 0, "<stdin>", options);
        if (whitespace_error) {
@@ -4726,7 +4790,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                               "%d lines add whitespace errors.",
                               whitespace_error),
                            whitespace_error);
-               if (applied_after_fixing_ws && apply)
+               if (applied_after_fixing_ws && state.apply)
                        warning("%d line%s applied after"
                                " fixing whitespace errors.",
                                applied_after_fixing_ws,