Coverage Report

Created: 2024-09-08 06:23

/src/git/builtin/config.c
Line
Count
Source (jump to first uncovered line)
1
#include "builtin.h"
2
#include "abspath.h"
3
#include "config.h"
4
#include "color.h"
5
#include "editor.h"
6
#include "environment.h"
7
#include "repository.h"
8
#include "gettext.h"
9
#include "ident.h"
10
#include "parse-options.h"
11
#include "urlmatch.h"
12
#include "path.h"
13
#include "quote.h"
14
#include "setup.h"
15
#include "strbuf.h"
16
#include "worktree.h"
17
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] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
21
  N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
22
  N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
23
  N_("git config rename-section [<file-option>] <old-name> <new-name>"),
24
  N_("git config remove-section [<file-option>] <name>"),
25
  N_("git config edit [<file-option>]"),
26
  N_("git config [<file-option>] --get-colorbool <name> [<stdout-is-tty>]"),
27
  NULL
28
};
29
30
static const char *const builtin_config_list_usage[] = {
31
  N_("git config list [<file-option>] [<display-option>] [--includes]"),
32
  NULL
33
};
34
35
static const char *const builtin_config_get_usage[] = {
36
  N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
37
  NULL
38
};
39
40
static const char *const builtin_config_set_usage[] = {
41
  N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
42
  NULL
43
};
44
45
static const char *const builtin_config_unset_usage[] = {
46
  N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
47
  NULL
48
};
49
50
static const char *const builtin_config_rename_section_usage[] = {
51
  N_("git config rename-section [<file-option>] <old-name> <new-name>"),
52
  NULL
53
};
54
55
static const char *const builtin_config_remove_section_usage[] = {
56
  N_("git config remove-section [<file-option>] <name>"),
57
  NULL
58
};
59
60
static const char *const builtin_config_edit_usage[] = {
61
  N_("git config edit [<file-option>]"),
62
  NULL
63
};
64
65
#define CONFIG_LOCATION_OPTIONS(opts) \
66
0
  OPT_GROUP(N_("Config file location")), \
67
0
  OPT_BOOL(0, "global", &opts.use_global_config, N_("use global config file")), \
68
0
  OPT_BOOL(0, "system", &opts.use_system_config, N_("use system config file")), \
69
0
  OPT_BOOL(0, "local", &opts.use_local_config, N_("use repository config file")), \
70
0
  OPT_BOOL(0, "worktree", &opts.use_worktree_config, N_("use per-worktree config file")), \
71
0
  OPT_STRING('f', "file", &opts.source.file, N_("file"), N_("use given config file")), \
72
0
  OPT_STRING(0, "blob", &opts.source.blob, N_("blob-id"), N_("read config from given blob object"))
73
74
struct config_location_options {
75
  struct git_config_source source;
76
  struct config_options options;
77
  char *file_to_free;
78
  int use_global_config;
79
  int use_system_config;
80
  int use_local_config;
81
  int use_worktree_config;
82
  int respect_includes_opt;
83
};
84
0
#define CONFIG_LOCATION_OPTIONS_INIT { \
85
0
  .respect_includes_opt = -1, \
86
0
}
87
88
#define CONFIG_TYPE_OPTIONS(type) \
89
0
  OPT_GROUP(N_("Type")), \
90
0
  OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
91
0
  OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), \
92
0
  OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), \
93
0
  OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), \
94
0
  OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), \
95
0
  OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), \
96
0
  OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE)
97
98
#define CONFIG_DISPLAY_OPTIONS(opts) \
99
0
  OPT_GROUP(N_("Display options")), \
100
0
  OPT_BOOL('z', "null", &opts.end_nul, N_("terminate values with NUL byte")), \
101
0
  OPT_BOOL(0, "name-only", &opts.omit_values, N_("show variable names only")), \
102
0
  OPT_BOOL(0, "show-origin", &opts.show_origin, N_("show origin of config (file, standard input, blob, command line)")), \
103
0
  OPT_BOOL(0, "show-scope", &opts.show_scope, N_("show scope of config (worktree, local, global, system, command)")), \
104
0
  OPT_BOOL(0, "show-names", &opts.show_keys, N_("show config keys in addition to their values")), \
105
0
  CONFIG_TYPE_OPTIONS(opts.type)
106
107
struct config_display_options {
108
  int end_nul;
109
  int omit_values;
110
  int show_origin;
111
  int show_scope;
112
  int show_keys;
113
  int type;
114
  char *default_value;
115
  /* Populated via `display_options_init()`. */
116
  int term;
117
  int delim;
118
  int key_delim;
119
};
120
0
#define CONFIG_DISPLAY_OPTIONS_INIT { \
121
0
  .term = '\n', \
122
0
  .delim = '=', \
123
0
  .key_delim = ' ', \
124
0
}
125
126
0
#define TYPE_BOOL   1
127
0
#define TYPE_INT    2
128
0
#define TYPE_BOOL_OR_INT  3
129
0
#define TYPE_PATH   4
130
0
#define TYPE_EXPIRY_DATE  5
131
0
#define TYPE_COLOR    6
132
0
#define TYPE_BOOL_OR_STR  7
133
134
#define OPT_CALLBACK_VALUE(s, l, v, h, i) \
135
0
  { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
136
0
  PARSE_OPT_NONEG, option_parse_type, (i) }
137
138
static int option_parse_type(const struct option *opt, const char *arg,
139
           int unset)
140
0
{
141
0
  int new_type, *to_type;
142
143
0
  if (unset) {
144
0
    *((int *) opt->value) = 0;
145
0
    return 0;
146
0
  }
147
148
  /*
149
   * To support '--<type>' style flags, begin with new_type equal to
150
   * opt->defval.
151
   */
152
0
  new_type = opt->defval;
153
0
  if (!new_type) {
154
0
    if (!strcmp(arg, "bool"))
155
0
      new_type = TYPE_BOOL;
156
0
    else if (!strcmp(arg, "int"))
157
0
      new_type = TYPE_INT;
158
0
    else if (!strcmp(arg, "bool-or-int"))
159
0
      new_type = TYPE_BOOL_OR_INT;
160
0
    else if (!strcmp(arg, "bool-or-str"))
161
0
      new_type = TYPE_BOOL_OR_STR;
162
0
    else if (!strcmp(arg, "path"))
163
0
      new_type = TYPE_PATH;
164
0
    else if (!strcmp(arg, "expiry-date"))
165
0
      new_type = TYPE_EXPIRY_DATE;
166
0
    else if (!strcmp(arg, "color"))
167
0
      new_type = TYPE_COLOR;
168
0
    else
169
0
      die(_("unrecognized --type argument, %s"), arg);
170
0
  }
171
172
0
  to_type = opt->value;
173
0
  if (*to_type && *to_type != new_type) {
174
    /*
175
     * Complain when there is a new type not equal to the old type.
176
     * This allows for combinations like '--int --type=int' and
177
     * '--type=int --type=int', but disallows ones like '--type=bool
178
     * --int' and '--type=bool
179
     * --type=int'.
180
     */
181
0
    error(_("only one type at a time"));
182
0
    exit(129);
183
0
  }
184
0
  *to_type = new_type;
185
186
0
  return 0;
187
0
}
188
189
static void check_argc(int argc, int min, int max)
190
0
{
191
0
  if (argc >= min && argc <= max)
192
0
    return;
193
0
  if (min == max)
194
0
    error(_("wrong number of arguments, should be %d"), min);
195
0
  else
196
0
    error(_("wrong number of arguments, should be from %d to %d"),
197
0
          min, max);
198
0
  exit(129);
199
0
}
200
201
static void show_config_origin(const struct config_display_options *opts,
202
             const struct key_value_info *kvi,
203
             struct strbuf *buf)
