git add: make -p/-i aware of sparse index

It is slow to expand a sparse index in-memory due to parsing of trees. We aim to minimize that performance cost when possible. 'git add -p' uses 'git apply' child processes to modify the index, but still there are some expansions that occur. It turns out that control flows out of cmd_add() in the interactive cases before the lines that confirm that the builtin is integrated with the sparse index. Moving that integration point earlier in cmd_add() allows 'git add -i' and 'git add -p' to operate without expanding a sparse index to a full one. Add test cases that confirm that these interactive add options work with the sparse index. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed May 16, 2025 at 14:55 UTC 02ed8555f68440c5f533ad3c098ac01fc8965861
2 files changed +64 -3
builtin/add.c
+4 -3
@@ -391,6 +391,10 @@ int cmd_add(int argc,
391
392 argc = parse_options(argc, argv, prefix, builtin_add_options,
393 builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
394 +
395 + prepare_repo_settings(repo);
396 + repo->settings.command_requires_full_index = 0;
397 +
398 if (patch_interactive)
399 add_interactive = 1;
400 if (add_interactive) {
@@ -427,9 +431,6 @@ int cmd_add(int argc,
431 add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
432 require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
433
430 - prepare_repo_settings(repo);
431 - repo->settings.command_requires_full_index = 0;
432 -
434 repo_hold_locked_index(repo, &lock_file, LOCK_DIE_ON_ERROR);
435
436 /*
t/t1092-sparse-checkout-compatibility.sh
+60
@@ -384,6 +384,38 @@ test_expect_success 'add, commit, checkout' '
384 test_all_match git checkout -
385 '
386
387 +test_expect_success 'git add -p' '
388 + init_repos &&
389 +
390 + write_script edit-contents <<-\EOF &&
391 + echo text >>$1
392 + EOF
393 +
394 + # Does not expand when edits are within sparse checkout.
395 + run_on_all ../edit-contents deep/a &&
396 + run_on_all ../edit-contents deep/deeper1/a &&
397 +
398 + test_write_lines y n >in &&
399 + run_on_all git add -p <in &&
400 + test_all_match git status --porcelain=v2 &&
401 + test_all_match git reset &&
402 +
403 + test_write_lines u 1 "" q >in &&
404 + run_on_all git add -i <in &&
405 + test_all_match git status --porcelain=v2 &&
406 + test_all_match git reset --hard &&
407 +
408 + run_on_sparse mkdir -p folder1 &&
409 + run_on_all ../edit-contents folder1/a &&
410 + test_write_lines y n y >in &&
411 + run_on_all git add -p <in &&
412 + test_sparse_match git status --porcelain=v2 &&
413 + test_sparse_match git reset &&
414 + test_write_lines u 2 3 "" q >in &&
415 + run_on_all git add -i <in &&
416 + test_sparse_match git status --porcelain=v2
417 +'
418 +
419 test_expect_success 'deep changes during checkout' '
420 init_repos &&
421
@@ -2400,6 +2432,34 @@ test_expect_success 'sparse-index is not expanded: git apply' '
2432 ensure_expanded apply --cached ../patch-outside
2433 '
2434
2435 +test_expect_success 'sparse-index is not expanded: git add -p' '
2436 + init_repos &&
2437 +
2438 + # Does not expand when edits are within sparse checkout.
2439 + echo "new content" >sparse-index/deep/a &&
2440 + echo "new content" >sparse-index/deep/deeper1/a &&
2441 + test_write_lines y n >in &&
2442 + ensure_not_expanded add -p <in &&
2443 + git -C sparse-index reset &&
2444 + ensure_not_expanded add -i <in &&
2445 +
2446 + # -p does expand when edits are outside sparse checkout.
2447 + mkdir -p sparse-index/folder1 &&
2448 + echo "new content" >sparse-index/folder1/a &&
2449 + test_write_lines y n y >in &&
2450 + ensure_expanded add -p <in &&
2451 +
2452 + # Fully reset the index.
2453 + git -C sparse-index reset --hard &&
2454 + git -C sparse-index sparse-checkout reapply &&
2455 +
2456 + # -i does expand when edits are outside sparse checkout.
2457 + mkdir -p sparse-index/folder1 &&
2458 + echo "new content" >sparse-index/folder1/a &&
2459 + test_write_lines u 2 3 "" q >in &&
2460 + ensure_expanded add -i <in
2461 +'
2462 +
2463 test_expect_success 'advice.sparseIndexExpanded' '
2464 init_repos &&
2465