| #!/bin/sh | |
| test_description='parallel-checkout basics | |
| Ensure that parallel-checkout basically works on clone and checkout, spawning | |
| the required number of workers and correctly populating both the index and the | |
| working tree. | |
| ' | |
| TEST_NO_CREATE_REPO=1 | |
| . ./test-lib.sh | |
| . "$TEST_DIRECTORY/lib-parallel-checkout.sh" | |
| # Test parallel-checkout with a branch switch containing a variety of file | |
| # creations, deletions, and modifications, involving different entry types. | |
| # The branches B1 and B2 have the following paths: | |
| # | |
| # B1 B2 | |
| # a/a (file) a (file) | |
| # b (file) b/b (file) | |
| # | |
| # c/c (file) c (symlink) | |
| # d (symlink) d/d (file) | |
| # | |
| # e/e (file) e (submodule) | |
| # f (submodule) f/f (file) | |
| # | |
| # g (submodule) g (symlink) | |
| # h (symlink) h (submodule) | |
| # | |
| # Additionally, the following paths are present on both branches, but with | |
| # different contents: | |
| # | |
| # i (file) i (file) | |
| # j (symlink) j (symlink) | |
| # k (submodule) k (submodule) | |
| # | |
| # And the following paths are only present in one of the branches: | |
| # | |
| # l/l (file) - | |
| # - m/m (file) | |
| # | |
| test_expect_success 'setup repo for checkout with various types of changes' ' | |
| test_config_global protocol.file.allow always && | |
| git init sub && | |
| ( | |
| cd sub && | |
| git checkout -b B2 && | |
| echo B2 >file && | |
| git add file && | |
| git commit -m file && | |
| git checkout -b B1 && | |
| echo B1 >file && | |
| git add file && | |
| git commit -m file | |
| ) && | |
| git init various && | |
| ( | |
| cd various && | |
| git checkout -b B1 && | |
| mkdir a c e && | |
| echo a/a >a/a && | |
| echo b >b && | |
| echo c/c >c/c && | |
| test_ln_s_add c d && | |
| echo e/e >e/e && | |
| git submodule add ../sub f && | |
| git submodule add ../sub g && | |
| test_ln_s_add c h && | |
| echo "B1 i" >i && | |
| test_ln_s_add c j && | |
| git submodule add -b B1 ../sub k && | |
| mkdir l && | |
| echo l/l >l/l && | |
| git add . && | |
| git commit -m B1 && | |
| git checkout -b B2 && | |
| git rm -rf :^.gitmodules :^k && | |
| mkdir b d f && | |
| echo a >a && | |
| echo b/b >b/b && | |
| test_ln_s_add b c && | |
| echo d/d >d/d && | |
| git submodule add ../sub e && | |
| echo f/f >f/f && | |
| test_ln_s_add b g && | |
| git submodule add ../sub h && | |
| echo "B2 i" >i && | |
| test_ln_s_add b j && | |
| git -C k checkout B2 && | |
| mkdir m && | |
| echo m/m >m/m && | |
| git add . && | |
| git commit -m B2 && | |
| git checkout --recurse-submodules B1 | |
| ) | |
| ' | |
| for mode in sequential parallel sequential-fallback | |
| do | |
| case $mode in | |
| sequential) workers=1 threshold=0 expected_workers=0 ;; | |
| parallel) workers=2 threshold=0 expected_workers=2 ;; | |
| sequential-fallback) workers=2 threshold=100 expected_workers=0 ;; | |
| esac | |
| test_expect_success "$mode checkout" ' | |
| repo=various_$mode && | |
| cp -R -P various $repo && | |
| # The just copied files have more recent timestamps than their | |
| # associated index entries. So refresh the cached timestamps | |
| # to avoid an "entry not up-to-date" error from `git checkout`. | |
| # We only have to do this for the submodules as `git checkout` | |
| # will already refresh the superproject index before performing | |
| # the up-to-date check. | |
| # | |
| git -C $repo submodule foreach "git update-index --refresh" && | |
| set_checkout_config $workers $threshold && | |
| test_checkout_workers $expected_workers \ | |
| git -C $repo checkout --recurse-submodules B2 && | |
| verify_checkout $repo | |
| ' | |
| done | |
| for mode in parallel sequential-fallback | |
| do | |
| case $mode in | |
| parallel) workers=2 threshold=0 expected_workers=2 ;; | |
| sequential-fallback) workers=2 threshold=100 expected_workers=0 ;; | |
| esac | |
| test_expect_success "$mode checkout on clone" ' | |
| test_config_global protocol.file.allow always && | |
| repo=various_${mode}_clone && | |
| set_checkout_config $workers $threshold && | |
| test_checkout_workers $expected_workers \ | |
| git clone --recurse-submodules --branch B2 various $repo && | |
| verify_checkout $repo | |
| ' | |
| done | |
| # Just to be paranoid, actually compare the working trees' contents directly. | |
| test_expect_success 'compare the working trees' ' | |
| rm -rf various_*/.git && | |
| rm -rf various_*/*/.git && | |
| # We use `git diff` instead of `diff -r` because the latter would | |
| # follow symlinks, and not all `diff` implementations support the | |
| # `--no-dereference` option. | |
| # | |
| git diff --no-index various_sequential various_parallel && | |
| git diff --no-index various_sequential various_parallel_clone && | |
| git diff --no-index various_sequential various_sequential-fallback && | |
| git diff --no-index various_sequential various_sequential-fallback_clone | |
| ' | |
| # Currently, each submodule is checked out in a separated child process, but | |
| # these subprocesses must also be able to use parallel checkout workers to | |
| # write the submodules' entries. | |
| test_expect_success 'submodules can use parallel checkout' ' | |
| set_checkout_config 2 0 && | |
| git init super && | |
| ( | |
| cd super && | |
| git init sub && | |
| test_commit -C sub A && | |
| test_commit -C sub B && | |
| git submodule add ./sub && | |
| git commit -m sub && | |
| rm sub/* && | |
| test_checkout_workers 2 git checkout --recurse-submodules . | |
| ) | |
| ' | |
| test_expect_success 'parallel checkout respects --[no]-force' ' | |
| set_checkout_config 2 0 && | |
| git init dirty && | |
| ( | |
| cd dirty && | |
| mkdir D && | |
| test_commit D/F && | |
| test_commit F && | |
| rm -rf D && | |
| echo changed >D && | |
| echo changed >F.t && | |
| # We expect 0 workers because there is nothing to be done | |
| test_checkout_workers 0 git checkout HEAD && | |
| test_path_is_file D && | |
| test_grep changed D && | |
| test_grep changed F.t && | |
| test_checkout_workers 2 git checkout --force HEAD && | |
| test_path_is_dir D && | |
| test_grep D/F D/F.t && | |
| test_grep F F.t | |
| ) | |
| ' | |
| test_expect_success SYMLINKS 'parallel checkout checks for symlinks in leading dirs' ' | |
| set_checkout_config 2 0 && | |
| git init symlinks && | |
| ( | |
| cd symlinks && | |
| mkdir D untracked && | |
| # Commit 2 files to have enough work for 2 parallel workers | |
| test_commit D/A && | |
| test_commit D/B && | |
| rm -rf D && | |
| ln -s untracked D && | |
| test_checkout_workers 2 git checkout --force HEAD && | |
| ! test -h D && | |
| test_grep D/A D/A.t && | |
| test_grep D/B D/B.t | |
| ) | |
| ' | |
| # This test is here (and not in e.g. t2022-checkout-paths.sh), because we | |
| # check the final report including sequential, parallel, and delayed entries | |
| # all at the same time. So we must have finer control of the parallel checkout | |
| # variables. | |
| test_expect_success '"git checkout ." report should not include failed entries' ' | |
| test_config_global filter.delay.process \ | |
| "test-tool rot13-filter --always-delay --log=delayed.log clean smudge delay" && | |
| test_config_global filter.delay.required true && | |
| test_config_global filter.cat.clean cat && | |
| test_config_global filter.cat.smudge cat && | |
| test_config_global filter.cat.required true && | |
| set_checkout_config 2 0 && | |
| git init failed_entries && | |
| ( | |
| cd failed_entries && | |
| cat >.gitattributes <<-EOF && | |
| *delay* filter=delay | |
| parallel-ineligible* filter=cat | |
| EOF | |
| echo a >missing-delay.a && | |
| echo a >parallel-ineligible.a && | |
| echo a >parallel-eligible.a && | |
| echo b >success-delay.b && | |
| echo b >parallel-ineligible.b && | |
| echo b >parallel-eligible.b && | |
| git add -A && | |
| git commit -m files && | |
| a_blob="$(git rev-parse :parallel-ineligible.a)" && | |
| rm .git/objects/$(test_oid_to_path $a_blob) && | |
| rm *.a *.b && | |
| test_checkout_workers 2 test_must_fail git checkout . 2>err && | |
| # All *.b entries should succeed and all *.a entries should fail: | |
| # - missing-delay.a: the delay filter will drop this path | |
| # - parallel-*.a: the blob will be missing | |
| # | |
| test_grep "Updated 3 paths from the index" err && | |
| test_stdout_line_count = 3 ls *.b && | |
| ! ls *.a | |
| ) | |
| ' | |
| test_done |