204
0
{
205
0
  const char term = opts->end_nul ? '\0' : '\t';
206
207
0
  strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
208
0
  strbuf_addch(buf, ':');
209
0
  if (opts->end_nul)
210
0
    strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
211
0
  else
212
0
    quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
213
0
  strbuf_addch(buf, term);
214
0
}
215
216
static void show_config_scope(const struct config_display_options *opts,
217
            const struct key_value_info *kvi,
218
            struct strbuf *buf)
219
0
{
220
0
  const char term = opts->end_nul ? '\0' : '\t';
221
0
  const char *scope = config_scope_name(kvi->scope);
222
223
0
  strbuf_addstr(buf, N_(scope));
224
0
  strbuf_addch(buf, term);
225
0
}
226
227
static int show_all_config(const char *key_, const char *value_,
228
         const struct config_context *ctx,
229
         void *cb)
230
0
{
231
0
  const struct config_display_options *opts = cb;
232
0
  const struct key_value_info *kvi = ctx->kvi;
233
234
0
  if (opts->show_origin || opts->show_scope) {
235
0
    struct strbuf buf = STRBUF_INIT;
236
0
    if (opts->show_scope)
237
0
      show_config_scope(opts, kvi, &buf);
238
0
    if (opts->show_origin)
239
0
      show_config_origin(opts, kvi, &buf);
240
    /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
241
0
    fwrite(buf.buf, 1, buf.len, stdout);
242
0
    strbuf_release(&buf);
243
0
  }
244
0
  if (!opts->omit_values && value_)
245
0
    printf("%s%c%s%c", key_, opts->delim, value_, opts->term);
246
0
  else
247
0
    printf("%s%c", key_, opts->term);
248
0
  return 0;
249
0
}
250
251
struct strbuf_list {
252
  struct strbuf *items;
253
  int nr;
254
  int alloc;
255
};
256
257
static int format_config(const struct config_display_options *opts,
258
       struct strbuf *buf, const char *key_,
259
       const char *value_, const struct key_value_info *kvi)
260
0
{
261
0
  if (opts->show_scope)
262
0
    show_config_scope(opts, kvi, buf);
263
0
  if (opts->show_origin)
264
0
    show_config_origin(opts, kvi, buf);
265
0
  if (opts->show_keys)
266
0
    strbuf_addstr(buf, key_);
267
0
  if (!opts->omit_values) {
268
0
    if (opts->show_keys)
269
0
      strbuf_addch(buf, opts->key_delim);
270
271
0
    if (opts->type == TYPE_INT)
272
0
      strbuf_addf(buf, "%"PRId64,
273
0
            git_config_int64(key_, value_ ? value_ : "", kvi));
274
0
    else if (opts->type == TYPE_BOOL)
275
0
      strbuf_addstr(buf, git_config_bool(key_, value_) ?
276
0
              "true" : "false");
277
0
    else if (opts->type == TYPE_BOOL_OR_INT) {
278
0
      int is_bool, v;
279
0
      v = git_config_bool_or_int(key_, value_, kvi,
280
0
               &is_bool);
281
0
      if (is_bool)
282
0
        strbuf_addstr(buf, v ? "true" : "false");
283
0
      else
284
0
        strbuf_addf(buf, "%d", v);
285
0
    } else if (opts->type == TYPE_BOOL_OR_STR) {
286
0
      int v = git_parse_maybe_bool(value_);
287
0
      if (v < 0)
288
0
        strbuf_addstr(buf, value_);
289
0
      else
290
0
        strbuf_addstr(buf, v ? "true" : "false");
291
0
    } else if (opts->type == TYPE_PATH) {
292
0
      char *v;
293
0
      if (git_config_pathname(&v, key_, value_) < 0)
294
0
        return -1;
295
0
      strbuf_addstr(buf, v);
296
0
      free((char *)v);
297
0
    } else if (opts->type == TYPE_EXPIRY_DATE) {
298
0
      timestamp_t t;
299
0
      if (git_config_expiry_date(&t, key_, value_) < 0)
300
0
        return -1;
301
0
      strbuf_addf(buf, "%"PRItime, t);
302
0
    } else if (opts->type == TYPE_COLOR) {
303
0
      char v[COLOR_MAXLEN];
304
0
      if (git_config_color(v, key_, value_) < 0)
305
0
        return -1;
306
0
      strbuf_addstr(buf, v);
307
0
    } else if (value_) {
308
0
      strbuf_addstr(buf, value_);
309
0
    } else {
310
      /* Just show the key name; back out delimiter */
311
0
      if (opts->show_keys)
312
0
        strbuf_setlen(buf, buf->len - 1);
313
0
    }
314
0
  }
315
0
  strbuf_addch(buf, opts->term);
316
0
  return 0;
317
0
}
318
319
0
#define GET_VALUE_ALL        (1 << 0)
320
0
#define GET_VALUE_KEY_REGEXP (1 << 1)
321
322
struct collect_config_data {
323
  const struct config_display_options *display_opts;
324
  struct strbuf_list *values;
325
  const char *value_pattern;
326
  const char *key;
327
  regex_t *regexp;
328
  regex_t *key_regexp;
329
  int do_not_match;
330
  unsigned get_value_flags;
331
  unsigned flags;
332
};
333
334
static int collect_config(const char *key_, const char *value_,
335
        const struct config_context *ctx, void *cb)
336
0
{
337
0
  struct collect_config_data *data = cb;
338
0
  struct strbuf_list *values = data->values;
339
0
  const struct key_value_info *kvi = ctx->kvi;
340
341
0
  if (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
342
0
      strcmp(key_, data->key))
343
0
    return 0;
344
0
  if ((data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
345
0
      regexec(data->key_regexp, key_, 0, NULL, 0))
346
0
    return 0;
347
0
  if ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&
348
0
      strcmp(data->value_pattern, (value_?value_:"")))
349
0
    return 0;
350
0
  if (data->regexp &&
351
0
      (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:""), 0, NULL, 0)))
352
0
    return 0;
353
354
0
  ALLOC_GROW(values->items, values->nr + 1, values->alloc);
355
0
  strbuf_init(&values->items[values->nr], 0);
356
357
0
  return format_config(data->display_opts, &values->items[values->nr++],
358
0
           key_, value_, kvi);
