mirror of
https://github.com/git/git
synced 2024-08-25 10:45:59 +00:00
![SZEDER Gábor](/assets/img/avatar_default.png)
If a repository contains more than one root commit, then its HEAD reflog may contain multiple "creation events", i.e. entries whose "from" value is the null sha1. Listing such a reflog currently stops prematurely at the first such entry, even when the reflog still contains older entries. This can scare users into thinking that their reflog got truncated after 'git checkout --orphan'. Continue walking the reflog past such creation events based on the preceeding reflog entry's "new" value. The test 'symbolic-ref writes reflog entry' in t1401-symbolic-ref implicitly relies on the current behavior of the reflog walker to stop at a root commit and thus to list only the reflog entries that are relevant for that test. Adjust the test to explicitly specify the number of relevant reflog entries to be listed. Reported-by: Patrik Gustafsson <pvn@textalk.se> Signed-off-by: SZEDER Gábor <szeder@ira.uka.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
374 lines
8.8 KiB
Bash
Executable file
374 lines
8.8 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2007 Junio C Hamano
|
|
#
|
|
|
|
test_description='Test prune and reflog expiration'
|
|
. ./test-lib.sh
|
|
|
|
check_have () {
|
|
gaah= &&
|
|
for N in "$@"
|
|
do
|
|
eval "o=\$$N" && git cat-file -t $o || {
|
|
echo Gaah $N
|
|
gaah=$N
|
|
break
|
|
}
|
|
done &&
|
|
test -z "$gaah"
|
|
}
|
|
|
|
check_fsck () {
|
|
output=$(git fsck --full)
|
|
case "$1" in
|
|
'')
|
|
test -z "$output" ;;
|
|
*)
|
|
echo "$output" | grep "$1" ;;
|
|
esac
|
|
}
|
|
|
|
corrupt () {
|
|
aa=${1%??????????????????????????????????????} zz=${1#??}
|
|
mv .git/objects/$aa/$zz .git/$aa$zz
|
|
}
|
|
|
|
recover () {
|
|
aa=${1%??????????????????????????????????????} zz=${1#??}
|
|
mkdir -p .git/objects/$aa
|
|
mv .git/$aa$zz .git/objects/$aa/$zz
|
|
}
|
|
|
|
check_dont_have () {
|
|
gaah= &&
|
|
for N in "$@"
|
|
do
|
|
eval "o=\$$N"
|
|
git cat-file -t $o && {
|
|
echo Gaah $N
|
|
gaah=$N
|
|
break
|
|
}
|
|
done
|
|
test -z "$gaah"
|
|
}
|
|
|
|
test_expect_success setup '
|
|
mkdir -p A/B &&
|
|
echo rat >C &&
|
|
echo ox >A/D &&
|
|
echo tiger >A/B/E &&
|
|
git add . &&
|
|
|
|
test_tick && git commit -m rabbit &&
|
|
H=$(git rev-parse --verify HEAD) &&
|
|
A=$(git rev-parse --verify HEAD:A) &&
|
|
B=$(git rev-parse --verify HEAD:A/B) &&
|
|
C=$(git rev-parse --verify HEAD:C) &&
|
|
D=$(git rev-parse --verify HEAD:A/D) &&
|
|
E=$(git rev-parse --verify HEAD:A/B/E) &&
|
|
check_fsck &&
|
|
|
|
test_chmod +x C &&
|
|
git add C &&
|
|
test_tick && git commit -m dragon &&
|
|
L=$(git rev-parse --verify HEAD) &&
|
|
check_fsck &&
|
|
|
|
rm -f C A/B/E &&
|
|
echo snake >F &&
|
|
echo horse >A/G &&
|
|
git add F A/G &&
|
|
test_tick && git commit -a -m sheep &&
|
|
F=$(git rev-parse --verify HEAD:F) &&
|
|
G=$(git rev-parse --verify HEAD:A/G) &&
|
|
I=$(git rev-parse --verify HEAD:A) &&
|
|
J=$(git rev-parse --verify HEAD) &&
|
|
check_fsck &&
|
|
|
|
rm -f A/G &&
|
|
test_tick && git commit -a -m monkey &&
|
|
K=$(git rev-parse --verify HEAD) &&
|
|
check_fsck &&
|
|
|
|
check_have A B C D E F G H I J K L &&
|
|
|
|
git prune &&
|
|
|
|
check_have A B C D E F G H I J K L &&
|
|
|
|
check_fsck &&
|
|
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 4 output
|
|
'
|
|
|
|
test_expect_success rewind '
|
|
test_tick && git reset --hard HEAD~2 &&
|
|
test -f C &&
|
|
test -f A/B/E &&
|
|
! test -f F &&
|
|
! test -f A/G &&
|
|
|
|
check_have A B C D E F G H I J K L &&
|
|
|
|
git prune &&
|
|
|
|
check_have A B C D E F G H I J K L &&
|
|
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 5 output
|
|
'
|
|
|
|
test_expect_success 'corrupt and check' '
|
|
|
|
corrupt $F &&
|
|
check_fsck "missing blob $F"
|
|
|
|
'
|
|
|
|
test_expect_success 'reflog expire --dry-run should not touch reflog' '
|
|
|
|
git reflog expire --dry-run \
|
|
--expire=$(($test_tick - 10000)) \
|
|
--expire-unreachable=$(($test_tick - 10000)) \
|
|
--stale-fix \
|
|
--all &&
|
|
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 5 output &&
|
|
|
|
check_fsck "missing blob $F"
|
|
'
|
|
|
|
test_expect_success 'reflog expire' '
|
|
|
|
git reflog expire --verbose \
|
|
--expire=$(($test_tick - 10000)) \
|
|
--expire-unreachable=$(($test_tick - 10000)) \
|
|
--stale-fix \
|
|
--all &&
|
|
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 2 output &&
|
|
|
|
check_fsck "dangling commit $K"
|
|
'
|
|
|
|
test_expect_success 'prune and fsck' '
|
|
|
|
git prune &&
|
|
check_fsck &&
|
|
|
|
check_have A B C D E H L &&
|
|
check_dont_have F G I J K
|
|
|
|
'
|
|
|
|
test_expect_success 'recover and check' '
|
|
|
|
recover $F &&
|
|
check_fsck "dangling blob $F"
|
|
|
|
'
|
|
|
|
test_expect_success 'delete' '
|
|
echo 1 > C &&
|
|
test_tick &&
|
|
git commit -m rat C &&
|
|
|
|
echo 2 > C &&
|
|
test_tick &&
|
|
git commit -m ox C &&
|
|
|
|
echo 3 > C &&
|
|
test_tick &&
|
|
git commit -m tiger C &&
|
|
|
|
HEAD_entry_count=$(git reflog | wc -l) &&
|
|
master_entry_count=$(git reflog show master | wc -l) &&
|
|
|
|
test $HEAD_entry_count = 5 &&
|
|
test $master_entry_count = 5 &&
|
|
|
|
|
|
git reflog delete master@{1} &&
|
|
git reflog show master > output &&
|
|
test $(($master_entry_count - 1)) = $(wc -l < output) &&
|
|
test $HEAD_entry_count = $(git reflog | wc -l) &&
|
|
! grep ox < output &&
|
|
|
|
master_entry_count=$(wc -l < output) &&
|
|
|
|
git reflog delete HEAD@{1} &&
|
|
test $(($HEAD_entry_count -1)) = $(git reflog | wc -l) &&
|
|
test $master_entry_count = $(git reflog show master | wc -l) &&
|
|
|
|
HEAD_entry_count=$(git reflog | wc -l) &&
|
|
|
|
git reflog delete master@{07.04.2005.15:15:00.-0700} &&
|
|
git reflog show master > output &&
|
|
test $(($master_entry_count - 1)) = $(wc -l < output) &&
|
|
! grep dragon < output
|
|
|
|
'
|
|
|
|
test_expect_success 'rewind2' '
|
|
|
|
test_tick && git reset --hard HEAD~2 &&
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 4 output
|
|
'
|
|
|
|
test_expect_success '--expire=never' '
|
|
|
|
git reflog expire --verbose \
|
|
--expire=never \
|
|
--expire-unreachable=never \
|
|
--all &&
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 4 output
|
|
'
|
|
|
|
test_expect_success 'gc.reflogexpire=never' '
|
|
|
|
git config gc.reflogexpire never &&
|
|
git config gc.reflogexpireunreachable never &&
|
|
git reflog expire --verbose --all &&
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 4 output
|
|
'
|
|
|
|
test_expect_success 'gc.reflogexpire=false' '
|
|
|
|
git config gc.reflogexpire false &&
|
|
git config gc.reflogexpireunreachable false &&
|
|
git reflog expire --verbose --all &&
|
|
git reflog refs/heads/master >output &&
|
|
test_line_count = 4 output &&
|
|
|
|
git config --unset gc.reflogexpire &&
|
|
git config --unset gc.reflogexpireunreachable
|
|
|
|
'
|
|
|
|
test_expect_success 'checkout should not delete log for packed ref' '
|
|
test $(git reflog master | wc -l) = 4 &&
|
|
git branch foo &&
|
|
git pack-refs --all &&
|
|
git checkout foo &&
|
|
test $(git reflog master | wc -l) = 4
|
|
'
|
|
|
|
test_expect_success 'stale dirs do not cause d/f conflicts (reflogs on)' '
|
|
test_when_finished "git branch -d one || git branch -d one/two" &&
|
|
|
|
git branch one/two master &&
|
|
echo "one/two@{0} branch: Created from master" >expect &&
|
|
git log -g --format="%gd %gs" one/two >actual &&
|
|
test_cmp expect actual &&
|
|
git branch -d one/two &&
|
|
|
|
# now logs/refs/heads/one is a stale directory, but
|
|
# we should move it out of the way to create "one" reflog
|
|
git branch one master &&
|
|
echo "one@{0} branch: Created from master" >expect &&
|
|
git log -g --format="%gd %gs" one >actual &&
|
|
test_cmp expect actual
|
|
'
|
|
|
|
test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
|
|
test_when_finished "git branch -d one || git branch -d one/two" &&
|
|
|
|
git branch one/two master &&
|
|
echo "one/two@{0} branch: Created from master" >expect &&
|
|
git log -g --format="%gd %gs" one/two >actual &&
|
|
test_cmp expect actual &&
|
|
git branch -d one/two &&
|
|
|
|
# same as before, but we only create a reflog for "one" if
|
|
# it already exists, which it does not
|
|
git -c core.logallrefupdates=false branch one master &&
|
|
: >expect &&
|
|
git log -g --format="%gd %gs" one >actual &&
|
|
test_cmp expect actual
|
|
'
|
|
|
|
# Triggering the bug detected by this test requires a newline to fall
|
|
# exactly BUFSIZ-1 bytes from the end of the file. We don't know
|
|
# what that value is, since it's platform dependent. However, if
|
|
# we choose some value N, we also catch any D which divides N evenly
|
|
# (since we will read backwards in chunks of D). So we choose 8K,
|
|
# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
|
|
#
|
|
# Each line is 114 characters, so we need 75 to still have a few before the
|
|
# last 8K. The 89-character padding on the final entry lines up our
|
|
# newline exactly.
|
|
test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
|
|
git checkout -b reflogskip &&
|
|
z38=00000000000000000000000000000000000000 &&
|
|
ident="abc <xyz> 0000000001 +0000" &&
|
|
for i in $(test_seq 1 75); do
|
|
printf "$z38%02d $z38%02d %s\t" $i $(($i+1)) "$ident" &&
|
|
if test $i = 75; then
|
|
for j in $(test_seq 1 89); do
|
|
printf X
|
|
done
|
|
else
|
|
printf X
|
|
fi &&
|
|
printf "\n"
|
|
done >.git/logs/refs/heads/reflogskip &&
|
|
git rev-parse reflogskip@{73} >actual &&
|
|
echo ${z38}03 >expect &&
|
|
test_cmp expect actual
|
|
'
|
|
|
|
test_expect_success 'no segfaults for reflog containing non-commit sha1s' '
|
|
git update-ref --create-reflog -m "Creating ref" \
|
|
refs/tests/tree-in-reflog HEAD &&
|
|
git update-ref -m "Forcing tree" refs/tests/tree-in-reflog HEAD^{tree} &&
|
|
git update-ref -m "Restoring to commit" refs/tests/tree-in-reflog HEAD &&
|
|
git reflog refs/tests/tree-in-reflog
|
|
'
|
|
|
|
test_expect_failure 'reflog with non-commit entries displays all entries' '
|
|
git reflog refs/tests/tree-in-reflog >actual &&
|
|
test_line_count = 3 actual
|
|
'
|
|
|
|
test_expect_success 'reflog expire operates on symref not referrent' '
|
|
git branch -l the_symref &&
|
|
git branch -l referrent &&
|
|
git update-ref referrent HEAD &&
|
|
git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
|
|
test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
|
|
touch .git/refs/heads/referrent.lock &&
|
|
git reflog expire --expire=all the_symref
|
|
'
|
|
|
|
test_expect_success 'continue walking past root commits' '
|
|
git init orphanage &&
|
|
(
|
|
cd orphanage &&
|
|
cat >expect <<-\EOF &&
|
|
HEAD@{0} commit (initial): orphan2-1
|
|
HEAD@{1} commit: orphan1-2
|
|
HEAD@{2} commit (initial): orphan1-1
|
|
HEAD@{3} commit (initial): initial
|
|
EOF
|
|
test_commit initial &&
|
|
git reflog &&
|
|
git checkout --orphan orphan1 &&
|
|
test_commit orphan1-1 &&
|
|
test_commit orphan1-2 &&
|
|
git checkout --orphan orphan2 &&
|
|
test_commit orphan2-1 &&
|
|
git log -g --format="%gd %gs" >actual &&
|
|
test_cmp expect actual
|
|
)
|
|
'
|
|
|
|
test_done
|