git/t/t2030-unresolve-info.sh
Junio C Hamano 5a5ea141e7 revision: mark blobs needed for resolve-undo as reachable
The resolve-undo extension was added to the index in cfc5789a
(resolve-undo: record resolved conflicts in a new index extension
section, 2009-12-25).  This extension records the blob object names
and their modes of conflicted paths when the path gets resolved
(e.g. with "git add"), to allow "undoing" the resolution with
"checkout -m path".  These blob objects should be guarded from
garbage-collection while we have the resolve-undo information in the
index (otherwise unresolve operation may try to use a blob object
that has already been pruned away).

But the code called from mark_reachable_objects() for the index
forgets to do so.  Teach add_index_objects_to_pending() helper to
also add objects referred to by the resolve-undo extension.

Also make matching changes to "fsck", which has code that is fairly
similar to the reachability stuff, but have parallel implementations
for all these stuff, which may (or may not) someday want to be unified.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-09 16:45:07 -07:00

268 lines
7.1 KiB
Bash
Executable file

#!/bin/sh
test_description='undoing resolution'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
check_resolve_undo () {
msg=$1
shift
while case $# in
0) break ;;
1|2|3) die "Bug in check-resolve-undo test" ;;
esac
do
path=$1
shift
for stage in 1 2 3
do
sha1=$1
shift
case "$sha1" in
'') continue ;;
esac
sha1=$(git rev-parse --verify "$sha1")
printf "100644 %s %s\t%s\n" $sha1 $stage $path
done
done >"$msg.expect" &&
git ls-files --resolve-undo >"$msg.actual" &&
test_cmp "$msg.expect" "$msg.actual"
}
prime_resolve_undo () {
git reset --hard &&
git checkout second^0 &&
test_tick &&
test_must_fail git merge third^0 &&
echo merge does not leave anything &&
check_resolve_undo empty &&
echo different >fi/le &&
git add fi/le &&
echo resolving records &&
check_resolve_undo recorded fi/le initial:fi/le second:fi/le third:fi/le
}
test_expect_success setup '
mkdir fi &&
printf "a\0a" >binary &&
git add binary &&
test_commit initial fi/le first &&
git branch side &&
git branch another &&
printf "a\0b" >binary &&
git add binary &&
test_commit second fi/le second &&
git checkout side &&
test_commit third fi/le third &&
git branch add-add &&
git checkout another &&
test_commit fourth fi/le fourth &&
git checkout add-add &&
test_commit fifth add-differently &&
git checkout main
'
test_expect_success 'add records switch clears' '
prime_resolve_undo &&
test_tick &&
git commit -m merged &&
echo committing keeps &&
check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
git checkout second^0 &&
echo switching clears &&
check_resolve_undo cleared
'
test_expect_success 'rm records reset clears' '
prime_resolve_undo &&
test_tick &&
git commit -m merged &&
echo committing keeps &&
check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
echo merge clears upfront &&
test_must_fail git merge fourth^0 &&
check_resolve_undo nuked &&
git rm -f fi/le &&
echo resolving records &&
check_resolve_undo recorded fi/le initial:fi/le HEAD:fi/le fourth:fi/le &&
git reset --hard &&
echo resetting discards &&
check_resolve_undo discarded
'
test_expect_success 'plumbing clears' '
prime_resolve_undo &&
test_tick &&
git commit -m merged &&
echo committing keeps &&
check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
echo plumbing clear &&
git update-index --clear-resolve-undo &&
check_resolve_undo cleared
'
test_expect_success 'add records checkout -m undoes' '
prime_resolve_undo &&
git diff HEAD &&
git checkout --conflict=merge fi/le &&
echo checkout used the record and removed it &&
check_resolve_undo removed &&
echo the index and the work tree is unmerged again &&
git diff >actual &&
grep "^++<<<<<<<" actual
'
test_expect_success 'unmerge with plumbing' '
prime_resolve_undo &&
git update-index --unresolve fi/le &&
git ls-files -u >actual &&
test_line_count = 3 actual
'
test_expect_success 'rerere and rerere forget' '
mkdir .git/rr-cache &&
prime_resolve_undo &&
echo record the resolution &&
git rerere &&
rerere_id=$(cd .git/rr-cache && echo */postimage) &&
rerere_id=${rerere_id%/postimage} &&
test -f .git/rr-cache/$rerere_id/postimage &&
git checkout -m fi/le &&
echo resurrect the conflict &&
grep "^=======" fi/le &&
echo reresolve the conflict &&
git rerere &&
test "z$(cat fi/le)" = zdifferent &&
echo register the resolution again &&
git add fi/le &&
check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
test -z "$(git ls-files -u)" &&
git rerere forget fi/le &&
! test -f .git/rr-cache/$rerere_id/postimage &&
tr "\0" "\n" <.git/MERGE_RR >actual &&
echo "$rerere_id fi/le" >expect &&
test_cmp expect actual
'
test_expect_success 'rerere and rerere forget (subdirectory)' '
rm -fr .git/rr-cache &&
mkdir .git/rr-cache &&
prime_resolve_undo &&
echo record the resolution &&
(cd fi && git rerere) &&
rerere_id=$(cd .git/rr-cache && echo */postimage) &&
rerere_id=${rerere_id%/postimage} &&
test -f .git/rr-cache/$rerere_id/postimage &&
(cd fi && git checkout -m le) &&
echo resurrect the conflict &&
grep "^=======" fi/le &&
echo reresolve the conflict &&
(cd fi && git rerere) &&
test "z$(cat fi/le)" = zdifferent &&
echo register the resolution again &&
(cd fi && git add le) &&
check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
test -z "$(git ls-files -u)" &&
(cd fi && git rerere forget le) &&
! test -f .git/rr-cache/$rerere_id/postimage &&
tr "\0" "\n" <.git/MERGE_RR >actual &&
echo "$rerere_id fi/le" >expect &&
test_cmp expect actual
'
test_expect_success 'rerere forget (binary)' '
git checkout -f side &&
test_commit --printf binary binary "a\0c" &&
test_must_fail git merge second &&
git rerere forget binary
'
test_expect_success 'rerere forget (add-add conflict)' '
git checkout -f main &&
echo main >add-differently &&
git add add-differently &&
git commit -m "add differently" &&
test_must_fail git merge fifth &&
git rerere forget add-differently 2>actual &&
test_i18ngrep "no remembered" actual
'
test_expect_success 'resolve-undo keeps blobs from gc' '
git checkout -f main &&
# First make sure we do not have any cruft left in the object store
git repack -a -d &&
git prune --expire=now &&
git prune-packed &&
git gc --prune=now &&
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
# Now add three otherwise unreferenced blob objects to the index
git reset --hard &&
B1=$(echo "resolve undo test data 1" | git hash-object -w --stdin) &&
B2=$(echo "resolve undo test data 2" | git hash-object -w --stdin) &&
B3=$(echo "resolve undo test data 3" | git hash-object -w --stdin) &&
git update-index --add --index-info <<-EOF &&
100644 $B1 1 frotz
100644 $B2 2 frotz
100644 $B3 3 frotz
EOF
# These three blob objects are reachable (only) from the index
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
# and they should be protected from GC
git gc --prune=now &&
git cat-file -e $B1 &&
git cat-file -e $B2 &&
git cat-file -e $B3 &&
# Now resolve the conflicted path
B0=$(echo "resolve undo test data 0" | git hash-object -w --stdin) &&
git update-index --add --cacheinfo 100644,$B0,frotz &&
# These three blob objects are now reachable only from the resolve-undo
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
# and they should survive GC
git gc --prune=now &&
git cat-file -e $B0 &&
git cat-file -e $B1 &&
git cat-file -e $B2 &&
git cat-file -e $B3 &&
# Now we switch away, which nukes resolve-undo, and
# blobs B0..B3 would become dangling. fsck should
# notice that they are now unreachable.
git checkout -f side &&
git fsck --unreachable >cruft &&
sort cruft >actual &&
sort <<-EOF >expect &&
unreachable blob $B0
unreachable blob $B1
unreachable blob $B2
unreachable blob $B3
EOF
test_cmp expect actual &&
# And they should go away when gc runs.
git gc --prune=now &&
git fsck --unreachable >cruft &&
test_must_be_empty cruft &&
test_must_fail git cat-file -e $B0 &&
test_must_fail git cat-file -e $B1 &&
test_must_fail git cat-file -e $B2 &&
test_must_fail git cat-file -e $B3
'
test_done