359
0
}
360
361
static int get_value(const struct config_location_options *opts,
362
         const struct config_display_options *display_opts,
363
         const char *key_, const char *regex_,
364
         unsigned get_value_flags, unsigned flags)
365
0
{
366
0
  int ret = CONFIG_GENERIC_ERROR;
367
0
  struct strbuf_list values = {NULL};
368
0
  struct collect_config_data data = {
369
0
    .display_opts = display_opts,
370
0
    .values = &values,
371
0
    .get_value_flags = get_value_flags,
372
0
    .flags = flags,
373
0
  };
374
0
  char *key = NULL;
375
0
  int i;
376
377
0
  if (get_value_flags & GET_VALUE_KEY_REGEXP) {
378
0
    char *tl;
379
380
    /*
381
     * NEEDSWORK: this naive pattern lowercasing obviously does not
382
     * work for more complex patterns like "^[^.]*Foo.*bar".
383
     * Perhaps we should deprecate this altogether someday.
384
     */
385
386
0
    key = xstrdup(key_);
387
0
    for (tl = key + strlen(key) - 1;
388
0
         tl >= key && *tl != '.';
389
0
         tl--)
390
0
      *tl = tolower(*tl);
391
0
    for (tl = key; *tl && *tl != '.'; tl++)
392
0
      *tl = tolower(*tl);
393
394
0
    data.key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
395
0
    if (regcomp(data.key_regexp, key, REG_EXTENDED)) {
396
0
      error(_("invalid key pattern: %s"), key_);
397
0
      FREE_AND_NULL(data.key_regexp);
398
0
      ret = CONFIG_INVALID_PATTERN;
399
0
      goto free_strings;
400
0
    }
401
0
  } else {
402
0
    if (git_config_parse_key(key_, &key, NULL)) {
403
0
      ret = CONFIG_INVALID_KEY;
404
0
      goto free_strings;
405
0
    }
406
407
0
    data.key = key;
408
0
  }
409
410
0
  if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
411
0
    data.value_pattern = regex_;
412
0
  else if (regex_) {
413
0
    if (regex_[0] == '!') {
414
0
      data.do_not_match = 1;
415
0
      regex_++;
416
0
    }
417
418
0
    data.regexp = (regex_t*)xmalloc(sizeof(regex_t));
419
0
    if (regcomp(data.regexp, regex_, REG_EXTENDED)) {
420
0
      error(_("invalid pattern: %s"), regex_);
421
0
      FREE_AND_NULL(data.regexp);
422
0
      ret = CONFIG_INVALID_PATTERN;
423
0
      goto free_strings;
424
0
    }
425
0
  }
426
427
0
  config_with_options(collect_config, &data,
428
0
          &opts->source, the_repository,
429
0
          &opts->options);
430
431
0
  if (!values.nr && display_opts->default_value) {
432
0
    struct key_value_info kvi = KVI_INIT;
433
0
    struct strbuf *item;
434
435
0
    kvi_from_param(&kvi);
436
0
    ALLOC_GROW(values.items, values.nr + 1, values.alloc);
437
0
    item = &values.items[values.nr++];
438
0
    strbuf_init(item, 0);
439
0
    if (format_config(display_opts, item, key_,
440
0
          display_opts->default_value, &kvi) < 0)
441
0
      die(_("failed to format default config value: %s"),
442
0
          display_opts->default_value);
443
0
  }
444
445
0
  ret = !values.nr;
446
447
0
  for (i = 0; i < values.nr; i++) {
448
0
    struct strbuf *buf = values.items + i;
449
0
    if ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1)
450
0
      fwrite(buf->buf, 1, buf->len, stdout);
451
0
    strbuf_release(buf);
452
0
  }
453
0
  free(values.items);
454
455
0
free_strings:
456
0
  free(key);
457
0
  if (data.key_regexp) {
458
0
    regfree(data.key_regexp);
459
0
    free(data.key_regexp);
460
0
  }
461
0
  if (data.regexp) {
462
0
    regfree(data.regexp);
463
0
    free(data.regexp);
464
0
  }
465
466
0
  return ret;
467
0
}
468
469
static char *normalize_value(const char *key, const char *value,
470
           int type, struct key_value_info *kvi)
471
0
{
472
0
  if (!value)
473
0
    return NULL;
474
475
0
  if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)
476
    /*
477
     * We don't do normalization for TYPE_PATH here: If
478
     * the path is like ~/foobar/, we prefer to store
479
     * "~/foobar/" in the config file, and to expand the ~
480
     * when retrieving the value.
481
     * Also don't do normalization for expiry dates.
482
     */
483
0
    return xstrdup(value);
484
0
  if (type == TYPE_INT)
485
0
    return xstrfmt("%"PRId64, git_config_int64(key, value, kvi));
486
0
  if (type == TYPE_BOOL)
487
0
    return xstrdup(git_config_bool(key, value) ?  "true" : "false");
488
0
  if (type == TYPE_BOOL_OR_INT) {
489
0
    int is_bool, v;
490
0
    v = git_config_bool_or_int(key, value, kvi, &is_bool);
491
0
    if (!is_bool)
492
0
      return xstrfmt("%d", v);
493
0
    else
494
0
      return xstrdup(v ? "true" : "false");
495
0
  }
496
0
  if (type == TYPE_BOOL_OR_STR) {
497
0
    int v = git_parse_maybe_bool(value);
498
0
    if (v < 0)
499
0
      return xstrdup(value);
500
0
    else
501
0
      return xstrdup(v ? "true" : "false");
502
0
  }
503
0
  if (type == TYPE_COLOR) {
504
0
    char v[COLOR_MAXLEN];
505
0
    if (git_config_color(v, key, value))
506
0
      die(_("cannot parse color '%s'"), value);
507
508
    /*
509
     * The contents of `v` now contain an ANSI escape
510
     * sequence, not suitable for including within a
511
     * configuration file. Treat the above as a
512
     * "sanity-check", and return the given value, which we
513
     * know is representable as valid color code.
514
     */
515
0
    return xstrdup(value);
516
0
  }
517
518
0
  BUG("cannot normalize type %d", type);
519
0
}
520
521
struct get_color_config_data {
522
  int get_color_found;
523
  const char *get_color_slot;
524
  char parsed_color[COLOR_MAXLEN];
525
};
526
527
static int git_get_color_config(const char *var, const char *value,
528
        const struct config_context *ctx UNUSED,
529
        void *cb)
530
0
{
531
0
  struct get_color_config_data *data = cb;
532
533
0
  if (!strcmp(var, data->get_color_slot)) {
534
0
    if (!value)
535
0
      config_error_nonbool(var);
536
0
    if (color_parse(value, data->parsed_color) < 0)
537
0
      return -1;
538
0
    data->get_color_found = 1;
539
0
  }
540
0
  return 0;
541
0
}
542
543
static void get_color(const struct config_location_options *opts,
544
          const char *var, const char *def_color)
