Skip to content

Commit 1497050

Browse files
pks-tgitster
authored andcommitted
builtin/config: introduce "list" subcommand
While git-config(1) has several modes, those modes are not exposed with subcommands but instead by specifying action flags like `--unset` or `--list`. This user interface is not really in line with how our more modern commands work, where it is a lot more customary to say e.g. `git remote list`. Furthermore, to add to the confusion, git-config(1) also allows the user to request modes implicitly by just specifying the correct number of arguments. Thus, `git config foo.bar` will retrieve the value of "foo.bar" while `git config foo.bar baz` will set it to "baz". Overall, this makes for a confusing interface that could really use a makeover. It hurts discoverability of what you can do with git-config(1) and is comparatively easy to get wrong. Converting the command to have subcommands instead would go a long way to help address these issues. One concern in this context is backwards compatibility. Luckily, we can introduce subcommands without breaking backwards compatibility at all. This is because all the implicit modes of git-config(1) require that the first argument is a properly formatted config key. And as config keys _must_ have a dot in their name, any value without a dot would have been discarded by git-config(1) previous to this change. Thus, given that none of the subcommands do have a dot, they are unambiguous. Introduce the first such new subcommand, which is "git config list". To retain backwards compatibility we only conditionally use subcommands and will fall back to the old syntax in case no subcommand was detected. This should help to transition to the new-style syntax until we eventually deprecate and remove the old-style syntax. Note that the way we handle this we're duplicating some functionality across old and new syntax. While this isn't pretty, it helps us to ensure that there really is no change in behaviour for the old syntax. Amend tests such that we run them both with old and new style syntax. As tests are now run twice, state from the first run may be still be around in the second run and thus cause tests to fail. Add cleanup logic as required to fix such tests. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent fee3796 commit 1497050

File tree

3 files changed

+162
-64
lines changed

3 files changed

+162
-64
lines changed

Documentation/git-config.txt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ git-config - Get and set repository or global options
99
SYNOPSIS
1010
--------
1111
[verse]
12+
'git config list' [<file-option>] [<display-option>] [--includes]
1213
'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
1314
'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
1415
'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
@@ -20,7 +21,6 @@ SYNOPSIS
2021
'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
2122
'git config' [<file-option>] --rename-section <old-name> <new-name>
2223
'git config' [<file-option>] --remove-section <name>
23-
'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
2424
'git config' [<file-option>] --get-color <name> [<default>]
2525
'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
2626
'git config' [<file-option>] -e | --edit
@@ -74,6 +74,12 @@ On success, the command returns the exit code 0.
7474
A list of all available configuration variables can be obtained using the
7575
`git help --config` command.
7676

77+
COMMANDS
78+
--------
79+
80+
list::
81+
List all variables set in config file, along with their values.
82+
7783
[[OPTIONS]]
7884
OPTIONS
7985
-------
@@ -190,10 +196,6 @@ See also <<FILES>>.
190196
--unset-all::
191197
Remove all lines matching the key from config file.
192198

193-
-l::
194-
--list::
195-
List all variables set in config file, along with their values.
196-
197199
--fixed-value::
198200
When used with the `value-pattern` argument, treat `value-pattern` as
199201
an exact string instead of a regular expression. This will restrict
@@ -248,7 +250,7 @@ Valid `<type>`'s include:
248250
contain line breaks.
249251

250252
--name-only::
251-
Output only the names of config variables for `--list` or
253+
Output only the names of config variables for `list` or
252254
`--get-regexp`.
253255

254256
--show-origin::
@@ -300,10 +302,20 @@ Valid `<type>`'s include:
300302
When using `--get`, and the requested variable is not found, behave as if
301303
<value> were the value assigned to that variable.
302304

305+
DEPRECATED MODES
306+
----------------
307+
308+
The following modes have been deprecated in favor of subcommands. It is
309+
recommended to migrate to the new syntax.
310+
311+
-l::
312+
--list::
313+
Replaced by `git config list`.
314+
303315
CONFIGURATION
304316
-------------
305317
`pager.config` is only respected when listing configuration, i.e., when
306-
using `--list` or any of the `--get-*` which may return multiple results.
318+
using `list` or any of the `--get-*` which may return multiple results.
307319
The default is to use a pager.
308320

309321
[[FILES]]

builtin/config.c

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@
1616
#include "worktree.h"
1717

