diff --git a/repository.c b/repository.c index 34610c5a33e013..dddee32258f848 100644 --- a/repository.c +++ b/repository.c @@ -301,6 +301,13 @@ int repo_read_index(struct repository *repo) if (repo->settings.command_requires_full_index) ensure_full_index(repo->index); + /* + * If sparse checkouts are in use, check whether paths with the + * SKIP_WORKTREE attribute are missing from the worktree; if not, + * clear that attribute for that path. + */ + clear_skip_worktree_from_present_files(repo->index); + return res; } diff --git a/sparse-index.c b/sparse-index.c index a1d505d50e98cf..b82648b10ee66e 100644 --- a/sparse-index.c +++ b/sparse-index.c @@ -341,6 +341,27 @@ void ensure_correct_sparsity(struct index_state *istate) ensure_full_index(istate); } +void clear_skip_worktree_from_present_files(struct index_state *istate) +{ + int i; + if (!core_apply_sparse_checkout) + return; + +restart: + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + struct stat st; + + if (ce_skip_worktree(ce) && !lstat(ce->name, &st)) { + if (S_ISSPARSEDIR(ce->ce_mode)) { + ensure_full_index(istate); + goto restart; + } + ce->ce_flags &= ~CE_SKIP_WORKTREE; + } + } +} + /* * This static global helps avoid infinite recursion between * expand_to_path() and index_file_exists(). diff --git a/sparse-index.h b/sparse-index.h index 656bd835b25e06..633d4fb7e318af 100644 --- a/sparse-index.h +++ b/sparse-index.h @@ -5,6 +5,7 @@ struct index_state; #define SPARSE_INDEX_MEMORY_ONLY (1 << 0) int convert_to_sparse(struct index_state *istate, int flags); void ensure_correct_sparsity(struct index_state *istate); +void clear_skip_worktree_from_present_files(struct index_state *istate); /* * Some places in the codebase expect to search for a specific path. diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh index 4ed0885bf2fa3d..dd957be1b78c56 100755 --- a/t/t1011-read-tree-sparse-checkout.sh +++ b/t/t1011-read-tree-sparse-checkout.sh @@ -212,7 +212,7 @@ test_expect_success 'read-tree updates worktree, dirty case' ' echo sub/added >.git/info/sparse-checkout && git checkout -f top && echo dirty >init.t && - read_tree_u_must_succeed -m -u HEAD^ && + read_tree_u_must_fail -m -u HEAD^ && grep -q dirty init.t && rm init.t ' diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index f3a059e5af557a..2c44c00db25fec 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -367,7 +367,7 @@ test_expect_success 'status/add: outside sparse cone' ' write_script edit-contents <<-\EOF && echo text >>$1 EOF - run_on_sparse ../edit-contents folder1/a && + run_on_all ../edit-contents folder1/a && run_on_all ../edit-contents folder1/new && test_sparse_match git status --porcelain=v2 && @@ -376,8 +376,8 @@ test_expect_success 'status/add: outside sparse cone' ' test_sparse_match test_must_fail git add folder1/a && grep "Disable or modify the sparsity rules" sparse-checkout-err && test_sparse_unstaged folder1/a && - test_sparse_match test_must_fail git add --refresh folder1/a && - grep "Disable or modify the sparsity rules" sparse-checkout-err && + test_all_match git add --refresh folder1/a && + test_must_be_empty sparse-checkout-err && test_sparse_unstaged folder1/a && test_sparse_match test_must_fail git add folder1/new && grep "Disable or modify the sparsity rules" sparse-checkout-err && @@ -643,11 +643,11 @@ test_expect_success 'update-index modify outside sparse definition' ' run_on_sparse cp ../initial-repo/folder1/a folder1/a && run_on_all ../edit-contents folder1/a && - # If file has skip-worktree enabled, update-index does not modify the - # index entry - test_sparse_match git update-index folder1/a && - test_sparse_match git status --porcelain=v2 && - test_must_be_empty sparse-checkout-out && + # If file has skip-worktree enabled, but the file is present, it is + # treated the same as if skip-worktree is disabled + test_all_match git status --porcelain=v2 && + test_all_match git update-index folder1/a && + test_all_match git status --porcelain=v2 && # When skip-worktree is disabled (even on files outside sparse cone), file # is updated in the index diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh index 81f3384eeed4bb..95609046c61d37 100755 --- a/t/t3705-add-sparse-checkout.sh +++ b/t/t3705-add-sparse-checkout.sh @@ -19,6 +19,7 @@ setup_sparse_entry () { fi && git add sparse_entry && git update-index --skip-worktree sparse_entry && + git config core.sparseCheckout false && git commit --allow-empty -m "ensure sparse_entry exists at HEAD" && SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry) } @@ -126,6 +127,7 @@ test_expect_success 'git add --chmod does not update sparse entries' ' ' test_expect_success 'git add --renormalize does not update sparse entries' ' + test_when_finished rm .gitattributes && test_config core.autocrlf false && setup_sparse_entry "LINEONE\r\nLINETWO\r\n" && echo "sparse_entry text=auto" >.gitattributes && diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh index 7e8bf497f821da..142c9aaabc5309 100755 --- a/t/t6428-merge-conflicts-sparse.sh +++ b/t/t6428-merge-conflicts-sparse.sh @@ -112,7 +112,7 @@ test_expect_success 'conflicting entries written to worktree even if sparse' ' ) ' -test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handled reasonably' ' +test_expect_success 'present-despite-SKIP_WORKTREE handled reasonably' ' test_setup_numerals in_the_way && ( cd numerals_in_the_way && @@ -132,26 +132,13 @@ test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handl test_must_fail git merge -s recursive B^0 && - git ls-files -t >index_files && - test_cmp expected-index index_files && + test_path_is_missing .git/MERGE_HEAD && - test_path_is_file README && test_path_is_file numerals && - test_cmp expected-merge numerals && - - # There should still be a file with "foobar" in it - grep foobar * && - - # 5 other files: - # * expected-merge - # * expected-index - # * index_files - # * others - # * whatever name was given to the numerals file that had - # "foobar" in it - git ls-files -o >others && - test_line_count = 5 others + # numerals should still have "foobar" in it + echo foobar >expect && + test_cmp expect numerals ) ' diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh index a1080b94e388ae..cb9f1a6981eb5f 100755 --- a/t/t7012-skip-worktree-writing.sh +++ b/t/t7012-skip-worktree-writing.sh @@ -171,50 +171,20 @@ test_expect_success 'stash restore in sparse checkout' ' # Put a file in the working directory in the way echo in the way >modified && - git stash apply && + test_must_fail git stash apply 2>error&& - # Ensure stash vivifies modifies paths... - cat >expect <<-EOF && - H addme - H modified - H removeme - H subdir/A - S untouched - EOF - git ls-files -t >actual && - test_cmp expect actual && + grep "changes.*would be overwritten by merge" error && - # ...and that the paths show up in status as changed... - cat >expect <<-EOF && - A addme - M modified - D removeme - M subdir/A - ?? actual - ?? expect - ?? modified.stash.XXXXXX - EOF - git status --porcelain | \ - sed -e s/stash......./stash.XXXXXX/ >actual && - test_cmp expect actual && + echo in the way >expect && + test_cmp expect modified && + git diff --quiet HEAD ":!modified" && # ...and that working directory reflects the files correctly - test_path_is_file addme && + test_path_is_missing addme && test_path_is_file modified && test_path_is_missing removeme && test_path_is_file subdir/A && - test_path_is_missing untouched && - - # ...including that we have the expected "modified" file... - cat >expect <<-EOF && - modified - tweaked - EOF - test_cmp expect modified && - - # ...and that the other "modified" file is still present... - echo in the way >expect && - test_cmp expect modified.stash.* + test_path_is_missing untouched ) ' diff --git a/t/t7817-grep-sparse-checkout.sh b/t/t7817-grep-sparse-checkout.sh index 590b99bbb6f7bb..eb595645657fad 100755 --- a/t/t7817-grep-sparse-checkout.sh +++ b/t/t7817-grep-sparse-checkout.sh @@ -83,10 +83,13 @@ test_expect_success 'setup' ' # The test below covers a special case: the sparsity patterns exclude '/b' and # sparse checkout is enabled, but the path exists in the working tree (e.g. -# manually created after `git sparse-checkout init`). git grep should skip it. +# manually created after `git sparse-checkout init`). Although b is marked +# as SKIP_WORKTREE, git grep should notice it IS present in the worktree and +# report it. test_expect_success 'working tree grep honors sparse checkout' ' cat >expect <<-EOF && a:text + b:new-text EOF test_when_finished "rm -f b" && echo "new-text" >b && @@ -126,12 +129,16 @@ test_expect_success 'grep --cached searches entries with the SKIP_WORKTREE bit' ' # Note that sub2/ is present in the worktree but it is excluded by the sparsity -# patterns, so grep should not recurse into it. +# patterns. We also explicitly mark it as SKIP_WORKTREE in case it got cleared +# by previous git commands. Thus sub2 starts as SKIP_WORKTREE but since it is +# present in the working tree, grep should recurse into it. test_expect_success 'grep --recurse-submodules honors sparse checkout in submodule' ' cat >expect <<-EOF && a:text sub/B/b:text + sub2/a:text EOF + git update-index --skip-worktree sub2 && git grep --recurse-submodules "text" >actual && test_cmp expect actual '