| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='git log for a path with Bloom filters' |
| 4 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 5 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 6 | |
| 7 | . ./test-lib.sh |
| 8 | . "$TEST_DIRECTORY"/lib-chunk.sh |
| 9 | |
| 10 | GIT_TEST_COMMIT_GRAPH=0 |
| 11 | GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0 |
| 12 | |
| 13 | test_expect_success 'setup test - repo, commits, commit graph, log outputs' ' |
| 14 | git init && |
| 15 | mkdir A A/B A/B/C && |
| 16 | test_commit c1 A/file1 && |
| 17 | test_commit c2 A/B/file2 && |
| 18 | test_commit c3 A/B/C/file3 && |
| 19 | test_commit c4 A/file1 && |
| 20 | test_commit c5 A/B/file2 && |
| 21 | test_commit c6 A/B/C/file3 && |
| 22 | test_commit c7 A/file1 && |
| 23 | test_commit c8 A/B/file2 && |
| 24 | test_commit c9 A/B/C/file3 && |
| 25 | test_commit c10 file_to_be_deleted && |
| 26 | git checkout -b side HEAD~4 && |
| 27 | test_commit side-1 file4 && |
| 28 | git checkout main && |
| 29 | git merge side && |
| 30 | test_commit c11 file5 && |
| 31 | mv file5 file5_renamed && |
| 32 | git add file5_renamed && |
| 33 | git commit -m "rename" && |
| 34 | rm file_to_be_deleted && |
| 35 | git add . && |
| 36 | git commit -m "file removed" && |
| 37 | git commit --allow-empty -m "empty" && |
| 38 | git commit-graph write --reachable --changed-paths && |
| 39 | |
| 40 | test_oid_cache <<-EOF |
| 41 | oid_version sha1:1 |
| 42 | oid_version sha256:2 |
| 43 | EOF |
| 44 | ' |
| 45 | |
| 46 | graph_read_expect () { |
| 47 | NUM_CHUNKS=6 |
| 48 | cat >expect <<- EOF |
| 49 | header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0 |
| 50 | num_commits: $1 |
| 51 | chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data |
| 52 | options: bloom(1,10,7) read_generation_data |
| 53 | EOF |
| 54 | test-tool read-graph >actual && |
| 55 | test_cmp expect actual |
| 56 | } |
| 57 | |
| 58 | test_expect_success 'commit-graph write wrote out the bloom chunks' ' |
| 59 | graph_read_expect 16 |
| 60 | ' |
| 61 | |
| 62 | # Turn off any inherited trace2 settings for this test. |
| 63 | sane_unset GIT_TRACE2 GIT_TRACE2_PERF GIT_TRACE2_EVENT |
| 64 | sane_unset GIT_TRACE2_PERF_BRIEF |
| 65 | sane_unset GIT_TRACE2_CONFIG_PARAMS |
| 66 | |
| 67 | setup () { |
| 68 | rm -f "$TRASH_DIRECTORY/trace.perf" && |
| 69 | eval git -c core.commitGraph=false log --pretty="format:%s" "$1" >log_wo_bloom && |
| 70 | eval "GIT_TRACE2_PERF=\"$TRASH_DIRECTORY/trace.perf\"" \ |
| 71 | git -c core.commitGraph=true log --pretty="format:%s" "$1" >log_w_bloom |
| 72 | } |
| 73 | |
| 74 | test_bloom_filters_used () { |
| 75 | log_args=$1 |
| 76 | bloom_trace_prefix="statistics:{\"filter_not_present\":${2:-0},\"maybe\"" |
| 77 | setup "$log_args" && |
| 78 | grep -q "$bloom_trace_prefix" "$TRASH_DIRECTORY/trace.perf" && |
| 79 | test_cmp log_wo_bloom log_w_bloom && |
| 80 | test_path_is_file "$TRASH_DIRECTORY/trace.perf" |
| 81 | } |
| 82 | |
| 83 | test_bloom_filters_not_used () { |
| 84 | log_args=$1 |
| 85 | setup "$log_args" && |
| 86 | |
| 87 | if grep -q "statistics:{\"filter_not_present\":" "$TRASH_DIRECTORY/trace.perf" |
| 88 | then |
| 89 | # if the Bloom filter system is initialized, ensure that no |
| 90 | # filters were used |
| 91 | data="statistics:{" |
| 92 | # unusable filters (e.g., those computed with a |
| 93 | # different value of commitGraph.changedPathsVersion) |
| 94 | # are counted in the filter_not_present bucket, so any |
| 95 | # value is OK there. |
| 96 | data="$data\"filter_not_present\":[0-9][0-9]*," |
| 97 | data="$data\"maybe\":0," |
| 98 | data="$data\"definitely_not\":0," |
| 99 | data="$data\"false_positive\":0}" |
| 100 | |
| 101 | grep -q "$data" "$TRASH_DIRECTORY/trace.perf" |
| 102 | fi && |
| 103 | test_cmp log_wo_bloom log_w_bloom |
| 104 | } |
| 105 | |
| 106 | for path in A A/B A/B/C A/file1 A/B/file2 A/B/C/file3 file4 file5 file5_renamed file_to_be_deleted |
| 107 | do |
| 108 | for option in "" \ |
| 109 | "--all" \ |
| 110 | "--full-history" \ |
| 111 | "--full-history --simplify-merges" \ |
| 112 | "--simplify-merges" \ |
| 113 | "--simplify-by-decoration" \ |
| 114 | "--follow" \ |
| 115 | "--first-parent" \ |
| 116 | "--topo-order" \ |
| 117 | "--date-order" \ |
| 118 | "--author-date-order" \ |
| 119 | "--ancestry-path side..main" |
| 120 | do |
| 121 | test_expect_success "git log option: $option for path: $path" ' |
| 122 | test_bloom_filters_used "$option -- $path" && |
| 123 | test_config commitgraph.readChangedPaths false && |
| 124 | test_bloom_filters_not_used "$option -- $path" |
| 125 | ' |
| 126 | done |
| 127 | done |
| 128 | |
| 129 | test_expect_success 'git log -- folder works with and without the trailing slash' ' |
| 130 | test_bloom_filters_used "-- A" && |
| 131 | test_bloom_filters_used "-- A/" |
| 132 | ' |
| 133 | |
| 134 | test_expect_success 'git log for path that does not exist. ' ' |
| 135 | test_bloom_filters_used "-- path_does_not_exist" |
| 136 | ' |
| 137 | |
| 138 | test_expect_success 'git log with --walk-reflogs does not use Bloom filters' ' |
| 139 | test_bloom_filters_not_used "--walk-reflogs -- A" |
| 140 | ' |
| 141 | |
| 142 | test_expect_success 'git log -- "." pathspec at root does not use Bloom filters' ' |
| 143 | test_bloom_filters_not_used "-- ." |
| 144 | ' |
| 145 | |
| 146 | test_expect_success 'git log with wildcard that resolves to a single path uses Bloom filters' ' |
| 147 | test_bloom_filters_used "-- *4" && |
| 148 | test_bloom_filters_used "-- *renamed" |
| 149 | ' |
| 150 | |
| 151 | test_expect_success 'git log with multiple literal paths uses Bloom filter' ' |
| 152 | test_bloom_filters_used "-- file4 A/file1" && |
| 153 | test_bloom_filters_used "-- *" && |
| 154 | test_bloom_filters_used "-- file*" |
| 155 | ' |
| 156 | |
| 157 | test_expect_success 'git log with paths all contain non-wildcard part uses Bloom filter' ' |
| 158 | test_bloom_filters_used "-- A/\* file4" && |
| 159 | test_bloom_filters_used "-- A/file\*" && |
| 160 | test_bloom_filters_used "-- * A/\*" |
| 161 | ' |
| 162 | |
| 163 | test_expect_success 'git log with path only contains wildcard part does not use Bloom filter' ' |
| 164 | test_bloom_filters_not_used "-- file\*" && |
| 165 | test_bloom_filters_not_used "-- file\* A/\*" && |
| 166 | test_bloom_filters_not_used "-- file\* *" && |
| 167 | test_bloom_filters_not_used "-- \*" |
| 168 | ' |
| 169 | |
| 170 | test_expect_success 'git log with path contains various magic signatures' ' |
| 171 | cd A && |
| 172 | test_bloom_filters_used "-- \:\(top\)B" && |
| 173 | cd .. && |
| 174 | |
| 175 | test_bloom_filters_used "-- \:\(glob\)A/\*\*/C" && |
| 176 | test_bloom_filters_not_used "-- \:\(icase\)FILE4" && |
| 177 | test_bloom_filters_not_used "-- \:\(exclude\)A/B/C" && |
| 178 | |
| 179 | test_when_finished "rm -f .gitattributes" && |
| 180 | cat >.gitattributes <<-EOF && |
| 181 | A/file1 text |
| 182 | A/B/file2 -text |
| 183 | EOF |
| 184 | test_bloom_filters_used "-- \:\(attr\:text\)A" |
| 185 | ' |
| 186 | |
| 187 | test_expect_success 'setup - add commit-graph to the chain without Bloom filters' ' |
| 188 | test_commit c14 A/anotherFile2 && |
| 189 | test_commit c15 A/B/anotherFile2 && |
| 190 | test_commit c16 A/B/C/anotherFile2 && |
| 191 | git commit-graph write --reachable --split --no-changed-paths && |
| 192 | test_line_count = 2 .git/objects/info/commit-graphs/commit-graph-chain |
| 193 | ' |
| 194 | |
| 195 | test_expect_success 'use Bloom filters even if the latest graph does not have Bloom filters' ' |
| 196 | # Ensure that the number of empty filters is equal to the number of |
| 197 | # filters in the latest graph layer to prove that they are loaded (and |
| 198 | # ignored). |
| 199 | test_bloom_filters_used "-- A/B" 3 |
| 200 | ' |
| 201 | |
| 202 | test_expect_success 'setup - add commit-graph to the chain with Bloom filters' ' |
| 203 | test_commit c17 A/anotherFile3 && |
| 204 | git commit-graph write --reachable --changed-paths --split && |
| 205 | test_line_count = 3 .git/objects/info/commit-graphs/commit-graph-chain |
| 206 | ' |
| 207 | |
| 208 | test_bloom_filters_used_when_some_filters_are_missing () { |
| 209 | log_args=$1 |
| 210 | bloom_trace_prefix="statistics:{\"filter_not_present\":3,\"maybe\":6,\"definitely_not\":10" |
| 211 | setup "$log_args" && |
| 212 | grep -q "$bloom_trace_prefix" "$TRASH_DIRECTORY/trace.perf" && |
| 213 | test_cmp log_wo_bloom log_w_bloom |
| 214 | } |
| 215 | |
| 216 | test_expect_success 'Use Bloom filters if they exist in the latest but not all commit graphs in the chain.' ' |
| 217 | test_bloom_filters_used_when_some_filters_are_missing "-- A/B" |
| 218 | ' |
| 219 | |
| 220 | test_expect_success 'persist filter settings' ' |
| 221 | test_when_finished rm -rf .git/objects/info/commit-graph* && |
| 222 | rm -rf .git/objects/info/commit-graph* && |
| 223 | GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ |
| 224 | GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=9 \ |
| 225 | GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY=15 \ |
| 226 | git commit-graph write --reachable --changed-paths && |
| 227 | grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2.txt && |
| 228 | GIT_TRACE2_EVENT="$(pwd)/trace2-auto.txt" \ |
| 229 | git commit-graph write --reachable --changed-paths && |
| 230 | grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2-auto.txt |
| 231 | ' |
| 232 | |
| 233 | test_max_changed_paths () { |
| 234 | grep "\"max_changed_paths\":$1" $2 |
| 235 | } |
| 236 | |
| 237 | test_filter_not_computed () { |
| 238 | grep "\"key\":\"filter-not-computed\",\"value\":\"$1\"" $2 |
| 239 | } |
| 240 | |
| 241 | test_filter_computed () { |
| 242 | grep "\"key\":\"filter-computed\",\"value\":\"$1\"" $2 |
| 243 | } |
| 244 | |
| 245 | test_filter_trunc_empty () { |
| 246 | grep "\"key\":\"filter-trunc-empty\",\"value\":\"$1\"" $2 |
| 247 | } |
| 248 | |
| 249 | test_filter_trunc_large () { |
| 250 | grep "\"key\":\"filter-trunc-large\",\"value\":\"$1\"" $2 |
| 251 | } |
| 252 | |
| 253 | test_filter_upgraded () { |
| 254 | grep "\"key\":\"filter-upgraded\",\"value\":\"$1\"" $2 |
| 255 | } |
| 256 | |
| 257 | test_expect_success 'correctly report changes over limit' ' |
| 258 | git init limits && |
| 259 | ( |
| 260 | cd limits && |
| 261 | mkdir d && |
| 262 | mkdir d/e && |
| 263 | |
| 264 | for i in $(test_seq 1 2) |
| 265 | do |
| 266 | printf $i >d/file$i.txt && |
| 267 | printf $i >d/e/file$i.txt || return 1 |
| 268 | done && |
| 269 | |
| 270 | mkdir mode && |
| 271 | printf bash >mode/script.sh && |
| 272 | |
| 273 | mkdir foo && |
| 274 | touch foo/bar && |
| 275 | touch foo.txt && |
| 276 | |
| 277 | git add d foo foo.txt mode && |
| 278 | git commit -m "files" && |
| 279 | |
| 280 | # Commit has 7 file and 4 directory adds |
| 281 | GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS=10 \ |
| 282 | GIT_TRACE2_EVENT="$(pwd)/trace" \ |
| 283 | git commit-graph write --reachable --changed-paths && |
| 284 | test_max_changed_paths 10 trace && |
| 285 | test_filter_computed 1 trace && |
| 286 | test_filter_trunc_large 1 trace && |
| 287 | |
| 288 | for path in $(git ls-tree -r --name-only HEAD) |
| 289 | do |
| 290 | git -c commitGraph.readChangedPaths=false log \ |
| 291 | -- $path >expect && |
| 292 | git log -- $path >actual && |
| 293 | test_cmp expect actual || return 1 |
| 294 | done && |
| 295 | |
| 296 | # Make a variety of path changes |
| 297 | printf new1 >d/e/file1.txt && |
| 298 | printf new2 >d/file2.txt && |
| 299 | rm d/e/file2.txt && |
| 300 | rm -r foo && |
| 301 | printf text >foo && |
| 302 | mkdir f && |
| 303 | printf new1 >f/file1.txt && |
| 304 | |
| 305 | # including a mode-only change (counts as modified) |
| 306 | git update-index --chmod=+x mode/script.sh && |
| 307 | |
| 308 | git add foo d f && |
| 309 | git commit -m "complicated" && |
| 310 | |
| 311 | # start from scratch and rebuild |
| 312 | rm -f .git/objects/info/commit-graph && |
| 313 | GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS=10 \ |
| 314 | GIT_TRACE2_EVENT="$(pwd)/trace-edit" \ |
| 315 | git commit-graph write --reachable --changed-paths && |
| 316 | test_max_changed_paths 10 trace-edit && |
| 317 | test_filter_computed 2 trace-edit && |
| 318 | test_filter_trunc_large 2 trace-edit && |
| 319 | |
| 320 | for path in $(git ls-tree -r --name-only HEAD) |
| 321 | do |
| 322 | git -c commitGraph.readChangedPaths=false log \ |
| 323 | -- $path >expect && |
| 324 | git log -- $path >actual && |
| 325 | test_cmp expect actual || return 1 |
| 326 | done && |
| 327 | |
| 328 | # start from scratch and rebuild |
| 329 | rm -f .git/objects/info/commit-graph && |
| 330 | GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS=11 \ |
| 331 | GIT_TRACE2_EVENT="$(pwd)/trace-update" \ |
| 332 | git commit-graph write --reachable --changed-paths && |
| 333 | test_max_changed_paths 11 trace-update && |
| 334 | test_filter_computed 2 trace-update && |
| 335 | test_filter_trunc_large 0 trace-update && |
| 336 | |
| 337 | for path in $(git ls-tree -r --name-only HEAD) |
| 338 | do |
| 339 | git -c commitGraph.readChangedPaths=false log \ |
| 340 | -- $path >expect && |
| 341 | git log -- $path >actual && |
| 342 | test_cmp expect actual || return 1 |
| 343 | done |
| 344 | ) |
| 345 | ' |
| 346 | |
| 347 | test_expect_success 'correctly report commits with no changed paths' ' |
| 348 | git init empty && |
| 349 | test_when_finished "rm -fr empty" && |
| 350 | ( |
| 351 | cd empty && |
| 352 | |
| 353 | git commit --allow-empty -m "initial commit" && |
| 354 | |
| 355 | GIT_TRACE2_EVENT="$(pwd)/trace.event" \ |
| 356 | git commit-graph write --reachable --changed-paths && |
| 357 | test_filter_computed 1 trace.event && |
| 358 | test_filter_not_computed 0 trace.event && |
| 359 | test_filter_trunc_empty 1 trace.event && |
| 360 | test_filter_trunc_large 0 trace.event |
| 361 | ) |
| 362 | ' |
| 363 | |
| 364 | test_expect_success 'Bloom generation is limited by --max-new-filters' ' |
| 365 | ( |
| 366 | cd limits && |
| 367 | test_commit c2 filter && |
| 368 | test_commit c3 filter && |
| 369 | test_commit c4 no-filter && |
| 370 | |
| 371 | rm -f trace.event && |
| 372 | GIT_TRACE2_EVENT="$(pwd)/trace.event" \ |
| 373 | git commit-graph write --reachable --split=replace \ |
| 374 | --changed-paths --max-new-filters=2 && |
| 375 | |
| 376 | test_filter_computed 2 trace.event && |
| 377 | test_filter_not_computed 3 trace.event && |
| 378 | test_filter_trunc_empty 0 trace.event && |
| 379 | test_filter_trunc_large 0 trace.event |
| 380 | ) |
| 381 | ' |
| 382 | |
| 383 | test_expect_success 'Bloom generation backfills previously-skipped filters' ' |
| 384 | # Check specifying commitGraph.maxNewFilters over "git config" works. |
| 385 | test_config -C limits commitGraph.maxNewFilters 1 && |
| 386 | ( |
| 387 | cd limits && |
| 388 | |
| 389 | rm -f trace.event && |
| 390 | GIT_TRACE2_EVENT="$(pwd)/trace.event" \ |
| 391 | git commit-graph write --reachable --changed-paths \ |
| 392 | --split=replace && |
| 393 | test_filter_computed 1 trace.event && |
| 394 | test_filter_not_computed 4 trace.event && |
| 395 | test_filter_trunc_empty 0 trace.event && |
| 396 | test_filter_trunc_large 0 trace.event |
| 397 | ) |
| 398 | ' |
| 399 | |
| 400 | test_expect_success '--max-new-filters overrides configuration' ' |
| 401 | git init override && |
| 402 | test_when_finished "rm -fr override" && |
| 403 | test_config -C override commitGraph.maxNewFilters 2 && |
| 404 | ( |
| 405 | cd override && |
| 406 | test_commit one && |
| 407 | test_commit two && |
| 408 | |
| 409 | rm -f trace.event && |
| 410 | GIT_TRACE2_EVENT="$(pwd)/trace.event" \ |
| 411 | git commit-graph write --reachable --changed-paths \ |
| 412 | --max-new-filters=1 && |
| 413 | test_filter_computed 1 trace.event && |
| 414 | test_filter_not_computed 1 trace.event && |
| 415 | test_filter_trunc_empty 0 trace.event && |
| 416 | test_filter_trunc_large 0 trace.event |
| 417 | ) |
| 418 | ' |
| 419 | |
| 420 | test_expect_success 'Bloom generation backfills empty commits' ' |
| 421 | git init empty && |
| 422 | test_when_finished "rm -fr empty" && |
| 423 | ( |
| 424 | cd empty && |
| 425 | for i in $(test_seq 1 6) |
| 426 | do |
| 427 | git commit --allow-empty -m "$i" || return 1 |
| 428 | done && |
| 429 | |
| 430 | # Generate Bloom filters for empty commits 1-6, two at a time. |
| 431 | for i in $(test_seq 1 3) |
| 432 | do |
| 433 | rm -f trace.event && |
| 434 | GIT_TRACE2_EVENT="$(pwd)/trace.event" \ |
| 435 | git commit-graph write --reachable \ |
| 436 | --changed-paths --max-new-filters=2 && |
| 437 | test_filter_computed 2 trace.event && |
| 438 | test_filter_not_computed 4 trace.event && |
| 439 | test_filter_trunc_empty 2 trace.event && |
| 440 | test_filter_trunc_large 0 trace.event || return 1 |
| 441 | done && |
| 442 | |
| 443 | # Finally, make sure that once all commits have filters, that |
| 444 | # none are subsequently recomputed. |
| 445 | rm -f trace.event && |
| 446 | GIT_TRACE2_EVENT="$(pwd)/trace.event" \ |
| 447 | git commit-graph write --reachable \ |
| 448 | --changed-paths --max-new-filters=2 && |
| 449 | test_filter_computed 0 trace.event && |
| 450 | test_filter_not_computed 6 trace.event && |
| 451 | test_filter_trunc_empty 0 trace.event && |
| 452 | test_filter_trunc_large 0 trace.event |
| 453 | ) |
| 454 | ' |
| 455 | |
| 456 | graph=.git/objects/info/commit-graph |
| 457 | graphdir=.git/objects/info/commit-graphs |
| 458 | chain=$graphdir/commit-graph-chain |
| 459 | |
| 460 | test_expect_success 'setup for mixed Bloom setting tests' ' |
| 461 | repo=mixed-bloom-settings && |
| 462 | |
| 463 | git init $repo && |
| 464 | for i in one two three |
| 465 | do |
| 466 | test_commit -C $repo $i file || return 1 |
| 467 | done |
| 468 | ' |
| 469 | |
| 470 | test_expect_success 'ensure Bloom filters with incompatible settings are ignored' ' |
| 471 | # Compute Bloom filters with "unusual" settings. |
| 472 | git -C $repo rev-parse one >in && |
| 473 | GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=3 git -C $repo commit-graph write \ |
| 474 | --stdin-commits --changed-paths --split <in && |
| 475 | layer=$(head -n 1 $repo/$chain) && |
| 476 | |
| 477 | # A commit-graph layer without Bloom filters "hides" the layers |
| 478 | # below ... |
| 479 | git -C $repo rev-parse two >in && |
| 480 | git -C $repo commit-graph write --stdin-commits --no-changed-paths \ |
| 481 | --split=no-merge <in && |
| 482 | |
| 483 | # Another commit-graph layer that has Bloom filters, but with |
| 484 | # standard settings, and is thus incompatible with the base |
| 485 | # layer written above. |
| 486 | git -C $repo rev-parse HEAD >in && |
| 487 | git -C $repo commit-graph write --stdin-commits --changed-paths \ |
| 488 | --split=no-merge <in && |
| 489 | |
| 490 | test_line_count = 3 $repo/$chain && |
| 491 | |
| 492 | # Ensure that incompatible Bloom filters are ignored. |
| 493 | git -C $repo -c core.commitGraph=false log --oneline --no-decorate -- file \ |
| 494 | >expect 2>err && |
| 495 | git -C $repo log --oneline --no-decorate -- file >actual 2>err && |
| 496 | test_cmp expect actual && |
| 497 | grep "disabling Bloom filters for commit-graph layer .$layer." err |
| 498 | ' |
| 499 | |
| 500 | test_expect_success 'merge graph layers with incompatible Bloom settings' ' |
| 501 | # Ensure that incompatible Bloom filters are ignored when |
| 502 | # merging existing layers. |
| 503 | >trace2.txt && |
| 504 | GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ |
| 505 | git -C $repo commit-graph write --reachable --changed-paths 2>err && |
| 506 | grep "disabling Bloom filters for commit-graph layer .$layer." err && |
| 507 | grep "{\"hash_version\":1,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt && |
| 508 | |
| 509 | test_path_is_file $repo/$graph && |
| 510 | test_dir_is_empty $repo/$graphdir && |
| 511 | |
| 512 | git -C $repo -c core.commitGraph=false log --oneline --no-decorate -- \ |
| 513 | file >expect && |
| 514 | trace_out="$(pwd)/trace.perf" && |
| 515 | GIT_TRACE2_PERF="$trace_out" \ |
| 516 | git -C $repo log --oneline --no-decorate -- file >actual 2>err && |
| 517 | |
| 518 | test_cmp expect actual && |
| 519 | grep "statistics:{\"filter_not_present\":0," trace.perf && |
| 520 | test_must_be_empty err |
| 521 | ' |
| 522 | |
| 523 | # chosen to be the same under all Unicode normalization forms |
| 524 | CENT=$(printf "\302\242") |
| 525 | |
| 526 | test_expect_success 'ensure Bloom filter with incompatible versions are ignored' ' |
| 527 | rm "$repo/$graph" && |
| 528 | |
| 529 | git -C $repo log --oneline --no-decorate -- $CENT >expect && |
| 530 | |
| 531 | # Compute v1 Bloom filters for commits at the bottom. |
| 532 | git -C $repo rev-parse HEAD^ >in && |
| 533 | git -C $repo commit-graph write --stdin-commits --changed-paths \ |
| 534 | --split <in && |
| 535 | |
| 536 | # Compute v2 Bloomfilters for the rest of the commits at the top. |
| 537 | git -C $repo rev-parse HEAD >in && |
| 538 | git -C $repo -c commitGraph.changedPathsVersion=2 commit-graph write \ |
| 539 | --stdin-commits --changed-paths --split=no-merge <in && |
| 540 | |
| 541 | test_line_count = 2 $repo/$chain && |
| 542 | |
| 543 | git -C $repo log --oneline --no-decorate -- $CENT >actual 2>err && |
| 544 | test_cmp expect actual && |
| 545 | |
| 546 | layer="$(head -n 1 $repo/$chain)" && |
| 547 | cat >expect.err <<-EOF && |
| 548 | warning: disabling Bloom filters for commit-graph layer $SQ$layer$SQ due to incompatible settings |
| 549 | EOF |
| 550 | test_cmp expect.err err && |
| 551 | |
| 552 | # Merge the two layers with incompatible bloom filter versions, |
| 553 | # ensuring that the v2 filters are used. |
| 554 | >trace2.txt && |
| 555 | GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ |
| 556 | git -C $repo -c commitGraph.changedPathsVersion=2 commit-graph write --reachable --changed-paths 2>err && |
| 557 | grep "disabling Bloom filters for commit-graph layer .$layer." err && |
| 558 | grep "{\"hash_version\":2,\"num_hashes\":7,\"bits_per_entry\":10,\"max_changed_paths\":512" trace2.txt |
| 559 | ' |
| 560 | |
| 561 | get_first_changed_path_filter () { |
| 562 | test-tool read-graph bloom-filters >filters.dat && |
| 563 | head -n 1 filters.dat |
| 564 | } |
| 565 | |
| 566 | test_expect_success 'set up repo with high bit path, version 1 changed-path' ' |
| 567 | git init highbit1 && |
| 568 | test_commit -C highbit1 c1 "$CENT" && |
| 569 | git -C highbit1 commit-graph write --reachable --changed-paths |
| 570 | ' |
| 571 | |
| 572 | test_expect_success 'setup make another commit' ' |
| 573 | # "git log" does not use Bloom filters for root commits - see how, in |
| 574 | # revision.c, rev_compare_tree() (the only code path that eventually calls |
| 575 | # get_bloom_filter()) is only called by try_to_simplify_commit() when the commit |
| 576 | # has one parent. Therefore, make another commit so that we perform the tests on |
| 577 | # a non-root commit. |
| 578 | test_commit -C highbit1 anotherc1 "another$CENT" |
| 579 | ' |
| 580 | |
| 581 | test_expect_success 'version 1 changed-path used when version 1 requested' ' |
| 582 | ( |
| 583 | cd highbit1 && |
| 584 | test_bloom_filters_used "-- another$CENT" |
| 585 | ) |
| 586 | ' |
| 587 | |
| 588 | test_expect_success 'version 1 changed-path not used when version 2 requested' ' |
| 589 | ( |
| 590 | cd highbit1 && |
| 591 | git config --add commitGraph.changedPathsVersion 2 && |
| 592 | test_bloom_filters_not_used "-- another$CENT" |
| 593 | ) |
| 594 | ' |
| 595 | |
| 596 | test_expect_success 'version 1 changed-path used when autodetect requested' ' |
| 597 | ( |
| 598 | cd highbit1 && |
| 599 | git config --add commitGraph.changedPathsVersion -1 && |
| 600 | test_bloom_filters_used "-- another$CENT" |
| 601 | ) |
| 602 | ' |
| 603 | |
| 604 | test_expect_success 'when writing another commit graph, preserve existing version 1 of changed-path' ' |
| 605 | test_commit -C highbit1 c1double "$CENT$CENT" && |
| 606 | git -C highbit1 commit-graph write --reachable --changed-paths && |
| 607 | ( |
| 608 | cd highbit1 && |
| 609 | git config --add commitGraph.changedPathsVersion -1 && |
| 610 | echo "options: bloom(1,10,7) read_generation_data" >expect && |
| 611 | test-tool read-graph >full && |
| 612 | grep options full >actual && |
| 613 | test_cmp expect actual |
| 614 | ) |
| 615 | ' |
| 616 | |
| 617 | test_expect_success 'set up repo with high bit path, version 2 changed-path' ' |
| 618 | git init highbit2 && |
| 619 | git -C highbit2 config --add commitGraph.changedPathsVersion 2 && |
| 620 | test_commit -C highbit2 c2 "$CENT" && |
| 621 | git -C highbit2 commit-graph write --reachable --changed-paths |
| 622 | ' |
| 623 | |
| 624 | test_expect_success 'check value of version 2 changed-path' ' |
| 625 | ( |
| 626 | cd highbit2 && |
| 627 | echo "c01f" >expect && |
| 628 | get_first_changed_path_filter >actual && |
| 629 | test_cmp expect actual |
| 630 | ) |
| 631 | ' |
| 632 | |
| 633 | test_expect_success 'setup make another commit' ' |
| 634 | # "git log" does not use Bloom filters for root commits - see how, in |
| 635 | # revision.c, rev_compare_tree() (the only code path that eventually calls |
| 636 | # get_bloom_filter()) is only called by try_to_simplify_commit() when the commit |
| 637 | # has one parent. Therefore, make another commit so that we perform the tests on |
| 638 | # a non-root commit. |
| 639 | test_commit -C highbit2 anotherc2 "another$CENT" |
| 640 | ' |
| 641 | |
| 642 | test_expect_success 'version 2 changed-path used when version 2 requested' ' |
| 643 | ( |
| 644 | cd highbit2 && |
| 645 | test_bloom_filters_used "-- another$CENT" |
| 646 | ) |
| 647 | ' |
| 648 | |
| 649 | test_expect_success 'version 2 changed-path not used when version 1 requested' ' |
| 650 | ( |
| 651 | cd highbit2 && |
| 652 | git config --add commitGraph.changedPathsVersion 1 && |
| 653 | test_bloom_filters_not_used "-- another$CENT" |
| 654 | ) |
| 655 | ' |
| 656 | |
| 657 | test_expect_success 'version 2 changed-path used when autodetect requested' ' |
| 658 | ( |
| 659 | cd highbit2 && |
| 660 | git config --add commitGraph.changedPathsVersion -1 && |
| 661 | test_bloom_filters_used "-- another$CENT" |
| 662 | ) |
| 663 | ' |
| 664 | |
| 665 | test_expect_success 'when writing another commit graph, preserve existing version 2 of changed-path' ' |
| 666 | test_commit -C highbit2 c2double "$CENT$CENT" && |
| 667 | git -C highbit2 commit-graph write --reachable --changed-paths && |
| 668 | ( |
| 669 | cd highbit2 && |
| 670 | git config --add commitGraph.changedPathsVersion -1 && |
| 671 | echo "options: bloom(2,10,7) read_generation_data" >expect && |
| 672 | test-tool read-graph >full && |
| 673 | grep options full >actual && |
| 674 | test_cmp expect actual |
| 675 | ) |
| 676 | ' |
| 677 | |
| 678 | test_expect_success 'when writing commit graph, do not reuse changed-path of another version' ' |
| 679 | git init doublewrite && |
| 680 | test_commit -C doublewrite c "$CENT" && |
| 681 | |
| 682 | git -C doublewrite config --add commitGraph.changedPathsVersion 1 && |
| 683 | >trace2.txt && |
| 684 | GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ |
| 685 | git -C doublewrite commit-graph write --reachable --changed-paths && |
| 686 | test_filter_computed 1 trace2.txt && |
| 687 | test_filter_upgraded 0 trace2.txt && |
| 688 | |
| 689 | git -C doublewrite commit-graph write --reachable --changed-paths && |
| 690 | for v in -2 3 |
| 691 | do |
| 692 | git -C doublewrite config --add commitGraph.changedPathsVersion $v && |
| 693 | git -C doublewrite commit-graph write --reachable --changed-paths 2>err && |
| 694 | cat >expect <<-EOF && |
| 695 | warning: attempting to write a commit-graph, but ${SQ}commitGraph.changedPathsVersion${SQ} ($v) is not supported |
| 696 | EOF |
| 697 | test_cmp expect err || return 1 |
| 698 | done && |
| 699 | |
| 700 | git -C doublewrite config --add commitGraph.changedPathsVersion 2 && |
| 701 | >trace2.txt && |
| 702 | GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ |
| 703 | git -C doublewrite commit-graph write --reachable --changed-paths && |
| 704 | test_filter_computed 1 trace2.txt && |
| 705 | test_filter_upgraded 0 trace2.txt && |
| 706 | |
| 707 | ( |
| 708 | cd doublewrite && |
| 709 | echo "c01f" >expect && |
| 710 | get_first_changed_path_filter >actual && |
| 711 | test_cmp expect actual |
| 712 | ) |
| 713 | ' |
| 714 | |
| 715 | test_expect_success 'when writing commit graph, reuse changed-path of another version where possible' ' |
| 716 | git init upgrade && |
| 717 | |
| 718 | test_commit -C upgrade base no-high-bits && |
| 719 | |
| 720 | git -C upgrade config --add commitGraph.changedPathsVersion 1 && |
| 721 | >trace2.txt && |
| 722 | GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ |
| 723 | git -C upgrade commit-graph write --reachable --changed-paths && |
| 724 | test_filter_computed 1 trace2.txt && |
| 725 | test_filter_upgraded 0 trace2.txt && |
| 726 | |
| 727 | git -C upgrade config --add commitGraph.changedPathsVersion 2 && |
| 728 | >trace2.txt && |
| 729 | GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ |
| 730 | git -C upgrade commit-graph write --reachable --changed-paths && |
| 731 | test_filter_computed 0 trace2.txt && |
| 732 | test_filter_upgraded 1 trace2.txt |
| 733 | ' |
| 734 | |
| 735 | corrupt_graph () { |
| 736 | test_when_finished "rm -rf $graph" && |
| 737 | git commit-graph write --reachable --changed-paths && |
| 738 | corrupt_chunk_file $graph "$@" |
| 739 | } |
| 740 | |
| 741 | check_corrupt_graph () { |
| 742 | corrupt_graph "$@" && |
| 743 | git -c core.commitGraph=false log -- A/B/file2 >expect.out && |
| 744 | git -c core.commitGraph=true log -- A/B/file2 >out 2>err && |
| 745 | test_cmp expect.out out |
| 746 | } |
| 747 | |
| 748 | test_expect_success PERL_TEST_HELPERS 'Bloom reader notices too-small data chunk' ' |
| 749 | check_corrupt_graph BDAT clear 00000000 && |
| 750 | echo "warning: ignoring too-small changed-path chunk" \ |
| 751 | "(4 < 12) in commit-graph file" >expect.err && |
| 752 | test_cmp expect.err err |
| 753 | ' |
| 754 | |
| 755 | test_expect_success PERL_TEST_HELPERS 'Bloom reader notices out-of-bounds filter offsets' ' |
| 756 | check_corrupt_graph BIDX 12 FFFFFFFF && |
| 757 | # use grep to avoid depending on exact chunk size |
| 758 | grep "warning: ignoring out-of-range offset (4294967295) for changed-path filter at pos 3 of .git/objects/info/commit-graph" err |
| 759 | ' |
| 760 | |
| 761 | test_expect_success PERL_TEST_HELPERS 'Bloom reader notices too-small index chunk' ' |
| 762 | # replace the index with a single entry, making most |
| 763 | # lookups out-of-bounds |
| 764 | check_corrupt_graph BIDX clear 00000000 && |
| 765 | echo "warning: commit-graph changed-path index chunk" \ |
| 766 | "is too small" >expect.err && |
| 767 | test_cmp expect.err err |
| 768 | ' |
| 769 | |
| 770 | test_expect_success PERL_TEST_HELPERS 'Bloom reader notices out-of-order index offsets' ' |
| 771 | # we do not know any real offsets, but we can pick |
| 772 | # something plausible; we should not get to the point of |
| 773 | # actually reading from the bogus offsets anyway. |
| 774 | corrupt_graph BIDX 4 0000000c00000005 && |
| 775 | echo "warning: ignoring decreasing changed-path index offsets" \ |
| 776 | "(12 > 5) for positions 1 and 2 of .git/objects/info/commit-graph" >expect.err && |
| 777 | git -c core.commitGraph=false log -- A/B/file2 >expect.out && |
| 778 | git -c core.commitGraph=true log -- A/B/file2 >out 2>err && |
| 779 | test_cmp expect.out out && |
| 780 | test_cmp expect.err err |
| 781 | ' |
| 782 | |
| 783 | test_done |