Skip to content

Commit 7506484

Browse files
matheustavaresgitster
authored andcommitted
config: add setting to ignore sparsity patterns in some cmds
When sparse checkout is enabled, some users expect the output of certain commands (such as grep, diff, and log) to be also restricted within the sparsity patterns. This would allow them to effectively work only on the subset of files in which they are interested; and allow some commands to possibly perform better, by not considering uninteresting paths. For this reason, we taught grep to honor the sparsity patterns, in the previous patch. But, on the other hand, allowing grep and the other commands mentioned to optionally ignore the patterns also make for some interesting use cases. E.g. using grep to search for a function documentation that resides outside the sparse checkout. In any case, there is no current way for users to configure the behavior they want for these commands. Aiming to provide this flexibility, let's introduce the sparse.restrictCmds setting (and the analogous --[no]-restrict-to-sparse-paths global option). The default value is true. For now, grep is the only one affected by this setting, but the goal is to have support for more commands, in the future. Helped-by: Elijah Newren <[email protected]> Signed-off-by: Matheus Tavares <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e26e8bf commit 7506484

File tree

12 files changed

+214
-6
lines changed

12 files changed

+214
-6
lines changed

Documentation/config.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ include::config/sequencer.txt[]
436436

437437
include::config/showbranch.txt[]
438438

439+
include::config/sparse.txt[]
440+
439441
include::config/splitindex.txt[]
440442

441443
include::config/ssh.txt[]

Documentation/config/grep.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ grep.fullName::
2828
grep.fallbackToNoIndex::
2929
If set to true, fall back to git grep --no-index if git grep
3030
is executed outside of a git repository. Defaults to false.
31+
32+
ifdef::git-grep[]
33+
sparse.restrictCmds::
34+
See base definition in linkgit:git-config[1]. grep honors
35+
sparse.restrictCmds by limiting searches to the sparsity paths in three
36+
cases: when searching the working tree, when searching the index with
37+
--cached, and when searching a specified commit.
38+
endif::git-grep[]

Documentation/config/sparse.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
sparse.restrictCmds::
2+
Only meaningful in conjunction with core.sparseCheckout. This option
3+
extends sparse checkouts (which limit which paths are written to the
4+
working tree), so that output and operations are also limited to the
5+
sparsity paths where possible and implemented. The purpose of this
6+
option is to (1) focus output for the user on the portion of the
7+
repository that is of interest to them, and (2) enable potentially
8+
dramatic performance improvements, especially in conjunction with
9+
partial clones.
10+
+
11+
When this option is true (default), some git commands may limit their behavior
12+
to the paths specified by the sparsity patterns, or to the intersection of
13+
those paths and any (like `*.c`) that the user might also specify on the
14+
command line. When false, the affected commands will work on full trees,
15+
ignoring the sparsity patterns. For now, only git-grep honors this setting.
16+
+
17+
Note: commands which export, integrity check, or create history will always
18+
operate on full trees (e.g. fast-export, format-patch, fsck, commit, etc.),
19+
unaffected by any sparsity patterns. Also, writing commands such as
20+
sparse-checkout and read-tree will not be affected by this configuration.

Documentation/git.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ If you just want to run git as if it was started in `<path>` then use
180180
Do not perform optional operations that require locks. This is
181181
equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
182182

183+
--[no-]restrict-to-sparse-paths::
184+
Overrides the sparse.restrictCmds configuration (see
185+
linkgit:git-config[1]) for this execution.
186+
183187
--list-cmds=group[,group...]::
184188
List commands by group. This is an internal/experimental
185189
option and may change or be removed in the future. Supported

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,7 @@ LIB_OBJS += sha1-name.o
986986
LIB_OBJS += shallow.o
987987
LIB_OBJS += sideband.o
988988
LIB_OBJS += sigchain.o
989+
LIB_OBJS += sparse-checkout.o
989990
LIB_OBJS += split-index.o
990991
LIB_OBJS += stable-qsort.o
991992
LIB_OBJS += strbuf.o

builtin/grep.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "submodule-config.h"
2626
#include "object-store.h"
2727
#include "packfile.h"
28+
#include "sparse-checkout.h"
2829

