Skip to content

Commit 57f583c

Browse files
alexhenriegitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4c42d5f commit 57f583c

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
lines changed

Documentation/git-apply.txt

+8-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ git-apply - Apply a patch to files and/or to the index
99
SYNOPSIS
1010
--------
1111
[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]
1314
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
1415
[--allow-binary-replacement | --binary] [--reject] [-z]
1516
[-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
@@ -92,6 +93,12 @@ OPTIONS
9293
When used with the `--cached` option, any conflicts are left at higher stages
9394
in the cache.
9495

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.
101+
95102
--build-fake-ancestor=<file>::
96103
Newer 'git diff' output has embedded 'index information'
97104
for each blob to help identify the original version that

apply.c

+18-2
Original file line numberDiff line numberDiff line change
@@ -3561,6 +3561,7 @@ static int three_way_merge(struct apply_state *state,
35613561
const struct object_id *theirs)
35623562
{
35633563
mmfile_t base_file, our_file, their_file;
3564+
struct ll_merge_options merge_opts = LL_MERGE_OPTIONS_INIT;
35643565
mmbuffer_t result = { NULL };
35653566
enum ll_merge_result status;
35663567

@@ -3573,12 +3574,13 @@ static int three_way_merge(struct apply_state *state,
35733574
read_mmblob(&base_file, base);
35743575
read_mmblob(&our_file, ours);
35753576
read_mmblob(&their_file, theirs);
3577+
merge_opts.variant = state->merge_variant;
35763578
status = ll_merge(&result, path,
35773579
&base_file, "base",
35783580
&our_file, "ours",
35793581
&their_file, "theirs",
35803582
state->repo->index,
3581-
NULL);
3583+
&merge_opts);
35823584
if (status == LL_MERGE_BINARY_CONFLICT)
35833585
warning("Cannot merge binary files: %s (%s vs. %s)",
35843586
path, "ours", "theirs");
@@ -5151,6 +5153,15 @@ int apply_parse_options(int argc, const char **argv,
51515153
N_("also apply the patch (use with --stat/--summary/--check)")),
51525154
OPT_BOOL('3', "3way", &state->threeway,
51535155
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),
51545165
OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor,
51555166
N_("build a temporary index based on embedded index information")),
51565167
/* Think twice before adding "--nul" synonym to this */
@@ -5190,5 +5201,10 @@ int apply_parse_options(int argc, const char **argv,
51905201
OPT_END()
51915202
};
51925203

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;
51945210
}

apply.h

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct apply_state {
5959
struct repository *repo;
6060
const char *index_file;
6161
enum apply_verbosity apply_verbosity;
62+
int merge_variant;
6263
char *fake_ancestor;
6364
const char *patch_input_file;
6465
int line_termination;

t/t4108-apply-threeway.sh

+40
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,46 @@ test_expect_success 'apply with --3way with merge.conflictStyle = diff3' '
8282
test_apply_with_3way
8383
'
8484

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+
85125
test_expect_success 'apply with --3way with rerere enabled' '
86126
test_config rerere.enabled true &&
87127

0 commit comments

Comments
 (0)