#!/bin/sh global test_description := ''git fsck random collection of tests * (HEAD) B * (master) A '' source ./test-lib.sh test_expect_success setup ' git config gc.auto 0 && git config i18n.commitencoding ISO-8859-1 && test_commit A fileA one && git config --unset i18n.commitencoding && git checkout HEAD^0 && test_commit B fileB two && git tag -d A B && git reflog expire --expire=now --all && >empty ' test_expect_success 'loose objects borrowed from alternate are not missing' ' mkdir another && ( cd another && git init && echo ../../../.git/objects >.git/objects/info/alternates && test_commit C fileC one && git fsck --no-dangling >../actual 2>&1 ) && test_cmp empty actual ' test_expect_success 'HEAD is part of refs, valid objects appear valid' ' git fsck >actual 2>&1 && test_cmp empty actual ' # Corruption tests follow. Make sure to remove all traces of the # specific corruption you test afterwards, lest a later test trip over # it. test_expect_success 'setup: helpers for corruption tests' ' sha1_file() { echo "$*" | sed "s#..#.git/objects/&/#" } && remove_object() { file=$(sha1_file "$*") && test -e "$file" && rm -f "$file" } ' test_expect_success 'object with bad sha1' ' sha=$(echo blob | git hash-object -w --stdin) && old=$(echo $sha | sed "s+^..+&/+") && new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff && sha="$(dirname $new)$(basename $new)" && mv .git/objects/$old .git/objects/$new && test_when_finished "remove_object $sha" && git update-index --add --cacheinfo 100644 $sha foo && test_when_finished "git read-tree -u --reset HEAD" && tree=$(git write-tree) && test_when_finished "remove_object $tree" && cmt=$(echo bogus | git commit-tree $tree) && test_when_finished "remove_object $cmt" && git update-ref refs/heads/bogus $cmt && test_when_finished "git update-ref -d refs/heads/bogus" && test_must_fail git fsck 2>out && cat out && grep "$sha.*corrupt" out ' test_expect_success 'branch pointing to non-commit' ' git rev-parse HEAD^{tree} >.git/refs/heads/invalid && test_when_finished "git update-ref -d refs/heads/invalid" && test_must_fail git fsck 2>out && cat out && grep "not a commit" out ' test_expect_success 'HEAD link pointing at a funny object' ' test_when_finished "mv .git/SAVED_HEAD .git/HEAD" && mv .git/HEAD .git/SAVED_HEAD && echo 0000000000000000000000000000000000000000 >.git/HEAD && # avoid corrupt/broken HEAD from interfering with repo discovery test_must_fail env GIT_DIR=.git git fsck 2>out && cat out && grep "detached HEAD points" out ' test_expect_success 'HEAD link pointing at a funny place' ' test_when_finished "mv .git/SAVED_HEAD .git/HEAD" && mv .git/HEAD .git/SAVED_HEAD && echo "ref: refs/funny/place" >.git/HEAD && # avoid corrupt/broken HEAD from interfering with repo discovery test_must_fail env GIT_DIR=.git git fsck 2>out && cat out && grep "HEAD points to something strange" out ' test_expect_success 'email without @ is okay' ' git cat-file commit HEAD >basis && sed "s/@/AT/" basis >okay && new=$(git hash-object -t commit -w --stdin out && cat out && ! grep "commit $new" out ' test_expect_success 'email with embedded > is not okay' ' git cat-file commit HEAD >basis && sed "s/@[a-z]/&>/" basis >bad-email && new=$(git hash-object -t commit -w --stdin out && cat out && grep "error in commit $new" out ' test_expect_success 'missing < email delimiter is reported nicely' ' git cat-file commit HEAD >basis && sed "s/bad-email-2 && new=$(git hash-object -t commit -w --stdin out && cat out && grep "error in commit $new.* - bad name" out ' test_expect_success 'missing email is reported nicely' ' git cat-file commit HEAD >basis && sed "s/[a-z]* <[^>]*>//" basis >bad-email-3 && new=$(git hash-object -t commit -w --stdin out && cat out && grep "error in commit $new.* - missing email" out ' test_expect_success '> in name is reported' ' git cat-file commit HEAD >basis && sed "s/ bad-email-4 && new=$(git hash-object -t commit -w --stdin out && cat out && grep "error in commit $new" out ' # date is 2^64 + 1 test_expect_success 'integer overflow in timestamps is reported' ' git cat-file commit HEAD >basis && sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \ bad-timestamp && new=$(git hash-object -t commit -w --stdin out && cat out && grep "error in commit $new.*integer overflow" out ' test_expect_success 'commit with NUL in header' ' git cat-file commit HEAD >basis && sed "s/author ./author Q/" commit-NUL-header && new=$(git hash-object -t commit -w --stdin out && cat out && grep "error in commit $new.*unterminated header: NUL at offset" out ' test_expect_success 'tree object with duplicate entries' ' test_when_finished "remove_object \$T" && T=$( GIT_INDEX_FILE=test-index && export GIT_INDEX_FILE && rm -f test-index && >x && git add x && T=$(git write-tree) && ( git cat-file tree $T && git cat-file tree $T ) | git hash-object -w -t tree --stdin ) && test_must_fail git fsck 2>out && grep "error in tree .*contains duplicate file entries" out ' test_expect_success 'unparseable tree object' ' test_when_finished "git update-ref -d refs/heads/wrong" && test_when_finished "remove_object \$tree_sha1" && test_when_finished "remove_object \$commit_sha1" && tree_sha1=$(printf "100644 \0twenty-bytes-of-junk" | git hash-object -t tree --stdin -w --literally) && commit_sha1=$(git commit-tree $tree_sha1) && git update-ref refs/heads/wrong $commit_sha1 && test_must_fail git fsck 2>out && test_i18ngrep "error: empty filename in tree entry" out && test_i18ngrep "$tree_sha1" out && test_i18ngrep ! "fatal: empty filename in tree entry" out ' test_expect_success 'tag pointing to nonexistent' ' cat >invalid-tag <<-\EOF && object ffffffffffffffffffffffffffffffffffffffff type commit tag invalid tagger T A Gger 1234567890 -0000 This is an invalid tag. EOF tag=$(git hash-object -t tag -w --stdin .git/refs/tags/invalid && test_when_finished "git update-ref -d refs/tags/invalid" && test_must_fail git fsck --tags >out && cat out && grep "broken link" out ' test_expect_success 'tag pointing to something else than its type' ' sha=$(echo blob | git hash-object -w --stdin) && test_when_finished "remove_object $sha" && cat >wrong-tag <<-EOF && object $sha type commit tag wrong tagger T A Gger 1234567890 -0000 This is an invalid tag. EOF tag=$(git hash-object -t tag -w --stdin .git/refs/tags/wrong && test_when_finished "git update-ref -d refs/tags/wrong" && test_must_fail git fsck --tags ' test_expect_success 'tag with incorrect tag name & missing tagger' ' sha=$(git rev-parse HEAD) && cat >wrong-tag <<-EOF && object $sha type commit tag wrong name format This is an invalid tag. EOF tag=$(git hash-object -t tag -w --stdin .git/refs/tags/wrong && test_when_finished "git update-ref -d refs/tags/wrong" && git fsck --tags 2>out && cat >expect <<-EOF && warning in tag $tag: badTagName: invalid '''''tag''''' name: wrong name format warning in tag $tag: missingTaggerEntry: invalid format - expected '''''tagger''''' line EOF test_cmp expect out ' test_expect_success 'tag with bad tagger' ' sha=$(git rev-parse HEAD) && cat >wrong-tag <<-EOF && object $sha type commit tag not-quite-wrong tagger Bad Tagger Name This is an invalid tag. EOF tag=$(git hash-object --literally -t tag -w --stdin .git/refs/tags/wrong && test_when_finished "git update-ref -d refs/tags/wrong" && test_must_fail git fsck --tags 2>out && grep "error in tag .*: invalid author/committer" out ' test_expect_success 'tag with NUL in header' ' sha=$(git rev-parse HEAD) && q_to_nul >tag-NUL-header <<-EOF && object $sha type commit tag contains-Q-in-header tagger T A Gger 1234567890 -0000 This is an invalid tag. EOF tag=$(git hash-object --literally -t tag -w --stdin .git/refs/tags/wrong && test_when_finished "git update-ref -d refs/tags/wrong" && test_must_fail git fsck --tags 2>out && cat out && grep "error in tag $tag.*unterminated header: NUL at offset" out ' test_expect_success 'cleaned up' ' git fsck >actual 2>&1 && test_cmp empty actual ' test_expect_success 'rev-list --verify-objects' ' git rev-list --verify-objects --all >/dev/null 2>out && test_cmp empty out ' test_expect_success 'rev-list --verify-objects with bad sha1' ' sha=$(echo blob | git hash-object -w --stdin) && old=$(echo $sha | sed "s+^..+&/+") && new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff && sha="$(dirname $new)$(basename $new)" && mv .git/objects/$old .git/objects/$new && test_when_finished "remove_object $sha" && git update-index --add --cacheinfo 100644 $sha foo && test_when_finished "git read-tree -u --reset HEAD" && tree=$(git write-tree) && test_when_finished "remove_object $tree" && cmt=$(echo bogus | git commit-tree $tree) && test_when_finished "remove_object $cmt" && git update-ref refs/heads/bogus $cmt && test_when_finished "git update-ref -d refs/heads/bogus" && test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out && cat out && grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out ' test_expect_success 'force fsck to ignore double author' ' git cat-file commit HEAD >basis && sed "s/^author .*/&,&/" multiple-authors && new=$(git hash-object -t commit -w --stdin out && cat out && grep "warning.*null sha1" out ) ' test_expect_success 'fsck notices submodule entry pointing to null sha1' ' (git init null-commit && cd null-commit && sha=$(printf "160000 submodule$_bz$_bz20" | git hash-object -w --stdin -t tree) && git fsck 2>out && cat out && grep "warning.*null sha1" out ) ' while read name path pretty { while read mode type { : $(pretty:=$path) test_expect_success "fsck notices $pretty as $type" ' ( git init $name-$type && cd $name-$type && echo content >file && git add file && git commit -m base && blob=$(git rev-parse :file) && tree=$(git rev-parse HEAD^{tree}) && value=$(eval "echo \$$type") && printf "$mode $type %s\t%s" "$value" "$path" >bad && bad_tree=$(git mktree out && cat out && grep "warning.*tree $bad_tree" out )' } <<-\EOF } <<-EOF dot . dotdot .. dotgit .git dotgit-case .GIT dotgit-unicode .gI${u200c}T .gI{u200c}T dotgit-case2 .Git git-tilde1 git~1 dotgitdot .git. dot-backslash-case .\\\\.GIT\\\\foobar dotgit-case-backslash .git\\\\foobar test_expect_success 'fsck allows .Ňit' ' ( git init not-dotgit && cd not-dotgit && echo content >file && git add file && git commit -m base && blob=$(git rev-parse :file) && printf "100644 blob $blob\t.\\305\\207it" >tree && tree=$(git mktree err && test_line_count = 0 err ) ' test_expect_success 'NUL in commit' ' rm -fr nul-in-commit && git init nul-in-commit && ( cd nul-in-commit && git commit --allow-empty -m "initial commitQNUL after message" && git cat-file commit HEAD >original && q_to_nul munged && git hash-object -w -t commit --stdin name && git branch bad $(cat name) && test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 && grep nulInCommit warn.1 && git fsck 2>warn.2 && grep nulInCommit warn.2 ) ' # create a static test repo which is broken by omitting # one particular object ($1, which is looked up via rev-parse # in the new repository). proc create_repo_missing { rm -rf missing && git init missing && shell { cd missing && git commit -m one --allow-empty && mkdir subdir && echo content >subdir/file && git add subdir/file && git commit -m two && global unrelated := $[echo unrelated | git hash-object --stdin -w] && git tag -m foo tag $unrelated && global sha1 := $[git rev-parse --verify $1] && global path := $[echo $sha1 | sed 's|..|&/|] && rm .git/objects/$path } } test_expect_success 'fsck notices missing blob' ' create_repo_missing HEAD:subdir/file && test_must_fail git -C missing fsck ' test_expect_success 'fsck notices missing subtree' ' create_repo_missing HEAD:subdir && test_must_fail git -C missing fsck ' test_expect_success 'fsck notices missing root tree' ' create_repo_missing HEAD^{tree} && test_must_fail git -C missing fsck ' test_expect_success 'fsck notices missing parent' ' create_repo_missing HEAD^ && test_must_fail git -C missing fsck ' test_expect_success 'fsck notices missing tagged object' ' create_repo_missing tag^{blob} && test_must_fail git -C missing fsck ' test_expect_success 'fsck notices ref pointing to missing commit' ' create_repo_missing HEAD && test_must_fail git -C missing fsck ' test_expect_success 'fsck notices ref pointing to missing tag' ' create_repo_missing tag && test_must_fail git -C missing fsck ' test_expect_success 'fsck --connectivity-only' ' rm -rf connectivity-only && git init connectivity-only && ( cd connectivity-only && touch empty && git add empty && test_commit empty && empty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 && rm -f $empty && echo invalid >$empty && test_must_fail git fsck --strict && git fsck --strict --connectivity-only && tree=$(git rev-parse HEAD:) && suffix=${tree#??} && tree=.git/objects/${tree%$suffix}/$suffix && rm -f $tree && echo invalid >$tree && test_must_fail git fsck --strict --connectivity-only ) ' proc remove_loose_object { global sha1 := $[git rev-parse $1] && global remainder := $(sha1#??) && global firsttwo := $(sha1%$remainder) && rm .git/objects/$firsttwo/$remainder } test_expect_success 'fsck --name-objects' ' rm -rf name-objects && git init name-objects && ( cd name-objects && test_commit julius caesar.t && test_commit augustus && test_commit caesar && remove_loose_object $(git rev-parse julius:caesar.t) && test_must_fail git fsck --name-objects >out && tree=$(git rev-parse --verify julius:) && grep "$tree (\(refs/heads/master\|HEAD\)@{[0-9]*}:" out ) ' test_done (CommandList children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:test_description) op: Equal rhs: { (SQ <"git fsck random collection of tests\n"> <"\n"> <"* (HEAD) B\n"> <"* (master) A\n">) } spids: [4] ) ] spids: [4] ) (C {(.)} {(./test-lib.sh)}) (C {(test_expect_success)} {(setup)} { (SQ <"\n"> <"\tgit config gc.auto 0 &&\n"> <"\tgit config i18n.commitencoding ISO-8859-1 &&\n"> <"\ttest_commit A fileA one &&\n"> <"\tgit config --unset i18n.commitencoding &&\n"> <"\tgit checkout HEAD^0 &&\n"> <"\ttest_commit B fileB two &&\n"> <"\tgit tag -d A B &&\n"> <"\tgit reflog expire --expire=now --all &&\n"> <"\t>empty\n"> ) } ) (C {(test_expect_success)} {(SQ <"loose objects borrowed from alternate are not missing">)} { (SQ <"\n"> <"\tmkdir another &&\n"> <"\t(\n"> <"\t\tcd another &&\n"> <"\t\tgit init &&\n"> <"\t\techo ../../../.git/objects >.git/objects/info/alternates &&\n"> <"\t\ttest_commit C fileC one &&\n"> <"\t\tgit fsck --no-dangling >../actual 2>&1\n"> <"\t) &&\n"> <"\ttest_cmp empty actual\n"> ) } ) (C {(test_expect_success)} {(SQ <"HEAD is part of refs, valid objects appear valid">)} {(SQ <"\n"> <"\tgit fsck >actual 2>&1 &&\n"> <"\ttest_cmp empty actual\n">)} ) (C {(test_expect_success)} {(SQ <"setup: helpers for corruption tests">)} { (SQ <"\n"> <"\tsha1_file() {\n"> <"\t\techo \"$*\" | sed \"s#..#.git/objects/&/#\"\n"> <"\t} &&\n"> <"\n"> <"\tremove_object() {\n"> <"\t\tfile=$(sha1_file \"$*\") &&\n"> <"\t\ttest -e \"$file\" &&\n"> <"\t\trm -f \"$file\"\n"> <"\t}\n"> ) } ) (C {(test_expect_success)} {(SQ <"object with bad sha1">)} { (SQ <"\n"> <"\tsha=$(echo blob | git hash-object -w --stdin) &&\n"> <"\told=$(echo $sha | sed \"s+^..+&/+\") &&\n"> <"\tnew=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&\n"> <"\tsha=\"$(dirname $new)$(basename $new)\" &&\n"> <"\tmv .git/objects/$old .git/objects/$new &&\n"> <"\ttest_when_finished \"remove_object $sha\" &&\n"> <"\tgit update-index --add --cacheinfo 100644 $sha foo &&\n"> <"\ttest_when_finished \"git read-tree -u --reset HEAD\" &&\n"> <"\ttree=$(git write-tree) &&\n"> <"\ttest_when_finished \"remove_object $tree\" &&\n"> <"\tcmt=$(echo bogus | git commit-tree $tree) &&\n"> <"\ttest_when_finished \"remove_object $cmt\" &&\n"> <"\tgit update-ref refs/heads/bogus $cmt &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"$sha.*corrupt\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"branch pointing to non-commit">)} { (SQ <"\n"> <"\tgit rev-parse HEAD^{tree} >.git/refs/heads/invalid &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/invalid\" &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"not a commit\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"HEAD link pointing at a funny object">)} { (SQ <"\n"> <"\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n"> <"\tmv .git/HEAD .git/SAVED_HEAD &&\n"> <"\techo 0000000000000000000000000000000000000000 >.git/HEAD &&\n"> <"\t# avoid corrupt/broken HEAD from interfering with repo discovery\n"> <"\ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"detached HEAD points\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"HEAD link pointing at a funny place">)} { (SQ <"\n"> <"\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n"> <"\tmv .git/HEAD .git/SAVED_HEAD &&\n"> <"\techo \"ref: refs/funny/place\" >.git/HEAD &&\n"> <"\t# avoid corrupt/broken HEAD from interfering with repo discovery\n"> <"\ttest_must_fail env GIT_DIR=.git git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"HEAD points to something strange\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"email without @ is okay">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/@/AT/\" basis >okay &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\tgit fsck 2>out &&\n"> <"\tcat out &&\n"> <"\t! grep \"commit $new\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"email with embedded > is not okay">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/@[a-z]/&>/\" basis >bad-email &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"error in commit $new\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"missing < email delimiter is reported nicely">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/bad-email-2 &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"error in commit $new.* - bad name\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"missing email is reported nicely">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/[a-z]* <[^>]*>//\" basis >bad-email-3 &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"error in commit $new.* - missing email\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"> in name is reported">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/ bad-email-4 &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"error in commit $new\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"integer overflow in timestamps is reported">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/^\\\\(author .*>\\\\) [0-9]*/\\\\1 18446744073709551617/\" \\\n"> <"\t\tbad-timestamp &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"error in commit $new.*integer overflow\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"commit with NUL in header">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/author ./author Q/\" commit-NUL-header &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"error in commit $new.*unterminated header: NUL at offset\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"tree object with duplicate entries">)} { (SQ <"\n"> <"\ttest_when_finished \"remove_object \\$T\" &&\n"> <"\tT=$(\n"> <"\t\tGIT_INDEX_FILE=test-index &&\n"> <"\t\texport GIT_INDEX_FILE &&\n"> <"\t\trm -f test-index &&\n"> <"\t\t>x &&\n"> <"\t\tgit add x &&\n"> <"\t\tT=$(git write-tree) &&\n"> <"\t\t(\n"> <"\t\t\tgit cat-file tree $T &&\n"> <"\t\t\tgit cat-file tree $T\n"> <"\t\t) |\n"> <"\t\tgit hash-object -w -t tree --stdin\n"> <"\t) &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\tgrep \"error in tree .*contains duplicate file entries\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"unparseable tree object">)} { (SQ <"\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/wrong\" &&\n"> <"\ttest_when_finished \"remove_object \\$tree_sha1\" &&\n"> <"\ttest_when_finished \"remove_object \\$commit_sha1\" &&\n"> < "\ttree_sha1=$(printf \"100644 \\0twenty-bytes-of-junk\" | git hash-object -t tree --stdin -w --literally) &&\n" > <"\tcommit_sha1=$(git commit-tree $tree_sha1) &&\n"> <"\tgit update-ref refs/heads/wrong $commit_sha1 &&\n"> <"\ttest_must_fail git fsck 2>out &&\n"> <"\ttest_i18ngrep \"error: empty filename in tree entry\" out &&\n"> <"\ttest_i18ngrep \"$tree_sha1\" out &&\n"> <"\ttest_i18ngrep ! \"fatal: empty filename in tree entry\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"tag pointing to nonexistent">)} { (SQ <"\n"> <"\tcat >invalid-tag <<-\\EOF &&\n"> <"\tobject ffffffffffffffffffffffffffffffffffffffff\n"> <"\ttype commit\n"> <"\ttag invalid\n"> <"\ttagger T A Gger 1234567890 -0000\n"> <"\n"> <"\tThis is an invalid tag.\n"> <"\tEOF\n"> <"\n"> <"\ttag=$(git hash-object -t tag -w --stdin <"\ttest_when_finished \"remove_object $tag\" &&\n"> <"\techo $tag >.git/refs/tags/invalid &&\n"> <"\ttest_when_finished \"git update-ref -d refs/tags/invalid\" &&\n"> <"\ttest_must_fail git fsck --tags >out &&\n"> <"\tcat out &&\n"> <"\tgrep \"broken link\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"tag pointing to something else than its type">)} { (SQ <"\n"> <"\tsha=$(echo blob | git hash-object -w --stdin) &&\n"> <"\ttest_when_finished \"remove_object $sha\" &&\n"> <"\tcat >wrong-tag <<-EOF &&\n"> <"\tobject $sha\n"> <"\ttype commit\n"> <"\ttag wrong\n"> <"\ttagger T A Gger 1234567890 -0000\n"> <"\n"> <"\tThis is an invalid tag.\n"> <"\tEOF\n"> <"\n"> <"\ttag=$(git hash-object -t tag -w --stdin <"\ttest_when_finished \"remove_object $tag\" &&\n"> <"\techo $tag >.git/refs/tags/wrong &&\n"> <"\ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n"> <"\ttest_must_fail git fsck --tags\n"> ) } ) (C {(test_expect_success)} {(SQ <"tag with incorrect tag name & missing tagger">)} { (SQ <"\n"> <"\tsha=$(git rev-parse HEAD) &&\n"> <"\tcat >wrong-tag <<-EOF &&\n"> <"\tobject $sha\n"> <"\ttype commit\n"> <"\ttag wrong name format\n"> <"\n"> <"\tThis is an invalid tag.\n"> <"\tEOF\n"> <"\n"> <"\ttag=$(git hash-object -t tag -w --stdin <"\ttest_when_finished \"remove_object $tag\" &&\n"> <"\techo $tag >.git/refs/tags/wrong &&\n"> <"\ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n"> <"\tgit fsck --tags 2>out &&\n"> <"\n"> <"\tcat >expect <<-EOF &&\n"> <"\twarning in tag $tag: badTagName: invalid "> ) (EscapedLiteralPart token:) (SQ ) (EscapedLiteralPart token:) (SQ <" name: wrong name format\n"> <"\twarning in tag $tag: missingTaggerEntry: invalid format - expected "> ) (EscapedLiteralPart token:) (SQ ) (EscapedLiteralPart token:) (SQ <" line\n"> <"\tEOF\n"> <"\ttest_cmp expect out\n">) } ) (C {(test_expect_success)} {(SQ <"tag with bad tagger">)} { (SQ <"\n"> <"\tsha=$(git rev-parse HEAD) &&\n"> <"\tcat >wrong-tag <<-EOF &&\n"> <"\tobject $sha\n"> <"\ttype commit\n"> <"\ttag not-quite-wrong\n"> <"\ttagger Bad Tagger Name\n"> <"\n"> <"\tThis is an invalid tag.\n"> <"\tEOF\n"> <"\n"> <"\ttag=$(git hash-object --literally -t tag -w --stdin <"\ttest_when_finished \"remove_object $tag\" &&\n"> <"\techo $tag >.git/refs/tags/wrong &&\n"> <"\ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n"> <"\ttest_must_fail git fsck --tags 2>out &&\n"> <"\tgrep \"error in tag .*: invalid author/committer\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"tag with NUL in header">)} { (SQ <"\n"> <"\tsha=$(git rev-parse HEAD) &&\n"> <"\tq_to_nul >tag-NUL-header <<-EOF &&\n"> <"\tobject $sha\n"> <"\ttype commit\n"> <"\ttag contains-Q-in-header\n"> <"\ttagger T A Gger 1234567890 -0000\n"> <"\n"> <"\tThis is an invalid tag.\n"> <"\tEOF\n"> <"\n"> <"\ttag=$(git hash-object --literally -t tag -w --stdin <"\ttest_when_finished \"remove_object $tag\" &&\n"> <"\techo $tag >.git/refs/tags/wrong &&\n"> <"\ttest_when_finished \"git update-ref -d refs/tags/wrong\" &&\n"> <"\ttest_must_fail git fsck --tags 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep \"error in tag $tag.*unterminated header: NUL at offset\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"cleaned up">)} {(SQ <"\n"> <"\tgit fsck >actual 2>&1 &&\n"> <"\ttest_cmp empty actual\n">)} ) (C {(test_expect_success)} {(SQ <"rev-list --verify-objects">)} { (SQ <"\n"> <"\tgit rev-list --verify-objects --all >/dev/null 2>out &&\n"> <"\ttest_cmp empty out\n"> ) } ) (C {(test_expect_success)} {(SQ <"rev-list --verify-objects with bad sha1">)} { (SQ <"\n"> <"\tsha=$(echo blob | git hash-object -w --stdin) &&\n"> <"\told=$(echo $sha | sed \"s+^..+&/+\") &&\n"> <"\tnew=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&\n"> <"\tsha=\"$(dirname $new)$(basename $new)\" &&\n"> <"\tmv .git/objects/$old .git/objects/$new &&\n"> <"\ttest_when_finished \"remove_object $sha\" &&\n"> <"\tgit update-index --add --cacheinfo 100644 $sha foo &&\n"> <"\ttest_when_finished \"git read-tree -u --reset HEAD\" &&\n"> <"\ttree=$(git write-tree) &&\n"> <"\ttest_when_finished \"remove_object $tree\" &&\n"> <"\tcmt=$(echo bogus | git commit-tree $tree) &&\n"> <"\ttest_when_finished \"remove_object $cmt\" &&\n"> <"\tgit update-ref refs/heads/bogus $cmt &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\n"> <"\ttest_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&\n"> <"\tcat out &&\n"> <"\tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n"> ) } ) (C {(test_expect_success)} {(SQ <"force fsck to ignore double author">)} { (SQ <"\n"> <"\tgit cat-file commit HEAD >basis &&\n"> <"\tsed \"s/^author .*/&,&/\" multiple-authors &&\n"> <"\tnew=$(git hash-object -t commit -w --stdin <"\ttest_when_finished \"remove_object $new\" &&\n"> <"\tgit update-ref refs/heads/bogus \"$new\" &&\n"> <"\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n"> <"\ttest_must_fail git fsck &&\n"> <"\tgit -c fsck.multipleAuthors=ignore fsck\n"> ) } ) (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:_bz) op:Equal rhs:{(SQ <"\\0">)} spids:[602])] spids: [602] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:_bz5) op: Equal rhs: { (DQ ($ VSub_Name "$_bz") ($ VSub_Name "$_bz") ($ VSub_Name "$_bz") ($ VSub_Name "$_bz") ($ VSub_Name "$_bz") ) } spids: [607] ) ] spids: [607] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:_bz20) op: Equal rhs: { (DQ ($ VSub_Name "$_bz5") ($ VSub_Name "$_bz5") ($ VSub_Name "$_bz5") ($ VSub_Name "$_bz5") ) } spids: [616] ) ] spids: [616] ) (C {(test_expect_success)} {(SQ <"fsck notices blob entry pointing to null sha1">)} { (SQ <"\n"> <"\t(git init null-blob &&\n"> <"\t cd null-blob &&\n"> <"\t sha=$(printf \"100644 file$_bz$_bz20\" |\n"> <"\t git hash-object -w --stdin -t tree) &&\n"> <"\t git fsck 2>out &&\n"> <"\t cat out &&\n"> <"\t grep \"warning.*null sha1\" out\n"> <"\t)\n"> ) } ) (C {(test_expect_success)} {(SQ <"fsck notices submodule entry pointing to null sha1">)} { (SQ <"\n"> <"\t(git init null-commit &&\n"> <"\t cd null-commit &&\n"> <"\t sha=$(printf \"160000 submodule$_bz$_bz20\" |\n"> <"\t git hash-object -w --stdin -t tree) &&\n"> <"\t git fsck 2>out &&\n"> <"\t cat out &&\n"> <"\t grep \"warning.*null sha1\" out\n"> <"\t)\n"> ) } ) (While cond: [(Sentence child:(C {(read)} {(name)} {(path)} {(pretty)}) terminator:)] body: (DoGroup children: [ (While cond: [(Sentence child:(C {(read)} {(mode)} {(type)}) terminator:)] body: (DoGroup children: [ (C {(Lit_Other ":")} { (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonEquals arg_word: {($ VSub_Name "$path")} ) spids: [691 695] ) } ) (C {(test_expect_success)} {(DQ ("fsck notices ") ($ VSub_Name "$pretty") (" as ") ($ VSub_Name "$type"))} { (SQ <"\n"> <"\t\t(\n"> <"\t\t\tgit init $name-$type &&\n"> <"\t\t\tcd $name-$type &&\n"> <"\t\t\techo content >file &&\n"> <"\t\t\tgit add file &&\n"> <"\t\t\tgit commit -m base &&\n"> <"\t\t\tblob=$(git rev-parse :file) &&\n"> <"\t\t\ttree=$(git rev-parse HEAD^{tree}) &&\n"> <"\t\t\tvalue=$(eval \"echo \\$$type\") &&\n"> <"\t\t\tprintf \"$mode $type %s\\t%s\" \"$value\" \"$path\" >bad &&\n"> <"\t\t\tbad_tree=$(git mktree <"\t\t\tgit fsck 2>out &&\n"> <"\t\t\tcat out &&\n"> <"\t\t\tgrep \"warning.*tree $bad_tree\" out\n"> <"\t\t)"> ) } ) ] spids: [686 727] ) redirects: [ (HereDoc op_id: Redir_DLessDash fd: -1 body: {("100644 blob\n") ("040000 tree\n")} do_expansion: False here_end: EOF was_filled: True spids: [729] ) ] ) ] spids: [674 733] ) redirects: [ (HereDoc op_id: Redir_DLessDash fd: -1 body: { (DQ ("dot .\n") ("dotdot ..\n") ("dotgit .git\n") ("dotgit-case .GIT\n") ("dotgit-unicode .gI") (${ VSub_Name u200c) ("T .gI{u200c}T\n") ("dotgit-case2 .Git\n") ("git-tilde1 git~1\n") ("dotgitdot .git.\n") ("dot-backslash-case .") (EscapedLiteralPart token:) (EscapedLiteralPart token:) (.GIT) (EscapedLiteralPart token:) (EscapedLiteralPart token:) ("foobar\n") ("dotgit-case-backslash .git") (EscapedLiteralPart token:) (EscapedLiteralPart token:) ("foobar\n") ) } do_expansion: True here_end: EOF was_filled: True spids: [735] ) ] ) (C {(test_expect_success)} {(SQ <"fsck allows .\u0147it">)} { (SQ <"\n"> <"\t(\n"> <"\t\tgit init not-dotgit &&\n"> <"\t\tcd not-dotgit &&\n"> <"\t\techo content >file &&\n"> <"\t\tgit add file &&\n"> <"\t\tgit commit -m base &&\n"> <"\t\tblob=$(git rev-parse :file) &&\n"> <"\t\tprintf \"100644 blob $blob\\t.\\\\305\\\\207it\" >tree &&\n"> <"\t\ttree=$(git mktree <"\t\tgit fsck 2>err &&\n"> <"\t\ttest_line_count = 0 err\n"> <"\t)\n"> ) } ) (C {(test_expect_success)} {(SQ <"NUL in commit">)} { (SQ <"\n"> <"\trm -fr nul-in-commit &&\n"> <"\tgit init nul-in-commit &&\n"> <"\t(\n"> <"\t\tcd nul-in-commit &&\n"> <"\t\tgit commit --allow-empty -m \"initial commitQNUL after message\" &&\n"> <"\t\tgit cat-file commit HEAD >original &&\n"> <"\t\tq_to_nul munged &&\n"> <"\t\tgit hash-object -w -t commit --stdin name &&\n"> <"\t\tgit branch bad $(cat name) &&\n"> <"\n"> <"\t\ttest_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&\n"> <"\t\tgrep nulInCommit warn.1 &&\n"> <"\t\tgit fsck 2>warn.2 &&\n"> <"\t\tgrep nulInCommit warn.2\n"> <"\t)\n"> ) } ) (FuncDef name: create_repo_missing body: (BraceGroup children: [ (AndOr children: [ (C {(rm)} {(-rf)} {(missing)}) (AndOr children: [ (C {(git)} {(init)} {(missing)}) (Subshell child: (AndOr children: [ (C {(cd)} {(missing)}) (AndOr children: [ (C {(git)} {(commit)} {(-m)} {(one)} {(--allow-empty)}) (AndOr children: [ (C {(mkdir)} {(subdir)}) (AndOr children: [ (SimpleCommand words: [{(echo)} {(content)}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(subdir/file)} spids: [880] ) ] ) (AndOr children: [ (C {(git)} {(add)} {(subdir/file)}) (AndOr children: [ (C {(git)} {(commit)} {(-m)} {(two)}) (AndOr children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:unrelated) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {(unrelated)}) (C {(git)} {(hash-object)} {(--stdin)} {(-w)} ) ] negated: False ) ] ) left_token: spids: [907 921] ) } spids: [906] ) ] spids: [906] ) (AndOr children: [ (C {(git)} {(tag)} {(-m)} {(foo)} {(tag)} {($ VSub_Name "$unrelated")} ) (AndOr children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:sha1) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [ (C {(git)} {(rev-parse)} {(--verify)} {(DQ ($ VSub_Number "$1"))} ) ] ) left_token: spids: [942 952] ) } spids: [941] ) ] spids: [941] ) (AndOr children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:path) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} { ($ VSub_Name "$sha1" ) } ) (C {(sed)} { (SQ < "s|..|&/|" > ) } ) ] negated: False ) ] ) left_token: spids: [958 970] ) } spids: [957] ) ] spids: [957] ) (C {(rm)} {(.git/objects/) ($ VSub_Name "$path") } ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) spids: [846 981] ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] spids: [825] ) spids: [820 824] ) (C {(test_expect_success)} {(SQ <"fsck notices missing blob">)} { (SQ <"\n"> <"\tcreate_repo_missing HEAD:subdir/file &&\n"> <"\ttest_must_fail git -C missing fsck\n"> ) } ) (C {(test_expect_success)} {(SQ <"fsck notices missing subtree">)} { (SQ <"\n"> <"\tcreate_repo_missing HEAD:subdir &&\n"> <"\ttest_must_fail git -C missing fsck\n"> ) } ) (C {(test_expect_success)} {(SQ <"fsck notices missing root tree">)} { (SQ <"\n"> <"\tcreate_repo_missing HEAD^{tree} &&\n"> <"\ttest_must_fail git -C missing fsck\n"> ) } ) (C {(test_expect_success)} {(SQ <"fsck notices missing parent">)} {(SQ <"\n"> <"\tcreate_repo_missing HEAD^ &&\n"> <"\ttest_must_fail git -C missing fsck\n">)} ) (C {(test_expect_success)} {(SQ <"fsck notices missing tagged object">)} { (SQ <"\n"> <"\tcreate_repo_missing tag^{blob} &&\n"> <"\ttest_must_fail git -C missing fsck\n">) } ) (C {(test_expect_success)} {(SQ <"fsck notices ref pointing to missing commit">)} {(SQ <"\n"> <"\tcreate_repo_missing HEAD &&\n"> <"\ttest_must_fail git -C missing fsck\n">)} ) (C {(test_expect_success)} {(SQ <"fsck notices ref pointing to missing tag">)} {(SQ <"\n"> <"\tcreate_repo_missing tag &&\n"> <"\ttest_must_fail git -C missing fsck\n">)} ) (C {(test_expect_success)} {(SQ <"fsck --connectivity-only">)} { (SQ <"\n"> <"\trm -rf connectivity-only &&\n"> <"\tgit init connectivity-only &&\n"> <"\t(\n"> <"\t\tcd connectivity-only &&\n"> <"\t\ttouch empty &&\n"> <"\t\tgit add empty &&\n"> <"\t\ttest_commit empty &&\n"> <"\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n"> <"\t\trm -f $empty &&\n"> <"\t\techo invalid >$empty &&\n"> <"\t\ttest_must_fail git fsck --strict &&\n"> <"\t\tgit fsck --strict --connectivity-only &&\n"> <"\t\ttree=$(git rev-parse HEAD:) &&\n"> <"\t\tsuffix=${tree#??} &&\n"> <"\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n"> <"\t\trm -f $tree &&\n"> <"\t\techo invalid >$tree &&\n"> <"\t\ttest_must_fail git fsck --strict --connectivity-only\n"> <"\t)\n"> ) } ) (FuncDef name: remove_loose_object body: (BraceGroup children: [ (AndOr children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:sha1) op: Equal rhs: { (DQ (CommandSubPart command_list: (CommandList children: [(C {(git)} {(rev-parse)} {(DQ ($ VSub_Number "$1"))})] ) left_token: spids: [1117 1125] ) ) } spids: [1115] ) ] spids: [1115] ) (AndOr children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:remainder) op: Equal rhs: { (BracedVarSub token: suffix_op: (StringUnary op_id:VOp1_Pound arg_word:{("??")}) spids: [1132 1136] ) } spids: [1131] ) ] spids: [1131] ) (AndOr children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:firsttwo) op: Equal rhs: { (BracedVarSub token: suffix_op: (StringUnary op_id: VOp1_Percent arg_word: {($ VSub_Name "$remainder")} ) spids: [1142 1146] ) } spids: [1141] ) ] spids: [1141] ) (C {(rm)} {(.git/objects/) ($ VSub_Name "$firsttwo") (/) ($ VSub_Name "$remainder")} ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] spids: [1112] ) spids: [1107 1111] ) (C {(test_expect_success)} {(SQ <"fsck --name-objects">)} { (SQ <"\n"> <"\trm -rf name-objects &&\n"> <"\tgit init name-objects &&\n"> <"\t(\n"> <"\t\tcd name-objects &&\n"> <"\t\ttest_commit julius caesar.t &&\n"> <"\t\ttest_commit augustus &&\n"> <"\t\ttest_commit caesar &&\n"> <"\t\tremove_loose_object $(git rev-parse julius:caesar.t) &&\n"> <"\t\ttest_must_fail git fsck --name-objects >out &&\n"> <"\t\ttree=$(git rev-parse --verify julius:) &&\n"> <"\t\tgrep \"$tree (\\(refs/heads/master\\|HEAD\\)@{[0-9]*}:\" out\n"> <"\t)\n"> ) } ) (C {(test_done)}) ] )