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>
This commit is contained in:

committed by
Junio C Hamano

parent
4c42d5ff28
commit
57f583c748
@ -9,7 +9,8 @@ git-apply - Apply a patch to files and/or to the index
|
|||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git apply' [--stat] [--numstat] [--summary] [--check] [--index | --intent-to-add] [--3way]
|
'git apply' [--stat] [--numstat] [--summary] [--check]
|
||||||
|
[--index | --intent-to-add] [--3way] [--ours | --theirs | --union]
|
||||||
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
|
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
|
||||||
[--allow-binary-replacement | --binary] [--reject] [-z]
|
[--allow-binary-replacement | --binary] [--reject] [-z]
|
||||||
[-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
|
[-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
|
||||||
@ -92,6 +93,12 @@ OPTIONS
|
|||||||
When used with the `--cached` option, any conflicts are left at higher stages
|
When used with the `--cached` option, any conflicts are left at higher stages
|
||||||
in the cache.
|
in the cache.
|
||||||
|
|
||||||
|
--ours::
|
||||||
|
--theirs::
|
||||||
|
--union::
|
||||||
|
Instead of leaving conflicts in the file, resolve conflicts favouring
|
||||||
|
our (or their or both) side of the lines. Requires --3way.
|
||||||
|
|
||||||
--build-fake-ancestor=<file>::
|
--build-fake-ancestor=<file>::
|
||||||
Newer 'git diff' output has embedded 'index information'
|
Newer 'git diff' output has embedded 'index information'
|
||||||
for each blob to help identify the original version that
|
for each blob to help identify the original version that
|
||||||
|
20
apply.c
20
apply.c
@ -3561,6 +3561,7 @@ static int three_way_merge(struct apply_state *state,
|
|||||||
const struct object_id *theirs)
|
const struct object_id *theirs)
|
||||||
{
|
{
|
||||||
mmfile_t base_file, our_file, their_file;
|
mmfile_t base_file, our_file, their_file;
|
||||||
|
struct ll_merge_options merge_opts = LL_MERGE_OPTIONS_INIT;
|
||||||
mmbuffer_t result = { NULL };
|
mmbuffer_t result = { NULL };
|
||||||
enum ll_merge_result status;
|
enum ll_merge_result status;
|
||||||
|
|
||||||
@ -3573,12 +3574,13 @@ static int three_way_merge(struct apply_state *state,
|
|||||||
read_mmblob(&base_file, base);
|
read_mmblob(&base_file, base);
|
||||||
read_mmblob(&our_file, ours);
|
read_mmblob(&our_file, ours);
|
||||||
read_mmblob(&their_file, theirs);
|
read_mmblob(&their_file, theirs);
|
||||||
|
merge_opts.variant = state->merge_variant;
|
||||||
status = ll_merge(&result, path,
|
status = ll_merge(&result, path,
|
||||||
&base_file, "base",
|
&base_file, "base",
|
||||||
&our_file, "ours",
|
&our_file, "ours",
|
||||||
&their_file, "theirs",
|
&their_file, "theirs",
|
||||||
state->repo->index,
|
state->repo->index,
|
||||||
NULL);
|
&merge_opts);
|
||||||
if (status == LL_MERGE_BINARY_CONFLICT)
|
if (status == LL_MERGE_BINARY_CONFLICT)
|
||||||
warning("Cannot merge binary files: %s (%s vs. %s)",
|
warning("Cannot merge binary files: %s (%s vs. %s)",
|
||||||
path, "ours", "theirs");
|
path, "ours", "theirs");
|
||||||
@ -5151,6 +5153,15 @@ int apply_parse_options(int argc, const char **argv,
|
|||||||
N_("also apply the patch (use with --stat/--summary/--check)")),
|
N_("also apply the patch (use with --stat/--summary/--check)")),
|
||||||
OPT_BOOL('3', "3way", &state->threeway,
|
OPT_BOOL('3', "3way", &state->threeway,
|
||||||
N_( "attempt three-way merge, fall back on normal patch if that fails")),
|
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,
|
OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor,
|
||||||
N_("build a temporary index based on embedded index information")),
|
N_("build a temporary index based on embedded index information")),
|
||||||
/* Think twice before adding "--nul" synonym to this */
|
/* Think twice before adding "--nul" synonym to this */
|
||||||
@ -5190,5 +5201,10 @@ int apply_parse_options(int argc, const char **argv,
|
|||||||
OPT_END()
|
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;
|
||||||
}
|
}
|
||||||
|
1
apply.h
1
apply.h
@ -59,6 +59,7 @@ struct apply_state {
|
|||||||
struct repository *repo;
|
struct repository *repo;
|
||||||
const char *index_file;
|
const char *index_file;
|
||||||
enum apply_verbosity apply_verbosity;
|
enum apply_verbosity apply_verbosity;
|
||||||
|
int merge_variant;
|
||||||
char *fake_ancestor;
|
char *fake_ancestor;
|
||||||
const char *patch_input_file;
|
const char *patch_input_file;
|
||||||
int line_termination;
|
int line_termination;
|
||||||
|
@ -82,6 +82,46 @@ test_expect_success 'apply with --3way with merge.conflictStyle = diff3' '
|
|||||||
test_apply_with_3way
|
test_apply_with_3way
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_apply_with_3way_favoritism () {
|
||||||
|
apply_arg=$1
|
||||||
|
merge_arg=$2
|
||||||
|
|
||||||
|
# Merging side should be similar to applying this patch
|
||||||
|
git diff ...side >P.diff &&
|
||||||
|
|
||||||
|
# The corresponding conflicted merge
|
||||||
|
git reset --hard &&
|
||||||
|
git checkout main^0 &&
|
||||||
|
git merge --no-commit $merge_arg side &&
|
||||||
|
git ls-files -s >expect.ls &&
|
||||||
|
print_sanitized_conflicted_diff >expect.diff &&
|
||||||
|
|
||||||
|
# should apply successfully
|
||||||
|
git reset --hard &&
|
||||||
|
git checkout main^0 &&
|
||||||
|
git apply --index --3way $apply_arg P.diff &&
|
||||||
|
git ls-files -s >actual.ls &&
|
||||||
|
print_sanitized_conflicted_diff >actual.diff &&
|
||||||
|
|
||||||
|
# The result should resemble the corresponding merge
|
||||||
|
test_cmp expect.ls actual.ls &&
|
||||||
|
test_cmp expect.diff actual.diff
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'apply with --3way --ours' '
|
||||||
|
test_apply_with_3way_favoritism --ours -Xours
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'apply with --3way --theirs' '
|
||||||
|
test_apply_with_3way_favoritism --theirs -Xtheirs
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'apply with --3way --union' '
|
||||||
|
echo "* merge=union" >.gitattributes &&
|
||||||
|
test_apply_with_3way_favoritism --union &&
|
||||||
|
rm .gitattributes
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'apply with --3way with rerere enabled' '
|
test_expect_success 'apply with --3way with rerere enabled' '
|
||||||
test_config rerere.enabled true &&
|
test_config rerere.enabled true &&
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user