diff options
Diffstat (limited to 't/t1006-cat-file.sh')
-rwxr-xr-x | t/t1006-cat-file.sh | 974 |
1 files changed, 840 insertions, 134 deletions
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 43c4be1..e12b221 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -4,96 +4,208 @@ test_description='git cat-file' . ./test-lib.sh +test_cmdmode_usage () { + test_expect_code 129 "$@" 2>err && + grep "^error: .* cannot be used together" err +} + +for switches in \ + '-e -p' \ + '-p -t' \ + '-t -s' \ + '-s --textconv' \ + '--textconv --filters' \ + '--batch-all-objects -e' +do + test_expect_success "usage: cmdmode $switches" ' + test_cmdmode_usage git cat-file $switches + ' +done + +test_incompatible_usage () { + test_expect_code 129 "$@" 2>err && + grep -E "^(fatal|error):.*(requires|incompatible with|needs)" err +} + +for opt in --batch --batch-check +do + test_expect_success "usage: incompatible options: --path with $opt" ' + test_incompatible_usage git cat-file --path=foo $opt + ' +done + +test_missing_usage () { + test_expect_code 129 "$@" 2>err && + grep -E "^fatal:.*required" err +} + +short_modes="-e -p -t -s" +cw_modes="--textconv --filters" + +for opt in $cw_modes +do + test_expect_success "usage: $opt requires another option" ' + test_missing_usage git cat-file $opt + ' +done + +for opt in $short_modes +do + test_expect_success "usage: $opt requires another option" ' + test_missing_usage git cat-file $opt + ' + + for opt2 in --batch \ + --batch-check \ + --follow-symlinks \ + "--path=foo HEAD:some-path.txt" + do + test_expect_success "usage: incompatible options: $opt and $opt2" ' + test_incompatible_usage git cat-file $opt $opt2 + ' + done +done + +test_too_many_arguments () { + test_expect_code 129 "$@" 2>err && + grep -E "^fatal: too many arguments$" err +} + +for opt in $short_modes $cw_modes +do + args="one two three" + test_expect_success "usage: too many arguments: $opt $args" ' + test_too_many_arguments git cat-file $opt $args + ' + + for opt2 in --buffer --follow-symlinks + do + test_expect_success "usage: incompatible arguments: $opt with batch option $opt2" ' + test_incompatible_usage git cat-file $opt $opt2 + ' + done +done + +for opt in --buffer \ + --follow-symlinks \ + --batch-all-objects \ + -z \ + -Z +do + test_expect_success "usage: bad option combination: $opt without batch mode" ' + test_incompatible_usage git cat-file $opt && + test_incompatible_usage git cat-file $opt commit HEAD + ' +done + echo_without_newline () { printf '%s' "$*" } -strlen () { - echo_without_newline "$1" | wc -c | sed -e 's/^ *//' +echo_without_newline_nul () { + echo_without_newline "$@" | tr '\n' '\0' } -maybe_remove_timestamp () { - if test -z "$2"; then - echo_without_newline "$1" - else - echo_without_newline "$(printf '%s\n' "$1" | sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//')" - fi +strlen () { + echo_without_newline "$1" | wc -c | sed -e 's/^ *//' } run_tests () { type=$1 - sha1=$2 + oid=$2 size=$3 content=$4 pretty_content=$5 - no_ts=$6 - batch_output="$sha1 $type $size + batch_output="$oid $type $size $content" test_expect_success "$type exists" ' - git cat-file -e $sha1 + git cat-file -e $oid ' test_expect_success "Type of $type is correct" ' echo $type >expect && - git cat-file -t $sha1 >actual && + git cat-file -t $oid >actual && test_cmp expect actual ' test_expect_success "Size of $type is correct" ' echo $size >expect && - git cat-file -s $sha1 >actual && + git cat-file -s $oid >actual && test_cmp expect actual ' test_expect_success "Type of $type is correct using --allow-unknown-type" ' echo $type >expect && - git cat-file -t --allow-unknown-type $sha1 >actual && + git cat-file -t --allow-unknown-type $oid >actual && test_cmp expect actual ' test_expect_success "Size of $type is correct using --allow-unknown-type" ' echo $size >expect && - git cat-file -s --allow-unknown-type $sha1 >actual && + git cat-file -s --allow-unknown-type $oid >actual && test_cmp expect actual ' test -z "$content" || test_expect_success "Content of $type is correct" ' - maybe_remove_timestamp "$content" $no_ts >expect && - maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual && + echo_without_newline "$content" >expect && + git cat-file $type $oid >actual && test_cmp expect actual ' test_expect_success "Pretty content of $type is correct" ' - maybe_remove_timestamp "$pretty_content" $no_ts >expect && - maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual && + echo_without_newline "$pretty_content" >expect && + git cat-file -p $oid >actual && test_cmp expect actual ' test -z "$content" || test_expect_success "--batch output of $type is correct" ' - maybe_remove_timestamp "$batch_output" $no_ts >expect && - maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual && + echo "$batch_output" >expect && + echo $oid | git cat-file --batch >actual && test_cmp expect actual ' test_expect_success "--batch-check output of $type is correct" ' - echo "$sha1 $type $size" >expect && - echo_without_newline $sha1 | git cat-file --batch-check >actual && + echo "$oid $type $size" >expect && + echo_without_newline $oid | git cat-file --batch-check >actual && test_cmp expect actual ' + for opt in --buffer --no-buffer + do + test -z "$content" || + test_expect_success "--batch-command $opt output of $type content is correct" ' + echo "$batch_output" >expect && + test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual && + test_cmp expect actual + ' + + test_expect_success "--batch-command $opt output of $type info is correct" ' + echo "$oid $type $size" >expect && + test_write_lines "info $oid" | + git cat-file --batch-command $opt >actual && + test_cmp expect actual + ' + done + test_expect_success "custom --batch-check format" ' - echo "$type $sha1" >expect && - echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && + echo "$type $oid" >expect && + echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && + test_cmp expect actual + ' + + test_expect_success "custom --batch-command format" ' + echo "$type $oid" >expect && + echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual && test_cmp expect actual ' test_expect_success '--batch-check with %(rest)' ' echo "$type this is some extra content" >expect && - echo "$sha1 this is some extra content" | + echo "$oid this is some extra content" | git cat-file --batch-check="%(objecttype) %(rest)" >actual && test_cmp expect actual ' @@ -102,10 +214,9 @@ $content" test_expect_success "--batch without type ($type)" ' { echo "$size" && - maybe_remove_timestamp "$content" $no_ts + echo "$content" } >expect && - echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full && - maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual && + echo $oid | git cat-file --batch="%(objectsize)" >actual && test_cmp expect actual ' @@ -113,128 +224,200 @@ $content" test_expect_success "--batch without size ($type)" ' { echo "$type" && - maybe_remove_timestamp "$content" $no_ts + echo "$content" } >expect && - echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full && - maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual && + echo $oid | git cat-file --batch="%(objecttype)" >actual && test_cmp expect actual ' } hello_content="Hello World" hello_size=$(strlen "$hello_content") -hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin) +hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin) test_expect_success "setup" ' + git config core.repositoryformatversion 1 && + git config extensions.objectformat $test_hash_algo && + git config extensions.compatobjectformat $test_compat_hash_algo && echo_without_newline "$hello_content" > hello && git update-index --add hello ' -run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content" +run_blob_tests () { + oid=$1 + + run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content" + + test_expect_success '--batch-command --buffer with flush for blob info' ' + echo "$oid blob $hello_size" >expect && + test_write_lines "info $oid" "flush" | + GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \ + git cat-file --batch-command --buffer >actual && + test_cmp expect actual + ' + + test_expect_success '--batch-command --buffer without flush for blob info' ' + touch output && + test_write_lines "info $oid" | + GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \ + git cat-file --batch-command --buffer >>output && + test_must_be_empty output + ' +} + +hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid) +run_blob_tests $hello_oid +run_blob_tests $hello_compat_oid test_expect_success '--batch-check without %(rest) considers whole line' ' - echo "$hello_sha1 blob $hello_size" >expect && - git update-index --add --cacheinfo 100644 $hello_sha1 "white space" && + echo "$hello_oid blob $hello_size" >expect && + git update-index --add --cacheinfo 100644 $hello_oid "white space" && test_when_finished "git update-index --remove \"white space\"" && echo ":white space" | git cat-file --batch-check >actual && test_cmp expect actual ' -test_oid_init - -tree_sha1=$(git write-tree) +tree_oid=$(git write-tree) +tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid) tree_size=$(($(test_oid rawsz) + 13)) -tree_pretty_content="100644 blob $hello_sha1 hello" +tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13)) +tree_pretty_content="100644 blob $hello_oid hello${LF}" +tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}" -run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content" +run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content" +run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content" commit_message="Initial commit" -commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1) +commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid) +commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid) commit_size=$(($(test_oid hexsz) + 137)) -commit_content="tree $tree_sha1 -author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000 -committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000 +commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137)) +commit_content="tree $tree_oid +author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + +$commit_message" + +commit_compat_content="tree $tree_compat_oid +author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE $commit_message" -run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content" 1 +run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content" +run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content" -tag_header_without_timestamp="object $hello_sha1 -type blob +tag_header_without_oid="type blob tag hellotag tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" +tag_header_without_timestamp="object $hello_oid +$tag_header_without_oid" +tag_compat_header_without_timestamp="object $hello_compat_oid +$tag_header_without_oid" tag_description="This is a tag" -tag_content="$tag_header_without_timestamp 0000000000 +0000 +tag_content="$tag_header_without_timestamp 0 +0000 $tag_description" +tag_compat_content="$tag_compat_header_without_timestamp 0 +0000 -tag_sha1=$(echo_without_newline "$tag_content" | git mktag) +$tag_description" + +tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w) tag_size=$(strlen "$tag_content") -run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1 +tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid) +tag_compat_size=$(strlen "$tag_compat_content") -test_expect_success \ - "Reach a blob from a tag pointing to it" \ - "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\"" +run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content" +run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content" + +test_expect_success "Reach a blob from a tag pointing to it" ' + echo_without_newline "$hello_content" >expect && + git cat-file blob $tag_oid >actual && + test_cmp expect actual +' -for batch in batch batch-check +for oid in $hello_oid $hello_compat_oid do - for opt in t s e p + for batch in batch batch-check batch-command do + for opt in t s e p + do test_expect_success "Passing -$opt with --$batch fails" ' - test_must_fail git cat-file --$batch -$opt $hello_sha1 + test_must_fail git cat-file --$batch -$opt $oid ' test_expect_success "Passing --$batch with -$opt fails" ' - test_must_fail git cat-file -$opt --$batch $hello_sha1 + test_must_fail git cat-file -$opt --$batch $oid ' - done + done - test_expect_success "Passing <type> with --$batch fails" ' - test_must_fail git cat-file --$batch blob $hello_sha1 - ' + test_expect_success "Passing <type> with --$batch fails" ' + test_must_fail git cat-file --$batch blob $oid + ' - test_expect_success "Passing --$batch with <type> fails" ' - test_must_fail git cat-file blob --$batch $hello_sha1 - ' + test_expect_success "Passing --$batch with <type> fails" ' + test_must_fail git cat-file blob --$batch $oid + ' - test_expect_success "Passing sha1 with --$batch fails" ' - test_must_fail git cat-file --$batch $hello_sha1 - ' + test_expect_success "Passing oid with --$batch fails" ' + test_must_fail git cat-file --$batch $oid + ' + done done -for opt in t s e p +for oid in $hello_oid $hello_compat_oid do - test_expect_success "Passing -$opt with --follow-symlinks fails" ' - test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1 + for opt in t s e p + do + test_expect_success "Passing -$opt with --follow-symlinks fails" ' + test_must_fail git cat-file --follow-symlinks -$opt $oid ' + done done test_expect_success "--batch-check for a non-existent named object" ' - test "foobar42 missing -foobar84 missing" = \ - "$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)" + cat >expect <<-EOF && + foobar42 missing + foobar84 missing + EOF + + printf "foobar42\nfoobar84" >in && + git cat-file --batch-check <in >actual && + test_cmp expect actual ' test_expect_success "--batch-check for a non-existent hash" ' - test "0000000000000000000000000000000000000042 missing -0000000000000000000000000000000000000084 missing" = \ - "$( ( echo 0000000000000000000000000000000000000042; - echo_without_newline 0000000000000000000000000000000000000084; ) | - git cat-file --batch-check)" + cat >expect <<-EOF && + 0000000000000000000000000000000000000042 missing + 0000000000000000000000000000000000000084 missing + EOF + + printf "0000000000000000000000000000000000000042\n0000000000000000000000000000000000000084" >in && + git cat-file --batch-check <in >actual && + test_cmp expect actual ' test_expect_success "--batch for an existent and a non-existent hash" ' - test "$tag_sha1 tag $tag_size -$tag_content -0000000000000000000000000000000000000000 missing" = \ - "$( ( echo $tag_sha1; - echo_without_newline 0000000000000000000000000000000000000000; ) | - git cat-file --batch)" + cat >expect <<-EOF && + $tag_oid tag $tag_size + $tag_content + 0000000000000000000000000000000000000000 missing + EOF + + printf "$tag_oid\n0000000000000000000000000000000000000000" >in && + git cat-file --batch <in >actual && + test_cmp expect actual ' test_expect_success "--batch-check for an empty line" ' - test " missing" = "$(echo | git cat-file --batch-check)" + cat >expect <<-EOF && + missing + EOF + + echo >in && + git cat-file --batch-check <in >actual && + test_cmp expect actual ' test_expect_success 'empty --batch-check notices missing object' ' @@ -243,49 +426,185 @@ test_expect_success 'empty --batch-check notices missing object' ' test_cmp expect actual ' -batch_input="$hello_sha1 -$commit_sha1 -$tag_sha1 +batch_tests () { + boid=$1 + loid=$2 + lsize=$3 + coid=$4 + csize=$5 + ccontent=$6 + toid=$7 + tsize=$8 + tcontent=$9 + + batch_input="$boid +$coid +$toid deadbeef " -batch_output="$hello_sha1 blob $hello_size -$hello_content -$commit_sha1 commit $commit_size -$commit_content -$tag_sha1 tag $tag_size -$tag_content -deadbeef missing - missing" + printf "%s\0" \ + "$boid blob $hello_size" \ + "$hello_content" \ + "$coid commit $csize" \ + "$ccontent" \ + "$toid tag $tsize" \ + "$tcontent" \ + "deadbeef missing" \ + " missing" >batch_output + + test_expect_success '--batch with multiple oids gives correct format' ' + tr "\0" "\n" <batch_output >expect && + echo_without_newline "$batch_input" >in && + git cat-file --batch <in >actual && + test_cmp expect actual + ' -test_expect_success '--batch with multiple sha1s gives correct format' ' - test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)" -' + test_expect_success '--batch, -z with multiple oids gives correct format' ' + echo_without_newline_nul "$batch_input" >in && + tr "\0" "\n" <batch_output >expect && + git cat-file --batch -z <in >actual && + test_cmp expect actual + ' -batch_check_input="$hello_sha1 -$tree_sha1 -$commit_sha1 -$tag_sha1 + test_expect_success '--batch, -Z with multiple oids gives correct format' ' + echo_without_newline_nul "$batch_input" >in && + git cat-file --batch -Z <in >actual && + test_cmp batch_output actual + ' + +batch_check_input="$boid +$loid +$coid +$toid deadbeef " -batch_check_output="$hello_sha1 blob $hello_size -$tree_sha1 tree $tree_size -$commit_sha1 commit $commit_size -$tag_sha1 tag $tag_size -deadbeef missing - missing" + printf "%s\0" \ + "$boid blob $hello_size" \ + "$loid tree $lsize" \ + "$coid commit $csize" \ + "$toid tag $tsize" \ + "deadbeef missing" \ + " missing" >batch_check_output + + test_expect_success "--batch-check with multiple oids gives correct format" ' + tr "\0" "\n" <batch_check_output >expect && + echo_without_newline "$batch_check_input" >in && + git cat-file --batch-check <in >actual && + test_cmp expect actual + ' + + test_expect_success "--batch-check, -z with multiple oids gives correct format" ' + tr "\0" "\n" <batch_check_output >expect && + echo_without_newline_nul "$batch_check_input" >in && + git cat-file --batch-check -z <in >actual && + test_cmp expect actual + ' -test_expect_success "--batch-check with multiple sha1s gives correct format" ' - test "$batch_check_output" = \ - "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)" + test_expect_success "--batch-check, -Z with multiple oids gives correct format" ' + echo_without_newline_nul "$batch_check_input" >in && + git cat-file --batch-check -Z <in >actual && + test_cmp batch_check_output actual + ' + +batch_command_multiple_info="info $boid +info $loid +info $coid +info $toid +info deadbeef" + + test_expect_success '--batch-command with multiple info calls gives correct format' ' + cat >expect <<-EOF && + $boid blob $hello_size + $loid tree $lsize + $coid commit $csize + $toid tag $tsize + deadbeef missing + EOF + + echo "$batch_command_multiple_info" >in && + git cat-file --batch-command --buffer <in >actual && + + test_cmp expect actual && + + echo "$batch_command_multiple_info" | tr "\n" "\0" >in && + git cat-file --batch-command --buffer -z <in >actual && + + test_cmp expect actual && + + echo "$batch_command_multiple_info" | tr "\n" "\0" >in && + tr "\n" "\0" <expect >expect_nul && + git cat-file --batch-command --buffer -Z <in >actual && + + test_cmp expect_nul actual + ' + +batch_command_multiple_contents="contents $boid +contents $coid +contents $toid +contents deadbeef +flush" + + test_expect_success '--batch-command with multiple command calls gives correct format' ' + printf "%s\0" \ + "$boid blob $hello_size" \ + "$hello_content" \ + "$coid commit $csize" \ + "$ccontent" \ + "$toid tag $tsize" \ + "$tcontent" \ + "deadbeef missing" >expect_nul && + tr "\0" "\n" <expect_nul >expect && + + echo "$batch_command_multiple_contents" >in && + git cat-file --batch-command --buffer <in >actual && + + test_cmp expect actual && + + echo "$batch_command_multiple_contents" | tr "\n" "\0" >in && + git cat-file --batch-command --buffer -z <in >actual && + + test_cmp expect actual && + + echo "$batch_command_multiple_contents" | tr "\n" "\0" >in && + git cat-file --batch-command --buffer -Z <in >actual && + + test_cmp expect_nul actual + ' + +} + +batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content" +batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content" + + +test_expect_success FUNNYNAMES 'setup with newline in input' ' + touch -- "newline${LF}embedded" && + git add -- "newline${LF}embedded" && + git commit -m "file with newline embedded" && + test_tick && + + printf "HEAD:newline${LF}embedded" >in +' + +test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' ' + git cat-file --batch-check -z <in >actual && + echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect && + test_cmp expect actual +' + +test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' ' + git cat-file --batch-check -Z <in >actual && + printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect && + test_cmp expect actual ' test_expect_success 'setup blobs which are likely to delta' ' test-tool genrandom foo 10240 >foo && - { cat foo; echo plus; } >foo-plus && + { cat foo && echo plus; } >foo-plus && git add foo foo-plus && git commit -m foo && cat >blobs <<-\EOF @@ -307,7 +626,7 @@ test_expect_success 'confirm that neither loose blob is a delta' ' # we will check only that one of the two objects is a delta # against the other, but not the order. We can do so by just # asking for the base of both, and checking whether either -# sha1 appears in the output. +# oid appears in the output. test_expect_success '%(deltabase) reports packed delta bases' ' git repack -ad && git cat-file --batch-check="%(deltabase)" <blobs >actual && @@ -317,39 +636,244 @@ test_expect_success '%(deltabase) reports packed delta bases' ' } ' -bogus_type="bogus" -bogus_content="bogus" -bogus_size=$(strlen "$bogus_content") -bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin) +test_expect_success 'setup bogus data' ' + bogus_short_type="bogus" && + bogus_short_content="bogus" && + bogus_short_size=$(strlen "$bogus_short_content") && + bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) && + + bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" && + bogus_long_content="bogus" && + bogus_long_size=$(strlen "$bogus_long_content") && + bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin) +' + +for arg1 in '' --allow-unknown-type +do + for arg2 in -s -t -p + do + if test "$arg1" = "--allow-unknown-type" && test "$arg2" = "-p" + then + continue + fi + + + test_expect_success "cat-file $arg1 $arg2 error on bogus short OID" ' + cat >expect <<-\EOF && + fatal: invalid object type + EOF + + if test "$arg1" = "--allow-unknown-type" + then + git cat-file $arg1 $arg2 $bogus_short_oid + else + test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual && + test_must_be_empty out && + test_cmp expect actual + fi + ' + + test_expect_success "cat-file $arg1 $arg2 error on bogus full OID" ' + if test "$arg2" = "-p" + then + cat >expect <<-EOF + error: header for $bogus_long_oid too long, exceeds 32 bytes + fatal: Not a valid object name $bogus_long_oid + EOF + else + cat >expect <<-EOF + error: header for $bogus_long_oid too long, exceeds 32 bytes + fatal: git cat-file: could not get object info + EOF + fi && + + if test "$arg1" = "--allow-unknown-type" + then + git cat-file $arg1 $arg2 $bogus_short_oid + else + test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual && + test_must_be_empty out && + test_cmp expect actual + fi + ' + + test_expect_success "cat-file $arg1 $arg2 error on missing short OID" ' + cat >expect.err <<-EOF && + fatal: Not a valid object name $(test_oid deadbeef_short) + EOF + test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual && + test_must_be_empty out && + test_cmp expect.err err.actual + ' + + test_expect_success "cat-file $arg1 $arg2 error on missing full OID" ' + if test "$arg2" = "-p" + then + cat >expect.err <<-EOF + fatal: Not a valid object name $(test_oid deadbeef) + EOF + else + cat >expect.err <<-\EOF + fatal: git cat-file: could not get object info + EOF + fi && + test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef) >out 2>err.actual && + test_must_be_empty out && + test_cmp expect.err err.actual + ' + done +done + +test_expect_success '-e is OK with a broken object without --allow-unknown-type' ' + git cat-file -e $bogus_short_oid +' + +test_expect_success '-e can not be combined with --allow-unknown-type' ' + test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_oid +' + +test_expect_success '-p cannot print a broken object even with --allow-unknown-type' ' + test_must_fail git cat-file -p $bogus_short_oid && + test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid +' + +test_expect_success '<type> <hash> does not work with objects of broken types' ' + cat >err.expect <<-\EOF && + fatal: invalid object type "bogus" + EOF + test_must_fail git cat-file $bogus_short_type $bogus_short_oid 2>err.actual && + test_cmp err.expect err.actual +' + +test_expect_success 'broken types combined with --batch and --batch-check' ' + echo $bogus_short_oid >bogus-oid && + + cat >err.expect <<-\EOF && + fatal: invalid object type + EOF + + test_must_fail git cat-file --batch <bogus-oid 2>err.actual && + test_cmp err.expect err.actual && + + test_must_fail git cat-file --batch-check <bogus-oid 2>err.actual && + test_cmp err.expect err.actual +' + +test_expect_success 'the --batch and --batch-check options do not combine with --allow-unknown-type' ' + test_expect_code 128 git cat-file --batch --allow-unknown-type <bogus-oid && + test_expect_code 128 git cat-file --batch-check --allow-unknown-type <bogus-oid +' + +test_expect_success 'the --allow-unknown-type option does not consider replacement refs' ' + cat >expect <<-EOF && + $bogus_short_type + EOF + git cat-file -t --allow-unknown-type $bogus_short_oid >actual && + test_cmp expect actual && + + # Create it manually, as "git replace" will die on bogus + # types. + head=$(git rev-parse --verify HEAD) && + test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" && + test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION && + + cat >expect <<-EOF && + commit + EOF + git cat-file -t --allow-unknown-type $bogus_short_oid >actual && + test_cmp expect actual +' test_expect_success "Type of broken object is correct" ' - echo $bogus_type >expect && - git cat-file -t --allow-unknown-type $bogus_sha1 >actual && + echo $bogus_short_type >expect && + git cat-file -t --allow-unknown-type $bogus_short_oid >actual && test_cmp expect actual ' test_expect_success "Size of broken object is correct" ' - echo $bogus_size >expect && - git cat-file -s --allow-unknown-type $bogus_sha1 >actual && + echo $bogus_short_size >expect && + git cat-file -s --allow-unknown-type $bogus_short_oid >actual && test_cmp expect actual ' -bogus_type="abcdefghijklmnopqrstuvwxyz1234679" -bogus_content="bogus" -bogus_size=$(strlen "$bogus_content") -bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin) + +test_expect_success 'clean up broken object' ' + rm .git/objects/$(test_oid_to_path $bogus_short_oid) +' test_expect_success "Type of broken object is correct when type is large" ' - echo $bogus_type >expect && - git cat-file -t --allow-unknown-type $bogus_sha1 >actual && + echo $bogus_long_type >expect && + git cat-file -t --allow-unknown-type $bogus_long_oid >actual && test_cmp expect actual ' test_expect_success "Size of large broken object is correct when type is large" ' - echo $bogus_size >expect && - git cat-file -s --allow-unknown-type $bogus_sha1 >actual && + echo $bogus_long_size >expect && + git cat-file -s --allow-unknown-type $bogus_long_oid >actual && test_cmp expect actual ' +test_expect_success 'clean up broken object' ' + rm .git/objects/$(test_oid_to_path $bogus_long_oid) +' + +test_expect_success 'cat-file -t and -s on corrupt loose object' ' + git init --bare corrupt-loose.git && + ( + cd corrupt-loose.git && + + # Setup and create the empty blob and its path + empty_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$EMPTY_BLOB")) && + empty_blob=$(git hash-object -w --stdin </dev/null) && + + # Create another blob and its path + echo other >other.blob && + other_blob=$(git hash-object -w --stdin <other.blob) && + other_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$other_blob")) && + + # Before the swap the size is 0 + cat >out.expect <<-EOF && + 0 + EOF + git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual && + test_must_be_empty err.actual && + test_cmp out.expect out.actual && + + # Swap the two to corrupt the repository + mv -f "$other_path" "$empty_path" && + test_must_fail git fsck 2>err.fsck && + grep "hash-path mismatch" err.fsck && + + # confirm that cat-file is reading the new swapped-in + # blob... + cat >out.expect <<-EOF && + blob + EOF + git cat-file -t "$EMPTY_BLOB" >out.actual 2>err.actual && + test_must_be_empty err.actual && + test_cmp out.expect out.actual && + + # ... since it has a different size now. + cat >out.expect <<-EOF && + 6 + EOF + git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual && + test_must_be_empty err.actual && + test_cmp out.expect out.actual && + + # So far "cat-file" has been happy to spew the found + # content out as-is. Try to make it zlib-invalid. + mv -f other.blob "$empty_path" && + test_must_fail git fsck 2>err.fsck && + cat >expect <<-EOF && + error: inflate: data stream error (incorrect header check) + error: unable to unpack header of ./$empty_path + error: $empty_blob: object corrupt or missing: ./$empty_path + EOF + grep "^error: " err.fsck >actual && + test_cmp expect actual + ) +' + # Tests for git cat-file --follow-symlinks test_expect_success 'prep for symlink tests' ' echo_without_newline "$hello_content" >morx && @@ -386,7 +910,7 @@ test_expect_success 'prep for symlink tests' ' test_ln_s_add loop2 loop1 && git add morx dir/subdir/ind2 dir/ind1 && git commit -am "test" && - echo $hello_sha1 blob $hello_size >found + echo $hello_oid blob $hello_size >found ' test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' ' @@ -415,6 +939,13 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for brok test_cmp expect actual ' +test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for broken in-repo, same-dir links' ' + printf "HEAD:broken-same-dir-link\0" >in && + printf "dangling 25\0HEAD:broken-same-dir-link\0" >expect && + git cat-file --batch-check --follow-symlinks -Z <in >actual && + test_cmp expect actual +' + test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' ' echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual && test_cmp found actual @@ -429,6 +960,15 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for pare test_cmp expect actual ' +test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for parent-dir links' ' + echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual && + test_cmp found actual && + printf "notdir 29\0HEAD:dir/parent-dir-link/nope\0" >expect && + printf "HEAD:dir/parent-dir-link/nope\0" >in && + git cat-file --batch-check --follow-symlinks -Z <in >actual && + test_cmp expect actual +' + test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' ' echo dangling 22 >expect && echo HEAD:dir/link-dir/nope >>expect && @@ -458,7 +998,7 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ echo HEAD:dirlink/morx >>expect && echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual && test_cmp expect actual && - echo $hello_sha1 blob $hello_size >expect && + echo $hello_oid blob $hello_size >expect && echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual && test_cmp expect actual ' @@ -543,6 +1083,13 @@ test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' ' test_cmp expect actual ' +test_expect_success 'git cat-file --batch-check --follow-symlink -Z breaks loops' ' + printf "loop 10\0HEAD:loop1\0" >expect && + printf "HEAD:loop1\0" >in && + git cat-file --batch-check --follow-symlinks -Z <in >actual && + test_cmp expect actual +' + test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' ' echo HEAD:morx | git cat-file --batch >expect && echo HEAD:morx | git cat-file --batch --follow-symlinks >actual && @@ -588,4 +1135,163 @@ test_expect_success 'cat-file --unordered works' ' test_cmp expect actual ' +test_expect_success 'set up object list for --batch-all-objects tests' ' + git -C all-two cat-file --batch-all-objects --batch-check="%(objectname)" >objects +' + +test_expect_success 'cat-file --batch="%(objectname)" with --batch-all-objects will work' ' + git -C all-two cat-file --batch="%(objectname)" <objects >expect && + git -C all-two cat-file --batch-all-objects --batch="%(objectname)" >actual && + cmp expect actual +' + +test_expect_success 'cat-file --batch="%(rest)" with --batch-all-objects will work' ' + git -C all-two cat-file --batch="%(rest)" <objects >expect && + git -C all-two cat-file --batch-all-objects --batch="%(rest)" >actual && + cmp expect actual +' + +test_expect_success 'cat-file --batch="batman" with --batch-all-objects will work' ' + git -C all-two cat-file --batch="batman" <objects >expect && + git -C all-two cat-file --batch-all-objects --batch="batman" >actual && + cmp expect actual +' + +test_expect_success 'cat-file %(objectsize:disk) with --batch-all-objects' ' + # our state has both loose and packed objects, + # so find both for our expected output + { + find .git/objects/?? -type f | + awk -F/ "{ print \$0, \$3\$4 }" | + while read path oid + do + size=$(test_file_size "$path") && + echo "$oid $size" || + return 1 + done && + rawsz=$(test_oid rawsz) && + find .git/objects/pack -name "*.idx" | + while read idx + do + git show-index <"$idx" >idx.raw && + sort -nr <idx.raw >idx.sorted && + packsz=$(test_file_size "${idx%.idx}.pack") && + end=$((packsz - rawsz)) && + while read start oid rest + do + size=$((end - start)) && + end=$start && + echo "$oid $size" || + return 1 + done <idx.sorted || + return 1 + done + } >expect.raw && + sort <expect.raw >expect && + git cat-file --batch-all-objects \ + --batch-check="%(objectname) %(objectsize:disk)" >actual && + test_cmp expect actual +' + +test_expect_success 'set up replacement object' ' + orig=$(git rev-parse HEAD) && + git cat-file commit $orig >orig && + { + cat orig && + echo extra + } >fake && + fake=$(git hash-object -t commit -w fake) && + orig_size=$(git cat-file -s $orig) && + fake_size=$(git cat-file -s $fake) && + git replace $orig $fake +' + +test_expect_success 'cat-file --batch respects replace objects' ' + git cat-file --batch >actual <<-EOF && + $orig + EOF + { + echo "$orig commit $fake_size" && + cat fake && + echo + } >expect && + test_cmp expect actual +' + +test_expect_success 'cat-file --batch-check respects replace objects' ' + git cat-file --batch-check >actual <<-EOF && + $orig + EOF + echo "$orig commit $fake_size" >expect && + test_cmp expect actual +' + +# Pull the entry for object with oid "$1" out of the output of +# "cat-file --batch", including its object content (which requires +# parsing and reading a set amount of bytes, hence perl). +extract_batch_output () { + perl -ne ' + BEGIN { $oid = shift } + if (/^$oid \S+ (\d+)$/) { + print; + read STDIN, my $buf, $1; + print $buf; + print "\n"; + } + ' "$@" +} + +test_expect_success 'cat-file --batch-all-objects --batch ignores replace' ' + git cat-file --batch-all-objects --batch >actual.raw && + extract_batch_output $orig <actual.raw >actual && + { + echo "$orig commit $orig_size" && + cat orig && + echo + } >expect && + test_cmp expect actual +' + +test_expect_success 'cat-file --batch-all-objects --batch-check ignores replace' ' + git cat-file --batch-all-objects --batch-check >actual.raw && + grep ^$orig actual.raw >actual && + echo "$orig commit $orig_size" >expect && + test_cmp expect actual +' +test_expect_success 'batch-command empty command' ' + echo "" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*empty command in input.*" err +' + +test_expect_success 'batch-command whitespace before command' ' + echo " info deadbeef" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*whitespace before command.*" err +' + +test_expect_success 'batch-command unknown command' ' + echo unknown_command >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*unknown command.*" err +' + +test_expect_success 'batch-command missing arguments' ' + echo "info" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*info requires arguments.*" err +' + +test_expect_success 'batch-command flush with arguments' ' + echo "flush arg" >cmd && + test_expect_code 128 git cat-file --batch-command --buffer <cmd 2>err && + grep "^fatal:.*flush takes no arguments.*" err +' + +test_expect_success 'batch-command flush without --buffer' ' + echo "flush" >cmd && + test_expect_code 128 git cat-file --batch-command <cmd 2>err && + grep "^fatal:.*flush is only for --buffer mode.*" err +' + test_done |