config: "git config --get-urlmatch" parses section.<url>.key

Using the same urlmatch_config_entry() infrastructure, add a new mode "--get-urlmatch" to the "git config" command, to learn values for the "virtual" two-level variables customized for the specific URL. git config [--<type>] --get-urlmatch <section>[.<key>] <url> With <section>.<key> fully specified, the configuration data for <section>.<urlpattern>.<key> for <urlpattern> that best matches the given <url> is sought (and if not found, <section>.<key> is used) and reported. For example, with this configuration: [http] sslVerify [http "https://weak.example.com"] cookieFile = /tmp/cookie.txt sslVerify = false You would get $ git config --bool --get-urlmatch http.sslVerify https://good.example.com true $ git config --bool --get-urlmatch http.sslVerify https://weak.example.com false With only <section> specified, you can get a list of all variables in the section with their values that apply to the given URL. E.g $ git config --get-urlmatch http https://weak.example.com http.cookiefile /tmp/cookie.txt http.sslverify false Helped-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 31, 2013 at 11:14 UTC d4770964d5058a679ebfc051acceb7968a831f84
3 files changed +152
Documentation/git-config.txt
+29
@@ -15,6 +15,7 @@ SYNOPSIS
15 'git config' [<file-option>] [type] [-z|--null] --get name [value_regex]
16 'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
17 'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
18 +'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL
19 'git config' [<file-option>] --unset name [value_regex]
20 'git config' [<file-option>] --unset-all name [value_regex]
21 'git config' [<file-option>] --rename-section old_name new_name
@@ -95,6 +96,14 @@ OPTIONS
96 in which section and variable names are lowercased, but subsection
97 names are not.
98
99 +--get-urlmatch name URL::
100 + When given a two-part name section.key, the value for
101 + section.<url>.key whose <url> part matches the best to the
102 + given URL is returned (if no such key exists, the value for
103 + section.key is used as a fallback). When given just the
104 + section as name, do so for all the keys in the section and
105 + list them.
106 +
107 --global::
108 For writing options: write to global ~/.gitconfig file rather than
109 the repository .git/config, write to $XDG_CONFIG_HOME/git/config file
@@ -273,6 +282,13 @@ Given a .git/config like this:
282 gitproxy=proxy-command for kernel.org
283 gitproxy=default-proxy ; for all the rest
284
285 + ; HTTP
286 + [http]
287 + sslVerify
288 + [http "https://weak.example.com"]
289 + sslVerify = false
290 + cookieFile = /tmp/cookie.txt
291 +
292 you can set the filemode to true with
293
294 ------------
@@ -358,6 +374,19 @@ RESET=$(git config --get-color "" "reset")
374 echo "${WS}your whitespace color or blue reverse${RESET}"
375 ------------
376
377 +For URLs in `https://weak.example.com`, `http.sslVerify` is set to
378 +false, while it is set to `true` for all others:
379 +
380 +------------
381 +% git config --bool --get-urlmatch http.sslverify https://good.example.com
382 +true
383 +% git config --bool --get-urlmatch http.sslverify https://weak.example.com
384 +false
385 +% git config --get-urlmatch http https://weak.example.com
386 +http.cookiefile /tmp/cookie.txt
387 +http.sslverify false
388 +------------
389 +
390 include::config.txt[]
391
392 GIT
builtin/config.c
+98
@@ -2,6 +2,7 @@
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]"),
@@ -41,6 +42,7 @@ static int respect_includes = -1;
42 #define ACTION_SET_ALL (1<<12)
43 #define ACTION_GET_COLOR (1<<13)
44 #define ACTION_GET_COLORBOOL (1<<14)
45 +#define ACTION_GET_URLMATCH (1<<15)
46
47 #define TYPE_BOOL (1<<0)
48 #define TYPE_INT (1<<1)
@@ -57,6 +59,7 @@ static struct option builtin_config_options[] = {
59 OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
60 OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
61 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 OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
65 OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
@@ -348,6 +351,97 @@ static int get_colorbool(int print)
351 return get_colorbool_found ? 0 : 1;
352 }
353
354 +struct urlmatch_current_candidate_value {
355 + char value_is_null;
356 + struct strbuf value;
357 +};
358 +
359 +static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
360 +{
361 + struct string_list *values = cb;
362 + struct string_list_item *item = string_list_insert(values, var);
363 + struct urlmatch_current_candidate_value *matched = item->util;
364 +
365 + if (!matched) {
366 + matched = xmalloc(sizeof(*matched));
367 + strbuf_init(&matched->value, 0);
368 + item->util = matched;
369 + } else {
370 + strbuf_reset(&matched->value);
371 + }
372 +
373 + if (value) {
374 + strbuf_addstr(&matched->value, value);
375 + matched->value_is_null = 0;
376 + } else {
377 + matched->value_is_null = 1;
378 + }
379 + return 0;
380 +}
381 +
382 +static char *dup_downcase(const char *string)
383 +{
384 + char *result;
385 + size_t len, i;
386 +
387 + len = strlen(string);
388 + result = xmalloc(len + 1);
389 + for (i = 0; i < len; i++)
390 + result[i] = tolower(string[i]);
391 + result[i] = '\0';
392 + return result;
393 +}
394 +
395 +static int get_urlmatch(const char *var, const char *url)
396 +{
397 + char *section_tail;
398 + struct string_list_item *item;
399 + struct urlmatch_config config = { STRING_LIST_INIT_DUP };
400 + struct string_list values = STRING_LIST_INIT_DUP;
401 +
402 + config.collect_fn = urlmatch_collect_fn;
403 + config.cascade_fn = NULL;
404 + config.cb = &values;
405 +
406 + if (!url_normalize(url, &config.url))
407 + die(config.url.err);
408 +
409 + config.section = dup_downcase(var);
410 + section_tail = strchr(config.section, '.');
411 + if (section_tail) {
412 + *section_tail = '\0';
413 + config.key = section_tail + 1;
414 + show_keys = 0;
415 + } else {
416 + config.key = NULL;
417 + show_keys = 1;
418 + }
419 +
420 + git_config_with_options(urlmatch_config_entry, &config,
421 + given_config_file, respect_includes);
422 +
423 + for_each_string_list_item(item, &values) {
424 + struct urlmatch_current_candidate_value *matched = item->util;
425 + struct strbuf key = STRBUF_INIT;
426 + struct strbuf buf = STRBUF_INIT;
427 +
428 + strbuf_addstr(&key, item->string);
429 + format_config(&buf, key.buf,
430 + matched->value_is_null ? NULL : matched->value.buf);
431 + fwrite(buf.buf, 1, buf.len, stdout);
432 + strbuf_release(&key);
433 + strbuf_release(&buf);
434 +
435 + strbuf_release(&matched->value);
436 + }
437 + string_list_clear(&config.vars, 1);
438 + string_list_clear(&values, 1);
439 + free(config.url.url);
440 +
441 + free((void *)config.section);
442 + return 0;
443 +}
444 +
445 int cmd_config(int argc, const char **argv, const char *prefix)
446 {
447 int nongit = !startup_info->have_repository;
@@ -499,6 +593,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)
593 check_argc(argc, 1, 2);
594 return get_value(argv[0], argv[1]);
595 }
596 + else if (actions == ACTION_GET_URLMATCH) {
597 + check_argc(argc, 2, 2);
598 + return get_urlmatch(argv[0], argv[1]);
599 + }
600 else if (actions == ACTION_UNSET) {
601 check_argc(argc, 1, 2);
602 if (argc == 2)
t/t1300-repo-config.sh
+25
@@ -1087,6 +1087,31 @@ test_expect_success 'barf on incomplete string' '
1087 grep " line 3 " error
1088 '
1089
1090 +test_expect_success 'urlmatch' '
1091 + cat >.git/config <<-\EOF &&
1092 + [http]
1093 + sslVerify
1094 + [http "https://weak.example.com"]
1095 + sslVerify = false
1096 + cookieFile = /tmp/cookie.txt
1097 + EOF
1098 +
1099 + echo true >expect &&
1100 + git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
1101 + test_cmp expect actual &&
1102 +
1103 + echo false >expect &&
1104 + git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
1105 + test_cmp expect actual &&
1106 +
1107 + {
1108 + echo http.cookiefile /tmp/cookie.txt &&
1109 + echo http.sslverify false
1110 + } >expect &&
1111 + git config --get-urlmatch HTTP https://weak.example.com >actual &&
1112 + test_cmp expect actual
1113 +'
1114 +
1115 # good section hygiene
1116 test_expect_failure 'unsetting the last key in a section removes header' '
1117 cat >.git/config <<-\EOF &&