| 1 | #!/bin/sh |
| 2 | |
| 3 | # NOTICE: |
| 4 | # This testsuite does a number of diffs and checks that the output match. |
| 5 | # However, it is a "garbage in, garbage out" situation; the trees have |
| 6 | # duplicate entries for individual paths, and it results in diffs that do |
| 7 | # not make much sense. As such, it is not clear that the diffs are |
| 8 | # "correct". The primary purpose of these tests was to verify that |
| 9 | # diff-tree does not segfault, but there is perhaps some value in ensuring |
| 10 | # that the diff output isn't wildly unreasonable. |
| 11 | |
| 12 | test_description='test tree diff when trees have duplicate entries' |
| 13 | |
| 14 | . ./test-lib.sh |
| 15 | |
| 16 | if ! test_have_prereq PERL_TEST_HELPERS |
| 17 | then |
| 18 | skip_all='skipping diff duplicates tests; Perl not available' |
| 19 | test_done |
| 20 | fi |
| 21 | |
| 22 | # make_tree_entry <mode> <mode> <sha1> |
| 23 | # |
| 24 | # We have to rely on perl here because not all printfs understand |
| 25 | # hex escapes (only octal), and xxd is not portable. |
| 26 | make_tree_entry () { |
| 27 | printf '%s %s\0' "$1" "$2" && |
| 28 | perl -e 'print chr(hex($_)) for ($ARGV[0] =~ /../g)' "$3" |
| 29 | } |
| 30 | |
| 31 | # Like git-mktree, but without all of the pesky sanity checking. |
| 32 | # Arguments come in groups of three, each group specifying a single |
| 33 | # tree entry (see make_tree_entry above). |
| 34 | make_tree () { |
| 35 | while test $# -gt 2; do |
| 36 | make_tree_entry "$1" "$2" "$3" |
| 37 | shift; shift; shift |
| 38 | done | |
| 39 | git hash-object --literally -w -t tree --stdin |
| 40 | } |
| 41 | |
| 42 | # this is kind of a convoluted setup, but matches |
| 43 | # a real-world case. Each tree contains four entries |
| 44 | # for the given path, one with one sha1, and three with |
| 45 | # the other. The first tree has them split across |
| 46 | # two subtrees (which are themselves duplicate entries in |
| 47 | # the root tree), and the second has them all in a single subtree. |
| 48 | test_expect_success 'create trees with duplicate entries' ' |
| 49 | blob_one=$(echo one | git hash-object -w --stdin) && |
| 50 | blob_two=$(echo two | git hash-object -w --stdin) && |
| 51 | inner_one_a=$(make_tree \ |
| 52 | 100644 inner $blob_one |
| 53 | ) && |
| 54 | inner_one_b=$(make_tree \ |
| 55 | 100644 inner $blob_two \ |
| 56 | 100644 inner $blob_two \ |
| 57 | 100644 inner $blob_two |
| 58 | ) && |
| 59 | outer_one=$(make_tree \ |
| 60 | 040000 outer $inner_one_a \ |
| 61 | 040000 outer $inner_one_b |
| 62 | ) && |
| 63 | inner_two=$(make_tree \ |
| 64 | 100644 inner $blob_one \ |
| 65 | 100644 inner $blob_two \ |
| 66 | 100644 inner $blob_two \ |
| 67 | 100644 inner $blob_two |
| 68 | ) && |
| 69 | outer_two=$(make_tree \ |
| 70 | 040000 outer $inner_two |
| 71 | ) && |
| 72 | git tag one $outer_one && |
| 73 | git tag two $outer_two |
| 74 | ' |
| 75 | |
| 76 | test_expect_success 'create tree without duplicate entries' ' |
| 77 | blob_one=$(echo one | git hash-object -w --stdin) && |
| 78 | outer_three=$(make_tree \ |
| 79 | 100644 renamed $blob_one |
| 80 | ) && |
| 81 | git tag three $outer_three |
| 82 | ' |
| 83 | |
| 84 | test_expect_success 'diff-tree between duplicate trees' ' |
| 85 | # See NOTICE at top of file |
| 86 | { |
| 87 | printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" && |
| 88 | printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" && |
| 89 | printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" && |
| 90 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 91 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 92 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" |
| 93 | } >expect && |
| 94 | git diff-tree -r --no-abbrev one two >actual && |
| 95 | test_cmp expect actual |
| 96 | ' |
| 97 | |
| 98 | test_expect_success 'diff-tree with renames' ' |
| 99 | # See NOTICE at top of file. |
| 100 | git diff-tree -M -r --no-abbrev one two >actual && |
| 101 | test_must_be_empty actual |
| 102 | ' |
| 103 | |
| 104 | test_expect_success 'diff-tree FROM duplicate tree' ' |
| 105 | # See NOTICE at top of file. |
| 106 | { |
| 107 | printf ":100644 000000 $blob_one $ZERO_OID D\touter/inner\n" && |
| 108 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 109 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 110 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 111 | printf ":000000 100644 $ZERO_OID $blob_one A\trenamed\n" |
| 112 | } >expect && |
| 113 | git diff-tree -r --no-abbrev one three >actual && |
| 114 | test_cmp expect actual |
| 115 | ' |
| 116 | |
| 117 | test_expect_success 'diff-tree FROM duplicate tree, with renames' ' |
| 118 | # See NOTICE at top of file. |
| 119 | { |
| 120 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 121 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 122 | printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && |
| 123 | printf ":100644 100644 $blob_one $blob_one R100\touter/inner\trenamed\n" |
| 124 | } >expect && |
| 125 | git diff-tree -M -r --no-abbrev one three >actual && |
| 126 | test_cmp expect actual |
| 127 | ' |
| 128 | |
| 129 | test_expect_success 'create a few commits' ' |
| 130 | git commit-tree -m "Duplicate Entries" two^{tree} >commit_id && |
| 131 | git branch base $(cat commit_id) && |
| 132 | |
| 133 | git commit-tree -p $(cat commit_id) -m "Just one" three^{tree} >up && |
| 134 | git branch update $(cat up) && |
| 135 | |
| 136 | git commit-tree -p $(cat up) -m "Back to weird" two^{tree} >final && |
| 137 | git branch final $(cat final) && |
| 138 | |
| 139 | rm commit_id up final |
| 140 | ' |
| 141 | |
| 142 | test_expect_success 'git read-tree does not segfault' ' |
| 143 | test_must_fail git read-tree --reset base 2>err && |
| 144 | test_grep "error: corrupted cache-tree has entries not present in index" err |
| 145 | ' |
| 146 | |
| 147 | test_expect_success 'reset --hard does not segfault' ' |
| 148 | git checkout base && |
| 149 | test_must_fail git reset --hard 2>err && |
| 150 | test_grep "error: corrupted cache-tree has entries not present in index" err |
| 151 | ' |
| 152 | |
| 153 | test_expect_success 'git diff HEAD does not segfault' ' |
| 154 | git checkout base && |
| 155 | GIT_TEST_CHECK_CACHE_TREE=false && |
| 156 | git reset --hard && |
| 157 | test_must_fail git diff HEAD 2>err && |
| 158 | test_grep "error: corrupted cache-tree has entries not present in index" err |
| 159 | ' |
| 160 | |
| 161 | test_expect_failure 'can switch to another branch when status is empty' ' |
| 162 | git clean -ffdqx && |
| 163 | git status --porcelain -uno >actual && |
| 164 | test_must_be_empty actual && |
| 165 | git checkout update |
| 166 | ' |
| 167 | |
| 168 | test_expect_success 'forcibly switch to another branch, verify status empty' ' |
| 169 | git checkout -f update && |
| 170 | git status --porcelain -uno >actual && |
| 171 | test_must_be_empty actual |
| 172 | ' |
| 173 | |
| 174 | test_expect_success 'fast-forward from non-duplicate entries to duplicate' ' |
| 175 | git merge final |
| 176 | ' |
| 177 | |
| 178 | test_expect_failure 'clean status, switch branches, status still clean' ' |
| 179 | git status --porcelain -uno >actual && |
| 180 | test_must_be_empty actual && |
| 181 | git checkout base && |
| 182 | git status --porcelain -uno >actual && |
| 183 | test_must_be_empty actual |
| 184 | ' |
| 185 | |
| 186 | test_expect_success 'switch to base branch and force status to be clean' ' |
| 187 | git checkout base && |
| 188 | GIT_TEST_CHECK_CACHE_TREE=false git reset --hard && |
| 189 | git status --porcelain -uno >actual && |
| 190 | test_must_be_empty actual |
| 191 | ' |
| 192 | |
| 193 | test_expect_failure 'fast-forward from duplicate entries to non-duplicate' ' |
| 194 | git merge update |
| 195 | ' |
| 196 | |
| 197 | test_done |