Skip to content

Commit a10e784

Browse files
committed
Merge branch 'ds/config-literal-value'
Various subcommands of "git config" that takes value_regex learn the "--literal-value" option to take the value_regex option as a literal string. * ds/config-literal-value: config doc: value-pattern is not necessarily a regexp config: implement --fixed-value with --get* config: plumb --fixed-value into config API config: add --fixed-value option, un-implemented t1300: add test for --replace-all with value-pattern t1300: test "set all" mode with value-pattern config: replace 'value_regex' with 'value_pattern' config: convert multi_replace to flags
2 parents 6bac6a1 + c902618 commit a10e784

File tree

7 files changed

+312
-76
lines changed

7 files changed

+312
-76
lines changed

Documentation/git-config.txt

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ git-config - Get and set repository or global options
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] name [value [value_regex]]
12+
'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]]
1313
'git config' [<file-option>] [--type=<type>] --add name value
14-
'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]
15-
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get name [value_regex]
16-
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] --get-all name [value_regex]
17-
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]
14+
'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value-pattern]
15+
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value-pattern]
16+
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value-pattern]
17+
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value-pattern]
1818
'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL
19-
'git config' [<file-option>] --unset name [value_regex]
20-
'git config' [<file-option>] --unset-all name [value_regex]
19+
'git config' [<file-option>] [--fixed-value] --unset name [value-pattern]
20+
'git config' [<file-option>] [--fixed-value] --unset-all name [value-pattern]
2121
'git config' [<file-option>] --rename-section old_name new_name
2222
'git config' [<file-option>] --remove-section name
2323
'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
@@ -33,10 +33,13 @@ escaped.
3333

3434
Multiple lines can be added to an option by using the `--add` option.
3535
If you want to update or unset an option which can occur on multiple
36-
lines, a POSIX regexp `value_regex` needs to be given. Only the
37-
existing values that match the regexp are updated or unset. If
38-
you want to handle the lines that do *not* match the regex, just
39-
prepend a single exclamation mark in front (see also <<EXAMPLES>>).
36+
lines, a `value-pattern` (which is an extended regular expression,
37+
unless the `--fixed-value` option is given) needs to be given. Only the
38+
existing values that match the pattern are updated or unset. If
39+
you want to handle the lines that do *not* match the pattern, just
40+
prepend a single exclamation mark in front (see also <<EXAMPLES>>),
41+
but note that this only works when the `--fixed-value` option is not
42+
in use.
4043

4144
The `--type=<type>` option instructs 'git config' to ensure that incoming and
4245
outgoing values are canonicalize-able under the given <type>. If no
@@ -73,11 +76,11 @@ OPTIONS
7376

7477
--replace-all::
7578
Default behavior is to replace at most one line. This replaces
76-
all lines matching the key (and optionally the value_regex).
79+
all lines matching the key (and optionally the `value-pattern`).
7780

7881
--add::
7982
Adds a new line to the option without altering any existing
80-
values. This is the same as providing '^$' as the value_regex
83+
values. This is the same as providing '^$' as the `value-pattern`
8184
in `--replace-all`.
8285

8386
--get::
@@ -165,6 +168,12 @@ See also <<FILES>>.
165168
--list::
166169
List all variables set in config file, along with their values.
167170

171+
--fixed-value::
172+
When used with the `value-pattern` argument, treat `value-pattern` as
173+
an exact string instead of a regular expression. This will restrict
174+
the name/value pairs that are matched to only those where the value
175+
is exactly equal to the `value-pattern`.
176+
168177
--type <type>::
169178
'git config' will ensure that any input or output is valid under the given
170179
type constraint(s), and will canonicalize outgoing values in `<type>`'s

builtin/branch.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,10 +829,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
829829
die(_("Branch '%s' has no upstream information"), branch->name);
830830

