diff options
Diffstat (limited to 'apply.c')
-rw-r--r-- | apply.c | 192 |
1 files changed, 88 insertions, 104 deletions
@@ -22,6 +22,12 @@ #include "rerere.h" #include "apply.h" +struct gitdiff_data { + struct strbuf *root; + int linenr; + int p_value; +}; + static void git_apply_config(void) { git_config_get_string_const("apply.whitespace", &apply_default_whitespace); @@ -56,6 +62,10 @@ static int parse_whitespace_option(struct apply_state *state, const char *option state->ws_error_action = correct_ws_error; return 0; } + /* + * Please update $__git_whitespacelist in git-completion.bash + * when you add new options. + */ return error(_("unrecognized whitespace option '%s'"), option); } @@ -197,40 +207,6 @@ struct fragment { #define BINARY_DELTA_DEFLATED 1 #define BINARY_LITERAL_DEFLATED 2 -/* - * This represents a "patch" to a file, both metainfo changes - * such as creation/deletion, filemode and content changes represented - * as a series of fragments. - */ -struct patch { - char *new_name, *old_name, *def_name; - unsigned int old_mode, new_mode; - int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */ - int rejected; - unsigned ws_rule; - int lines_added, lines_deleted; - int score; - int extension_linenr; /* first line specifying delete/new/rename/copy */ - unsigned int is_toplevel_relative:1; - unsigned int inaccurate_eof:1; - unsigned int is_binary:1; - unsigned int is_copy:1; - unsigned int is_rename:1; - unsigned int recount:1; - unsigned int conflicted_threeway:1; - unsigned int direct_to_threeway:1; - unsigned int crlf_in_old:1; - struct fragment *fragments; - char *result; - size_t resultsize; - char old_oid_prefix[GIT_MAX_HEXSZ + 1]; - char new_oid_prefix[GIT_MAX_HEXSZ + 1]; - struct patch *next; - - /* three-way fallback result */ - struct object_id threeway_stage[3]; -}; - static void free_fragment_list(struct fragment *list) { while (list) { @@ -465,7 +441,7 @@ static char *squash_slash(char *name) return name; } -static char *find_name_gnu(struct apply_state *state, +static char *find_name_gnu(struct strbuf *root, const char *line, int p_value) { @@ -474,7 +450,7 @@ static char *find_name_gnu(struct apply_state *state, /* * Proposed "new-style" GNU patch/diff format; see - * http://marc.info/?l=git&m=112927316408690&w=2 + * https://public-inbox.org/git/7vll0wvb2a.fsf@assigned-by-dhcp.cox.net/ */ if (unquote_c_style(&name, line, NULL)) { strbuf_release(&name); @@ -491,8 +467,8 @@ static char *find_name_gnu(struct apply_state *state, } strbuf_remove(&name, 0, cp - name.buf); - if (state->root.len) - strbuf_insert(&name, 0, state->root.buf, state->root.len); + if (root->len) + strbuf_insert(&name, 0, root->buf, root->len); return squash_slash(strbuf_detach(&name, NULL)); } @@ -655,7 +631,7 @@ static size_t diff_timestamp_len(const char *line, size_t len) return line + len - end; } -static char *find_name_common(struct apply_state *state, +static char *find_name_common(struct strbuf *root, const char *line, const char *def, int p_value, @@ -698,30 +674,30 @@ static char *find_name_common(struct apply_state *state, return squash_slash(xstrdup(def)); } - if (state->root.len) { - char *ret = xstrfmt("%s%.*s", state->root.buf, len, start); + if (root->len) { + char *ret = xstrfmt("%s%.*s", root->buf, len, start); return squash_slash(ret); } return squash_slash(xmemdupz(start, len)); } -static char *find_name(struct apply_state *state, +static char *find_name(struct strbuf *root, const char *line, char *def, int p_value, int terminate) { if (*line == '"') { - char *name = find_name_gnu(state, line, p_value); + char *name = find_name_gnu(root, line, p_value); if (name) return name; } - return find_name_common(state, line, def, p_value, NULL, terminate); + return find_name_common(root, line, def, p_value, NULL, terminate); } -static char *find_name_traditional(struct apply_state *state, +static char *find_name_traditional(struct strbuf *root, const char *line, char *def, int p_value) @@ -730,7 +706,7 @@ static char *find_name_traditional(struct apply_state *state, size_t date_len; if (*line == '"') { - char *name = find_name_gnu(state, line, p_value); + char *name = find_name_gnu(root, line, p_value); if (name) return name; } @@ -738,10 +714,10 @@ static char *find_name_traditional(struct apply_state *state, len = strchrnul(line, '\n') - line; date_len = diff_timestamp_len(line, len); if (!date_len) - return find_name_common(state, line, def, p_value, NULL, TERM_TAB); + return find_name_common(root, line, def, p_value, NULL, TERM_TAB); len -= date_len; - return find_name_common(state, line, def, p_value, line + len, 0); + return find_name_common(root, line, def, p_value, line + len, 0); } /* @@ -755,7 +731,7 @@ static int guess_p_value(struct apply_state *state, const char *nameline) if (is_dev_null(nameline)) return -1; - name = find_name_traditional(state, nameline, NULL, 0); + name = find_name_traditional(&state->root, nameline, NULL, 0); if (!name) return -1; cp = strchr(name, '/'); @@ -879,17 +855,17 @@ static int parse_traditional_patch(struct apply_state *state, if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; - name = find_name_traditional(state, second, NULL, state->p_value); + name = find_name_traditional(&state->root, 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(state, first, NULL, state->p_value); + name = find_name_traditional(&state->root, first, NULL, state->p_value); patch->old_name = name; } else { char *first_name; - first_name = find_name_traditional(state, first, NULL, state->p_value); - name = find_name_traditional(state, second, first_name, state->p_value); + first_name = find_name_traditional(&state->root, first, NULL, state->p_value); + name = find_name_traditional(&state->root, second, first_name, state->p_value); free(first_name); if (has_epoch_timestamp(first)) { patch->is_new = 1; @@ -910,7 +886,7 @@ static int parse_traditional_patch(struct apply_state *state, return 0; } -static int gitdiff_hdrend(struct apply_state *state, +static int gitdiff_hdrend(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -929,14 +905,14 @@ static int gitdiff_hdrend(struct apply_state *state, #define DIFF_OLD_NAME 0 #define DIFF_NEW_NAME 1 -static int gitdiff_verify_name(struct apply_state *state, +static int gitdiff_verify_name(struct gitdiff_data *state, const char *line, int isnull, char **name, int side) { if (!*name && !isnull) { - *name = find_name(state, line, NULL, state->p_value, TERM_TAB); + *name = find_name(state->root, line, NULL, state->p_value, TERM_TAB); return 0; } @@ -945,7 +921,7 @@ static int gitdiff_verify_name(struct apply_state *state, if (isnull) return error(_("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); + another = find_name(state->root, line, NULL, state->p_value, TERM_TAB); if (!another || strcmp(another, *name)) { free(another); return error((side == DIFF_NEW_NAME) ? @@ -961,7 +937,7 @@ static int gitdiff_verify_name(struct apply_state *state, return 0; } -static int gitdiff_oldname(struct apply_state *state, +static int gitdiff_oldname(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -970,7 +946,7 @@ static int gitdiff_oldname(struct apply_state *state, DIFF_OLD_NAME); } -static int gitdiff_newname(struct apply_state *state, +static int gitdiff_newname(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -988,21 +964,21 @@ static int parse_mode_line(const char *line, int linenr, unsigned int *mode) return 0; } -static int gitdiff_oldmode(struct apply_state *state, +static int gitdiff_oldmode(struct gitdiff_data *state, const char *line, struct patch *patch) { return parse_mode_line(line, state->linenr, &patch->old_mode); } -static int gitdiff_newmode(struct apply_state *state, +static int gitdiff_newmode(struct gitdiff_data *state, const char *line, struct patch *patch) { return parse_mode_line(line, state->linenr, &patch->new_mode); } -static int gitdiff_delete(struct apply_state *state, +static int gitdiff_delete(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -1012,7 +988,7 @@ static int gitdiff_delete(struct apply_state *state, return gitdiff_oldmode(state, line, patch); } -static int gitdiff_newfile(struct apply_state *state, +static int gitdiff_newfile(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -1022,47 +998,47 @@ static int gitdiff_newfile(struct apply_state *state, return gitdiff_newmode(state, line, patch); } -static int gitdiff_copysrc(struct apply_state *state, +static int gitdiff_copysrc(struct gitdiff_data *state, const char *line, struct patch *patch) { patch->is_copy = 1; free(patch->old_name); - patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_copydst(struct apply_state *state, +static int gitdiff_copydst(struct gitdiff_data *state, const char *line, struct patch *patch) { patch->is_copy = 1; free(patch->new_name); - patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_renamesrc(struct apply_state *state, +static int gitdiff_renamesrc(struct gitdiff_data *state, const char *line, struct patch *patch) { patch->is_rename = 1; free(patch->old_name); - patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_renamedst(struct apply_state *state, +static int gitdiff_renamedst(struct gitdiff_data *state, const char *line, struct patch *patch) { patch->is_rename = 1; free(patch->new_name); - patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_similarity(struct apply_state *state, +static int gitdiff_similarity(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -1072,7 +1048,7 @@ static int gitdiff_similarity(struct apply_state *state, return 0; } -static int gitdiff_dissimilarity(struct apply_state *state, +static int gitdiff_dissimilarity(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -1082,7 +1058,7 @@ static int gitdiff_dissimilarity(struct apply_state *state, return 0; } -static int gitdiff_index(struct apply_state *state, +static int gitdiff_index(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -1122,7 +1098,7 @@ static int gitdiff_index(struct apply_state *state, * 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(struct apply_state *state, +static int gitdiff_unrecognized(struct gitdiff_data *state, const char *line, struct patch *patch) { @@ -1133,17 +1109,17 @@ static int gitdiff_unrecognized(struct apply_state *state, * 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(struct apply_state *state, +static const char *skip_tree_prefix(int p_value, const char *line, int llen) { int nslash; int i; - if (!state->p_value) + if (!p_value) return (llen && line[0] == '/') ? NULL : line; - nslash = state->p_value; + nslash = p_value; for (i = 0; i < llen; i++) { int ch = line[i]; if (ch == '/' && --nslash <= 0) @@ -1160,7 +1136,7 @@ static const char *skip_tree_prefix(struct apply_state *state, * 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(struct apply_state *state, +static char *git_header_name(int p_value, const char *line, int llen) { @@ -1180,7 +1156,7 @@ static char *git_header_name(struct apply_state *state, goto free_and_fail1; /* strip the a/b prefix including trailing slash */ - cp = skip_tree_prefix(state, first.buf, first.len); + cp = skip_tree_prefix(p_value, first.buf, first.len); if (!cp) goto free_and_fail1; strbuf_remove(&first, 0, cp - first.buf); @@ -1197,7 +1173,7 @@ static char *git_header_name(struct apply_state *state, if (*second == '"') { if (unquote_c_style(&sp, second, NULL)) goto free_and_fail1; - cp = skip_tree_prefix(state, sp.buf, sp.len); + cp = skip_tree_prefix(p_value, sp.buf, sp.len); if (!cp) goto free_and_fail1; /* They must match, otherwise ignore */ @@ -1208,7 +1184,7 @@ static char *git_header_name(struct apply_state *state, } /* unquoted second */ - cp = skip_tree_prefix(state, second, line + llen - second); + cp = skip_tree_prefix(p_value, second, line + llen - second); if (!cp) goto free_and_fail1; if (line + llen - cp != first.len || @@ -1223,7 +1199,7 @@ static char *git_header_name(struct apply_state *state, } /* unquoted first name */ - name = skip_tree_prefix(state, line, llen); + name = skip_tree_prefix(p_value, line, llen); if (!name) return NULL; @@ -1239,7 +1215,7 @@ static char *git_header_name(struct apply_state *state, if (unquote_c_style(&sp, second, NULL)) goto free_and_fail2; - np = skip_tree_prefix(state, sp.buf, sp.len); + np = skip_tree_prefix(p_value, sp.buf, sp.len); if (!np) goto free_and_fail2; @@ -1283,7 +1259,7 @@ static char *git_header_name(struct apply_state *state, */ if (!name[len + 1]) return NULL; /* no postimage name */ - second = skip_tree_prefix(state, name + len + 1, + second = skip_tree_prefix(p_value, name + len + 1, line_len - (len + 1)); if (!second) return NULL; @@ -1298,26 +1274,28 @@ static char *git_header_name(struct apply_state *state, } } -static int check_header_line(struct apply_state *state, struct patch *patch) +static int check_header_line(int linenr, struct patch *patch) { int extensions = (patch->is_delete == 1) + (patch->is_new == 1) + (patch->is_rename == 1) + (patch->is_copy == 1); if (extensions > 1) return error(_("inconsistent header lines %d and %d"), - patch->extension_linenr, state->linenr); + patch->extension_linenr, linenr); if (extensions && !patch->extension_linenr) - patch->extension_linenr = state->linenr; + patch->extension_linenr = linenr; return 0; } -/* Verify that we recognize the lines following a git header */ -static int parse_git_header(struct apply_state *state, - const char *line, - int len, - unsigned int size, - struct patch *patch) +int parse_git_diff_header(struct strbuf *root, + int *linenr, + int p_value, + const char *line, + int len, + unsigned int size, + struct patch *patch) { unsigned long offset; + struct gitdiff_data parse_hdr_state; /* A git diff has explicit new/delete information, so we don't guess */ patch->is_new = 0; @@ -1329,20 +1307,24 @@ static int parse_git_header(struct apply_state *state, * or removing or adding empty files), so we get * the default name from the header. */ - 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); + patch->def_name = git_header_name(p_value, line, len); + if (patch->def_name && root->len) { + char *s = xstrfmt("%s%s", root->buf, patch->def_name); free(patch->def_name); patch->def_name = s; } line += len; size -= len; - state->linenr++; - for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) { + (*linenr)++; + parse_hdr_state.root = root; + parse_hdr_state.linenr = *linenr; + parse_hdr_state.p_value = p_value; + + for (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) { static const struct opentry { const char *str; - int (*fn)(struct apply_state *, const char *, struct patch *); + int (*fn)(struct gitdiff_data *, const char *, struct patch *); } optable[] = { { "@@ -", gitdiff_hdrend }, { "--- ", gitdiff_oldname }, @@ -1373,10 +1355,10 @@ static int parse_git_header(struct apply_state *state, int res; if (len < oplen || memcmp(p->str, line, oplen)) continue; - res = p->fn(state, line + oplen, patch); + res = p->fn(&parse_hdr_state, line + oplen, patch); if (res < 0) return -1; - if (check_header_line(state, patch)) + if (check_header_line(*linenr, patch)) return -1; if (res > 0) return offset; @@ -1557,7 +1539,9 @@ static int find_header(struct apply_state *state, * or mode change, so we handle that specially */ if (!memcmp("diff --git ", line, 11)) { - int git_hdr_len = parse_git_header(state, line, len, size, patch); + int git_hdr_len = parse_git_diff_header(&state->root, &state->linenr, + state->p_value, line, len, + size, patch); if (git_hdr_len < 0) return -128; if (git_hdr_len <= len) @@ -4306,7 +4290,7 @@ static int add_index_file(struct apply_state *state, "created file '%s'"), path); } - fill_stat_cache_info(ce, &st); + fill_stat_cache_info(state->repo->index, ce, &st); } if (write_object_file(buf, size, blob_type, &ce->oid) < 0) { discard_cache_entry(ce); |