Skip to content

Commit f7fbec4

Browse files
committed
Merge branch 'mt/grep-sparse-checkout' into seen
"git grep" has been tweaked to be limited to the sparse checkout paths. * mt/grep-sparse-checkout: config: add setting to ignore sparsity patterns in some cmds grep: honor sparse checkout patterns config: correctly read worktree configs in submodules config: make do_git_config_sequence receive a 'struct repository' t/helper/test-config: unify exit labels t/helper/test-config: diagnose missing arguments t/helper/test-config: be consistent with exit codes t1308-config-set: avoid false positives when using test-config doc: grep: unify info on configuration variables
2 parents ec498eb + aaff72b commit f7fbec4

25 files changed

+682
-138
lines changed

Documentation/config.txt

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

445445
include::config/showbranch.txt[]
446446

447+
include::config/sparse.txt[]
448+
447449
include::config/splitindex.txt[]
448450

449451
include::config/ssh.txt[]

Documentation/config/grep.txt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,23 @@ grep.extendedRegexp::
1616
other than 'default'.
1717

1818
grep.threads::
19-
Number of grep worker threads to use.
20-
See `grep.threads` in linkgit:git-grep[1] for more information.
19+
Number of grep worker threads to use. See `--threads`
20+
ifndef::git-grep[]
21+
in linkgit:git-grep[1]
22+
endif::git-grep[]
23+
for more information.
24+
25+
grep.fullName::
26+
If set to true, enable `--full-name` option by default.
2127

2228
grep.fallbackToNoIndex::
2329
If set to true, fall back to git grep --no-index if git grep
2430
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-grep.txt

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,34 +41,8 @@ characters. An empty string as search expression matches all lines.
4141
CONFIGURATION
4242
-------------
4343

44-
grep.lineNumber::
45-
If set to true, enable `-n` option by default.
46-
47-
grep.column::
48-
If set to true, enable the `--column` option by default.
49-
50-
grep.patternType::
51-
Set the default matching behavior. Using a value of 'basic', 'extended',
52-
'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
53-
`--fixed-strings`, or `--perl-regexp` option accordingly, while the
54-
value 'default' will return to the default matching behavior.
55-
56-
grep.extendedRegexp::
57-
If set to true, enable `--extended-regexp` option by default. This
58-
option is ignored when the `grep.patternType` option is set to a value
59-
other than 'default'.
60-
61-
grep.threads::
62-
Number of grep worker threads to use. If unset (or set to 0), Git will
63-
use as many threads as the number of logical cores available.
64-
65-
grep.fullName::
66-
If set to true, enable `--full-name` option by default.
67-
68-
grep.fallbackToNoIndex::
69-
If set to true, fall back to git grep --no-index if git grep
70-
is executed outside of a git repository. Defaults to false.
71-
44+
:git-grep: 1
45+
include::config/grep.txt[]
7246

7347
OPTIONS
7448
-------
@@ -269,8 +243,10 @@ providing this option will cause it to die.
269243
found.
270244

271245
--threads <num>::
272-
Number of grep worker threads to use.
273-
See `grep.threads` in 'CONFIGURATION' for more information.
246+
Number of grep worker threads to use. If not provided (or set to
247+
0), Git will use as many worker threads as the number of logical
248+
cores available. The default value can also be set with the
249+
`grep.threads` configuration.
274250

275251
-f <file>::
276252
Read patterns from <file>, one per line.

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
@@ -996,6 +996,7 @@ LIB_OBJS += sha1-name.o
996996
LIB_OBJS += shallow.o
997997
LIB_OBJS += sideband.o
998998
LIB_OBJS += sigchain.o
999+
LIB_OBJS += sparse-checkout.o
9991000
LIB_OBJS += split-index.o
10001001
LIB_OBJS += stable-qsort.o
10011002
LIB_OBJS += strbuf.o

