Merge branch 'ps/config-subcommands'

The operation mode options (like "--get") the "git config" command
uses have been deprecated and replaced with subcommands (like "git
config get").

* ps/config-subcommands:
  builtin/config: display subcommand help
  builtin/config: introduce "edit" subcommand
  builtin/config: introduce "remove-section" subcommand
  builtin/config: introduce "rename-section" subcommand
  builtin/config: introduce "unset" subcommand
  builtin/config: introduce "set" subcommand
  builtin/config: introduce "get" subcommand
  builtin/config: introduce "list" subcommand
  builtin/config: pull out function to handle `--null`
  builtin/config: pull out function to handle config location
  builtin/config: use `OPT_CMDMODE()` to specify modes
  builtin/config: move "fixed-value" option to correct group
  builtin/config: move option array around
  config: clarify memory ownership when preparing comment strings
This commit is contained in:
Junio C Hamano 2024-05-15 09:52:52 -07:00
commit fe3ccc7aab
6 changed files with 812 additions and 370 deletions

View file

@ -9,21 +9,14 @@ git-config - Get and set repository or global options
SYNOPSIS
--------
[verse]
'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch <name> <URL>
'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
'git config' [<file-option>] --rename-section <old-name> <new-name>
'git config' [<file-option>] --remove-section <name>
'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
'git config' [<file-option>] --get-color <name> [<default>]
'git config list' [<file-option>] [<display-option>] [--includes]
'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>
'git config set' [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>
'git config unset' [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>
'git config rename-section' [<file-option>] <old-name> <new-name>
'git config remove-section' [<file-option>] <name>
'git config edit' [<file-option>]
'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
'git config' [<file-option>] -e | --edit
DESCRIPTION
-----------
@ -31,7 +24,7 @@ You can query/set/replace/unset options with this command. The name is
actually the section and the key separated by a dot, and the value will be
escaped.
Multiple lines can be added to an option by using the `--add` option.
Multiple lines can be added to an option by using the `--append` option.
If you want to update or unset an option which can occur on multiple
lines, a `value-pattern` (which is an extended regular expression,
unless the `--fixed-value` option is given) needs to be given. Only the
@ -74,6 +67,42 @@ On success, the command returns the exit code 0.
A list of all available configuration variables can be obtained using the
`git help --config` command.
COMMANDS
--------
list::
List all variables set in config file, along with their values.
get::
Emits the value of the specified key. If key is present multiple times
in the configuration, emits the last value. If `--all` is specified,
emits all values associated with key. Returns error code 1 if key is
not present.
set::
Set value for one or more config options. By default, this command
refuses to write multi-valued config options. Passing `--all` will
replace all multi-valued config options with the new value, whereas
`--value=` will replace all config options whose values match the given
pattern.
unset::
Unset value for one or more config options. By default, this command
refuses to unset multi-valued keys. Passing `--all` will unset all
multi-valued config options, whereas `--value` will unset all config
options whose values match the given pattern.
rename-section::
Rename the given section to a new name.
remove-section::
Remove the given section from the configuration file.
edit::
Opens an editor to modify the specified config file; either
`--system`, `--global`, `--local` (default), `--worktree`, or
`--file <config-file>`.
[[OPTIONS]]
OPTIONS
-------
@ -82,10 +111,9 @@ OPTIONS
Default behavior is to replace at most one line. This replaces
all lines matching the key (and optionally the `value-pattern`).
--add::
--append::
Adds a new line to the option without altering any existing
values. This is the same as providing '^$' as the `value-pattern`
in `--replace-all`.
values. This is the same as providing '--value=^$' in `set`.
--comment <message>::
Append a comment at the end of new or modified lines.
@ -99,22 +127,16 @@ OPTIONS
not contain linefeed characters (no multi-line comments are
permitted).
--get::
Get the value for a given key (optionally filtered by a regex
matching the value). Returns error code 1 if the key was not
found and the last value if multiple key values were found.
--all::
With `get`, return all values for a multi-valued key.
--get-all::
Like get, but returns all values for a multi-valued key.
---regexp::
With `get`, interpret the name as a regular expression. Regular
expression matching is currently case-sensitive and done against a
canonicalized version of the key in which section and variable names
are lowercased, but subsection names are not.
--get-regexp::
Like --get-all, but interprets the name as a regular expression and
writes out the key names. Regular expression matching is currently
case-sensitive and done against a canonicalized version of the key
in which section and variable names are lowercased, but subsection
names are not.
--get-urlmatch <name> <URL>::
--url=<URL>::
When given a two-part <name> as <section>.<key>, the value for
<section>.<URL>.<key> whose <URL> part matches the best to the
given URL is returned (if no such key exists, the value for
@ -178,22 +200,6 @@ See also <<FILES>>.
section in linkgit:gitrevisions[7] for a more complete list of
ways to spell blob names.
--remove-section::
Remove the given section from the configuration file.
--rename-section::
Rename the given section to a new name.
--unset::
Remove the line matching the key from config file.
--unset-all::
Remove all lines matching the key from config file.
-l::
--list::
List all variables set in config file, along with their values.
--fixed-value::
When used with the `value-pattern` argument, treat `value-pattern` as
an exact string instead of a regular expression. This will restrict
@ -248,8 +254,8 @@ Valid `<type>`'s include:
contain line breaks.
--name-only::
Output only the names of config variables for `--list` or
`--get-regexp`.
Output only the names of config variables for `list` or
`get`.
--show-origin::
Augment the output of all queried config options with the
@ -273,23 +279,6 @@ Valid `<type>`'s include:
When the color setting for `name` is undefined, the command uses
`color.ui` as fallback.
--get-color <name> [<default>]::
Find the color configured for `name` (e.g. `color.diff.new`) and
output it as the ANSI color escape sequence to the standard
output. The optional `default` parameter is used instead, if
there is no color configured for `name`.
+
`--type=color [--default=<default>]` is preferred over `--get-color`
(but note that `--get-color` will omit the trailing newline printed by
`--type=color`).
-e::
--edit::
Opens an editor to modify the specified config file; either
`--system`, `--global`, `--local` (default), `--worktree`, or
`--file <config-file>`.
--[no-]includes::
Respect `include.*` directives in config files when looking up
values. Defaults to `off` when a specific file is given (e.g.,
@ -297,14 +286,64 @@ Valid `<type>`'s include:
config files.
--default <value>::
When using `--get`, and the requested variable is not found, behave as if
When using `get`, and the requested variable is not found, behave as if
<value> were the value assigned to that variable.
DEPRECATED MODES
----------------
The following modes have been deprecated in favor of subcommands. It is
recommended to migrate to the new syntax.
'git config <name>'::
Replaced by `git config get <name>`.
'git config <name> <value> [<value-pattern>]'::
Replaced by `git config set [--value=<pattern>] <name> <value>`.
-l::
--list::
Replaced by `git config list`.
--get <name> [<value-pattern>]::
Replaced by `git config get [--value=<pattern>] <name>`.
--get-all <name> [<value-pattern>]::
Replaced by `git config get [--value=<pattern>] --all --show-names <name>`.
--get-regexp <name-regexp>::
Replaced by `git config get --all --show-names --regexp <name-regexp>`.
--get-urlmatch <name> <URL>::
Replaced by `git config get --all --show-names --url=<URL> <name>`.
--get-color <name> [<default>]::
Replaced by `git config get --type=color [--default=<default>] <name>`.
--add <name> <value>::
Replaced by `git config set --append <name> <value>`.
--unset <name> [<value-pattern>]::
Replaced by `git config unset [--value=<pattern>] <name>`.
--unset-all <name> [<value-pattern>]::
Replaced by `git config unset [--value=<pattern>] --all <name>`.
--rename-section <old-name> <new-name>::
Replaced by `git config rename-section <old-name> <new-name>`.
--remove-section <name>::
Replaced by `git config remove-section <name>`.
-e::
--edit::
Replaced by `git config edit`.
CONFIGURATION
-------------
`pager.config` is only respected when listing configuration, i.e., when
using `--list` or any of the `--get-*` which may return multiple results.
The default is to use a pager.
using `list` or `get` which may return multiple results. The default is to use
a pager.
[[FILES]]
FILES
@ -346,8 +385,8 @@ precedence over values read earlier. When multiple values are taken then all
values of a key from all files will be used.
By default, options are only written to the repository specific
configuration file. Note that this also affects options like `--replace-all`
and `--unset`. *'git config' will only ever change one file at a time*.
configuration file. Note that this also affects options like `set`
and `unset`. *'git config' will only ever change one file at a time*.
You can limit which configuration sources are read from or written to by
specifying the path of a file with the `--file` option, or by specifying a
@ -482,7 +521,7 @@ Given a .git/config like this:
you can set the filemode to true with
------------
% git config core.filemode true
% git config set core.filemode true
------------
The hypothetical proxy command entries actually have a postfix to discern
@ -490,7 +529,7 @@ what URL they apply to. Here is how to change the entry for kernel.org
to "ssh".
------------
% git config core.gitproxy '"ssh" for kernel.org' 'for kernel.org$'
% git config set --value='for kernel.org$' core.gitproxy '"ssh" for kernel.org'
------------
This makes sure that only the key/value pair for kernel.org is replaced.
@ -498,7 +537,7 @@ This makes sure that only the key/value pair for kernel.org is replaced.
To delete the entry for renames, do
------------
% git config --unset diff.renames
% git config unset diff.renames
------------
If you want to delete an entry for a multivar (like core.gitproxy above),
@ -507,51 +546,45 @@ you have to provide a regex matching the value of exactly one line.
To query the value for a given key, do
------------
% git config --get core.filemode
------------
or
------------
% git config core.filemode
% git config get core.filemode
------------
or, to query a multivar:
------------
% git config --get core.gitproxy "for kernel.org$"
% git config get --value="for kernel.org$" core.gitproxy
------------
If you want to know all the values for a multivar, do:
------------
% git config --get-all core.gitproxy
% git config get --all --show-names core.gitproxy
------------
If you like to live dangerously, you can replace *all* core.gitproxy by a
new one with
------------
% git config --replace-all core.gitproxy ssh
% git config set --all core.gitproxy ssh
------------
However, if you really only want to replace the line for the default proxy,
i.e. the one without a "for ..." postfix, do something like this:
------------
% git config core.gitproxy ssh '! for '
% git config set --value='! for ' core.gitproxy ssh
------------
To actually match only values with an exclamation mark, you have to
------------
% git config section.key value '[!]'
% git config set --value='[!]' section.key value
------------
To add a new proxy, without altering any of the existing ones, use
------------
% git config --add core.gitproxy '"proxy-command" for example.com'
% git config set --append core.gitproxy '"proxy-command" for example.com'
------------
An example to use customized color from the configuration in your
@ -559,8 +592,8 @@ script:
------------
#!/bin/sh
WS=$(git config --get-color color.diff.whitespace "blue reverse")
RESET=$(git config --get-color "" "reset")
WS=$(git config get --type=color --default="blue reverse" color.diff.whitespace)
RESET=$(git config get --type=color --default="reset" "")
echo "${WS}your whitespace color or blue reverse${RESET}"
------------
@ -568,11 +601,11 @@ For URLs in `https://weak.example.com`, `http.sslVerify` is set to
false, while it is set to `true` for all others:
------------
% git config --type=bool --get-urlmatch http.sslverify https://good.example.com
% git config get --type=bool --url=https://good.example.com http.sslverify
true
% git config --type=bool --get-urlmatch http.sslverify https://weak.example.com
% git config get --type=bool --url=https://weak.example.com http.sslverify
false
% git config --get-urlmatch http https://weak.example.com
% git config get --url=https://weak.example.com http
http.cookieFile /tmp/cookie.txt
http.sslverify false
------------

View file

@ -16,7 +16,49 @@
#include "worktree.h"
static const char *const builtin_config_usage[] = {
N_("git config [<options>]"),
N_("git config list [<file-option>] [<display-option>] [--includes]"),
N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
N_("git config rename-section [<file-option>] <old-name> <new-name>"),
N_("git config remove-section [<file-option>] <name>"),
N_("git config edit [<file-option>]"),
N_("git config [<file-option>] --get-colorbool <name> [<stdout-is-tty>]"),
NULL
};
static const char *const builtin_config_list_usage[] = {
N_("git config list [<file-option>] [<display-option>] [--includes]"),
NULL
};
static const char *const builtin_config_get_usage[] = {
N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
NULL
};
static const char *const builtin_config_set_usage[] = {
N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
NULL
};
static const char *const builtin_config_unset_usage[] = {
N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
NULL
};
static const char *const builtin_config_rename_section_usage[] = {
N_("git config rename-section [<file-option>] <old-name> <new-name>"),
NULL
};
static const char *const builtin_config_remove_section_usage[] = {
N_("git config remove-section [<file-option>] <name>"),
NULL
};
static const char *const builtin_config_edit_usage[] = {
N_("git config edit [<file-option>]"),
NULL
};
@ -33,6 +75,7 @@ static char delim = '=';
static char key_delim = ' ';
static char term = '\n';
static parse_opt_subcommand_fn *subcommand;
static int use_global_config, use_system_config, use_local_config;
static int use_worktree_config;
static struct git_config_source given_config_source;
@ -44,7 +87,7 @@ static struct config_options config_options;
static int show_origin;
static int show_scope;
static int fixed_value;
static const char *comment;
static const char *comment_arg;
#define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1)
@ -135,54 +178,6 @@ static int option_parse_type(const struct option *opt, const char *arg,
return 0;
}
static struct option builtin_config_options[] = {
OPT_GROUP(N_("Config file location")),
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
OPT_GROUP(N_("Action")),
OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
OPT_GROUP(N_("Type")),
OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
OPT_GROUP(N_("Other")),
OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
OPT_END(),
};
static NORETURN void usage_builtin_config(void)
{
usage_with_options(builtin_config_usage, builtin_config_options);
}
static void check_argc(int argc, int min, int max)
{
if (argc >= min && argc <= max)
@ -671,20 +666,8 @@ static char *default_user_config(void)
return strbuf_detach(&buf, NULL);
}
int cmd_config(int argc, const char **argv, const char *prefix)
static void handle_config_location(const char *prefix)
{
int nongit = !startup_info->have_repository;
char *value = NULL;
int flags = 0;
int ret = 0;
struct key_value_info default_kvi = KVI_INIT;
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
argc = parse_options(argc, argv, prefix, builtin_config_options,
builtin_config_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (use_global_config + use_system_config + use_local_config +
use_worktree_config +
!!given_config_source.file + !!given_config_source.blob > 1) {
@ -692,14 +675,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config();
}
if (nongit) {
if (!startup_info->have_repository) {
if (use_local_config)
die(_("--local can only be used inside a git repository"));
if (given_config_source.blob)
die(_("--blob can only be used inside a git repository"));
if (use_worktree_config)
die(_("--worktree can only be used inside a git repository"));
}
if (given_config_source.file &&
@ -753,26 +735,384 @@ int cmd_config(int argc, const char **argv, const char *prefix)
config_options.respect_includes = !given_config_source.file;
else
config_options.respect_includes = respect_includes_opt;
if (!nongit) {
if (startup_info->have_repository) {
config_options.commondir = get_git_common_dir();
config_options.git_dir = get_git_dir();
}
}
static void handle_nul(void) {
if (end_nul) {
term = '\0';
delim = '\n';
key_delim = '\n';
}
}
#define CONFIG_LOCATION_OPTIONS \
OPT_GROUP(N_("Config file location")), \
OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), \
OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), \
OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), \
OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")), \
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), \
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object"))
#define CONFIG_TYPE_OPTIONS \
OPT_GROUP(N_("Type")), \
OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), \
OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), \
OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), \
OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), \
OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), \
OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE)
#define CONFIG_DISPLAY_OPTIONS \
OPT_GROUP(N_("Display options")), \
OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")), \
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), \
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), \
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)"))
static struct option builtin_config_options[] = {
CONFIG_LOCATION_OPTIONS,
OPT_GROUP(N_("Action")),
OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP),
OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL),
OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET),
OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL),
OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR),
OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL),
CONFIG_TYPE_OPTIONS,
CONFIG_DISPLAY_OPTIONS,
OPT_GROUP(N_("Other")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
OPT_END(),
};
static NORETURN void usage_builtin_config(void)
{
usage_with_options(builtin_config_usage, builtin_config_options);
}
static int cmd_config_list(int argc, const char **argv, const char *prefix)
{
struct option opts[] = {
CONFIG_LOCATION_OPTIONS,
CONFIG_DISPLAY_OPTIONS,
OPT_GROUP(N_("Other")),
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
OPT_END(),
};
argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);
check_argc(argc, 0, 0);
handle_config_location(prefix);
handle_nul();
setup_auto_pager("config", 1);
if (config_with_options(show_all_config, NULL,
&given_config_source, the_repository,
&config_options) < 0) {
if (given_config_source.file)
die_errno(_("unable to read config file '%s'"),
given_config_source.file);
else
die(_("error processing config file(s)"));
}
return 0;
}
static int cmd_config_get(int argc, const char **argv, const char *prefix)
{
const char *value_pattern = NULL, *url = NULL;
int flags = 0;
struct option opts[] = {
CONFIG_LOCATION_OPTIONS,
CONFIG_TYPE_OPTIONS,
OPT_GROUP(N_("Filter options")),
OPT_BOOL(0, "all", &do_all, N_("return all values for multi-valued config options")),
OPT_BOOL(0, "regexp", &use_key_regexp, N_("interpret the name as a regular expression")),
OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
OPT_STRING(0, "url", &url, N_("URL"), N_("show config matching the given URL")),
CONFIG_DISPLAY_OPTIONS,
OPT_BOOL(0, "show-names", &show_keys, N_("show config keys in addition to their values")),
OPT_GROUP(N_("Other")),
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("use default value when missing entry")),
OPT_END(),
};
argc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
check_argc(argc, 1, 1);
if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
die(_("--fixed-value only applies with 'value-pattern'"));
if (default_value && (do_all || url))
die(_("--default= cannot be used with --all or --url="));
if (url && (do_all || use_key_regexp || value_pattern))
die(_("--url= cannot be used with --all, --regexp or --value"));
handle_config_location(prefix);
handle_nul();
setup_auto_pager("config", 1);
if (url)
return get_urlmatch(argv[0], url);
return get_value(argv[0], value_pattern, flags);
}
static int cmd_config_set(int argc, const char **argv, const char *prefix)
{
const char *value_pattern = NULL, *comment_arg = NULL;
char *comment = NULL;
int flags = 0, append = 0;
struct option opts[] = {
CONFIG_LOCATION_OPTIONS,
CONFIG_TYPE_OPTIONS,
OPT_GROUP(N_("Filter")),
OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
OPT_GROUP(N_("Other")),
OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
OPT_BOOL(0, "append", &append, N_("add a new line without altering any existing values")),
OPT_END(),
};
struct key_value_info default_kvi = KVI_INIT;
char *value;
int ret;
argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
check_write();
check_argc(argc, 2, 2);
if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
die(_("--fixed-value only applies with --value=<pattern>"));
if (append && value_pattern)
die(_("--append cannot be used with --value=<pattern>"));
if (append)
value_pattern = CONFIG_REGEX_NONE;
comment = git_config_prepare_comment_string(comment_arg);
handle_config_location(prefix);
value = normalize_value(argv[0], argv[1], &default_kvi);
if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {
ret = git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, value_pattern,
comment, flags);
} else {
ret = git_config_set_in_file_gently(given_config_source.file,
argv[0], comment, value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]);
}
free(comment);
free(value);
return ret;
}
static int cmd_config_unset(int argc, const char **argv, const char *prefix)
{
const char *value_pattern = NULL;
int flags = 0;
struct option opts[] = {
CONFIG_LOCATION_OPTIONS,
OPT_GROUP(N_("Filter")),
OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
OPT_END(),
};
argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
check_write();
check_argc(argc, 1, 1);
if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
die(_("--fixed-value only applies with 'value-pattern'"));
handle_config_location(prefix);
if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)
return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], NULL, value_pattern,
NULL, flags);
else
return git_config_set_in_file_gently(given_config_source.file, argv[0],
NULL, NULL);
}
static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)
{
struct option opts[] = {
CONFIG_LOCATION_OPTIONS,
OPT_END(),
};
int ret;
argc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
check_write();
check_argc(argc, 2, 2);
handle_config_location(prefix);
ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], argv[1]);
if (ret < 0)
return ret;
else if (!ret)
die(_("no such section: %s"), argv[0]);
return 0;
}
static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)
{
struct option opts[] = {
CONFIG_LOCATION_OPTIONS,
OPT_END(),
};
int ret;
argc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
check_write();
check_argc(argc, 1, 1);
handle_config_location(prefix);
ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], NULL);
if (ret < 0)
return ret;
else if (!ret)
die(_("no such section: %s"), argv[0]);
return 0;
}
static int show_editor(void)
{
char *config_file;
if (!given_config_source.file && !startup_info->have_repository)
die(_("not in a git directory"));
if (given_config_source.use_stdin)
die(_("editing stdin is not supported"));
if (given_config_source.blob)
die(_("editing blobs is not supported"));
git_config(git_default_config, NULL);
config_file = given_config_source.file ?
xstrdup(given_config_source.file) :
git_pathdup("config");
if (use_global_config) {
int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd >= 0) {
char *content = default_user_config();
write_str_in_full(fd, content);
free(content);
close(fd);
}
else if (errno != EEXIST)
die_errno(_("cannot create configuration file %s"), config_file);
}
launch_editor(config_file, NULL, NULL);
free(config_file);
return 0;
}
static int cmd_config_edit(int argc, const char **argv, const char *prefix)
{
struct option opts[] = {
CONFIG_LOCATION_OPTIONS,
OPT_END(),
};
argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);
check_write();
check_argc(argc, 0, 0);
handle_config_location(prefix);
return show_editor();
}
static struct option builtin_subcommand_options[] = {
OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset),
OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section),
OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section),
OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit),
OPT_END(),
};
int cmd_config(int argc, const char **argv, const char *prefix)
{
char *value = NULL, *comment = NULL;
int flags = 0;
int ret = 0;
struct key_value_info default_kvi = KVI_INIT;
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
/*
* This is somewhat hacky: we first parse the command line while
* keeping all args intact in order to determine whether a subcommand
* has been specified. If so, we re-parse it a second time, but this
* time we drop KEEP_ARGV0. This is so that we don't munge the command
* line in case no subcommand was given, which would otherwise confuse
* us when parsing the legacy-style modes that don't use subcommands.
*/
argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,
PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);
if (subcommand) {
argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage,
PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);
return subcommand(argc, argv, prefix);
}
argc = parse_options(argc, argv, prefix, builtin_config_options,
builtin_config_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
handle_config_location(prefix);
handle_nul();
if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
error(_("--get-color and variable type are incoherent"));
usage_builtin_config();
}
if (HAS_MULTI_BITS(actions)) {
error(_("only one action at a time"));
usage_builtin_config();
}
if (actions == 0)
switch (argc) {
case 1: actions = ACTION_GET; break;
@ -799,7 +1139,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config();
}
if (comment &&
if (comment_arg &&
!(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
error(_("--comment is only applicable to add/set/replace operations"));
usage_builtin_config();
@ -841,7 +1181,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
flags |= CONFIG_FLAGS_FIXED_VALUE;
}
comment = git_config_prepare_comment_string(comment);
comment = git_config_prepare_comment_string(comment_arg);
if (actions & PAGING_ACTIONS)
setup_auto_pager("config", 1);
@ -859,32 +1199,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
}
else if (actions == ACTION_EDIT) {
char *config_file;
check_argc(argc, 0, 0);
if (!given_config_source.file && nongit)
die(_("not in a git directory"));
if (given_config_source.use_stdin)
die(_("editing stdin is not supported"));
if (given_config_source.blob)
die(_("editing blobs is not supported"));
git_config(git_default_config, NULL);
config_file = given_config_source.file ?
xstrdup(given_config_source.file) :
git_pathdup("config");
if (use_global_config) {
int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd >= 0) {
char *content = default_user_config();
write_str_in_full(fd, content);
free(content);
close(fd);
}
else if (errno != EEXIST)
die_errno(_("cannot create configuration file %s"), config_file);
}
launch_editor(config_file, NULL, NULL);
free(config_file);
ret = show_editor();
}
else if (actions == ACTION_SET) {
check_write();
@ -993,6 +1308,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
return get_colorbool(argv[0], argc == 2);
}
free(comment);
free(value);
return ret;
}

