|
| 1 | +#!/bin/sh |
| 2 | + |
| 3 | +test_description='parallel-checkout collisions' |
| 4 | + |
| 5 | +. ./test-lib.sh |
| 6 | + |
| 7 | +# When there are pathname collisions during a clone, Git should report a warning |
| 8 | +# listing all of the colliding entries. The sequential code detects a collision |
| 9 | +# by calling lstat() before trying to open(O_CREAT) the file. Then, to find the |
| 10 | +# colliding pair of an item k, it searches cache_entry[0, k-1]. |
| 11 | +# |
| 12 | +# This is not sufficient in parallel-checkout mode since colliding files may be |
| 13 | +# created in a racy order. The tests in this file make sure the collision |
| 14 | +# detection code is extended for parallel-checkout. This is done in two parts: |
| 15 | +# |
| 16 | +# - First, two parallel workers create four colliding files racily. |
| 17 | +# - Then this exercise is repeated but forcing the colliding pair to appear in |
| 18 | +# the second half of the cache_entry's array. |
| 19 | +# |
| 20 | +# The second item uses the fact that files with clean/smudge filters are not |
| 21 | +# parallel-eligible; and that they are processed sequentially *before* any |
| 22 | +# worker is spawned. We set a filter attribute to the last entry in the |
| 23 | +# cache_entry[] array, making it non-eligible, so that it is populated first. |
| 24 | +# This way, we can test if the collision detection code is correctly looking |
| 25 | +# for collision pairs in the second half of the array. |
| 26 | + |
| 27 | +test_expect_success CASE_INSENSITIVE_FS 'setup' ' |
| 28 | + file_hex=$(git hash-object -w --stdin </dev/null) && |
| 29 | + file_oct=$(echo $file_hex | hex2oct) && |
| 30 | +
|
| 31 | + attr_hex=$(echo "file_x filter=logger" | git hash-object -w --stdin) && |
| 32 | + attr_oct=$(echo $attr_hex | hex2oct) && |
| 33 | +
|
| 34 | + printf "100644 FILE_X\0${file_oct}" >tree && |
| 35 | + printf "100644 FILE_x\0${file_oct}" >>tree && |
| 36 | + printf "100644 file_X\0${file_oct}" >>tree && |
| 37 | + printf "100644 file_x\0${file_oct}" >>tree && |
| 38 | + printf "100644 .gitattributes\0${attr_oct}" >>tree && |
| 39 | +
|
| 40 | + tree_hex=$(git hash-object -w -t tree --stdin <tree) && |
| 41 | + commit_hex=$(git commit-tree -m collisions $tree_hex) && |
| 42 | + git update-ref refs/heads/collisions $commit_hex && |
| 43 | +
|
| 44 | + write_script logger_script <<-\EOF |
| 45 | + echo "$@" >>filter.log |
| 46 | + EOF |
| 47 | +' |
| 48 | + |
| 49 | +clone_and_check_collision() |
| 50 | +{ |
| 51 | + id=$1 workers=$2 threshold=$3 expected_workers=$4 filter=$5 && |
| 52 | + |
| 53 | + filter_opts= |
| 54 | + if test "$filter" -eq "use_filter" |
| 55 | + then |
| 56 | + # We use `core.ignoreCase=0` so that only `file_x` |
| 57 | + # matches the pattern in .gitattributes. |
| 58 | + # |
| 59 | + filter_opts='-c filter.logger.smudge="../logger_script %f" -c core.ignoreCase=0' |
| 60 | + fi && |
| 61 | + |
| 62 | + test_path_is_missing $id.trace && |
| 63 | + GIT_TRACE2="$(pwd)/$id.trace" git \ |
| 64 | + -c checkout.workers=$workers \ |
| 65 | + -c checkout.thresholdForParallelism=$threshold \ |
| 66 | + $filter_opts clone --branch=collisions -- . r_$id 2>$id.warning && |
| 67 | + |
| 68 | + # Check that checkout spawned the right number of workers |
| 69 | + workers_in_trace=$(grep "child_start\[.\] git checkout--helper" $id.trace | wc -l) && |
| 70 | + test $workers_in_trace -eq $expected_workers && |
| 71 | + |
| 72 | + if test $filter -eq "use_filter" |
| 73 | + then |
| 74 | + # Make sure only 'file_x' was filtered |
| 75 | + test_path_is_file r_$id/filter.log && |
| 76 | + echo file_x >expected.filter.log && |
| 77 | + test_cmp r_$id/filter.log expected.filter.log |
| 78 | + else |
| 79 | + test_path_is_missing r_$id/filter.log |
| 80 | + fi && |
| 81 | + |
| 82 | + grep FILE_X $id.warning && |
| 83 | + grep FILE_x $id.warning && |
| 84 | + grep file_X $id.warning && |
| 85 | + grep file_x $id.warning && |
| 86 | + test_i18ngrep "the following paths have collided" $id.warning |
| 87 | +} |
| 88 | + |
| 89 | +test_expect_success CASE_INSENSITIVE_FS 'collision detection on parallel clone' ' |
| 90 | + clone_and_check_collision parallel 2 0 2 |
| 91 | +' |
| 92 | + |
| 93 | +test_expect_success CASE_INSENSITIVE_FS 'collision detection on fallback to sequential clone' ' |
| 94 | + git ls-tree --name-only -r collisions >files && |
| 95 | + nr_files=$(wc -l <files) && |
| 96 | + threshold=$(($nr_files + 1)) && |
| 97 | + clone_and_check_collision sequential 2 $threshold 0 |
| 98 | +' |
| 99 | + |
| 100 | +# The next two tests don't work on Windows because, on this system, collision |
| 101 | +# detection uses strcmp() (when core.ignoreCase=0) to find the colliding pair. |
| 102 | +# But they work on OSX, where collision detection uses inode. |
| 103 | + |
| 104 | +test_expect_success CASE_INSENSITIVE_FS,!MINGW,!CYGWIN 'collision detection on parallel clone w/ filter' ' |
| 105 | + clone_and_check_collision parallel-with-filter 2 0 2 use_filter |
| 106 | +' |
| 107 | + |
| 108 | +test_expect_success CASE_INSENSITIVE_FS,!MINGW,!CYGWIN 'collision detection on fallback to sequential clone w/ filter' ' |
| 109 | + git ls-tree --name-only -r collisions >files && |
| 110 | + nr_files=$(wc -l <files) && |
| 111 | + threshold=$(($nr_files + 1)) && |
| 112 | + clone_and_check_collision sequential-with-filter 2 $threshold 0 use_filter |
| 113 | +' |
| 114 | + |
| 115 | +test_done |
0 commit comments