config.c 17.7 KB
Newer Older
1
#include "builtin.h"
2
#include "cache.h"
3
#include "color.h"
4
#include "parse-options.h"
5
#include "urlmatch.h"
6

7
static const char *const builtin_config_usage[] = {
8
	N_("git config [options]"),
9 10
	NULL
};
11

12 13 14 15 16 17 18
static char *key;
static regex_t *key_regexp;
static regex_t *regexp;
static int show_keys;
static int use_key_regexp;
static int do_all;
static int do_not_match;
19 20 21
static char delim = '=';
static char key_delim = ' ';
static char term = '\n';
22

S
Sverre Rabbelier 已提交
23
static int use_global_config, use_system_config, use_local_config;
24
static const char *given_config_file;
25
static int actions, types;
26 27
static const char *get_color_slot, *get_colorbool_slot;
static int end_null;
J
Jeff King 已提交
28
static int respect_includes = -1;
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

#define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1)
#define ACTION_GET_REGEXP (1<<2)
#define ACTION_REPLACE_ALL (1<<3)
#define ACTION_ADD (1<<4)
#define ACTION_UNSET (1<<5)
#define ACTION_UNSET_ALL (1<<6)
#define ACTION_RENAME_SECTION (1<<7)
#define ACTION_REMOVE_SECTION (1<<8)
#define ACTION_LIST (1<<9)
#define ACTION_EDIT (1<<10)
#define ACTION_SET (1<<11)
#define ACTION_SET_ALL (1<<12)
#define ACTION_GET_COLOR (1<<13)
#define ACTION_GET_COLORBOOL (1<<14)
45
#define ACTION_GET_URLMATCH (1<<15)
46

47 48 49
#define TYPE_BOOL (1<<0)
#define TYPE_INT (1<<1)
#define TYPE_BOOL_OR_INT (1<<2)
50
#define TYPE_PATH (1<<3)
51

52
static struct option builtin_config_options[] = {
53 54 55 56 57 58 59 60 61
	OPT_GROUP(N_("Config file location")),
	OPT_BOOLEAN(0, "global", &use_global_config, N_("use global config file")),
	OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
	OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
	OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
	OPT_GROUP(N_("Action")),
	OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
	OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
	OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
62
	OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
63
	OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
64 65 66
	OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
	OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
	OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
67 68 69
	OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
	OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
	OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
70
	OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
71 72 73 74 75 76 77 78 79 80
	OPT_STRING(0, "get-color", &get_color_slot, N_("slot"), N_("find the color configured: [default]")),
	OPT_STRING(0, "get-colorbool", &get_colorbool_slot, N_("slot"), N_("find the color setting: [stdout-is-tty]")),
	OPT_GROUP(N_("Type")),
	OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL),
	OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT),
	OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
	OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
	OPT_GROUP(N_("Other")),
	OPT_BOOLEAN('z', "null", &end_null, N_("terminate values with NUL byte")),
	OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
81 82 83 84 85 86 87 88 89 90
	OPT_END(),
};

static void check_argc(int argc, int min, int max) {
	if (argc >= min && argc <= max)
		return;
	error("wrong number of arguments");
	usage_with_options(builtin_config_usage, builtin_config_options);
}

91
static int show_all_config(const char *key_, const char *value_, void *cb)
P
Petr Baudis 已提交
92 93
{
	if (value_)
94
		printf("%s%c%s%c", key_, delim, value_, term);
P
Petr Baudis 已提交
95
	else
96
		printf("%s%c", key_, term);
P
Petr Baudis 已提交
97 98 99
	return 0;
}

100 101 102 103 104 105
struct strbuf_list {
	struct strbuf *items;
	int nr;
	int alloc;
};