545
0
{
546
0
  struct get_color_config_data data = {
547
0
    .get_color_slot = var,
548
0
    .parsed_color[0] = '\0',
549
0
  };
550
551
0
  config_with_options(git_get_color_config, &data,
552
0
          &opts->source, the_repository,
553
0
          &opts->options);
554
555
0
  if (!data.get_color_found && def_color) {
556
0
    if (color_parse(def_color, data.parsed_color) < 0)
557
0
      die(_("unable to parse default color value"));
558
0
  }
559
560
0
  fputs(data.parsed_color, stdout);
561
0
}
562
563
struct get_colorbool_config_data {
564
  int get_colorbool_found;
565
  int get_diff_color_found;
566
  int get_color_ui_found;
567
  const char *get_colorbool_slot;
568
};
569
570
static int git_get_colorbool_config(const char *var, const char *value,
571
            const struct config_context *ctx UNUSED,
572
            void *cb)
573
0
{
574
0
  struct get_colorbool_config_data *data = cb;
575
576
0
  if (!strcmp(var, data->get_colorbool_slot))
577
0
    data->get_colorbool_found = git_config_colorbool(var, value);
578
0
  else if (!strcmp(var, "diff.color"))
579
0
    data->get_diff_color_found = git_config_colorbool(var, value);
580
0
  else if (!strcmp(var, "color.ui"))
581
0
    data->get_color_ui_found = git_config_colorbool(var, value);
582
0
  return 0;
583
0
}
584
585
static int get_colorbool(const struct config_location_options *opts,
586
       const char *var, int print)
587
0
{
588
0
  struct get_colorbool_config_data data = {
589
0
    .get_colorbool_slot = var,
590
0
    .get_colorbool_found = -1,
591
0
    .get_diff_color_found = -1,
592
0
    .get_color_ui_found = -1,
593
0
  };
594
595
0
  config_with_options(git_get_colorbool_config, &data,
596
0
          &opts->source, the_repository,
597
0
          &opts->options);
598
599
0
  if (data.get_colorbool_found < 0) {
600
0
    if (!strcmp(data.get_colorbool_slot, "color.diff"))
601
0
      data.get_colorbool_found = data.get_diff_color_found;
602
0
    if (data.get_colorbool_found < 0)
603
0
      data.get_colorbool_found = data.get_color_ui_found;
604
0
  }
605
606
0
  if (data.get_colorbool_found < 0)
607
    /* default value if none found in config */
608
0
    data.get_colorbool_found = GIT_COLOR_AUTO;
609
610
0
  data.get_colorbool_found = want_color(data.get_colorbool_found);
611
612
0
  if (print) {
613
0
    printf("%s\n", data.get_colorbool_found ? "true" : "false");
614
0
    return 0;
615
0
  } else
616
0
    return data.get_colorbool_found ? 0 : 1;
617
0
}
618
619
static void check_write(const struct git_config_source *source)
620
0
{
621
0
  if (!source->file && !startup_info->have_repository)
622
0
    die(_("not in a git directory"));
623
624
0
  if (source->use_stdin)
625
0
    die(_("writing to stdin is not supported"));
626
627
0
  if (source->blob)
628
0
    die(_("writing config blobs is not supported"));
629
0
}
630
631
struct urlmatch_current_candidate_value {
632
  char value_is_null;
633
  struct strbuf value;
634
  struct key_value_info kvi;
635
};
636
637
static int urlmatch_collect_fn(const char *var, const char *value,
638
             const struct config_context *ctx,
639
             void *cb)
640
0
{
641
0
  struct string_list *values = cb;
642
0
  struct string_list_item *item = string_list_insert(values, var);
643
0
  struct urlmatch_current_candidate_value *matched = item->util;
644
0
  const struct key_value_info *kvi = ctx->kvi;
645
646
0
  if (!matched) {
647
0
    matched = xmalloc(sizeof(*matched));
648
0
    strbuf_init(&matched->value, 0);
649
0
    item->util = matched;
650
0
  } else {
651
0
    strbuf_reset(&matched->value);
652
0
  }
653
0
  matched->kvi = *kvi;
654
655
0
  if (value) {
656
0
    strbuf_addstr(&matched->value, value);
657
0
    matched->value_is_null = 0;
658
0
  } else {
659
0
    matched->value_is_null = 1;
660
0
  }
661
0
  return 0;
662
0
}
663
664
static int get_urlmatch(const struct config_location_options *opts,
665
      const struct config_display_options *_display_opts,
666
      const char *var, const char *url)
667
0
{
668
0
  int ret;
669
0
  char *section_tail;
670
0
  struct config_display_options display_opts = *_display_opts;
671
0
  struct string_list_item *item;
672
0
  struct urlmatch_config config = URLMATCH_CONFIG_INIT;
673
0
  struct string_list values = STRING_LIST_INIT_DUP;
674
675
0
  config.collect_fn = urlmatch_collect_fn;
676
0
  config.cascade_fn = NULL;
677
0
  config.cb = &values;
678
679
0
  if (!url_normalize(url, &config.url))
680
0
    die("%s", config.url.err);
681
682
0
  config.section = xstrdup_tolower(var);
683
0
  section_tail = strchr(config.section, '.');
684
0
  if (section_tail) {
685
0
    *section_tail = '\0';
686
0
    config.key = section_tail + 1;
687
0
    display_opts.show_keys = 0;
688
0
  } else {
689
0
    config.key = NULL;
690
0
    display_opts.show_keys = 1;
691
0
  }
692
693
0
  config_with_options(urlmatch_config_entry, &config,
694
0
          &opts->source, the_repository,
695
0
          &opts->options);
696
697
0
  ret = !values.nr;
698
699
0
  for_each_string_list_item(item, &values) {
700
0
    struct urlmatch_current_candidate_value *matched = item->util;
701
0
    struct strbuf buf = STRBUF_INIT;
702
703
0
    format_config(&display_opts, &buf, item->string,
704
0
            matched->value_is_null ? NULL : matched->value.buf,
705
0
            &matched->kvi);
706
0
    fwrite(buf.buf, 1, buf.len, stdout);
707
0
    strbuf_release(&buf);
708
709
0
    strbuf_release(&matched->value);
710
0
  }
711
0
  urlmatch_config_release(&config);
712
0
  string_list_clear(&values, 1);
713
0
  free(config.url.url);
714
715
0
  free((void *)config.section);
716
0
  return ret;
717
0
}
718
719
static char *default_user_config(void)
720
0
{
721
0
  struct strbuf buf = STRBUF_INIT;
722
0
  strbuf_addf(&buf,
723
0
        _("# This is Git's per-user configuration file.\n"
724
0
          "[user]\n"
725
0
          "# Please adapt and uncomment the following lines:\n"
726
0
          "#  name = %s\n"
727
0
          "#  email = %s\n"),
728
0
        ident_default_name(),
729
0
        ident_default_email());
730
0
  return strbuf_detach(&buf, NULL);
731
0
}
732
733
static void location_options_init(struct config_location_options *opts,
734
          const char *prefix)
