2f78a02ca1d893c7c0a57b56dfba65d291acf49e
[git/git.git] / builtin / config.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "config.h"
4 #include "color.h"
5 #include "parse-options.h"
6 #include "urlmatch.h"
7 #include "quote.h"
8
9 static const char *const builtin_config_usage[] = {
10 N_("git config [<options>]"),
11 NULL
12 };
13
14 static char *key;
15 static regex_t *key_regexp;
16 static regex_t *regexp;
17 static int show_keys;
18 static int omit_values;
19 static int use_key_regexp;
20 static int do_all;
21 static int do_not_match;
22 static char delim = '=';
23 static char key_delim = ' ';
24 static char term = '\n';
25
26 static int use_global_config, use_system_config, use_local_config;
27 static struct git_config_source given_config_source;
28 static int actions, type;
29 static char *default_value;
30 static int end_null;
31 static int respect_includes_opt = -1;
32 static struct config_options config_options;
33 static int show_origin;
34
35 #define ACTION_GET (1<<0)
36 #define ACTION_GET_ALL (1<<1)
37 #define ACTION_GET_REGEXP (1<<2)
38 #define ACTION_REPLACE_ALL (1<<3)
39 #define ACTION_ADD (1<<4)
40 #define ACTION_UNSET (1<<5)
41 #define ACTION_UNSET_ALL (1<<6)
42 #define ACTION_RENAME_SECTION (1<<7)
43 #define ACTION_REMOVE_SECTION (1<<8)
44 #define ACTION_LIST (1<<9)
45 #define ACTION_EDIT (1<<10)
46 #define ACTION_SET (1<<11)
47 #define ACTION_SET_ALL (1<<12)
48 #define ACTION_GET_COLOR (1<<13)
49 #define ACTION_GET_COLORBOOL (1<<14)
50 #define ACTION_GET_URLMATCH (1<<15)
51
52 /*
53 * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
54 * one line of output and which should therefore be paged.
55 */
56 #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
57 ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
58
59 #define TYPE_BOOL 1
60 #define TYPE_INT 2
61 #define TYPE_BOOL_OR_INT 3
62 #define TYPE_PATH 4
63 #define TYPE_EXPIRY_DATE 5
64
65 #define OPT_CALLBACK_VALUE(s, l, v, h, i) \
66 { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
67 PARSE_OPT_NONEG, option_parse_type, (i) }
68
69 static struct option builtin_config_options[];
70
71 static int option_parse_type(const struct option *opt, const char *arg,
72 int unset)
73 {
74 int new_type, *to_type;
75
76 if (unset) {
77 *((int *) opt->value) = 0;
78 return 0;
79 }
80
81 /*
82 * To support '--<type>' style flags, begin with new_type equal to
83 * opt->defval.
84 */
85 new_type = opt->defval;
86 if (!new_type) {
87 if (!strcmp(arg, "bool"))
88 new_type = TYPE_BOOL;
89 else if (!strcmp(arg, "int"))
90 new_type = TYPE_INT;
91 else if (!strcmp(arg, "bool-or-int"))
92 new_type = TYPE_BOOL_OR_INT;
93 else if (!strcmp(arg, "path"))
94 new_type = TYPE_PATH;
95 else if (!strcmp(arg, "expiry-date"))
96 new_type = TYPE_EXPIRY_DATE;
97 else
98 die(_("unrecognized --type argument, %s"), arg);
99 }
100
101 to_type = opt->value;
102 if (*to_type && *to_type != new_type) {
103 /*
104 * Complain when there is a new type not equal to the old type.
105 * This allows for combinations like '--int --type=int' and
106 * '--type=int --type=int', but disallows ones like '--type=bool
107 * --int' and '--type=bool
108 * --type=int'.
109 */
110 error("only one type at a time.");
111 usage_with_options(builtin_config_usage,
112 builtin_config_options);
113 }
114 *to_type = new_type;
115
116 return 0;
117 }
118
119 static struct option builtin_config_options[] = {
120 OPT_GROUP(N_("Config file location")),
121 OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
122 OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
123 OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
124 OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
125 OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
126 OPT_GROUP(N_("Action")),
127 OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
128 OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
129 OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
130 OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
131 OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
132 OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
133 OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
134 OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
135 OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
136 OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
137 OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
138 OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
139 OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
140 OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
141 OPT_GROUP(N_("Type")),
142 OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type),
143 OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
144 OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
145 OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
146 OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
147 OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
148 OPT_GROUP(N_("Other")),
149 OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
150 OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
151 OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
152 OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
153 OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
154 OPT_END(),
155 };
156
157 static void check_argc(int argc, int min, int max) {
158 if (argc >= min && argc <= max)
159 return;
160 error("wrong number of arguments");
161 usage_with_options(builtin_config_usage, builtin_config_options);
162 }
163
164 static void show_config_origin(struct strbuf *buf)
165 {
166 const char term = end_null ? '\0' : '\t';
167
168 strbuf_addstr(buf, current_config_origin_type());
169 strbuf_addch(buf, ':');
170 if (end_null)
171 strbuf_addstr(buf, current_config_name());
172 else
173 quote_c_style(current_config_name(), buf, NULL, 0);
174 strbuf_addch(buf, term);
175 }
176
177 static int show_all_config(const char *key_, const char *value_, void *cb)
178 {
179 if (show_origin) {
180 struct strbuf buf = STRBUF_INIT;
181 show_config_origin(&buf);
182 /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
183 fwrite(buf.buf, 1, buf.len, stdout);
184 strbuf_release(&buf);
185 }
186 if (!omit_values && value_)
187 printf("%s%c%s%c", key_, delim, value_, term);
188 else
189 printf("%s%c", key_, term);
190 return 0;
191 }
192
193 struct strbuf_list {
194 struct strbuf *items;
195 int nr;
196 int alloc;
197 };
198
199 static int format_config(struct strbuf *buf, const char *key_, const char *value_)
200 {
201 if (show_origin)
202 show_config_origin(buf);
203 if (show_keys)
204 strbuf_addstr(buf, key_);
205 if (!omit_values) {
206 if (show_keys)
207 strbuf_addch(buf, key_delim);
208
209 if (type == TYPE_INT)
210 strbuf_addf(buf, "%"PRId64,
211 git_config_int64(key_, value_ ? value_ : ""));
212 else if (type == TYPE_BOOL)
213 strbuf_addstr(buf, git_config_bool(key_, value_) ?
214 "true" : "false");
215 else if (type == TYPE_BOOL_OR_INT) {
216 int is_bool, v;
217 v = git_config_bool_or_int(key_, value_, &is_bool);
218 if (is_bool)
219 strbuf_addstr(buf, v ? "true" : "false");
220 else
221 strbuf_addf(buf, "%d", v);
222 } else if (type == TYPE_PATH) {
223 const char *v;
224 if (git_config_pathname(&v, key_, value_) < 0)
225 return -1;
226 strbuf_addstr(buf, v);
227 free((char *)v);
228 } else if (type == TYPE_EXPIRY_DATE) {
229 timestamp_t t;
230 if (git_config_expiry_date(&t, key_, value_) < 0)
231 return -1;
232 strbuf_addf(buf, "%"PRItime, t);
233 } else if (value_) {
234 strbuf_addstr(buf, value_);
235 } else {
236 /* Just show the key name; back out delimiter */
237 if (show_keys)
238 strbuf_setlen(buf, buf->len - 1);
239 }
240 }
241 strbuf_addch(buf, term);
242 return 0;
243 }
244
245 static int collect_config(const char *key_, const char *value_, void *cb)
246 {
247 struct strbuf_list *values = cb;
248
249 if (!use_key_regexp && strcmp(key_, key))
250 return 0;
251 if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
252 return 0;
253 if (regexp != NULL &&
254 (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
255 return 0;
256
257 ALLOC_GROW(values->items, values->nr + 1, values->alloc);
258 strbuf_init(&values->items[values->nr], 0);
259
260 return format_config(&values->items[values->nr++], key_, value_);
261 }
262
263 static int get_value(const char *key_, const char *regex_)
264 {
265 int ret = CONFIG_GENERIC_ERROR;
266 struct strbuf_list values = {NULL};
267 int i;
268
269 if (use_key_regexp) {
270 char *tl;
271
272 /*
273 * NEEDSWORK: this naive pattern lowercasing obviously does not
274 * work for more complex patterns like "^[^.]*Foo.*bar".
275 * Perhaps we should deprecate this altogether someday.
276 */
277
278 key = xstrdup(key_);
279 for (tl = key + strlen(key) - 1;
280 tl >= key && *tl != '.';
281 tl--)
282 *tl = tolower(*tl);
283 for (tl = key; *tl && *tl != '.'; tl++)
284 *tl = tolower(*tl);
285
286 key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
287 if (regcomp(key_regexp, key, REG_EXTENDED)) {
288 error("invalid key pattern: %s", key_);
289 FREE_AND_NULL(key_regexp);
290 ret = CONFIG_INVALID_PATTERN;
291 goto free_strings;
292 }
293 } else {
294 if (git_config_parse_key(key_, &key, NULL)) {
295 ret = CONFIG_INVALID_KEY;
296 goto free_strings;
297 }
298 }
299
300 if (regex_) {
301 if (regex_[0] == '!') {
302 do_not_match = 1;
303 regex_++;
304 }
305
306 regexp = (regex_t*)xmalloc(sizeof(regex_t));
307 if (regcomp(regexp, regex_, REG_EXTENDED)) {
308 error("invalid pattern: %s", regex_);
309 FREE_AND_NULL(regexp);
310 ret = CONFIG_INVALID_PATTERN;
311 goto free_strings;
312 }
313 }
314
315 config_with_options(collect_config, &values,
316 &given_config_source, &config_options);
317
318 if (!values.nr && default_value) {
319 struct strbuf *item;
320 ALLOC_GROW(values.items, values.nr + 1, values.alloc);
321 item = &values.items[values.nr++];
322 strbuf_init(item, 0);
323 if (format_config(item, key_, default_value) < 0)
324 die(_("failed to format default config value: %s"),
325 default_value);
326 }
327
328 ret = !values.nr;
329
330 for (i = 0; i < values.nr; i++) {
331 struct strbuf *buf = values.items + i;
332 if (do_all || i == values.nr - 1)
333 fwrite(buf->buf, 1, buf->len, stdout);
334 strbuf_release(buf);
335 }
336 free(values.items);
337
338 free_strings:
339 free(key);
340 if (key_regexp) {
341 regfree(key_regexp);
342 free(key_regexp);
343 }
344 if (regexp) {
345 regfree(regexp);
346 free(regexp);
347 }
348
349 return ret;
350 }
351
352 static char *normalize_value(const char *key, const char *value)
353 {
354 if (!value)
355 return NULL;
356
357 if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)
358 /*
359 * We don't do normalization for TYPE_PATH here: If
360 * the path is like ~/foobar/, we prefer to store
361 * "~/foobar/" in the config file, and to expand the ~
362 * when retrieving the value.
363 * Also don't do normalization for expiry dates.
364 */
365 return xstrdup(value);
366 if (type == TYPE_INT)
367 return xstrfmt("%"PRId64, git_config_int64(key, value));
368 if (type == TYPE_BOOL)
369 return xstrdup(git_config_bool(key, value) ? "true" : "false");
370 if (type == TYPE_BOOL_OR_INT) {
371 int is_bool, v;
372 v = git_config_bool_or_int(key, value, &is_bool);
373 if (!is_bool)
374 return xstrfmt("%d", v);
375 else
376 return xstrdup(v ? "true" : "false");
377 }
378
379 die("BUG: cannot normalize type %d", type);
380 }
381
382 static int get_color_found;
383 static const char *get_color_slot;
384 static const char *get_colorbool_slot;
385 static char parsed_color[COLOR_MAXLEN];
386
387 static int git_get_color_config(const char *var, const char *value, void *cb)
388 {
389 if (!strcmp(var, get_color_slot)) {
390 if (!value)
391 config_error_nonbool(var);
392 if (color_parse(value, parsed_color) < 0)
393 return -1;
394 get_color_found = 1;
395 }
396 return 0;
397 }
398
399 static void get_color(const char *var, const char *def_color)
400 {
401 get_color_slot = var;
402 get_color_found = 0;
403 parsed_color[0] = '\0';
404 config_with_options(git_get_color_config, NULL,
405 &given_config_source, &config_options);
406
407 if (!get_color_found && def_color) {
408 if (color_parse(def_color, parsed_color) < 0)
409 die(_("unable to parse default color value"));
410 }
411
412 fputs(parsed_color, stdout);
413 }
414
415 static int get_colorbool_found;
416 static int get_diff_color_found;
417 static int get_color_ui_found;
418 static int git_get_colorbool_config(const char *var, const char *value,
419 void *cb)
420 {
421 if (!strcmp(var, get_colorbool_slot))
422 get_colorbool_found = git_config_colorbool(var, value);
423 else if (!strcmp(var, "diff.color"))
424 get_diff_color_found = git_config_colorbool(var, value);
425 else if (!strcmp(var, "color.ui"))
426 get_color_ui_found = git_config_colorbool(var, value);
427 return 0;
428 }
429
430 static int get_colorbool(const char *var, int print)
431 {
432 get_colorbool_slot = var;
433 get_colorbool_found = -1;
434 get_diff_color_found = -1;
435 get_color_ui_found = -1;
436 config_with_options(git_get_colorbool_config, NULL,
437 &given_config_source, &config_options);
438
439 if (get_colorbool_found < 0) {
440 if (!strcmp(get_colorbool_slot, "color.diff"))
441 get_colorbool_found = get_diff_color_found;
442 if (get_colorbool_found < 0)
443 get_colorbool_found = get_color_ui_found;
444 }
445
446 if (get_colorbool_found < 0)
447 /* default value if none found in config */
448 get_colorbool_found = GIT_COLOR_AUTO;
449
450 get_colorbool_found = want_color(get_colorbool_found);
451
452 if (print) {
453 printf("%s\n", get_colorbool_found ? "true" : "false");
454 return 0;
455 } else
456 return get_colorbool_found ? 0 : 1;
457 }
458
459 static void check_write(void)
460 {
461 if (!given_config_source.file && !startup_info->have_repository)
462 die("not in a git directory");
463
464 if (given_config_source.use_stdin)
465 die("writing to stdin is not supported");
466
467 if (given_config_source.blob)
468 die("writing config blobs is not supported");
469 }
470
471 struct urlmatch_current_candidate_value {
472 char value_is_null;
473 struct strbuf value;
474 };
475
476 static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
477 {
478 struct string_list *values = cb;
479 struct string_list_item *item = string_list_insert(values, var);
480 struct urlmatch_current_candidate_value *matched = item->util;
481
482 if (!matched) {
483 matched = xmalloc(sizeof(*matched));
484 strbuf_init(&matched->value, 0);
485 item->util = matched;
486 } else {
487 strbuf_reset(&matched->value);
488 }
489
490 if (value) {
491 strbuf_addstr(&matched->value, value);
492 matched->value_is_null = 0;
493 } else {
494 matched->value_is_null = 1;
495 }
496 return 0;
497 }
498
499 static int get_urlmatch(const char *var, const char *url)
500 {
501 int ret;
502 char *section_tail;
503 struct string_list_item *item;
504 struct urlmatch_config config = { STRING_LIST_INIT_DUP };
505 struct string_list values = STRING_LIST_INIT_DUP;
506
507 config.collect_fn = urlmatch_collect_fn;
508 config.cascade_fn = NULL;
509 config.cb = &values;
510
511 if (!url_normalize(url, &config.url))
512 die("%s", config.url.err);
513
514 config.section = xstrdup_tolower(var);
515 section_tail = strchr(config.section, '.');
516 if (section_tail) {
517 *section_tail = '\0';
518 config.key = section_tail + 1;
519 show_keys = 0;
520 } else {
521 config.key = NULL;
522 show_keys = 1;
523 }
524
525 config_with_options(urlmatch_config_entry, &config,
526 &given_config_source, &config_options);
527
528 ret = !values.nr;
529
530 for_each_string_list_item(item, &values) {
531 struct urlmatch_current_candidate_value *matched = item->util;
532 struct strbuf buf = STRBUF_INIT;
533
534 format_config(&buf, item->string,
535 matched->value_is_null ? NULL : matched->value.buf);
536 fwrite(buf.buf, 1, buf.len, stdout);
537 strbuf_release(&buf);
538
539 strbuf_release(&matched->value);
540 }
541 string_list_clear(&config.vars, 1);
542 string_list_clear(&values, 1);
543 free(config.url.url);
544
545 free((void *)config.section);
546 return ret;
547 }
548
549 static char *default_user_config(void)
550 {
551 struct strbuf buf = STRBUF_INIT;
552 strbuf_addf(&buf,
553 _("# This is Git's per-user configuration file.\n"
554 "[user]\n"
555 "# Please adapt and uncomment the following lines:\n"
556 "# name = %s\n"
557 "# email = %s\n"),
558 ident_default_name(),
559 ident_default_email());
560 return strbuf_detach(&buf, NULL);
561 }
562
563 int cmd_config(int argc, const char **argv, const char *prefix)
564 {
565 int nongit = !startup_info->have_repository;
566 char *value;
567
568 given_config_source.file = getenv(CONFIG_ENVIRONMENT);
569
570 argc = parse_options(argc, argv, prefix, builtin_config_options,
571 builtin_config_usage,
572 PARSE_OPT_STOP_AT_NON_OPTION);
573
574 if (use_global_config + use_system_config + use_local_config +
575 !!given_config_source.file + !!given_config_source.blob > 1) {
576 error("only one config file at a time.");
577 usage_with_options(builtin_config_usage, builtin_config_options);
578 }
579
580 if (use_local_config && nongit)
581 die(_("--local can only be used inside a git repository"));
582
583 if (given_config_source.file &&
584 !strcmp(given_config_source.file, "-")) {
585 given_config_source.file = NULL;
586 given_config_source.use_stdin = 1;
587 }
588
589 if (use_global_config) {
590 char *user_config = expand_user_path("~/.gitconfig", 0);
591 char *xdg_config = xdg_config_home("config");
592
593 if (!user_config)
594 /*
595 * It is unknown if HOME/.gitconfig exists, so
596 * we do not know if we should write to XDG
597 * location; error out even if XDG_CONFIG_HOME
598 * is set and points at a sane location.
599 */
600 die("$HOME not set");
601
602 if (access_or_warn(user_config, R_OK, 0) &&
603 xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
604 given_config_source.file = xdg_config;
605 free(user_config);
606 } else {
607 given_config_source.file = user_config;
608 free(xdg_config);
609 }
610 }
611 else if (use_system_config)
612 given_config_source.file = git_etc_gitconfig();
613 else if (use_local_config)
614 given_config_source.file = git_pathdup("config");
615 else if (given_config_source.file) {
616 if (!is_absolute_path(given_config_source.file) && prefix)
617 given_config_source.file =
618 prefix_filename(prefix, given_config_source.file);
619 }
620
621 if (respect_includes_opt == -1)
622 config_options.respect_includes = !given_config_source.file;
623 else
624 config_options.respect_includes = respect_includes_opt;
625 if (!nongit) {
626 config_options.commondir = get_git_common_dir();
627 config_options.git_dir = get_git_dir();
628 }
629
630 if (end_null) {
631 term = '\0';
632 delim = '\n';
633 key_delim = '\n';
634 }
635
636 if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
637 error("--get-color and variable type are incoherent");
638 usage_with_options(builtin_config_usage, builtin_config_options);
639 }
640
641 if (HAS_MULTI_BITS(actions)) {
642 error("only one action at a time.");
643 usage_with_options(builtin_config_usage, builtin_config_options);
644 }
645 if (actions == 0)
646 switch (argc) {
647 case 1: actions = ACTION_GET; break;
648 case 2: actions = ACTION_SET; break;
649 case 3: actions = ACTION_SET_ALL; break;
650 default:
651 usage_with_options(builtin_config_usage, builtin_config_options);
652 }
653 if (omit_values &&
654 !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
655 error("--name-only is only applicable to --list or --get-regexp");
656 usage_with_options(builtin_config_usage, builtin_config_options);
657 }
658
659 if (show_origin && !(actions &
660 (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
661 error("--show-origin is only applicable to --get, --get-all, "
662 "--get-regexp, and --list.");
663 usage_with_options(builtin_config_usage, builtin_config_options);
664 }
665
666 if (default_value && !(actions & ACTION_GET)) {
667 error("--default is only applicable to --get");
668 usage_with_options(builtin_config_usage,
669 builtin_config_options);
670 }
671
672 if (actions & PAGING_ACTIONS)
673 setup_auto_pager("config", 1);
674
675 if (actions == ACTION_LIST) {
676 check_argc(argc, 0, 0);
677 if (config_with_options(show_all_config, NULL,
678 &given_config_source,
679 &config_options) < 0) {
680 if (given_config_source.file)
681 die_errno("unable to read config file '%s'",
682 given_config_source.file);
683 else
684 die("error processing config file(s)");
685 }
686 }
687 else if (actions == ACTION_EDIT) {
688 char *config_file;
689
690 check_argc(argc, 0, 0);
691 if (!given_config_source.file && nongit)
692 die("not in a git directory");
693 if (given_config_source.use_stdin)
694 die("editing stdin is not supported");
695 if (given_config_source.blob)
696 die("editing blobs is not supported");
697 git_config(git_default_config, NULL);
698 config_file = given_config_source.file ?
699 xstrdup(given_config_source.file) :
700 git_pathdup("config");
701 if (use_global_config) {
702 int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
703 if (fd >= 0) {
704 char *content = default_user_config();
705 write_str_in_full(fd, content);
706 free(content);
707 close(fd);
708 }
709 else if (errno != EEXIST)
710 die_errno(_("cannot create configuration file %s"), config_file);
711 }
712 launch_editor(config_file, NULL, NULL);
713 free(config_file);
714 }
715 else if (actions == ACTION_SET) {
716 int ret;
717 check_write();
718 check_argc(argc, 2, 2);
719 value = normalize_value(argv[0], argv[1]);
720 UNLEAK(value);
721 ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
722 if (ret == CONFIG_NOTHING_SET)
723 error(_("cannot overwrite multiple values with a single value\n"
724 " Use a regexp, --add or --replace-all to change %s."), argv[0]);
725 return ret;
726 }
727 else if (actions == ACTION_SET_ALL) {
728 check_write();
729 check_argc(argc, 2, 3);
730 value = normalize_value(argv[0], argv[1]);
731 UNLEAK(value);
732 return git_config_set_multivar_in_file_gently(given_config_source.file,
733 argv[0], value, argv[2], 0);
734 }
735 else if (actions == ACTION_ADD) {
736 check_write();
737 check_argc(argc, 2, 2);
738 value = normalize_value(argv[0], argv[1]);
739 UNLEAK(value);
740 return git_config_set_multivar_in_file_gently(given_config_source.file,
741 argv[0], value,
742 CONFIG_REGEX_NONE, 0);
743 }
744 else if (actions == ACTION_REPLACE_ALL) {
745 check_write();
746 check_argc(argc, 2, 3);
747 value = normalize_value(argv[0], argv[1]);
748 UNLEAK(value);
749 return git_config_set_multivar_in_file_gently(given_config_source.file,
750 argv[0], value, argv[2], 1);
751 }
752 else if (actions == ACTION_GET) {
753 check_argc(argc, 1, 2);
754 return get_value(argv[0], argv[1]);
755 }
756 else if (actions == ACTION_GET_ALL) {
757 do_all = 1;
758 check_argc(argc, 1, 2);
759 return get_value(argv[0], argv[1]);
760 }
761 else if (actions == ACTION_GET_REGEXP) {
762 show_keys = 1;
763 use_key_regexp = 1;
764 do_all = 1;
765 check_argc(argc, 1, 2);
766 return get_value(argv[0], argv[1]);
767 }
768 else if (actions == ACTION_GET_URLMATCH) {
769 check_argc(argc, 2, 2);
770 return get_urlmatch(argv[0], argv[1]);
771 }
772 else if (actions == ACTION_UNSET) {
773 check_write();
774 check_argc(argc, 1, 2);
775 if (argc == 2)
776 return git_config_set_multivar_in_file_gently(given_config_source.file,
777 argv[0], NULL, argv[1], 0);
778 else
779 return git_config_set_in_file_gently(given_config_source.file,
780 argv[0], NULL);
781 }
782 else if (actions == ACTION_UNSET_ALL) {
783 check_write();
784 check_argc(argc, 1, 2);
785 return git_config_set_multivar_in_file_gently(given_config_source.file,
786 argv[0], NULL, argv[1], 1);
787 }
788 else if (actions == ACTION_RENAME_SECTION) {
789 int ret;
790 check_write();
791 check_argc(argc, 2, 2);
792 ret = git_config_rename_section_in_file(given_config_source.file,
793 argv[0], argv[1]);
794 if (ret < 0)
795 return ret;
796 if (ret == 0)
797 die("No such section!");
798 }
799 else if (actions == ACTION_REMOVE_SECTION) {
800 int ret;
801 check_write();
802 check_argc(argc, 1, 1);
803 ret = git_config_rename_section_in_file(given_config_source.file,
804 argv[0], NULL);
805 if (ret < 0)
806 return ret;
807 if (ret == 0)
808 die("No such section!");
809 }
810 else if (actions == ACTION_GET_COLOR) {
811 check_argc(argc, 1, 2);
812 get_color(argv[0], argv[1]);
813 }
814 else if (actions == ACTION_GET_COLORBOOL) {
815 check_argc(argc, 1, 2);
816 if (argc == 2)
817 color_stdout_is_tty = git_config_bool("command line", argv[1]);
818 return get_colorbool(argv[0], argc == 2);
819 }
820
821 return 0;
822 }