diff options
Diffstat (limited to 'builtin/apply.c')
-rw-r--r-- | builtin/apply.c | 1501 |
1 files changed, 849 insertions, 652 deletions
diff --git a/builtin/apply.c b/builtin/apply.c index 0769b09287..ecb7f1b258 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -21,133 +21,161 @@ #include "ll-merge.h" #include "rerere.h" -/* - * --check turns on checking that the working tree matches the - * files that are being modified, but doesn't apply the patch - * --stat does just a diffstat, and doesn't actually apply - * --numstat does numeric diffstat, and doesn't actually apply - * --index-info shows the old and new index info for paths if available. - * --index updates the cache as well. - * --cached updates only the cache without ever touching the working tree. - */ -static const char *prefix; -static int prefix_length = -1; -static int newfd = -1; - -static int unidiff_zero; -static int p_value = 1; -static int p_value_known; -static int check_index; -static int update_index; -static int cached; -static int diffstat; -static int numstat; -static int summary; -static int check; -static int apply = 1; -static int apply_in_reverse; -static int apply_with_reject; -static int apply_verbosely; -static int allow_overlap; -static int no_add; -static int threeway; -static int unsafe_paths; -static const char *fake_ancestor; -static int line_termination = '\n'; -static unsigned int p_context = UINT_MAX; -static const char * const apply_usage[] = { - N_("git apply [<options>] [<patch>...]"), - NULL -}; - -static enum ws_error_action { +enum ws_error_action { nowarn_ws_error, warn_on_ws_error, die_on_ws_error, correct_ws_error -} ws_error_action = warn_on_ws_error; -static int whitespace_error; -static int squelch_whitespace_errors = 5; -static int applied_after_fixing_ws; +}; + -static enum ws_ignore { +enum ws_ignore { ignore_ws_none, ignore_ws_change -} ws_ignore_action = ignore_ws_none; +}; + +/* + * We need to keep track of how symlinks in the preimage are + * manipulated by the patches. A patch to add a/b/c where a/b + * is a symlink should not be allowed to affect the directory + * the symlink points at, but if the same patch removes a/b, + * it is perfectly fine, as the patch removes a/b to make room + * to create a directory a/b so that a/b/c can be created. + * + * See also "struct string_list symlink_changes" in "struct + * apply_state". + */ +#define SYMLINK_GOES_AWAY 01 +#define SYMLINK_IN_RESULT 02 + +struct apply_state { + const char *prefix; + int prefix_length; + + /* These are lock_file related */ + struct lock_file *lock_file; + int newfd; + + /* These control what gets looked at and modified */ + int apply; /* this is not a dry-run */ + int cached; /* apply to the index only */ + int check; /* preimage must match working tree, don't actually apply */ + int check_index; /* preimage must match the indexed version */ + int update_index; /* check_index && apply */ + + /* These control cosmetic aspect of the output */ + int diffstat; /* just show a diffstat, and don't actually apply */ + int numstat; /* just show a numeric diffstat, and don't actually apply */ + int summary; /* just report creation, deletion, etc, and don't actually apply */ + + /* These boolean parameters control how the apply is done */ + int allow_overlap; + int apply_in_reverse; + int apply_with_reject; + int apply_verbosely; + int no_add; + int threeway; + int unidiff_zero; + int unsafe_paths; + + /* Other non boolean parameters */ + const char *fake_ancestor; + const char *patch_input_file; + int line_termination; + struct strbuf root; + int p_value; + int p_value_known; + unsigned int p_context; + + /* Exclude and include path parameters */ + struct string_list limit_by_name; + int has_include; + + /* Various "current state" */ + int linenr; /* current line number */ + struct string_list symlink_changes; /* we have to track symlinks */ + /* + * For "diff-stat" like behaviour, we keep track of the biggest change + * we've seen, and the longest filename. That allows us to do simple + * scaling. + */ + int max_change; + int max_len; + + /* + * Records filenames that have been touched, in order to handle + * the case where more than one patches touch the same file. + */ + struct string_list fn_table; + + /* These control whitespace errors */ + enum ws_error_action ws_error_action; + enum ws_ignore ws_ignore_action; + const char *whitespace_option; + int whitespace_error; + int squelch_whitespace_errors; + int applied_after_fixing_ws; +}; -static const char *patch_input_file; -static const char *root; -static int root_len; -static int read_stdin = 1; -static int options; +static const char * const apply_usage[] = { + N_("git apply [<options>] [<patch>...]"), + NULL +}; -static void parse_whitespace_option(const char *option) +static void parse_whitespace_option(struct apply_state *state, const char *option) { if (!option) { - ws_error_action = warn_on_ws_error; + state->ws_error_action = warn_on_ws_error; return; } if (!strcmp(option, "warn")) { - ws_error_action = warn_on_ws_error; + state->ws_error_action = warn_on_ws_error; return; } if (!strcmp(option, "nowarn")) { - ws_error_action = nowarn_ws_error; + state->ws_error_action = nowarn_ws_error; return; } if (!strcmp(option, "error")) { - ws_error_action = die_on_ws_error; + state->ws_error_action = die_on_ws_error; return; } if (!strcmp(option, "error-all")) { - ws_error_action = die_on_ws_error; - squelch_whitespace_errors = 0; + state->ws_error_action = die_on_ws_error; + state->squelch_whitespace_errors = 0; return; } if (!strcmp(option, "strip") || !strcmp(option, "fix")) { - ws_error_action = correct_ws_error; + state->ws_error_action = correct_ws_error; return; } die(_("unrecognized whitespace option '%s'"), option); } -static void parse_ignorewhitespace_option(const char *option) +static void parse_ignorewhitespace_option(struct apply_state *state, + const char *option) { if (!option || !strcmp(option, "no") || !strcmp(option, "false") || !strcmp(option, "never") || !strcmp(option, "none")) { - ws_ignore_action = ignore_ws_none; + state->ws_ignore_action = ignore_ws_none; return; } if (!strcmp(option, "change")) { - ws_ignore_action = ignore_ws_change; + state->ws_ignore_action = ignore_ws_change; return; } die(_("unrecognized whitespace ignore option '%s'"), option); } -static void set_default_whitespace_mode(const char *whitespace_option) +static void set_default_whitespace_mode(struct apply_state *state) { - if (!whitespace_option && !apply_default_whitespace) - ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error); + if (!state->whitespace_option && !apply_default_whitespace) + state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); } /* - * For "diff-stat" like behaviour, we keep track of the biggest change - * we've seen, and the longest filename. That allows us to do simple - * scaling. - */ -static int max_change, max_len; - -/* - * Various "current state", notably line numbers and what - * file (and how) we're patching right now.. The "is_xxxx" - * things are flags, where -1 means "don't know yet". - */ -static int linenr = 1; - -/* * This represents one "hunk" from a patch, starting with * "@@ -oldpos,oldlines +newpos,newlines @@" marker. The * patch text is pointed at by patch, and its byte length @@ -208,7 +236,7 @@ struct patch { struct patch *next; /* three-way fallback result */ - unsigned char threeway_stage[3][20]; + struct object_id threeway_stage[3]; }; static void free_fragment_list(struct fragment *list) @@ -266,13 +294,6 @@ struct image { struct line *line; }; -/* - * Records filenames that have been touched, in order to handle - * the case where more than one patches touch the same file. - */ - -static struct string_list fn_table; - static uint32_t hash_line(const char *cp, size_t len) { size_t i; @@ -443,7 +464,7 @@ static int is_dev_null(const char *str) #define TERM_SPACE 1 #define TERM_TAB 2 -static int name_terminate(const char *name, int namelen, int c, int terminate) +static int name_terminate(int c, int terminate) { if (c == ' ' && !(terminate & TERM_SPACE)) return 0; @@ -470,7 +491,10 @@ static char *squash_slash(char *name) return name; } -static char *find_name_gnu(const char *line, const char *def, int p_value) +static char *find_name_gnu(struct apply_state *state, + const char *line, + const char *def, + int p_value) { struct strbuf name = STRBUF_INIT; char *cp; @@ -494,8 +518,8 @@ static char *find_name_gnu(const char *line, const char *def, int p_value) } strbuf_remove(&name, 0, cp - name.buf); - if (root) - strbuf_insert(&name, 0, root, root_len); + if (state->root.len) + strbuf_insert(&name, 0, state->root.buf, state->root.len); return squash_slash(strbuf_detach(&name, NULL)); } @@ -658,8 +682,12 @@ static size_t diff_timestamp_len(const char *line, size_t len) return line + len - end; } -static char *find_name_common(const char *line, const char *def, - int p_value, const char *end, int terminate) +static char *find_name_common(struct apply_state *state, + const char *line, + const char *def, + int p_value, + const char *end, + int terminate) { int len; const char *start = NULL; @@ -672,7 +700,7 @@ static char *find_name_common(const char *line, const char *def, if (!end && isspace(c)) { if (c == '\n') break; - if (name_terminate(start, line-start, c, terminate)) + if (name_terminate(c, terminate)) break; } line++; @@ -697,35 +725,39 @@ static char *find_name_common(const char *line, const char *def, return squash_slash(xstrdup(def)); } - if (root) { - char *ret = xmalloc(root_len + len + 1); - strcpy(ret, root); - memcpy(ret + root_len, start, len); - ret[root_len + len] = '\0'; + if (state->root.len) { + char *ret = xstrfmt("%s%.*s", state->root.buf, len, start); return squash_slash(ret); } return squash_slash(xmemdupz(start, len)); } -static char *find_name(const char *line, char *def, int p_value, int terminate) +static char *find_name(struct apply_state *state, + const char *line, + char *def, + int p_value, + int terminate) { if (*line == '"') { - char *name = find_name_gnu(line, def, p_value); + char *name = find_name_gnu(state, line, def, p_value); if (name) return name; } - return find_name_common(line, def, p_value, NULL, terminate); + return find_name_common(state, line, def, p_value, NULL, terminate); } -static char *find_name_traditional(const char *line, char *def, int p_value) +static char *find_name_traditional(struct apply_state *state, + const char *line, + char *def, + int p_value) { size_t len; size_t date_len; if (*line == '"') { - char *name = find_name_gnu(line, def, p_value); + char *name = find_name_gnu(state, line, def, p_value); if (name) return name; } @@ -733,10 +765,10 @@ static char *find_name_traditional(const char *line, char *def, int p_value) len = strchrnul(line, '\n') - line; date_len = diff_timestamp_len(line, len); if (!date_len) - return find_name_common(line, def, p_value, NULL, TERM_TAB); + return find_name_common(state, line, def, p_value, NULL, TERM_TAB); len -= date_len; - return find_name_common(line, def, p_value, line + len, 0); + return find_name_common(state, line, def, p_value, line + len, 0); } static int count_slashes(const char *cp) @@ -754,30 +786,30 @@ static int count_slashes(const char *cp) * Given the string after "--- " or "+++ ", guess the appropriate * p_value for the given patch. */ -static int guess_p_value(const char *nameline) +static int guess_p_value(struct apply_state *state, const char *nameline) { char *name, *cp; int val = -1; if (is_dev_null(nameline)) return -1; - name = find_name_traditional(nameline, NULL, 0); + name = find_name_traditional(state, nameline, NULL, 0); if (!name) return -1; cp = strchr(name, '/'); if (!cp) val = 0; - else if (prefix) { + else if (state->prefix) { /* * Does it begin with "a/$our-prefix" and such? Then this is * very likely to apply to our directory. */ - if (!strncmp(name, prefix, prefix_length)) - val = count_slashes(prefix); + if (!strncmp(name, state->prefix, state->prefix_length)) + val = count_slashes(state->prefix); else { cp++; - if (!strncmp(cp, prefix, prefix_length)) - val = count_slashes(prefix) + 1; + if (!strncmp(cp, state->prefix, state->prefix_length)) + val = count_slashes(state->prefix) + 1; } } free(name); @@ -785,7 +817,7 @@ static int guess_p_value(const char *nameline) } /* - * Does the ---/+++ line has the POSIX timestamp after the last HT? + * Does the ---/+++ line have the POSIX timestamp after the last HT? * GNU diff puts epoch there to signal a creation/deletion event. Is * this such a timestamp? */ @@ -864,36 +896,39 @@ 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; first += 4; /* skip "--- " */ second += 4; /* skip "+++ " */ - if (!p_value_known) { + if (!state->p_value_known) { int p, q; - p = guess_p_value(first); - q = guess_p_value(second); + p = guess_p_value(state, first); + q = guess_p_value(state, second); if (p < 0) p = q; if (0 <= p && p == q) { - p_value = p; - p_value_known = 1; + state->p_value = p; + state->p_value_known = 1; } } if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; - name = find_name_traditional(second, NULL, p_value); + name = find_name_traditional(state, second, NULL, state->p_value); patch->new_name = name; } else if (is_dev_null(second)) { patch->is_new = 0; patch->is_delete = 1; - name = find_name_traditional(first, NULL, p_value); + name = find_name_traditional(state, first, NULL, state->p_value); patch->old_name = name; } else { char *first_name; - first_name = find_name_traditional(first, NULL, p_value); - name = find_name_traditional(second, first_name, p_value); + first_name = find_name_traditional(state, first, NULL, state->p_value); + name = find_name_traditional(state, second, first_name, state->p_value); free(first_name); if (has_epoch_timestamp(first)) { patch->is_new = 1; @@ -909,10 +944,12 @@ 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) +static int gitdiff_hdrend(struct apply_state *state, + const char *line, + struct patch *patch) { return -1; } @@ -929,116 +966,135 @@ 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(struct apply_state *state, + const char *line, + int isnull, + char **name, + int side) { - if (!orig_name && !isnull) - return find_name(line, NULL, p_value, TERM_TAB); + if (!*name && !isnull) { + *name = find_name(state, line, NULL, state->p_value, TERM_TAB); + return; + } - if (orig_name) { - int len; - const char *name; + if (*name) { + int len = strlen(*name); char *another; - name = orig_name; - len = strlen(name); if (isnull) - die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr); - another = find_name(line, NULL, p_value, TERM_TAB); - if (!another || memcmp(another, name, len + 1)) + die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), + *name, state->linenr); + another = find_name(state, line, NULL, state->p_value, TERM_TAB); + if (!another || memcmp(another, *name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : - _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr); + _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr); free(another); - return orig_name; - } - else { + } else { /* expect "/dev/null" */ if (memcmp("/dev/null", line, 9) || line[9] != '\n') - die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr); - return NULL; + die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); } } -static int gitdiff_oldname(const char *line, struct patch *patch) +static int gitdiff_oldname(struct apply_state *state, + const char *line, + struct patch *patch) { - char *orig = patch->old_name; - patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, - DIFF_OLD_NAME); - if (orig != patch->old_name) - free(orig); + 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) { - char *orig = patch->new_name; - patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, - DIFF_NEW_NAME); - if (orig != patch->new_name) - free(orig); + gitdiff_verify_name(state, line, + patch->is_delete, &patch->new_name, + DIFF_NEW_NAME); return 0; } -static int gitdiff_oldmode(const char *line, struct patch *patch) +static int gitdiff_oldmode(struct apply_state *state, + const char *line, + struct patch *patch) { patch->old_mode = strtoul(line, NULL, 8); return 0; } -static int gitdiff_newmode(const char *line, struct patch *patch) +static int gitdiff_newmode(struct apply_state *state, + const char *line, + struct patch *patch) { patch->new_mode = strtoul(line, NULL, 8); return 0; } -static int gitdiff_delete(const char *line, struct patch *patch) +static int gitdiff_delete(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_delete = 1; free(patch->old_name); patch->old_name = xstrdup_or_null(patch->def_name); - return gitdiff_oldmode(line, patch); + return gitdiff_oldmode(state, line, patch); } -static int gitdiff_newfile(const char *line, struct patch *patch) +static int gitdiff_newfile(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_new = 1; free(patch->new_name); patch->new_name = xstrdup_or_null(patch->def_name); - return gitdiff_newmode(line, patch); + return gitdiff_newmode(state, line, patch); } -static int gitdiff_copysrc(const char *line, struct patch *patch) +static int gitdiff_copysrc(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_copy = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_copydst(const char *line, struct patch *patch) +static int gitdiff_copydst(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_copy = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_renamesrc(const char *line, struct patch *patch) +static int gitdiff_renamesrc(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_rename = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_renamedst(const char *line, struct patch *patch) +static int gitdiff_renamedst(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_rename = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_similarity(const char *line, struct patch *patch) +static int gitdiff_similarity(struct apply_state *state, + const char *line, + struct patch *patch) { unsigned long val = strtoul(line, NULL, 10); if (val <= 100) @@ -1046,7 +1102,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) @@ -1054,7 +1112,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, @@ -1091,7 +1151,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; } @@ -1100,15 +1162,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 (!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) @@ -1125,7 +1189,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; @@ -1143,7 +1209,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); @@ -1160,7 +1226,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 */ @@ -1171,7 +1237,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 || @@ -1186,7 +1252,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; @@ -1202,7 +1268,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; @@ -1246,7 +1312,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; @@ -1262,7 +1328,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; @@ -1276,20 +1346,20 @@ 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); - if (patch->def_name && root) { - char *s = xstrfmt("%s%s", root, patch->def_name); + patch->def_name = git_header_name(state, line, len); + if (patch->def_name && state->root.len) { + char *s = xstrfmt("%s%s", state->root.buf, patch->def_name); free(patch->def_name); patch->def_name = s; } line += len; size -= len; - linenr++; - for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) { + state->linenr++; + for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) { static const struct opentry { const char *str; - int (*fn)(const char *, struct patch *); + int (*fn)(struct apply_state *, const char *, struct patch *); } optable[] = { { "@@ -", gitdiff_hdrend }, { "--- ", gitdiff_oldname }, @@ -1319,7 +1389,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; } @@ -1444,7 +1514,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; @@ -1453,7 +1527,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); @@ -1474,7 +1548,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) @@ -1485,7 +1559,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc * 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) { @@ -1494,14 +1568,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; @@ -1521,37 +1595,44 @@ 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; } -static void record_ws_error(unsigned result, const char *line, int len, int linenr) +static void record_ws_error(struct apply_state *state, + unsigned result, + const char *line, + int len, + int linenr) { char *err; if (!result) return; - whitespace_error++; - if (squelch_whitespace_errors && - squelch_whitespace_errors < whitespace_error) + state->whitespace_error++; + if (state->squelch_whitespace_errors && + state->squelch_whitespace_errors < state->whitespace_error) return; err = whitespace_error_string(result); fprintf(stderr, "%s:%d: %s.\n%.*s\n", - patch_input_file, linenr, err, len, line); + state->patch_input_file, linenr, err, len, line); free(err); } -static void check_whitespace(const char *line, int len, unsigned ws_rule) +static void check_whitespace(struct apply_state *state, + const char *line, + int len, + unsigned ws_rule) { unsigned result = ws_check(line + 1, len - 1, ws_rule); - record_ws_error(result, line + 1, len - 2, linenr); + record_ws_error(state, result, line + 1, len - 2, state->linenr); } /* @@ -1560,8 +1641,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; @@ -1581,11 +1665,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); @@ -1601,22 +1685,22 @@ static int parse_fragment(const char *line, unsigned long size, if (!deleted && !added) leading++; trailing++; - if (!apply_in_reverse && - ws_error_action == correct_ws_error) - check_whitespace(line, len, patch->ws_rule); + if (!state->apply_in_reverse && + state->ws_error_action == correct_ws_error) + check_whitespace(state, line, len, patch->ws_rule); break; case '-': - if (apply_in_reverse && - ws_error_action != nowarn_ws_error) - check_whitespace(line, len, patch->ws_rule); + if (state->apply_in_reverse && + state->ws_error_action != nowarn_ws_error) + check_whitespace(state, line, len, patch->ws_rule); deleted++; oldlines--; trailing = 0; break; case '+': - if (!apply_in_reverse && - ws_error_action != nowarn_ws_error) - check_whitespace(line, len, patch->ws_rule); + if (!state->apply_in_reverse && + state->ws_error_action != nowarn_ws_error) + check_whitespace(state, line, len, patch->ws_rule); added++; newlines--; trailing = 0; @@ -1638,6 +1722,9 @@ static int parse_fragment(const char *line, unsigned long size, } if (oldlines || newlines) return -1; + if (!deleted && !added) + return -1; + fragment->leading = leading; fragment->trailing = trailing; @@ -1667,7 +1754,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; @@ -1678,10 +1768,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; @@ -1767,7 +1857,8 @@ static char *inflate_it(const void *data, unsigned long size, * points at an allocated memory that the caller must free, so * it is marked as "->free_patch = 1". */ -static struct fragment *parse_binary_hunk(char **buf_p, +static struct fragment *parse_binary_hunk(struct apply_state *state, + char **buf_p, unsigned long *sz_p, int *status_p, int *used_p) @@ -1809,13 +1900,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++; @@ -1869,11 +1960,19 @@ 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; } -static int parse_binary(char *buffer, unsigned long size, struct patch *patch) +/* + * Returns: + * -1 in case of error, + * the length of the parsed binary patch otherwise + */ +static int parse_binary(struct apply_state *state, + char *buffer, + unsigned long size, + struct patch *patch) { /* * We have read "GIT binary patch\n"; what follows is a line @@ -1894,15 +1993,15 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) int status; int used, used_1; - forward = parse_binary_hunk(&buffer, &size, &status, &used); + forward = parse_binary_hunk(state, &buffer, &size, &status, &used); if (!forward && !status) /* there has to be one hunk (forward hunk) */ - return error(_("unrecognized binary patch at line %d"), linenr-1); + return error(_("unrecognized binary patch at line %d"), state->linenr-1); if (status) /* otherwise we already gave an error message */ return status; - reverse = parse_binary_hunk(&buffer, &size, &status, &used_1); + reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1); if (reverse) used += used_1; else if (status) { @@ -1920,53 +2019,53 @@ 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); } /* * 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; } -static int use_patch(struct patch *p) +static int use_patch(struct apply_state *state, struct patch *p) { const char *pathname = p->new_name ? p->new_name : p->old_name; int i; /* Paths outside are not touched regardless of "--include" */ - if (0 < prefix_length) { + if (0 < state->prefix_length) { int pathlen = strlen(pathname); - if (pathlen <= prefix_length || - memcmp(prefix, pathname, prefix_length)) + if (pathlen <= state->prefix_length || + memcmp(state->prefix, pathname, state->prefix_length)) return 0; } /* See if it matches any of exclude/include rule */ - for (i = 0; i < limit_by_name.nr; i++) { - struct string_list_item *it = &limit_by_name.items[i]; + for (i = 0; i < state->limit_by_name.nr; i++) { + struct string_list_item *it = &state->limit_by_name.items[i]; if (!wildmatch(it->string, pathname, 0, NULL)) return (it->util != NULL); } @@ -1976,7 +2075,7 @@ static int use_patch(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; } @@ -1987,25 +2086,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"; @@ -2015,9 +2116,11 @@ 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++; - used = parse_binary(buffer + hd + llen, + state->linenr++; + used = parse_binary(state, buffer + hd + llen, size - hd - llen, patch); + if (used < 0) + return -1; if (used) patchsize = used + llen; else @@ -2034,7 +2137,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; @@ -2046,9 +2149,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 ((state->apply || state->check) && (!patch->is_binary && !metadata_changes(patch))) - die(_("patch with only garbage at line %d"), linenr); + die(_("patch with only garbage at line %d"), state->linenr); } return offset + hdrsize + patchsize; @@ -2086,7 +2189,7 @@ static const char pluses[] = static const char minuses[]= "----------------------------------------------------------------------"; -static void show_stats(struct patch *patch) +static void show_stats(struct apply_state *state, struct patch *patch) { struct strbuf qname = STRBUF_INIT; char *cp = patch->new_name ? patch->new_name : patch->old_name; @@ -2097,7 +2200,7 @@ static void show_stats(struct patch *patch) /* * "scale" the filename */ - max = max_len; + max = state->max_len; if (max > 50) max = 50; @@ -2120,13 +2223,13 @@ static void show_stats(struct patch *patch) /* * scale the add/delete */ - max = max + max_change > 70 ? 70 - max : max_change; + max = max + state->max_change > 70 ? 70 - max : state->max_change; add = patch->lines_added; del = patch->lines_deleted; - if (max_change > 0) { - int total = ((add + del) * max + max_change / 2) / max_change; - add = (add * max + max_change / 2) / max_change; + if (state->max_change > 0) { + int total = ((add + del) * max + state->max_change / 2) / state->max_change; + add = (add * max + state->max_change / 2) / state->max_change; del = total - add; } printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted, @@ -2197,17 +2300,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 && @@ -2226,11 +2329,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++; } @@ -2245,7 +2348,76 @@ static void update_pre_post_images(struct image *preimage, postimage->nr -= reduced; } -static int match_fragment(struct image *img, +static int line_by_line_fuzzy_match(struct image *img, + struct image *preimage, + struct image *postimage, + unsigned long try, + int try_lno, + int preimage_limit) +{ + int i; + size_t imgoff = 0; + size_t preoff = 0; + size_t postlen = postimage->len; + size_t extra_chars; + char *buf; + char *preimage_eof; + char *preimage_end; + struct strbuf fixed; + char *fixed_buf; + size_t fixed_len; + + for (i = 0; i < preimage_limit; i++) { + size_t prelen = preimage->line[i].len; + size_t imglen = img->line[try_lno+i].len; + + if (!fuzzy_matchlines(img->buf + try + imgoff, imglen, + preimage->buf + preoff, prelen)) + return 0; + if (preimage->line[i].flag & LINE_COMMON) + postlen += imglen - prelen; + imgoff += imglen; + preoff += prelen; + } + + /* + * Ok, the preimage matches with whitespace fuzz. + * + * imgoff now holds the true length of the target that + * matches the preimage before the end of the file. + * + * Count the number of characters in the preimage that fall + * beyond the end of the file and make sure that all of them + * are whitespace characters. (This can only happen if + * we are removing blank lines at the end of the file.) + */ + buf = preimage_eof = preimage->buf + preoff; + for ( ; i < preimage->nr; i++) + preoff += preimage->line[i].len; + preimage_end = preimage->buf + preoff; + for ( ; buf < preimage_end; buf++) + if (!isspace(*buf)) + return 0; + + /* + * Update the preimage and the common postimage context + * lines to use the same whitespace as the target. + * If whitespace is missing in the target (i.e. + * if the preimage extends beyond the end of the file), + * use the whitespace from the preimage. + */ + extra_chars = preimage_end - preimage_eof; + strbuf_init(&fixed, imgoff + extra_chars); + strbuf_add(&fixed, img->buf + try, imgoff); + strbuf_add(&fixed, preimage_eof, extra_chars); + fixed_buf = strbuf_detach(&fixed, &fixed_len); + update_pre_post_images(preimage, postimage, + fixed_buf, fixed_len, postlen); + return 1; +} + +static int match_fragment(struct apply_state *state, + struct image *img, struct image *preimage, struct image *postimage, unsigned long try, @@ -2266,7 +2438,7 @@ static int match_fragment(struct image *img, preimage_limit = preimage->nr; if (match_end && (preimage->nr + try_lno != img->nr)) return 0; - } else if (ws_error_action == correct_ws_error && + } else if (state->ws_error_action == correct_ws_error && (ws_rule & WS_BLANK_AT_EOF)) { /* * This hunk extends beyond the end of img, and we are @@ -2334,63 +2506,11 @@ 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; - } + if (state->ws_ignore_action == ignore_ws_change) + return line_by_line_fuzzy_match(img, preimage, postimage, + try, try_lno, preimage_limit); - /* - * Ok, the preimage matches with whitespace fuzz. - * - * imgoff now holds the true length of the target that - * matches the preimage before the end of the file. - * - * Count the number of characters in the preimage that fall - * beyond the end of the file and make sure that all of them - * are whitespace characters. (This can only happen if - * we are removing blank lines at the end of the file.) - */ - buf = preimage_eof = preimage->buf + preoff; - for ( ; i < preimage->nr; i++) - preoff += preimage->line[i].len; - preimage_end = preimage->buf + preoff; - for ( ; buf < preimage_end; buf++) - if (!isspace(*buf)) - return 0; - - /* - * Update the preimage and the common postimage context - * lines to use the same whitespace as the target. - * If whitespace is missing in the target (i.e. - * if the preimage extends beyond the end of the file), - * use the whitespace from the preimage. - */ - extra_chars = preimage_end - preimage_eof; - strbuf_init(&fixed, imgoff + extra_chars); - strbuf_add(&fixed, img->buf + try, imgoff); - strbuf_add(&fixed, preimage_eof, extra_chars); - fixed_buf = strbuf_detach(&fixed, &fixed_len); - update_pre_post_images(preimage, postimage, - fixed_buf, fixed_len, postlen); - return 1; - } - - if (ws_error_action != correct_ws_error) + if (state->ws_error_action != correct_ws_error) return 0; /* @@ -2502,7 +2622,8 @@ static int match_fragment(struct image *img, return 0; } -static int find_pos(struct image *img, +static int find_pos(struct apply_state *state, + struct image *img, struct image *preimage, struct image *postimage, int line, @@ -2546,7 +2667,7 @@ static int find_pos(struct image *img, try_lno = line; for (i = 0; ; i++) { - if (match_fragment(img, preimage, postimage, + if (match_fragment(state, img, preimage, postimage, try, try_lno, ws_rule, match_beginning, match_end)) return try_lno; @@ -2597,7 +2718,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) @@ -2633,7 +2755,7 @@ static void update_image(struct image *img, insert_count = postimage->len; /* Adjust the contents */ - result = xmalloc(img->len + insert_count - remove_count + 1); + result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1)); memcpy(result, img->buf, applied_at); memcpy(result + applied_at, postimage->buf, postimage->len); memcpy(result + applied_at + postimage->len, @@ -2662,7 +2784,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; @@ -2673,7 +2795,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) { @@ -2718,7 +2841,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 == '+') @@ -2751,17 +2874,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, /* Fall-through for ' ' */ case '+': /* --no-add does not add new lines */ - if (first == '+' && no_add) + if (first == '+' && state->no_add) break; start = newlines.len; if (first != '+' || - !whitespace_error || - ws_error_action != correct_ws_error) { + !state->whitespace_error || + state->ws_error_action != correct_ws_error) { strbuf_add(&newlines, patch + 1, plen); } else { - ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws); + ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws); } add_line_info(&postimage, newlines.buf + start, newlines.len - start, (first == '+' ? 0 : LINE_COMMON)); @@ -2774,7 +2897,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; @@ -2815,7 +2938,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. @@ -2823,7 +2946,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; @@ -2835,14 +2958,14 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, for (;;) { - applied_pos = find_pos(img, &preimage, &postimage, pos, + applied_pos = find_pos(state, img, &preimage, &postimage, pos, ws_rule, match_beginning, match_end); if (applied_pos >= 0) break; /* Am I at my context limits? */ - if ((leading <= p_context) && (trailing <= p_context)) + if ((leading <= state->p_context) && (trailing <= state->p_context)) break; if (match_beginning || match_end) { match_beginning = match_end = 0; @@ -2871,10 +2994,10 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, if (new_blank_lines_at_end && preimage.nr + applied_pos >= img->nr && (ws_rule & WS_BLANK_AT_EOF) && - ws_error_action != nowarn_ws_error) { - record_ws_error(WS_BLANK_AT_EOF, "+", 1, + state->ws_error_action != nowarn_ws_error) { + record_ws_error(state, WS_BLANK_AT_EOF, "+", 1, found_new_blank_lines_at_end); - if (ws_error_action == correct_ws_error) { + if (state->ws_error_action == correct_ws_error) { while (new_blank_lines_at_end--) remove_last_line(&postimage); } @@ -2885,13 +3008,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, * apply_patch->check_patch_list->check_patch-> * apply_data->apply_fragments->apply_one_fragment */ - if (ws_error_action == die_on_ws_error) - apply = 0; + if (state->ws_error_action == die_on_ws_error) + state->apply = 0; } - if (apply_verbosely && applied_pos != pos) { + if (state->apply_verbosely && applied_pos != pos) { int offset = applied_pos - pos; - if (apply_in_reverse) + if (state->apply_in_reverse) offset = 0 - offset; fprintf_ln(stderr, Q_("Hunk #%d succeeded at %d (offset %d line).", @@ -2909,9 +3032,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); } @@ -2925,7 +3048,9 @@ out: return (applied_pos < 0); } -static int apply_binary_fragment(struct image *img, struct patch *patch) +static int apply_binary_fragment(struct apply_state *state, + struct image *img, + struct patch *patch) { struct fragment *fragment = patch->fragments; unsigned long len; @@ -2938,7 +3063,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'", @@ -2971,7 +3096,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]; @@ -3032,7 +3159,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); @@ -3046,7 +3173,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; @@ -3055,13 +3182,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; } @@ -3096,14 +3223,14 @@ static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf return read_blob_object(buf, ce->sha1, ce->ce_mode); } -static struct patch *in_fn_table(const char *name) +static struct patch *in_fn_table(struct apply_state *state, const char *name) { struct string_list_item *item; if (name == NULL) return NULL; - item = string_list_lookup(&fn_table, name); + item = string_list_lookup(&state->fn_table, name); if (item != NULL) return (struct patch *)item->util; @@ -3135,7 +3262,7 @@ static int was_deleted(struct patch *patch) return patch == PATH_WAS_DELETED; } -static void add_to_fn_table(struct patch *patch) +static void add_to_fn_table(struct apply_state *state, struct patch *patch) { struct string_list_item *item; @@ -3145,7 +3272,7 @@ static void add_to_fn_table(struct patch *patch) * file creations and copies */ if (patch->new_name != NULL) { - item = string_list_insert(&fn_table, patch->new_name); + item = string_list_insert(&state->fn_table, patch->new_name); item->util = patch; } @@ -3154,12 +3281,12 @@ static void add_to_fn_table(struct patch *patch) * later chunks shouldn't patch old names */ if ((patch->new_name == NULL) || (patch->is_rename)) { - item = string_list_insert(&fn_table, patch->old_name); + item = string_list_insert(&state->fn_table, patch->old_name); item->util = PATH_WAS_DELETED; } } -static void prepare_fn_table(struct patch *patch) +static void prepare_fn_table(struct apply_state *state, struct patch *patch) { /* * store information about incoming file deletion @@ -3167,7 +3294,7 @@ static void prepare_fn_table(struct patch *patch) while (patch) { if ((patch->new_name == NULL) || (patch->is_rename)) { struct string_list_item *item; - item = string_list_insert(&fn_table, patch->old_name); + item = string_list_insert(&state->fn_table, patch->old_name); item->util = PATH_TO_BE_DELETED; } patch = patch->next; @@ -3188,7 +3315,9 @@ static int checkout_target(struct index_state *istate, return 0; } -static struct patch *previous_patch(struct patch *patch, int *gone) +static struct patch *previous_patch(struct apply_state *state, + struct patch *patch, + int *gone) { struct patch *previous; @@ -3196,7 +3325,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone) if (patch->is_copy || patch->is_rename) return NULL; /* "git" patches do not depend on the order */ - previous = in_fn_table(patch->old_name); + previous = in_fn_table(state, patch->old_name); if (!previous) return NULL; @@ -3221,13 +3350,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) { @@ -3253,7 +3383,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) { @@ -3263,7 +3394,7 @@ static int load_preimage(struct image *image, struct patch *previous; int status; - previous = previous_patch(patch, &status); + previous = previous_patch(state, patch, &status); if (status) return error(_("path %s has been renamed/deleted"), patch->old_name); @@ -3271,7 +3402,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; @@ -3330,7 +3461,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; @@ -3357,7 +3490,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) @@ -3367,8 +3500,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; @@ -3394,7 +3530,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; } @@ -3404,11 +3540,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); } @@ -3426,11 +3562,11 @@ static int try_threeway(struct image *image, struct patch *patch, if (status) { patch->conflicted_threeway = 1; if (patch->is_new) - hashclr(patch->threeway_stage[0]); + oidclr(&patch->threeway_stage[0]); else - hashcpy(patch->threeway_stage[0], pre_sha1); - hashcpy(patch->threeway_stage[1], our_sha1); - hashcpy(patch->threeway_stage[2], post_sha1); + hashcpy(patch->threeway_stage[0].hash, pre_sha1); + hashcpy(patch->threeway_stage[1].hash, our_sha1); + hashcpy(patch->threeway_stage[2].hash, post_sha1); fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name); } else { fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name); @@ -3438,22 +3574,23 @@ 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 (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0) return -1; } patch->result = image.buf; patch->resultsize = image.len; - add_to_fn_table(patch); + add_to_fn_table(state, patch); free(image.line_allocated); if (0 < patch->is_delete && patch->resultsize) @@ -3473,7 +3610,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; @@ -3484,19 +3624,19 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return 0; assert(patch->is_new <= 0); - previous = previous_patch(patch, &status); + previous = previous_patch(state, patch, &status); if (status) return error(_("path %s has been renamed/deleted"), old_name); if (previous) { st_mode = previous->new_mode; - } else if (!cached) { + } else if (!state->cached) { stat_ret = lstat(old_name, st); if (stat_ret && errno != ENOENT) return error(_("%s: %s"), old_name, strerror(errno)); } - if (check_index && !previous) { + if (state->check_index && !previous) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { if (patch->is_new < 0) @@ -3508,9 +3648,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) @@ -3518,7 +3658,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) @@ -3546,15 +3686,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)) { @@ -3577,56 +3719,46 @@ static int check_to_create(const char *new_name, int ok_if_exists) return 0; } -/* - * We need to keep track of how symlinks in the preimage are - * manipulated by the patches. A patch to add a/b/c where a/b - * is a symlink should not be allowed to affect the directory - * the symlink points at, but if the same patch removes a/b, - * it is perfectly fine, as the patch removes a/b to make room - * to create a directory a/b so that a/b/c can be created. - */ -static struct string_list symlink_changes; -#define SYMLINK_GOES_AWAY 01 -#define SYMLINK_IN_RESULT 02 - -static uintptr_t register_symlink_changes(const char *path, uintptr_t what) +static uintptr_t register_symlink_changes(struct apply_state *state, + const char *path, + uintptr_t what) { struct string_list_item *ent; - ent = string_list_lookup(&symlink_changes, path); + ent = string_list_lookup(&state->symlink_changes, path); if (!ent) { - ent = string_list_insert(&symlink_changes, path); + ent = string_list_insert(&state->symlink_changes, path); ent->util = (void *)0; } ent->util = (void *)(what | ((uintptr_t)ent->util)); return (uintptr_t)ent->util; } -static uintptr_t check_symlink_changes(const char *path) +static uintptr_t check_symlink_changes(struct apply_state *state, const char *path) { struct string_list_item *ent; - ent = string_list_lookup(&symlink_changes, path); + ent = string_list_lookup(&state->symlink_changes, path); if (!ent) return 0; return (uintptr_t)ent->util; } -static void prepare_symlink_changes(struct patch *patch) +static void prepare_symlink_changes(struct apply_state *state, struct patch *patch) { for ( ; patch; patch = patch->next) { if ((patch->old_name && S_ISLNK(patch->old_mode)) && (patch->is_rename || patch->is_delete)) /* the symlink at patch->old_name is removed */ - register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY); + register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY); if (patch->new_name && S_ISLNK(patch->new_mode)) /* the symlink at patch->new_name is created or remains */ - register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT); + register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT); } } -static int path_is_beyond_symlink_1(struct strbuf *name) +static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name) { do { unsigned int change; @@ -3636,7 +3768,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name) if (!name->len) break; name->buf[name->len] = '\0'; - change = check_symlink_changes(name->buf); + change = check_symlink_changes(state, name->buf); if (change & SYMLINK_IN_RESULT) return 1; if (change & SYMLINK_GOES_AWAY) @@ -3647,7 +3779,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); @@ -3662,14 +3794,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; @@ -3696,7 +3828,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; @@ -3709,7 +3841,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; @@ -3728,7 +3860,7 @@ static int check_patch(struct patch *patch) * B and rename from A to B is handled the same way by asking * was_deleted(). */ - if ((tpatch = in_fn_table(new_name)) && + if ((tpatch = in_fn_table(state, new_name)) && (was_deleted(tpatch) || to_be_deleted(tpatch))) ok_if_exists = 1; else @@ -3736,9 +3868,9 @@ 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) { + if (err && state->threeway) { patch->direct_to_threeway = 1; } else switch (err) { case 0: @@ -3779,7 +3911,7 @@ static int check_patch(struct patch *patch) } } - if (!unsafe_paths) + if (!state->unsafe_paths) die_on_unsafe_path(patch); /* @@ -3791,27 +3923,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); + prepare_symlink_changes(state, patch); + prepare_fn_table(state, patch); while (patch) { - if (apply_verbosely) + if (state->apply_verbosely) say_patch_name(stderr, _("Checking patch %s..."), patch); - err |= check_patch(patch); + err |= check_patch(state, patch); patch = patch->next; } return err; @@ -3915,7 +4047,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename) discard_index(&result); } -static void stat_patch_list(struct patch *patch) +static void stat_patch_list(struct apply_state *state, struct patch *patch) { int files, adds, dels; @@ -3923,13 +4055,14 @@ static void stat_patch_list(struct patch *patch) files++; adds += patch->lines_added; dels += patch->lines_deleted; - show_stats(patch); + show_stats(state, patch); } print_stat_summary(stdout, files, adds, dels); } -static void numstat_patch_list(struct patch *patch) +static void numstat_patch_list(struct apply_state *state, + struct patch *patch) { for ( ; patch; patch = patch->next) { const char *name; @@ -3938,7 +4071,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); } } @@ -4020,49 +4153,53 @@ static void summary_patch_list(struct patch *patch) } } -static void patch_stats(struct patch *patch) +static void patch_stats(struct apply_state *state, struct patch *patch) { int lines = patch->lines_added + patch->lines_deleted; - if (lines > max_change) - max_change = lines; + if (lines > state->max_change) + state->max_change = lines; if (patch->old_name) { int len = quote_c_style(patch->old_name, NULL, NULL, 0); if (!len) len = strlen(patch->old_name); - if (len > max_len) - max_len = len; + if (len > state->max_len) + state->max_len = len; } if (patch->new_name) { int len = quote_c_style(patch->new_name, NULL, NULL, 0); if (!len) len = strlen(patch->new_name); - if (len > max_len) - max_len = len; + if (len > state->max_len) + state->max_len = len; } } -static void remove_file(struct patch *patch, int rmdir_empty) +static void remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) { - if (update_index) { + if (state->update_index) { if (remove_file_from_cache(patch->old_name) < 0) die(_("unable to remove %s from index"), patch->old_name); } - if (!cached) { + if (!state->cached) { if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { remove_path(patch->old_name); } } } -static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size) +static void add_index_file(struct apply_state *state, + const char *path, + unsigned mode, + void *buf, + unsigned long size) { struct stat st; struct cache_entry *ce; int namelen = strlen(path); unsigned ce_size = cache_entry_size(namelen); - if (!update_index) + if (!state->update_index) return; ce = xcalloc(1, ce_size); @@ -4077,7 +4214,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); @@ -4129,9 +4266,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; @@ -4172,13 +4313,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); @@ -4186,20 +4328,20 @@ static void add_conflicted_stages_file(struct patch *patch) remove_file_from_cache(patch->new_name); for (stage = 1; stage < 4; stage++) { - if (is_null_sha1(patch->threeway_stage[stage - 1])) + if (is_null_oid(&patch->threeway_stage[stage - 1])) continue; ce = xcalloc(1, ce_size); memcpy(ce->name, patch->new_name, namelen); ce->ce_mode = create_ce_mode(mode); ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = namelen; - hashcpy(ce->sha1, patch->threeway_stage[stage - 1]); + hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash); if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) die(_("unable to add cache entry for %s"), patch->new_name); } } -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; @@ -4208,25 +4350,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; } /* @@ -4234,12 +4378,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]; @@ -4254,7 +4398,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; @@ -4310,7 +4454,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; @@ -4323,9 +4467,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); @@ -4356,14 +4500,17 @@ static struct lock_file lock_file; #define INACCURATE_EOF (1<<0) #define RECOUNT (1<<1) -static int apply_patch(int fd, const char *filename, int options) +static int apply_patch(struct apply_state *state, + int fd, + const char *filename, + int options) { size_t offset; struct strbuf buf = STRBUF_INIT; /* owns the patch text */ struct patch *list = NULL, **listp = &list; int skipped_patch = 0; - patch_input_file = filename; + state->patch_input_file = filename; read_patch_file(&buf, fd); offset = 0; while (offset < buf.len) { @@ -4373,17 +4520,21 @@ 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); - if (nr < 0) + nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); + if (nr < 0) { + free_patch(patch); break; - if (apply_in_reverse) + } + if (state->apply_in_reverse) reverse_patches(patch); - if (use_patch(patch)) { - patch_stats(patch); + if (use_patch(state, patch)) { + patch_stats(state, patch); *listp = patch; listp = &patch->next; } else { + if (state->apply_verbosely) + say_patch_name(stderr, _("Skipped patch '%s'."), patch); free_patch(patch); skipped_patch++; } @@ -4393,45 +4544,45 @@ static int apply_patch(int fd, const char *filename, int options) if (!list && !skipped_patch) die(_("unrecognized input")); - if (whitespace_error && (ws_error_action == die_on_ws_error)) - apply = 0; + if (state->whitespace_error && (state->ws_error_action == die_on_ws_error)) + state->apply = 0; - update_index = check_index && apply; - if (update_index && newfd < 0) - newfd = hold_locked_index(&lock_file, 1); + state->update_index = state->check_index && state->apply; + if (state->update_index && state->newfd < 0) + state->newfd = hold_locked_index(state->lock_file, 1); - if (check_index) { + if (state->check_index) { if (read_cache() < 0) die(_("unable to read index file")); } - if ((check || apply) && - check_patch_list(list) < 0 && - !apply_with_reject) + if ((state->check || state->apply) && + check_patch_list(state, list) < 0 && + !state->apply_with_reject) exit(1); - if (apply && write_out_results(list)) { - if (apply_with_reject) + if (state->apply && write_out_results(state, list)) { + if (state->apply_with_reject) exit(1); /* with --3way, we still need to write the index out */ return 1; } - if (fake_ancestor) - build_fake_ancestor(list, fake_ancestor); + if (state->fake_ancestor) + build_fake_ancestor(list, state->fake_ancestor); - if (diffstat) - stat_patch_list(list); + if (state->diffstat) + stat_patch_list(state, list); - if (numstat) - numstat_patch_list(list); + if (state->numstat) + numstat_patch_list(state, list); - if (summary) + if (state->summary) summary_patch_list(list); free_patch_list(list); strbuf_release(&buf); - string_list_clear(&fn_table, 0); + string_list_clear(&state->fn_table, 0); return 0; } @@ -4445,235 +4596,281 @@ 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) -{ - p_value = atoi(arg); - p_value_known = 1; - return 0; -} - -static int option_parse_z(const struct option *opt, - const char *arg, int unset) + const char *arg, + int unset) { - if (unset) - line_termination = '\n'; - else - line_termination = 0; + struct apply_state *state = opt->value; + state->p_value = atoi(arg); + state->p_value_known = 1; return 0; } static int option_parse_space_change(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset) { + struct apply_state *state = opt->value; if (unset) - ws_ignore_action = ignore_ws_none; + state->ws_ignore_action = ignore_ws_none; else - ws_ignore_action = ignore_ws_change; + state->ws_ignore_action = ignore_ws_change; return 0; } static int option_parse_whitespace(const struct option *opt, const char *arg, int unset) { - const char **whitespace_option = opt->value; - - *whitespace_option = arg; - parse_whitespace_option(arg); + struct apply_state *state = opt->value; + state->whitespace_option = arg; + parse_whitespace_option(state, arg); return 0; } static int option_parse_directory(const struct option *opt, const char *arg, int unset) { - root_len = strlen(arg); - if (root_len && arg[root_len - 1] != '/') { - char *new_root; - root = new_root = xmalloc(root_len + 2); - strcpy(new_root, arg); - strcpy(new_root + root_len++, "/"); - } else - root = arg; + struct apply_state *state = opt->value; + strbuf_reset(&state->root); + strbuf_addstr(&state->root, arg); + strbuf_complete(&state->root, '/'); return 0; } -int cmd_apply(int argc, const char **argv, const char *prefix_) +static void init_apply_state(struct apply_state *state, + const char *prefix, + struct lock_file *lock_file) +{ + memset(state, 0, sizeof(*state)); + state->prefix = prefix; + state->prefix_length = state->prefix ? strlen(state->prefix) : 0; + state->lock_file = lock_file; + state->newfd = -1; + state->apply = 1; + state->line_termination = '\n'; + state->p_value = 1; + state->p_context = UINT_MAX; + state->squelch_whitespace_errors = 5; + state->ws_error_action = warn_on_ws_error; + state->ws_ignore_action = ignore_ws_none; + state->linenr = 1; + strbuf_init(&state->root, 0); + + git_apply_config(); + if (apply_default_whitespace) + parse_whitespace_option(state, apply_default_whitespace); + if (apply_default_ignorewhitespace) + parse_ignorewhitespace_option(state, apply_default_ignorewhitespace); +} + +static void clear_apply_state(struct apply_state *state) +{ + string_list_clear(&state->limit_by_name, 0); + string_list_clear(&state->symlink_changes, 0); + strbuf_release(&state->root); + + /* &state->fn_table is cleared at the end of apply_patch() */ +} + +static void check_apply_state(struct apply_state *state, int force_apply) +{ + int is_not_gitdir = !startup_info->have_repository; + + if (state->apply_with_reject && state->threeway) + die("--reject and --3way cannot be used together."); + if (state->cached && state->threeway) + die("--cached and --3way cannot be used together."); + if (state->threeway) { + if (is_not_gitdir) + die(_("--3way outside a repository")); + state->check_index = 1; + } + if (state->apply_with_reject) + state->apply = state->apply_verbosely = 1; + if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor)) + state->apply = 0; + if (state->check_index && is_not_gitdir) + die(_("--index outside a repository")); + if (state->cached) { + if (is_not_gitdir) + die(_("--cached outside a repository")); + state->check_index = 1; + } + if (state->check_index) + state->unsafe_paths = 0; + if (!state->lock_file) + die("BUG: state->lock_file should not be NULL"); +} + +static int apply_all_patches(struct apply_state *state, + int argc, + const char **argv, + int options) { int i; int errs = 0; - int is_not_gitdir = !startup_info->have_repository; - int force_apply = 0; + int read_stdin = 1; - const char *whitespace_option = NULL; + for (i = 0; i < argc; i++) { + const char *arg = argv[i]; + int fd; + + if (!strcmp(arg, "-")) { + errs |= apply_patch(state, 0, "<stdin>", options); + read_stdin = 0; + continue; + } else if (0 < state->prefix_length) + arg = prefix_filename(state->prefix, + state->prefix_length, + arg); + + fd = open(arg, O_RDONLY); + if (fd < 0) + die_errno(_("can't open patch '%s'"), arg); + read_stdin = 0; + set_default_whitespace_mode(state); + errs |= apply_patch(state, fd, arg, options); + close(fd); + } + set_default_whitespace_mode(state); + if (read_stdin) + errs |= apply_patch(state, 0, "<stdin>", options); + + if (state->whitespace_error) { + if (state->squelch_whitespace_errors && + state->squelch_whitespace_errors < state->whitespace_error) { + int squelched = + state->whitespace_error - state->squelch_whitespace_errors; + warning(Q_("squelched %d whitespace error", + "squelched %d whitespace errors", + squelched), + squelched); + } + if (state->ws_error_action == die_on_ws_error) + die(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + state->whitespace_error), + state->whitespace_error); + if (state->applied_after_fixing_ws && state->apply) + warning("%d line%s applied after" + " fixing whitespace errors.", + state->applied_after_fixing_ws, + state->applied_after_fixing_ws == 1 ? "" : "s"); + else if (state->whitespace_error) + warning(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + state->whitespace_error), + state->whitespace_error); + } + + if (state->update_index) { + if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK)) + die(_("Unable to write new index file")); + state->newfd = -1; + } + + return !!errs; +} + +int cmd_apply(int argc, const char **argv, const char *prefix) +{ + int force_apply = 0; + int options = 0; + int ret; + struct apply_state state; struct option builtin_apply_options[] = { - { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"), + { OPTION_CALLBACK, 0, "exclude", &state, N_("path"), N_("don't apply changes matching the given path"), 0, option_parse_exclude }, - { OPTION_CALLBACK, 0, "include", NULL, N_("path"), + { OPTION_CALLBACK, 0, "include", &state, N_("path"), N_("apply changes matching the given path"), 0, option_parse_include }, - { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"), + { OPTION_CALLBACK, 'p', NULL, &state, N_("num"), N_("remove <num> leading slashes from traditional diff paths"), 0, option_parse_p }, - OPT_BOOL(0, "no-add", &no_add, + OPT_BOOL(0, "no-add", &state.no_add, N_("ignore additions made by the patch")), - OPT_BOOL(0, "stat", &diffstat, + OPT_BOOL(0, "stat", &state.diffstat, N_("instead of applying the patch, output diffstat for the input")), OPT_NOOP_NOARG(0, "allow-binary-replacement"), OPT_NOOP_NOARG(0, "binary"), - OPT_BOOL(0, "numstat", &numstat, + OPT_BOOL(0, "numstat", &state.numstat, N_("show number of added and deleted lines in decimal notation")), - OPT_BOOL(0, "summary", &summary, + OPT_BOOL(0, "summary", &state.summary, N_("instead of applying the patch, output a summary for the input")), - OPT_BOOL(0, "check", &check, + OPT_BOOL(0, "check", &state.check, N_("instead of applying the patch, see if the patch is applicable")), - OPT_BOOL(0, "index", &check_index, + OPT_BOOL(0, "index", &state.check_index, N_("make sure the patch is applicable to the current index")), - OPT_BOOL(0, "cached", &cached, + OPT_BOOL(0, "cached", &state.cached, N_("apply a patch without touching the working tree")), - OPT_BOOL(0, "unsafe-paths", &unsafe_paths, + OPT_BOOL(0, "unsafe-paths", &state.unsafe_paths, N_("accept a patch that touches outside the working area")), OPT_BOOL(0, "apply", &force_apply, N_("also apply the patch (use with --stat/--summary/--check)")), - OPT_BOOL('3', "3way", &threeway, + OPT_BOOL('3', "3way", &state.threeway, N_( "attempt three-way merge if a patch does not apply")), - OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, + OPT_FILENAME(0, "build-fake-ancestor", &state.fake_ancestor, N_("build a temporary index based on embedded index information")), - { OPTION_CALLBACK, 'z', NULL, NULL, NULL, - N_("paths are separated with NUL character"), - PARSE_OPT_NOARG, option_parse_z }, - OPT_INTEGER('C', NULL, &p_context, + /* Think twice before adding "--nul" synonym to this */ + OPT_SET_INT('z', NULL, &state.line_termination, + N_("paths are separated with NUL character"), '\0'), + OPT_INTEGER('C', NULL, &state.p_context, N_("ensure at least <n> lines of context match")), - { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"), + { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"), N_("detect new or modified lines that have whitespace errors"), 0, option_parse_whitespace }, - { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL, + { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL, N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, - { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL, + { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL, N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, - OPT_BOOL('R', "reverse", &apply_in_reverse, + OPT_BOOL('R', "reverse", &state.apply_in_reverse, N_("apply the patch in reverse")), - OPT_BOOL(0, "unidiff-zero", &unidiff_zero, + OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero, N_("don't expect at least one line of context")), - OPT_BOOL(0, "reject", &apply_with_reject, + OPT_BOOL(0, "reject", &state.apply_with_reject, N_("leave the rejected hunks in corresponding *.rej files")), - OPT_BOOL(0, "allow-overlap", &allow_overlap, + OPT_BOOL(0, "allow-overlap", &state.allow_overlap, N_("allow overlapping hunks")), - OPT__VERBOSE(&apply_verbosely, N_("be verbose")), + OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")), OPT_BIT(0, "inaccurate-eof", &options, N_("tolerate incorrectly detected missing new-line at the end of file"), INACCURATE_EOF), OPT_BIT(0, "recount", &options, N_("do not trust the line counts in the hunk headers"), RECOUNT), - { OPTION_CALLBACK, 0, "directory", NULL, N_("root"), + { OPTION_CALLBACK, 0, "directory", &state, N_("root"), N_("prepend <root> to all filenames"), 0, option_parse_directory }, OPT_END() }; - prefix = prefix_; - prefix_length = prefix ? strlen(prefix) : 0; - git_apply_config(); - if (apply_default_whitespace) - parse_whitespace_option(apply_default_whitespace); - if (apply_default_ignorewhitespace) - parse_ignorewhitespace_option(apply_default_ignorewhitespace); + init_apply_state(&state, prefix, &lock_file); - argc = parse_options(argc, argv, prefix, builtin_apply_options, + argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); - if (apply_with_reject && threeway) - die("--reject and --3way cannot be used together."); - if (cached && threeway) - die("--cached and --3way cannot be used together."); - if (threeway) { - if (is_not_gitdir) - die(_("--3way outside a repository")); - check_index = 1; - } - if (apply_with_reject) - apply = apply_verbosely = 1; - if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor)) - apply = 0; - if (check_index && is_not_gitdir) - die(_("--index outside a repository")); - if (cached) { - if (is_not_gitdir) - die(_("--cached outside a repository")); - check_index = 1; - } - if (check_index) - unsafe_paths = 0; + check_apply_state(&state, force_apply); - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - int fd; + ret = apply_all_patches(&state, argc, argv, options); - if (!strcmp(arg, "-")) { - errs |= apply_patch(0, "<stdin>", options); - read_stdin = 0; - continue; - } else if (0 < prefix_length) - arg = prefix_filename(prefix, prefix_length, arg); + clear_apply_state(&state); - fd = open(arg, O_RDONLY); - if (fd < 0) - die_errno(_("can't open patch '%s'"), arg); - read_stdin = 0; - set_default_whitespace_mode(whitespace_option); - errs |= apply_patch(fd, arg, options); - close(fd); - } - set_default_whitespace_mode(whitespace_option); - if (read_stdin) - errs |= apply_patch(0, "<stdin>", options); - if (whitespace_error) { - if (squelch_whitespace_errors && - squelch_whitespace_errors < whitespace_error) { - int squelched = - whitespace_error - squelch_whitespace_errors; - warning(Q_("squelched %d whitespace error", - "squelched %d whitespace errors", - squelched), - squelched); - } - if (ws_error_action == die_on_ws_error) - die(Q_("%d line adds whitespace errors.", - "%d lines add whitespace errors.", - whitespace_error), - whitespace_error); - if (applied_after_fixing_ws && apply) - warning("%d line%s applied after" - " fixing whitespace errors.", - applied_after_fixing_ws, - applied_after_fixing_ws == 1 ? "" : "s"); - else if (whitespace_error) - warning(Q_("%d line adds whitespace errors.", - "%d lines add whitespace errors.", - whitespace_error), - whitespace_error); - } - - if (update_index) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("Unable to write new index file")); - } - - return !!errs; + return ret; } |