Git fork

ls-files: conditionally leave index sparse

When running 'git ls-files' with a pathspec, the index entries get
filtered according to that pathspec before iterating over them in
show_files(). In 78087097b8 (ls-files: add --sparse option,
2021-12-22), this iteration was prefixed with a check for the '--sparse'
option which allows the command to output directory entries; this
created a pre-loop call to ensure_full_index().

However, when a user runs 'git ls-files' where the pathspec matches
directories that are recursively matched in the sparse-checkout, there
are not any sparse directories that match the pathspec so they would not
be written to the output. The expansion in this case is just a
performance drop for no behavior difference.

Replace this global check to expand the index with a check inside the
loop for a matched sparse directory. If we see one, then expand the
index and continue from the current location. This is safe since the
previous entries in the index did not have any sparse directories and
thus would remain stable in this expansion.

A test in t1092 confirms that this changes the behavior.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Derrick Stolee and committed by
Junio C Hamano
681f26bc f368df43

+23 -3
+10 -3
builtin/ls-files.c
··· 413 if (!(show_cached || show_stage || show_deleted || show_modified)) 414 return; 415 416 - if (!show_sparse_dirs) 417 - ensure_full_index(repo->index); 418 - 419 for (i = 0; i < repo->index->cache_nr; i++) { 420 const struct cache_entry *ce = repo->index->cache[i]; 421 struct stat st; 422 int stat_err; 423 424 construct_fullname(&fullname, repo, ce); 425
··· 413 if (!(show_cached || show_stage || show_deleted || show_modified)) 414 return; 415 416 for (i = 0; i < repo->index->cache_nr; i++) { 417 const struct cache_entry *ce = repo->index->cache[i]; 418 struct stat st; 419 int stat_err; 420 + 421 + if (S_ISSPARSEDIR(ce->ce_mode) && !show_sparse_dirs) { 422 + /* 423 + * This is the first time we've hit a sparse dir, 424 + * so expansion will leave the first 'i' entries 425 + * alone. 426 + */ 427 + ensure_full_index(repo->index); 428 + ce = repo->index->cache[i]; 429 + } 430 431 construct_fullname(&fullname, repo, ce); 432
+13
t/t1092-sparse-checkout-compatibility.sh
··· 1506 ensure_not_expanded reset --hard && 1507 ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 && 1508 1509 echo >>sparse-index/README.md && 1510 ensure_not_expanded add -A && 1511 echo >>sparse-index/extra.txt && ··· 1607 test_all_match git describe --dirty 1608 ' 1609 1610 1611 test_expect_success 'sparse-index is not expanded: describe' ' 1612 init_repos &&
··· 1506 ensure_not_expanded reset --hard && 1507 ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 && 1508 1509 + ensure_not_expanded ls-files deep/deeper1 && 1510 + 1511 echo >>sparse-index/README.md && 1512 ensure_not_expanded add -A && 1513 echo >>sparse-index/extra.txt && ··· 1609 test_all_match git describe --dirty 1610 ' 1611 1612 + test_expect_success 'ls-files filtering and expansion' ' 1613 + init_repos && 1614 + 1615 + # This filtering will hit a sparse directory midway 1616 + # through the iteration. 1617 + test_all_match git ls-files deep && 1618 + 1619 + # This pathspec will filter the index to only a sparse 1620 + # directory. 1621 + test_all_match git ls-files folder1 1622 + ' 1623 1624 test_expect_success 'sparse-index is not expanded: describe' ' 1625 init_repos &&