831831
strbuf_addf(&buf, "branch.%s.remote", branch->name);
832-
git_config_set_multivar(buf.buf, NULL, NULL, 1);
832+
git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
833833
strbuf_reset(&buf);
834834
strbuf_addf(&buf, "branch.%s.merge", branch->name);
835-
git_config_set_multivar(buf.buf, NULL, NULL, 1);
835+
git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
836836
strbuf_release(&buf);
837837
} else if (argc > 0 && argc <= 2) {
838838
if (filter.kind != FILTER_REFS_BRANCHES)

builtin/config.c

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ static const char *const builtin_config_usage[] = {
1414

1515
static char *key;
1616
static regex_t *key_regexp;
17+
static const char *value_pattern;
1718
static regex_t *regexp;
1819
static int show_keys;
1920
static int omit_values;
@@ -34,6 +35,7 @@ static int respect_includes_opt = -1;
3435
static struct config_options config_options;
3536
static int show_origin;
3637
static int show_scope;
38+
static int fixed_value;
3739

3840
#define ACTION_GET (1<<0)
3941
#define ACTION_GET_ALL (1<<1)
@@ -133,17 +135,18 @@ static struct option builtin_config_options[] = {
133135
OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
134136
OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
135137
OPT_GROUP(N_("Action")),
136-
OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
137-
OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
138-
OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
138+
OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
139+
OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
140+
OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
139141
OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
140-
OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
142+
OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
141143
OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
142-
OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
143-
OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
144+
OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
145+
OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
144146
OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
145147
OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
146148
OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
149+
OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
147150
OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
148151
OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
149152
OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
@@ -296,6 +299,8 @@ static int collect_config(const char *key_, const char *value_, void *cb)
296299
return 0;
297300
if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
298301
return 0;
302+
if (fixed_value && strcmp(value_pattern, (value_?value_:"")))
303+
return 0;
299304
if (regexp != NULL &&
300305
(do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
301306
return 0;
@@ -306,7 +311,7 @@ static int collect_config(const char *key_, const char *value_, void *cb)
306311
return format_config(&values->items[values->nr++], key_, value_);
307312
}
308313

309-
static int get_value(const char *key_, const char *regex_)
314+
static int get_value(const char *key_, const char *regex_, unsigned flags)
310315
{
311316
int ret = CONFIG_GENERIC_ERROR;
312317
struct strbuf_list values = {NULL};
@@ -343,7 +348,9 @@ static int get_value(const char *key_, const char *regex_)
343348
}
344349
}
345350

346-
if (regex_) {
351+
if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
352+
value_pattern = regex_;
353+
else if (regex_) {
347354
if (regex_[0] == '!') {
348355
do_not_match = 1;
349356
regex_++;
@@ -631,6 +638,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
631638
{
632639
int nongit = !startup_info->have_repository;
633640
char *value;
641+
int flags = 0;
634642

635643
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
636644

@@ -766,6 +774,42 @@ int cmd_config(int argc, const char **argv, const char *prefix)
766774
usage_builtin_config();
767775
}
768776

777+
/* check usage of --fixed-value */
778+
if (fixed_value) {
779+
int allowed_usage = 0;
780+
781+
switch (actions) {
782+
/* git config --get <name> <value-pattern> */
783+
case ACTION_GET:
784+
/* git config --get-all <name> <value-pattern> */
785+
case ACTION_GET_ALL:
786+
/* git config --get-regexp <name-pattern> <value-pattern> */
787+
case ACTION_GET_REGEXP:
788+
/* git config --unset <name> <value-pattern> */
789+
case ACTION_UNSET:
790+
/* git config --unset-all <name> <value-pattern> */
791+
case ACTION_UNSET_ALL:
792+
allowed_usage = argc > 1 && !!argv[1];
793+
break;
794+
795+
/* git config <name> <value> <value-pattern> */
796+
case ACTION_SET_ALL:
797+
/* git config --replace-all <name> <value> <value-pattern> */
798+
case ACTION_REPLACE_ALL:
799+
allowed_usage = argc > 2 && !!argv[2];
800+
break;
801+
802+
/* other options don't allow --fixed-value */
803+
}
804+
805+
if (!allowed_usage) {
806+
error(_("--fixed-value only applies with 'value-pattern'"));
807+
usage_builtin_config();
808+
}
809+
810+
flags |= CONFIG_FLAGS_FIXED_VALUE;
811+
}
812+
769813
if (actions & PAGING_ACTIONS)
770814
setup_auto_pager("config", 1);
771815

@@ -827,7 +871,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
827871
value = normalize_value(argv[0], argv[1]);
828872
UNLEAK(value);
829873
return git_config_set_multivar_in_file_gently(given_config_source.file,
830-
argv[0], value, argv[2], 0);
874+
argv[0], value, argv[2],
875+
flags);
831876
}
832877
else if (actions == ACTION_ADD) {
833878
check_write();
@@ -836,31 +881,33 @@ int cmd_config(int argc, const char **argv, const char *prefix)
836881
UNLEAK(value);
837882
return git_config_set_multivar_in_file_gently(given_config_source.file,
838883
argv[0], value,
839-
CONFIG_REGEX_NONE, 0);
884+
CONFIG_REGEX_NONE,
885+
flags);
840886
}
841887
else if (actions == ACTION_REPLACE_ALL) {
842888
check_write();
843889
check_argc(argc, 2, 3);
844890
value = normalize_value(argv[0], argv[1]);
845891
UNLEAK(value);
846892
return git_config_set_multivar_in_file_gently(given_config_source.file,
847-
argv[0], value, argv[2], 1);
893+
argv[0], value, argv[2],
894+
flags | CONFIG_FLAGS_MULTI_REPLACE);
848895
}
849896
else if (actions == ACTION_GET) {
850897
check_argc(argc, 1, 2);
851-
return get_value(argv[0], argv[1]);
898+
return get_value(argv[0], argv[1], flags);
852899
}
853900
else if (actions == ACTION_GET_ALL) {
854901
do_all = 1;
855902
check_argc(argc, 1, 2);
856-
return get_value(argv[0], argv[1]);
903+
return get_value(argv[0], argv[1], flags);
857904
}
858905
else if (actions == ACTION_GET_REGEXP) {
859906
show_keys = 1;
860907
use_key_regexp = 1;
861908
do_all = 1;
862909
check_argc(argc, 1, 2);
863-
return get_value(argv[0], argv[1]);
910+
return get_value(argv[0], argv[1], flags);
864911
}
865912
else if (actions == ACTION_GET_URLMATCH) {
866913
check_argc(argc, 2, 2);
@@ -871,7 +918,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
871918
check_argc(argc, 1, 2);
872919
if (argc == 2)
873920
return git_config_set_multivar_in_file_gently(given_config_source.file,
874-
argv[0], NULL, argv[1], 0);
921+
argv[0], NULL, argv[1],
922+
flags);
875923
else
876924
return git_config_set_in_file_gently(given_config_source.file,
877925
argv[0], NULL);
@@ -880,7 +928,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
880928
check_write();
881929
check_argc(argc, 1, 2);
882930
return git_config_set_multivar_in_file_gently(given_config_source.file,
883-
argv[0], NULL, argv[1], 1);
931+
argv[0], NULL, argv[1],
932+
flags | CONFIG_FLAGS_MULTI_REPLACE);
884933
}
885934
else if (actions == ACTION_RENAME_SECTION) {
886935
int ret;

builtin/remote.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ static int mv(int argc, const char **argv)
712712

713713
strbuf_reset(&buf);
714714
strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
715-
git_config_set_multivar(buf.buf, NULL, NULL, 1);
715+
git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
716716
strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
717717
for (i = 0; i < oldremote->fetch.raw_nr; i++) {
718718
char *ptr;
@@ -1491,7 +1491,8 @@ static int update(int argc, const char **argv)
14911491

14921492
static int remove_all_fetch_refspecs(const char *key)
14931493
{
1494-
return git_config_set_multivar_gently(key, NULL, NULL, 1);
1494+
return git_config_set_multivar_gently(key, NULL, NULL,
1495+
CONFIG_FLAGS_MULTI_REPLACE);
14951496
}
14961497

14971498
static void add_branches(struct remote *remote, const char **branches,
@@ -1686,7 +1687,8 @@ static int set_url(int argc, const char **argv)
16861687
if (!delete_mode)
16871688
git_config_set_multivar(name_buf.buf, newurl, oldurl, 0);
16881689
else
1689-
git_config_set_multivar(name_buf.buf, NULL, oldurl, 1);
1690+
git_config_set_multivar(name_buf.buf, NULL, oldurl,
1691+
CONFIG_FLAGS_MULTI_REPLACE);
16901692
out:
16911693
strbuf_release(&name_buf);
16921694
return 0;

0 commit comments

Comments
 (0)