builtin/config.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
694694
given_config_source.scope = CONFIG_SCOPE_LOCAL;
695695
} else if (use_worktree_config) {
696696
struct worktree **worktrees = get_worktrees();
697-
if (repository_format_worktree_config)
697+
if (!nongit && the_repository->worktree_config_extension)
698698
given_config_source.file = git_pathdup("config.worktree");
699699
else if (worktrees[0] && worktrees[1])
700700
die(_("--worktree cannot be used with multiple "
@@ -720,10 +720,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
720720
config_options.respect_includes = !given_config_source.file;
721721
else
722722
config_options.respect_includes = respect_includes_opt;
723-
if (!nongit) {
724-
config_options.commondir = get_git_common_dir();
725-
config_options.git_dir = get_git_dir();
726-
}
723+
if (!nongit)
724+
config_options.repo = the_repository;
727725

728726
if (end_nul) {
729727
term = '\0';

builtin/grep.c

Lines changed: 126 additions & 8 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>...]"),
@@ -410,7 +411,7 @@ static int grep_cache(struct grep_opt *opt,
410411
const struct pathspec *pathspec, int cached);
411412
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
412413
struct tree_desc *tree, struct strbuf *base, int tn_len,
413-
int check_attr);
414+
int is_root_tree);
414415

415416
static int grep_submodule(struct grep_opt *opt,
416417
const struct pathspec *pathspec,
@@ -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);
@@ -508,6 +510,10 @@ static int grep_cache(struct grep_opt *opt,
508510

509511
for (nr = 0; nr < repo->index->cache_nr; nr++) {
510512
const struct cache_entry *ce = repo->index->cache[nr];
513+
514+
if (sparse_paths_only && ce_skip_worktree(ce))
515+
continue;
516+
511517
strbuf_setlen(&name, name_base_len);
512518
strbuf_addstr(&name, ce->name);
513519

@@ -520,8 +526,7 @@ static int grep_cache(struct grep_opt *opt,
520526
* cache entry are identical, even if worktree file has
521527
* been modified, so use cache version instead
522528
*/
523-
if (cached || (ce->ce_flags & CE_VALID) ||
524-
ce_skip_worktree(ce)) {
529+
if (cached || (ce->ce_flags & CE_VALID)) {
525530
if (ce_stage(ce) || ce_intent_to_add(ce))
526531
continue;
527532
hit |= grep_oid(opt, &ce->oid, name.buf,
@@ -552,9 +557,76 @@ static int grep_cache(struct grep_opt *opt,
552557
return hit;
553558
}
554559

555-
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
556-
struct tree_desc *tree, struct strbuf *base, int tn_len,
557-
int check_attr)
560+
static struct pattern_list *get_sparsity_patterns(struct repository *repo)
561+
{
562+
struct pattern_list *patterns;
563+
char *sparse_file;
564+
int sparse_config, cone_config;
565+
566+
if (repo_config_get_bool(repo, "core.sparsecheckout", &sparse_config) ||
567+
!sparse_config) {
568+
return NULL;
569+
}
570+
571+
sparse_file = repo_git_path(repo, "info/sparse-checkout");
572+
patterns = xcalloc(1, sizeof(*patterns));
573+
574+
if (repo_config_get_bool(repo, "core.sparsecheckoutcone", &cone_config))
575+
cone_config = 0;
576+
patterns->use_cone_patterns = cone_config;
577+
578+
if (add_patterns_from_file_to_list(sparse_file, "", 0, patterns, NULL)) {
579+
if (file_exists(sparse_file)) {
580+
warning(_("failed to load sparse-checkout file: '%s'"),
581+
sparse_file);
582+
}
583+
free(sparse_file);
584+
free(patterns);
585+
return NULL;
586+
}
587+
588+
free(sparse_file);
589+
return patterns;
590+
}
591+
592+
static int path_in_sparse_checkout(struct strbuf *path, int prefix_len,
593+
unsigned int entry_mode,
594+
struct index_state *istate,
595+
struct pattern_list *sparsity,
596+
enum pattern_match_result parent_match,
597+
enum pattern_match_result *match)
598+
{
599+
int dtype = DT_UNKNOWN;
600+
int is_dir = S_ISDIR(entry_mode);
601+
602+
if (parent_match == MATCHED_RECURSIVE) {
603+
*match = parent_match;
604+
return 1;
605+
}
606+
607+
if (is_dir && !is_dir_sep(path->buf[path->len - 1]))
608+
strbuf_addch(path, '/');
609+
610+
*match = path_matches_pattern_list(path->buf, path->len,
611+
path->buf + prefix_len, &dtype,
612+
sparsity, istate);
613+
if (*match == UNDECIDED)
614+
*match = parent_match;
615+
616+
if (is_dir)
617+
strbuf_trim_trailing_dir_sep(path);
618+
619+
if (*match == NOT_MATCHED &&
620+
(!is_dir || (is_dir && sparsity->use_cone_patterns)))
621+
return 0;
622+
623+
return 1;
624+
}
625+
626+
static int do_grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
627+
struct tree_desc *tree, struct strbuf *base, int tn_len,
628+
int check_attr, struct pattern_list *sparsity,
629+
enum pattern_match_result default_sparsity_match)
558630
{
559631
struct repository *repo = opt->repo;
560632
int hit = 0;
@@ -570,6 +642,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
570642

571643
while (tree_entry(tree, &entry)) {
572644
int te_len = tree_entry_len(&entry);
645+
enum pattern_match_result sparsity_match = 0;
573646

574647
if (match != all_entries_interesting) {
575648
strbuf_addstr(&name, base->buf + tn_len);
@@ -586,6 +659,19 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
586659

587660
strbuf_add(base, entry.path, te_len);
588661

662+
if (sparsity) {
663+
struct strbuf path = STRBUF_INIT;
664+
strbuf_addstr(&path, base->buf + tn_len);
665+
666+
if (!path_in_sparse_checkout(&path, old_baselen - tn_len,
667+
entry.mode, repo->index,
668+
sparsity, default_sparsity_match,
669+
&sparsity_match)) {
670+
strbuf_setlen(base, old_baselen);
671+
continue;
672+
}
673+
}
674+
589675
if (S_ISREG(entry.mode)) {
590676
hit |= grep_oid(opt, &entry.oid, base->buf, tn_len,
591677
check_attr ? base->buf + tn_len : NULL);
@@ -602,8 +688,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
602688

603689
strbuf_addch(base, '/');
604690
init_tree_desc(&sub, data, size);
605-
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
606-
check_attr);
691+
hit |= do_grep_tree(opt, pathspec, &sub, base, tn_len,
692+
check_attr, sparsity, sparsity_match);
607693
free(data);
608694
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
609695
hit |= grep_submodule(opt, pathspec, &entry.oid,
@@ -621,6 +707,32 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
621707
return hit;
622708
}
623709

710+
/*
711+
* Note: sparsity patterns and paths' attributes will only be considered if
712+
* is_root_tree has true value. (Otherwise, we cannot properly perform pattern
713+
* matching on paths.)
714+
*/
715+
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
716+
struct tree_desc *tree, struct strbuf *base, int tn_len,
717+
int is_root_tree)
718+
{
719+
struct pattern_list *patterns = NULL;
720+
int sparse_paths_only = restrict_to_sparse_paths(opt->repo);
721+
int ret;
722+
723+
if (is_root_tree && sparse_paths_only)
724+
patterns = get_sparsity_patterns(opt->repo);
725+
726+
ret = do_grep_tree(opt, pathspec, tree, base, tn_len, is_root_tree,
727+
patterns, 0);
728+
729+
if (patterns) {
730+
clear_pattern_list(patterns);
731+
free(patterns);
732+
}
733+
return ret;
734+
}
735+
624736
static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
625737
struct object *obj, const char *name, const char *path)
626738
{
@@ -1149,6 +1261,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
11491261

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

cache.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,6 @@ extern int grafts_replace_parents;
10291029
#define GIT_REPO_VERSION 0
10301030
#define GIT_REPO_VERSION_READ 1
10311031
extern int repository_format_precious_objects;
1032-
extern int repository_format_worktree_config;
10331032

10341033
/*
10351034
* You _have_ to initialize a `struct repository_format` using

0 commit comments

Comments
 (0)