git-apply: try threeway first when "--3way" is used
The apply_fragments() method of "git apply"
can silently apply patches incorrectly if
a file has repeating contents. In these
cases a three-way merge is capable of applying
it correctly in more situations, and will
show a conflict rather than applying it
incorrectly. However, because the patches
apply "successfully" using apply_fragments(),
git will never fall back to the merge, even
if the "--3way" flag is used, and the user has
no way to ensure correctness by forcing the
three-way merge method.
Change the behavior so that when "--3way" is used,
git will always try the three-way merge first and
will only fall back to apply_fragments() in cases
where blobs are not available or some other error
(but not in the case of a merge conflict).
Since user-facing results will be different,
this has backwards compatibility implications
for users depending on the old behavior. In
addition, the three-way merge will be slower
than direct patch application.
Signed-off-by: Jerry Zhang <jerry@skydio.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jerry Zhang committedApr 6, 2021 at 16:25 UTC923cd87ac8550a8e277bfeb19198a11b6a8ed854
3 files changed+28-10
Documentation/git-apply.txt
+2-3
index 91d9a8601c..9144575299 100644--- a/Documentation/git-apply.txt+++ b/Documentation/git-apply.txt@@ -84,9 +84,8 @@ OPTIONS -3:: --3way::- When the patch does not apply cleanly, fall back on 3-way merge if- the patch records the identity of blobs it is supposed to apply to,- and we have those blobs available locally, possibly leaving the+ Attempt 3-way merge if the patch records the identity of blobs it is supposed+ to apply to and we have those blobs available locally, possibly leaving the conflict markers in the files in the working tree for the user to resolve. This option implies the `--index` option, and is incompatible with the `--reject` and the `--cached` options.
apply.c
+6-7
index 466f880d73..69197268cc 100644--- a/apply.c+++ b/apply.c@@ -3570,10 +3570,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"));+ fprintf(stderr, _("Performing three-way merge...\n")); img = strbuf_detach(&buf, &len); prepare_image(&tmp_image, img, len, 1);@@ -3605,7 +3605,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; }@@ -3638,10 +3638,9 @@ 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) { /* 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;@@ -5018,7 +5017,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 */