diff options
Diffstat (limited to 't/t1092-sparse-checkout-compatibility.sh')
-rwxr-xr-x | t/t1092-sparse-checkout-compatibility.sh | 448 |
1 files changed, 394 insertions, 54 deletions
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index 16fbd2c6db..4ba1617752 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -19,6 +19,8 @@ test_expect_success 'setup' ' mkdir folder1 folder2 deep x && mkdir deep/deeper1 deep/deeper2 deep/before deep/later && mkdir deep/deeper1/deepest && + mkdir deep/deeper1/deepest2 && + mkdir deep/deeper1/deepest3 && echo "after deeper1" >deep/e && echo "after deepest" >deep/deeper1/e && cp a folder1 && @@ -30,7 +32,9 @@ test_expect_success 'setup' ' cp a deep/deeper2 && cp a deep/later && cp a deep/deeper1/deepest && - cp -r deep/deeper1/deepest deep/deeper2 && + cp a deep/deeper1/deepest2 && + cp a deep/deeper1/deepest3 && + cp -r deep/deeper1/ deep/deeper2 && mkdir deep/deeper1/0 && mkdir deep/deeper1/0/0 && touch deep/deeper1/0/1 && @@ -126,6 +130,8 @@ test_expect_success 'setup' ' git checkout -b deepest base && echo "updated deepest" >deep/deeper1/deepest/a && + echo "updated deepest2" >deep/deeper1/deepest2/a && + echo "updated deepest3" >deep/deeper1/deepest3/a && git commit -a -m "update deepest" && git checkout -f base && @@ -200,45 +206,42 @@ test_sparse_unstaged () { test_expect_success 'sparse-index contents' ' init_repos && - test-tool -C sparse-index read-cache --table >cache && + git -C sparse-index ls-files --sparse --stage >cache && for dir in folder1 folder2 x do TREE=$(git -C sparse-index rev-parse HEAD:$dir) && - grep "040000 tree $TREE $dir/" cache \ + grep "040000 $TREE 0 $dir/" cache \ || return 1 done && git -C sparse-index sparse-checkout set folder1 && - test-tool -C sparse-index read-cache --table >cache && + git -C sparse-index ls-files --sparse --stage >cache && for dir in deep folder2 x do TREE=$(git -C sparse-index rev-parse HEAD:$dir) && - grep "040000 tree $TREE $dir/" cache \ + grep "040000 $TREE 0 $dir/" cache \ || return 1 done && git -C sparse-index sparse-checkout set deep/deeper1 && - test-tool -C sparse-index read-cache --table >cache && + git -C sparse-index ls-files --sparse --stage >cache && for dir in deep/deeper2 folder1 folder2 x do TREE=$(git -C sparse-index rev-parse HEAD:$dir) && - grep "040000 tree $TREE $dir/" cache \ + grep "040000 $TREE 0 $dir/" cache \ || return 1 done && - # Disabling the sparse-index removes tree entries with full ones + # Disabling the sparse-index replaces tree entries with full ones git -C sparse-index sparse-checkout init --no-sparse-index && - - test-tool -C sparse-index read-cache --table >cache && - ! grep "040000 tree" cache && - test_sparse_match test-tool read-cache --table + test_sparse_match git ls-files --stage --sparse ' test_expect_success 'expanded in-memory index matches full index' ' init_repos && - test_sparse_match test-tool read-cache --expand --table + test_sparse_match git ls-files --stage ' test_expect_success 'status with options' ' @@ -301,6 +304,14 @@ test_expect_success 'add, commit, checkout' ' test_all_match git checkout - ' +test_expect_success 'deep changes during checkout' ' + init_repos && + + test_sparse_match git sparse-checkout set deep/deeper1/deepest && + test_all_match git checkout deepest && + test_all_match git checkout base +' + test_expect_success 'add outside sparse cone' ' init_repos && @@ -401,7 +412,7 @@ test_expect_success 'checkout and reset --hard' ' test_all_match git reset --hard update-folder2 ' -test_expect_success 'diff --staged' ' +test_expect_success 'diff --cached' ' init_repos && write_script edit-contents <<-\EOF && @@ -410,10 +421,10 @@ test_expect_success 'diff --staged' ' run_on_all ../edit-contents && test_all_match git diff && - test_all_match git diff --staged && + test_all_match git diff --cached && test_all_match git add README.md && test_all_match git diff && - test_all_match git diff --staged + test_all_match git diff --cached ' # NEEDSWORK: sparse-checkout behaves differently from full-checkout when @@ -430,8 +441,8 @@ test_expect_success 'diff with renames and conflicts' ' test_all_match git checkout rename-base && test_all_match git checkout $branch -- . && test_all_match git status --porcelain=v2 && - test_all_match git diff --staged --no-renames && - test_all_match git diff --staged --find-renames || return 1 + test_all_match git diff --cached --no-renames && + test_all_match git diff --cached --find-renames || return 1 done ' @@ -450,8 +461,8 @@ test_expect_success 'diff with directory/file conflicts' ' test_all_match git checkout $branch && test_all_match git checkout rename-base -- . && test_all_match git status --porcelain=v2 && - test_all_match git diff --staged --no-renames && - test_all_match git diff --staged --find-renames || return 1 + test_all_match git diff --cached --no-renames && + test_all_match git diff --cached --find-renames || return 1 done ' @@ -472,43 +483,150 @@ test_expect_success 'log with pathspec outside sparse definition' ' test_expect_success 'blame with pathspec inside sparse definition' ' init_repos && - test_all_match git blame a && - test_all_match git blame deep/a && - test_all_match git blame deep/deeper1/a && - test_all_match git blame deep/deeper1/deepest/a + for file in a \ + deep/a \ + deep/deeper1/a \ + deep/deeper1/deepest/a + do + test_all_match git blame $file + done ' -# TODO: blame currently does not support blaming files outside of the -# sparse definition. It complains that the file doesn't exist locally. -test_expect_failure 'blame with pathspec outside sparse definition' ' +# Without a revision specified, blame will error if passed any file that +# is not present in the working directory (even if the file is tracked). +# Here we just verify that this is also true with sparse checkouts. +test_expect_success 'blame with pathspec outside sparse definition' ' init_repos && + test_sparse_match git sparse-checkout set && - test_all_match git blame folder1/a && - test_all_match git blame folder2/a && - test_all_match git blame deep/deeper2/a && - test_all_match git blame deep/deeper2/deepest/a + for file in a \ + deep/a \ + deep/deeper1/a \ + deep/deeper1/deepest/a + do + test_sparse_match test_must_fail git blame $file && + cat >expect <<-EOF && + fatal: Cannot lstat '"'"'$file'"'"': No such file or directory + EOF + # We compare sparse-checkout-err and sparse-index-err in + # `test_sparse_match`. Given we know they are the same, we + # only check the content of sparse-index-err here. + test_cmp expect sparse-index-err + done ' -# NEEDSWORK: a sparse-checkout behaves differently from a full checkout -# in this scenario, but it shouldn't. -test_expect_failure 'checkout and reset (mixed)' ' +test_expect_success 'checkout and reset (mixed)' ' init_repos && test_all_match git checkout -b reset-test update-deep && test_all_match git reset deepest && - test_all_match git reset update-folder1 && - test_all_match git reset update-folder2 + + # Because skip-worktree is preserved, resetting to update-folder1 + # will show worktree changes for folder1/a in full-checkout, but not + # in sparse-checkout or sparse-index. + git -C full-checkout reset update-folder1 >full-checkout-out && + test_sparse_match git reset update-folder1 && + grep "M folder1/a" full-checkout-out && + ! grep "M folder1/a" sparse-checkout-out && + run_on_sparse test_path_is_missing folder1 ' -# NEEDSWORK: a sparse-checkout behaves differently from a full checkout -# in this scenario, but it shouldn't. -test_expect_success 'checkout and reset (mixed) [sparse]' ' +test_expect_success 'checkout and reset (merge)' ' init_repos && - test_sparse_match git checkout -b reset-test update-deep && - test_sparse_match git reset deepest && - test_sparse_match git reset update-folder1 && - test_sparse_match git reset update-folder2 + write_script edit-contents <<-\EOF && + echo text >>$1 + EOF + + test_all_match git checkout -b reset-test update-deep && + run_on_all ../edit-contents a && + test_all_match git reset --merge deepest && + test_all_match git status --porcelain=v2 && + + test_all_match git reset --hard update-deep && + run_on_all ../edit-contents deep/a && + test_all_match test_must_fail git reset --merge deepest +' + +test_expect_success 'checkout and reset (keep)' ' + init_repos && + + write_script edit-contents <<-\EOF && + echo text >>$1 + EOF + + test_all_match git checkout -b reset-test update-deep && + run_on_all ../edit-contents a && + test_all_match git reset --keep deepest && + test_all_match git status --porcelain=v2 && + + test_all_match git reset --hard update-deep && + run_on_all ../edit-contents deep/a && + test_all_match test_must_fail git reset --keep deepest +' + +test_expect_success 'reset with pathspecs inside sparse definition' ' + init_repos && + + write_script edit-contents <<-\EOF && + echo text >>$1 + EOF + + test_all_match git checkout -b reset-test update-deep && + run_on_all ../edit-contents deep/a && + + test_all_match git reset base -- deep/a && + test_all_match git status --porcelain=v2 && + + test_all_match git reset base -- nonexistent-file && + test_all_match git status --porcelain=v2 && + + test_all_match git reset deepest -- deep && + test_all_match git status --porcelain=v2 +' + +# Although the working tree differs between full and sparse checkouts after +# reset, the state of the index is the same. +test_expect_success 'reset with pathspecs outside sparse definition' ' + init_repos && + test_all_match git checkout -b reset-test base && + + test_sparse_match git reset update-folder1 -- folder1 && + git -C full-checkout reset update-folder1 -- folder1 && + test_sparse_match git status --porcelain=v2 && + test_all_match git rev-parse HEAD:folder1 && + + test_sparse_match git reset update-folder2 -- folder2/a && + git -C full-checkout reset update-folder2 -- folder2/a && + test_sparse_match git status --porcelain=v2 && + test_all_match git rev-parse HEAD:folder2/a +' + +test_expect_success 'reset with wildcard pathspec' ' + init_repos && + + test_all_match git reset update-deep -- deep\* && + test_all_match git ls-files -s -- deep && + + test_all_match git reset deepest -- deep\*\*\* && + test_all_match git ls-files -s -- deep && + + # The following `git reset`s result in updating the index on files with + # `skip-worktree` enabled. To avoid failing due to discrepencies in reported + # "modified" files, `test_sparse_match` reset is performed separately from + # "full-checkout" reset, then the index contents of all repos are verified. + + test_sparse_match git reset update-folder1 -- \*/a && + git -C full-checkout reset update-folder1 -- \*/a && + test_all_match git ls-files -s -- deep/a folder1/a && + + test_sparse_match git reset update-folder2 -- folder\* && + git -C full-checkout reset update-folder2 -- folder\* && + test_all_match git ls-files -s -- folder10 folder1 folder2 && + + test_sparse_match git reset base -- folder1/\* && + git -C full-checkout reset base -- folder1/\* && + test_all_match git ls-files -s -- folder1 ' test_expect_success 'merge, cherry-pick, and rebase' ' @@ -680,20 +798,61 @@ test_expect_success 'submodule handling' ' # having a submodule prevents "modules" from collapse test_sparse_match git sparse-checkout set deep/deeper1 && - test-tool -C sparse-index read-cache --table >cache && - grep "100644 blob .* modules/a" cache && - grep "160000 commit $(git -C initial-repo rev-parse HEAD) modules/sub" cache + git -C sparse-index ls-files --sparse --stage >cache && + grep "100644 .* modules/a" cache && + grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache ' +# When working with a sparse index, some commands will need to expand the +# index to operate properly. If those commands also write the index back +# to disk, they need to convert the index to sparse before writing. +# This test verifies that both of these events are logged in trace2 logs. test_expect_success 'sparse-index is expanded and converted back' ' init_repos && - GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ - git -C sparse-index -c core.fsmonitor="" reset --hard && + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ + git -C sparse-index reset -- folder1/a && test_region index convert_to_sparse trace2.txt && + test_region index ensure_full_index trace2.txt && + + # ls-files expands on read, but does not write. + rm trace2.txt && + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ + git -C sparse-index ls-files && test_region index ensure_full_index trace2.txt ' +test_expect_success 'index.sparse disabled inline uses full index' ' + init_repos && + + # When index.sparse is disabled inline with `git status`, the + # index is expanded at the beginning of the execution then never + # converted back to sparse. It is then written to disk as a full index. + rm -f trace2.txt && + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ + git -C sparse-index -c index.sparse=false status && + ! test_region index convert_to_sparse trace2.txt && + test_region index ensure_full_index trace2.txt && + + # Since index.sparse is set to true at a repo level, the index + # is converted from full to sparse when read, then never expanded + # over the course of `git status`. It is written to disk as a sparse + # index. + rm -f trace2.txt && + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ + git -C sparse-index status && + test_region index convert_to_sparse trace2.txt && + ! test_region index ensure_full_index trace2.txt && + + # Now that the index has been written to disk as sparse, it is not + # converted to sparse (or expanded to full) when read by `git status`. + rm -f trace2.txt && + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ + git -C sparse-index status && + ! test_region index convert_to_sparse trace2.txt && + ! test_region index ensure_full_index trace2.txt +' + ensure_not_expanded () { rm -f trace2.txt && echo >>sparse-index/untracked.txt && @@ -702,10 +861,10 @@ ensure_not_expanded () { then shift && test_must_fail env \ - GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ git -C sparse-index "$@" || return 1 else - GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \ + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \ git -C sparse-index "$@" || return 1 fi && test_region ! index ensure_full_index trace2.txt @@ -715,6 +874,7 @@ test_expect_success 'sparse-index is not expanded' ' init_repos && ensure_not_expanded status && + ensure_not_expanded ls-files --sparse && ensure_not_expanded commit --allow-empty -m empty && echo >>sparse-index/a && ensure_not_expanded commit -a -m a && @@ -726,9 +886,9 @@ test_expect_success 'sparse-index is not expanded' ' ensure_not_expanded checkout - && ensure_not_expanded switch rename-out-to-out && ensure_not_expanded switch - && - git -C sparse-index reset --hard && + ensure_not_expanded reset --hard && ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 && - git -C sparse-index reset --hard && + ensure_not_expanded reset --hard && ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 && echo >>sparse-index/README.md && @@ -738,6 +898,34 @@ test_expect_success 'sparse-index is not expanded' ' echo >>sparse-index/untracked.txt && ensure_not_expanded add . && + for ref in update-deep update-folder1 update-folder2 update-deep + do + echo >>sparse-index/README.md && + ensure_not_expanded reset --hard $ref || return 1 + done && + + ensure_not_expanded reset --mixed base && + ensure_not_expanded reset --hard update-deep && + ensure_not_expanded reset --keep base && + ensure_not_expanded reset --merge update-deep && + ensure_not_expanded reset --hard && + + ensure_not_expanded reset base -- deep/a && + ensure_not_expanded reset base -- nonexistent-file && + ensure_not_expanded reset deepest -- deep && + + # Although folder1 is outside the sparse definition, it exists as a + # directory entry in the index, so the pathspec will not force the + # index to be expanded. + ensure_not_expanded reset deepest -- folder1 && + ensure_not_expanded reset deepest -- folder1/ && + + # Wildcard identifies only in-cone files, no index expansion + ensure_not_expanded reset deepest -- deep/\* && + + # Wildcard identifies only full sparse directories, no index expansion + ensure_not_expanded reset deepest -- folder\* && + ensure_not_expanded checkout -f update-deep && test_config -C sparse-index pull.twohead ort && ( @@ -767,6 +955,158 @@ test_expect_success 'sparse-index is not expanded: merge conflict in cone' ' ) ' +test_expect_success 'sparse index is not expanded: diff' ' + init_repos && + + write_script edit-contents <<-\EOF && + echo text >>$1 + EOF + + # Add file within cone + test_sparse_match git sparse-checkout set deep && + run_on_all ../edit-contents deep/testfile && + test_all_match git add deep/testfile && + run_on_all ../edit-contents deep/testfile && + + test_all_match git diff && + test_all_match git diff --cached && + ensure_not_expanded diff && + ensure_not_expanded diff --cached && + + # Add file outside cone + test_all_match git reset --hard && + run_on_all mkdir newdirectory && + run_on_all ../edit-contents newdirectory/testfile && + test_sparse_match git sparse-checkout set newdirectory && + test_all_match git add newdirectory/testfile && + run_on_all ../edit-contents newdirectory/testfile && + test_sparse_match git sparse-checkout set && + + test_all_match git diff && + test_all_match git diff --cached && + ensure_not_expanded diff && + ensure_not_expanded diff --cached && + + # Merge conflict outside cone + # The sparse checkout will report a warning that is not in the + # full checkout, so we use `run_on_all` instead of + # `test_all_match` + run_on_all git reset --hard && + test_all_match git checkout merge-left && + test_all_match test_must_fail git merge merge-right && + + test_all_match git diff && + test_all_match git diff --cached && + ensure_not_expanded diff && + ensure_not_expanded diff --cached +' + +test_expect_success 'sparse index is not expanded: blame' ' + init_repos && + + for file in a \ + deep/a \ + deep/deeper1/a \ + deep/deeper1/deepest/a + do + ensure_not_expanded blame $file + done +' + +test_expect_success 'sparse index is not expanded: fetch/pull' ' + init_repos && + + git -C sparse-index remote add full "file://$(pwd)/full-checkout" && + ensure_not_expanded fetch full && + git -C full-checkout commit --allow-empty -m "for pull merge" && + git -C sparse-index commit --allow-empty -m "for pull merge" && + ensure_not_expanded pull full base +' + +test_expect_success 'ls-files' ' + init_repos && + + # Use a smaller sparse-checkout for reduced output + test_sparse_match git sparse-checkout set && + + # Behavior agrees by default. Sparse index is expanded. + test_all_match git ls-files && + + # With --sparse, the sparse index data changes behavior. + git -C sparse-index ls-files --sparse >actual && + + cat >expect <<-\EOF && + a + deep/ + e + folder1- + folder1.x + folder1/ + folder10 + folder2/ + g + x/ + z + EOF + + test_cmp expect actual && + + # With --sparse and no sparse index, nothing changes. + git -C sparse-checkout ls-files >dense && + git -C sparse-checkout ls-files --sparse >sparse && + test_cmp dense sparse && + + # Set up a strange condition of having a file edit + # outside of the sparse-checkout cone. This is just + # to verify that sparse-checkout and sparse-index + # behave the same in this case. + write_script edit-content <<-\EOF && + mkdir folder1 && + echo content >>folder1/a + EOF + run_on_sparse ../edit-content && + + # ls-files does not currently notice modified files whose + # cache entries are marked SKIP_WORKTREE. This may change + # in the future, but here we test that sparse index does + # not accidentally create a change of behavior. + test_sparse_match git ls-files --modified && + test_must_be_empty sparse-checkout-out && + test_must_be_empty sparse-index-out && + + git -C sparse-index ls-files --sparse --modified >sparse-index-out && + test_must_be_empty sparse-index-out && + + # Add folder1 to the sparse-checkout cone and + # check that ls-files shows the expanded files. + test_sparse_match git sparse-checkout add folder1 && + test_sparse_match git ls-files --modified && + + test_all_match git ls-files && + git -C sparse-index ls-files --sparse >actual && + + cat >expect <<-\EOF && + a + deep/ + e + folder1- + folder1.x + folder1/0/0/0 + folder1/0/1 + folder1/a + folder10 + folder2/ + g + x/ + z + EOF + + test_cmp expect actual && + + # Double-check index expansion is avoided + ensure_not_expanded ls-files --sparse +' + # NEEDSWORK: a sparse-checkout behaves differently from a full checkout # in this scenario, but it shouldn't. test_expect_success 'reset mixed and checkout orphan' ' @@ -782,13 +1122,13 @@ test_expect_success 'reset mixed and checkout orphan' ' # the sparse checkouts skip "adding" the other side of # the conflict. test_sparse_match git reset --mixed HEAD~1 && - test_sparse_match test-tool read-cache --table --expand && + test_sparse_match git ls-files --stage && test_sparse_match git status --porcelain=v2 && # At this point, sparse-checkouts behave differently # from the full-checkout. test_sparse_match git checkout --orphan new-branch && - test_sparse_match test-tool read-cache --table --expand && + test_sparse_match git ls-files --stage && test_sparse_match git status --porcelain=v2 ' |