diff options
Diffstat (limited to '')
-rwxr-xr-x | t/t4200-rerere.sh | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh new file mode 100755 index 0000000..fb53ddd --- /dev/null +++ b/t/t4200-rerere.sh @@ -0,0 +1,674 @@ +#!/bin/sh +# +# Copyright (c) 2006 Johannes E. Schindelin +# + +test_description='git rerere + +! [fifth] version1 + ! [first] first + ! [fourth] version1 + ! [main] initial + ! [second] prefer first over second + ! [third] version2 +------ + + [third] version2 ++ [fifth] version1 + + [fourth] version1 ++ + + [third^] third + - [second] prefer first over second + + + [first] first + + [second^] second +++++++ [main] initial +' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh + +test_expect_success 'setup' ' + cat >a1 <<-\EOF && + Some title + ========== + Whether '\''tis nobler in the mind to suffer + The slings and arrows of outrageous fortune, + Or to take arms against a sea of troubles, + And by opposing end them? To die: to sleep; + No more; and by a sleep to say we end + The heart-ache and the thousand natural shocks + That flesh is heir to, '\''tis a consummation + Devoutly to be wish'\''d. + EOF + + git add a1 && + test_tick && + git commit -q -a -m initial && + + cat >>a1 <<-\EOF && + Some title + ========== + To die, to sleep; + To sleep: perchance to dream: ay, there'\''s the rub; + For in that sleep of death what dreams may come + When we have shuffled off this mortal coil, + Must give us pause: there'\''s the respect + That makes calamity of so long life; + EOF + + git checkout -b first && + test_tick && + git commit -q -a -m first && + + git checkout -b second main && + git show first:a1 | + sed -e "s/To die, t/To die! T/" -e "s/Some title/Some Title/" >a1 && + echo "* END *" >>a1 && + test_tick && + git commit -q -a -m second +' + +test_expect_success 'nothing recorded without rerere' ' + rm -rf .git/rr-cache && + git config rerere.enabled false && + test_must_fail git merge first && + ! test -d .git/rr-cache +' + +test_expect_success 'activate rerere, old style (conflicting merge)' ' + git reset --hard && + mkdir .git/rr-cache && + test_might_fail git config --unset rerere.enabled && + test_must_fail git merge first && + + sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && + rr=.git/rr-cache/$sha1 && + grep "^=======\$" $rr/preimage && + ! test -f $rr/postimage && + ! test -f $rr/thisimage +' + +test_expect_success 'rerere.enabled works, too' ' + rm -rf .git/rr-cache && + git config rerere.enabled true && + git reset --hard && + test_must_fail git merge first && + + sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && + rr=.git/rr-cache/$sha1 && + grep ^=======$ $rr/preimage +' + +test_expect_success 'set up rr-cache' ' + rm -rf .git/rr-cache && + git config rerere.enabled true && + git reset --hard && + test_must_fail git merge first && + sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && + rr=.git/rr-cache/$sha1 +' + +test_expect_success 'rr-cache looks sane' ' + # no postimage or thisimage yet + ! test -f $rr/postimage && + ! test -f $rr/thisimage && + + # preimage has right number of lines + cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) && + echo $cnt && + test $cnt = 13 +' + +test_expect_success 'rerere diff' ' + git show first:a1 >a1 && + cat >expect <<-\EOF && + --- a/a1 + +++ b/a1 + @@ -1,4 +1,4 @@ + -Some Title + +Some title + ========== + Whether '\''tis nobler in the mind to suffer + The slings and arrows of outrageous fortune, + @@ -8,21 +8,11 @@ + The heart-ache and the thousand natural shocks + That flesh is heir to, '\''tis a consummation + Devoutly to be wish'\''d. + -<<<<<<< + -Some Title + -========== + -To die! To sleep; + -======= + Some title + ========== + To die, to sleep; + ->>>>>>> + To sleep: perchance to dream: ay, there'\''s the rub; + For in that sleep of death what dreams may come + When we have shuffled off this mortal coil, + Must give us pause: there'\''s the respect + That makes calamity of so long life; + -<<<<<<< + -======= + -* END * + ->>>>>>> + EOF + git rerere diff >out && + test_cmp expect out +' + +test_expect_success 'rerere status' ' + echo a1 >expect && + git rerere status >out && + test_cmp expect out +' + +test_expect_success 'first postimage wins' ' + git show first:a1 | sed "s/To die: t/To die! T/" >expect && + + git commit -q -a -m "prefer first over second" && + test -f $rr/postimage && + + oldmtimepost=$(test-tool chmtime --get -60 $rr/postimage) && + + git checkout -b third main && + git show second^:a1 | sed "s/To die: t/To die! T/" >a1 && + git commit -q -a -m third && + + test_must_fail git merge first && + # rerere kicked in + ! grep "^=======\$" a1 && + test_cmp expect a1 +' + +test_expect_success 'rerere updates postimage timestamp' ' + newmtimepost=$(test-tool chmtime --get $rr/postimage) && + test $oldmtimepost -lt $newmtimepost +' + +test_expect_success 'rerere clear' ' + mv $rr/postimage .git/post-saved && + echo "$sha1 a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR && + git rerere clear && + ! test -d $rr +' + +test_expect_success 'leftover directory' ' + git reset --hard && + mkdir -p $rr && + test_must_fail git merge first && + test -f $rr/preimage +' + +test_expect_success 'missing preimage' ' + git reset --hard && + mkdir -p $rr && + cp .git/post-saved $rr/postimage && + test_must_fail git merge first && + test -f $rr/preimage +' + +test_expect_success 'set up for garbage collection tests' ' + mkdir -p $rr && + echo Hello >$rr/preimage && + echo World >$rr/postimage && + + sha2=$(test_oid deadbeef) && + rr2=.git/rr-cache/$sha2 && + mkdir $rr2 && + echo Hello >$rr2/preimage && + + almost_15_days_ago=$((60-15*86400)) && + just_over_15_days_ago=$((-1-15*86400)) && + almost_60_days_ago=$((60-60*86400)) && + just_over_60_days_ago=$((-1-60*86400)) && + + test-tool chmtime =$just_over_60_days_ago $rr/preimage && + test-tool chmtime =$almost_60_days_ago $rr/postimage && + test-tool chmtime =$almost_15_days_ago $rr2/preimage +' + +test_expect_success 'gc preserves young or recently used records' ' + git rerere gc && + test -f $rr/preimage && + test -f $rr2/preimage +' + +test_expect_success 'old records rest in peace' ' + test-tool chmtime =$just_over_60_days_ago $rr/postimage && + test-tool chmtime =$just_over_15_days_ago $rr2/preimage && + git rerere gc && + ! test -f $rr/preimage && + ! test -f $rr2/preimage +' + +rerere_gc_custom_expiry_test () { + five_days="$1" right_now="$2" + test_expect_success "rerere gc with custom expiry ($five_days, $right_now)" ' + rm -fr .git/rr-cache && + rr=.git/rr-cache/$ZERO_OID && + mkdir -p "$rr" && + >"$rr/preimage" && + >"$rr/postimage" && + + two_days_ago=$((-2*86400)) && + test-tool chmtime =$two_days_ago "$rr/preimage" && + test-tool chmtime =$two_days_ago "$rr/postimage" && + + find .git/rr-cache -type f | sort >original && + + git -c "gc.rerereresolved=$five_days" \ + -c "gc.rerereunresolved=$five_days" rerere gc && + find .git/rr-cache -type f | sort >actual && + test_cmp original actual && + + git -c "gc.rerereresolved=$five_days" \ + -c "gc.rerereunresolved=$right_now" rerere gc && + find .git/rr-cache -type f | sort >actual && + test_cmp original actual && + + git -c "gc.rerereresolved=$right_now" \ + -c "gc.rerereunresolved=$right_now" rerere gc && + find .git/rr-cache -type f | sort >actual && + test_must_be_empty actual + ' +} + +rerere_gc_custom_expiry_test 5 0 + +rerere_gc_custom_expiry_test 5.days.ago now + +test_expect_success 'setup: file2 added differently in two branches' ' + git reset --hard && + + git checkout -b fourth && + echo Hallo >file2 && + git add file2 && + test_tick && + git commit -m version1 && + + git checkout third && + echo Bello >file2 && + git add file2 && + test_tick && + git commit -m version2 && + + test_must_fail git merge fourth && + echo Cello >file2 && + git add file2 && + git commit -m resolution +' + +test_expect_success 'resolution was recorded properly' ' + echo Cello >expected && + + git reset --hard HEAD~2 && + git checkout -b fifth && + + echo Hallo >file3 && + git add file3 && + test_tick && + git commit -m version1 && + + git checkout third && + echo Bello >file3 && + git add file3 && + test_tick && + git commit -m version2 && + git tag version2 && + + test_must_fail git merge fifth && + test_cmp expected file3 && + test_must_fail git update-index --refresh +' + +test_expect_success 'rerere.autoupdate' ' + git config rerere.autoupdate true && + git reset --hard && + git checkout version2 && + test_must_fail git merge fifth && + git update-index --refresh +' + +test_expect_success 'merge --rerere-autoupdate' ' + test_might_fail git config --unset rerere.autoupdate && + git reset --hard && + git checkout version2 && + test_must_fail git merge --rerere-autoupdate fifth && + git update-index --refresh +' + +test_expect_success 'merge --no-rerere-autoupdate' ' + headblob=$(git rev-parse version2:file3) && + mergeblob=$(git rev-parse fifth:file3) && + cat >expected <<-EOF && + 100644 $headblob 2 file3 + 100644 $mergeblob 3 file3 + EOF + + git config rerere.autoupdate true && + git reset --hard && + git checkout version2 && + test_must_fail git merge --no-rerere-autoupdate fifth && + git ls-files -u >actual && + test_cmp expected actual +' + +test_expect_success 'set up an unresolved merge' ' + headblob=$(git rev-parse version2:file3) && + mergeblob=$(git rev-parse fifth:file3) && + cat >expected.unresolved <<-EOF && + 100644 $headblob 2 file3 + 100644 $mergeblob 3 file3 + EOF + + test_might_fail git config --unset rerere.autoupdate && + git reset --hard && + git checkout version2 && + ancestor=$(git merge-base version2 fifth) && + test_must_fail git merge-recursive "$ancestor" -- HEAD fifth && + + git ls-files --stage >failedmerge && + cp file3 file3.conflict && + + git ls-files -u >actual && + test_cmp expected.unresolved actual +' + +test_expect_success 'explicit rerere' ' + test_might_fail git config --unset rerere.autoupdate && + git rm -fr --cached . && + git update-index --index-info <failedmerge && + cp file3.conflict file3 && + test_must_fail git update-index --refresh -q && + + git rerere && + git ls-files -u >actual && + test_cmp expected.unresolved actual +' + +test_expect_success 'explicit rerere with autoupdate' ' + git config rerere.autoupdate true && + git rm -fr --cached . && + git update-index --index-info <failedmerge && + cp file3.conflict file3 && + test_must_fail git update-index --refresh -q && + + git rerere && + git update-index --refresh +' + +test_expect_success 'explicit rerere --rerere-autoupdate overrides' ' + git config rerere.autoupdate false && + git rm -fr --cached . && + git update-index --index-info <failedmerge && + cp file3.conflict file3 && + git rerere && + git ls-files -u >actual1 && + + git rm -fr --cached . && + git update-index --index-info <failedmerge && + cp file3.conflict file3 && + git rerere --rerere-autoupdate && + git update-index --refresh && + + git rm -fr --cached . && + git update-index --index-info <failedmerge && + cp file3.conflict file3 && + git rerere --rerere-autoupdate --no-rerere-autoupdate && + git ls-files -u >actual2 && + + git rm -fr --cached . && + git update-index --index-info <failedmerge && + cp file3.conflict file3 && + git rerere --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate && + git update-index --refresh && + + test_cmp expected.unresolved actual1 && + test_cmp expected.unresolved actual2 +' + +test_expect_success 'rerere --no-no-rerere-autoupdate' ' + git rm -fr --cached . && + git update-index --index-info <failedmerge && + cp file3.conflict file3 && + test_must_fail git rerere --no-no-rerere-autoupdate 2>err && + test_grep [Uu]sage err && + test_must_fail git update-index --refresh +' + +test_expect_success 'rerere -h' ' + test_must_fail git rerere -h >help && + test_grep [Uu]sage help +' + +concat_insert () { + last=$1 + shift + cat early && printf "%s\n" "$@" && cat late "$last" +} + +count_pre_post () { + find .git/rr-cache/ -type f -name "preimage*" >actual && + test_line_count = "$1" actual && + find .git/rr-cache/ -type f -name "postimage*" >actual && + test_line_count = "$2" actual +} + +merge_conflict_resolve () { + git reset --hard && + test_must_fail git merge six.1 && + # Resolution is to replace 7 with 6.1 and 6.2 (i.e. take both) + concat_insert short 6.1 6.2 >file1 && + concat_insert long 6.1 6.2 >file2 +} + +test_expect_success 'multiple identical conflicts' ' + rm -fr .git/rr-cache && + mkdir .git/rr-cache && + git reset --hard && + + test_seq 1 6 >early && + >late && + test_seq 11 15 >short && + test_seq 111 120 >long && + concat_insert short >file1 && + concat_insert long >file2 && + git add file1 file2 && + git commit -m base && + git tag base && + git checkout -b six.1 && + concat_insert short 6.1 >file1 && + concat_insert long 6.1 >file2 && + git add file1 file2 && + git commit -m 6.1 && + git checkout -b six.2 HEAD^ && + concat_insert short 6.2 >file1 && + concat_insert long 6.2 >file2 && + git add file1 file2 && + git commit -m 6.2 && + + # At this point, six.1 and six.2 + # - derive from common ancestor that has two files + # 1...6 7 11..15 (file1) and 1...6 7 111..120 (file2) + # - six.1 replaces these 7s with 6.1 + # - six.2 replaces these 7s with 6.2 + + merge_conflict_resolve && + + # Check that rerere knows that file1 and file2 have conflicts + + printf "%s\n" file1 file2 >expect && + git ls-files -u | sed -e "s/^.* //" | sort -u >actual && + test_cmp expect actual && + + git rerere status | sort >actual && + test_cmp expect actual && + + git rerere remaining >actual && + test_cmp expect actual && + + count_pre_post 2 0 && + + # Pretend that the conflicts were made quite some time ago + test-tool chmtime -172800 $(find .git/rr-cache/ -type f) && + + # Unresolved entries have not expired yet + git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc && + count_pre_post 2 0 && + + # Unresolved entries have expired + git -c gc.rerereresolved=5 -c gc.rerereunresolved=1 rerere gc && + count_pre_post 0 0 && + + # Recreate the conflicted state + merge_conflict_resolve && + count_pre_post 2 0 && + + # Clear it + git rerere clear && + count_pre_post 0 0 && + + # Recreate the conflicted state + merge_conflict_resolve && + count_pre_post 2 0 && + + # We resolved file1 and file2 + git rerere && + git rerere remaining >actual && + test_must_be_empty actual && + + # We must have recorded both of them + count_pre_post 2 2 && + + # Now we should be able to resolve them both + git reset --hard && + test_must_fail git merge six.1 && + git rerere && + + git rerere remaining >actual && + test_must_be_empty actual && + + concat_insert short 6.1 6.2 >file1.expect && + concat_insert long 6.1 6.2 >file2.expect && + test_cmp file1.expect file1 && + test_cmp file2.expect file2 && + + # Forget resolution for file2 + git rerere forget file2 && + echo file2 >expect && + git rerere status >actual && + test_cmp expect actual && + count_pre_post 2 1 && + + # file2 already has correct resolution, so record it again + git rerere && + + # Pretend that the resolutions are old again + test-tool chmtime -172800 $(find .git/rr-cache/ -type f) && + + # Resolved entries have not expired yet + git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc && + + count_pre_post 2 2 && + + # Resolved entries have expired + git -c gc.rerereresolved=1 -c gc.rerereunresolved=5 rerere gc && + count_pre_post 0 0 +' + +test_expect_success 'rerere with unexpected conflict markers does not crash' ' + git reset --hard && + + git checkout -b branch-1 main && + echo "bar" >test && + git add test && + git commit -q -m two && + + git reset --hard && + git checkout -b branch-2 main && + echo "foo" >test && + git add test && + git commit -q -a -m one && + + test_must_fail git merge branch-1 && + echo "<<<<<<< a" >test && + git rerere && + + git rerere clear +' + +test_expect_success 'rerere with inner conflict markers' ' + git reset --hard && + + git checkout -b A main && + echo "bar" >test && + git add test && + git commit -q -m two && + echo "baz" >test && + git add test && + git commit -q -m three && + + git reset --hard && + git checkout -b B main && + echo "foo" >test && + git add test && + git commit -q -a -m one && + + test_must_fail git merge A~ && + git add test && + git commit -q -m "will solve conflicts later" && + test_must_fail git merge A && + + echo "resolved" >test && + git add test && + git commit -q -m "solved conflict" && + + echo "resolved" >expect && + + git reset --hard HEAD~~ && + test_must_fail git merge A~ && + git add test && + git commit -q -m "will solve conflicts later" && + test_must_fail git merge A && + cat test >actual && + test_cmp expect actual && + + git add test && + git commit -m "rerere solved conflict" && + git reset --hard HEAD~ && + test_must_fail git merge A && + cat test >actual && + test_cmp expect actual +' + +test_expect_success 'setup simple stage 1 handling' ' + test_create_repo stage_1_handling && + ( + cd stage_1_handling && + + test_seq 1 10 >original && + git add original && + git commit -m original && + + git checkout -b A main && + git mv original A && + git commit -m "rename to A" && + + git checkout -b B main && + git mv original B && + git commit -m "rename to B" + ) +' + +test_expect_success 'test simple stage 1 handling' ' + ( + cd stage_1_handling && + + git config rerere.enabled true && + git checkout A^0 && + test_must_fail git merge B^0 + ) +' + +test_done |