diff options
Diffstat (limited to 'apply.c')
| -rw-r--r-- | apply.c | 201 |
1 files changed, 136 insertions, 65 deletions
@@ -7,6 +7,8 @@ * */ +#define USE_THE_REPOSITORY_VARIABLE + #include "git-compat-util.h" #include "abspath.h" #include "base85.h" @@ -28,6 +30,7 @@ #include "path.h" #include "quote.h" #include "read-cache.h" +#include "repository.h" #include "rerere.h" #include "apply.h" #include "entry.h" @@ -77,7 +80,8 @@ static int parse_whitespace_option(struct apply_state *state, const char *option return 0; } /* - * Please update $__git_whitespacelist in git-completion.bash + * Please update $__git_whitespacelist in git-completion.bash, + * Documentation/git-apply.txt, and Documentation/git-am.txt * when you add new options. */ return error(_("unrecognized whitespace option '%s'"), option); @@ -134,6 +138,7 @@ void clear_apply_state(struct apply_state *state) strset_clear(&state->removed_symlinks); strset_clear(&state->kept_symlinks); strbuf_release(&state->root); + FREE_AND_NULL(state->fake_ancestor); /* &state->fn_table is cleared at the end of apply_patch() */ } @@ -991,6 +996,7 @@ static int parse_mode_line(const char *line, int linenr, unsigned int *mode) *mode = strtoul(line, &end, 8); if (end == line || !isspace(*end)) return error(_("invalid mode on line %d: %s"), linenr, line); + *mode = canon_mode(*mode); return 0; } @@ -1291,8 +1297,15 @@ static char *git_header_name(int p_value, return NULL; /* no postimage name */ second = skip_tree_prefix(p_value, name + len + 1, line_len - (len + 1)); + /* + * If we are at the SP at the end of a directory, + * skip_tree_prefix() may return NULL as that makes + * it appears as if we have an absolute path. + * Keep going to find another SP. + */ if (!second) - return NULL; + continue; + /* * Does len bytes starting at "name" and "second" * (that are separated by one HT or SP we just @@ -2219,7 +2232,8 @@ static void reverse_patches(struct patch *p) struct fragment *frag = p->fragments; SWAP(p->new_name, p->old_name); - SWAP(p->new_mode, p->old_mode); + if (p->new_mode) + SWAP(p->new_mode, p->old_mode); SWAP(p->is_new, p->is_delete); SWAP(p->lines_added, p->lines_deleted); SWAP(p->old_oid_prefix, p->new_oid_prefix); @@ -2484,18 +2498,21 @@ static int match_fragment(struct apply_state *state, int match_beginning, int match_end) { int i; - char *fixed_buf, *buf, *orig, *target; - struct strbuf fixed; - size_t fixed_len, postlen; + const char *orig, *target; + struct strbuf fixed = STRBUF_INIT; + size_t postlen; int preimage_limit; + int ret; if (preimage->nr + current_lno <= img->nr) { /* * The hunk falls within the boundaries of img. */ preimage_limit = preimage->nr; - if (match_end && (preimage->nr + current_lno != img->nr)) - return 0; + if (match_end && (preimage->nr + current_lno != img->nr)) { + ret = 0; + goto out; + } } else if (state->ws_error_action == correct_ws_error && (ws_rule & WS_BLANK_AT_EOF)) { /* @@ -2512,17 +2529,23 @@ static int match_fragment(struct apply_state *state, * we are not removing blanks at the end, so we * should reject the hunk at this position. */ - return 0; + ret = 0; + goto out; } - if (match_beginning && current_lno) - return 0; + if (match_beginning && current_lno) { + ret = 0; + goto out; + } /* Quick hash check */ - for (i = 0; i < preimage_limit; i++) + for (i = 0; i < preimage_limit; i++) { if ((img->line[current_lno + i].flag & LINE_PATCHED) || - (preimage->line[i].hash != img->line[current_lno + i].hash)) - return 0; + (preimage->line[i].hash != img->line[current_lno + i].hash)) { + ret = 0; + goto out; + } + } if (preimage_limit == preimage->nr) { /* @@ -2535,8 +2558,10 @@ static int match_fragment(struct apply_state *state, if ((match_end ? (current + preimage->len == img->len) : (current + preimage->len <= img->len)) && - !memcmp(img->buf + current, preimage->buf, preimage->len)) - return 1; + !memcmp(img->buf + current, preimage->buf, preimage->len)) { + ret = 1; + goto out; + } } else { /* * The preimage extends beyond the end of img, so @@ -2545,7 +2570,7 @@ static int match_fragment(struct apply_state *state, * There must be one non-blank context line that match * a line before the end of img. */ - char *buf_end; + const char *buf, *buf_end; buf = preimage->buf; buf_end = buf; @@ -2555,8 +2580,10 @@ static int match_fragment(struct apply_state *state, for ( ; buf < buf_end; buf++) if (!isspace(*buf)) break; - if (buf == buf_end) - return 0; + if (buf == buf_end) { + ret = 0; + goto out; + } } /* @@ -2564,12 +2591,16 @@ static int match_fragment(struct apply_state *state, * fuzzy matching. We collect all the line length information because * we need it to adjust whitespace if we match. */ - if (state->ws_ignore_action == ignore_ws_change) - return line_by_line_fuzzy_match(img, preimage, postimage, - current, current_lno, preimage_limit); + if (state->ws_ignore_action == ignore_ws_change) { + ret = line_by_line_fuzzy_match(img, preimage, postimage, + current, current_lno, preimage_limit); + goto out; + } - if (state->ws_error_action != correct_ws_error) - return 0; + if (state->ws_error_action != correct_ws_error) { + ret = 0; + goto out; + } /* * The hunk does not apply byte-by-byte, but the hash says @@ -2598,7 +2629,7 @@ static int match_fragment(struct apply_state *state, * but in this loop we will only handle the part of the * preimage that falls within the file. */ - strbuf_init(&fixed, preimage->len + 1); + strbuf_grow(&fixed, preimage->len + 1); orig = preimage->buf; target = img->buf + current; for (i = 0; i < preimage_limit; i++) { @@ -2634,8 +2665,10 @@ static int match_fragment(struct apply_state *state, postlen += tgtfix.len; strbuf_release(&tgtfix); - if (!match) - goto unmatch_exit; + if (!match) { + ret = 0; + goto out; + } orig += oldlen; target += tgtlen; @@ -2656,9 +2689,13 @@ static int match_fragment(struct apply_state *state, /* Try fixing the line in the preimage */ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL); - for (j = fixstart; j < fixed.len; j++) - if (!isspace(fixed.buf[j])) - goto unmatch_exit; + for (j = fixstart; j < fixed.len; j++) { + if (!isspace(fixed.buf[j])) { + ret = 0; + goto out; + } + } + orig += oldlen; } @@ -2668,16 +2705,16 @@ static int match_fragment(struct apply_state *state, * has whitespace breakages unfixed, and fixing them makes the * hunk match. Update the context lines in the postimage. */ - fixed_buf = strbuf_detach(&fixed, &fixed_len); if (postlen < postimage->len) postlen = 0; update_pre_post_images(preimage, postimage, - fixed_buf, fixed_len, postlen); - return 1; + fixed.buf, fixed.len, postlen); - unmatch_exit: + ret = 1; + +out: strbuf_release(&fixed); - return 0; + return ret; } static int find_pos(struct apply_state *state, @@ -3525,6 +3562,7 @@ static int three_way_merge(struct apply_state *state, const struct object_id *theirs) { mmfile_t base_file, our_file, their_file; + struct ll_merge_options merge_opts = LL_MERGE_OPTIONS_INIT; mmbuffer_t result = { NULL }; enum ll_merge_result status; @@ -3537,12 +3575,13 @@ static int three_way_merge(struct apply_state *state, read_mmblob(&base_file, base); read_mmblob(&our_file, ours); read_mmblob(&their_file, theirs); + merge_opts.variant = state->merge_variant; status = ll_merge(&result, path, &base_file, "base", &our_file, "ours", &their_file, "theirs", state->repo->index, - NULL); + &merge_opts); if (status == LL_MERGE_BINARY_CONFLICT) warning("Cannot merge binary files: %s (%s vs. %s)", path, "ours", "theirs"); @@ -3671,7 +3710,7 @@ static int try_threeway(struct apply_state *state, if (status) { patch->conflicted_threeway = 1; if (patch->is_new) - oidclr(&patch->threeway_stage[0]); + oidclr(&patch->threeway_stage[0], the_repository->hash_algo); else oidcpy(&patch->threeway_stage[0], &pre_oid); oidcpy(&patch->threeway_stage[1], &our_oid); @@ -3703,8 +3742,10 @@ static int apply_data(struct apply_state *state, struct patch *patch, fprintf(stderr, _("Falling back to direct application...\n")); /* Note: with --reject, apply_fragments() returns 0 */ - if (patch->direct_to_threeway || apply_fragments(state, &image, patch) < 0) + if (patch->direct_to_threeway || apply_fragments(state, &image, patch) < 0) { + clear_image(&image); return -1; + } } patch->result = image.buf; patch->resultsize = image.len; @@ -3777,8 +3818,17 @@ static int check_preimage(struct apply_state *state, return error_errno("%s", old_name); } - if (!state->cached && !previous) - st_mode = ce_mode_from_stat(*ce, st->st_mode); + if (!state->cached && !previous) { + if (*ce && !(*ce)->ce_mode) + BUG("ce_mode == 0 for path '%s'", old_name); + + if (trust_executable_bit) + st_mode = ce_mode_from_stat(*ce, st->st_mode); + else if (*ce) + st_mode = (*ce)->ce_mode; + else + st_mode = patch->old_mode; + } if (patch->is_new < 0) patch->is_new = 0; @@ -4064,7 +4114,7 @@ static int read_apply_cache(struct apply_state *state) { if (state->index_file) return read_index_from(state->repo->index, state->index_file, - get_git_dir()); + repo_get_git_dir(the_repository)); else return repo_read_index(state->repo); } @@ -4430,6 +4480,7 @@ static int create_one_file(struct apply_state *state, const char *buf, unsigned long size) { + char *newpath = NULL; int res; if (state->cached) @@ -4491,24 +4542,26 @@ static int create_one_file(struct apply_state *state, unsigned int nr = getpid(); for (;;) { - char newpath[PATH_MAX]; - mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr); + newpath = mkpathdup("%s~%u", path, nr); res = try_create_file(state, newpath, mode, buf, size); if (res < 0) - return -1; + goto out; if (!res) { if (!rename(newpath, path)) - return 0; + goto out; unlink_or_warn(newpath); break; } if (errno != EEXIST) break; ++nr; + FREE_AND_NULL(newpath); } } - return error_errno(_("unable to write file '%s' mode %o"), - path, mode); + res = error_errno(_("unable to write file '%s' mode %o"), path, mode); +out: + free(newpath); + return res; } static int add_conflicted_stages_file(struct apply_state *state, @@ -4591,7 +4644,7 @@ static int write_out_one_result(struct apply_state *state, static int write_out_one_reject(struct apply_state *state, struct patch *patch) { FILE *rej; - char namebuf[PATH_MAX]; + char *namebuf; struct fragment *frag; int fd, cnt = 0; struct strbuf sb = STRBUF_INIT; @@ -4624,28 +4677,30 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch) say_patch_name(stderr, sb.buf, patch); strbuf_release(&sb); - cnt = strlen(patch->new_name); - if (ARRAY_SIZE(namebuf) <= cnt + 5) { - cnt = ARRAY_SIZE(namebuf) - 5; - warning(_("truncating .rej filename to %.*s.rej"), - cnt - 1, patch->new_name); - } - memcpy(namebuf, patch->new_name, cnt); - memcpy(namebuf + cnt, ".rej", 5); + namebuf = xstrfmt("%s.rej", patch->new_name); fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666); if (fd < 0) { - if (errno != EEXIST) - return error_errno(_("cannot open %s"), namebuf); - if (unlink(namebuf)) - return error_errno(_("cannot unlink '%s'"), namebuf); + if (errno != EEXIST) { + error_errno(_("cannot open %s"), namebuf); + goto error; + } + if (unlink(namebuf)) { + error_errno(_("cannot unlink '%s'"), namebuf); + goto error; + } fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (fd < 0) - return error_errno(_("cannot open %s"), namebuf); + if (fd < 0) { + error_errno(_("cannot open %s"), namebuf); + goto error; + } } rej = fdopen(fd, "w"); - if (!rej) - return error_errno(_("cannot open %s"), namebuf); + if (!rej) { + error_errno(_("cannot open %s"), namebuf); + close(fd); + goto error; + } /* Normal git tools never deal with .rej, so do not pretend * this is a git patch by saying --git or giving extended @@ -4669,6 +4724,8 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch) fputc('\n', rej); } fclose(rej); +error: + free(namebuf); return -1; } @@ -5097,6 +5154,15 @@ int apply_parse_options(int argc, const char **argv, N_("also apply the patch (use with --stat/--summary/--check)")), OPT_BOOL('3', "3way", &state->threeway, N_( "attempt three-way merge, fall back on normal patch if that fails")), + OPT_SET_INT_F(0, "ours", &state->merge_variant, + N_("for conflicts, use our version"), + XDL_MERGE_FAVOR_OURS, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "theirs", &state->merge_variant, + N_("for conflicts, use their version"), + XDL_MERGE_FAVOR_THEIRS, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "union", &state->merge_variant, + N_("for conflicts, use a union version"), + XDL_MERGE_FAVOR_UNION, PARSE_OPT_NONEG), 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 */ @@ -5136,5 +5202,10 @@ int apply_parse_options(int argc, const char **argv, OPT_END() }; - return parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0); + argc = parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0); + + if (state->merge_variant && !state->threeway) + die(_("--ours, --theirs, and --union require --3way")); + + return argc; } |