1818
static const char *const builtin_config_usage[] = {
19+
N_("git config list [<file-option>] [<display-option>] [--includes]"),
1920
N_("git config [<options>]"),
2021
NULL
2122
};
2223

24+
static const char *const builtin_config_list_usage[] = {
25+
N_("git config list [<file-option>] [<display-option>] [--includes]"),
26+
NULL
27+
};
28+
2329
static char *key;
2430
static regex_t *key_regexp;
2531
static const char *value_pattern;
@@ -33,6 +39,7 @@ static char delim = '=';
3339
static char key_delim = ' ';
3440
static char term = '\n';
3541

42+
static parse_opt_subcommand_fn *subcommand;
3643
static int use_global_config, use_system_config, use_local_config;
3744
static int use_worktree_config;
3845
static struct git_config_source given_config_source;
@@ -706,14 +713,24 @@ static void handle_nul(void) {
706713
}
707714
}
708715

716+
#define CONFIG_LOCATION_OPTIONS \
717+
OPT_GROUP(N_("Config file location")), \
718+
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), \
719+
OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), \
720+
OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), \
721+
OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")), \
722+
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), \
723+
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object"))
724+
725+
#define CONFIG_DISPLAY_OPTIONS \
726+
OPT_GROUP(N_("Display options")), \
727+
OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")), \
728+
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), \
729+
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), \
730+
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)"))
731+
709732
static struct option builtin_config_options[] = {
710-
OPT_GROUP(N_("Config file location")),
711-
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
712-
OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
713-
OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
714-
OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
715-
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
716-
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
733+
CONFIG_LOCATION_OPTIONS,
717734
OPT_GROUP(N_("Action")),
718735
OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
719736
OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
@@ -737,15 +754,12 @@ static struct option builtin_config_options[] = {
737754
OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
738755
OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
739756
OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
757+
CONFIG_DISPLAY_OPTIONS,
740758
OPT_GROUP(N_("Other")),
741-
OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
742-
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
743-
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
744-
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
745-
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
746759
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
747760
OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
748761
OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
762+
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
749763
OPT_END(),
750764
};
751765

@@ -754,6 +768,42 @@ static NORETURN void usage_builtin_config(void)
754768
usage_with_options(builtin_config_usage, builtin_config_options);
755769
}
756770

771+
static int cmd_config_list(int argc, const char **argv, const char *prefix)
772+
{
773+
struct option opts[] = {
774+
CONFIG_LOCATION_OPTIONS,
775+
CONFIG_DISPLAY_OPTIONS,
776+
OPT_GROUP(N_("Other")),
777+
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
778+
OPT_END(),
779+
};
780+
781+
argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);
782+
check_argc(argc, 0, 0);
783+
784+
handle_config_location(prefix);
785+
handle_nul();
786+
787+
setup_auto_pager("config", 1);
788+
789+
if (config_with_options(show_all_config, NULL,
790+
&given_config_source, the_repository,
791+
&config_options) < 0) {
792+
if (given_config_source.file)
793+
die_errno(_("unable to read config file '%s'"),
794+
given_config_source.file);
795+
else
796+
die(_("error processing config file(s)"));
797+
}
798+
799+
return 0;
800+
}
801+
802+
static struct option builtin_subcommand_options[] = {
803+
OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
804+
OPT_END(),
805+
};
806+
757807
int cmd_config(int argc, const char **argv, const char *prefix)
758808
{
759809
char *value = NULL, *comment = NULL;
@@ -763,6 +813,22 @@ int cmd_config(int argc, const char **argv, const char *prefix)
763813

764814
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
765815

816+
/*
817+
* This is somewhat hacky: we first parse the command line while
818+
* keeping all args intact in order to determine whether a subcommand
819+
* has been specified. If so, we re-parse it a second time, but this
820+
* time we drop KEEP_ARGV0. This is so that we don't munge the command
821+
* line in case no subcommand was given, which would otherwise confuse
822+
* us when parsing the legacy-style modes that don't use subcommands.
823+
*/
824+
argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,
825+
PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_NO_INTERNAL_HELP|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);
826+
if (subcommand) {
827+
argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,
828+
PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_NO_INTERNAL_HELP|PARSE_OPT_KEEP_UNKNOWN_OPT);
829+
return subcommand(argc, argv, prefix);
830+
}
831+
766832
argc = parse_options(argc, argv, prefix, builtin_config_options,
767833
builtin_config_usage,
768834
PARSE_OPT_STOP_AT_NON_OPTION);

0 commit comments

Comments
 (0)