106
static int format_config(struct strbuf *buf, const char *key_, const char *value_)
107
{
108
	int must_free_vptr = 0;
109
	int must_print_delim = 0;
110 111
	char value[256];
	const char *vptr = value;
112

113 114
	strbuf_init(buf, 0);

115
	if (show_keys) {
116
		strbuf_addstr(buf, key_);
117
		must_print_delim = 1;
118
	}
119
	if (types == TYPE_INT)
120
		sprintf(value, "%d", git_config_int(key_, value_ ? value_ : ""));
121
	else if (types == TYPE_BOOL)
122
		vptr = git_config_bool(key_, value_) ? "true" : "false";
123
	else if (types == TYPE_BOOL_OR_INT) {
J
Junio C Hamano 已提交
124 125 126 127 128 129
		int is_bool, v;
		v = git_config_bool_or_int(key_, value_, &is_bool);
		if (is_bool)
			vptr = v ? "true" : "false";
		else
			sprintf(value, "%d", v);
130
	} else if (types == TYPE_PATH) {
131 132
		if (git_config_pathname(&vptr, key_, value_) < 0)
			return -1;
133
		must_free_vptr = 1;
134 135 136 137 138 139
	} else if (value_) {
		vptr = value_;
	} else {
		/* Just show the key name */
		vptr = "";
		must_print_delim = 0;
J
Junio C Hamano 已提交
140
	}
141 142 143 144 145 146

	if (must_print_delim)
		strbuf_addch(buf, key_delim);
	strbuf_addstr(buf, vptr);
	strbuf_addch(buf, term);

147 148
	if (must_free_vptr)
		free((char *)vptr);
149 150 151
	return 0;
}

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
static int collect_config(const char *key_, const char *value_, void *cb)
{
	struct strbuf_list *values = cb;

	if (!use_key_regexp && strcmp(key_, key))
		return 0;
	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
		return 0;
	if (regexp != NULL &&
	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
		return 0;

	ALLOC_GROW(values->items, values->nr + 1, values->alloc);

	return format_config(&values->items[values->nr++], key_, value_);
}

169
static int get_value(const char *key_, const char *regex_)
170
{
171
	int ret = CONFIG_GENERIC_ERROR;
172
	struct strbuf_list values = {NULL};
173
	int i;
174

175
	if (use_key_regexp) {
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
		char *tl;

		/*
		 * NEEDSWORK: this naive pattern lowercasing obviously does not
		 * work for more complex patterns like "^[^.]*Foo.*bar".
		 * Perhaps we should deprecate this altogether someday.
		 */

		key = xstrdup(key_);
		for (tl = key + strlen(key) - 1;
		     tl >= key && *tl != '.';
		     tl--)
			*tl = tolower(*tl);
		for (tl = key; *tl && *tl != '.'; tl++)
			*tl = tolower(*tl);

J
Jonas Fonseca 已提交
192
		key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
193
		if (regcomp(key_regexp, key, REG_EXTENDED)) {
194
			fprintf(stderr, "Invalid key pattern: %s\n", key_);
195 196
			free(key_regexp);
			key_regexp = NULL;
197
			ret = CONFIG_INVALID_PATTERN;
198
			goto free_strings;
199
		}
200
	} else {
201 202
		if (git_config_parse_key(key_, &key, NULL)) {
			ret = CONFIG_INVALID_KEY;
203
			goto free_strings;
204
		}
205 206
	}

207
	if (regex_) {
208 209 210 211 212
		if (regex_[0] == '!') {
			do_not_match = 1;
			regex_++;
		}

J
Jonas Fonseca 已提交
213
		regexp = (regex_t*)xmalloc(sizeof(regex_t));
214
		if (regcomp(regexp, regex_, REG_EXTENDED)) {
215
			fprintf(stderr, "Invalid pattern: %s\n", regex_);
216 217
			free(regexp);
			regexp = NULL;
218
			ret = CONFIG_INVALID_PATTERN;
219
			goto free_strings;
220 221 222
		}
	}

223 224
	git_config_with_options(collect_config, &values,
				given_config_file, respect_includes);
225

226
	ret = !values.nr;
J
Jeff King 已提交
227

228 229
	for (i = 0; i < values.nr; i++) {
		struct strbuf *buf = values.items + i;
230 231
		if (do_all || i == values.nr - 1)
			fwrite(buf->buf, 1, buf->len, stdout);
232 233 234
		strbuf_release(buf);
	}
	free(values.items);
235

236
free_strings:
237
	free(key);
238 239 240 241
	if (key_regexp) {
		regfree(key_regexp);
		free(key_regexp);
	}
242 243 244
	if (regexp) {
		regfree(regexp);
		free(regexp);
245 246
	}

247
	return ret;
248
}
249

250
static char *normalize_value(const char *key, const char *value)
251 252 253 254 255 256
{
	char *normalized;

	if (!value)
		return NULL;

257 258 259 260 261 262 263
	if (types == 0 || types == TYPE_PATH)
		/*
		 * We don't do normalization for TYPE_PATH here: If
		 * the path is like ~/foobar/, we prefer to store
		 * "~/foobar/" in the config file, and to expand the ~
		 * when retrieving the value.
		 */
264 265 266
		normalized = xstrdup(value);
	else {
		normalized = xmalloc(64);
267
		if (types == TYPE_INT) {
268 269 270
			int v = git_config_int(key, value);
			sprintf(normalized, "%d", v);
		}
271
		else if (types == TYPE_BOOL)
272 273
			sprintf(normalized, "%s",
				git_config_bool(key, value) ? "true" : "false");
274
		else if (types == TYPE_BOOL_OR_INT) {
J
Junio C Hamano 已提交
275 276 277 278 279 280 281
			int is_bool, v;
			v = git_config_bool_or_int(key, value, &is_bool);
			if (!is_bool)
				sprintf(normalized, "%d", v);
			else
				sprintf(normalized, "%s", v ? "true" : "false");
		}
282 283 284 285 286
	}

	return normalized;
}

287 288
static int get_color_found;
static const char *get_color_slot;
289
static const char *get_colorbool_slot;
290 291
static char parsed_color[COLOR_MAXLEN];

292
static int git_get_color_config(const char *var, const char *value, void *cb)
293 294
{
	if (!strcmp(var, get_color_slot)) {
295 296
		if (!value)
			config_error_nonbool(var);
297 298 299 300 301 302
		color_parse(value, var, parsed_color);
		get_color_found = 1;
	}
	return 0;
}

303
static void get_color(const char *def_color)
304 305 306
{
	get_color_found = 0;
	parsed_color[0] = '\0';
307
	git_config_with_options(git_get_color_config, NULL,
J
Jeff King 已提交
308
				given_config_file, respect_includes);
309 310 311 312 313 314 315

	if (!get_color_found && def_color)
		color_parse(def_color, "command line", parsed_color);

	fputs(parsed_color, stdout);
}

J
Junio C Hamano 已提交
316
static int get_colorbool_found;
317
static int get_diff_color_found;
318
static int get_color_ui_found;
319 320
static int git_get_colorbool_config(const char *var, const char *value,
		void *cb)
J
Junio C Hamano 已提交
321
{
322 323 324 325 326
	if (!strcmp(var, get_colorbool_slot))
		get_colorbool_found = git_config_colorbool(var, value);
	else if (!strcmp(var, "diff.color"))
		get_diff_color_found = git_config_colorbool(var, value);
	else if (!strcmp(var, "color.ui"))
327
		get_color_ui_found = git_config_colorbool(var, value);
J
Junio C Hamano 已提交
328 329 330
	return 0;
}

331
static int get_colorbool(int print)
J
Junio C Hamano 已提交
332
{
333 334
	get_colorbool_found = -1;
	get_diff_color_found = -1;
335
	git_config_with_options(git_get_colorbool_config, NULL,
J
Jeff King 已提交
336
				given_config_file, respect_includes);
J
Junio C Hamano 已提交
337

338
	if (get_colorbool_found < 0) {
339
		if (!strcmp(get_colorbool_slot, "color.diff"))
340 341
			get_colorbool_found = get_diff_color_found;
		if (get_colorbool_found < 0)
342
			get_colorbool_found = get_color_ui_found;
343 344
	}

345 346
	get_colorbool_found = want_color(get_colorbool_found);

347
	if (print) {
J
Junio C Hamano 已提交
348 349
		printf("%s\n", get_colorbool_found ? "true" : "false");
		return 0;
350 351
	} else
		return get_colorbool_found ? 0 : 1;
J
Junio C Hamano 已提交
352 353
}

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
struct urlmatch_current_candidate_value {
	char value_is_null;
	struct strbuf value;
};

static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
{
	struct string_list *values = cb;
	struct string_list_item *item = string_list_insert(values, var);
	struct urlmatch_current_candidate_value *matched = item->util;

	if (!matched) {
		matched = xmalloc(sizeof(*matched));
		strbuf_init(&matched->value, 0);
		item->util = matched;
	} else {
		strbuf_reset(&matched->value);
	}

	if (value) {
		strbuf_addstr(&matched->value, value);
		matched->value_is_null = 0;
	} else {
		matched->value_is_null = 1;
	}
	return 0;
}

static char *dup_downcase(const char *string)
{
	char *result;
	size_t len, i;

	len = strlen(string);
	result = xmalloc(len + 1);
	for (i = 0; i < len; i++)
		result[i] = tolower(string[i]);
	result[i] = '\0';
	return result;
}

static int get_urlmatch(const char *var, const char *url)
{
	char *section_tail;
	struct string_list_item *item;
	struct urlmatch_config config = { STRING_LIST_INIT_DUP };
	struct string_list values = STRING_LIST_INIT_DUP;

	config.collect_fn = urlmatch_collect_fn;
	config.cascade_fn = NULL;
	config.cb = &values;

	if (!url_normalize(url, &config.url))
		die(config.url.err);

	config.section = dup_downcase(var);
	section_tail = strchr(config.section, '.');
	if (section_tail) {
		*section_tail = '\0';
		config.key = section_tail + 1;
		show_keys = 0;
	} else {
		config.key = NULL;
		show_keys = 1;
	}

	git_config_with_options(urlmatch_config_entry, &config,
				given_config_file, respect_includes);

	for_each_string_list_item(item, &values) {
		struct urlmatch_current_candidate_value *matched = item->util;
		struct strbuf key = STRBUF_INIT;
		struct strbuf buf = STRBUF_INIT;

		strbuf_addstr(&key, item->string);
		format_config(&buf, key.buf,
			      matched->value_is_null ? NULL : matched->value.buf);
		fwrite(buf.buf, 1, buf.len, stdout);
		strbuf_release(&key);
		strbuf_release(&buf);

		strbuf_release(&matched->value);
	}
	string_list_clear(&config.vars, 1);
	string_list_clear(&values, 1);
	free(config.url.url);

	free((void *)config.section);
	return 0;
}

445
int cmd_config(int argc, const char **argv, const char *prefix)
446
{
447
	int nongit = !startup_info->have_repository;
448
	char *value;
449

450
	given_config_file = getenv(CONFIG_ENVIRONMENT);
451

452 453
	argc = parse_options(argc, argv, prefix, builtin_config_options,
			     builtin_config_usage,
454 455
			     PARSE_OPT_STOP_AT_NON_OPTION);

S
Sverre Rabbelier 已提交
456
	if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
457 458 459 460
		error("only one config file at a time.");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}

461
	if (use_global_config) {
462 463 464 465 466
		char *user_config = NULL;
		char *xdg_config = NULL;

		home_config_paths(&user_config, &xdg_config, "config");

467 468 469 470 471 472 473 474 475
		if (!user_config)
			/*
			 * It is unknown if HOME/.gitconfig exists, so
			 * we do not know if we should write to XDG
			 * location; error out even if XDG_CONFIG_HOME
			 * is set and points at a sane location.
			 */
			die("$HOME not set");

J
Jeff King 已提交
476 477
		if (access_or_warn(user_config, R_OK) &&
		    xdg_config && !access_or_warn(xdg_config, R_OK))
478 479
			given_config_file = xdg_config;
		else
480
			given_config_file = user_config;
481 482
	}
	else if (use_system_config)
483
		given_config_file = git_etc_gitconfig();
S
Sverre Rabbelier 已提交
484
	else if (use_local_config)
485
		given_config_file = git_pathdup("config");
486 487
	else if (given_config_file) {
		if (!is_absolute_path(given_config_file) && prefix)
488
			given_config_file =
489 490 491
				xstrdup(prefix_filename(prefix,
							strlen(prefix),
							given_config_file));
492 493
	}

J
Jeff King 已提交
494 495 496
	if (respect_includes == -1)
		respect_includes = !given_config_file;

497 498 499 500 501 502
	if (end_null) {
		term = '\0';
		delim = '\n';
		key_delim = '\n';
	}

503 504 505 506 507
	if (HAS_MULTI_BITS(types)) {
		error("only one type at a time.");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}

508 509 510 511 512
	if (get_color_slot)
	    actions |= ACTION_GET_COLOR;
	if (get_colorbool_slot)
	    actions |= ACTION_GET_COLORBOOL;

513 514 515 516 517
	if ((get_color_slot || get_colorbool_slot) && types) {
		error("--get-color and variable type are incoherent");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}

518 519 520 521 522 523 524 525 526 527 528
	if (HAS_MULTI_BITS(actions)) {
		error("only one action at a time.");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}
	if (actions == 0)
		switch (argc) {
		case 1: actions = ACTION_GET; break;
		case 2: actions = ACTION_SET; break;
		case 3: actions = ACTION_SET_ALL; break;
		default:
			usage_with_options(builtin_config_usage, builtin_config_options);
529
		}
530 531

	if (actions == ACTION_LIST) {
532
		check_argc(argc, 0, 0);
533
		if (git_config_with_options(show_all_config, NULL,
J
Jeff King 已提交
534 535
					    given_config_file,
					    respect_includes) < 0) {
536
			if (given_config_file)
537
				die_errno("unable to read config file '%s'",
538
					  given_config_file);
539 540
			else
				die("error processing config file(s)");
541
		}
542
	}
543
	else if (actions == ACTION_EDIT) {
544
		check_argc(argc, 0, 0);
545
		if (!given_config_file && nongit)
546
			die("not in a git directory");
547
		git_config(git_default_config, NULL);
548 549
		launch_editor(given_config_file ?
			      given_config_file : git_path("config"),
550 551 552
			      NULL, NULL);
	}
	else if (actions == ACTION_SET) {
553
		int ret;
554 555
		check_argc(argc, 2, 2);
		value = normalize_value(argv[0], argv[1]);
556
		ret = git_config_set_in_file(given_config_file, argv[0], value);
557 558
		if (ret == CONFIG_NOTHING_SET)
			error("cannot overwrite multiple values with a single value\n"
559
			"       Use a regexp, --add or --replace-all to change %s.", argv[0]);
560
		return ret;
561 562 563 564
	}
	else if (actions == ACTION_SET_ALL) {
		check_argc(argc, 2, 3);
		value = normalize_value(argv[0], argv[1]);
565 566
		return git_config_set_multivar_in_file(given_config_file,
						       argv[0], value, argv[2], 0);
567 568 569 570
	}
	else if (actions == ACTION_ADD) {
		check_argc(argc, 2, 2);
		value = normalize_value(argv[0], argv[1]);
571 572
		return git_config_set_multivar_in_file(given_config_file,
						       argv[0], value, "^$", 0);
573 574 575 576
	}
	else if (actions == ACTION_REPLACE_ALL) {
		check_argc(argc, 2, 3);
		value = normalize_value(argv[0], argv[1]);
577 578
		return git_config_set_multivar_in_file(given_config_file,
						       argv[0], value, argv[2], 1);
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
	}
	else if (actions == ACTION_GET) {
		check_argc(argc, 1, 2);
		return get_value(argv[0], argv[1]);
	}
	else if (actions == ACTION_GET_ALL) {
		do_all = 1;
		check_argc(argc, 1, 2);
		return get_value(argv[0], argv[1]);
	}
	else if (actions == ACTION_GET_REGEXP) {
		show_keys = 1;
		use_key_regexp = 1;
		do_all = 1;
		check_argc(argc, 1, 2);
		return get_value(argv[0], argv[1]);
	}
596 597 598 599
	else if (actions == ACTION_GET_URLMATCH) {
		check_argc(argc, 2, 2);
		return get_urlmatch(argv[0], argv[1]);
	}
600 601 602
	else if (actions == ACTION_UNSET) {
		check_argc(argc, 1, 2);
		if (argc == 2)
603 604
			return git_config_set_multivar_in_file(given_config_file,
							       argv[0], NULL, argv[1], 0);
605
		else
606 607
			return git_config_set_in_file(given_config_file,
						      argv[0], NULL);
608 609 610
	}
	else if (actions == ACTION_UNSET_ALL) {
		check_argc(argc, 1, 2);
611 612
		return git_config_set_multivar_in_file(given_config_file,
						       argv[0], NULL, argv[1], 1);
613 614 615 616
	}
	else if (actions == ACTION_RENAME_SECTION) {
		int ret;
		check_argc(argc, 2, 2);
617 618
		ret = git_config_rename_section_in_file(given_config_file,
							argv[0], argv[1]);
619 620 621 622 623 624 625 626
		if (ret < 0)
			return ret;
		if (ret == 0)
			die("No such section!");
	}
	else if (actions == ACTION_REMOVE_SECTION) {
		int ret;
		check_argc(argc, 1, 1);
627 628
		ret = git_config_rename_section_in_file(given_config_file,
							argv[0], NULL);
629 630 631 632 633 634 635 636 637 638
		if (ret < 0)
			return ret;
		if (ret == 0)
			die("No such section!");
	}
	else if (actions == ACTION_GET_COLOR) {
		get_color(argv[0]);
	}
	else if (actions == ACTION_GET_COLORBOOL) {
		if (argc == 1)
639
			color_stdout_is_tty = git_config_bool("command line", argv[0]);
640 641 642
		return get_colorbool(argc != 0);
	}

643 644
	return 0;
}
645 646 647 648 649 650

int cmd_repo_config(int argc, const char **argv, const char *prefix)
{
	fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n");
	return cmd_config(argc, argv, prefix);
}