{"thread":{"id":"35912","subject":"[PATCH 4/4] config: teach \"git config --file -\" to read from the standard input","startedAt":"2014-02-18T22:58:52Z","lastAt":"2014-02-22T08:37:44Z","messageCount":5,"participants":["Kirill A. Shutemov","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"235034","messageId":"1392764335-13274-1-git-send-email-kirill@shutemov.name","threadId":"35912","inReplyTo":null,"subject":"[PATCH 1/4] config: disallow relative include paths from blobs","fromName":"Kirill A. Shutemov","fromEmail":"kirill@shutemov.name","sentAt":"2014-02-18T22:58:52Z","receivedAt":"2014-02-18T22:58:52Z","isPatch":true,"sender":{"key":"kirill@shutemov.name","avatar":"https://avatars.githubusercontent.com/u/588661?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nWhen we see a relative config include like:\n\n  [include]\n  path = foo\n\nwe make it relative to the containing directory of the file\nthat contains the snippet. This makes no sense for config\nread from a blob, as it is not on the filesystem.  Something\nlike \"HEAD:some/path\" could have a relative path within the\ntree, but:\n\n  1. It would not be part of include.path, which explicitly\n     refers to the filesystem.\n\n  2. It would need different parsing rules anyway to\n     determine that it is a tree path.\n\nThe current code just uses the \"name\" field, which is wrong.\nLet's split that into \"name\" and \"path\" fields, use the\nlatter for relative includes, and fill in only the former\nfor blobs.\n\nSigned-off-by: Jeff King <peff@peff.net>\nSigned-off-by: Kirill A. Shutemov <kirill@shutemov.name>\n---\n config.c                  | 10 ++++++----\n t/t1305-config-include.sh | 16 ++++++++++++++++\n 2 files changed, 22 insertions(+), 4 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex d969a5aefc2b..b295310d3c22 100644\n--- a/config.c\n+++ b/config.c\n@@ -21,6 +21,7 @@ struct config_source {\n \t\t} buf;\n \t} u;\n \tconst char *name;\n+\tconst char *path;\n \tint die_on_error;\n \tint linenr;\n \tint eof;\n@@ -97,12 +98,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc\n \tif (!is_absolute_path(path)) {\n \t\tchar *slash;\n \n-\t\tif (!cf || !cf->name)\n+\t\tif (!cf || !cf->path)\n \t\t\treturn error(\"relative config includes must come from files\");\n \n-\t\tslash = find_last_dir_sep(cf->name);\n+\t\tslash = find_last_dir_sep(cf->path);\n \t\tif (slash)\n-\t\t\tstrbuf_add(&buf, cf->name, slash - cf->name + 1);\n+\t\t\tstrbuf_add(&buf, cf->path, slash - cf->path + 1);\n \t\tstrbuf_addstr(&buf, path);\n \t\tpath = buf.buf;\n \t}\n@@ -1040,7 +1041,7 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)\n \t\tstruct config_source top;\n \n \t\ttop.u.file = f;\n-\t\ttop.name = filename;\n+\t\ttop.name = top.path = filename;\n \t\ttop.die_on_error = 1;\n \t\ttop.do_fgetc = config_file_fgetc;\n \t\ttop.do_ungetc = config_file_ungetc;\n@@ -1062,6 +1063,7 @@ int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,\n \ttop.u.buf.len = len;\n \ttop.u.buf.pos = 0;\n \ttop.name = name;\n+\ttop.path = NULL;\n \ttop.die_on_error = 0;\n \ttop.do_fgetc = config_buf_fgetc;\n \ttop.do_ungetc = config_buf_ungetc;\ndiff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\nindex a70707620f14..6edd38b39a0b 100755\n--- a/t/t1305-config-include.sh\n+++ b/t/t1305-config-include.sh\n@@ -122,6 +122,22 @@ test_expect_success 'relative includes from command line fail' '\n \ttest_must_fail git -c include.path=one config test.one\n '\n \n+test_expect_success 'absolute includes from blobs work' '\n+\techo \"[test]one = 1\" >one &&\n+\techo \"[include]path=$(pwd)/one\" >blob &&\n+\tblob=$(git hash-object -w blob) &&\n+\techo 1 >expect &&\n+\tgit config --blob=$blob test.one >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'relative includes from blobs fail' '\n+\techo \"[test]one = 1\" >one &&\n+\techo \"[include]path=one\" >blob &&\n+\tblob=$(git hash-object -w blob) &&\n+\ttest_must_fail git config --blob=$blob test.one\n+'\n+\n test_expect_success 'include cycles are detected' '\n \tcat >.gitconfig <<-\\EOF &&\n \t[test]value = gitconfig\n-- \n1.9.0.rc3\n"},{"id":"235032","messageId":"1392764335-13274-2-git-send-email-kirill@shutemov.name","threadId":"35912","inReplyTo":"1392764335-13274-1-git-send-email-kirill@shutemov.name","subject":"[PATCH 2/4] builtin/config.c: rename check_blob_write() -> check_write()","fromName":"Kirill A. Shutemov","fromEmail":"kirill@shutemov.name","sentAt":"2014-02-18T22:58:53Z","receivedAt":"2014-02-18T22:58:53Z","isPatch":true,"sender":{"key":"kirill@shutemov.name","avatar":"https://avatars.githubusercontent.com/u/588661?v=4"},"body":"The function will be reused to check for other conditions which prevent\nwrite.\n\nSigned-off-by: Kirill A. Shutemov <kirill@shutemov.name>\n---\n builtin/config.c | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92ebf23f0a9a..a7c55e68883c 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -362,7 +362,7 @@ static int get_colorbool(int print)\n \t\treturn get_colorbool_found ? 0 : 1;\n }\n \n-static void check_blob_write(void)\n+static void check_write(void)\n {\n \tif (given_config_blob)\n \t\tdie(\"writing config blobs is not supported\");\n@@ -572,7 +572,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_SET) {\n \t\tint ret;\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\tret = git_config_set_in_file(given_config_file, argv[0], value);\n@@ -582,21 +582,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\treturn ret;\n \t}\n \telse if (actions == ACTION_SET_ALL) {\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\treturn git_config_set_multivar_in_file(given_config_file,\n \t\t\t\t\t\t       argv[0], value, argv[2], 0);\n \t}\n \telse if (actions == ACTION_ADD) {\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\treturn git_config_set_multivar_in_file(given_config_file,\n \t\t\t\t\t\t       argv[0], value, \"^$\", 0);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n \t\treturn git_config_set_multivar_in_file(given_config_file,\n@@ -623,7 +623,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\treturn get_urlmatch(argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_UNSET) {\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\treturn git_config_set_multivar_in_file(given_config_file,\n@@ -633,14 +633,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t\t      argv[0], NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\treturn git_config_set_multivar_in_file(given_config_file,\n \t\t\t\t\t\t       argv[0], NULL, argv[1], 1);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n \t\tret = git_config_rename_section_in_file(given_config_file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n@@ -651,7 +651,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_REMOVE_SECTION) {\n \t\tint ret;\n-\t\tcheck_blob_write();\n+\t\tcheck_write();\n \t\tcheck_argc(argc, 1, 1);\n \t\tret = git_config_rename_section_in_file(given_config_file,\n \t\t\t\t\t\t\targv[0], NULL);\n-- \n1.9.0.rc3\n"},{"id":"235033","messageId":"1392764335-13274-3-git-send-email-kirill@shutemov.name","threadId":"35912","inReplyTo":"1392764335-13274-1-git-send-email-kirill@shutemov.name","subject":"[PATCH 3/4] config: change git_config_with_options() interface","fromName":"Kirill A. Shutemov","fromEmail":"kirill@shutemov.name","sentAt":"2014-02-18T22:58:54Z","receivedAt":"2014-02-18T22:58:54Z","isPatch":true,"sender":{"key":"kirill@shutemov.name","avatar":"https://avatars.githubusercontent.com/u/588661?v=4"},"body":"We're going to have more options for config source.\n\nLet's alter git_config_with_options() interface to accept struct with\nall source options.\n\nSigned-off-by: Kirill A. Shutemov <kirill@shutemov.name>\n---\n builtin/config.c | 75 ++++++++++++++++++++++++++------------------------------\n cache.h          |  8 ++++--\n config.c         | 13 +++++-----\n 3 files changed, 47 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex a7c55e68883c..de41ba50e9ca 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -21,8 +21,7 @@ static char key_delim = ' ';\n static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n-static const char *given_config_file;\n-static const char *given_config_blob;\n+static struct git_config_source given_config_source;\n static int actions, types;\n static const char *get_color_slot, *get_colorbool_slot;\n static int end_null;\n@@ -55,8 +54,8 @@ static struct option builtin_config_options[] = {\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n \tOPT_BOOL(0, \"system\", &use_system_config, N_(\"use system config file\")),\n \tOPT_BOOL(0, \"local\", &use_local_config, N_(\"use repository config file\")),\n-\tOPT_STRING('f', \"file\", &given_config_file, N_(\"file\"), N_(\"use given config file\")),\n-\tOPT_STRING(0, \"blob\", &given_config_blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n+\tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")),\n+\tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n \tOPT_GROUP(N_(\"Action\")),\n \tOPT_BIT(0, \"get\", &actions, N_(\"get value: name [value-regex]\"), ACTION_GET),\n \tOPT_BIT(0, \"get-all\", &actions, N_(\"get all values: key [value-regex]\"), ACTION_GET_ALL),\n@@ -224,8 +223,7 @@ static int get_value(const char *key_, const char *regex_)\n \t}\n \n \tgit_config_with_options(collect_config, &values,\n-\t\t\t\tgiven_config_file, given_config_blob,\n-\t\t\t\trespect_includes);\n+\t\t\t\t&given_config_source, respect_includes);\n \n \tret = !values.nr;\n \n@@ -309,8 +307,7 @@ static void get_color(const char *def_color)\n \tget_color_found = 0;\n \tparsed_color[0] = '\\0';\n \tgit_config_with_options(git_get_color_config, NULL,\n-\t\t\t\tgiven_config_file, given_config_blob,\n-\t\t\t\trespect_includes);\n+\t\t\t\t&given_config_source, respect_includes);\n \n \tif (!get_color_found && def_color)\n \t\tcolor_parse(def_color, \"command line\", parsed_color);\n@@ -339,8 +336,7 @@ static int get_colorbool(int print)\n \tget_diff_color_found = -1;\n \tget_color_ui_found = -1;\n \tgit_config_with_options(git_get_colorbool_config, NULL,\n-\t\t\t\tgiven_config_file, given_config_blob,\n-\t\t\t\trespect_includes);\n+\t\t\t\t&given_config_source, respect_includes);\n \n \tif (get_colorbool_found < 0) {\n \t\tif (!strcmp(get_colorbool_slot, \"color.diff\"))\n@@ -364,7 +360,7 @@ static int get_colorbool(int print)\n \n static void check_write(void)\n {\n-\tif (given_config_blob)\n+\tif (given_config_source.blob)\n \t\tdie(\"writing config blobs is not supported\");\n }\n \n@@ -435,7 +431,7 @@ static int get_urlmatch(const char *var, const char *url)\n \t}\n \n \tgit_config_with_options(urlmatch_config_entry, &config,\n-\t\t\t\tgiven_config_file, NULL, respect_includes);\n+\t\t\t\t&given_config_source, respect_includes);\n \n \tfor_each_string_list_item(item, &values) {\n \t\tstruct urlmatch_current_candidate_value *matched = item->util;\n@@ -464,14 +460,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \tint nongit = !startup_info->have_repository;\n \tchar *value;\n \n-\tgiven_config_file = getenv(CONFIG_ENVIRONMENT);\n+\tgiven_config_source.file = getenv(CONFIG_ENVIRONMENT);\n \n \targc = parse_options(argc, argv, prefix, builtin_config_options,\n \t\t\t     builtin_config_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tif (use_global_config + use_system_config + use_local_config +\n-\t    !!given_config_file + !!given_config_blob > 1) {\n+\t    !!given_config_source.file + !!given_config_source.blob > 1) {\n \t\terror(\"only one config file at a time.\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\n@@ -493,24 +489,24 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\tif (access_or_warn(user_config, R_OK, 0) &&\n \t\t    xdg_config && !access_or_warn(xdg_config, R_OK, 0))\n-\t\t\tgiven_config_file = xdg_config;\n+\t\t\tgiven_config_source.file = xdg_config;\n \t\telse\n-\t\t\tgiven_config_file = user_config;\n+\t\t\tgiven_config_source.file = user_config;\n \t}\n \telse if (use_system_config)\n-\t\tgiven_config_file = git_etc_gitconfig();\n+\t\tgiven_config_source.file = git_etc_gitconfig();\n \telse if (use_local_config)\n-\t\tgiven_config_file = git_pathdup(\"config\");\n-\telse if (given_config_file) {\n-\t\tif (!is_absolute_path(given_config_file) && prefix)\n-\t\t\tgiven_config_file =\n+\t\tgiven_config_source.file = git_pathdup(\"config\");\n+\telse if (given_config_source.file) {\n+\t\tif (!is_absolute_path(given_config_source.file) && prefix)\n+\t\t\tgiven_config_source.file =\n \t\t\t\txstrdup(prefix_filename(prefix,\n \t\t\t\t\t\t\tstrlen(prefix),\n-\t\t\t\t\t\t\tgiven_config_file));\n+\t\t\t\t\t\t\tgiven_config_source.file));\n \t}\n \n \tif (respect_includes == -1)\n-\t\trespect_includes = !given_config_file;\n+\t\trespect_includes = !given_config_source.file;\n \n \tif (end_null) {\n \t\tterm = '\\0';\n@@ -549,25 +545,24 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \tif (actions == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n \t\tif (git_config_with_options(show_all_config, NULL,\n-\t\t\t\t\t    given_config_file,\n-\t\t\t\t\t    given_config_blob,\n+\t\t\t\t\t    &given_config_source,\n \t\t\t\t\t    respect_includes) < 0) {\n-\t\t\tif (given_config_file)\n+\t\t\tif (given_config_source.file)\n \t\t\t\tdie_errno(\"unable to read config file '%s'\",\n-\t\t\t\t\t  given_config_file);\n+\t\t\t\t\t  given_config_source.file);\n \t\t\telse\n \t\t\t\tdie(\"error processing config file(s)\");\n \t\t}\n \t}\n \telse if (actions == ACTION_EDIT) {\n \t\tcheck_argc(argc, 0, 0);\n-\t\tif (!given_config_file && nongit)\n+\t\tif (!given_config_source.file && nongit)\n \t\t\tdie(\"not in a git directory\");\n-\t\tif (given_config_blob)\n+\t\tif (given_config_source.blob)\n \t\t\tdie(\"editing blobs is not supported\");\n \t\tgit_config(git_default_config, NULL);\n-\t\tlaunch_editor(given_config_file ?\n-\t\t\t      given_config_file : git_path(\"config\"),\n+\t\tlaunch_editor(given_config_source.file ?\n+\t\t\t      given_config_source.file : git_path(\"config\"),\n \t\t\t      NULL, NULL);\n \t}\n \telse if (actions == ACTION_SET) {\n@@ -575,7 +570,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n-\t\tret = git_config_set_in_file(given_config_file, argv[0], value);\n+\t\tret = git_config_set_in_file(given_config_source.file, argv[0], value);\n \t\tif (ret == CONFIG_NOTHING_SET)\n \t\t\terror(\"cannot overwrite multiple values with a single value\\n\"\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\", argv[0]);\n@@ -585,21 +580,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n-\t\treturn git_config_set_multivar_in_file(given_config_file,\n+\t\treturn git_config_set_multivar_in_file(given_config_source.file,\n \t\t\t\t\t\t       argv[0], value, argv[2], 0);\n \t}\n \telse if (actions == ACTION_ADD) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n-\t\treturn git_config_set_multivar_in_file(given_config_file,\n+\t\treturn git_config_set_multivar_in_file(given_config_source.file,\n \t\t\t\t\t\t       argv[0], value, \"^$\", 0);\n \t}\n \telse if (actions == ACTION_REPLACE_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n-\t\treturn git_config_set_multivar_in_file(given_config_file,\n+\t\treturn git_config_set_multivar_in_file(given_config_source.file,\n \t\t\t\t\t\t       argv[0], value, argv[2], 1);\n \t}\n \telse if (actions == ACTION_GET) {\n@@ -626,23 +621,23 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n-\t\t\treturn git_config_set_multivar_in_file(given_config_file,\n+\t\t\treturn git_config_set_multivar_in_file(given_config_source.file,\n \t\t\t\t\t\t\t       argv[0], NULL, argv[1], 0);\n \t\telse\n-\t\t\treturn git_config_set_in_file(given_config_file,\n+\t\t\treturn git_config_set_in_file(given_config_source.file,\n \t\t\t\t\t\t      argv[0], NULL);\n \t}\n \telse if (actions == ACTION_UNSET_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n-\t\treturn git_config_set_multivar_in_file(given_config_file,\n+\t\treturn git_config_set_multivar_in_file(given_config_source.file,\n \t\t\t\t\t\t       argv[0], NULL, argv[1], 1);\n \t}\n \telse if (actions == ACTION_RENAME_SECTION) {\n \t\tint ret;\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n-\t\tret = git_config_rename_section_in_file(given_config_file,\n+\t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], argv[1]);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n@@ -653,7 +648,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tint ret;\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 1);\n-\t\tret = git_config_rename_section_in_file(given_config_file,\n+\t\tret = git_config_rename_section_in_file(given_config_source.file,\n \t\t\t\t\t\t\targv[0], NULL);\n \t\tif (ret < 0)\n \t\t\treturn ret;\ndiff --git a/cache.h b/cache.h\nindex dc040fb1aa99..9d94bd69f7db 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1146,6 +1146,11 @@ extern int update_server_info(int);\n #define CONFIG_INVALID_PATTERN 6\n #define CONFIG_GENERIC_ERROR 7\n \n+struct git_config_source {\n+\tconst char *file;\n+\tconst char *blob;\n+};\n+\n typedef int (*config_fn_t)(const char *, const char *, void *);\n extern int git_default_config(const char *, const char *, void *);\n extern int git_config_from_file(config_fn_t fn, const char *, void *);\n@@ -1155,8 +1160,7 @@ extern void git_config_push_parameter(const char *text);\n extern int git_config_from_parameters(config_fn_t fn, void *data);\n extern int git_config(config_fn_t fn, void *);\n extern int git_config_with_options(config_fn_t fn, void *,\n-\t\t\t\t   const char *filename,\n-\t\t\t\t   const char *blob_ref,\n+\t\t\t\t   struct git_config_source *config_source,\n \t\t\t\t   int respect_includes);\n extern int git_config_early(config_fn_t fn, void *, const char *repo_config);\n extern int git_parse_ulong(const char *, unsigned long *);\ndiff --git a/config.c b/config.c\nindex b295310d3c22..a21b0ddadecc 100644\n--- a/config.c\n+++ b/config.c\n@@ -1172,8 +1172,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n }\n \n int git_config_with_options(config_fn_t fn, void *data,\n-\t\t\t    const char *filename,\n-\t\t\t    const char *blob_ref,\n+\t\t\t    struct git_config_source *config_source,\n \t\t\t    int respect_includes)\n {\n \tchar *repo_config = NULL;\n@@ -1191,10 +1190,10 @@ int git_config_with_options(config_fn_t fn, void *data,\n \t * If we have a specific filename, use it. Otherwise, follow the\n \t * regular lookup sequence.\n \t */\n-\tif (filename)\n-\t\treturn git_config_from_file(fn, filename, data);\n-\telse if (blob_ref)\n-\t\treturn git_config_from_blob_ref(fn, blob_ref, data);\n+\tif (config_source && config_source->file)\n+\t\treturn git_config_from_file(fn, config_source->file, data);\n+\telse if (config_source && config_source->blob)\n+\t\treturn git_config_from_blob_ref(fn, config_source->blob, data);\n \n \trepo_config = git_pathdup(\"config\");\n \tret = git_config_early(fn, data, repo_config);\n@@ -1205,7 +1204,7 @@ int git_config_with_options(config_fn_t fn, void *data,\n \n int git_config(config_fn_t fn, void *data)\n {\n-\treturn git_config_with_options(fn, data, NULL, NULL, 1);\n+\treturn git_config_with_options(fn, data, NULL, 1);\n }\n \n /*\n-- \n1.9.0.rc3\n"},{"id":"235031","messageId":"1392764335-13274-4-git-send-email-kirill@shutemov.name","threadId":"35912","inReplyTo":"1392764335-13274-1-git-send-email-kirill@shutemov.name","subject":"[PATCH 4/4] config: teach \"git config --file -\" to read from the standard input","fromName":"Kirill A. Shutemov","fromEmail":"kirill@shutemov.name","sentAt":"2014-02-18T22:58:55Z","receivedAt":"2014-02-18T22:58:55Z","isPatch":true,"sender":{"key":"kirill@shutemov.name","avatar":"https://avatars.githubusercontent.com/u/588661?v=4"},"body":"The patch extends git config --file interface to allow read config from\nstdin.\n\nEditing stdin or setting value in stdin is an error.\n\nInclude by absolute path is allowed in stdin config, but not by relative\npath.\n\nSigned-off-by: Kirill A. Shutemov <kirill@shutemov.name>\n---\n builtin/config.c          | 11 +++++++++++\n cache.h                   |  1 +\n config.c                  | 41 +++++++++++++++++++++++++++--------------\n t/t1300-repo-config.sh    | 17 +++++++++++++++--\n t/t1305-config-include.sh | 16 +++++++++++++++-\n 5 files changed, 69 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex de41ba50e9ca..5677c942b693 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -360,6 +360,9 @@ static int get_colorbool(int print)\n \n static void check_write(void)\n {\n+\tif (given_config_source.use_stdin)\n+\t\tdie(\"writing to stdin is not supported\");\n+\n \tif (given_config_source.blob)\n \t\tdie(\"writing config blobs is not supported\");\n }\n@@ -472,6 +475,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\n \n+\tif (given_config_source.file &&\n+\t\t\t!strcmp(given_config_source.file, \"-\")) {\n+\t\tgiven_config_source.file = NULL;\n+\t\tgiven_config_source.use_stdin = 1;\n+\t}\n+\n \tif (use_global_config) {\n \t\tchar *user_config = NULL;\n \t\tchar *xdg_config = NULL;\n@@ -558,6 +567,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcheck_argc(argc, 0, 0);\n \t\tif (!given_config_source.file && nongit)\n \t\t\tdie(\"not in a git directory\");\n+\t\tif (given_config_source.use_stdin)\n+\t\t\tdie(\"editing stdin is not supported\");\n \t\tif (given_config_source.blob)\n \t\t\tdie(\"editing blobs is not supported\");\n \t\tgit_config(git_default_config, NULL);\ndiff --git a/cache.h b/cache.h\nindex 9d94bd69f7db..4db19b537059 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1147,6 +1147,7 @@ extern int update_server_info(int);\n #define CONFIG_GENERIC_ERROR 7\n \n struct git_config_source {\n+\tunsigned int use_stdin:1;\n \tconst char *file;\n \tconst char *blob;\n };\ndiff --git a/config.c b/config.c\nindex a21b0ddadecc..7b1febdf4af0 100644\n--- a/config.c\n+++ b/config.c\n@@ -1031,24 +1031,35 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)\n \treturn ret;\n }\n \n-int git_config_from_file(config_fn_t fn, const char *filename, void *data)\n+static int do_config_from_file(config_fn_t fn,\n+\t\tconst char *name, const char *path, FILE *f, void *data)\n {\n-\tint ret;\n-\tFILE *f = fopen(filename, \"r\");\n+\tstruct config_source top;\n \n-\tret = -1;\n-\tif (f) {\n-\t\tstruct config_source top;\n+\ttop.u.file = f;\n+\ttop.name = name;\n+\ttop.path = path;\n+\ttop.die_on_error = 1;\n+\ttop.do_fgetc = config_file_fgetc;\n+\ttop.do_ungetc = config_file_ungetc;\n+\ttop.do_ftell = config_file_ftell;\n \n-\t\ttop.u.file = f;\n-\t\ttop.name = top.path = filename;\n-\t\ttop.die_on_error = 1;\n-\t\ttop.do_fgetc = config_file_fgetc;\n-\t\ttop.do_ungetc = config_file_ungetc;\n-\t\ttop.do_ftell = config_file_ftell;\n+\treturn do_config_from(&top, fn, data);\n+}\n \n-\t\tret = do_config_from(&top, fn, data);\n+static int git_config_from_stdin(config_fn_t fn, void *data)\n+{\n+\treturn do_config_from_file(fn, \"<stdin>\", NULL, stdin, data);\n+}\n+\n+int git_config_from_file(config_fn_t fn, const char *filename, void *data)\n+{\n+\tint ret = -1;\n+\tFILE *f;\n \n+\tf = fopen(filename, \"r\");\n+\tif (f) {\n+\t\tret = do_config_from_file(fn, filename, filename, f, data);\n \t\tfclose(f);\n \t}\n \treturn ret;\n@@ -1190,7 +1201,9 @@ int git_config_with_options(config_fn_t fn, void *data,\n \t * If we have a specific filename, use it. Otherwise, follow the\n \t * regular lookup sequence.\n \t */\n-\tif (config_source && config_source->file)\n+\tif (config_source && config_source->use_stdin)\n+\t\treturn git_config_from_stdin(fn, data);\n+\telse if (config_source && config_source->file)\n \t\treturn git_config_from_file(fn, config_source->file, data);\n \telse if (config_source && config_source->blob)\n \t\treturn git_config_from_blob_ref(fn, config_source->blob, data);\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 967359344dab..c9c426c273e5 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -475,15 +475,28 @@ ein.bahn=strasse\n EOF\n \n test_expect_success 'alternative GIT_CONFIG' '\n-\tGIT_CONFIG=other-config git config -l >output &&\n+\tGIT_CONFIG=other-config git config --list >output &&\n \ttest_cmp expect output\n '\n \n test_expect_success 'alternative GIT_CONFIG (--file)' '\n-\tgit config --file other-config -l > output &&\n+\tgit config --file other-config --list >output &&\n \ttest_cmp expect output\n '\n \n+test_expect_success 'alternative GIT_CONFIG (--file=-)' '\n+\tgit config --file - --list <other-config >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'setting a value in stdin is an error' '\n+\ttest_must_fail git config --file - some.value foo\n+'\n+\n+test_expect_success 'editing stdin is an error' '\n+\ttest_must_fail git config --file - --edit\n+'\n+\n test_expect_success 'refer config from subdirectory' '\n \tmkdir x &&\n \t(\ndiff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\nindex 6edd38b39a0b..9ba2ba11c3ca 100755\n--- a/t/t1305-config-include.sh\n+++ b/t/t1305-config-include.sh\n@@ -113,7 +113,7 @@ test_expect_success 'missing include files are ignored' '\n test_expect_success 'absolute includes from command line work' '\n \techo \"[test]one = 1\" >one &&\n \techo 1 >expect &&\n-\tgit -c include.path=\"$PWD/one\" config test.one >actual &&\n+\tgit -c include.path=\"$(pwd)/one\" config test.one >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -138,6 +138,20 @@ test_expect_success 'relative includes from blobs fail' '\n \ttest_must_fail git config --blob=$blob test.one\n '\n \n+test_expect_success 'absolute includes from stdin work' '\n+\techo \"[test]one = 1\" >one &&\n+\techo 1 >expect &&\n+\techo \"[include]path=\\\"$(pwd)/one\\\"\" |\n+\tgit config --file - test.one >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'relative includes from stdin line fail' '\n+\techo \"[test]one = 1\" >one &&\n+\techo \"[include]path=one\" |\n+\ttest_must_fail git config --file - test.one\n+'\n+\n test_expect_success 'include cycles are detected' '\n \tcat >.gitconfig <<-\\EOF &&\n \t[test]value = gitconfig\n-- \n1.9.0.rc3\n"},{"id":"235172","messageId":"20140222083744.GG1576@sigill.intra.peff.net","threadId":"35912","inReplyTo":"1392764335-13274-4-git-send-email-kirill@shutemov.name","subject":"Re: [PATCH 4/4] config: teach \"git config --file -\" to read from the standard input","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-02-22T08:37:44Z","receivedAt":"2014-02-22T08:37:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Feb 19, 2014 at 12:58:55AM +0200, Kirill A. Shutemov wrote:\n\n> The patch extends git config --file interface to allow read config from\n> stdin.\n> \n> Editing stdin or setting value in stdin is an error.\n> \n> Include by absolute path is allowed in stdin config, but not by relative\n> path.\n> \n> Signed-off-by: Kirill A. Shutemov <kirill@shutemov.name>\n> ---\n\nThis version of the series looks good to me.\n\n-Peff\n"}]}