add: allow operating on a sparse-only index

Disable command_requires_full_index for 'git add'. This does not require
any additional removals of ensure_full_index(). The main reason is that
'git add' discovers changes based on the pathspec and the worktree
itself. These are then inserted into the index directly, and calls to
index_name_pos() or index_file_exists() already call expand_to_path() at
the appropriate time to support a sparse-index.

Add a test to check that 'git add -A' and 'git add <file>' does not
expand the index at all, as long as <file> is not within a sparse
directory. This does not help the global 'git add .' case.

We can measure the improvement using p2000-sparse-operations.sh with
these results:

Test                                  HEAD~1           HEAD
------------------------------------------------------------------------------
2000.6: git add -A (full-index-v3)    0.35(0.30+0.05)  0.37(0.29+0.06) +5.7%
2000.7: git add -A (full-index-v4)    0.31(0.26+0.06)  0.33(0.27+0.06) +6.5%
2000.8: git add -A (sparse-index-v3)  0.57(0.53+0.07)  0.05(0.04+0.08) -91.2%
2000.9: git add -A (sparse-index-v4)  0.58(0.55+0.06)  0.05(0.05+0.06) -91.4%

While the 91% improvement seems impressive, it's important to recognize
that previously we had significant overhead for expanding the
sparse-index. Comparing to the full index case, 'git add -A' goes from
0.37s to 0.05s, which is "only" an 86% improvement.

This modification to 'git add' creates some behavior change depending on
the use of a sparse index. We modify a test in t1092 to demonstrate
these changes which will be remedied in future changes.

Reviewed-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Derrick Stolee 2021-07-29 14:52:04 +00:00 коммит произвёл Junio C Hamano
Родитель 83ad8ca596
Коммит 5e7cbab196
2 изменённых файлов: 21 добавлений и 7 удалений

Просмотреть файл

@ -528,6 +528,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize; add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit)); require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
/* /*

Просмотреть файл

@ -340,21 +340,27 @@ test_expect_success 'status/add: outside sparse cone' '
test_sparse_match git status --porcelain=v2 && test_sparse_match git status --porcelain=v2 &&
# This "git add folder1/a" fails with a warning # Adding the path outside of the sparse-checkout cone should fail.
# in the sparse repos, differing from the full
# repo. This is intentional.
test_sparse_match test_must_fail git add folder1/a && test_sparse_match test_must_fail git add folder1/a &&
test_sparse_match test_must_fail git add --refresh folder1/a &&
test_all_match git status --porcelain=v2 && test_must_fail git -C sparse-checkout add --refresh folder1/a 2>sparse-checkout-err &&
test_must_fail git -C sparse-index add --refresh folder1/a 2>sparse-index-err &&
# NEEDSWORK: A sparse index changes the error message.
! test_cmp sparse-checkout-err sparse-index-err &&
# NEEDSWORK: Adding a newly-tracked file outside the cone succeeds
test_sparse_match git add folder1/new &&
test_all_match git add . && test_all_match git add . &&
test_all_match git status --porcelain=v2 && test_all_match git status --porcelain=v2 &&
test_all_match git commit -m folder1/new && test_all_match git commit -m folder1/new &&
test_all_match git rev-parse HEAD^{tree} &&
run_on_all ../edit-contents folder1/newer && run_on_all ../edit-contents folder1/newer &&
test_all_match git add folder1/ && test_all_match git add folder1/ &&
test_all_match git status --porcelain=v2 && test_all_match git status --porcelain=v2 &&
test_all_match git commit -m folder1/newer test_all_match git commit -m folder1/newer &&
test_all_match git rev-parse HEAD^{tree}
' '
test_expect_success 'checkout and reset --hard' ' test_expect_success 'checkout and reset --hard' '
@ -641,7 +647,12 @@ test_expect_success 'sparse-index is not expanded' '
git -C sparse-index reset --hard && git -C sparse-index reset --hard &&
ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 && ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 &&
git -C sparse-index reset --hard && git -C sparse-index reset --hard &&
ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 &&
echo >>sparse-index/README.md &&
ensure_not_expanded add -A &&
echo >>sparse-index/extra.txt &&
ensure_not_expanded add extra.txt
' '
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout # NEEDSWORK: a sparse-checkout behaves differently from a full checkout