git/t/t4108-apply-threeway.sh
Jerry Zhang 34d607032c git-apply: skip threeway in add / rename cases
Certain invocations of "git apply --3way" will attempt threeway and
fail due to missing objects, even though git is able to fall back on
apply_fragments and apply the patch successfully with a return value
of 0. To fix, return early from try_threeway() in the following
cases:

 - When the patch is a rename and no lines have changed. In this
   case, "git diff" doesn't record the blob info, so 3way is neither
   possible nor necessary.

 - When the patch is an addition and there is no add/add conflict,
   i.e. direct_to_threeway is false. In this case, threeway will
   fail since the preimage is not in cache, but isn't necessary
   anyway since there is no conflict.

This fixes a few unecessary error messages when applying these kinds
of patches with --3way.

It also fixes a reported issue where applying a concatenation of
several git produced patches will fail when those patches involve a
deletion followed by creation of the same file.  Add a test for this
case too.  (test provided by <i@zenithal.me>)

Signed-off-by: Jerry Zhang <jerry@skydio.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-20 12:39:45 -08:00

297 lines
7.5 KiB
Bash
Executable File

#!/bin/sh
test_description='git apply --3way'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
print_sanitized_conflicted_diff () {
git diff HEAD >diff.raw &&
sed -e '
/^index /d
s/^\(+[<>|][<>|][<>|][<>|]*\) .*/\1/
' diff.raw
}
test_expect_success setup '
test_tick &&
test_write_lines 1 2 3 4 5 6 7 >one &&
cat one >two &&
git add one two &&
git commit -m initial &&
git branch side &&
test_tick &&
test_write_lines 1 two 3 4 5 six 7 >one &&
test_write_lines 1 two 3 4 5 6 7 >two &&
git commit -a -m main &&
git checkout side &&
test_write_lines 1 2 3 4 five 6 7 >one &&
test_write_lines 1 2 3 4 five 6 7 >two &&
git commit -a -m side &&
git checkout main
'
test_expect_success 'apply without --3way' '
git diff side^ side >P.diff &&
# should fail to apply
git reset --hard &&
git checkout main^0 &&
test_must_fail git apply --index P.diff &&
# should leave things intact
git diff-files --exit-code &&
git diff-index --exit-code --cached HEAD
'
test_apply_with_3way () {
# 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 &&
test_must_fail git merge --no-commit side &&
git ls-files -s >expect.ls &&
print_sanitized_conflicted_diff >expect.diff &&
# should fail to apply
git reset --hard &&
git checkout main^0 &&
test_must_fail git apply --index --3way 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' '
test_apply_with_3way
'
test_expect_success 'apply with --3way with merge.conflictStyle = diff3' '
test_config merge.conflictStyle diff3 &&
test_apply_with_3way
'
test_expect_success 'apply with --3way with rerere enabled' '
test_config rerere.enabled true &&
# 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 &&
test_must_fail git merge --no-commit side &&
# Manually resolve and record the resolution
test_write_lines 1 two 3 4 five six 7 >one &&
git rerere &&
cat one >expect &&
# should fail to apply
git reset --hard &&
git checkout main^0 &&
test_must_fail git apply --index --3way P.diff &&
# but rerere should have replayed the recorded resolution
test_cmp expect one
'
test_expect_success 'apply -3 with add/add conflict setup' '
git reset --hard &&
git checkout -b adder &&
test_write_lines 1 2 3 4 5 6 7 >three &&
test_write_lines 1 2 3 4 5 6 7 >four &&
git add three four &&
git commit -m "add three and four" &&
git checkout -b another adder^ &&
test_write_lines 1 2 3 4 5 6 7 >three &&
test_write_lines 1 2 3 four 5 6 7 >four &&
git add three four &&
git commit -m "add three and four" &&
# Merging another should be similar to applying this patch
git diff adder...another >P.diff &&
git checkout adder^0 &&
test_must_fail git merge --no-commit another &&
git ls-files -s >expect.ls &&
print_sanitized_conflicted_diff >expect.diff
'
test_expect_success 'apply -3 with add/add conflict' '
# should fail to apply ...
git reset --hard &&
git checkout adder^0 &&
test_must_fail git apply --index --3way P.diff &&
# ... and leave conflicts in the index and in the working tree
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 -3 with add/add conflict (dirty working tree)' '
# should fail to apply ...
git reset --hard &&
git checkout adder^0 &&
echo >>four &&
cat four >four.save &&
cat three >three.save &&
git ls-files -s >expect.ls &&
test_must_fail git apply --index --3way P.diff &&
# ... and should not touch anything
git ls-files -s >actual.ls &&
test_cmp expect.ls actual.ls &&
test_cmp four.save four &&
test_cmp three.save three
'
test_expect_success 'apply -3 with ambiguous repeating file' '
git reset --hard &&
test_write_lines 1 2 1 2 1 2 1 2 1 2 1 >one_two_repeat &&
git add one_two_repeat &&
git commit -m "init one" &&
test_write_lines 1 2 1 2 1 2 1 2 one 2 1 >one_two_repeat &&
git commit -a -m "change one" &&
git diff HEAD~ >Repeat.diff &&
git reset --hard HEAD~ &&
test_write_lines 1 2 1 2 1 2 one 2 1 2 one >one_two_repeat &&
git commit -a -m "change surrounding one" &&
git apply --index --3way Repeat.diff &&
test_write_lines 1 2 1 2 1 2 one 2 one 2 one >expect &&
test_cmp expect one_two_repeat
'
test_expect_success 'apply with --3way --cached clean apply' '
# Merging side should be similar to applying this patch
git diff ...side >P.diff &&
# The corresponding cleanly applied merge
git reset --hard &&
git checkout main~ &&
git merge --no-commit side &&
git ls-files -s >expect.ls &&
# should succeed
git reset --hard &&
git checkout main~ &&
git apply --cached --3way P.diff &&
git ls-files -s >actual.ls &&
print_sanitized_conflicted_diff >actual.diff &&
# The cache should resemble the corresponding merge
# (both files at stage #0)
test_cmp expect.ls actual.ls &&
# However the working directory should not change
>expect.diff &&
test_cmp expect.diff actual.diff
'
test_expect_success 'apply with --3way --cached and conflicts' '
# 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 &&
test_must_fail git merge --no-commit side &&
git ls-files -s >expect.ls &&
# should fail to apply
git reset --hard &&
git checkout main^0 &&
test_must_fail git apply --cached --3way P.diff &&
git ls-files -s >actual.ls &&
print_sanitized_conflicted_diff >actual.diff &&
# The cache should resemble the corresponding merge
# (one file at stage #0, one file at stages #1 #2 #3)
test_cmp expect.ls actual.ls &&
# However the working directory should not change
>expect.diff &&
test_cmp expect.diff actual.diff
'
test_expect_success 'apply binary file patch' '
git reset --hard main &&
cp "$TEST_DIRECTORY/test-binary-1.png" bin.png &&
git add bin.png &&
git commit -m "add binary file" &&
cp "$TEST_DIRECTORY/test-binary-2.png" bin.png &&
git diff --binary >bin.diff &&
git reset --hard &&
# Apply must succeed.
git apply bin.diff
'
test_expect_success 'apply binary file patch with 3way' '
git reset --hard main &&
cp "$TEST_DIRECTORY/test-binary-1.png" bin.png &&
git add bin.png &&
git commit -m "add binary file" &&
cp "$TEST_DIRECTORY/test-binary-2.png" bin.png &&
git diff --binary >bin.diff &&
git reset --hard &&
# Apply must succeed.
git apply --3way --index bin.diff
'
test_expect_success 'apply full-index patch with 3way' '
git reset --hard main &&
cp "$TEST_DIRECTORY/test-binary-1.png" bin.png &&
git add bin.png &&
git commit -m "add binary file" &&
cp "$TEST_DIRECTORY/test-binary-2.png" bin.png &&
git diff --full-index >bin.diff &&
git reset --hard &&
# Apply must succeed.
git apply --3way --index bin.diff
'
test_expect_success 'apply delete then new patch with 3way' '
git reset --hard main &&
test_write_lines 2 > delnew &&
git add delnew &&
git diff --cached >> new.patch &&
git reset --hard &&
test_write_lines 1 > delnew &&
git add delnew &&
git commit -m "delnew" &&
rm delnew &&
git diff >> delete-then-new.patch &&
cat new.patch >> delete-then-new.patch &&
git checkout -- . &&
# Apply must succeed.
git apply --3way delete-then-new.patch
'
test_done