735
0
{
736
0
  if (!opts->source.file)
737
0
    opts->source.file = opts->file_to_free =
738
0
      xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
739
740
0
  if (opts->use_global_config + opts->use_system_config +
741
0
      opts->use_local_config + opts->use_worktree_config +
742
0
      !!opts->source.file + !!opts->source.blob > 1) {
743
0
    error(_("only one config file at a time"));
744
0
    exit(129);
745
0
  }
746
747
0
  if (!startup_info->have_repository) {
748
0
    if (opts->use_local_config)
749
0
      die(_("--local can only be used inside a git repository"));
750
0
    if (opts->source.blob)
751
0
      die(_("--blob can only be used inside a git repository"));
752
0
    if (opts->use_worktree_config)
753
0
      die(_("--worktree can only be used inside a git repository"));
754
0
  }
755
756
0
  if (opts->source.file &&
757
0
      !strcmp(opts->source.file, "-")) {
758
0
    opts->source.file = NULL;
759
0
    opts->source.use_stdin = 1;
760
0
    opts->source.scope = CONFIG_SCOPE_COMMAND;
761
0
  }
762
763
0
  if (opts->use_global_config) {
764
0
    opts->source.file = opts->file_to_free = git_global_config();
765
0
    if (!opts->source.file)
766
      /*
767
       * It is unknown if HOME/.gitconfig exists, so
768
       * we do not know if we should write to XDG
769
       * location; error out even if XDG_CONFIG_HOME
770
       * is set and points at a sane location.
771
       */
772
0
      die(_("$HOME not set"));
773
0
    opts->source.scope = CONFIG_SCOPE_GLOBAL;
774
0
  } else if (opts->use_system_config) {
775
0
    opts->source.file = opts->file_to_free = git_system_config();
776
0
    opts->source.scope = CONFIG_SCOPE_SYSTEM;
777
0
  } else if (opts->use_local_config) {
778
0
    opts->source.file = opts->file_to_free = git_pathdup("config");
779
0
    opts->source.scope = CONFIG_SCOPE_LOCAL;
780
0
  } else if (opts->use_worktree_config) {
781
0
    struct worktree **worktrees = get_worktrees();
782
0
    if (the_repository->repository_format_worktree_config)
783
0
      opts->source.file = opts->file_to_free =
784
0
        git_pathdup("config.worktree");
785
0
    else if (worktrees[0] && worktrees[1])
786
0
      die(_("--worktree cannot be used with multiple "
787
0
            "working trees unless the config\n"
788
0
            "extension worktreeConfig is enabled. "
789
0
            "Please read \"CONFIGURATION FILE\"\n"
790
0
            "section in \"git help worktree\" for details"));
791
0
    else
792
0
      opts->source.file = opts->file_to_free =
793
0
        git_pathdup("config");
794
0
    opts->source.scope = CONFIG_SCOPE_LOCAL;
795
0
    free_worktrees(worktrees);
796
0
  } else if (opts->source.file) {
797
0
    if (!is_absolute_path(opts->source.file) && prefix)
798
0
      opts->source.file = opts->file_to_free =
799
0
        prefix_filename(prefix, opts->source.file);
800
0
    opts->source.scope = CONFIG_SCOPE_COMMAND;
801
0
  } else if (opts->source.blob) {
802
0
    opts->source.scope = CONFIG_SCOPE_COMMAND;
803
0
  }
804
805
0
  if (opts->respect_includes_opt == -1)
806
0
    opts->options.respect_includes = !opts->source.file;
807
0
  else
808
0
    opts->options.respect_includes = opts->respect_includes_opt;
809
0
  if (startup_info->have_repository) {
810
0
    opts->options.commondir = get_git_common_dir();
811
0
    opts->options.git_dir = get_git_dir();
812
0
  }
813
0
}
814
815
static void location_options_release(struct config_location_options *opts)
816
0
{
817
0
  free(opts->file_to_free);
818
0
}
819
820
static void display_options_init(struct config_display_options *opts)
821
0
{
822
0
  if (opts->end_nul) {
823
0
    opts->term = '\0';
824
0
    opts->delim = '\n';
825
0
    opts->key_delim = '\n';
826
0
  }
827
0
}
828
829
static int cmd_config_list(int argc, const char **argv, const char *prefix)
830
0
{
831
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
832
0
  struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
833
0
  struct option opts[] = {
834
0
    CONFIG_LOCATION_OPTIONS(location_opts),
835
0
    CONFIG_DISPLAY_OPTIONS(display_opts),
836
0
    OPT_GROUP(N_("Other")),
837
0
    OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
838
0
       N_("respect include directives on lookup")),
839
0
    OPT_END(),
840
0
  };
841
842
0
  argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);
843
0
  check_argc(argc, 0, 0);
844
845
0
  location_options_init(&location_opts, prefix);
846
0
  display_options_init(&display_opts);
847
848
0
  setup_auto_pager("config", 1);
849
850
0
  if (config_with_options(show_all_config, &display_opts,
851
0
        &location_opts.source, the_repository,
852
0
        &location_opts.options) < 0) {
853
0
    if (location_opts.source.file)
854
0
      die_errno(_("unable to read config file '%s'"),
855
0
          location_opts.source.file);
856
0
    else
857
0
      die(_("error processing config file(s)"));
858
0
  }
859
860
0
  location_options_release(&location_opts);
861
0
  return 0;
862
0
}
863
864
static int cmd_config_get(int argc, const char **argv, const char *prefix)
865
0
{
866
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
867
0
  struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
868
0
  const char *value_pattern = NULL, *url = NULL;
869
0
  int flags = 0;
870
0
  unsigned get_value_flags = 0;
871
0
  struct option opts[] = {
872
0
    CONFIG_LOCATION_OPTIONS(location_opts),
873
0
    OPT_GROUP(N_("Filter options")),
874
0
    OPT_BIT(0, "all", &get_value_flags, N_("return all values for multi-valued config options"), GET_VALUE_ALL),
875
0
    OPT_BIT(0, "regexp", &get_value_flags, N_("interpret the name as a regular expression"), GET_VALUE_KEY_REGEXP),
876
0
    OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
877
0
    OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
878
0
    OPT_STRING(0, "url", &url, N_("URL"), N_("show config matching the given URL")),
879
0
    CONFIG_DISPLAY_OPTIONS(display_opts),
880
0
    OPT_GROUP(N_("Other")),
881
0
    OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
882
0
       N_("respect include directives on lookup")),
883
0
    OPT_STRING(0, "default", &display_opts.default_value,
884
0
         N_("value"), N_("use default value when missing entry")),
885
0
    OPT_END(),
886
0
  };
887
0
  int ret;
888
889
0
  argc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,
890
0
           PARSE_OPT_STOP_AT_NON_OPTION);
891
0
  check_argc(argc, 1, 1);
892
893
0
  if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
894
0
    die(_("--fixed-value only applies with 'value-pattern'"));
