Git fork

apply: support --ours, --theirs, and --union for three-way merges

--ours, --theirs, and --union are already supported in `git merge-file`
for automatically resolving conflicts in favor of one version or the
other, instead of leaving conflict markers in the file. Support them in
`git apply -3` as well because the two commands do the same kind of
file-level merges.

In case in the future --ours, --theirs, and --union gain a meaning
outside of three-way-merges, they do not imply --3way but rather must be
specified alongside it.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Alex Henrie and committed by
Junio C Hamano
57f583c7 4c42d5ff

+67 -3
+8 -1
Documentation/git-apply.txt
··· 9 9 SYNOPSIS 10 10 -------- 11 11 [verse] 12 - 'git apply' [--stat] [--numstat] [--summary] [--check] [--index | --intent-to-add] [--3way] 12 + 'git apply' [--stat] [--numstat] [--summary] [--check] 13 + [--index | --intent-to-add] [--3way] [--ours | --theirs | --union] 13 14 [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse] 14 15 [--allow-binary-replacement | --binary] [--reject] [-z] 15 16 [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached] ··· 91 92 `--cached` option is used, and is incompatible with the `--reject` option. 92 93 When used with the `--cached` option, any conflicts are left at higher stages 93 94 in the cache. 95 + 96 + --ours:: 97 + --theirs:: 98 + --union:: 99 + Instead of leaving conflicts in the file, resolve conflicts favouring 100 + our (or their or both) side of the lines. Requires --3way. 94 101 95 102 --build-fake-ancestor=<file>:: 96 103 Newer 'git diff' output has embedded 'index information'
+18 -2
apply.c
··· 3561 3561 const struct object_id *theirs) 3562 3562 { 3563 3563 mmfile_t base_file, our_file, their_file; 3564 + struct ll_merge_options merge_opts = LL_MERGE_OPTIONS_INIT; 3564 3565 mmbuffer_t result = { NULL }; 3565 3566 enum ll_merge_result status; 3566 3567 ··· 3573 3574 read_mmblob(&base_file, base); 3574 3575 read_mmblob(&our_file, ours); 3575 3576 read_mmblob(&their_file, theirs); 3577 + merge_opts.variant = state->merge_variant; 3576 3578 status = ll_merge(&result, path, 3577 3579 &base_file, "base", 3578 3580 &our_file, "ours", 3579 3581 &their_file, "theirs", 3580 3582 state->repo->index, 3581 - NULL); 3583 + &merge_opts); 3582 3584 if (status == LL_MERGE_BINARY_CONFLICT) 3583 3585 warning("Cannot merge binary files: %s (%s vs. %s)", 3584 3586 path, "ours", "theirs"); ··· 5151 5153 N_("also apply the patch (use with --stat/--summary/--check)")), 5152 5154 OPT_BOOL('3', "3way", &state->threeway, 5153 5155 N_( "attempt three-way merge, fall back on normal patch if that fails")), 5156 + OPT_SET_INT_F(0, "ours", &state->merge_variant, 5157 + N_("for conflicts, use our version"), 5158 + XDL_MERGE_FAVOR_OURS, PARSE_OPT_NONEG), 5159 + OPT_SET_INT_F(0, "theirs", &state->merge_variant, 5160 + N_("for conflicts, use their version"), 5161 + XDL_MERGE_FAVOR_THEIRS, PARSE_OPT_NONEG), 5162 + OPT_SET_INT_F(0, "union", &state->merge_variant, 5163 + N_("for conflicts, use a union version"), 5164 + XDL_MERGE_FAVOR_UNION, PARSE_OPT_NONEG), 5154 5165 OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor, 5155 5166 N_("build a temporary index based on embedded index information")), 5156 5167 /* Think twice before adding "--nul" synonym to this */ ··· 5190 5201 OPT_END() 5191 5202 }; 5192 5203 5193 - return parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0); 5204 + argc = parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0); 5205 + 5206 + if (state->merge_variant && !state->threeway) 5207 + die(_("--ours, --theirs, and --union require --3way")); 5208 + 5209 + return argc; 5194 5210 }
+1
apply.h
··· 59 59 struct repository *repo; 60 60 const char *index_file; 61 61 enum apply_verbosity apply_verbosity; 62 + int merge_variant; 62 63 char *fake_ancestor; 63 64 const char *patch_input_file; 64 65 int line_termination;
+40
t/t4108-apply-threeway.sh
··· 82 82 test_apply_with_3way 83 83 ' 84 84 85 + test_apply_with_3way_favoritism () { 86 + apply_arg=$1 87 + merge_arg=$2 88 + 89 + # Merging side should be similar to applying this patch 90 + git diff ...side >P.diff && 91 + 92 + # The corresponding conflicted merge 93 + git reset --hard && 94 + git checkout main^0 && 95 + git merge --no-commit $merge_arg side && 96 + git ls-files -s >expect.ls && 97 + print_sanitized_conflicted_diff >expect.diff && 98 + 99 + # should apply successfully 100 + git reset --hard && 101 + git checkout main^0 && 102 + git apply --index --3way $apply_arg P.diff && 103 + git ls-files -s >actual.ls && 104 + print_sanitized_conflicted_diff >actual.diff && 105 + 106 + # The result should resemble the corresponding merge 107 + test_cmp expect.ls actual.ls && 108 + test_cmp expect.diff actual.diff 109 + } 110 + 111 + test_expect_success 'apply with --3way --ours' ' 112 + test_apply_with_3way_favoritism --ours -Xours 113 + ' 114 + 115 + test_expect_success 'apply with --3way --theirs' ' 116 + test_apply_with_3way_favoritism --theirs -Xtheirs 117 + ' 118 + 119 + test_expect_success 'apply with --3way --union' ' 120 + echo "* merge=union" >.gitattributes && 121 + test_apply_with_3way_favoritism --union && 122 + rm .gitattributes 123 + ' 124 + 85 125 test_expect_success 'apply with --3way with rerere enabled' ' 86 126 test_config rerere.enabled true && 87 127