Skip to content

Commit 67bf4a8

Browse files
committed
Merge branch 'sy/sparse-grep'
"git grep" learned to expand the sparse-index more lazily and on demand in a sparse checkout. * sy/sparse-grep: builtin/grep.c: integrate with sparse index
2 parents 4b4d97c + 7cae762 commit 67bf4a8

File tree

3 files changed

+118
-3
lines changed

3 files changed

+118
-3
lines changed

builtin/grep.c

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,33 @@ static int grep_submodule(struct grep_opt *opt,
458458
* subrepo's odbs to the in-memory alternates list.
459459
*/
460460
obj_read_lock();
461+
462+
/*
463+
* NEEDSWORK: when reading a submodule, the sparsity settings in the
464+
* superproject are incorrectly forgotten or misused. For example:
465+
*
466+
* 1. "command_requires_full_index"
467+
* When this setting is turned on for `grep`, only the superproject
468+
* knows it. All the submodules are read with their own configs
469+
* and get prepare_repo_settings()'d. Therefore, these submodules
470+
* "forget" the sparse-index feature switch. As a result, the index
471+
* of these submodules are expanded unexpectedly.
472+
*
473+
* 2. "core_apply_sparse_checkout"
474+
* When running `grep` in the superproject, this setting is
475+
* populated using the superproject's configs. However, once
476+
* initialized, this config is globally accessible and is read by
477+
* prepare_repo_settings() for the submodules. For instance, if a
478+
* submodule is using a sparse-checkout, however, the superproject
479+
* is not, the result is that the config from the superproject will
480+
* dictate the behavior for the submodule, making it "forget" its
481+
* sparse-checkout state.
482+
*
483+
* 3. "core_sparse_checkout_cone"
484+
* ditto.
485+
*
486+
* Note that this list is not exhaustive.
487+
*/
461488
repo_read_gitmodules(subrepo, 0);
462489

463490
/*
@@ -520,8 +547,6 @@ static int grep_cache(struct grep_opt *opt,
520547
if (repo_read_index(repo) < 0)
521548
die(_("index file corrupt"));
522549

523-
/* TODO: audit for interaction with sparse-index. */
524-
ensure_full_index(repo->index);
525550
for (nr = 0; nr < repo->index->cache_nr; nr++) {
526551
const struct cache_entry *ce = repo->index->cache[nr];
527552

@@ -530,8 +555,20 @@ static int grep_cache(struct grep_opt *opt,
530555

531556
strbuf_setlen(&name, name_base_len);
532557
strbuf_addstr(&name, ce->name);
558+
if (S_ISSPARSEDIR(ce->ce_mode)) {
559+
enum object_type type;
560+
struct tree_desc tree;
561+
void *data;
562+
unsigned long size;
533563

534-
if (S_ISREG(ce->ce_mode) &&
564+
data = read_object_file(&ce->oid, &type, &size);
565+
init_tree_desc(&tree, data, size);
566+
567+
hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
568+
strbuf_setlen(&name, name_base_len);
569+
strbuf_addstr(&name, ce->name);
570+
free(data);
571+
} else if (S_ISREG(ce->ce_mode) &&
535572
match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL,
536573
S_ISDIR(ce->ce_mode) ||
537574
S_ISGITLINK(ce->ce_mode))) {
@@ -984,6 +1021,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
9841021
PARSE_OPT_KEEP_DASHDASH |
9851022
PARSE_OPT_STOP_AT_NON_OPTION);
9861023

1024+
if (the_repository->gitdir) {
1025+
prepare_repo_settings(the_repository);
1026+
the_repository->settings.command_requires_full_index = 0;
1027+
}
1028+
9871029
if (use_index && !startup_info->have_repository) {
9881030
int fallback = 0;
9891031
git_config_get_bool("grep.fallbacktonoindex", &fallback);

t/perf/p2000-sparse-operations.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,5 +124,6 @@ test_perf_on_all git read-tree -mu HEAD
124124
test_perf_on_all git checkout-index -f --all
125125
test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
126126
test_perf_on_all "git rm -f $SPARSE_CONE/a && git checkout HEAD -- $SPARSE_CONE/a"
127+
test_perf_on_all git grep --cached --sparse bogus -- "f2/f1/f1/*"
127128

128129
test_done

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ init_repos () {
162162
git -C sparse-index sparse-checkout set deep
163163
}
164164

165+
init_repos_as_submodules () {
166+
git reset --hard &&
167+
init_repos &&
168+
git submodule add ./full-checkout &&
169+
git submodule add ./sparse-checkout &&
170+
git submodule add ./sparse-index &&
171+
172+
git submodule status >actual &&
173+
grep full-checkout actual &&
174+
grep sparse-checkout actual &&
175+
grep sparse-index actual
176+
}
177+
165178
run_on_sparse () {
166179
(
167180
cd sparse-checkout &&
@@ -1981,4 +1994,63 @@ test_expect_success 'sparse index is not expanded: rm' '
19811994
ensure_not_expanded rm -r deep
19821995
'
19831996

1997+
test_expect_success 'grep with and --cached' '
1998+
init_repos &&
1999+
2000+
test_all_match git grep --cached a &&
2001+
test_all_match git grep --cached a -- "folder1/*"
2002+
'
2003+
2004+
test_expect_success 'grep is not expanded' '
2005+
init_repos &&
2006+
2007+
ensure_not_expanded grep a &&
2008+
ensure_not_expanded grep a -- deep/* &&
2009+
2010+
# All files within the folder1/* pathspec are sparse,
2011+
# so this command does not find any matches
2012+
ensure_not_expanded ! grep a -- folder1/* &&
2013+
2014+
# test out-of-cone pathspec with or without wildcard
2015+
ensure_not_expanded grep --cached a -- "folder1/a" &&
2016+
ensure_not_expanded grep --cached a -- "folder1/*" &&
2017+
2018+
# test in-cone pathspec with or without wildcard
2019+
ensure_not_expanded grep --cached a -- "deep/a" &&
2020+
ensure_not_expanded grep --cached a -- "deep/*"
2021+
'
2022+
2023+
# NEEDSWORK: when running `grep` in the superproject with --recurse-submodules,
2024+
# Git expands the index of the submodules unexpectedly. Even though `grep`
2025+
# builtin is marked as "command_requires_full_index = 0", this config is only
2026+
# useful for the superproject. Namely, the submodules have their own configs,
2027+
# which are _not_ populated by the one-time sparse-index feature switch.
2028+
test_expect_failure 'grep within submodules is not expanded' '
2029+
init_repos_as_submodules &&
2030+
2031+
# do not use ensure_not_expanded() here, becasue `grep` should be
2032+
# run in the superproject, not in "./sparse-index"
2033+
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
2034+
git grep --cached --recurse-submodules a -- "*/folder1/*" &&
2035+
test_region ! index ensure_full_index trace2.txt
2036+
'
2037+
2038+
# NEEDSWORK: this test is not actually testing the code. The design purpose
2039+
# of this test is to verify the grep result when the submodules are using a
2040+
# sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but
2041+
# because of the index expansion, we are now grepping the "folder1/a" blob.
2042+
# Because of the problem stated above 'grep within submodules is not expanded',
2043+
# we don't have the ideal test environment yet.
2044+
test_expect_success 'grep sparse directory within submodules' '
2045+
init_repos_as_submodules &&
2046+
2047+
cat >expect <<-\EOF &&
2048+
full-checkout/folder1/a:a
2049+
sparse-checkout/folder1/a:a
2050+
sparse-index/folder1/a:a
2051+
EOF
2052+
git grep --cached --recurse-submodules a -- "*/folder1/*" >actual &&
2053+
test_cmp actual expect
2054+
'
2055+
19842056
test_done

0 commit comments

Comments
 (0)