895
0
  if (display_opts.default_value &&
896
0
      ((get_value_flags & GET_VALUE_ALL) || url))
897
0
    die(_("--default= cannot be used with --all or --url="));
898
0
  if (url && ((get_value_flags & GET_VALUE_ALL) ||
899
0
        (get_value_flags & GET_VALUE_KEY_REGEXP) ||
900
0
        value_pattern))
901
0
    die(_("--url= cannot be used with --all, --regexp or --value"));
902
903
0
  location_options_init(&location_opts, prefix);
904
0
  display_options_init(&display_opts);
905
906
0
  setup_auto_pager("config", 1);
907
908
0
  if (url)
909
0
    ret = get_urlmatch(&location_opts, &display_opts, argv[0], url);
910
0
  else
911
0
    ret = get_value(&location_opts, &display_opts, argv[0], value_pattern,
912
0
        get_value_flags, flags);
913
914
0
  location_options_release(&location_opts);
915
0
  return ret;
916
0
}
917
918
static int cmd_config_set(int argc, const char **argv, const char *prefix)
919
0
{
920
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
921
0
  const char *value_pattern = NULL, *comment_arg = NULL;
922
0
  char *comment = NULL;
923
0
  int flags = 0, append = 0, type = 0;
924
0
  struct option opts[] = {
925
0
    CONFIG_LOCATION_OPTIONS(location_opts),
926
0
    CONFIG_TYPE_OPTIONS(type),
927
0
    OPT_GROUP(N_("Filter")),
928
0
    OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
929
0
    OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
930
0
    OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
931
0
    OPT_GROUP(N_("Other")),
932
0
    OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
933
0
    OPT_BOOL(0, "append", &append, N_("add a new line without altering any existing values")),
934
0
    OPT_END(),
935
0
  };
936
0
  struct key_value_info default_kvi = KVI_INIT;
937
0
  char *value;
938
0
  int ret;
939
940
0
  argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,
941
0
           PARSE_OPT_STOP_AT_NON_OPTION);
942
0
  check_argc(argc, 2, 2);
943
944
0
  if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
945
0
    die(_("--fixed-value only applies with --value=<pattern>"));
946
0
  if (append && value_pattern)
947
0
    die(_("--append cannot be used with --value=<pattern>"));
948
0
  if (append)
949
0
    value_pattern = CONFIG_REGEX_NONE;
950
951
0
  comment = git_config_prepare_comment_string(comment_arg);
952
953
0
  location_options_init(&location_opts, prefix);
954
0
  check_write(&location_opts.source);
955
956
0
  value = normalize_value(argv[0], argv[1], type, &default_kvi);
957
958
0
  if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {
959
0
    ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
960
0
                   argv[0], value, value_pattern,
961
0
                   comment, flags);
962
0
  } else {
963
0
    ret = git_config_set_in_file_gently(location_opts.source.file,
964
0
                argv[0], comment, value);
965
0
    if (ret == CONFIG_NOTHING_SET)
966
0
      error(_("cannot overwrite multiple values with a single value\n"
967
0
      "       Use a regexp, --add or --replace-all to change %s."), argv[0]);
968
0
  }
969
970
0
  location_options_release(&location_opts);
971
0
  free(comment);
972
0
  free(value);
973
0
  return ret;
974
0
}
975
976
static int cmd_config_unset(int argc, const char **argv, const char *prefix)
977
0
{
978
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
979
0
  const char *value_pattern = NULL;
980
0
  int flags = 0;
981
0
  struct option opts[] = {
982
0
    CONFIG_LOCATION_OPTIONS(location_opts),
983
0
    OPT_GROUP(N_("Filter")),
984
0
    OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
985
0
    OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
986
0
    OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
987
0
    OPT_END(),
988
0
  };
989
0
  int ret;
990
991
0
  argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,
992
0
           PARSE_OPT_STOP_AT_NON_OPTION);
993
0
  check_argc(argc, 1, 1);
994
995
0
  if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
996
0
    die(_("--fixed-value only applies with 'value-pattern'"));
997
998
0
  location_options_init(&location_opts, prefix);
999
0
  check_write(&location_opts.source);
1000
1001
0
  if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)
1002
0
    ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
1003
0
                   argv[0], NULL, value_pattern,
1004
0
                   NULL, flags);
1005
0
  else
1006
0
    ret = git_config_set_in_file_gently(location_opts.source.file, argv[0],
1007
0
                NULL, NULL);
1008
1009
0
  location_options_release(&location_opts);
1010
0
  return ret;
1011
0
}
1012
1013
static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)
1014
0
{
1015
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
1016
0
  struct option opts[] = {
1017
0
    CONFIG_LOCATION_OPTIONS(location_opts),
1018
0
    OPT_END(),
1019
0
  };
1020
0
  int ret;
1021
1022
0
  argc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,
1023
0
           PARSE_OPT_STOP_AT_NON_OPTION);
1024
0
  check_argc(argc, 2, 2);
1025
1026
0
  location_options_init(&location_opts, prefix);
1027
0
  check_write(&location_opts.source);
1028
1029
0
  ret = repo_config_rename_section_in_file(the_repository, location_opts.source.file,
1030
0
             argv[0], argv[1]);
1031
0
  if (ret < 0)
1032
0
    goto out;
1033
0
  else if (!ret)
1034
0
    die(_("no such section: %s"), argv[0]);
1035
0
  ret = 0;
1036
1037
0
out:
1038
0
  location_options_release(&location_opts);
1039
0
  return ret;
1040
0
}
1041
1042
static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)
1043
0
{
1044
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
1045
0
  struct option opts[] = {
1046
0
    CONFIG_LOCATION_OPTIONS(location_opts),
1047
0
    OPT_END(),
1048
0
  };
1049
0
  int ret;
1050
1051
0
  argc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,
1052
0
           PARSE_OPT_STOP_AT_NON_OPTION);
1053
0
  check_argc(argc, 1, 1);
1054
1055
0
  location_options_init(&location_opts, prefix);
1056
0
  check_write(&location_opts.source);
1057
1058
0
  ret = repo_config_rename_section_in_file(the_repository, location_opts.source.file,
1059
0
             argv[0], NULL);
1060
0
  if (ret < 0)
1061
0
    goto out;
1062
0
  else if (!ret)
1063
0
    die(_("no such section: %s"), argv[0]);
1064
0
  ret = 0;
1065
1066
0
out:
1067
0
  location_options_release(&location_opts);
1068
0
  return ret;
