mirror of
https://github.com/git/git
synced 2024-08-27 19:49:21 +00:00
![Elijah Newren](/assets/img/avatar_default.png)
Typically with sparse checkouts, we expect files outside the sparsity patterns to be marked as SKIP_WORKTREE and be missing from the working tree. Sometimes this expectation would be violated however; including in cases such as: * users grabbing files from elsewhere and writing them to the worktree (perhaps by editing a cached copy in an editor, copying/renaming, or even untarring) * various git commands having incomplete or no support for the SKIP_WORKTREE bit[1,2] * users attempting to "abort" a sparse-checkout operation with a not-so-early Ctrl+C (updating $GIT_DIR/info/sparse-checkout and the working tree is not atomic)[3]. When the SKIP_WORKTREE bit in the index did not reflect the presence of the file in the working tree, it traditionally caused confusion and was difficult to detect and recover from. So, in a sparse checkout, sinceaf6a51875a
(repo_read_index: clear SKIP_WORKTREE bit from files present in worktree, 2022-01-14), Git automatically clears the SKIP_WORKTREE bit at index read time for entries corresponding to files that are present in the working tree. There is another workflow, however, where it is expected that paths outside the sparsity patterns appear to exist in the working tree and that they do not lose the SKIP_WORKTREE bit, at least until they get modified. A Git-aware virtual file system[4] takes advantage of its position as a file system driver to expose all files in the working tree, fetch them on demand using partial clone on access, and tell Git to pay attention to them on demand by updating the sparse checkout pattern on writes. This means that commands like "git status" only have to examine files that have potentially been modified, whereas commands like "ls" are able to show the entire codebase without requiring manual updates to the sparse checkout pattern. Thus sinceaf6a51875a
, Git with such Git-aware virtual file systems unsets the SKIP_WORKTREE bit for all files and commands like "git status" have to fetch and examine them all. Introduce a configuration setting sparse.expectFilesOutsideOfPatterns to allow limiting the tracked set of files to a small set once again. A Git-aware virtual file system or other application that wants to maintain files outside of the sparse checkout can set this in a repository to instruct Git not to check for the presence of SKIP_WORKTREE files. The setting defaults to false, so most users of sparse checkout will still get the benefit of an automatically updating index to recover from the variety of difficult issues detailed inaf6a51875a
for paths with SKIP_WORKTREE set despite the path being present. [1] https://lore.kernel.org/git/xmqqbmb1a7ga.fsf@gitster-ct.c.googlers.com/ [2] The three long paragraphs in the middle of https://lore.kernel.org/git/CABPp-BH9tju7WVm=QZDOvaMDdZbpNXrVWQdN-jmfN8wC6YVhmw@mail.gmail.com/ [3] https://lore.kernel.org/git/CABPp-BFnFpzwGC11TLoLs8YK5yiisA5D5-fFjXnJsbESVDwZsA@mail.gmail.com/ [4] such as the vfsd described in https://lore.kernel.org/git/20220207190320.2960362-1-jonathantanmy@google.com/ Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Elijah Newren <newren@gmail.com> Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
107 lines
2.6 KiB
Bash
Executable file
107 lines
2.6 KiB
Bash
Executable file
#!/bin/sh
|
|
|
|
test_description='sparse checkout scope tests'
|
|
|
|
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
|
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
|
|
|
. ./test-lib.sh
|
|
|
|
test_expect_success 'setup' '
|
|
echo "initial" >a &&
|
|
echo "initial" >b &&
|
|
echo "initial" >c &&
|
|
git add a b c &&
|
|
git commit -m "initial commit"
|
|
'
|
|
|
|
test_expect_success 'create feature branch' '
|
|
git checkout -b feature &&
|
|
echo "modified" >b &&
|
|
echo "modified" >c &&
|
|
git add b c &&
|
|
git commit -m "modification"
|
|
'
|
|
|
|
test_expect_success 'perform sparse checkout of main' '
|
|
git config --local --bool core.sparsecheckout true &&
|
|
echo "!/*" >.git/info/sparse-checkout &&
|
|
echo "/a" >>.git/info/sparse-checkout &&
|
|
echo "/c" >>.git/info/sparse-checkout &&
|
|
git checkout main &&
|
|
test_path_is_file a &&
|
|
test_path_is_missing b &&
|
|
test_path_is_file c
|
|
'
|
|
|
|
test_expect_success 'merge feature branch into sparse checkout of main' '
|
|
git merge feature &&
|
|
test_path_is_file a &&
|
|
test_path_is_missing b &&
|
|
test_path_is_file c &&
|
|
test "$(cat c)" = "modified"
|
|
'
|
|
|
|
test_expect_success 'return to full checkout of main' '
|
|
git checkout feature &&
|
|
echo "/*" >.git/info/sparse-checkout &&
|
|
git checkout main &&
|
|
test_path_is_file a &&
|
|
test_path_is_file b &&
|
|
test_path_is_file c &&
|
|
test "$(cat b)" = "modified"
|
|
'
|
|
|
|
test_expect_success 'skip-worktree on files outside sparse patterns' '
|
|
git sparse-checkout disable &&
|
|
git sparse-checkout set --no-cone "a*" &&
|
|
git checkout-index --all --ignore-skip-worktree-bits &&
|
|
|
|
git ls-files -t >output &&
|
|
! grep ^S output >actual &&
|
|
test_must_be_empty actual &&
|
|
|
|
test_config sparse.expectFilesOutsideOfPatterns true &&
|
|
cat <<-\EOF >expect &&
|
|
S b
|
|
S c
|
|
EOF
|
|
git ls-files -t >output &&
|
|
grep ^S output >actual &&
|
|
test_cmp expect actual
|
|
'
|
|
|
|
test_expect_success 'in partial clone, sparse checkout only fetches needed blobs' '
|
|
test_create_repo server &&
|
|
git clone "file://$(pwd)/server" client &&
|
|
|
|
test_config -C server uploadpack.allowfilter 1 &&
|
|
test_config -C server uploadpack.allowanysha1inwant 1 &&
|
|
echo a >server/a &&
|
|
echo bb >server/b &&
|
|
mkdir server/c &&
|
|
echo ccc >server/c/c &&
|
|
git -C server add a b c/c &&
|
|
git -C server commit -m message &&
|
|
|
|
test_config -C client core.sparsecheckout 1 &&
|
|
echo "!/*" >client/.git/info/sparse-checkout &&
|
|
echo "/a" >>client/.git/info/sparse-checkout &&
|
|
git -C client fetch --filter=blob:none origin &&
|
|
git -C client checkout FETCH_HEAD &&
|
|
|
|
git -C client rev-list HEAD \
|
|
--quiet --objects --missing=print >unsorted_actual &&
|
|
(
|
|
printf "?" &&
|
|
git hash-object server/b &&
|
|
printf "?" &&
|
|
git hash-object server/c/c
|
|
) >unsorted_expect &&
|
|
sort unsorted_actual >actual &&
|
|
sort unsorted_expect >expect &&
|
|
test_cmp expect actual
|
|
'
|
|
|
|
test_done
|