Skip to content

Commit 49d93bb

Browse files
committed
Merge branch 'mt/grep-sparse-checkout' into seen
"git grep" has been tweaked to be limited to the sparse checkout paths. Review needed on 4/6; otherwise looking sane. cf. <CABPp-BGdEyEeajYZj_rdxp=MyEQdszuyjVTax=hhYj3fOtRQUQ@mail.gmail.com> * 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 t/helper/test-config: facilitate addition of new cli options t/helper/test-config: return exit codes consistently doc: grep: unify info on configuration variables
2 parents 01611d5 + 7506484 commit 49d93bb

17 files changed

+687
-118
lines changed

Documentation/config.txt

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

441441
include::config/showbranch.txt[]
442442

443+
include::config/sparse.txt[]
444+
443445
include::config/splitindex.txt[]
444446

445447
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
@@ -982,6 +982,7 @@ LIB_OBJS += sha1-name.o
982982
LIB_OBJS += shallow.o
983983
LIB_OBJS += sideband.o
984984
LIB_OBJS += sigchain.o
985+
LIB_OBJS += sparse-checkout.o
985986
LIB_OBJS += split-index.o
986987
LIB_OBJS += stable-qsort.o
987988
LIB_OBJS += strbuf.o

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
{
@@ -1148,6 +1260,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
11481260

11491261
if (!use_index || untracked) {
11501262
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+
11511269
hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
11521270
} else if (0 <= opt_exclude) {
11531271
die(_("--[no-]exclude-standard cannot be used for tracked contents"));

config.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,11 +1747,22 @@ static int do_git_config_sequence(const struct config_options *opts,
17471747
ret += git_config_from_file(fn, repo_config, data);
17481748

17491749
current_parsing_scope = CONFIG_SCOPE_WORKTREE;
1750-
if (!opts->ignore_worktree && repository_format_worktree_config) {
1751-
char *path = git_pathdup("config.worktree");
1752-
if (!access_or_die(path, R_OK, 0))
1753-
ret += git_config_from_file(fn, path, data);
1754-
free(path);
1750+
if (!opts->ignore_worktree && repo_config && opts->git_dir) {
1751+
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
1752+
struct strbuf buf = STRBUF_INIT;
1753+
1754+
read_repository_format(&repo_fmt, repo_config);
1755+
1756+
if (!verify_repository_format(&repo_fmt, &buf) &&
1757+
repo_fmt.worktree_config) {
1758+
char *path = mkpathdup("%s/config.worktree", opts->git_dir);
1759+
if (!access_or_die(path, R_OK, 0))
1760+
ret += git_config_from_file(fn, path, data);
1761+
free(path);
1762+
}
1763+
1764+
strbuf_release(&buf);
1765+
clear_repository_format(&repo_fmt);
17551766
}
17561767

17571768
current_parsing_scope = CONFIG_SCOPE_COMMAND;

contrib/completion/git-completion.bash

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3390,6 +3390,8 @@ __git_main ()
33903390
--namespace=
33913391
--no-replace-objects
33923392
--help
3393+
--restrict-to-sparse-paths
3394+
--no-restrict-to-sparse-paths
33933395
"
33943396
;;
33953397
*)

git.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "run-command.h"
66
#include "alias.h"
77
#include "shallow.h"
8+
#include "sparse-checkout.h"
89

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

0 commit comments

Comments
 (0)