diff options
Diffstat (limited to 'apply.c')
-rw-r--r-- | apply.c | 375 |
1 files changed, 203 insertions, 172 deletions
@@ -21,12 +21,19 @@ #include "quote.h" #include "rerere.h" #include "apply.h" +#include "entry.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); - git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace); - git_config(git_default_config, NULL); + git_config_get_string("apply.whitespace", &apply_default_whitespace); + git_config_get_string("apply.ignorewhitespace", &apply_default_ignorewhitespace); + git_config(git_xmerge_config, NULL); } static int parse_whitespace_option(struct apply_state *state, const char *option) @@ -94,9 +101,9 @@ int init_apply_state(struct apply_state *state, state->ws_error_action = warn_on_ws_error; state->ws_ignore_action = ignore_ws_none; state->linenr = 1; - string_list_init(&state->fn_table, 0); - string_list_init(&state->limit_by_name, 0); - string_list_init(&state->symlink_changes, 0); + string_list_init_nodup(&state->fn_table); + string_list_init_nodup(&state->limit_by_name); + string_list_init_nodup(&state->symlink_changes); strbuf_init(&state->root, 0); git_apply_config(); @@ -127,8 +134,6 @@ int check_apply_state(struct apply_state *state, int force_apply) if (state->apply_with_reject && state->threeway) return error(_("--reject and --3way cannot be used together.")); - if (state->cached && state->threeway) - return error(_("--cached and --3way cannot be used together.")); if (state->threeway) { if (is_not_gitdir) return error(_("--3way outside a repository")); @@ -201,40 +206,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) { @@ -469,7 +440,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) { @@ -478,7 +449,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://lore.kernel.org/git/7vll0wvb2a.fsf@assigned-by-dhcp.cox.net/ */ if (unquote_c_style(&name, line, NULL)) { strbuf_release(&name); @@ -495,8 +466,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)); } @@ -659,7 +630,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, @@ -702,30 +673,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) @@ -734,7 +705,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; } @@ -742,10 +713,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); } /* @@ -759,7 +730,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, '/'); @@ -883,17 +854,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; @@ -914,7 +885,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) { @@ -933,14 +904,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; } @@ -949,7 +920,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) ? @@ -965,7 +936,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) { @@ -974,7 +945,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) { @@ -992,21 +963,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) { @@ -1016,7 +987,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) { @@ -1026,47 +997,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) { @@ -1076,7 +1047,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) { @@ -1086,7 +1057,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) { @@ -1126,7 +1097,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) { @@ -1137,17 +1108,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) @@ -1164,7 +1135,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) { @@ -1184,7 +1155,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); @@ -1201,7 +1172,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 */ @@ -1212,7 +1183,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 || @@ -1227,7 +1198,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; @@ -1243,7 +1214,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; @@ -1287,7 +1258,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; @@ -1302,26 +1273,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; @@ -1333,20 +1306,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 }, @@ -1377,17 +1354,38 @@ 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; + goto done; break; } } +done: + if (!patch->old_name && !patch->new_name) { + if (!patch->def_name) { + error(Q_("git diff header lacks filename information when removing " + "%d leading pathname component (line %d)", + "git diff header lacks filename information when removing " + "%d leading pathname components (line %d)", + parse_hdr_state.p_value), + parse_hdr_state.p_value, *linenr); + return -128; + } + patch->old_name = xstrdup(patch->def_name); + patch->new_name = xstrdup(patch->def_name); + } + if ((!patch->new_name && !patch->is_delete) || + (!patch->old_name && !patch->is_new)) { + error(_("git diff header lacks filename information " + "(line %d)"), *linenr); + return -128; + } + patch->is_toplevel_relative = 1; return offset; } @@ -1561,31 +1559,13 @@ 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) continue; - if (!patch->old_name && !patch->new_name) { - if (!patch->def_name) { - error(Q_("git diff header lacks filename information when removing " - "%d leading pathname component (line %d)", - "git diff header lacks filename information when removing " - "%d leading pathname components (line %d)", - state->p_value), - state->p_value, state->linenr); - return -128; - } - patch->old_name = xstrdup(patch->def_name); - patch->new_name = xstrdup(patch->def_name); - } - if ((!patch->new_name && !patch->is_delete) || - (!patch->old_name && !patch->is_new)) { - error(_("git diff header lacks filename information " - "(line %d)"), state->linenr); - return -128; - } - patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; } @@ -1800,7 +1780,7 @@ static int parse_single_patch(struct apply_state *state, struct fragment *fragment; int len; - fragment = xcalloc(1, sizeof(*fragment)); + CALLOC_ARRAY(fragment, 1); fragment->linenr = state->linenr; len = parse_fragment(state, line, size, patch, fragment); if (len <= 0) { @@ -1979,7 +1959,7 @@ static struct fragment *parse_binary_hunk(struct apply_state *state, size -= llen; } - frag = xcalloc(1, sizeof(*frag)); + CALLOC_ARRAY(frag, 1); frag->patch = inflate_it(data, hunk_size, origlen); frag->free_patch = 1; if (!frag->patch) @@ -2682,6 +2662,16 @@ static int find_pos(struct apply_state *state, int backwards_lno, forwards_lno, current_lno; /* + * When running with --allow-overlap, it is possible that a hunk is + * seen that pretends to start at the beginning (but no longer does), + * and that *still* needs to match the end. So trust `match_end` more + * than `match_beginning`. + */ + if (state->allow_overlap && match_beginning && match_end && + img->nr - preimage->nr != 0) + match_beginning = 0; + + /* * If match_beginning or match_end is specified, there is no * point starting from a wrong line that will never match and * wander around and wait for a match at the specified end. @@ -3167,7 +3157,8 @@ static int apply_binary(struct apply_state *state, * See if the old one matches what the patch * applies to. */ - hash_object_file(img->buf, img->len, blob_type, &oid); + hash_object_file(the_hash_algo, img->buf, img->len, blob_type, + &oid); if (strcmp(oid_to_hex(&oid), patch->old_oid_prefix)) return error(_("the patch applies to '%s' (%s), " "which does not match the " @@ -3187,7 +3178,7 @@ static int apply_binary(struct apply_state *state, return 0; /* deletion patch */ } - if (has_object_file(&oid)) { + if (has_object(the_repository, &oid, 0)) { /* We already have the postimage */ enum object_type type; unsigned long size; @@ -3212,7 +3203,8 @@ static int apply_binary(struct apply_state *state, name); /* verify that the result matches */ - hash_object_file(img->buf, img->len, blob_type, &oid); + hash_object_file(the_hash_algo, img->buf, img->len, blob_type, + &oid); if (strcmp(oid_to_hex(&oid), patch->new_oid_prefix)) return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"), name, patch->new_oid_prefix, oid_to_hex(&oid)); @@ -3577,10 +3569,10 @@ static int try_threeway(struct apply_state *state, write_object_file("", 0, blob_type, &pre_oid); else if (get_oid(patch->old_oid_prefix, &pre_oid) || read_blob_object(&buf, &pre_oid, patch->old_mode)) - return error(_("repository lacks the necessary blob to fall back on 3-way merge.")); + return error(_("repository lacks the necessary blob to perform 3-way merge.")); - if (state->apply_verbosity > verbosity_silent) - fprintf(stderr, _("Falling back to three-way merge...\n")); + if (state->apply_verbosity > verbosity_silent && patch->direct_to_threeway) + fprintf(stderr, _("Performing three-way merge...\n")); img = strbuf_detach(&buf, &len); prepare_image(&tmp_image, img, len, 1); @@ -3612,7 +3604,7 @@ static int try_threeway(struct apply_state *state, if (status < 0) { if (state->apply_verbosity > verbosity_silent) fprintf(stderr, - _("Failed to fall back on three-way merge...\n")); + _("Failed to perform three-way merge...\n")); return status; } @@ -3645,10 +3637,13 @@ static int apply_data(struct apply_state *state, struct patch *patch, if (load_preimage(state, &image, patch, st, ce) < 0) return -1; - if (patch->direct_to_threeway || - apply_fragments(state, &image, patch) < 0) { + if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0) { + if (state->apply_verbosity > verbosity_silent && + state->threeway && !patch->direct_to_threeway) + fprintf(stderr, _("Falling back to direct application...\n")); + /* Note: with --reject, apply_fragments() returns 0 */ - if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0) + if (patch->direct_to_threeway || apply_fragments(state, &image, patch) < 0) return -1; } patch->result = image.buf; @@ -3748,6 +3743,7 @@ static int check_preimage(struct apply_state *state, #define EXISTS_IN_INDEX 1 #define EXISTS_IN_WORKTREE 2 +#define EXISTS_IN_INDEX_AS_ITA 3 static int check_to_create(struct apply_state *state, const char *new_name, @@ -3755,10 +3751,23 @@ static int check_to_create(struct apply_state *state, { struct stat nst; - if (state->check_index && - index_name_pos(state->repo->index, new_name, strlen(new_name)) >= 0 && - !ok_if_exists) - return EXISTS_IN_INDEX; + if (state->check_index && (!ok_if_exists || !state->cached)) { + int pos; + + pos = index_name_pos(state->repo->index, new_name, strlen(new_name)); + if (pos >= 0) { + struct cache_entry *ce = state->repo->index->cache[pos]; + + /* allow ITA, as they do not yet exist in the index */ + if (!ok_if_exists && !(ce->ce_flags & CE_INTENT_TO_ADD)) + return EXISTS_IN_INDEX; + + /* ITA entries can never match working tree files */ + if (!state->cached && (ce->ce_flags & CE_INTENT_TO_ADD)) + return EXISTS_IN_INDEX_AS_ITA; + } + } + if (state->cached) return 0; @@ -3942,7 +3951,8 @@ static int check_patch(struct apply_state *state, struct patch *patch) break; /* happy */ case EXISTS_IN_INDEX: return error(_("%s: already exists in index"), new_name); - break; + case EXISTS_IN_INDEX_AS_ITA: + return error(_("%s: does not match index"), new_name); case EXISTS_IN_WORKTREE: return error(_("%s: already exists in working directory"), new_name); @@ -4203,8 +4213,8 @@ static void show_rename_copy(struct patch *p) old_name = slash_old + 1; new_name = slash_new + 1; } - /* p->old_name thru old_name is the common prefix, and old_name and new_name - * through the end of names are renames + /* p->old_name through old_name is the common prefix, and old_name and + * new_name through the end of names are renames */ if (old_name != p->old_name) printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy, @@ -4357,7 +4367,7 @@ static int try_create_file(struct apply_state *state, const char *path, if (fd < 0) return 1; - if (convert_to_working_tree(state->repo->index, path, buf, size, &nbuf)) { + if (convert_to_working_tree(state->repo->index, path, buf, size, &nbuf, NULL)) { size = nbuf.len; buf = nbuf.buf; } @@ -4400,7 +4410,7 @@ static int create_one_file(struct apply_state *state, return 0; if (errno == ENOENT) { - if (safe_create_leading_directories(path)) + if (safe_create_leading_directories_no_share(path)) return 0; res = try_create_file(state, path, mode, buf, size); if (res < 0) @@ -4639,7 +4649,12 @@ static int write_out_results(struct apply_state *state, struct patch *list) } string_list_clear(&cpath, 0); - repo_rerere(state->repo, 0); + /* + * rerere relies on the partially merged result being in the working + * tree with conflict markers, but that isn't written with --cached. + */ + if (!state->cached) + repo_rerere(state->repo, 0); } return errs; @@ -4664,6 +4679,7 @@ static int apply_patch(struct apply_state *state, struct patch *list = NULL, **listp = &list; int skipped_patch = 0; int res = 0; + int flush_attributes = 0; state->patch_input_file = filename; if (read_patch_file(&buf, fd) < 0) @@ -4673,7 +4689,7 @@ static int apply_patch(struct apply_state *state, struct patch *patch; int nr; - patch = xcalloc(1, sizeof(*patch)); + CALLOC_ARRAY(patch, 1); patch->inaccurate_eof = !!(options & APPLY_OPT_INACCURATE_EOF); patch->recount = !!(options & APPLY_OPT_RECOUNT); nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); @@ -4689,8 +4705,21 @@ static int apply_patch(struct apply_state *state, reverse_patches(patch); if (use_patch(state, patch)) { patch_stats(state, patch); - *listp = patch; - listp = &patch->next; + if (!list || !state->apply_in_reverse) { + *listp = patch; + listp = &patch->next; + } else { + patch->next = list; + list = patch; + } + + if ((patch->new_name && + ends_with_path_components(patch->new_name, + GITATTRIBUTES_FILE)) || + (patch->old_name && + ends_with_path_components(patch->old_name, + GITATTRIBUTES_FILE))) + flush_attributes = 1; } else { if (state->apply_verbosity > verbosity_normal) @@ -4767,6 +4796,8 @@ static int apply_patch(struct apply_state *state, if (state->summary && state->apply_verbosity > verbosity_silent) summary_patch_list(list); + if (flush_attributes) + reset_parsed_attributes(); end: free_patch_list(list); strbuf_release(&buf); @@ -4961,15 +4992,15 @@ int apply_parse_options(int argc, const char **argv, const char * const *apply_usage) { struct option builtin_apply_options[] = { - { OPTION_CALLBACK, 0, "exclude", state, N_("path"), + OPT_CALLBACK_F(0, "exclude", state, N_("path"), N_("don't apply changes matching the given path"), - PARSE_OPT_NONEG, apply_option_parse_exclude }, - { OPTION_CALLBACK, 0, "include", state, N_("path"), + PARSE_OPT_NONEG, apply_option_parse_exclude), + OPT_CALLBACK_F(0, "include", state, N_("path"), N_("apply changes matching the given path"), - PARSE_OPT_NONEG, apply_option_parse_include }, - { OPTION_CALLBACK, 'p', NULL, state, N_("num"), + PARSE_OPT_NONEG, apply_option_parse_include), + OPT_CALLBACK('p', NULL, state, N_("num"), N_("remove <num> leading slashes from traditional diff paths"), - 0, apply_option_parse_p }, + apply_option_parse_p), OPT_BOOL(0, "no-add", &state->no_add, N_("ignore additions made by the patch")), OPT_BOOL(0, "stat", &state->diffstat, @@ -4994,7 +5025,7 @@ int apply_parse_options(int argc, const char **argv, OPT_BOOL(0, "apply", force_apply, N_("also apply the patch (use with --stat/--summary/--check)")), OPT_BOOL('3', "3way", &state->threeway, - N_( "attempt three-way merge if a patch does not apply")), + N_( "attempt three-way merge, fall back on normal patch if that fails")), OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor, N_("build a temporary index based on embedded index information")), /* Think twice before adding "--nul" synonym to this */ @@ -5002,15 +5033,15 @@ int apply_parse_options(int argc, const char **argv, 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", state, N_("action"), + OPT_CALLBACK(0, "whitespace", state, N_("action"), N_("detect new or modified lines that have whitespace errors"), - 0, apply_option_parse_whitespace }, - { OPTION_CALLBACK, 0, "ignore-space-change", state, NULL, + apply_option_parse_whitespace), + OPT_CALLBACK_F(0, "ignore-space-change", state, NULL, N_("ignore changes in whitespace when finding context"), - PARSE_OPT_NOARG, apply_option_parse_space_change }, - { OPTION_CALLBACK, 0, "ignore-whitespace", state, NULL, + PARSE_OPT_NOARG, apply_option_parse_space_change), + OPT_CALLBACK_F(0, "ignore-whitespace", state, NULL, N_("ignore changes in whitespace when finding context"), - PARSE_OPT_NOARG, apply_option_parse_space_change }, + PARSE_OPT_NOARG, apply_option_parse_space_change), OPT_BOOL('R', "reverse", &state->apply_in_reverse, N_("apply the patch in reverse")), OPT_BOOL(0, "unidiff-zero", &state->unidiff_zero, @@ -5026,9 +5057,9 @@ int apply_parse_options(int argc, const char **argv, OPT_BIT(0, "recount", options, N_("do not trust the line counts in the hunk headers"), APPLY_OPT_RECOUNT), - { OPTION_CALLBACK, 0, "directory", state, N_("root"), + OPT_CALLBACK(0, "directory", state, N_("root"), N_("prepend <root> to all filenames"), - 0, apply_option_parse_directory }, + apply_option_parse_directory), OPT_END() }; |