View file

@ -3193,14 +3193,10 @@ void git_config_set(const char *key, const char *value)
trace2_cmd_set_config(key, value);
}
/*
* The ownership rule is that the caller will own the string
* if it receives a piece of memory different from what it passed
* as the parameter.
*/
const char *git_config_prepare_comment_string(const char *comment)
char *git_config_prepare_comment_string(const char *comment)
{
size_t leading_blanks;
char *prepared;
if (!comment)
return NULL;
@ -3221,13 +3217,13 @@ const char *git_config_prepare_comment_string(const char *comment)
leading_blanks = strspn(comment, " \t");
if (leading_blanks && comment[leading_blanks] == '#')
; /* use it as-is */
prepared = xstrdup(comment); /* use it as-is */
else if (comment[0] == '#')
comment = xstrfmt(" %s", comment);
prepared = xstrfmt(" %s", comment);
else
comment = xstrfmt(" # %s", comment);
prepared = xstrfmt(" # %s", comment);
return comment;
return prepared;
}
static void validate_comment_string(const char *comment)

View file

@ -338,7 +338,7 @@ void git_config_set_multivar(const char *, const char *, const char *, unsigned)
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
const char *git_config_prepare_comment_string(const char *);
char *git_config_prepare_comment_string(const char *);
/**
* takes four parameters:

View file

@ -10,7 +10,6 @@ checkout
checkout-index
clone
column
config
credential
credential-cache
credential-store

File diff suppressed because it is too large Load diff