| #!/bin/sh |
| |
| test_description='object name disambiguation |
| |
| Create blobs, trees, commits and a tag that all share the same |
| prefix, and make sure "git rev-parse" can take advantage of |
| type information to disambiguate short object names that are |
| not necessarily unique. |
| |
| The final history used in the test has five commits, with the bottom |
| one tagged as v1.0.0. They all have one regular file each. |
| |
| +-------------------------------------------+ |
| | | |
| | .-------b3wettvi---- ad2uee | |
| | / / | |
| | a2onsxbvj---czy8f73t--ioiley5o | |
| | | |
| +-------------------------------------------+ |
| |
| ' |
| |
| GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| |
| . ./test-lib.sh |
| |
| test_cmp_failed_rev_parse () { |
| dir=$1 |
| rev=$2 |
| |
| cat >expect && |
| test_must_fail git -C "$dir" rev-parse "$rev" 2>actual.raw && |
| sed "s/\($rev\)[0-9a-f]*/\1.../" <actual.raw >actual && |
| test_cmp expect actual |
| } |
| |
| test_expect_success 'ambiguous blob output' ' |
| git init --bare blob.prefix && |
| ( |
| cd blob.prefix && |
| |
| # Both start with "dead..", under both SHA-1 and SHA-256 |
| echo brocdnra | git hash-object -w --stdin && |
| echo brigddsv | git hash-object -w --stdin && |
| |
| # Both start with "beef.." |
| echo 1agllotbh | git hash-object -w --stdin && |
| echo 1bbfctrkc | git hash-object -w --stdin |
| ) && |
| |
| test_must_fail git -C blob.prefix rev-parse dead && |
| test_cmp_failed_rev_parse blob.prefix beef <<-\EOF |
| error: short object ID beef... is ambiguous |
| hint: The candidates are: |
| hint: beef... blob |
| hint: beef... blob |
| fatal: ambiguous argument '\''beef...'\'': unknown revision or path not in the working tree. |
| Use '\''--'\'' to separate paths from revisions, like this: |
| '\''git <command> [<revision>...] -- [<file>...]'\'' |
| EOF |
| ' |
| |
| test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' ' |
| git init --bare blob.bad && |
| ( |
| cd blob.bad && |
| |
| # Both have the prefix "bad0" |
| echo xyzfaowcoh | git hash-object -t bad -w --stdin --literally && |
| echo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally |
| ) && |
| |
| test_cmp_failed_rev_parse blob.bad bad0 <<-\EOF |
| error: short object ID bad0... is ambiguous |
| fatal: invalid object type |
| EOF |
| ' |
| |
| test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' ' |
| git init --bare blob.corrupt && |
| ( |
| cd blob.corrupt && |
| |
| # Both have the prefix "cafe" |
| echo bnkxmdwz | git hash-object -w --stdin && |
| oid=$(echo bmwsjxzi | git hash-object -w --stdin) && |
| |
| oidf=objects/$(test_oid_to_path "$oid") && |
| chmod 755 $oidf && |
| echo broken >$oidf |
| ) && |
| |
| test_cmp_failed_rev_parse blob.corrupt cafe <<-\EOF |
| error: short object ID cafe... is ambiguous |
| error: inflate: data stream error (incorrect header check) |
| error: unable to unpack cafe... header |
| error: inflate: data stream error (incorrect header check) |
| error: unable to unpack cafe... header |
| hint: The candidates are: |
| hint: cafe... [bad object] |
| hint: cafe... blob |
| fatal: ambiguous argument '\''cafe...'\'': unknown revision or path not in the working tree. |
| Use '\''--'\'' to separate paths from revisions, like this: |
| '\''git <command> [<revision>...] -- [<file>...]'\'' |
| EOF |
| ' |
| |
| if ! test_have_prereq SHA1 |
| then |
| skip_all='not using SHA-1 for objects' |
| test_done |
| fi |
| |
| test_expect_success 'blob and tree' ' |
| test_tick && |
| ( |
| test_write_lines 0 1 2 3 4 5 6 7 8 9 && |
| echo && |
| echo b1rwzyc3 |
| ) >a0blgqsjc && |
| |
| # create one blob 0000000000b36 |
| git add a0blgqsjc && |
| |
| # create one tree 0000000000cdc |
| git write-tree |
| ' |
| |
| test_expect_success 'warn ambiguity when no candidate matches type hint' ' |
| test_must_fail git rev-parse --verify 000000000^{commit} 2>actual && |
| test_grep "short object ID 000000000 is ambiguous" actual |
| ' |
| |
| test_expect_success 'disambiguate tree-ish' ' |
| # feed tree-ish in an unambiguous way |
| git rev-parse --verify 0000000000cdc:a0blgqsjc && |
| |
| # ambiguous at the object name level, but there is only one |
| # such tree-ish (the other is a blob) |
| git rev-parse --verify 000000000:a0blgqsjc |
| ' |
| |
| test_expect_success 'disambiguate blob' ' |
| sed -e "s/|$//" >patch <<-EOF && |
| diff --git a/frotz b/frotz |
| index 000000000..ffffff 100644 |
| --- a/frotz |
| +++ b/frotz |
| @@ -10,3 +10,4 @@ |
| 9 |
| | |
| b1rwzyc3 |
| +irwry |
| EOF |
| ( |
| GIT_INDEX_FILE=frotz && |
| export GIT_INDEX_FILE && |
| git apply --build-fake-ancestor frotz patch && |
| git cat-file blob :frotz >actual |
| ) && |
| test_cmp a0blgqsjc actual |
| ' |
| |
| test_expect_success 'disambiguate tree' ' |
| commit=$(echo "d7xm" | git commit-tree 000000000) && |
| # this commit is fffff2e and not ambiguous with the 00000* objects |
| test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc) |
| ' |
| |
| test_expect_success 'first commit' ' |
| # create one commit 0000000000e4f |
| git commit -m a2onsxbvj |
| ' |
| |
| test_expect_success 'disambiguate commit-ish' ' |
| # feed commit-ish in an unambiguous way |
| git rev-parse --verify 0000000000e4f^{commit} && |
| |
| # ambiguous at the object name level, but there is only one |
| # such commit (the others are tree and blob) |
| git rev-parse --verify 000000000^{commit} && |
| |
| # likewise |
| git rev-parse --verify 000000000^0 |
| ' |
| |
| test_expect_success 'disambiguate commit' ' |
| commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) && |
| # this commit is ffffffd8 and not ambiguous with the 00000* objects |
| test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f) |
| ' |
| |
| test_expect_success 'log name1..name2 takes only commit-ishes on both ends' ' |
| # These are underspecified from the prefix-length point of view |
| # to disambiguate the commit with other objects, but there is only |
| # one commit that has 00000* prefix at this point. |
| git log 000000000..000000000 && |
| git log ..000000000 && |
| git log 000000000.. && |
| git log 000000000...000000000 && |
| git log ...000000000 && |
| git log 000000000... |
| ' |
| |
| test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' ' |
| # Likewise. |
| git rev-parse 000000000..000000000 && |
| git rev-parse ..000000000 && |
| git rev-parse 000000000.. |
| ' |
| |
| test_expect_success 'git log takes only commit-ish' ' |
| # Likewise. |
| git log 000000000 |
| ' |
| |
| test_expect_success 'git reset takes only commit-ish' ' |
| # Likewise. |
| git reset 000000000 |
| ' |
| |
| test_expect_success 'first tag' ' |
| # create one tag 0000000000f8f |
| git tag -a -m j7cp83um v1.0.0 |
| ' |
| |
| test_expect_failure 'two semi-ambiguous commit-ish' ' |
| # At this point, we have a tag 0000000000f8f that points |
| # at a commit 0000000000e4f, and a tree and a blob that |
| # share 0000000000 prefix with these tag and commit. |
| # |
| # Once the parser becomes ultra-smart, it could notice that |
| # 0000000000 before ^{commit} name many different objects, but |
| # that only two (HEAD and v1.0.0 tag) can be peeled to commit, |
| # and that peeling them down to commit yield the same commit |
| # without ambiguity. |
| git rev-parse --verify 0000000000^{commit} && |
| |
| # likewise |
| git log 0000000000..0000000000 && |
| git log ..0000000000 && |
| git log 0000000000.. && |
| git log 0000000000...0000000000 && |
| git log ...0000000000 && |
| git log 0000000000... |
| ' |
| |
| test_expect_failure 'three semi-ambiguous tree-ish' ' |
| # Likewise for tree-ish. HEAD, v1.0.0 and HEAD^{tree} share |
| # the prefix but peeling them to tree yields the same thing |
| git rev-parse --verify 0000000000^{tree} |
| ' |
| |
| test_expect_success 'parse describe name' ' |
| # feed an unambiguous describe name |
| git rev-parse --verify v1.0.0-0-g0000000000e4f && |
| |
| # ambiguous at the object name level, but there is only one |
| # such commit (others are blob, tree and tag) |
| git rev-parse --verify v1.0.0-0-g000000000 |
| ' |
| |
| test_expect_success 'more history' ' |
| # commit 0000000000043 |
| git mv a0blgqsjc d12cr3h8t && |
| echo h62xsjeu >>d12cr3h8t && |
| git add d12cr3h8t && |
| |
| test_tick && |
| git commit -m czy8f73t && |
| |
| # commit 00000000008ec |
| git mv d12cr3h8t j000jmpzn && |
| echo j08bekfvt >>j000jmpzn && |
| git add j000jmpzn && |
| |
| test_tick && |
| git commit -m ioiley5o && |
| |
| # commit 0000000005b0 |
| git checkout v1.0.0^0 && |
| git mv a0blgqsjc f5518nwu && |
| |
| test_write_lines h62xsjeu j08bekfvt kg7xflhm >>f5518nwu && |
| git add f5518nwu && |
| |
| test_tick && |
| git commit -m b3wettvi && |
| side=$(git rev-parse HEAD) && |
| |
| # commit 000000000066 |
| git checkout main && |
| |
| # If you use recursive, merge will fail and you will need to |
| # clean up a0blgqsjc as well. If you use resolve, merge will |
| # succeed. |
| test_might_fail git merge --no-commit -s recursive $side && |
| git rm -f f5518nwu j000jmpzn && |
| |
| test_might_fail git rm -f a0blgqsjc && |
| ( |
| git cat-file blob $side:f5518nwu && |
| echo j3l0i9s6 |
| ) >ab2gs879 && |
| git add ab2gs879 && |
| |
| test_tick && |
| git commit -m ad2uee |
| |
| ' |
| |
| test_expect_failure 'parse describe name taking advantage of generation' ' |
| # ambiguous at the object name level, but there is only one |
| # such commit at generation 0 |
| git rev-parse --verify v1.0.0-0-g000000000 && |
| |
| # likewise for generation 2 and 4 |
| git rev-parse --verify v1.0.0-2-g000000000 && |
| git rev-parse --verify v1.0.0-4-g000000000 |
| ' |
| |
| # Note: because rev-parse does not even try to disambiguate based on |
| # the generation number, this test currently succeeds for a wrong |
| # reason. When it learns to use the generation number, the previous |
| # test should succeed, and also this test should fail because the |
| # describe name used in the test with generation number can name two |
| # commits. Make sure that such a future enhancement does not randomly |
| # pick one. |
| test_expect_success 'parse describe name not ignoring ambiguity' ' |
| # ambiguous at the object name level, and there are two such |
| # commits at generation 1 |
| test_must_fail git rev-parse --verify v1.0.0-1-g000000000 |
| ' |
| |
| test_expect_success 'ambiguous commit-ish' ' |
| # Now there are many commits that begin with the |
| # common prefix, none of these should pick one at |
| # random. They all should result in ambiguity errors. |
| test_must_fail git rev-parse --verify 00000000^{commit} && |
| |
| # likewise |
| test_must_fail git log 000000000..000000000 && |
| test_must_fail git log ..000000000 && |
| test_must_fail git log 000000000.. && |
| test_must_fail git log 000000000...000000000 && |
| test_must_fail git log ...000000000 && |
| test_must_fail git log 000000000... |
| ' |
| |
| # There are three objects with this prefix: a blob, a tree, and a tag. We know |
| # the blob will not pass as a treeish, but the tree and tag should (and thus |
| # cause an error). |
| test_expect_success 'ambiguous tags peel to treeish' ' |
| test_must_fail git rev-parse 0000000000f^{tree} |
| ' |
| |
| test_expect_success 'rev-parse --disambiguate' ' |
| # The test creates 16 objects that share the prefix and two |
| # commits created by commit-tree in earlier tests share a |
| # different prefix. |
| git rev-parse --disambiguate=000000000 >actual && |
| test_line_count = 16 actual && |
| test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000 |
| ' |
| |
| test_expect_success 'rev-parse --disambiguate drops duplicates' ' |
| git rev-parse --disambiguate=000000000 >expect && |
| git pack-objects .git/objects/pack/pack <expect && |
| git rev-parse --disambiguate=000000000 >actual && |
| test_cmp expect actual |
| ' |
| |
| test_expect_success 'ambiguous 40-hex ref' ' |
| TREE=$(git mktree </dev/null) && |
| REF=$(git rev-parse HEAD) && |
| VAL=$(git commit-tree $TREE </dev/null) && |
| git update-ref refs/heads/$REF $VAL && |
| test $(git rev-parse $REF 2>err) = $REF && |
| grep "refname.*${REF}.*ambiguous" err |
| ' |
| |
| test_expect_success 'ambiguous short sha1 ref' ' |
| TREE=$(git mktree </dev/null) && |
| REF=$(git rev-parse --short HEAD) && |
| VAL=$(git commit-tree $TREE </dev/null) && |
| git update-ref refs/heads/$REF $VAL && |
| test $(git rev-parse $REF 2>err) = $VAL && |
| grep "refname.*${REF}.*ambiguous" err |
| ' |
| |
| test_expect_success 'ambiguity errors are not repeated (raw)' ' |
| test_must_fail git rev-parse 00000 2>stderr && |
| grep "is ambiguous" stderr >errors && |
| test_line_count = 1 errors |
| ' |
| |
| test_expect_success 'ambiguity errors are not repeated (treeish)' ' |
| test_must_fail git rev-parse 00000:foo 2>stderr && |
| grep "is ambiguous" stderr >errors && |
| test_line_count = 1 errors |
| ' |
| |
| test_expect_success 'ambiguity errors are not repeated (peel)' ' |
| test_must_fail git rev-parse 00000^{commit} 2>stderr && |
| grep "is ambiguous" stderr >errors && |
| test_line_count = 1 errors |
| ' |
| |
| test_expect_success 'ambiguity hints' ' |
| test_must_fail git rev-parse 000000000 2>stderr && |
| grep ^hint: stderr >hints && |
| # 16 candidates, plus one intro line |
| test_line_count = 17 hints |
| ' |
| |
| test_expect_success 'ambiguity hints respect type' ' |
| test_must_fail git rev-parse 000000000^{commit} 2>stderr && |
| grep ^hint: stderr >hints && |
| # 5 commits, 1 tag (which is a committish), plus intro line |
| test_line_count = 7 hints |
| ' |
| |
| test_expect_success 'failed type-selector still shows hint' ' |
| # these two blobs share the same prefix "ee3d", but neither |
| # will pass for a commit |
| echo 851 | git hash-object --stdin -w && |
| echo 872 | git hash-object --stdin -w && |
| test_must_fail git rev-parse ee3d^{commit} 2>stderr && |
| grep ^hint: stderr >hints && |
| test_line_count = 3 hints |
| ' |
| |
| test_expect_success 'core.disambiguate config can prefer types' ' |
| # ambiguous between tree and tag |
| sha1=0000000000f && |
| test_must_fail git rev-parse $sha1 && |
| git rev-parse $sha1^{commit} && |
| git -c core.disambiguate=committish rev-parse $sha1 |
| ' |
| |
| test_expect_success 'core.disambiguate does not override context' ' |
| # treeish ambiguous between tag and tree |
| test_must_fail \ |
| git -c core.disambiguate=committish rev-parse $sha1^{tree} |
| ' |
| |
| test_expect_success 'ambiguous commits are printed by type first, then hash order' ' |
| test_must_fail git rev-parse 0000 2>stderr && |
| grep ^hint: stderr >hints && |
| grep 0000 hints >objects && |
| cat >expected <<-\EOF && |
| tag |
| commit |
| tree |
| blob |
| EOF |
| awk "{print \$3}" <objects >objects.types && |
| uniq <objects.types >objects.types.uniq && |
| test_cmp expected objects.types.uniq && |
| for type in tag commit tree blob |
| do |
| grep $type objects >$type.objects && |
| sort $type.objects >$type.objects.sorted && |
| test_cmp $type.objects.sorted $type.objects || return 1 |
| done |
| ' |
| |
| test_expect_success 'cat-file --batch and --batch-check show ambiguous' ' |
| echo "0000 ambiguous" >expect && |
| echo 0000 | git cat-file --batch-check >actual 2>err && |
| test_cmp expect actual && |
| test_grep hint: err && |
| echo 0000 | git cat-file --batch >actual 2>err && |
| test_cmp expect actual && |
| test_grep hint: err |
| ' |
| |
| test_done |