git-commit-vandalism/t/t2023-checkout-m.sh
Nguyễn Thái Ngọc Duy ab5af825db unpack-trees: fix oneway_merge accidentally carry over stage index
Phillip found out that 'git checkout -f <branch>' does not restore
conflict/unmerged files correctly. All tracked files should be taken
from <branch> and all non-zero stages removed. Most of this is true,
except that the final file could be in stage one instead of zero.

"checkout -f" (among other commands) does this with one-way merge, which
is supposed to take stat info from the index and everything else from
the given tree. The add_entry(.., old, ...) call in oneway_merge()
though will keep stage index from the index.

This is normally not a problem if the entry from the index is
normal (stage ). But if there is a conflict, stage  does not exist
and we'll get stage  entry as "old" variable, which gets recorded in
the final index. Fix it by clearing stage mask.

This bug probably comes from b5b425074e (git-read-tree: make one-way
merge also honor the "update" flag, 2005-06-07). Before this commit, we
may create the final ("dst") index entry from the one in index, but we
do clear CE_STAGEMASK.

I briefly checked two- and three-way merge functions. I think we don't
have the same problem in those.

Reported-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-03-21 12:06:28 +09:00

74 lines
1.9 KiB
Bash
Executable File

#!/bin/sh
test_description='checkout -m -- <conflicted path>
Ensures that checkout -m on a resolved file restores the conflicted file'
. ./test-lib.sh
test_expect_success setup '
test_tick &&
test_commit both.txt both.txt initial &&
git branch topic &&
test_commit modified_in_master both.txt in_master &&
test_commit added_in_master each.txt in_master &&
git checkout topic &&
test_commit modified_in_topic both.txt in_topic &&
test_commit added_in_topic each.txt in_topic
'
test_expect_success 'git merge master' '
test_must_fail git merge master
'
clean_branchnames () {
# Remove branch names after conflict lines
sed 's/^\([<>]\{5,\}\) .*$/\1/'
}
test_expect_success '-m restores 2-way conflicted+resolved file' '
cp each.txt each.txt.conflicted &&
echo resolved >each.txt &&
git add each.txt &&
git checkout -m -- each.txt &&
clean_branchnames <each.txt >each.txt.cleaned &&
clean_branchnames <each.txt.conflicted >each.txt.conflicted.cleaned &&
test_cmp each.txt.conflicted.cleaned each.txt.cleaned
'
test_expect_success '-m restores 3-way conflicted+resolved file' '
cp both.txt both.txt.conflicted &&
echo resolved >both.txt &&
git add both.txt &&
git checkout -m -- both.txt &&
clean_branchnames <both.txt >both.txt.cleaned &&
clean_branchnames <both.txt.conflicted >both.txt.conflicted.cleaned &&
test_cmp both.txt.conflicted.cleaned both.txt.cleaned
'
test_expect_success 'force checkout a conflict file creates stage zero entry' '
git init co-force &&
(
cd co-force &&
echo a >a &&
git add a &&
git commit -ama &&
A_OBJ=$(git rev-parse :a) &&
git branch topic &&
echo b >a &&
git commit -amb &&
B_OBJ=$(git rev-parse :a) &&
git checkout topic &&
echo c >a &&
C_OBJ=$(git hash-object a) &&
git checkout -m master &&
test_cmp_rev :1:a $A_OBJ &&
test_cmp_rev :2:a $B_OBJ &&
test_cmp_rev :3:a $C_OBJ &&
git checkout -f topic &&
test_cmp_rev :0:a $A_OBJ
)
'
test_done