When 'git add' adds a tracked file that is outside of the
sparse-checkout cone, it checks the SKIP_WORKTREE bit to see if the file
exists outside of the sparse-checkout cone. This is usually correct,
except in the case of a merge conflict outside of the cone.
Modify add_pathspec_matched_against_index() to be more careful about
paths by checking the sparse-checkout patterns in addition to the
SKIP_WORKTREE bit. This causes 'git add' to no longer allow files
outside of the cone that removed the SKIP_WORKTREE bit due to a merge
conflict.
With only this change, users will only be able to add the file after
adding the file to the sparse-checkout cone. A later change will allow
users to force adding even though the file is outside of the
sparse-checkout cone.
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Derrick Stolee committedSep 24, 2021 at 15:39 UTC49fdd51a235fe2ca91a6d1b16315204f0f016a96
5 files changed+34-10
builtin/add.c
+4
index 8ea9cae0e7..09c3fad632 100644--- a/builtin/add.c+++ b/builtin/add.c@@ -94,6 +94,10 @@ static void update_callback(struct diff_queue_struct *q, for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path;++ if (!path_in_sparse_checkout(path, &the_index))+ continue;+ switch (fix_unmerged_status(p, data)) { default: die(_("unexpected diff status %c"), p->status);
pathspec.c
+3-2
index 44306fdaca..ddeeba7911 100644--- a/pathspec.c+++ b/pathspec.c@@ -39,7 +39,8 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec, return; for (i = 0; i < istate->cache_nr; i++) { const struct cache_entry *ce = istate->cache[i];- if (sw_action == PS_IGNORE_SKIP_WORKTREE && ce_skip_worktree(ce))+ if (sw_action == PS_IGNORE_SKIP_WORKTREE &&+ (ce_skip_worktree(ce) || !path_in_sparse_checkout(ce->name, istate))) continue; ce_path_match(istate, ce, pathspec, seen); }@@ -70,7 +71,7 @@ char *find_pathspecs_matching_skip_worktree(const struct pathspec *pathspec) for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i];- if (ce_skip_worktree(ce))+ if (ce_skip_worktree(ce) || !path_in_sparse_checkout(ce->name, istate)) ce_path_match(istate, ce, pathspec, seen); }
t/t1091-sparse-checkout-builtin.sh
+3-1
index 71236981e6..af99ae81b1 100755--- a/t/t1091-sparse-checkout-builtin.sh+++ b/t/t1091-sparse-checkout-builtin.sh@@ -406,7 +406,7 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged stat git -C unmerged sparse-checkout disable '-test_expect_success 'sparse-checkout reapply' '+test_expect_failure 'sparse-checkout reapply' ' git clone repo tweak && echo dirty >tweak/deep/deeper2/a &&@@ -438,6 +438,8 @@ test_expect_success 'sparse-checkout reapply' ' test_i18ngrep "warning.*The following paths are unmerged" err && test_path_is_file tweak/folder1/a &&+ # NEEDSWORK: We are asking to update a file outside of the+ # sparse-checkout cone, but this is no longer allowed. git -C tweak add folder1/a && git -C tweak sparse-checkout reapply 2>err && test_must_be_empty err &&
t/t1092-sparse-checkout-compatibility.sh
+12-7
index 94258d80bf..0dc3a755fe 100755--- a/t/t1092-sparse-checkout-compatibility.sh+++ b/t/t1092-sparse-checkout-compatibility.sh@@ -546,10 +546,9 @@ test_expect_failure 'merge with conflict outside cone' ' test_all_match git status --porcelain=v2 && # 2. Add the file with conflict markers- # NEEDSWORK: Even though the merge conflict removed the- # SKIP_WORKTREE bit from the index entry for folder1/a, we should- # warn that this is a problematic add.- test_all_match git add folder1/a &&+ 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_all_match git status --porcelain=v2 && # 3. Rename the file to another sparse filename and@@ -558,7 +557,9 @@ test_expect_failure 'merge with conflict outside cone' ' # NEEDSWORK: This mode now fails, because folder2/z is # outside of the sparse-checkout cone and does not match an # existing index entry with the SKIP_WORKTREE bit cleared.- test_all_match git add folder2 &&+ test_sparse_match test_must_fail git add folder2 &&+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&+ test_sparse_unstaged folder2/z && test_all_match git status --porcelain=v2 && test_all_match git merge --continue &&@@ -586,7 +587,9 @@ test_expect_failure 'cherry-pick/rebase with conflict outside cone' ' # NEEDSWORK: Even though the merge conflict removed the # SKIP_WORKTREE bit from the index entry for folder1/a, we should # warn that this is a problematic add.- test_all_match git add folder1/a &&+ 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_all_match git status --porcelain=v2 && # 3. Rename the file to another sparse filename and@@ -595,7 +598,9 @@ test_expect_failure 'cherry-pick/rebase with conflict outside cone' ' # outside of the sparse-checkout cone and does not match an # existing index entry with the SKIP_WORKTREE bit cleared. run_on_all mv folder2/a folder2/z &&- test_all_match git add folder2 &&+ test_sparse_match test_must_fail git add folder2 &&+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&+ test_sparse_unstaged folder2/z && test_all_match git status --porcelain=v2 && test_all_match git $OPERATION --continue &&