1069
0
}
1070
1071
static int show_editor(struct config_location_options *opts)
1072
0
{
1073
0
  char *config_file;
1074
1075
0
  if (!opts->source.file && !startup_info->have_repository)
1076
0
    die(_("not in a git directory"));
1077
0
  if (opts->source.use_stdin)
1078
0
    die(_("editing stdin is not supported"));
1079
0
  if (opts->source.blob)
1080
0
    die(_("editing blobs is not supported"));
1081
0
  git_config(git_default_config, NULL);
1082
0
  config_file = opts->source.file ?
1083
0
      xstrdup(opts->source.file) :
1084
0
      git_pathdup("config");
1085
0
  if (opts->use_global_config) {
1086
0
    int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
1087
0
    if (fd >= 0) {
1088
0
      char *content = default_user_config();
1089
0
      write_str_in_full(fd, content);
1090
0
      free(content);
1091
0
      close(fd);
1092
0
    }
1093
0
    else if (errno != EEXIST)
1094
0
      die_errno(_("cannot create configuration file %s"), config_file);
1095
0
  }
1096
0
  launch_editor(config_file, NULL, NULL);
1097
0
  free(config_file);
1098
1099
0
  return 0;
1100
0
}
1101
1102
static int cmd_config_edit(int argc, const char **argv, const char *prefix)
1103
0
{
1104
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
1105
0
  struct option opts[] = {
1106
0
    CONFIG_LOCATION_OPTIONS(location_opts),
1107
0
    OPT_END(),
1108
0
  };
1109
0
  int ret;
1110
1111
0
  argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);
1112
0
  check_argc(argc, 0, 0);
1113
1114
0
  location_options_init(&location_opts, prefix);
1115
0
  check_write(&location_opts.source);
1116
1117
0
  ret = show_editor(&location_opts);
1118
0
  location_options_release(&location_opts);
1119
0
  return ret;
1120
0
}
1121
1122
static int cmd_config_actions(int argc, const char **argv, const char *prefix)
1123
0
{
1124
0
  enum {
1125
0
    ACTION_GET = (1<<0),
1126
0
    ACTION_GET_ALL = (1<<1),
1127
0
    ACTION_GET_REGEXP = (1<<2),
1128
0
    ACTION_REPLACE_ALL = (1<<3),
1129
0
    ACTION_ADD = (1<<4),
1130
0
    ACTION_UNSET = (1<<5),
1131
0
    ACTION_UNSET_ALL = (1<<6),
1132
0
    ACTION_RENAME_SECTION = (1<<7),
1133
0
    ACTION_REMOVE_SECTION = (1<<8),
1134
0
    ACTION_LIST = (1<<9),
1135
0
    ACTION_EDIT = (1<<10),
1136
0
    ACTION_SET = (1<<11),
1137
0
    ACTION_SET_ALL = (1<<12),
1138
0
    ACTION_GET_COLOR = (1<<13),
1139
0
    ACTION_GET_COLORBOOL = (1<<14),
1140
0
    ACTION_GET_URLMATCH = (1<<15),
1141
0
  };
1142
0
  struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
1143
0
  struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
1144
0
  const char *comment_arg = NULL;
1145
0
  int actions = 0;
1146
0
  unsigned flags = 0;
1147
0
  struct option opts[] = {
1148
0
    CONFIG_LOCATION_OPTIONS(location_opts),
1149
0
    OPT_GROUP(N_("Action")),
1150
0
    OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
1151
0
    OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
1152
0
    OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP),
1153
0
    OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
1154
0
    OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL),
1155
0
    OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
1156
0
    OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET),
1157
0
    OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL),
1158
0
    OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
1159
0
    OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
1160
0
    OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
1161
0
    OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
1162
0
    OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR),
1163
0
    OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL),
1164
0
    CONFIG_DISPLAY_OPTIONS(display_opts),
1165
0
    OPT_GROUP(N_("Other")),
1166
0
    OPT_STRING(0, "default", &display_opts.default_value,
1167
0
         N_("value"), N_("with --get, use default value when missing entry")),
1168
0
    OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
1169
0
    OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
1170
0
    OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
1171
0
       N_("respect include directives on lookup")),
1172
0
    OPT_END(),
1173
0
  };
1174
0
  char *value = NULL, *comment = NULL;
1175
0
  int ret = 0;
1176
0
  struct key_value_info default_kvi = KVI_INIT;
1177
1178
0
  argc = parse_options(argc, argv, prefix, opts,
1179
0
           builtin_config_usage,
1180
0
           PARSE_OPT_STOP_AT_NON_OPTION);
1181
1182
0
  location_options_init(&location_opts, prefix);
1183
0
  display_options_init(&display_opts);
1184
1185
0
  if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) {
1186
0
    error(_("--get-color and variable type are incoherent"));
1187
0
    exit(129);
1188
0
  }
1189
1190
0
  if (actions == 0)
1191
0
    switch (argc) {
1192
0
    case 1: actions = ACTION_GET; break;
1193
0
    case 2: actions = ACTION_SET; break;
1194
0
    case 3: actions = ACTION_SET_ALL; break;
1195
0
    default:
1196
0
      error(_("no action specified"));
1197
0
      exit(129);
1198
0
    }
1199
0
  if (display_opts.omit_values &&
1200
0
      !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
1201
0
    error(_("--name-only is only applicable to --list or --get-regexp"));
1202
0
    exit(129);
1203
0
  }
1204
1205
0
  if (display_opts.show_origin && !(actions &
1206
0
    (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
1207
0
    error(_("--show-origin is only applicable to --get, --get-all, "
1208
0
      "--get-regexp, and --list"));
1209
0
    exit(129);
1210
0
  }
1211
1212
0
  if (display_opts.default_value && !(actions & ACTION_GET)) {
1213
0
    error(_("--default is only applicable to --get"));
1214
0
    exit(129);
1215
0
  }
1216
1217
0
  if (comment_arg &&
1218
0
      !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
1219
0
    error(_("--comment is only applicable to add/set/replace operations"));
1220
0
    exit(129);
1221
0
  }
1222
1223
  /* check usage of --fixed-value */
1224
0
  if (flags & CONFIG_FLAGS_FIXED_VALUE) {
1225
0
    int allowed_usage = 0;
1226
1227
0
    switch (actions) {
1228
    /* git config --get <name> <value-pattern> */
1229
0
    case ACTION_GET:
1230
    /* git config --get-all <name> <value-pattern> */
1231
0
    case ACTION_GET_ALL:
1232
    /* git config --get-regexp <name-pattern> <value-pattern> */
1233
0
    case ACTION_GET_REGEXP:
1234
    /* git config --unset <name> <value-pattern> */
1235
0
    case ACTION_UNSET:
1236
    /* git config --unset-all <name> <value-pattern> */
1237
0
    case ACTION_UNSET_ALL:
1238
0
      allowed_usage = argc > 1 && !!argv[1];
1239
0
      break;
1240
1241
    /* git config <name> <value> <value-pattern> */
1242
0
    case ACTION_SET_ALL:
1243
    /* git config --replace-all <name> <value> <value-pattern> */
1244
0
    case ACTION_REPLACE_ALL:
1245
0
      allowed_usage = argc > 2 && !!argv[2];
1246
0
      break;
1247
1248
    /* other options don't allow --fixed-value */
1249
0
    }
1250
1251
0
    if (!allowed_usage) {
1252
0
      error(_("--fixed-value only applies with 'value-pattern'"));
1253
0
      exit(129);
1254
0
    }
1255
0
  }
1256
1257
0
  comment = git_config_prepare_comment_string(comment_arg);
1258
1259
  /*
1260
   * The following actions may produce more than one line of output and
1261
   * should therefore be paged.
1262
   */
1263
0
  if (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))
1264
0
    setup_auto_pager("config", 1);
1265
1266
0
  if (actions == ACTION_LIST) {
1267
0
    check_argc(argc, 0, 0);
1268
0
    if (config_with_options(show_all_config, &display_opts,
1269
0
          &location_opts.source, the_repository,
1270
0
          &location_opts.options) < 0) {
1271
0
      if (location_opts.source.file)
1272
0
        die_errno(_("unable to read config file '%s'"),
1273
0
            location_opts.source.file);
1274
0
      else
1275
0
        die(_("error processing config file(s)"));
1276
0
    }
1277
0
  }
1278
0
  else if (actions == ACTION_EDIT) {
1279
0
    ret = show_editor(&location_opts);
1280
0
  }
1281
0
  else if (actions == ACTION_SET) {
1282
0
    check_write(&location_opts.source);
1283
0
    check_argc(argc, 2, 2);
1284
0
    value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
1285
0
    ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);
1286
0
    if (ret == CONFIG_NOTHING_SET)
1287
0
      error(_("cannot overwrite multiple values with a single value\n"
1288
0
      "       Use a regexp, --add or --replace-all to change %s."), argv[0]);
1289
0
  }
