Merge branch 'mt/grep-sparse-checkout' into pu
"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
This commit is contained in:
@ -436,6 +436,8 @@ include::config/sequencer.txt[]
|
|||||||
|
|
||||||
include::config/showbranch.txt[]
|
include::config/showbranch.txt[]
|
||||||
|
|
||||||
|
include::config/sparse.txt[]
|
||||||
|
|
||||||
include::config/splitindex.txt[]
|
include::config/splitindex.txt[]
|
||||||
|
|
||||||
include::config/ssh.txt[]
|
include::config/ssh.txt[]
|
||||||
|
@ -16,9 +16,23 @@ grep.extendedRegexp::
|
|||||||
other than 'default'.
|
other than 'default'.
|
||||||
|
|
||||||
grep.threads::
|
grep.threads::
|
||||||
Number of grep worker threads to use.
|
Number of grep worker threads to use. See `--threads`
|
||||||
See `grep.threads` in linkgit:git-grep[1] for more information.
|
ifndef::git-grep[]
|
||||||
|
in linkgit:git-grep[1]
|
||||||
|
endif::git-grep[]
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
grep.fullName::
|
||||||
|
If set to true, enable `--full-name` option by default.
|
||||||
|
|
||||||
grep.fallbackToNoIndex::
|
grep.fallbackToNoIndex::
|
||||||
If set to true, fall back to git grep --no-index if git grep
|
If set to true, fall back to git grep --no-index if git grep
|
||||||
is executed outside of a git repository. Defaults to false.
|
is executed outside of a git repository. Defaults to false.
|
||||||
|
|
||||||
|
ifdef::git-grep[]
|
||||||
|
sparse.restrictCmds::
|
||||||
|
See base definition in linkgit:git-config[1]. grep honors
|
||||||
|
sparse.restrictCmds by limiting searches to the sparsity paths in three
|
||||||
|
cases: when searching the working tree, when searching the index with
|
||||||
|
--cached, and when searching a specified commit.
|
||||||
|
endif::git-grep[]
|
||||||
|
20
Documentation/config/sparse.txt
Normal file
20
Documentation/config/sparse.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
sparse.restrictCmds::
|
||||||
|
Only meaningful in conjunction with core.sparseCheckout. This option
|
||||||
|
extends sparse checkouts (which limit which paths are written to the
|
||||||
|
working tree), so that output and operations are also limited to the
|
||||||
|
sparsity paths where possible and implemented. The purpose of this
|
||||||
|
option is to (1) focus output for the user on the portion of the
|
||||||
|
repository that is of interest to them, and (2) enable potentially
|
||||||
|
dramatic performance improvements, especially in conjunction with
|
||||||
|
partial clones.
|
||||||
|
+
|
||||||
|
When this option is true (default), some git commands may limit their behavior
|
||||||
|
to the paths specified by the sparsity patterns, or to the intersection of
|
||||||
|
those paths and any (like `*.c`) that the user might also specify on the
|
||||||
|
command line. When false, the affected commands will work on full trees,
|
||||||
|
ignoring the sparsity patterns. For now, only git-grep honors this setting.
|
||||||
|
+
|
||||||
|
Note: commands which export, integrity check, or create history will always
|
||||||
|
operate on full trees (e.g. fast-export, format-patch, fsck, commit, etc.),
|
||||||
|
unaffected by any sparsity patterns. Also, writing commands such as
|
||||||
|
sparse-checkout and read-tree will not be affected by this configuration.
|
@ -41,34 +41,8 @@ characters. An empty string as search expression matches all lines.
|
|||||||
CONFIGURATION
|
CONFIGURATION
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
grep.lineNumber::
|
:git-grep: 1
|
||||||
If set to true, enable `-n` option by default.
|
include::config/grep.txt[]
|
||||||
|
|
||||||
grep.column::
|
|
||||||
If set to true, enable the `--column` option by default.
|
|
||||||
|
|
||||||
grep.patternType::
|
|
||||||
Set the default matching behavior. Using a value of 'basic', 'extended',
|
|
||||||
'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
|
|
||||||
`--fixed-strings`, or `--perl-regexp` option accordingly, while the
|
|
||||||
value 'default' will return to the default matching behavior.
|
|
||||||
|
|
||||||
grep.extendedRegexp::
|
|
||||||
If set to true, enable `--extended-regexp` option by default. This
|
|
||||||
option is ignored when the `grep.patternType` option is set to a value
|
|
||||||
other than 'default'.
|
|
||||||
|
|
||||||
grep.threads::
|
|
||||||
Number of grep worker threads to use. If unset (or set to 0), Git will
|
|
||||||
use as many threads as the number of logical cores available.
|
|
||||||
|
|
||||||
grep.fullName::
|
|
||||||
If set to true, enable `--full-name` option by default.
|
|
||||||
|
|
||||||
grep.fallbackToNoIndex::
|
|
||||||
If set to true, fall back to git grep --no-index if git grep
|
|
||||||
is executed outside of a git repository. Defaults to false.
|
|
||||||
|
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
@ -269,8 +243,10 @@ providing this option will cause it to die.
|
|||||||
found.
|
found.
|
||||||
|
|
||||||
--threads <num>::
|
--threads <num>::
|
||||||
Number of grep worker threads to use.
|
Number of grep worker threads to use. If not provided (or set to
|
||||||
See `grep.threads` in 'CONFIGURATION' for more information.
|
0), Git will use as many worker threads as the number of logical
|
||||||
|
cores available. The default value can also be set with the
|
||||||
|
`grep.threads` configuration.
|
||||||
|
|
||||||
-f <file>::
|
-f <file>::
|
||||||
Read patterns from <file>, one per line.
|
Read patterns from <file>, one per line.
|
||||||
|
@ -180,6 +180,10 @@ If you just want to run git as if it was started in `<path>` then use
|
|||||||
Do not perform optional operations that require locks. This is
|
Do not perform optional operations that require locks. This is
|
||||||
equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
|
equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
|
||||||
|
|
||||||
|
--[no-]restrict-to-sparse-paths::
|
||||||
|
Overrides the sparse.restrictCmds configuration (see
|
||||||
|
linkgit:git-config[1]) for this execution.
|
||||||
|
|
||||||
--list-cmds=group[,group...]::
|
--list-cmds=group[,group...]::
|
||||||
List commands by group. This is an internal/experimental
|
List commands by group. This is an internal/experimental
|
||||||
option and may change or be removed in the future. Supported
|
option and may change or be removed in the future. Supported
|
||||||
|
1
Makefile
1
Makefile
@ -983,6 +983,7 @@ LIB_OBJS += sha1-name.o
|
|||||||
LIB_OBJS += shallow.o
|
LIB_OBJS += shallow.o
|
||||||
LIB_OBJS += sideband.o
|
LIB_OBJS += sideband.o
|
||||||
LIB_OBJS += sigchain.o
|
LIB_OBJS += sigchain.o
|
||||||
|
LIB_OBJS += sparse-checkout.o
|
||||||
LIB_OBJS += split-index.o
|
LIB_OBJS += split-index.o
|
||||||
LIB_OBJS += stable-qsort.o
|
LIB_OBJS += stable-qsort.o
|
||||||
LIB_OBJS += strbuf.o
|
LIB_OBJS += strbuf.o
|
||||||
|
134
builtin/grep.c
134
builtin/grep.c
@ -25,6 +25,7 @@
|
|||||||
#include "submodule-config.h"
|
#include "submodule-config.h"
|
||||||
#include "object-store.h"
|
#include "object-store.h"
|
||||||
#include "packfile.h"
|
#include "packfile.h"
|
||||||
|
#include "sparse-checkout.h"
|
||||||
|
|
||||||
static char const * const grep_usage[] = {
|
static char const * const grep_usage[] = {
|
||||||
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
|
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
|
||||||
@ -410,7 +411,7 @@ static int grep_cache(struct grep_opt *opt,
|
|||||||
const struct pathspec *pathspec, int cached);
|
const struct pathspec *pathspec, int cached);
|
||||||
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||||
struct tree_desc *tree, struct strbuf *base, int tn_len,
|
struct tree_desc *tree, struct strbuf *base, int tn_len,
|
||||||
int check_attr);
|
int is_root_tree);
|
||||||
|
|
||||||
static int grep_submodule(struct grep_opt *opt,
|
static int grep_submodule(struct grep_opt *opt,
|
||||||
const struct pathspec *pathspec,
|
const struct pathspec *pathspec,
|
||||||
@ -498,6 +499,7 @@ static int grep_cache(struct grep_opt *opt,
|
|||||||
int nr;
|
int nr;
|
||||||
struct strbuf name = STRBUF_INIT;
|
struct strbuf name = STRBUF_INIT;
|
||||||
int name_base_len = 0;
|
int name_base_len = 0;
|
||||||
|
int sparse_paths_only = restrict_to_sparse_paths(repo);
|
||||||
if (repo->submodule_prefix) {
|
if (repo->submodule_prefix) {
|
||||||
name_base_len = strlen(repo->submodule_prefix);
|
name_base_len = strlen(repo->submodule_prefix);
|
||||||
strbuf_addstr(&name, repo->submodule_prefix);
|
strbuf_addstr(&name, repo->submodule_prefix);
|
||||||
@ -508,6 +510,10 @@ static int grep_cache(struct grep_opt *opt,
|
|||||||
|
|
||||||
for (nr = 0; nr < repo->index->cache_nr; nr++) {
|
for (nr = 0; nr < repo->index->cache_nr; nr++) {
|
||||||
const struct cache_entry *ce = repo->index->cache[nr];
|
const struct cache_entry *ce = repo->index->cache[nr];
|
||||||
|
|
||||||
|
if (sparse_paths_only && ce_skip_worktree(ce))
|
||||||
|
continue;
|
||||||
|
|
||||||
strbuf_setlen(&name, name_base_len);
|
strbuf_setlen(&name, name_base_len);
|
||||||
strbuf_addstr(&name, ce->name);
|
strbuf_addstr(&name, ce->name);
|
||||||
|
|
||||||
@ -520,8 +526,7 @@ static int grep_cache(struct grep_opt *opt,
|
|||||||
* cache entry are identical, even if worktree file has
|
* cache entry are identical, even if worktree file has
|
||||||
* been modified, so use cache version instead
|
* been modified, so use cache version instead
|
||||||
*/
|
*/
|
||||||
if (cached || (ce->ce_flags & CE_VALID) ||
|
if (cached || (ce->ce_flags & CE_VALID)) {
|
||||||
ce_skip_worktree(ce)) {
|
|
||||||
if (ce_stage(ce) || ce_intent_to_add(ce))
|
if (ce_stage(ce) || ce_intent_to_add(ce))
|
||||||
continue;
|
continue;
|
||||||
hit |= grep_oid(opt, &ce->oid, name.buf,
|
hit |= grep_oid(opt, &ce->oid, name.buf,
|
||||||
@ -552,9 +557,76 @@ static int grep_cache(struct grep_opt *opt,
|
|||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
static struct pattern_list *get_sparsity_patterns(struct repository *repo)
|
||||||
struct tree_desc *tree, struct strbuf *base, int tn_len,
|
{
|
||||||
int check_attr)
|
struct pattern_list *patterns;
|
||||||
|
char *sparse_file;
|
||||||
|
int sparse_config, cone_config;
|
||||||
|
|
||||||
|
if (repo_config_get_bool(repo, "core.sparsecheckout", &sparse_config) ||
|
||||||
|
!sparse_config) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sparse_file = repo_git_path(repo, "info/sparse-checkout");
|
||||||
|
patterns = xcalloc(1, sizeof(*patterns));
|
||||||
|
|
||||||
|
if (repo_config_get_bool(repo, "core.sparsecheckoutcone", &cone_config))
|
||||||
|
cone_config = 0;
|
||||||
|
patterns->use_cone_patterns = cone_config;
|
||||||
|
|
||||||
|
if (add_patterns_from_file_to_list(sparse_file, "", 0, patterns, NULL)) {
|
||||||
|
if (file_exists(sparse_file)) {
|
||||||
|
warning(_("failed to load sparse-checkout file: '%s'"),
|
||||||
|
sparse_file);
|
||||||
|
}
|
||||||
|
free(sparse_file);
|
||||||
|
free(patterns);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sparse_file);
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int path_in_sparse_checkout(struct strbuf *path, int prefix_len,
|
||||||
|
unsigned int entry_mode,
|
||||||
|
struct index_state *istate,
|
||||||
|
struct pattern_list *sparsity,
|
||||||
|
enum pattern_match_result parent_match,
|
||||||
|
enum pattern_match_result *match)
|
||||||
|
{
|
||||||
|
int dtype = DT_UNKNOWN;
|
||||||
|
int is_dir = S_ISDIR(entry_mode);
|
||||||
|
|
||||||
|
if (parent_match == MATCHED_RECURSIVE) {
|
||||||
|
*match = parent_match;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dir && !is_dir_sep(path->buf[path->len - 1]))
|
||||||
|
strbuf_addch(path, '/');
|
||||||
|
|
||||||
|
*match = path_matches_pattern_list(path->buf, path->len,
|
||||||
|
path->buf + prefix_len, &dtype,
|
||||||
|
sparsity, istate);
|
||||||
|
if (*match == UNDECIDED)
|
||||||
|
*match = parent_match;
|
||||||
|
|
||||||
|
if (is_dir)
|
||||||
|
strbuf_trim_trailing_dir_sep(path);
|
||||||
|
|
||||||
|
if (*match == NOT_MATCHED &&
|
||||||
|
(!is_dir || (is_dir && sparsity->use_cone_patterns)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||||
|
struct tree_desc *tree, struct strbuf *base, int tn_len,
|
||||||
|
int check_attr, struct pattern_list *sparsity,
|
||||||
|
enum pattern_match_result default_sparsity_match)
|
||||||
{
|
{
|
||||||
struct repository *repo = opt->repo;
|
struct repository *repo = opt->repo;
|
||||||
int hit = 0;
|
int hit = 0;
|
||||||
@ -570,6 +642,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
|
|
||||||
while (tree_entry(tree, &entry)) {
|
while (tree_entry(tree, &entry)) {
|
||||||
int te_len = tree_entry_len(&entry);
|
int te_len = tree_entry_len(&entry);
|
||||||
|
enum pattern_match_result sparsity_match = 0;
|
||||||
|
|
||||||
if (match != all_entries_interesting) {
|
if (match != all_entries_interesting) {
|
||||||
strbuf_addstr(&name, base->buf + tn_len);
|
strbuf_addstr(&name, base->buf + tn_len);
|
||||||
@ -586,6 +659,19 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
|
|
||||||
strbuf_add(base, entry.path, te_len);
|
strbuf_add(base, entry.path, te_len);
|
||||||
|
|
||||||
|
if (sparsity) {
|
||||||
|
struct strbuf path = STRBUF_INIT;
|
||||||
|
strbuf_addstr(&path, base->buf + tn_len);
|
||||||
|
|
||||||
|
if (!path_in_sparse_checkout(&path, old_baselen - tn_len,
|
||||||
|
entry.mode, repo->index,
|
||||||
|
sparsity, default_sparsity_match,
|
||||||
|
&sparsity_match)) {
|
||||||
|
strbuf_setlen(base, old_baselen);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (S_ISREG(entry.mode)) {
|
if (S_ISREG(entry.mode)) {
|
||||||
hit |= grep_oid(opt, &entry.oid, base->buf, tn_len,
|
hit |= grep_oid(opt, &entry.oid, base->buf, tn_len,
|
||||||
check_attr ? base->buf + tn_len : NULL);
|
check_attr ? base->buf + tn_len : NULL);
|
||||||
@ -602,8 +688,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
|
|
||||||
strbuf_addch(base, '/');
|
strbuf_addch(base, '/');
|
||||||
init_tree_desc(&sub, data, size);
|
init_tree_desc(&sub, data, size);
|
||||||
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
|
hit |= do_grep_tree(opt, pathspec, &sub, base, tn_len,
|
||||||
check_attr);
|
check_attr, sparsity, sparsity_match);
|
||||||
free(data);
|
free(data);
|
||||||
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
|
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
|
||||||
hit |= grep_submodule(opt, pathspec, &entry.oid,
|
hit |= grep_submodule(opt, pathspec, &entry.oid,
|
||||||
@ -621,6 +707,32 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
|||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: sparsity patterns and paths' attributes will only be considered if
|
||||||
|
* is_root_tree has true value. (Otherwise, we cannot properly perform pattern
|
||||||
|
* matching on paths.)
|
||||||
|
*/
|
||||||
|
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||||
|
struct tree_desc *tree, struct strbuf *base, int tn_len,
|
||||||
|
int is_root_tree)
|
||||||
|
{
|
||||||
|
struct pattern_list *patterns = NULL;
|
||||||
|
int sparse_paths_only = restrict_to_sparse_paths(opt->repo);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (is_root_tree && sparse_paths_only)
|
||||||
|
patterns = get_sparsity_patterns(opt->repo);
|
||||||
|
|
||||||
|
ret = do_grep_tree(opt, pathspec, tree, base, tn_len, is_root_tree,
|
||||||
|
patterns, 0);
|
||||||
|
|
||||||
|
if (patterns) {
|
||||||
|
clear_pattern_list(patterns);
|
||||||
|
free(patterns);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
|
static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
|
||||||
struct object *obj, const char *name, const char *path)
|
struct object *obj, const char *name, const char *path)
|
||||||
{
|
{
|
||||||
@ -1148,6 +1260,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
if (!use_index || untracked) {
|
if (!use_index || untracked) {
|
||||||
int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
|
int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
|
||||||
|
|
||||||
|
if (opt_restrict_to_sparse_paths >= 0) {
|
||||||
|
die(_("--[no-]restrict-to-sparse-paths is incompatible"
|
||||||
|
" with --no-index and --untracked"));
|
||||||
|
}
|
||||||
|
|
||||||
hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
|
hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
|
||||||
} else if (0 <= opt_exclude) {
|
} else if (0 <= opt_exclude) {
|
||||||
die(_("--[no-]exclude-standard cannot be used for tracked contents"));
|
die(_("--[no-]exclude-standard cannot be used for tracked contents"));
|
||||||
|
21
config.c
21
config.c
@ -1747,11 +1747,22 @@ static int do_git_config_sequence(const struct config_options *opts,
|
|||||||
ret += git_config_from_file(fn, repo_config, data);
|
ret += git_config_from_file(fn, repo_config, data);
|
||||||
|
|
||||||
current_parsing_scope = CONFIG_SCOPE_WORKTREE;
|
current_parsing_scope = CONFIG_SCOPE_WORKTREE;
|
||||||
if (!opts->ignore_worktree && repository_format_worktree_config) {
|
if (!opts->ignore_worktree && repo_config && opts->git_dir) {
|
||||||
char *path = git_pathdup("config.worktree");
|
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
|
||||||
if (!access_or_die(path, R_OK, 0))
|
struct strbuf buf = STRBUF_INIT;
|
||||||
ret += git_config_from_file(fn, path, data);
|
|
||||||
free(path);
|
read_repository_format(&repo_fmt, repo_config);
|
||||||
|
|
||||||
|
if (!verify_repository_format(&repo_fmt, &buf) &&
|
||||||
|
repo_fmt.worktree_config) {
|
||||||
|
char *path = mkpathdup("%s/config.worktree", opts->git_dir);
|
||||||
|
if (!access_or_die(path, R_OK, 0))
|
||||||
|
ret += git_config_from_file(fn, path, data);
|
||||||
|
free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_release(&buf);
|
||||||
|
clear_repository_format(&repo_fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_parsing_scope = CONFIG_SCOPE_COMMAND;
|
current_parsing_scope = CONFIG_SCOPE_COMMAND;
|
||||||
|
@ -3382,6 +3382,8 @@ __git_main ()
|
|||||||
--namespace=
|
--namespace=
|
||||||
--no-replace-objects
|
--no-replace-objects
|
||||||
--help
|
--help
|
||||||
|
--restrict-to-sparse-paths
|
||||||
|
--no-restrict-to-sparse-paths
|
||||||
"
|
"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
5
git.c
5
git.c
@ -5,6 +5,7 @@
|
|||||||
#include "run-command.h"
|
#include "run-command.h"
|
||||||
#include "alias.h"
|
#include "alias.h"
|
||||||
#include "shallow.h"
|
#include "shallow.h"
|
||||||
|
#include "sparse-checkout.h"
|
||||||
|
|
||||||
#define RUN_SETUP (1<<0)
|
#define RUN_SETUP (1<<0)
|
||||||
#define RUN_SETUP_GENTLY (1<<1)
|
#define RUN_SETUP_GENTLY (1<<1)
|
||||||
@ -311,6 +312,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
|||||||
} else {
|
} else {
|
||||||
exit(list_cmds(cmd));
|
exit(list_cmds(cmd));
|
||||||
}
|
}
|
||||||
|
} else if (!strcmp(cmd, "--restrict-to-sparse-paths")) {
|
||||||
|
opt_restrict_to_sparse_paths = 1;
|
||||||
|
} else if (!strcmp(cmd, "--no-restrict-to-sparse-paths")) {
|
||||||
|
opt_restrict_to_sparse_paths = 0;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, _("unknown option: %s\n"), cmd);
|
fprintf(stderr, _("unknown option: %s\n"), cmd);
|
||||||
usage(git_usage_string);
|
usage(git_usage_string);
|
||||||
|
18
sparse-checkout.c
Normal file
18
sparse-checkout.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "cache.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "sparse-checkout.h"
|
||||||
|
|
||||||
|
int opt_restrict_to_sparse_paths = -1;
|
||||||
|
|
||||||
|
int restrict_to_sparse_paths(struct repository *repo)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (opt_restrict_to_sparse_paths >= 0)
|
||||||
|
return opt_restrict_to_sparse_paths;
|
||||||
|
|
||||||
|
if (repo_config_get_bool(repo, "sparse.restrictcmds", &ret))
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
11
sparse-checkout.h
Normal file
11
sparse-checkout.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef SPARSE_CHECKOUT_H
|
||||||
|
#define SPARSE_CHECKOUT_H
|
||||||
|
|
||||||
|
struct repository;
|
||||||
|
|
||||||
|
extern int opt_restrict_to_sparse_paths;
|
||||||
|
|
||||||
|
/* Whether or not cmds should restrict behavior on sparse paths, in this repo */
|
||||||
|
int restrict_to_sparse_paths(struct repository *repo);
|
||||||
|
|
||||||
|
#endif /* SPARSE_CHECKOUT_H */
|
@ -2,12 +2,19 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program exposes the C API of the configuration mechanism
|
* This program exposes the C API of the configuration mechanism
|
||||||
* as a set of simple commands in order to facilitate testing.
|
* as a set of simple commands in order to facilitate testing.
|
||||||
*
|
*
|
||||||
* Reads stdin and prints result of command to stdout:
|
* Usage: test-tool config [--submodule=<path>] <cmd> [<args>]
|
||||||
|
*
|
||||||
|
* If --submodule=<path> is given, <cmd> will operate on the submodule at the
|
||||||
|
* given <path>. This option is not valid for the commands: read_early_config,
|
||||||
|
* configset_get_value and configset_get_value_multi.
|
||||||
|
*
|
||||||
|
* Possible cmds are:
|
||||||
*
|
*
|
||||||
* get_value -> prints the value with highest priority for the entered key
|
* get_value -> prints the value with highest priority for the entered key
|
||||||
*
|
*
|
||||||
@ -30,6 +37,14 @@
|
|||||||
* iterate -> iterate over all values using git_config(), and print some
|
* iterate -> iterate over all values using git_config(), and print some
|
||||||
* data for each
|
* data for each
|
||||||
*
|
*
|
||||||
|
* Exit codes:
|
||||||
|
* 0: success
|
||||||
|
* 1: value not found for the given config key
|
||||||
|
* 2: config file path given as argument is inaccessible or doesn't exist
|
||||||
|
* 129: test-config usage error
|
||||||
|
*
|
||||||
|
* Note: tests may also expect 128 for die() calls in the config machinery.
|
||||||
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
*
|
*
|
||||||
* To print the value with highest priority for key "foo.bAr Baz.rock":
|
* To print the value with highest priority for key "foo.bAr Baz.rock":
|
||||||
@ -64,131 +79,173 @@ static int early_config_cb(const char *var, const char *value, void *vdata)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum test_config_exit_code {
|
||||||
|
TC_SUCCESS = 0,
|
||||||
|
TC_VALUE_NOT_FOUND = 1,
|
||||||
|
TC_CONFIG_FILE_ERROR = 2,
|
||||||
|
TC_USAGE_ERROR = 129,
|
||||||
|
};
|
||||||
|
|
||||||
int cmd__config(int argc, const char **argv)
|
int cmd__config(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
int i, val;
|
int i, val;
|
||||||
const char *v;
|
const char *v;
|
||||||
const struct string_list *strptr;
|
const struct string_list *strptr;
|
||||||
struct config_set cs;
|
struct config_set cs = { .hash_initialized = 0 };
|
||||||
|
enum test_config_exit_code ret = TC_SUCCESS;
|
||||||
|
struct repository *repo = the_repository;
|
||||||
|
const char *subrepo_path = NULL;
|
||||||
|
|
||||||
if (argc == 3 && !strcmp(argv[1], "read_early_config")) {
|
argc--; /* skip over "config" */
|
||||||
read_early_config(early_config_cb, (void *)argv[2]);
|
argv++;
|
||||||
return 0;
|
|
||||||
|
if (argc == 0)
|
||||||
|
goto print_usage_error;
|
||||||
|
|
||||||
|
if (skip_prefix(*argv, "--submodule=", &subrepo_path)) {
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
if (argc == 0)
|
||||||
|
goto print_usage_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 2 && !strcmp(argv[0], "read_early_config")) {
|
||||||
|
if (subrepo_path) {
|
||||||
|
fprintf(stderr, "Cannot use --submodule with read_early_config\n");
|
||||||
|
return TC_USAGE_ERROR;
|
||||||
|
}
|
||||||
|
read_early_config(early_config_cb, (void *)argv[1]);
|
||||||
|
return TC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_git_directory();
|
setup_git_directory();
|
||||||
|
|
||||||
git_configset_init(&cs);
|
git_configset_init(&cs);
|
||||||
|
|
||||||
if (argc < 2) {
|
if (subrepo_path) {
|
||||||
fprintf(stderr, "Please, provide a command name on the command-line\n");
|
const struct submodule *sub;
|
||||||
goto exit1;
|
struct repository *subrepo = xcalloc(1, sizeof(*repo));
|
||||||
} else if (argc == 3 && !strcmp(argv[1], "get_value")) {
|
|
||||||
if (!git_config_get_value(argv[2], &v)) {
|
sub = submodule_from_path(the_repository, &null_oid, subrepo_path);
|
||||||
if (!v)
|
if (!sub || repo_submodule_init(subrepo, the_repository, sub)) {
|
||||||
printf("(NULL)\n");
|
fprintf(stderr, "Invalid argument to --submodule: '%s'\n",
|
||||||
else
|
subrepo_path);
|
||||||
printf("%s\n", v);
|
free(subrepo);
|
||||||
goto exit0;
|
ret = TC_USAGE_ERROR;
|
||||||
} else {
|
goto out;
|
||||||
printf("Value not found for \"%s\"\n", argv[2]);
|
|
||||||
goto exit1;
|
|
||||||
}
|
}
|
||||||
} else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
|
repo = subrepo;
|
||||||
strptr = git_config_get_value_multi(argv[2]);
|
|
||||||
if (strptr) {
|
|
||||||
for (i = 0; i < strptr->nr; i++) {
|
|
||||||
v = strptr->items[i].string;
|
|
||||||
if (!v)
|
|
||||||
printf("(NULL)\n");
|
|
||||||
else
|
|
||||||
printf("%s\n", v);
|
|
||||||
}
|
|
||||||
goto exit0;
|
|
||||||
} else {
|
|
||||||
printf("Value not found for \"%s\"\n", argv[2]);
|
|
||||||
goto exit1;
|
|
||||||
}
|
|
||||||
} else if (argc == 3 && !strcmp(argv[1], "get_int")) {
|
|
||||||
if (!git_config_get_int(argv[2], &val)) {
|
|
||||||
printf("%d\n", val);
|
|
||||||
goto exit0;
|
|
||||||
} else {
|
|
||||||
printf("Value not found for \"%s\"\n", argv[2]);
|
|
||||||
goto exit1;
|
|
||||||
}
|
|
||||||
} else if (argc == 3 && !strcmp(argv[1], "get_bool")) {
|
|
||||||
if (!git_config_get_bool(argv[2], &val)) {
|
|
||||||
printf("%d\n", val);
|
|
||||||
goto exit0;
|
|
||||||
} else {
|
|
||||||
printf("Value not found for \"%s\"\n", argv[2]);
|
|
||||||
goto exit1;
|
|
||||||
}
|
|
||||||
} else if (argc == 3 && !strcmp(argv[1], "get_string")) {
|
|
||||||
if (!git_config_get_string_const(argv[2], &v)) {
|
|
||||||
printf("%s\n", v);
|
|
||||||
goto exit0;
|
|
||||||
} else {
|
|
||||||
printf("Value not found for \"%s\"\n", argv[2]);
|
|
||||||
goto exit1;
|
|
||||||
}
|
|
||||||
} else if (!strcmp(argv[1], "configset_get_value")) {
|
|
||||||
for (i = 3; i < argc; i++) {
|
|
||||||
int err;
|
|
||||||
if ((err = git_configset_add_file(&cs, argv[i]))) {
|
|
||||||
fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
|
|
||||||
goto exit2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!git_configset_get_value(&cs, argv[2], &v)) {
|
|
||||||
if (!v)
|
|
||||||
printf("(NULL)\n");
|
|
||||||
else
|
|
||||||
printf("%s\n", v);
|
|
||||||
goto exit0;
|
|
||||||
} else {
|
|
||||||
printf("Value not found for \"%s\"\n", argv[2]);
|
|
||||||
goto exit1;
|
|
||||||
}
|
|
||||||
} else if (!strcmp(argv[1], "configset_get_value_multi")) {
|
|
||||||
for (i = 3; i < argc; i++) {
|
|
||||||
int err;
|
|
||||||
if ((err = git_configset_add_file(&cs, argv[i]))) {
|
|
||||||
fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
|
|
||||||
goto exit2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strptr = git_configset_get_value_multi(&cs, argv[2]);
|
|
||||||
if (strptr) {
|
|
||||||
for (i = 0; i < strptr->nr; i++) {
|
|
||||||
v = strptr->items[i].string;
|
|
||||||
if (!v)
|
|
||||||
printf("(NULL)\n");
|
|
||||||
else
|
|
||||||
printf("%s\n", v);
|
|
||||||
}
|
|
||||||
goto exit0;
|
|
||||||
} else {
|
|
||||||
printf("Value not found for \"%s\"\n", argv[2]);
|
|
||||||
goto exit1;
|
|
||||||
}
|
|
||||||
} else if (!strcmp(argv[1], "iterate")) {
|
|
||||||
git_config(iterate_cb, NULL);
|
|
||||||
goto exit0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
die("%s: Please check the syntax and the function name", argv[0]);
|
if (argc == 2 && !strcmp(argv[0], "get_value")) {
|
||||||
|
if (!repo_config_get_value(repo, argv[1], &v)) {
|
||||||
|
if (!v)
|
||||||
|
printf("(NULL)\n");
|
||||||
|
else
|
||||||
|
printf("%s\n", v);
|
||||||
|
} else {
|
||||||
|
printf("Value not found for \"%s\"\n", argv[1]);
|
||||||
|
ret = TC_VALUE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
} else if (argc == 2 && !strcmp(argv[0], "get_value_multi")) {
|
||||||
|
strptr = repo_config_get_value_multi(repo, argv[1]);
|
||||||
|
if (strptr) {
|
||||||
|
for (i = 0; i < strptr->nr; i++) {
|
||||||
|
v = strptr->items[i].string;
|
||||||
|
if (!v)
|
||||||
|
printf("(NULL)\n");
|
||||||
|
else
|
||||||
|
printf("%s\n", v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Value not found for \"%s\"\n", argv[1]);
|
||||||
|
ret = TC_VALUE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
} else if (argc == 2 && !strcmp(argv[0], "get_int")) {
|
||||||
|
if (!repo_config_get_int(repo, argv[1], &val)) {
|
||||||
|
printf("%d\n", val);
|
||||||
|
} else {
|
||||||
|
printf("Value not found for \"%s\"\n", argv[1]);
|
||||||
|
ret = TC_VALUE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
} else if (argc == 2 && !strcmp(argv[0], "get_bool")) {
|
||||||
|
if (!repo_config_get_bool(repo, argv[1], &val)) {
|
||||||
|
printf("%d\n", val);
|
||||||
|
} else {
|
||||||
|
|
||||||
exit0:
|
printf("Value not found for \"%s\"\n", argv[1]);
|
||||||
git_configset_clear(&cs);
|
ret = TC_VALUE_NOT_FOUND;
|
||||||
return 0;
|
}
|
||||||
|
} else if (argc == 2 && !strcmp(argv[0], "get_string")) {
|
||||||
|
if (!repo_config_get_string_const(repo, argv[1], &v)) {
|
||||||
|
printf("%s\n", v);
|
||||||
|
} else {
|
||||||
|
printf("Value not found for \"%s\"\n", argv[1]);
|
||||||
|
ret = TC_VALUE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
} else if (argc >= 2 && !strcmp(argv[0], "configset_get_value")) {
|
||||||
|
if (subrepo_path) {
|
||||||
|
fprintf(stderr, "Cannot use --submodule with configset_get_value\n");
|
||||||
|
ret = TC_USAGE_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
for (i = 2; i < argc; i++) {
|
||||||
|
int err;
|
||||||
|
if ((err = git_configset_add_file(&cs, argv[i]))) {
|
||||||
|
fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
|
||||||
|
ret = TC_CONFIG_FILE_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!git_configset_get_value(&cs, argv[1], &v)) {
|
||||||
|
if (!v)
|
||||||
|
printf("(NULL)\n");
|
||||||
|
else
|
||||||
|
printf("%s\n", v);
|
||||||
|
} else {
|
||||||
|
printf("Value not found for \"%s\"\n", argv[1]);
|
||||||
|
ret = TC_VALUE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
} else if (argc >= 2 && !strcmp(argv[0], "configset_get_value_multi")) {
|
||||||
|
if (subrepo_path) {
|
||||||
|
fprintf(stderr, "Cannot use --submodule with configset_get_value_multi\n");
|
||||||
|
ret = TC_USAGE_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
for (i = 2; i < argc; i++) {
|
||||||
|
int err;
|
||||||
|
if ((err = git_configset_add_file(&cs, argv[i]))) {
|
||||||
|
fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
|
||||||
|
ret = TC_CONFIG_FILE_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strptr = git_configset_get_value_multi(&cs, argv[1]);
|
||||||
|
if (strptr) {
|
||||||
|
for (i = 0; i < strptr->nr; i++) {
|
||||||
|
v = strptr->items[i].string;
|
||||||
|
if (!v)
|
||||||
|
printf("(NULL)\n");
|
||||||
|
else
|
||||||
|
printf("%s\n", v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Value not found for \"%s\"\n", argv[1]);
|
||||||
|
ret = TC_VALUE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[0], "iterate")) {
|
||||||
|
repo_config(repo, iterate_cb, NULL);
|
||||||
|
} else {
|
||||||
|
print_usage_error:
|
||||||
|
fprintf(stderr, "Invalid syntax. Usage: test-tool config"
|
||||||
|
" [--submodule=<path>] <cmd> [args]\n");
|
||||||
|
ret = TC_USAGE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
exit1:
|
out:
|
||||||
git_configset_clear(&cs);
|
git_configset_clear(&cs);
|
||||||
return 1;
|
if (repo != the_repository) {
|
||||||
|
repo_clear(repo);
|
||||||
exit2:
|
free(repo);
|
||||||
git_configset_clear(&cs);
|
}
|
||||||
return 2;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -78,4 +78,20 @@ test_expect_success 'config.worktree no longer read without extension' '
|
|||||||
test_cmp_config -C wt2 shared this.is
|
test_cmp_config -C wt2 shared this.is
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correctly read config.worktree from submodules' '
|
||||||
|
test_unconfig extensions.worktreeConfig &&
|
||||||
|
git init sub &&
|
||||||
|
(
|
||||||
|
cd sub &&
|
||||||
|
test_commit A &&
|
||||||
|
git config extensions.worktreeConfig true &&
|
||||||
|
git config --worktree wtconfig.sub test-value
|
||||||
|
) &&
|
||||||
|
git submodule add ./sub &&
|
||||||
|
git commit -m "add sub" &&
|
||||||
|
echo test-value >expect &&
|
||||||
|
test-tool config --submodule=sub get_value wtconfig.sub >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -109,15 +109,6 @@ test_expect_success 'ls-files --modified' '
|
|||||||
test -z "$(git ls-files -m)"
|
test -z "$(git ls-files -m)"
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'grep with skip-worktree file' '
|
|
||||||
git update-index --no-skip-worktree 1 &&
|
|
||||||
echo test > 1 &&
|
|
||||||
git update-index 1 &&
|
|
||||||
git update-index --skip-worktree 1 &&
|
|
||||||
rm 1 &&
|
|
||||||
test "$(git grep --no-ext-grep test)" = "1:test"
|
|
||||||
'
|
|
||||||
|
|
||||||
echo ":000000 100644 $ZERO_OID $EMPTY_BLOB A 1" > expected
|
echo ":000000 100644 $ZERO_OID $EMPTY_BLOB A 1" > expected
|
||||||
test_expect_success 'diff-index does not examine skip-worktree absent entries' '
|
test_expect_success 'diff-index does not examine skip-worktree absent entries' '
|
||||||
setup_absent &&
|
setup_absent &&
|
||||||
|
321
t/t7817-grep-sparse-checkout.sh
Executable file
321
t/t7817-grep-sparse-checkout.sh
Executable file
@ -0,0 +1,321 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='grep in sparse checkout
|
||||||
|
|
||||||
|
This test creates a repo with the following structure:
|
||||||
|
|
||||||
|
.
|
||||||
|
|-- a
|
||||||
|
|-- b
|
||||||
|
|-- dir
|
||||||
|
| `-- c
|
||||||
|
|-- sub
|
||||||
|
| |-- A
|
||||||
|
| | `-- a
|
||||||
|
| `-- B
|
||||||
|
| `-- b
|
||||||
|
`-- sub2
|
||||||
|
`-- a
|
||||||
|
|
||||||
|
Where the outer repository has non-cone mode sparsity patterns, sub is a
|
||||||
|
submodule with cone mode sparsity patterns and sub2 is a submodule that is
|
||||||
|
excluded by the superproject sparsity patterns. The resulting sparse checkout
|
||||||
|
should leave the following structure in the working tree:
|
||||||
|
|
||||||
|
.
|
||||||
|
|-- a
|
||||||
|
|-- sub
|
||||||
|
| `-- B
|
||||||
|
| `-- b
|
||||||
|
`-- sub2
|
||||||
|
`-- a
|
||||||
|
|
||||||
|
But note that sub2 should have the SKIP_WORKTREE bit set.
|
||||||
|
'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
echo "text" >a &&
|
||||||
|
echo "text" >b &&
|
||||||
|
mkdir dir &&
|
||||||
|
echo "text" >dir/c &&
|
||||||
|
|
||||||
|
git init sub &&
|
||||||
|
(
|
||||||
|
cd sub &&
|
||||||
|
mkdir A B &&
|
||||||
|
echo "text" >A/a &&
|
||||||
|
echo "text" >B/b &&
|
||||||
|
git add A B &&
|
||||||
|
git commit -m sub &&
|
||||||
|
git sparse-checkout init --cone &&
|
||||||
|
git sparse-checkout set B
|
||||||
|
) &&
|
||||||
|
|
||||||
|
git init sub2 &&
|
||||||
|
(
|
||||||
|
cd sub2 &&
|
||||||
|
echo "text" >a &&
|
||||||
|
git add a &&
|
||||||
|
git commit -m sub2
|
||||||
|
) &&
|
||||||
|
|
||||||
|
git submodule add ./sub &&
|
||||||
|
git submodule add ./sub2 &&
|
||||||
|
git add a b dir &&
|
||||||
|
git commit -m super &&
|
||||||
|
git sparse-checkout init --no-cone &&
|
||||||
|
git sparse-checkout set "/*" "!b" "!/*/" "sub" &&
|
||||||
|
|
||||||
|
git tag -am tag-to-commit tag-to-commit HEAD &&
|
||||||
|
tree=$(git rev-parse HEAD^{tree}) &&
|
||||||
|
git tag -am tag-to-tree tag-to-tree $tree &&
|
||||||
|
|
||||||
|
test_path_is_missing b &&
|
||||||
|
test_path_is_missing dir &&
|
||||||
|
test_path_is_missing sub/A &&
|
||||||
|
test_path_is_file a &&
|
||||||
|
test_path_is_file sub/B/b &&
|
||||||
|
test_path_is_file sub2/a
|
||||||
|
'
|
||||||
|
|
||||||
|
# The two tests below check a special case: the sparsity patterns exclude '/b'
|
||||||
|
# and sparse checkout is enabled, but the path exists in the working tree (e.g.
|
||||||
|
# manually created after `git sparse-checkout init`). In this case, grep should
|
||||||
|
# skip the file by default, but not with --no-restrict-to-sparse-paths.
|
||||||
|
test_expect_success 'grep in working tree should honor sparse checkout' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
EOF
|
||||||
|
echo "new-text" >b &&
|
||||||
|
test_when_finished "rm b" &&
|
||||||
|
git grep "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
test_expect_success 'grep w/ --no-restrict-to-sparse-paths for sparsely excluded but present paths' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
b:new-text
|
||||||
|
EOF
|
||||||
|
echo "new-text" >b &&
|
||||||
|
test_when_finished "rm b" &&
|
||||||
|
git --no-restrict-to-sparse-paths grep "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep unmerged file despite not matching sparsity patterns' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
b:modified-b-in-branchX
|
||||||
|
b:modified-b-in-branchY
|
||||||
|
EOF
|
||||||
|
test_when_finished "test_might_fail git merge --abort && \
|
||||||
|
git checkout master" &&
|
||||||
|
|
||||||
|
git sparse-checkout disable &&
|
||||||
|
git checkout -b branchY master &&
|
||||||
|
test_commit modified-b-in-branchY b &&
|
||||||
|
git checkout -b branchX master &&
|
||||||
|
test_commit modified-b-in-branchX b &&
|
||||||
|
|
||||||
|
git sparse-checkout init &&
|
||||||
|
test_path_is_missing b &&
|
||||||
|
test_must_fail git merge branchY &&
|
||||||
|
git grep "modified-b" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep --cached should honor sparse checkout' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
EOF
|
||||||
|
git grep --cached "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep <commit-ish> should honor sparse checkout' '
|
||||||
|
commit=$(git rev-parse HEAD) &&
|
||||||
|
cat >expect_commit <<-EOF &&
|
||||||
|
$commit:a:text
|
||||||
|
EOF
|
||||||
|
cat >expect_tag-to-commit <<-EOF &&
|
||||||
|
tag-to-commit:a:text
|
||||||
|
EOF
|
||||||
|
git grep "text" $commit >actual_commit &&
|
||||||
|
test_cmp expect_commit actual_commit &&
|
||||||
|
git grep "text" tag-to-commit >actual_tag-to-commit &&
|
||||||
|
test_cmp expect_tag-to-commit actual_tag-to-commit
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep <tree-ish> should ignore sparsity patterns' '
|
||||||
|
commit=$(git rev-parse HEAD) &&
|
||||||
|
tree=$(git rev-parse HEAD^{tree}) &&
|
||||||
|
cat >expect_tree <<-EOF &&
|
||||||
|
$tree:a:text
|
||||||
|
$tree:b:text
|
||||||
|
$tree:dir/c:text
|
||||||
|
EOF
|
||||||
|
cat >expect_tag-to-tree <<-EOF &&
|
||||||
|
tag-to-tree:a:text
|
||||||
|
tag-to-tree:b:text
|
||||||
|
tag-to-tree:dir/c:text
|
||||||
|
EOF
|
||||||
|
git grep "text" $tree >actual_tree &&
|
||||||
|
test_cmp expect_tree actual_tree &&
|
||||||
|
git grep "text" tag-to-tree >actual_tag-to-tree &&
|
||||||
|
test_cmp expect_tag-to-tree actual_tag-to-tree
|
||||||
|
'
|
||||||
|
|
||||||
|
# Note that sub2/ is present in the worktree but it is excluded by the sparsity
|
||||||
|
# patterns, so grep should only recurse into it with --no-restrict-to-sparse-paths.
|
||||||
|
test_expect_success 'grep --recurse-submodules should honor sparse checkout in submodule' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
sub/B/b:text
|
||||||
|
EOF
|
||||||
|
git grep --recurse-submodules "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
test_expect_success 'grep --recurse-submodules should search in excluded submodules w/ --no-restrict-to-sparse-paths' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
sub/B/b:text
|
||||||
|
sub2/a:text
|
||||||
|
EOF
|
||||||
|
git --no-restrict-to-sparse-paths grep --recurse-submodules "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep --recurse-submodules --cached should honor sparse checkout in submodule' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
sub/B/b:text
|
||||||
|
EOF
|
||||||
|
git grep --recurse-submodules --cached "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep --recurse-submodules <commit-ish> should honor sparse checkout in submodule' '
|
||||||
|
commit=$(git rev-parse HEAD) &&
|
||||||
|
cat >expect_commit <<-EOF &&
|
||||||
|
$commit:a:text
|
||||||
|
$commit:sub/B/b:text
|
||||||
|
EOF
|
||||||
|
cat >expect_tag-to-commit <<-EOF &&
|
||||||
|
tag-to-commit:a:text
|
||||||
|
tag-to-commit:sub/B/b:text
|
||||||
|
EOF
|
||||||
|
git grep --recurse-submodules "text" $commit >actual_commit &&
|
||||||
|
test_cmp expect_commit actual_commit &&
|
||||||
|
git grep --recurse-submodules "text" tag-to-commit >actual_tag-to-commit &&
|
||||||
|
test_cmp expect_tag-to-commit actual_tag-to-commit
|
||||||
|
'
|
||||||
|
|
||||||
|
for cmd in 'git --no-restrict-to-sparse-paths grep' \
|
||||||
|
'git -c sparse.restrictCmds=false grep' \
|
||||||
|
'git -c sparse.restrictCmds=true --no-restrict-to-sparse-paths grep'
|
||||||
|
do
|
||||||
|
|
||||||
|
test_expect_success "$cmd --cached should ignore sparsity patterns" '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
b:text
|
||||||
|
dir/c:text
|
||||||
|
EOF
|
||||||
|
$cmd --cached "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "$cmd <commit-ish> should ignore sparsity patterns" '
|
||||||
|
commit=$(git rev-parse HEAD) &&
|
||||||
|
cat >expect_commit <<-EOF &&
|
||||||
|
$commit:a:text
|
||||||
|
$commit:b:text
|
||||||
|
$commit:dir/c:text
|
||||||
|
EOF
|
||||||
|
cat >expect_tag-to-commit <<-EOF &&
|
||||||
|
tag-to-commit:a:text
|
||||||
|
tag-to-commit:b:text
|
||||||
|
tag-to-commit:dir/c:text
|
||||||
|
EOF
|
||||||
|
$cmd "text" $commit >actual_commit &&
|
||||||
|
test_cmp expect_commit actual_commit &&
|
||||||
|
$cmd "text" tag-to-commit >actual_tag-to-commit &&
|
||||||
|
test_cmp expect_tag-to-commit actual_tag-to-commit
|
||||||
|
'
|
||||||
|
done
|
||||||
|
|
||||||
|
test_expect_success 'grep --recurse-submodules --cached w/ --no-restrict-to-sparse-paths' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
b:text
|
||||||
|
dir/c:text
|
||||||
|
sub/A/a:text
|
||||||
|
sub/B/b:text
|
||||||
|
sub2/a:text
|
||||||
|
EOF
|
||||||
|
git --no-restrict-to-sparse-paths grep --recurse-submodules --cached \
|
||||||
|
"text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'grep --recurse-submodules <commit-ish> w/ --no-restrict-to-sparse-paths' '
|
||||||
|
commit=$(git rev-parse HEAD) &&
|
||||||
|
cat >expect_commit <<-EOF &&
|
||||||
|
$commit:a:text
|
||||||
|
$commit:b:text
|
||||||
|
$commit:dir/c:text
|
||||||
|
$commit:sub/A/a:text
|
||||||
|
$commit:sub/B/b:text
|
||||||
|
$commit:sub2/a:text
|
||||||
|
EOF
|
||||||
|
cat >expect_tag-to-commit <<-EOF &&
|
||||||
|
tag-to-commit:a:text
|
||||||
|
tag-to-commit:b:text
|
||||||
|
tag-to-commit:dir/c:text
|
||||||
|
tag-to-commit:sub/A/a:text
|
||||||
|
tag-to-commit:sub/B/b:text
|
||||||
|
tag-to-commit:sub2/a:text
|
||||||
|
EOF
|
||||||
|
git --no-restrict-to-sparse-paths grep --recurse-submodules "text" \
|
||||||
|
$commit >actual_commit &&
|
||||||
|
test_cmp expect_commit actual_commit &&
|
||||||
|
git --no-restrict-to-sparse-paths grep --recurse-submodules "text" \
|
||||||
|
tag-to-commit >actual_tag-to-commit &&
|
||||||
|
test_cmp expect_tag-to-commit actual_tag-to-commit
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'should respect the sparse.restrictCmds values from submodules' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
sub/A/a:text
|
||||||
|
sub/B/b:text
|
||||||
|
EOF
|
||||||
|
test_config -C sub sparse.restrictCmds false &&
|
||||||
|
git grep --cached --recurse-submodules "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'should propagate --[no]-restrict-to-sparse-paths to submodules' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
a:text
|
||||||
|
b:text
|
||||||
|
dir/c:text
|
||||||
|
sub/A/a:text
|
||||||
|
sub/B/b:text
|
||||||
|
sub2/a:text
|
||||||
|
EOF
|
||||||
|
test_config -C sub sparse.restrictCmds true &&
|
||||||
|
git --no-restrict-to-sparse-paths grep --cached --recurse-submodules "text" >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
for opt in '--untracked' '--no-index'
|
||||||
|
do
|
||||||
|
test_expect_success "--[no]-restrict-to-sparse-paths and $opt are incompatible" "
|
||||||
|
test_must_fail git --restrict-to-sparse-paths grep $opt . 2>actual &&
|
||||||
|
test_i18ngrep 'restrict-to-sparse-paths is incompatible with' actual
|
||||||
|
"
|
||||||
|
done
|
||||||
|
|
||||||
|
test_done
|
@ -1928,6 +1928,8 @@ test_expect_success 'double dash "git" itself' '
|
|||||||
--namespace=
|
--namespace=
|
||||||
--no-replace-objects Z
|
--no-replace-objects Z
|
||||||
--help Z
|
--help Z
|
||||||
|
--restrict-to-sparse-paths Z
|
||||||
|
--no-restrict-to-sparse-paths Z
|
||||||
EOF
|
EOF
|
||||||
'
|
'
|
||||||
|
|
||||||
@ -1970,7 +1972,7 @@ test_expect_success 'general options' '
|
|||||||
test_completion "git --nam" "--namespace=" &&
|
test_completion "git --nam" "--namespace=" &&
|
||||||
test_completion "git --bar" "--bare " &&
|
test_completion "git --bar" "--bare " &&
|
||||||
test_completion "git --inf" "--info-path " &&
|
test_completion "git --inf" "--info-path " &&
|
||||||
test_completion "git --no-r" "--no-replace-objects "
|
test_completion "git --no-rep" "--no-replace-objects "
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'general options plus command' '
|
test_expect_success 'general options plus command' '
|
||||||
|
Reference in New Issue
Block a user