2930
static char const * const grep_usage[] = {
3031
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
@@ -498,6 +499,7 @@ static int grep_cache(struct grep_opt *opt,
498499
int nr;
499500
struct strbuf name = STRBUF_INIT;
500501
int name_base_len = 0;
502+
int sparse_paths_only = restrict_to_sparse_paths(repo);
501503
if (repo->submodule_prefix) {
502504
name_base_len = strlen(repo->submodule_prefix);
503505
strbuf_addstr(&name, repo->submodule_prefix);
@@ -509,7 +511,7 @@ static int grep_cache(struct grep_opt *opt,
509511
for (nr = 0; nr < repo->index->cache_nr; nr++) {
510512
const struct cache_entry *ce = repo->index->cache[nr];
511513

512-
if (ce_skip_worktree(ce))
514+
if (sparse_paths_only && ce_skip_worktree(ce))
513515
continue;
514516

515517
strbuf_setlen(&name, name_base_len);
@@ -715,9 +717,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
715717
int is_root_tree)
716718
{
717719
struct pattern_list *patterns = NULL;
720+
int sparse_paths_only = restrict_to_sparse_paths(opt->repo);
718721
int ret;
719722

720-
if (is_root_tree)
723+
if (is_root_tree && sparse_paths_only)
721724
patterns = get_sparsity_patterns(opt->repo);
722725

723726
ret = do_grep_tree(opt, pathspec, tree, base, tn_len, is_root_tree,
@@ -1257,6 +1260,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
12571260

12581261
if (!use_index || untracked) {
12591262
int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
1263+
1264+
if (opt_restrict_to_sparse_paths >= 0) {
1265+
die(_("--[no-]restrict-to-sparse-paths is incompatible"
1266+
" with --no-index and --untracked"));
1267+
}
1268+
12601269
hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
12611270
} else if (0 <= opt_exclude) {
12621271
die(_("--[no-]exclude-standard cannot be used for tracked contents"));

contrib/completion/git-completion.bash

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3207,6 +3207,8 @@ __git_main ()
32073207
--namespace=
32083208
--no-replace-objects
32093209
--help
3210+
--restrict-to-sparse-paths
3211+
--no-restrict-to-sparse-paths
32103212
"
32113213
;;
32123214
*)

git.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "help.h"
55
#include "run-command.h"
66
#include "alias.h"
7+
#include "sparse-checkout.h"
78

89
#define RUN_SETUP (1<<0)
910
#define RUN_SETUP_GENTLY (1<<1)
@@ -310,6 +311,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
310311
} else {
311312
exit(list_cmds(cmd));
312313
}
314+
} else if (!strcmp(cmd, "--restrict-to-sparse-paths")) {
315+
opt_restrict_to_sparse_paths = 1;
316+
} else if (!strcmp(cmd, "--no-restrict-to-sparse-paths")) {
317+
opt_restrict_to_sparse_paths = 0;
313318
} else {
314319
fprintf(stderr, _("unknown option: %s\n"), cmd);
315320
usage(git_usage_string);

sparse-checkout.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include "cache.h"
2+
#include "config.h"
3+
#include "sparse-checkout.h"
4+
5+
int opt_restrict_to_sparse_paths = -1;
6+
7+
int restrict_to_sparse_paths(struct repository *repo)
8+
{
9+
int ret;
10+
11+
if (opt_restrict_to_sparse_paths >= 0)
12+
return opt_restrict_to_sparse_paths;
13+
14+
if (repo_config_get_bool(repo, "sparse.restrictcmds", &ret))
15+
ret = 1;
16+
17+
return ret;
18+
}

sparse-checkout.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef SPARSE_CHECKOUT_H
2+
#define SPARSE_CHECKOUT_H
3+
4+
struct repository;
5+
6+
extern int opt_restrict_to_sparse_paths;
7+
8+
/* Whether or not cmds should restrict behavior on sparse paths, in this repo */
9+
int restrict_to_sparse_paths(struct repository *repo);
10+
11+
#endif /* SPARSE_CHECKOUT_H */

t/t7817-grep-sparse-checkout.sh

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ test_expect_success 'setup' '
8080
test_path_is_file sub2/a
8181
'
8282

83-
# The test below checks a special case: the sparsity patterns exclude '/b'
83+
# The two tests below check a special case: the sparsity patterns exclude '/b'
8484
# and sparse checkout is enabled, but the path exists in the working tree (e.g.
8585
# manually created after `git sparse-checkout init`). In this case, grep should
86-
# skip it.
86+
# skip the file by default, but not with --no-restrict-to-sparse-paths.
8787
test_expect_success 'grep in working tree should honor sparse checkout' '
8888
cat >expect <<-EOF &&
8989
a:text
@@ -93,6 +93,16 @@ test_expect_success 'grep in working tree should honor sparse checkout' '
9393
git grep "text" >actual &&
9494
test_cmp expect actual
9595
'
96+
test_expect_success 'grep w/ --no-restrict-to-sparse-paths for sparsely excluded but present paths' '
97+
cat >expect <<-EOF &&
98+
a:text
99+
b:new-text
100+
EOF
101+
echo "new-text" >b &&
102+
test_when_finished "rm b" &&
103+
git --no-restrict-to-sparse-paths grep "text" >actual &&
104+
test_cmp expect actual
105+
'
96106

97107
test_expect_success 'grep unmerged file despite not matching sparsity patterns' '
98108
cat >expect <<-EOF &&
@@ -157,7 +167,7 @@ test_expect_success 'grep <tree-ish> should ignore sparsity patterns' '
157167
'
158168

159169
# Note that sub2/ is present in the worktree but it is excluded by the sparsity
160-
# patterns, so grep should not recurse into it.
170+
# patterns, so grep should only recurse into it with --no-restrict-to-sparse-paths.
161171
test_expect_success 'grep --recurse-submodules should honor sparse checkout in submodule' '
162172
cat >expect <<-EOF &&
163173
a:text
@@ -166,6 +176,15 @@ test_expect_success 'grep --recurse-submodules should honor sparse checkout in s
166176
git grep --recurse-submodules "text" >actual &&
167177
test_cmp expect actual
168178
'
179+
test_expect_success 'grep --recurse-submodules should search in excluded submodules w/ --no-restrict-to-sparse-paths' '
180+
cat >expect <<-EOF &&
181+
a:text
182+
sub/B/b:text
183+
sub2/a:text
184+
EOF
185+
git --no-restrict-to-sparse-paths grep --recurse-submodules "text" >actual &&
186+
test_cmp expect actual
187+
'
169188

170189
test_expect_success 'grep --recurse-submodules --cached should honor sparse checkout in submodule' '
171190
cat >expect <<-EOF &&
@@ -192,4 +211,111 @@ test_expect_success 'grep --recurse-submodules <commit-ish> should honor sparse
192211
test_cmp expect_tag-to-commit actual_tag-to-commit
193212
'
194213

214+
for cmd in 'git --no-restrict-to-sparse-paths grep' \
215+
'git -c sparse.restrictCmds=false grep' \
216+
'git -c sparse.restrictCmds=true --no-restrict-to-sparse-paths grep'
217+
do
218+
219+
test_expect_success "$cmd --cached should ignore sparsity patterns" '
220+
cat >expect <<-EOF &&
221+
a:text
222+
b:text
223+
dir/c:text
224+
EOF
225+
$cmd --cached "text" >actual &&
226+
test_cmp expect actual
227+
'
228+
229+
test_expect_success "$cmd <commit-ish> should ignore sparsity patterns" '
230+
commit=$(git rev-parse HEAD) &&
231+
cat >expect_commit <<-EOF &&
232+
$commit:a:text
233+
$commit:b:text
234+
$commit:dir/c:text
235+
EOF
236+
cat >expect_tag-to-commit <<-EOF &&
237+
tag-to-commit:a:text
238+
tag-to-commit:b:text
239+
tag-to-commit:dir/c:text
240+
EOF
241+
$cmd "text" $commit >actual_commit &&
242+
test_cmp expect_commit actual_commit &&
243+
$cmd "text" tag-to-commit >actual_tag-to-commit &&
244+
test_cmp expect_tag-to-commit actual_tag-to-commit
245+
'
246+
done
247+
248+
test_expect_success 'grep --recurse-submodules --cached w/ --no-restrict-to-sparse-paths' '
249+
cat >expect <<-EOF &&
250+
a:text
251+
b:text
252+
dir/c:text
253+
sub/A/a:text
254+
sub/B/b:text
255+
sub2/a:text
256+
EOF
257+
git --no-restrict-to-sparse-paths grep --recurse-submodules --cached \
258+
"text" >actual &&
259+
test_cmp expect actual
260+
'
261+
262+
test_expect_success 'grep --recurse-submodules <commit-ish> w/ --no-restrict-to-sparse-paths' '
263+
commit=$(git rev-parse HEAD) &&
264+
cat >expect_commit <<-EOF &&
265+
$commit:a:text
266+
$commit:b:text
267+
$commit:dir/c:text
268+
$commit:sub/A/a:text
269+
$commit:sub/B/b:text
270+
$commit:sub2/a:text
271+
EOF
272+
cat >expect_tag-to-commit <<-EOF &&
273+
tag-to-commit:a:text
274+
tag-to-commit:b:text
275+
tag-to-commit:dir/c:text
276+
tag-to-commit:sub/A/a:text
277+
tag-to-commit:sub/B/b:text
278+
tag-to-commit:sub2/a:text
279+
EOF
280+
git --no-restrict-to-sparse-paths grep --recurse-submodules "text" \
281+
$commit >actual_commit &&
282+
test_cmp expect_commit actual_commit &&
283+
git --no-restrict-to-sparse-paths grep --recurse-submodules "text" \
284+
tag-to-commit >actual_tag-to-commit &&
285+
test_cmp expect_tag-to-commit actual_tag-to-commit
286+
'
287+
288+
test_expect_success 'should respect the sparse.restrictCmds values from submodules' '
289+
cat >expect <<-EOF &&
290+
a:text
291+
sub/A/a:text
292+
sub/B/b:text
293+
EOF
294+
test_config -C sub sparse.restrictCmds false &&
295+
git grep --cached --recurse-submodules "text" >actual &&
296+
test_cmp expect actual
297+
'
298+
299+
test_expect_success 'should propagate --[no]-restrict-to-sparse-paths to submodules' '
300+
cat >expect <<-EOF &&
301+
a:text
302+
b:text
303+
dir/c:text
304+
sub/A/a:text
305+
sub/B/b:text
306+
sub2/a:text
307+
EOF
308+
test_config -C sub sparse.restrictCmds true &&
309+
git --no-restrict-to-sparse-paths grep --cached --recurse-submodules "text" >actual &&
310+
test_cmp expect actual
311+
'
312+
313+
for opt in '--untracked' '--no-index'
314+
do
315+
test_expect_success "--[no]-restrict-to-sparse-paths and $opt are incompatible" "
316+
test_must_fail git --restrict-to-sparse-paths grep $opt . 2>actual &&
317+
test_i18ngrep 'restrict-to-sparse-paths is incompatible with' actual
318+
"
319+
done
320+
195321
test_done

t/t9902-completion.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,8 @@ test_expect_success 'double dash "git" itself' '
14731473
--namespace=
14741474
--no-replace-objects Z
14751475
--help Z
1476+
--restrict-to-sparse-paths Z
1477+
--no-restrict-to-sparse-paths Z
14761478
EOF
14771479
'
14781480

@@ -1515,7 +1517,7 @@ test_expect_success 'general options' '
15151517
test_completion "git --nam" "--namespace=" &&
15161518
test_completion "git --bar" "--bare " &&
15171519
test_completion "git --inf" "--info-path " &&
1518-
test_completion "git --no-r" "--no-replace-objects "
1520+
test_completion "git --no-rep" "--no-replace-objects "
15191521
'
15201522

15211523
test_expect_success 'general options plus command' '

0 commit comments

Comments
 (0)