config: teach "git config --file -" to read from the standard input

The patch extends git config --file interface to allow read config from stdin. Editing stdin or setting value in stdin is an error. Include by absolute path is allowed in stdin config, but not by relative path. Signed-off-by: Kirill A. Shutemov <kirill@shutemov.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Kirill A. Shutemov committed Feb 19, 2014 at 00:58 UTC 3caec73b5568341c5d8f303692423a8e9fb0cb39
5 files changed +69 -17
builtin/config.c
+11
@@ -360,6 +360,9 @@ static int get_colorbool(int print)
360
361 static void check_write(void)
362 {
363 + if (given_config_source.use_stdin)
364 + die("writing to stdin is not supported");
365 +
366 if (given_config_source.blob)
367 die("writing config blobs is not supported");
368 }
@@ -472,6 +475,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
475 usage_with_options(builtin_config_usage, builtin_config_options);
476 }
477
478 + if (given_config_source.file &&
479 + !strcmp(given_config_source.file, "-")) {
480 + given_config_source.file = NULL;
481 + given_config_source.use_stdin = 1;
482 + }
483 +
484 if (use_global_config) {
485 char *user_config = NULL;
486 char *xdg_config = NULL;
@@ -558,6 +567,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
567 check_argc(argc, 0, 0);
568 if (!given_config_source.file && nongit)
569 die("not in a git directory");
570 + if (given_config_source.use_stdin)
571 + die("editing stdin is not supported");
572 if (given_config_source.blob)
573 die("editing blobs is not supported");
574 git_config(git_default_config, NULL);
cache.h
+1
@@ -1147,6 +1147,7 @@ extern int update_server_info(int);
1147 #define CONFIG_GENERIC_ERROR 7
1148
1149 struct git_config_source {
1150 + unsigned int use_stdin:1;
1151 const char *file;
1152 const char *blob;
1153 };
config.c
+27 -14
@@ -1031,24 +1031,35 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
1031 return ret;
1032 }
1033
1034 -int git_config_from_file(config_fn_t fn, const char *filename, void *data)
1034 +static int do_config_from_file(config_fn_t fn,
1035 + const char *name, const char *path, FILE *f, void *data)
1036 {
1036 - int ret;
1037 - FILE *f = fopen(filename, "r");
1037 + struct config_source top;
1038
1039 - ret = -1;
1040 - if (f) {
1041 - struct config_source top;
1039 + top.u.file = f;
1040 + top.name = name;
1041 + top.path = path;
1042 + top.die_on_error = 1;
1043 + top.do_fgetc = config_file_fgetc;
1044 + top.do_ungetc = config_file_ungetc;
1045 + top.do_ftell = config_file_ftell;
1046
1043 - top.u.file = f;
1044 - top.name = top.path = filename;
1045 - top.die_on_error = 1;
1046 - top.do_fgetc = config_file_fgetc;
1047 - top.do_ungetc = config_file_ungetc;
1048 - top.do_ftell = config_file_ftell;
1047 + return do_config_from(&top, fn, data);
1048 +}
1049
1050 - ret = do_config_from(&top, fn, data);
1050 +static int git_config_from_stdin(config_fn_t fn, void *data)
1051 +{
1052 + return do_config_from_file(fn, "<stdin>", NULL, stdin, data);
1053 +}
1054 +
1055 +int git_config_from_file(config_fn_t fn, const char *filename, void *data)
1056 +{
1057 + int ret = -1;
1058 + FILE *f;
1059
1060 + f = fopen(filename, "r");
1061 + if (f) {
1062 + ret = do_config_from_file(fn, filename, filename, f, data);
1063 fclose(f);
1064 }
1065 return ret;
@@ -1190,7 +1201,9 @@ int git_config_with_options(config_fn_t fn, void *data,
1201 * If we have a specific filename, use it. Otherwise, follow the
1202 * regular lookup sequence.
1203 */
1193 - if (config_source && config_source->file)
1204 + if (config_source && config_source->use_stdin)
1205 + return git_config_from_stdin(fn, data);
1206 + else if (config_source && config_source->file)
1207 return git_config_from_file(fn, config_source->file, data);
1208 else if (config_source && config_source->blob)
1209 return git_config_from_blob_ref(fn, config_source->blob, data);
t/t1300-repo-config.sh
+15 -2
@@ -475,15 +475,28 @@ ein.bahn=strasse
475 EOF
476
477 test_expect_success 'alternative GIT_CONFIG' '
478 - GIT_CONFIG=other-config git config -l >output &&
478 + GIT_CONFIG=other-config git config --list >output &&
479 test_cmp expect output
480 '
481
482 test_expect_success 'alternative GIT_CONFIG (--file)' '
483 - git config --file other-config -l > output &&
483 + git config --file other-config --list >output &&
484 test_cmp expect output
485 '
486
487 +test_expect_success 'alternative GIT_CONFIG (--file=-)' '
488 + git config --file - --list <other-config >output &&
489 + test_cmp expect output
490 +'
491 +
492 +test_expect_success 'setting a value in stdin is an error' '
493 + test_must_fail git config --file - some.value foo
494 +'
495 +
496 +test_expect_success 'editing stdin is an error' '
497 + test_must_fail git config --file - --edit
498 +'
499 +
500 test_expect_success 'refer config from subdirectory' '
501 mkdir x &&
502 (
t/t1305-config-include.sh
+15 -1
@@ -113,7 +113,7 @@ test_expect_success 'missing include files are ignored' '
113 test_expect_success 'absolute includes from command line work' '
114 echo "[test]one = 1" >one &&
115 echo 1 >expect &&
116 - git -c include.path="$PWD/one" config test.one >actual &&
116 + git -c include.path="$(pwd)/one" config test.one >actual &&
117 test_cmp expect actual
118 '
119
@@ -138,6 +138,20 @@ test_expect_success 'relative includes from blobs fail' '
138 test_must_fail git config --blob=$blob test.one
139 '
140
141 +test_expect_success 'absolute includes from stdin work' '
142 + echo "[test]one = 1" >one &&
143 + echo 1 >expect &&
144 + echo "[include]path=\"$(pwd)/one\"" |
145 + git config --file - test.one >actual &&
146 + test_cmp expect actual
147 +'
148 +
149 +test_expect_success 'relative includes from stdin line fail' '
150 + echo "[test]one = 1" >one &&
151 + echo "[include]path=one" |
152 + test_must_fail git config --file - test.one
153 +'
154 +
155 test_expect_success 'include cycles are detected' '
156 cat >.gitconfig <<-\EOF &&
157 [test]value = gitconfig