]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/config.c
builtin/config: introduce "list" subcommand
[thirdparty/git.git] / builtin / config.c
CommitLineData
e12c095a 1#include "builtin.h"
0b027f6c 2#include "abspath.h"
b2141fc1 3#include "config.h"
9ce03522 4#include "color.h"
4e120823 5#include "editor.h"
32a8f510 6#include "environment.h"
3867f6d6 7#include "repository.h"
f394e093 8#include "gettext.h"
b5fa6081 9#include "ident.h"
d64ec16c 10#include "parse-options.h"
d4770964 11#include "urlmatch.h"
d1cbe1e6 12#include "path.h"
70bd879a 13#include "quote.h"
e38da487 14#include "setup.h"
88e4e183 15#include "strbuf.h"
58b284a2 16#include "worktree.h"
1b1e59c5 17
d64ec16c 18static const char *const builtin_config_usage[] = {
14970509 19 N_("git config list [<file-option>] [<display-option>] [--includes]"),
9c9b4f2f 20 N_("git config [<options>]"),
d64ec16c
FC
21 NULL
22};
4ddba79d 23
14970509
PS
24static const char *const builtin_config_list_usage[] = {
25 N_("git config list [<file-option>] [<display-option>] [--includes]"),
26 NULL
27};
28
96f1e58f
DR
29static char *key;
30static regex_t *key_regexp;
3f1bae1d 31static const char *value_pattern;
96f1e58f
DR
32static regex_t *regexp;
33static int show_keys;
578625fa 34static int omit_values;
96f1e58f
DR
35static int use_key_regexp;
36static int do_all;
37static int do_not_match;
2275d502
FL
38static char delim = '=';
39static char key_delim = ' ';
40static char term = '\n';
4ddba79d 41
14970509 42static parse_opt_subcommand_fn *subcommand;
57210a67 43static int use_global_config, use_system_config, use_local_config;
58b284a2 44static int use_worktree_config;
c8985ce0 45static struct git_config_source given_config_source;
0a8950be 46static int actions, type;
eeaa24b9 47static char *default_value;
329e6ec3 48static int end_nul;
c48f4b37
NTND
49static int respect_includes_opt = -1;
50static struct config_options config_options;
70bd879a 51static int show_origin;
145d59f4 52static int show_scope;
fda43942 53static int fixed_value;
a78b4629 54static const char *comment_arg;
d64ec16c
FC
55
56#define ACTION_GET (1<<0)
57#define ACTION_GET_ALL (1<<1)
58#define ACTION_GET_REGEXP (1<<2)
59#define ACTION_REPLACE_ALL (1<<3)
60#define ACTION_ADD (1<<4)
61#define ACTION_UNSET (1<<5)
62#define ACTION_UNSET_ALL (1<<6)
63#define ACTION_RENAME_SECTION (1<<7)
64#define ACTION_REMOVE_SECTION (1<<8)
65#define ACTION_LIST (1<<9)
66#define ACTION_EDIT (1<<10)
67#define ACTION_SET (1<<11)
68#define ACTION_SET_ALL (1<<12)
69#define ACTION_GET_COLOR (1<<13)
70#define ACTION_GET_COLORBOOL (1<<14)
d4770964 71#define ACTION_GET_URLMATCH (1<<15)
d64ec16c 72
32888b8f
73/*
74 * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
75 * one line of output and which should therefore be paged.
76 */
77#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
78 ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
79
0a8950be
TB
80#define TYPE_BOOL 1
81#define TYPE_INT 2
82#define TYPE_BOOL_OR_INT 3
83#define TYPE_PATH 4
84#define TYPE_EXPIRY_DATE 5
63e2a0f8 85#define TYPE_COLOR 6
dbd8c09b 86#define TYPE_BOOL_OR_STR 7
16c1e939 87
fb0dc3ba
TB
88#define OPT_CALLBACK_VALUE(s, l, v, h, i) \
89 { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
90 PARSE_OPT_NONEG, option_parse_type, (i) }
91
6aaded55 92static NORETURN void usage_builtin_config(void);
fb0dc3ba
TB
93
94static int option_parse_type(const struct option *opt, const char *arg,
95 int unset)
96{
97 int new_type, *to_type;
98
99 if (unset) {
100 *((int *) opt->value) = 0;
101 return 0;
102 }
103
104 /*
105 * To support '--<type>' style flags, begin with new_type equal to
106 * opt->defval.
107 */
108 new_type = opt->defval;
109 if (!new_type) {
110 if (!strcmp(arg, "bool"))
111 new_type = TYPE_BOOL;
112 else if (!strcmp(arg, "int"))
113 new_type = TYPE_INT;
114 else if (!strcmp(arg, "bool-or-int"))
115 new_type = TYPE_BOOL_OR_INT;
dbd8c09b
LS
116 else if (!strcmp(arg, "bool-or-str"))
117 new_type = TYPE_BOOL_OR_STR;
fb0dc3ba
TB
118 else if (!strcmp(arg, "path"))
119 new_type = TYPE_PATH;
120 else if (!strcmp(arg, "expiry-date"))
121 new_type = TYPE_EXPIRY_DATE;
63e2a0f8
TB
122 else if (!strcmp(arg, "color"))
123 new_type = TYPE_COLOR;
fb0dc3ba
TB
124 else
125 die(_("unrecognized --type argument, %s"), arg);
126 }
127
128 to_type = opt->value;
129 if (*to_type && *to_type != new_type) {
130 /*
131 * Complain when there is a new type not equal to the old type.
132 * This allows for combinations like '--int --type=int' and
133 * '--type=int --type=int', but disallows ones like '--type=bool
134 * --int' and '--type=bool
135 * --type=int'.
136 */
1d28ff4c 137 error(_("only one type at a time"));
6aaded55 138 usage_builtin_config();
fb0dc3ba
TB
139 }
140 *to_type = new_type;
141
142 return 0;
143}
144
3b335762
NTND
145static void check_argc(int argc, int min, int max)
146{
d64ec16c
FC
147 if (argc >= min && argc <= max)
148 return;
1a07e59c 149 if (min == max)
1d28ff4c 150 error(_("wrong number of arguments, should be %d"), min);
1a07e59c 151 else
1d28ff4c 152 error(_("wrong number of arguments, should be from %d to %d"),
1a07e59c 153 min, max);
6aaded55 154 usage_builtin_config();
d64ec16c
FC
155}
156
26b66932
GC
157static void show_config_origin(const struct key_value_info *kvi,
158 struct strbuf *buf)
70bd879a 159{
329e6ec3 160 const char term = end_nul ? '\0' : '\t';
70bd879a 161
26b66932 162 strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
70bd879a 163 strbuf_addch(buf, ':');
329e6ec3 164 if (end_nul)
26b66932 165 strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
70bd879a 166 else
26b66932 167 quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
70bd879a
LS
168 strbuf_addch(buf, term);
169}
170
26b66932
GC
171static void show_config_scope(const struct key_value_info *kvi,
172 struct strbuf *buf)
145d59f4
MR
173{
174 const char term = end_nul ? '\0' : '\t';
26b66932 175 const char *scope = config_scope_name(kvi->scope);
145d59f4
MR
176
177 strbuf_addstr(buf, N_(scope));
178 strbuf_addch(buf, term);
179}
180
783a86c1 181static int show_all_config(const char *key_, const char *value_,
26b66932 182 const struct config_context *ctx,
5cf88fd8 183 void *cb UNUSED)
de791f15 184{
26b66932
GC
185 const struct key_value_info *kvi = ctx->kvi;
186
145d59f4 187 if (show_origin || show_scope) {
70bd879a 188 struct strbuf buf = STRBUF_INIT;
145d59f4 189 if (show_scope)
26b66932 190 show_config_scope(kvi, &buf);
145d59f4 191 if (show_origin)
26b66932 192 show_config_origin(kvi, &buf);
70bd879a
LS
193 /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
194 fwrite(buf.buf, 1, buf.len, stdout);
195 strbuf_release(&buf);
196 }
578625fa 197 if (!omit_values && value_)
2275d502 198 printf("%s%c%s%c", key_, delim, value_, term);
de791f15 199 else
2275d502 200 printf("%s%c", key_, term);
de791f15
PB
201 return 0;
202}
203
7acdd6f0
JK
204struct strbuf_list {
205 struct strbuf *items;
206 int nr;
207 int alloc;
208};
209
26b66932
GC
210static int format_config(struct strbuf *buf, const char *key_,
211 const char *value_, const struct key_value_info *kvi)
4ddba79d 212{
145d59f4 213 if (show_scope)
26b66932 214 show_config_scope(kvi, buf);
70bd879a 215 if (show_origin)
26b66932 216 show_config_origin(kvi, buf);
ebca2d49 217 if (show_keys)
7acdd6f0 218 strbuf_addstr(buf, key_);
ebca2d49 219 if (!omit_values) {
f2259877
JK
220 if (show_keys)
221 strbuf_addch(buf, key_delim);
ebca2d49 222
0a8950be 223 if (type == TYPE_INT)
f2259877 224 strbuf_addf(buf, "%"PRId64,
8868b1eb 225 git_config_int64(key_, value_ ? value_ : "", kvi));
0a8950be 226 else if (type == TYPE_BOOL)
f2259877
JK
227 strbuf_addstr(buf, git_config_bool(key_, value_) ?
228 "true" : "false");
0a8950be 229 else if (type == TYPE_BOOL_OR_INT) {
ebca2d49 230 int is_bool, v;
8868b1eb
GC
231 v = git_config_bool_or_int(key_, value_, kvi,
232 &is_bool);
ebca2d49 233 if (is_bool)
f2259877 234 strbuf_addstr(buf, v ? "true" : "false");
ebca2d49 235 else
f2259877 236 strbuf_addf(buf, "%d", v);
dbd8c09b
LS
237 } else if (type == TYPE_BOOL_OR_STR) {
238 int v = git_parse_maybe_bool(value_);
239 if (v < 0)
240 strbuf_addstr(buf, value_);
241 else
242 strbuf_addstr(buf, v ? "true" : "false");
0a8950be 243 } else if (type == TYPE_PATH) {
f2259877
JK
244 const char *v;
245 if (git_config_pathname(&v, key_, value_) < 0)
ebca2d49 246 return -1;
f2259877
JK
247 strbuf_addstr(buf, v);
248 free((char *)v);
0a8950be 249 } else if (type == TYPE_EXPIRY_DATE) {
5f967424
HM
250 timestamp_t t;
251 if (git_config_expiry_date(&t, key_, value_) < 0)
252 return -1;
253 strbuf_addf(buf, "%"PRItime, t);
63e2a0f8
TB
254 } else if (type == TYPE_COLOR) {
255 char v[COLOR_MAXLEN];
256 if (git_config_color(v, key_, value_) < 0)
257 return -1;
258 strbuf_addstr(buf, v);
ebca2d49 259 } else if (value_) {
f2259877 260 strbuf_addstr(buf, value_);
ebca2d49 261 } else {
f2259877
JK
262 /* Just show the key name; back out delimiter */
263 if (show_keys)
264 strbuf_setlen(buf, buf->len - 1);
ebca2d49 265 }
578625fa 266 }
00b347d3 267 strbuf_addch(buf, term);
4ddba79d
JS
268 return 0;
269}
270
a4e7e317 271static int collect_config(const char *key_, const char *value_,
26b66932 272 const struct config_context *ctx, void *cb)
d9b9169b
JH
273{
274 struct strbuf_list *values = cb;
26b66932 275 const struct key_value_info *kvi = ctx->kvi;
d9b9169b
JH
276
277 if (!use_key_regexp && strcmp(key_, key))
278 return 0;
279 if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
280 return 0;
3f1bae1d
DS
281 if (fixed_value && strcmp(value_pattern, (value_?value_:"")))
282 return 0;
d9b9169b
JH
283 if (regexp != NULL &&
284 (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
285 return 0;
286
287 ALLOC_GROW(values->items, values->nr + 1, values->alloc);
9f1429df 288 strbuf_init(&values->items[values->nr], 0);
d9b9169b 289
26b66932 290 return format_config(&values->items[values->nr++], key_, value_, kvi);
d9b9169b
JH
291}
292
3f1bae1d 293static int get_value(const char *key_, const char *regex_, unsigned flags)
4ddba79d 294{
9409c7a5 295 int ret = CONFIG_GENERIC_ERROR;
5ba1a8a7 296 struct strbuf_list values = {NULL};
7acdd6f0 297 int i;
4ddba79d 298
2fa9a0fb 299 if (use_key_regexp) {
b09c53a3
LP
300 char *tl;
301
302 /*
303 * NEEDSWORK: this naive pattern lowercasing obviously does not
304 * work for more complex patterns like "^[^.]*Foo.*bar".
305 * Perhaps we should deprecate this altogether someday.
306 */
307
308 key = xstrdup(key_);
309 for (tl = key + strlen(key) - 1;
310 tl >= key && *tl != '.';
311 tl--)
312 *tl = tolower(*tl);
313 for (tl = key; *tl && *tl != '.'; tl++)
314 *tl = tolower(*tl);
315
2d7320d0 316 key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
2fa9a0fb 317 if (regcomp(key_regexp, key, REG_EXTENDED)) {
1d28ff4c 318 error(_("invalid key pattern: %s"), key_);
6a83d902 319 FREE_AND_NULL(key_regexp);
9409c7a5 320 ret = CONFIG_INVALID_PATTERN;
5f1a63e0 321 goto free_strings;
2fa9a0fb 322 }
b09c53a3 323 } else {
9409c7a5
JH
324 if (git_config_parse_key(key_, &key, NULL)) {
325 ret = CONFIG_INVALID_KEY;
b09c53a3 326 goto free_strings;
9409c7a5 327 }
2fa9a0fb
JS
328 }
329
3f1bae1d
DS
330 if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
331 value_pattern = regex_;
332 else if (regex_) {
f98d863d
JS
333 if (regex_[0] == '!') {
334 do_not_match = 1;
335 regex_++;
336 }
337
2d7320d0 338 regexp = (regex_t*)xmalloc(sizeof(regex_t));
0a152171 339 if (regcomp(regexp, regex_, REG_EXTENDED)) {
1d28ff4c 340 error(_("invalid pattern: %s"), regex_);
6a83d902 341 FREE_AND_NULL(regexp);
9409c7a5 342 ret = CONFIG_INVALID_PATTERN;
5f1a63e0 343 goto free_strings;
4ddba79d
JS
344 }
345 }
346
dc8441fd 347 config_with_options(collect_config, &values,
9b6b06c1
VD
348 &given_config_source, the_repository,
349 &config_options);
5f1a63e0 350
eeaa24b9 351 if (!values.nr && default_value) {
26b66932 352 struct key_value_info kvi = KVI_INIT;
eeaa24b9 353 struct strbuf *item;
26b66932
GC
354
355 kvi_from_param(&kvi);
eeaa24b9
TB
356 ALLOC_GROW(values.items, values.nr + 1, values.alloc);
357 item = &values.items[values.nr++];
358 strbuf_init(item, 0);
26b66932 359 if (format_config(item, key_, default_value, &kvi) < 0)
eeaa24b9
TB
360 die(_("failed to format default config value: %s"),
361 default_value);
362 }
363
00b347d3 364 ret = !values.nr;
9b25a0b5 365
7acdd6f0
JK
366 for (i = 0; i < values.nr; i++) {
367 struct strbuf *buf = values.items + i;
00b347d3
JK
368 if (do_all || i == values.nr - 1)
369 fwrite(buf->buf, 1, buf->len, stdout);
7acdd6f0
JK
370 strbuf_release(buf);
371 }
372 free(values.items);
5f1a63e0 373
97ed50f9 374free_strings:
4ddba79d 375 free(key);
35998c89
JK
376 if (key_regexp) {
377 regfree(key_regexp);
378 free(key_regexp);
379 }
0a152171
AW
380 if (regexp) {
381 regfree(regexp);
382 free(regexp);
4ddba79d
JS
383 }
384
5f1a63e0 385 return ret;
4ddba79d 386}
1b1e59c5 387
8868b1eb
GC
388static char *normalize_value(const char *key, const char *value,
389 struct key_value_info *kvi)
db1696b8 390{
db1696b8
FL
391 if (!value)
392 return NULL;
393
0a8950be 394 if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)
1349484e
MM
395 /*
396 * We don't do normalization for TYPE_PATH here: If
397 * the path is like ~/foobar/, we prefer to store
398 * "~/foobar/" in the config file, and to expand the ~
399 * when retrieving the value.
5f967424 400 * Also don't do normalization for expiry dates.
1349484e 401 */
3ec832c4 402 return xstrdup(value);
0a8950be 403 if (type == TYPE_INT)
8868b1eb 404 return xstrfmt("%"PRId64, git_config_int64(key, value, kvi));
0a8950be 405 if (type == TYPE_BOOL)
3ec832c4 406 return xstrdup(git_config_bool(key, value) ? "true" : "false");
0a8950be 407 if (type == TYPE_BOOL_OR_INT) {
3ec832c4 408 int is_bool, v;
8868b1eb 409 v = git_config_bool_or_int(key, value, kvi, &is_bool);
3ec832c4
JK
410 if (!is_bool)
411 return xstrfmt("%d", v);
412 else
413 return xstrdup(v ? "true" : "false");
db1696b8 414 }
dbd8c09b
LS
415 if (type == TYPE_BOOL_OR_STR) {
416 int v = git_parse_maybe_bool(value);
417 if (v < 0)
418 return xstrdup(value);
419 else
420 return xstrdup(v ? "true" : "false");
421 }
63e2a0f8
TB
422 if (type == TYPE_COLOR) {
423 char v[COLOR_MAXLEN];
424 if (git_config_color(v, key, value))
1d28ff4c 425 die(_("cannot parse color '%s'"), value);
63e2a0f8
TB
426
427 /*
428 * The contents of `v` now contain an ANSI escape
429 * sequence, not suitable for including within a
430 * configuration file. Treat the above as a
431 * "sanity-check", and return the given value, which we
432 * know is representable as valid color code.
433 */
434 return xstrdup(value);
435 }
db1696b8 436
50f08db5 437 BUG("cannot normalize type %d", type);
db1696b8
FL
438}
439
9ce03522
JH
440static int get_color_found;
441static const char *get_color_slot;
b408457f 442static const char *get_colorbool_slot;
9ce03522
JH
443static char parsed_color[COLOR_MAXLEN];
444
783a86c1 445static int git_get_color_config(const char *var, const char *value,
a4e7e317 446 const struct config_context *ctx UNUSED,
5cf88fd8 447 void *cb UNUSED)
9ce03522
JH
448{
449 if (!strcmp(var, get_color_slot)) {
f769982d
JH
450 if (!value)
451 config_error_nonbool(var);
f6c5a296
JK
452 if (color_parse(value, parsed_color) < 0)
453 return -1;
9ce03522
JH
454 get_color_found = 1;
455 }
456 return 0;
457}
458
d0e08d62 459static void get_color(const char *var, const char *def_color)
9ce03522 460{
d0e08d62 461 get_color_slot = var;
9ce03522
JH
462 get_color_found = 0;
463 parsed_color[0] = '\0';
dc8441fd 464 config_with_options(git_get_color_config, NULL,
9b6b06c1
VD
465 &given_config_source, the_repository,
466 &config_options);
9ce03522 467
f6c5a296
JK
468 if (!get_color_found && def_color) {
469 if (color_parse(def_color, parsed_color) < 0)
470 die(_("unable to parse default color value"));
471 }
9ce03522
JH
472
473 fputs(parsed_color, stdout);
9ce03522
JH
474}
475
0f6f5a40 476static int get_colorbool_found;
69243c2b 477static int get_diff_color_found;
c659f55b 478static int get_color_ui_found;
ef90d6d4 479static int git_get_colorbool_config(const char *var, const char *value,
a4e7e317 480 const struct config_context *ctx UNUSED,
5cf88fd8 481 void *data UNUSED)
0f6f5a40 482{
e269eb79
JK
483 if (!strcmp(var, get_colorbool_slot))
484 get_colorbool_found = git_config_colorbool(var, value);
485 else if (!strcmp(var, "diff.color"))
486 get_diff_color_found = git_config_colorbool(var, value);
487 else if (!strcmp(var, "color.ui"))
c659f55b 488 get_color_ui_found = git_config_colorbool(var, value);
0f6f5a40
JH
489 return 0;
490}
491
d0e08d62 492static int get_colorbool(const char *var, int print)
0f6f5a40 493{
d0e08d62 494 get_colorbool_slot = var;
69243c2b
JH
495 get_colorbool_found = -1;
496 get_diff_color_found = -1;
b8612b4d 497 get_color_ui_found = -1;
dc8441fd 498 config_with_options(git_get_colorbool_config, NULL,
9b6b06c1
VD
499 &given_config_source, the_repository,
500 &config_options);
0f6f5a40 501
69243c2b 502 if (get_colorbool_found < 0) {
b408457f 503 if (!strcmp(get_colorbool_slot, "color.diff"))
69243c2b
JH
504 get_colorbool_found = get_diff_color_found;
505 if (get_colorbool_found < 0)
c659f55b 506 get_colorbool_found = get_color_ui_found;
69243c2b
JH
507 }
508
b8612b4d
MM
509 if (get_colorbool_found < 0)
510 /* default value if none found in config */
4c7f1819 511 get_colorbool_found = GIT_COLOR_AUTO;
b8612b4d 512
daa0c3d9
JK
513 get_colorbool_found = want_color(get_colorbool_found);
514
0e854a28 515 if (print) {
0f6f5a40
JH
516 printf("%s\n", get_colorbool_found ? "true" : "false");
517 return 0;
0e854a28
FC
518 } else
519 return get_colorbool_found ? 0 : 1;
0f6f5a40
JH
520}
521
6aea9f0f 522static void check_write(void)
1bc88819 523{
638fa623 524 if (!given_config_source.file && !startup_info->have_repository)
1d28ff4c 525 die(_("not in a git directory"));
638fa623 526
3caec73b 527 if (given_config_source.use_stdin)
1d28ff4c 528 die(_("writing to stdin is not supported"));
3caec73b 529
c8985ce0 530 if (given_config_source.blob)
1d28ff4c 531 die(_("writing config blobs is not supported"));
1bc88819
HV
532}
533
d4770964
JH
534struct urlmatch_current_candidate_value {
535 char value_is_null;
536 struct strbuf value;
26b66932 537 struct key_value_info kvi;
d4770964
JH
538};
539
a4e7e317 540static int urlmatch_collect_fn(const char *var, const char *value,
26b66932 541 const struct config_context *ctx,
a4e7e317 542 void *cb)
d4770964
JH
543{
544 struct string_list *values = cb;
545 struct string_list_item *item = string_list_insert(values, var);
546 struct urlmatch_current_candidate_value *matched = item->util;
26b66932 547 const struct key_value_info *kvi = ctx->kvi;
d4770964
JH
548
549 if (!matched) {
550 matched = xmalloc(sizeof(*matched));
551 strbuf_init(&matched->value, 0);
552 item->util = matched;
553 } else {
554 strbuf_reset(&matched->value);
555 }
26b66932 556 matched->kvi = *kvi;
d4770964
JH
557
558 if (value) {
559 strbuf_addstr(&matched->value, value);
560 matched->value_is_null = 0;
561 } else {
562 matched->value_is_null = 1;
563 }
564 return 0;
565}
566
d4770964
JH
567static int get_urlmatch(const char *var, const char *url)
568{
27b30be6 569 int ret;
d4770964
JH
570 char *section_tail;
571 struct string_list_item *item;
73ee449b 572 struct urlmatch_config config = URLMATCH_CONFIG_INIT;
d4770964
JH
573 struct string_list values = STRING_LIST_INIT_DUP;
574
575 config.collect_fn = urlmatch_collect_fn;
576 config.cascade_fn = NULL;
577 config.cb = &values;
578
579 if (!url_normalize(url, &config.url))
6667a6ac 580 die("%s", config.url.err);
d4770964 581
88d5a6f6 582 config.section = xstrdup_tolower(var);
d4770964
JH
583 section_tail = strchr(config.section, '.');
584 if (section_tail) {
585 *section_tail = '\0';
586 config.key = section_tail + 1;
587 show_keys = 0;
588 } else {
589 config.key = NULL;
590 show_keys = 1;
591 }
592
dc8441fd 593 config_with_options(urlmatch_config_entry, &config,
9b6b06c1
VD
594 &given_config_source, the_repository,
595 &config_options);
d4770964 596
27b30be6
JK
597 ret = !values.nr;
598
d4770964
JH
599 for_each_string_list_item(item, &values) {
600 struct urlmatch_current_candidate_value *matched = item->util;
d4770964
JH
601 struct strbuf buf = STRBUF_INIT;
602
a92330d2 603 format_config(&buf, item->string,
26b66932
GC
604 matched->value_is_null ? NULL : matched->value.buf,
605 &matched->kvi);
d4770964 606 fwrite(buf.buf, 1, buf.len, stdout);
d4770964
JH
607 strbuf_release(&buf);
608
609 strbuf_release(&matched->value);
610 }
a41e8e74 611 urlmatch_config_release(&config);
d4770964
JH
612 string_list_clear(&values, 1);
613 free(config.url.url);
614
615 free((void *)config.section);
27b30be6 616 return ret;
d4770964
JH
617}
618
9830534e
MM
619static char *default_user_config(void)
620{
621 struct strbuf buf = STRBUF_INIT;
622 strbuf_addf(&buf,
623 _("# This is Git's per-user configuration file.\n"
7e110524 624 "[user]\n"
9830534e 625 "# Please adapt and uncomment the following lines:\n"
7e110524 626 "# name = %s\n"
9830534e
MM
627 "# email = %s\n"),
628 ident_default_name(),
629 ident_default_email());
630 return strbuf_detach(&buf, NULL);
631}
632
9dda6b72 633static void handle_config_location(const char *prefix)
424a29c3 634{
1bc88819 635 if (use_global_config + use_system_config + use_local_config +
58b284a2 636 use_worktree_config +
c8985ce0 637 !!given_config_source.file + !!given_config_source.blob > 1) {
1d28ff4c 638 error(_("only one config file at a time"));
6aaded55 639 usage_builtin_config();
67052c9d
FC
640 }
641
9dda6b72 642 if (!startup_info->have_repository) {
378fe5fc
MT
643 if (use_local_config)
644 die(_("--local can only be used inside a git repository"));
645 if (given_config_source.blob)
646 die(_("--blob can only be used inside a git repository"));
647 if (use_worktree_config)
648 die(_("--worktree can only be used inside a git repository"));
378fe5fc 649 }
17b8a2d6 650
3caec73b
KS
651 if (given_config_source.file &&
652 !strcmp(given_config_source.file, "-")) {
653 given_config_source.file = NULL;
654 given_config_source.use_stdin = 1;
e37efa40 655 given_config_source.scope = CONFIG_SCOPE_COMMAND;
3caec73b
KS
656 }
657
d64ec16c 658 if (use_global_config) {
c15129b6
KH
659 given_config_source.file = git_global_config();
660 if (!given_config_source.file)
1cb3b92f
KH
661 /*
662 * It is unknown if HOME/.gitconfig exists, so
663 * we do not know if we should write to XDG
664 * location; error out even if XDG_CONFIG_HOME
665 * is set and points at a sane location.
666 */
1d28ff4c 667 die(_("$HOME not set"));
e37efa40 668 given_config_source.scope = CONFIG_SCOPE_GLOBAL;
c15129b6 669 } else if (use_system_config) {
c62a999c 670 given_config_source.file = git_system_config();
e37efa40
MR
671 given_config_source.scope = CONFIG_SCOPE_SYSTEM;
672 } else if (use_local_config) {
c8985ce0 673 given_config_source.file = git_pathdup("config");
e37efa40
MR
674 given_config_source.scope = CONFIG_SCOPE_LOCAL;
675 } else if (use_worktree_config) {
03f2465b 676 struct worktree **worktrees = get_worktrees();
3867f6d6 677 if (the_repository->repository_format_worktree_config)
58b284a2
NTND
678 given_config_source.file = git_pathdup("config.worktree");
679 else if (worktrees[0] && worktrees[1])
680 die(_("--worktree cannot be used with multiple "
681 "working trees unless the config\n"
682 "extension worktreeConfig is enabled. "
683 "Please read \"CONFIGURATION FILE\"\n"
684 "section in \"git help worktree\" for details"));
685 else
686 given_config_source.file = git_pathdup("config");
e37efa40 687 given_config_source.scope = CONFIG_SCOPE_LOCAL;
58b284a2
NTND
688 free_worktrees(worktrees);
689 } else if (given_config_source.file) {
c8985ce0
KS
690 if (!is_absolute_path(given_config_source.file) && prefix)
691 given_config_source.file =
e4da43b1 692 prefix_filename(prefix, given_config_source.file);
e37efa40
MR
693 given_config_source.scope = CONFIG_SCOPE_COMMAND;
694 } else if (given_config_source.blob) {
695 given_config_source.scope = CONFIG_SCOPE_COMMAND;
d64ec16c
FC
696 }
697
c48f4b37
NTND
698 if (respect_includes_opt == -1)
699 config_options.respect_includes = !given_config_source.file;
700 else
701 config_options.respect_includes = respect_includes_opt;
9dda6b72 702 if (startup_info->have_repository) {
dc8441fd
BW
703 config_options.commondir = get_git_common_dir();
704 config_options.git_dir = get_git_dir();
705 }
9dda6b72
PS
706}
707
fee37966
PS
708static void handle_nul(void) {
709 if (end_nul) {
710 term = '\0';
711 delim = '\n';
712 key_delim = '\n';
713 }
714}
715
14970509
PS
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
9dda6b72 732static struct option builtin_config_options[] = {
14970509 733 CONFIG_LOCATION_OPTIONS,
9dda6b72
PS
734 OPT_GROUP(N_("Action")),
735 OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
736 OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
737 OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP),
738 OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
739 OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL),
740 OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
741 OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET),
742 OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL),
743 OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
744 OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
745 OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
746 OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
747 OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR),
748 OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL),
749 OPT_GROUP(N_("Type")),
750 OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
751 OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
752 OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
753 OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
754 OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
755 OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
756 OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
14970509 757 CONFIG_DISPLAY_OPTIONS,
9dda6b72 758 OPT_GROUP(N_("Other")),
9dda6b72
PS
759 OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
760 OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
761 OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
14970509 762 OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
9dda6b72
PS
763 OPT_END(),
764};
765
766static NORETURN void usage_builtin_config(void)
767{
768 usage_with_options(builtin_config_usage, builtin_config_options);
769}
770
14970509
PS
771static 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
802static struct option builtin_subcommand_options[] = {
803 OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
804 OPT_END(),
805};
806
9dda6b72
PS
807int cmd_config(int argc, const char **argv, const char *prefix)
808{
809 char *value = NULL, *comment = NULL;
810 int flags = 0;
811 int ret = 0;
812 struct key_value_info default_kvi = KVI_INIT;
813
814 given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
815
14970509
PS
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
9dda6b72
PS
832 argc = parse_options(argc, argv, prefix, builtin_config_options,
833 builtin_config_usage,
834 PARSE_OPT_STOP_AT_NON_OPTION);
835
836 handle_config_location(prefix);
fee37966 837 handle_nul();
d64ec16c 838
0a8950be 839 if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
1d28ff4c 840 error(_("--get-color and variable type are incoherent"));
6aaded55 841 usage_builtin_config();
c2387358
FC
842 }
843
d64ec16c
FC
844 if (actions == 0)
845 switch (argc) {
846 case 1: actions = ACTION_GET; break;
847 case 2: actions = ACTION_SET; break;
848 case 3: actions = ACTION_SET_ALL; break;
849 default:
6aaded55 850 usage_builtin_config();
db1696b8 851 }
578625fa
SG
852 if (omit_values &&
853 !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
1d28ff4c 854 error(_("--name-only is only applicable to --list or --get-regexp"));
6aaded55 855 usage_builtin_config();
578625fa 856 }
70bd879a
LS
857
858 if (show_origin && !(actions &
859 (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
1d28ff4c
NTND
860 error(_("--show-origin is only applicable to --get, --get-all, "
861 "--get-regexp, and --list"));
6aaded55 862 usage_builtin_config();
70bd879a
LS
863 }
864
eeaa24b9 865 if (default_value && !(actions & ACTION_GET)) {
1d28ff4c 866 error(_("--default is only applicable to --get"));
6aaded55 867 usage_builtin_config();
eeaa24b9
TB
868 }
869
a78b4629 870 if (comment_arg &&
fbad334d
JH
871 !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
872 error(_("--comment is only applicable to add/set/replace operations"));
873 usage_builtin_config();
42d5c033
RS
874 }
875
fda43942
DS
876 /* check usage of --fixed-value */
877 if (fixed_value) {
878 int allowed_usage = 0;
879
880 switch (actions) {
881 /* git config --get <name> <value-pattern> */
882 case ACTION_GET:
883 /* git config --get-all <name> <value-pattern> */
884 case ACTION_GET_ALL:
885 /* git config --get-regexp <name-pattern> <value-pattern> */
886 case ACTION_GET_REGEXP:
887 /* git config --unset <name> <value-pattern> */
888 case ACTION_UNSET:
889 /* git config --unset-all <name> <value-pattern> */
890 case ACTION_UNSET_ALL:
891 allowed_usage = argc > 1 && !!argv[1];
892 break;
893
894 /* git config <name> <value> <value-pattern> */
895 case ACTION_SET_ALL:
896 /* git config --replace-all <name> <value> <value-pattern> */
897 case ACTION_REPLACE_ALL:
898 allowed_usage = argc > 2 && !!argv[2];
899 break;
900
901 /* other options don't allow --fixed-value */
902 }
903
904 if (!allowed_usage) {
905 error(_("--fixed-value only applies with 'value-pattern'"));
906 usage_builtin_config();
907 }
c90702a1
DS
908
909 flags |= CONFIG_FLAGS_FIXED_VALUE;
fda43942
DS
910 }
911
a78b4629 912 comment = git_config_prepare_comment_string(comment_arg);
fbad334d 913
32888b8f 914 if (actions & PAGING_ACTIONS)
c0e9f5be 915 setup_auto_pager("config", 1);
32888b8f 916
d64ec16c 917 if (actions == ACTION_LIST) {
225a9caf 918 check_argc(argc, 0, 0);
dc8441fd 919 if (config_with_options(show_all_config, NULL,
9b6b06c1 920 &given_config_source, the_repository,
dc8441fd 921 &config_options) < 0) {
c8985ce0 922 if (given_config_source.file)
1d28ff4c 923 die_errno(_("unable to read config file '%s'"),
c8985ce0 924 given_config_source.file);
d64ec16c 925 else
1d28ff4c 926 die(_("error processing config file(s)"));
db1696b8 927 }
1b1e59c5 928 }
d64ec16c 929 else if (actions == ACTION_EDIT) {
3696a7c2
MH
930 char *config_file;
931
225a9caf 932 check_argc(argc, 0, 0);
9dda6b72 933 if (!given_config_source.file && !startup_info->have_repository)
1d28ff4c 934 die(_("not in a git directory"));
3caec73b 935 if (given_config_source.use_stdin)
1d28ff4c 936 die(_("editing stdin is not supported"));
c8985ce0 937 if (given_config_source.blob)
1d28ff4c 938 die(_("editing blobs is not supported"));
d64ec16c 939 git_config(git_default_config, NULL);
d9c69644
JK
940 config_file = given_config_source.file ?
941 xstrdup(given_config_source.file) :
942 git_pathdup("config");
9830534e
MM
943 if (use_global_config) {
944 int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
aabbd3f3 945 if (fd >= 0) {
9830534e
MM
946 char *content = default_user_config();
947 write_str_in_full(fd, content);
948 free(content);
949 close(fd);
950 }
951 else if (errno != EEXIST)
952 die_errno(_("cannot create configuration file %s"), config_file);
953 }
954 launch_editor(config_file, NULL, NULL);
3696a7c2 955 free(config_file);
d64ec16c
FC
956 }
957 else if (actions == ACTION_SET) {
6aea9f0f 958 check_write();
d64ec16c 959 check_argc(argc, 2, 2);
8868b1eb 960 value = normalize_value(argv[0], argv[1], &default_kvi);
42d5c033 961 ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
5a2df368 962 if (ret == CONFIG_NOTHING_SET)
ccf63801
VA
963 error(_("cannot overwrite multiple values with a single value\n"
964 " Use a regexp, --add or --replace-all to change %s."), argv[0]);
d64ec16c
FC
965 }
966 else if (actions == ACTION_SET_ALL) {
6aea9f0f 967 check_write();
d64ec16c 968 check_argc(argc, 2, 3);
8868b1eb 969 value = normalize_value(argv[0], argv[1], &default_kvi);
ac95f5d3
ÆAB
970 ret = git_config_set_multivar_in_file_gently(given_config_source.file,
971 argv[0], value, argv[2],
42d5c033 972 comment, flags);
d64ec16c
FC
973 }
974 else if (actions == ACTION_ADD) {
6aea9f0f 975 check_write();
d64ec16c 976 check_argc(argc, 2, 2);
8868b1eb 977 value = normalize_value(argv[0], argv[1], &default_kvi);
ac95f5d3
ÆAB
978 ret = git_config_set_multivar_in_file_gently(given_config_source.file,
979 argv[0], value,
980 CONFIG_REGEX_NONE,
42d5c033 981 comment, flags);
d64ec16c
FC
982 }
983 else if (actions == ACTION_REPLACE_ALL) {
6aea9f0f 984 check_write();
d64ec16c 985 check_argc(argc, 2, 3);
8868b1eb 986 value = normalize_value(argv[0], argv[1], &default_kvi);
ac95f5d3
ÆAB
987 ret = git_config_set_multivar_in_file_gently(given_config_source.file,
988 argv[0], value, argv[2],
42d5c033 989 comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
d64ec16c
FC
990 }
991 else if (actions == ACTION_GET) {
992 check_argc(argc, 1, 2);
3f1bae1d 993 return get_value(argv[0], argv[1], flags);
d64ec16c
FC
994 }
995 else if (actions == ACTION_GET_ALL) {
996 do_all = 1;
997 check_argc(argc, 1, 2);
3f1bae1d 998 return get_value(argv[0], argv[1], flags);
d64ec16c
FC
999 }
1000 else if (actions == ACTION_GET_REGEXP) {
1001 show_keys = 1;
1002 use_key_regexp = 1;
1003 do_all = 1;
1004 check_argc(argc, 1, 2);
3f1bae1d 1005 return get_value(argv[0], argv[1], flags);
d64ec16c 1006 }
d4770964
JH
1007 else if (actions == ACTION_GET_URLMATCH) {
1008 check_argc(argc, 2, 2);
1009 return get_urlmatch(argv[0], argv[1]);
1010 }
d64ec16c 1011 else if (actions == ACTION_UNSET) {
6aea9f0f 1012 check_write();
d64ec16c
FC
1013 check_argc(argc, 1, 2);
1014 if (argc == 2)
30598ad0 1015 return git_config_set_multivar_in_file_gently(given_config_source.file,
c90702a1 1016 argv[0], NULL, argv[1],
42d5c033 1017 NULL, flags);
d64ec16c 1018 else
30598ad0 1019 return git_config_set_in_file_gently(given_config_source.file,
42d5c033 1020 argv[0], NULL, NULL);
d64ec16c
FC
1021 }
1022 else if (actions == ACTION_UNSET_ALL) {
6aea9f0f 1023 check_write();
d64ec16c 1024 check_argc(argc, 1, 2);
30598ad0 1025 return git_config_set_multivar_in_file_gently(given_config_source.file,
504ee129 1026 argv[0], NULL, argv[1],
42d5c033 1027 NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
d64ec16c
FC
1028 }
1029 else if (actions == ACTION_RENAME_SECTION) {
6aea9f0f 1030 check_write();
d64ec16c 1031 check_argc(argc, 2, 2);
c8985ce0 1032 ret = git_config_rename_section_in_file(given_config_source.file,
270a3443 1033 argv[0], argv[1]);
d64ec16c
FC
1034 if (ret < 0)
1035 return ret;
ac95f5d3 1036 else if (!ret)
1d28ff4c 1037 die(_("no such section: %s"), argv[0]);
ac95f5d3
ÆAB
1038 else
1039 ret = 0;
d64ec16c
FC
1040 }
1041 else if (actions == ACTION_REMOVE_SECTION) {
6aea9f0f 1042 check_write();
d64ec16c 1043 check_argc(argc, 1, 1);
c8985ce0 1044 ret = git_config_rename_section_in_file(given_config_source.file,
270a3443 1045 argv[0], NULL);
d64ec16c
FC
1046 if (ret < 0)
1047 return ret;
ac95f5d3 1048 else if (!ret)
1d28ff4c 1049 die(_("no such section: %s"), argv[0]);
ac95f5d3
ÆAB
1050 else
1051 ret = 0;
d64ec16c
FC
1052 }
1053 else if (actions == ACTION_GET_COLOR) {
d0e08d62
JK
1054 check_argc(argc, 1, 2);
1055 get_color(argv[0], argv[1]);
d64ec16c
FC
1056 }
1057 else if (actions == ACTION_GET_COLORBOOL) {
d0e08d62
JK
1058 check_argc(argc, 1, 2);
1059 if (argc == 2)
1060 color_stdout_is_tty = git_config_bool("command line", argv[1]);
1061 return get_colorbool(argv[0], argc == 2);
d64ec16c
FC
1062 }
1063
a78b4629 1064 free(comment);
ac95f5d3
ÆAB
1065 free(value);
1066 return ret;
1b1e59c5 1067}