1290
0
  else if (actions == ACTION_SET_ALL) {
1291
0
    check_write(&location_opts.source);
1292
0
    check_argc(argc, 2, 3);
1293
0
    value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
1294
0
    ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
1295
0
                   argv[0], value, argv[2],
1296
0
                   comment, flags);
1297
0
  }
1298
0
  else if (actions == ACTION_ADD) {
1299
0
    check_write(&location_opts.source);
1300
0
    check_argc(argc, 2, 2);
1301
0
    value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
1302
0
    ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
1303
0
                   argv[0], value,
1304
0
                   CONFIG_REGEX_NONE,
1305
0
                   comment, flags);
1306
0
  }
1307
0
  else if (actions == ACTION_REPLACE_ALL) {
1308
0
    check_write(&location_opts.source);
1309
0
    check_argc(argc, 2, 3);
1310
0
    value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
1311
0
    ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
1312
0
                   argv[0], value, argv[2],
1313
0
                   comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
1314
0
  }
1315
0
  else if (actions == ACTION_GET) {
1316
0
    check_argc(argc, 1, 2);
1317
0
    ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
1318
0
        0, flags);
1319
0
  }
1320
0
  else if (actions == ACTION_GET_ALL) {
1321
0
    check_argc(argc, 1, 2);
1322
0
    ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
1323
0
        GET_VALUE_ALL, flags);
1324
0
  }
1325
0
  else if (actions == ACTION_GET_REGEXP) {
1326
0
    display_opts.show_keys = 1;
1327
0
    check_argc(argc, 1, 2);
1328
0
    ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
1329
0
        GET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags);
1330
0
  }
1331
0
  else if (actions == ACTION_GET_URLMATCH) {
1332
0
    check_argc(argc, 2, 2);
1333
0
    ret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]);
1334
0
  }
1335
0
  else if (actions == ACTION_UNSET) {
1336
0
    check_write(&location_opts.source);
1337
0
    check_argc(argc, 1, 2);
1338
0
    if (argc == 2)
1339
0
      ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
1340
0
                     argv[0], NULL, argv[1],
1341
0
                     NULL, flags);
1342
0
    else
1343
0
      ret = git_config_set_in_file_gently(location_opts.source.file,
1344
0
                  argv[0], NULL, NULL);
1345
0
  }
1346
0
  else if (actions == ACTION_UNSET_ALL) {
1347
0
    check_write(&location_opts.source);
1348
0
    check_argc(argc, 1, 2);
1349
0
    ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
1350
0
                   argv[0], NULL, argv[1],
1351
0
                   NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
1352
0
  }
1353
0
  else if (actions == ACTION_RENAME_SECTION) {
1354
0
    check_write(&location_opts.source);
1355
0
    check_argc(argc, 2, 2);
1356
0
    ret = repo_config_rename_section_in_file(the_repository, location_opts.source.file,
1357
0
               argv[0], argv[1]);
1358
0
    if (ret < 0)
1359
0
      goto out;
1360
0
    else if (!ret)
1361
0
      die(_("no such section: %s"), argv[0]);
1362
0
    else
1363
0
      ret = 0;
1364
0
  }
1365
0
  else if (actions == ACTION_REMOVE_SECTION) {
1366
0
    check_write(&location_opts.source);
1367
0
    check_argc(argc, 1, 1);
1368
0
    ret = repo_config_rename_section_in_file(the_repository, location_opts.source.file,
1369
0
               argv[0], NULL);
1370
0
    if (ret < 0)
1371
0
      goto out;
1372
0
    else if (!ret)
1373
0
      die(_("no such section: %s"), argv[0]);
1374
0
    else
1375
0
      ret = 0;
1376
0
  }
1377
0
  else if (actions == ACTION_GET_COLOR) {
1378
0
    check_argc(argc, 1, 2);
1379
0
    get_color(&location_opts, argv[0], argv[1]);
1380
0
  }
1381
0
  else if (actions == ACTION_GET_COLORBOOL) {
1382
0
    check_argc(argc, 1, 2);
1383
0
    if (argc == 2)
1384
0
      color_stdout_is_tty = git_config_bool("command line", argv[1]);
1385
0
    ret = get_colorbool(&location_opts, argv[0], argc == 2);
1386
0
  }
1387
1388
0
out:
1389
0
  location_options_release(&location_opts);
1390
0
  free(comment);
1391
0
  free(value);
1392
0
  return ret;
1393
0
}
1394
1395
int cmd_config(int argc, const char **argv, const char *prefix)
1396
0
{
1397
0
  parse_opt_subcommand_fn *subcommand = NULL;
1398
0
  struct option subcommand_opts[] = {
1399
0
    OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
1400
0
    OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
1401
0
    OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
1402
0
    OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset),
1403
0
    OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section),
1404
0
    OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section),
1405
0
    OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit),
1406
0
    OPT_END(),
1407
0
  };
1408
1409
  /*
1410
   * This is somewhat hacky: we first parse the command line while
1411
   * keeping all args intact in order to determine whether a subcommand
1412
   * has been specified. If so, we re-parse it a second time, but this
1413
   * time we drop KEEP_ARGV0. This is so that we don't munge the command
1414
   * line in case no subcommand was given, which would otherwise confuse
1415
   * us when parsing the legacy-style modes that don't use subcommands.
1416
   */
1417
0
  argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
1418
0
           PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);
1419
0
  if (subcommand) {
1420
0
    argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
1421
0
           PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);
1422
0
    return subcommand(argc, argv, prefix);
1423
0
  }
1424
1425
0
  return cmd_config_actions(argc, argv, prefix);
1426
0
}