This series makes git-grep restrict its output to the sparsity patterns when requested by the user. A new global option is added to control this behavior in grep and hopefully more commands in the future. There are also a couple fixes in t/helper/test-config and in a test that uses it. Changes since v4: - Rebased on top of master to use repo_config_get_string_tmp(), added in jk/leakfix, in t/helper/test-config (patch 6). - Added patch 2, to make sure a test that relies on test-config checks its output in addition to the exit code, to avoid false positives. - Split patch "t/helper/test-config: return exit codes consistently" into three separated ones, as these are in fact three non-related changes: "t/helper/test-config: unify exit labels" "t/helper/test-config: check argc before accessing argv" "t/helper/test-config: be consistent with exit codes" - Removed TC_USAGE_ERROR in favor of calling die(). Also removed the test_config_exit_code enum. - On "config: correctly read worktree configs in submodules": * Improved commit message to focus on the problem instead of the implementation and remove section about the grep example. * Made use of the parse_options API * Allocated subrepo struct in the stack instead of malloc()'ing. Matheus Tavares (8): doc: grep: unify info on configuration variables t1308-config-set: avoid false positives when using test-config t/helper/test-config: be consistent with exit codes t/helper/test-config: check argc before accessing argv t/helper/test-config: unify exit labels config: correctly read worktree configs in submodules grep: honor sparse checkout patterns config: add setting to ignore sparsity patterns in some cmds Documentation/config.txt | 2 + Documentation/config/grep.txt | 18 +- Documentation/config/sparse.txt | 20 ++ Documentation/git-grep.txt | 36 +-- Documentation/git.txt | 4 + Makefile | 1 + builtin/grep.c | 134 ++++++++++- config.c | 21 +- contrib/completion/git-completion.bash | 2 + git.c | 5 + sparse-checkout.c | 18 ++ sparse-checkout.h | 11 + t/helper/test-config.c | 126 ++++++---- t/t1308-config-set.sh | 8 +- t/t2404-worktree-config.sh | 16 ++ t/t7011-skip-worktree-reading.sh | 9 - t/t7817-grep-sparse-checkout.sh | 321 +++++++++++++++++++++++++ t/t9902-completion.sh | 4 +- 18 files changed, 652 insertions(+), 104 deletions(-) create mode 100644 Documentation/config/sparse.txt create mode 100644 sparse-checkout.c create mode 100644 sparse-checkout.h create mode 100755 t/t7817-grep-sparse-checkout.sh Range-diff against v4: 1: fc47a96bfa = 1: 70c9a4e741 doc: grep: unify info on configuration variables -: ---------- > 2: f53782f14c t1308-config-set: avoid false positives when using test-config -: ---------- > 3: 85e1588d6c t/helper/test-config: be consistent with exit codes -: ---------- > 4: 0750191342 t/helper/test-config: check argc before accessing argv 2: 874aab36dd ! 5: 56535b0e36 t/helper/test-config: return exit codes consistently @@ Metadata Author: Matheus Tavares <matheus.bernardino@xxxxxx> ## Commit message ## - t/helper/test-config: return exit codes consistently + t/helper/test-config: unify exit labels - The test-config helper may exit with a variety of at least four - different codes, to reflect the status of the requested operations. - These codes are sometimes checked in the tests, but not all of the codes - are returned consistently by the helper: 1 will usually refer to a - "value not found", but usage errors can also return 1 or 128. Moreover, - 128 is also expected on errors within the configset functions. These - inconsistent uses of the exit codes can lead to false positives in the - tests. Although all tests which expect errors and check the helper's - exit code currently also check the output, it's still better to - standardize the exit codes and avoid future problems in new tests. - While we are here, let's also check that we have the expected argc for - configset_get_value and configset_get_value_multi, before trying to use - argv. - - Note: this change is implemented with the unification of the exit - labels. This might seem unnecessary, for now, but it will benefit the - next patch, which will increase the cleanup section. + test-config's main function has three different exit labels, all of + which have to perform the same cleanup code before returning. Unify the + labels in preparation for a future patch which will increase the cleanup + section. Signed-off-by: Matheus Tavares <matheus.bernardino@xxxxxx> ## t/helper/test-config.c ## -@@ - * iterate -> iterate over all values using git_config(), and print some - * 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: - * - * To print the value with highest priority for key "foo.bAr Baz.rock": @@ t/helper/test-config.c: static int early_config_cb(const char *var, const char *value, void *vdata) return 0; } -+enum test_config_exit_code { -+ TC_SUCCESS = 0, -+ TC_VALUE_NOT_FOUND = 1, -+ TC_CONFIG_FILE_ERROR = 2, -+ TC_USAGE_ERROR = 129, -+}; ++#define TC_VALUE_NOT_FOUND 1 ++#define TC_CONFIG_FILE_ERROR 2 + int cmd__config(int argc, const char **argv) { - int i, val; +- int i, val; ++ int i, val, ret = 0; const char *v; const struct string_list *strptr; struct config_set cs; -+ enum test_config_exit_code ret = TC_SUCCESS; if (argc == 3 && !strcmp(argv[1], "read_early_config")) { read_early_config(early_config_cb, (void *)argv[2]); - return 0; -+ return TC_SUCCESS; ++ return ret; } setup_git_directory(); - - git_configset_init(&cs); - -- if (argc < 2) { -- fprintf(stderr, "Please, provide a command name on the command-line\n"); -- goto exit1; -- } else if (argc == 3 && !strcmp(argv[1], "get_value")) { -+ if (argc < 2) -+ goto print_usage_error; -+ -+ if (argc == 3 && !strcmp(argv[1], "get_value")) { - if (!git_config_get_value(argv[2], &v)) { - if (!v) +@@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) printf("(NULL)\n"); else printf("%s\n", v); @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) - goto exit1; + ret = TC_VALUE_NOT_FOUND; } -- } else if (!strcmp(argv[1], "configset_get_value")) { -+ } else if (argc >= 3 && !strcmp(argv[1], "configset_get_value")) { + } else if (argc >= 3 && !strcmp(argv[1], "configset_get_value")) { for (i = 3; i < argc; i++) { int err; if ((err = git_configset_add_file(&cs, argv[i]))) { @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) - goto exit1; + ret = TC_VALUE_NOT_FOUND; } -- } else if (!strcmp(argv[1], "configset_get_value_multi")) { -+ } else if (argc >= 3 && !strcmp(argv[1], "configset_get_value_multi")) { + } else if (argc >= 3 && !strcmp(argv[1], "configset_get_value_multi")) { for (i = 3; i < argc; i++) { int err; if ((err = git_configset_add_file(&cs, argv[i]))) { @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) git_config(iterate_cb, NULL); - goto exit0; + } else { -+print_usage_error: -+ fprintf(stderr, "Invalid syntax. Usage: test-tool config <cmd> [args]\n"); -+ ret = TC_USAGE_ERROR; ++ die("%s: Please check the syntax and the function name", argv[0]); } - die("%s: Please check the syntax and the function name", argv[0]); 3: c5093099f3 < -: ---------- t/helper/test-config: facilitate addition of new cli options 4: b510de0de0 ! 6: 3e02e1bd24 config: correctly read worktree configs in submodules @@ Metadata ## Commit message ## config: correctly read worktree configs in submodules - One of the steps in do_git_config_sequence() is to load the - worktree-specific config file. Although the function receives a git_dir - string, it relies on git_pathdup(), which uses the_repository->git_dir, - to make the path to the file. Furthermore, it also checks that - extensions.worktreeConfig is set through the - repository_format_worktree_config variable, which refers to - the_repository only. Thus, when a submodule has worktree-specific - settings, a command executed in the superproject that recurses into the - submodule won't find the said settings. + The config machinery is not able to read worktree configs from a + submodule in a process where the_repository represents the superproject. + Furthermore, when extensions.worktreeConfig is set on the superproject, + querying for a worktree config in a submodule will, instead, return + the value set at the superproject. - This will be especially important in the next patch: git-grep will learn - to honor sparse checkouts and, when running with --recurse-submodules, - the submodule's sparse checkout settings must be loaded. As these - settings are stored in the config.worktree file, they would be ignored - without this patch. So let's fix this by reading the right - config.worktree file and extensions.worktreeConfig setting, based on the - git_dir and commondir paths given to do_git_config_sequence(). Also - add a test to avoid any regressions. + The problem resides in do_git_config_sequence(). Although the function + receives a git_dir string, it uses the_repository->git_dir when making + the path to the worktree config file. And when checking if + extensions.worktreeConfig is set, it uses the global + repository_format_worktree_config variable, which refers to + the_repository only. So let's fix this by using the git_dir given to the + function and reading the extension value from the right place. Also add + a test to avoid any regressions. Signed-off-by: Matheus Tavares <matheus.bernardino@xxxxxx> @@ t/helper/test-config.c #include "config.h" #include "string-list.h" +#include "submodule-config.h" ++#include "parse-options.h" /* * This program exposes the C API of the configuration mechanism @@ t/helper/test-config.c * * get_value -> prints the value with highest priority for the entered key * -@@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) +@@ t/helper/test-config.c: static int early_config_cb(const char *var, const char *value, void *vdata) + #define TC_VALUE_NOT_FOUND 1 + #define TC_CONFIG_FILE_ERROR 2 + ++static const char *test_config_usage[] = { ++ "test-tool config [--submodule=<path>] <cmd> [<args>]", ++ NULL ++}; ++ + int cmd__config(int argc, const char **argv) + { + int i, val, ret = 0; + const char *v; const struct string_list *strptr; - struct config_set cs = { .hash_initialized = 0 }; - enum test_config_exit_code ret = TC_SUCCESS; -+ struct repository *repo = the_repository; + struct config_set cs; ++ struct repository subrepo, *repo = the_repository; + const char *subrepo_path = NULL; - - argc--; /* skip over "config" */ - argv++; -@@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) - 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; ++ struct option options[] = { ++ OPT_STRING(0, "submodule", &subrepo_path, "path", ++ "run <cmd> on the submodule at <path>"), ++ OPT_END() ++ }; ++ ++ argc = parse_options(argc, argv, NULL, options, test_config_usage, ++ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_STOP_AT_NON_OPTION); ++ if (argc < 2) ++ die("Please, provide a command name on the command-line"); + + if (argc == 3 && !strcmp(argv[1], "read_early_config")) { ++ if (subrepo_path) ++ die("cannot use --submodule with read_early_config"); + read_early_config(early_config_cb, (void *)argv[2]); + return ret; } @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) - setup_git_directory(); + git_configset_init(&cs); +- if (argc < 2) +- die("Please, provide a command name on the command-line"); + if (subrepo_path) { + const struct submodule *sub; -+ struct repository *subrepo = xcalloc(1, sizeof(*repo)); + + sub = submodule_from_path(the_repository, &null_oid, subrepo_path); -+ if (!sub || repo_submodule_init(subrepo, the_repository, sub)) { -+ fprintf(stderr, "Invalid argument to --submodule: '%s'\n", -+ subrepo_path); -+ free(subrepo); -+ ret = TC_USAGE_ERROR; -+ goto out; -+ } -+ repo = subrepo; -+ } ++ if (!sub || repo_submodule_init(&subrepo, the_repository, sub)) ++ die("invalid argument to --submodule: '%s'", subrepo_path); + - if (argc == 2 && !strcmp(argv[0], "get_value")) { -- if (!git_config_get_value(argv[1], &v)) { -+ if (!repo_config_get_value(repo, argv[1], &v)) { ++ repo = &subrepo; ++ } + + if (argc == 3 && !strcmp(argv[1], "get_value")) { +- if (!git_config_get_value(argv[2], &v)) { ++ if (!repo_config_get_value(repo, argv[2], &v)) { if (!v) printf("(NULL)\n"); else @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) ret = TC_VALUE_NOT_FOUND; } - } else if (argc == 2 && !strcmp(argv[0], "get_value_multi")) { -- strptr = git_config_get_value_multi(argv[1]); -+ strptr = repo_config_get_value_multi(repo, argv[1]); + } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) { +- strptr = git_config_get_value_multi(argv[2]); ++ strptr = repo_config_get_value_multi(repo, argv[2]); if (strptr) { for (i = 0; i < strptr->nr; i++) { v = strptr->items[i].string; @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) ret = TC_VALUE_NOT_FOUND; } - } else if (argc == 2 && !strcmp(argv[0], "get_int")) { -- if (!git_config_get_int(argv[1], &val)) { -+ if (!repo_config_get_int(repo, argv[1], &val)) { + } else if (argc == 3 && !strcmp(argv[1], "get_int")) { +- if (!git_config_get_int(argv[2], &val)) { ++ if (!repo_config_get_int(repo, argv[2], &val)) { printf("%d\n", val); } else { - printf("Value not found for \"%s\"\n", argv[1]); + printf("Value not found for \"%s\"\n", argv[2]); ret = TC_VALUE_NOT_FOUND; } - } else if (argc == 2 && !strcmp(argv[0], "get_bool")) { -- if (!git_config_get_bool(argv[1], &val)) { -+ if (!repo_config_get_bool(repo, argv[1], &val)) { + } else if (argc == 3 && !strcmp(argv[1], "get_bool")) { +- if (!git_config_get_bool(argv[2], &val)) { ++ if (!repo_config_get_bool(repo, argv[2], &val)) { printf("%d\n", val); } else { + - printf("Value not found for \"%s\"\n", argv[1]); + printf("Value not found for \"%s\"\n", argv[2]); ret = TC_VALUE_NOT_FOUND; } - } else if (argc == 2 && !strcmp(argv[0], "get_string")) { -- if (!git_config_get_string_tmp(argv[1], &v)) { -+ if (!repo_config_get_string_tmp(repo, argv[1], &v)) { + } else if (argc == 3 && !strcmp(argv[1], "get_string")) { +- if (!git_config_get_string_tmp(argv[2], &v)) { ++ if (!repo_config_get_string_tmp(repo, argv[2], &v)) { printf("%s\n", v); } else { - printf("Value not found for \"%s\"\n", argv[1]); + printf("Value not found for \"%s\"\n", argv[2]); 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++) { + } else if (argc >= 3 && !strcmp(argv[1], "configset_get_value")) { ++ if (subrepo_path) ++ die("cannot use --submodule with configset_get_value"); ++ + for (i = 3; i < argc; i++) { int err; if ((err = git_configset_add_file(&cs, argv[i]))) { @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) 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++) { + } else if (argc >= 3 && !strcmp(argv[1], "configset_get_value_multi")) { ++ if (subrepo_path) ++ die("cannot use --submodule with configset_get_value_multi"); ++ + for (i = 3; i < argc; i++) { int err; if ((err = git_configset_add_file(&cs, argv[i]))) { @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv) ret = TC_VALUE_NOT_FOUND; } - } else if (!strcmp(argv[0], "iterate")) { + } else if (!strcmp(argv[1], "iterate")) { - git_config(iterate_cb, NULL); + repo_config(repo, iterate_cb, NULL); } else { - print_usage_error: -- fprintf(stderr, "Invalid syntax. Usage: test-tool config <cmd> [args]\n"); -+ fprintf(stderr, "Invalid syntax. Usage: test-tool config" -+ " [--submodule=<path>] <cmd> [args]\n"); - ret = TC_USAGE_ERROR; + die("%s: Please check the syntax and the function name", argv[0]); } out: git_configset_clear(&cs); -+ if (repo != the_repository) { ++ if (repo != the_repository) + repo_clear(repo); -+ free(repo); -+ } return ret; } 5: 6d9720abf5 = 7: 902556a7b6 grep: honor sparse checkout patterns 6: affb931d35 = 8: 70e7d7b90c config: add setting to ignore sparsity patterns in some cmds -- 2.28.0