builtin/config: introduce "set" subcommand
Introduce a new "set" subcommand to git-config(1). Please refer to preceding commits regarding the motivation behind this change. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Patrick Steinhardt committed
May 6, 2024 at 10:56 UTC
00bbdde141f5bff378281d47c5c62ca06687b807
3 files changed
+140
-63
Documentation/git-config.txt
+24
-14
@@ -11,9 +11,7 @@ SYNOPSIS
11
[verse]
12
'git config list' [<file-option>] [<display-option>] [--includes]
13
'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>
14
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
15
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
16
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
14
+'git config set' [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>
15
'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
16
'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
17
'git config' [<file-option>] --rename-section <old-name> <new-name>
@@ -27,7 +25,7 @@ You can query/set/replace/unset options with this command. The name is
25
actually the section and the key separated by a dot, and the value will be
26
escaped.
27
30
-Multiple lines can be added to an option by using the `--add` option.
28
+Multiple lines can be added to an option by using the `--append` option.
29
If you want to update or unset an option which can occur on multiple
30
lines, a `value-pattern` (which is an extended regular expression,
31
unless the `--fixed-value` option is given) needs to be given. Only the
@@ -82,6 +80,13 @@ get::
80
emits all values associated with key. Returns error code 1 if key is
81
not present.
82
83
+set::
84
+ Set value for one or more config options. By default, this command
85
+ refuses to write multi-valued config options. Passing `--all` will
86
+ replace all multi-valued config options with the new value, whereas
87
+ `--value=` will replace all config options whose values match the given
88
+ pattern.
89
+
90
[[OPTIONS]]
91
OPTIONS
92
-------
@@ -90,10 +95,9 @@ OPTIONS
95
Default behavior is to replace at most one line. This replaces
96
all lines matching the key (and optionally the `value-pattern`).
97
93
---add::
98
+--append::
99
Adds a new line to the option without altering any existing
95
- values. This is the same as providing '^$' as the `value-pattern`
96
- in `--replace-all`.
100
+ values. This is the same as providing '--value=^$' in `set`.
101
102
--comment <message>::
103
Append a comment at the end of new or modified lines.
@@ -296,6 +300,9 @@ recommended to migrate to the new syntax.
300
'git config <name>'::
301
Replaced by `git config get <name>`.
302
303
+'git config <name> <value> [<value-pattern>]'::
304
+ Replaced by `git config set [--value=<pattern>] <name> <value>`.
305
+
306
-l::
307
--list::
308
Replaced by `git config list`.
@@ -315,6 +322,9 @@ recommended to migrate to the new syntax.
322
--get-color <name> [<default>]::
323
Replaced by `git config get --type=color [--default=<default>] <name>`.
324
325
+--add <name> <value>::
326
+ Replaced by `git config set --append <name> <value>`.
327
+
328
CONFIGURATION
329
-------------
330
`pager.config` is only respected when listing configuration, i.e., when
@@ -361,7 +371,7 @@ precedence over values read earlier. When multiple values are taken then all
371
values of a key from all files will be used.
372
373
By default, options are only written to the repository specific
364
-configuration file. Note that this also affects options like `--replace-all`
374
+configuration file. Note that this also affects options like `set`
375
and `--unset`. *'git config' will only ever change one file at a time*.
376
377
You can limit which configuration sources are read from or written to by
@@ -497,7 +507,7 @@ Given a .git/config like this:
507
you can set the filemode to true with
508
509
------------
500
-% git config core.filemode true
510
+% git config set core.filemode true
511
------------
512
513
The hypothetical proxy command entries actually have a postfix to discern
@@ -505,7 +515,7 @@ what URL they apply to. Here is how to change the entry for kernel.org
515
to "ssh".
516
517
------------
508
-% git config core.gitproxy '"ssh" for kernel.org' 'for kernel.org$'
518
+% git config set --value='for kernel.org$' core.gitproxy '"ssh" for kernel.org'
519
------------
520
521
This makes sure that only the key/value pair for kernel.org is replaced.
@@ -541,26 +551,26 @@ If you like to live dangerously, you can replace *all* core.gitproxy by a
551
new one with
552
553
------------
544
-% git config --replace-all core.gitproxy ssh
554
+% git config set --all core.gitproxy ssh
555
------------
556
557
However, if you really only want to replace the line for the default proxy,
558
i.e. the one without a "for ..." postfix, do something like this:
559
560
------------
551
-% git config core.gitproxy ssh '! for '
561
+% git config set --value='! for ' core.gitproxy ssh
562
------------
563
564
To actually match only values with an exclamation mark, you have to
565
566
------------
557
-% git config section.key value '[!]'
567
+% git config set --value='[!]' section.key value
568
------------
569
570
To add a new proxy, without altering any of the existing ones, use
571
572
------------
563
-% git config --add core.gitproxy '"proxy-command" for example.com'
573
+% git config set --append core.gitproxy '"proxy-command" for example.com'
574
------------
575
576
An example to use customized color from the configuration in your
builtin/config.c
+63
@@ -18,6 +18,7 @@
18
static const char *const builtin_config_usage[] = {
19
N_("git config list [<file-option>] [<display-option>] [--includes]"),
20
N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
21
+ N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
22
NULL
23
};
24
@@ -31,6 +32,11 @@ static const char *const builtin_config_get_usage[] = {
32
NULL
33
};
34
35
+static const char *const builtin_config_set_usage[] = {
36
+ N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
37
+ NULL
38
+};
39
+
40
static char *key;
41
static regex_t *key_regexp;
42
static const char *value_pattern;
@@ -849,9 +855,66 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix)
855
return get_value(argv[0], value_pattern, flags);
856
}
857
858
+static int cmd_config_set(int argc, const char **argv, const char *prefix)
859
+{
860
+ const char *value_pattern = NULL, *comment_arg = NULL;
861
+ char *comment = NULL;
862
+ int flags = 0, append = 0;
863
+ struct option opts[] = {
864
+ CONFIG_LOCATION_OPTIONS,
865
+ CONFIG_TYPE_OPTIONS,
866
+ OPT_GROUP(N_("Filter")),
867
+ OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
868
+ OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
869
+ OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
870
+ OPT_GROUP(N_("Other")),
871
+ OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
872
+ OPT_BOOL(0, "append", &append, N_("add a new line without altering any existing values")),
873
+ OPT_END(),
874
+ };
875
+ struct key_value_info default_kvi = KVI_INIT;
876
+ char *value;
877
+ int ret;
878
+
879
+ argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,
880
+ PARSE_OPT_STOP_AT_NON_OPTION);
881
+ check_write();
882
+ check_argc(argc, 2, 2);
883
+
884
+ if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
885
+ die(_("--fixed-value only applies with --value=<pattern>"));
886
+ if (append && value_pattern)
887
+ die(_("--append cannot be used with --value=<pattern>"));
888
+ if (append)
889
+ value_pattern = CONFIG_REGEX_NONE;
890
+
891
+ comment = git_config_prepare_comment_string(comment_arg);
892
+
893
+ handle_config_location(prefix);
894
+
895
+ value = normalize_value(argv[0], argv[1], &default_kvi);
896
+
897
+ if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {
898
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
899
+ argv[0], value, value_pattern,
900
+ comment, flags);
901
+ } else {
902
+ ret = git_config_set_in_file_gently(given_config_source.file,
903
+ argv[0], comment, value);
904
+ if (ret == CONFIG_NOTHING_SET)
905
+ error(_("cannot overwrite multiple values with a single value\n"
906
+ " Use a regexp, --add or --replace-all to change %s."), argv[0]);
907
+ }
908
+
909
+ free(comment);
910
+ free(value);
911
+ return ret;
912
+}
913
+
914
static struct option builtin_subcommand_options[] = {
915
OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
916
OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
917
+ OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
918
OPT_END(),
919
};
920
t/t1300-config.sh
+53
-49
@@ -20,12 +20,16 @@ legacy)
20
mode_get=""
21
mode_get_all="--get-all"
22
mode_get_regexp="--get-regexp"
23
+ mode_set=""
24
+ mode_replace_all="--replace-all"
25
;;
26
subcommands)
27
mode_prefix=""
28
mode_get="get"
29
mode_get_all="get --all"
30
mode_get_regexp="get --regexp --all --show-names"
31
+ mode_set="set"
32
+ mode_replace_all="set --all"
33
;;
34
*)
35
BUG "unknown mode $mode";;
@@ -132,7 +136,7 @@ cat > expect << EOF
136
penguin = little blue
137
EOF
138
test_expect_success 'initial' '
135
- git config section.penguin "little blue" &&
139
+ git config ${mode_set} section.penguin "little blue" &&
140
test_cmp expect .git/config
141
'
142
@@ -142,7 +146,7 @@ cat > expect << EOF
146
Movie = BadPhysics
147
EOF
148
test_expect_success 'mixed case' '
145
- git config Section.Movie BadPhysics &&
149
+ git config ${mode_set} Section.Movie BadPhysics &&
150
test_cmp expect .git/config
151
'
152
@@ -154,7 +158,7 @@ cat > expect << EOF
158
WhatEver = Second
159
EOF
160
test_expect_success 'similar section' '
157
- git config Sections.WhatEver Second &&
161
+ git config ${mode_set} Sections.WhatEver Second &&
162
test_cmp expect .git/config
163
'
164
@@ -167,7 +171,7 @@ cat > expect << EOF
171
WhatEver = Second
172
EOF
173
test_expect_success 'uppercase section' '
170
- git config SECTION.UPPERCASE true &&
174
+ git config ${mode_set} SECTION.UPPERCASE true &&
175
test_cmp expect .git/config
176
'
177
@@ -194,8 +198,8 @@ EOF
198
199
test_expect_success 'append comments' '
200
git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
197
- git config --comment="find fish" section.disposition peckish &&
198
- git config --comment="#abc" section.foo bar &&
201
+ git config ${mode_set} --comment="find fish" section.disposition peckish &&
202
+ git config ${mode_set} --comment="#abc" section.foo bar &&
203
204
git config --comment="and comment" section.spsp value &&
205
git config --comment=" # and comment" section.htsp value &&
@@ -204,7 +208,7 @@ test_expect_success 'append comments' '
208
'
209
210
test_expect_success 'Prohibited LF in comment' '
207
- test_must_fail git config --comment="a${LF}b" section.k v
211
+ test_must_fail git config ${mode_set} --comment="a${LF}b" section.k v
212
'
213
214
test_expect_success 'non-match result' 'test_cmp expect .git/config'
@@ -301,14 +305,14 @@ test_expect_success 'multiple unset is correct' '
305
cp .git/config2 .git/config
306
307
test_expect_success '--replace-all missing value' '
304
- test_must_fail git config --replace-all beta.haha &&
308
+ test_must_fail git config ${mode_replace_all} beta.haha &&
309
test_cmp .git/config2 .git/config
310
'
311
312
rm .git/config2
313
314
test_expect_success '--replace-all' '
311
- git config --replace-all beta.haha gamma
315
+ git config ${mode_replace_all} beta.haha gamma
316
'
317
318
cat > expect << EOF
@@ -335,7 +339,7 @@ noIndent= sillyValue ; 'nother silly comment
339
[nextSection] noNewline = ouch
340
EOF
341
test_expect_success 'really mean test' '
338
- git config beta.haha alpha &&
342
+ git config ${mode_set} beta.haha alpha &&
343
test_cmp expect .git/config
344
'
345
@@ -350,7 +354,7 @@ noIndent= sillyValue ; 'nother silly comment
354
nonewline = wow
355
EOF
356
test_expect_success 'really really mean test' '
353
- git config nextsection.nonewline wow &&
357
+ git config ${mode_set} nextsection.nonewline wow &&
358
test_cmp expect .git/config
359
'
360
@@ -824,16 +828,16 @@ EOF
828
829
test_expect_success 'section ending' '
830
rm -f .git/config &&
827
- git config gitcvs.enabled true &&
828
- git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
829
- git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
831
+ git config ${mode_set} gitcvs.enabled true &&
832
+ git config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
833
+ git config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
834
test_cmp expect .git/config
835
836
'
837
838
test_expect_success numbers '
835
- git config kilo.gram 1k &&
836
- git config mega.ton 1m &&
839
+ git config ${mode_set} kilo.gram 1k &&
840
+ git config ${mode_set} mega.ton 1m &&
841
echo 1024 >expect &&
842
echo 1048576 >>expect &&
843
git config --int --get kilo.gram >actual &&
@@ -842,20 +846,20 @@ test_expect_success numbers '
846
'
847
848
test_expect_success '--int is at least 64 bits' '
845
- git config giga.watts 121g &&
849
+ git config ${mode_set} giga.watts 121g &&
850
echo >expect &&
851
test_cmp_config 129922760704 --int --get giga.watts
852
'
853
854
test_expect_success 'invalid unit' '
851
- git config aninvalid.unit "1auto" &&
855
+ git config ${mode_set} aninvalid.unit "1auto" &&
856
test_cmp_config 1auto aninvalid.unit &&
857
test_must_fail git config --int --get aninvalid.unit 2>actual &&
858
test_grep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
859
'
860
861
test_expect_success 'invalid unit boolean' '
858
- git config commit.gpgsign "1true" &&
862
+ git config ${mode_set} commit.gpgsign "1true" &&
863
test_cmp_config 1true commit.gpgsign &&
864
test_must_fail git config --bool --get commit.gpgsign 2>actual &&
865
test_grep "bad boolean config value .1true. for .commit.gpgsign." actual
@@ -885,14 +889,14 @@ EOF
889
890
test_expect_success bool '
891
888
- git config bool.true1 01 &&
889
- git config bool.true2 -1 &&
890
- git config bool.true3 YeS &&
891
- git config bool.true4 true &&
892
- git config bool.false1 000 &&
893
- git config bool.false2 "" &&
894
- git config bool.false3 nO &&
895
- git config bool.false4 FALSE &&
892
+ git config ${mode_set} bool.true1 01 &&
893
+ git config ${mode_set} bool.true2 -1 &&
894
+ git config ${mode_set} bool.true3 YeS &&
895
+ git config ${mode_set} bool.true4 true &&
896
+ git config ${mode_set} bool.false1 000 &&
897
+ git config ${mode_set} bool.false2 "" &&
898
+ git config ${mode_set} bool.false3 nO &&
899
+ git config ${mode_set} bool.false4 FALSE &&
900
rm -f result &&
901
for i in 1 2 3 4
902
do
@@ -903,7 +907,7 @@ test_expect_success bool '
907
908
test_expect_success 'invalid bool (--get)' '
909
906
- git config bool.nobool foobar &&
910
+ git config ${mode_set} bool.nobool foobar &&
911
test_must_fail git config --bool --get bool.nobool'
912
913
test_expect_success 'invalid bool (set)' '
@@ -1092,7 +1096,7 @@ test_expect_success 'get --expiry-date' '
1096
1097
test_expect_success 'get --type=color' '
1098
rm .git/config &&
1095
- git config foo.color "red" &&
1099
+ git config ${mode_set} foo.color "red" &&
1100
git config --get --type=color foo.color >actual.raw &&
1101
test_decode_color <actual.raw >actual &&
1102
echo "<RED>" >expect &&
@@ -1129,10 +1133,10 @@ cat > expect << EOF
1133
EOF
1134
test_expect_success 'quoting' '
1135
rm -f .git/config &&
1132
- git config quote.leading " test" &&
1133
- git config quote.ending "test " &&
1134
- git config quote.semicolon "test;test" &&
1135
- git config quote.hash "test#test" &&
1136
+ git config ${mode_set} quote.leading " test" &&
1137
+ git config ${mode_set} quote.ending "test " &&
1138
+ git config ${mode_set} quote.semicolon "test;test" &&
1139
+ git config ${mode_set} quote.hash "test#test" &&
1140
test_cmp expect .git/config
1141
'
1142
@@ -1140,7 +1144,7 @@ test_expect_success 'key with newline' '
1144
test_must_fail git config ${mode_get} "key.with
1145
newline" 123'
1146
1143
-test_expect_success 'value with newline' 'git config key.sub value.with\\\
1147
+test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\
1148
newline'
1149
1150
cat > .git/config <<\EOF
@@ -1199,21 +1203,21 @@ test_expect_success '--null --get-regexp' '
1203
1204
test_expect_success 'inner whitespace kept verbatim, spaces only' '
1205
echo "foo bar" >expect &&
1202
- git config section.val "foo bar" &&
1206
+ git config ${mode_set} section.val "foo bar" &&
1207
git config ${mode_get} section.val >actual &&
1208
test_cmp expect actual
1209
'
1210
1211
test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
1212
echo "fooQQbar" | q_to_tab >expect &&
1209
- git config section.val "$(cat expect)" &&
1213
+ git config ${mode_set} section.val "$(cat expect)" &&
1214
git config ${mode_get} section.val >actual &&
1215
test_cmp expect actual
1216
'
1217
1218
test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
1219
echo "foo Q bar" | q_to_tab >expect &&
1216
- git config section.val "$(cat expect)" &&
1220
+ git config ${mode_set} section.val "$(cat expect)" &&
1221
git config ${mode_get} section.val >actual &&
1222
test_cmp expect actual
1223
'
@@ -1252,12 +1256,12 @@ test_expect_success 'check split_cmdline return' '
1256
git init repo &&
1257
(
1258
cd repo &&
1255
- git config alias.split-cmdline-fix "echo \"" &&
1259
+ git config ${mode_set} alias.split-cmdline-fix "echo \"" &&
1260
test_must_fail git split-cmdline-fix &&
1261
echo foo >foo &&
1262
git add foo &&
1263
git commit -m "initial commit" &&
1260
- git config branch.main.mergeoptions "echo \"" &&
1264
+ git config ${mode_set} branch.main.mergeoptions "echo \"" &&
1265
test_must_fail git merge main
1266
)
1267
'
@@ -1295,12 +1299,12 @@ test_expect_success 'key sanity-checking' '
1299
test_must_fail git config ${mode_get} foo.1bar &&
1300
test_must_fail git config ${mode_get} foo."ba
1301
z".bar &&
1298
- test_must_fail git config . false &&
1299
- test_must_fail git config .foo false &&
1300
- test_must_fail git config foo. false &&
1301
- test_must_fail git config .foo. false &&
1302
- git config foo.bar true &&
1303
- git config foo."ba =z".bar false
1302
+ test_must_fail git config ${mode_set} . false &&
1303
+ test_must_fail git config ${mode_set} .foo false &&
1304
+ test_must_fail git config ${mode_set} foo. false &&
1305
+ test_must_fail git config ${mode_set} .foo. false &&
1306
+ git config ${mode_set} foo.bar true &&
1307
+ git config ${mode_set} foo."ba =z".bar false
1308
'
1309
1310
test_expect_success 'git -c works with aliases of builtins' '
@@ -2523,7 +2527,7 @@ test_expect_success '--replace-all does not invent newlines' '
2527
[abc]
2528
Qkey = b
2529
EOF
2526
- git config --replace-all abc.key b &&
2530
+ git config ${mode_replace_all} abc.key b &&
2531
test_cmp expect .git/config
2532
'
2533
@@ -2595,8 +2599,8 @@ test_expect_success 'refuse --fixed-value for incompatible actions' '
2599
test_must_fail git config --file=config --fixed-value --get-colorbool dev.null &&
2600
2601
# These modes complain when --fixed-value has no value-pattern
2598
- test_must_fail git config --file=config --fixed-value dev.null bogus &&
2599
- test_must_fail git config --file=config --fixed-value --replace-all dev.null bogus &&
2602
+ test_must_fail git config ${mode_set} --file=config --fixed-value dev.null bogus &&
2603
+ test_must_fail git config ${mode_replace_all} --file=config --fixed-value dev.null bogus &&
2604
test_must_fail git config ${mode_prefix}get --file=config --fixed-value dev.null &&
2605
test_must_fail git config ${mode_get_all} --file=config --fixed-value dev.null &&
2606
test_must_fail git config ${mode_get_regexp} --file=config --fixed-value "dev.*" &&
@@ -2637,7 +2641,7 @@ test_expect_success '--fixed-value uses exact string matching' '
2641
test_must_fail git config ${mode_get} --file=config fixed.test &&
2642
2643
cp initial config &&
2640
- git config --file=config --replace-all fixed.test bogus "$META" &&
2644
+ git config --file=config fixed.test bogus "$META" &&
2645
git config ${mode_prefix}list --file=config >actual &&
2646
cat >expect <<-EOF &&
2647
fixed.test=$META