{"thread":{"id":"35868","subject":"[PATCH] config: git_config_from_file(): handle \"-\" filename as stdin","startedAt":"2014-02-14T13:34:38Z","lastAt":"2014-02-14T20:35:19Z","messageCount":4,"participants":["Kirill A. Shutemov","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"234767","messageId":"1392384878-7080-1-git-send-email-kirill@shutemov.name","threadId":"35868","inReplyTo":null,"subject":"[PATCH] config: git_config_from_file(): handle \"-\" filename as stdin","fromName":"Kirill A. Shutemov","fromEmail":"kirill@shutemov.name","sentAt":"2014-02-14T13:34:38Z","receivedAt":"2014-02-14T13:34:38Z","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\nSigned-off-by: Kirill A. Shutemov <kirill@shutemov.name>\n---\n config.c               | 10 ++++++----\n t/t1300-repo-config.sh |  4 ++++\n 2 files changed, 10 insertions(+), 4 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex d969a5aefc2b..f80cc7e657e8 100644\n--- a/config.c\n+++ b/config.c\n@@ -1032,10 +1032,11 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)\n \n int git_config_from_file(config_fn_t fn, const char *filename, void *data)\n {\n-\tint ret;\n-\tFILE *f = fopen(filename, \"r\");\n+\tint from_stdin = !strcmp(filename, \"-\");\n+\tint ret = -1;\n+\tFILE *f;\n \n-\tret = -1;\n+\tf = from_stdin ? stdin : fopen(filename, \"r\");\n \tif (f) {\n \t\tstruct config_source top;\n \n@@ -1048,7 +1049,8 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)\n \n \t\tret = do_config_from(&top, fn, data);\n \n-\t\tfclose(f);\n+\t\tif (!from_stdin)\n+\t\t\tfclose(f);\n \t}\n \treturn ret;\n }\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 967359344dab..f1a63075e34f 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -484,6 +484,10 @@ test_expect_success 'alternative GIT_CONFIG (--file)' '\n \ttest_cmp expect output\n '\n \n+test_expect_success 'alternative GIT_CONFIG (--file=-)' '\n+\tgit config --file - -l < other-config > output &&\n+\ttest_cmp expect output\n+'\n test_expect_success 'refer config from subdirectory' '\n \tmkdir x &&\n \t(\n-- \n1.8.5.2\n"},{"id":"234782","messageId":"xmqqlhxdim80.fsf@gitster.dls.corp.google.com","threadId":"35868","inReplyTo":"1392384878-7080-1-git-send-email-kirill@shutemov.name","subject":"Re: [PATCH] config: git_config_from_file(): handle \"-\" filename as stdin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-02-14T18:27:11Z","receivedAt":"2014-02-14T18:27:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kirill A. Shutemov\" <kirill@shutemov.name> writes:\n\n> The patch extends git config --file interface to allow read config from\n> stdin.\n\nThanks.  The external interface proposed by this change that behaves\nthe way your new test expects is a good addition to the system.  I\nwould describe it as:\n\n  Subject: config: teach \"git config --file -\" to read from the standard input\n\nI however think the patch implements it at the level that is too low\nin the callchain.  It will affect a lot more than the dash given to\n\"git config --file -\".  Fortunately, it does not make it possible\nfor users to make this mistake\n\n\t[include]\n        \tpath = -\n\nand scratch their heads, wondering why \"git config\" is not answering\nuntil they hit ^D.  But that is _only_ because we check if a file\nwhose name is \"-\" actually exists in the current directory before\nfalling into this codepath (and usually no such file exists).  If\nsuch a funnily-named file does exist, we read from that file, not\nthe standard input.  So that \"include\" codepath happens to be safe,\nbut who knows what dragons lie in other codepaths that call this\nfunction.\n\nI recall that an earlier implementation of \"git diff --no-index\"\nthat made \"-\" read one side to be compared from the standard input\nhad exactly the same issue of comparing filename with \"-\", which we\nhad to fix with code reorganization recently.  I'd prefer to see\nthis update to \"git config --file -\" done the right way from the\nstart.\n\n> diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> index 967359344dab..f1a63075e34f 100755\n> --- a/t/t1300-repo-config.sh\n> +++ b/t/t1300-repo-config.sh\n> @@ -484,6 +484,10 @@ test_expect_success 'alternative GIT_CONFIG (--file)' '\n>  \ttest_cmp expect output\n>  '\n>  \n> +test_expect_success 'alternative GIT_CONFIG (--file=-)' '\n> +\tgit config --file - -l < other-config > output &&\n\nPlease leave no SP between redirection operator and its file, i.e.\n\n\tgit config --file - --list <other-config >output\n\nThanks.\n"},{"id":"234806","messageId":"20140214200435.GA13633@node.dhcp.inet.fi","threadId":"35868","inReplyTo":"xmqqlhxdim80.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] config: git_config_from_file(): handle \"-\" filename as stdin","fromName":"Kirill A. Shutemov","fromEmail":"kirill@shutemov.name","sentAt":"2014-02-14T20:04:35Z","receivedAt":"2014-02-14T20:04:35Z","isPatch":true,"sender":{"key":"kirill@shutemov.name","avatar":"https://avatars.githubusercontent.com/u/588661?v=4"},"body":"On Fri, Feb 14, 2014 at 10:27:11AM -0800, Junio C Hamano wrote:\n> \"Kirill A. Shutemov\" <kirill@shutemov.name> writes:\n> \n> > The patch extends git config --file interface to allow read config from\n> > stdin.\n> \n> Thanks.  The external interface proposed by this change that behaves\n> the way your new test expects is a good addition to the system.  I\n> would describe it as:\n> \n>   Subject: config: teach \"git config --file -\" to read from the standard input\n> \n> I however think the patch implements it at the level that is too low\n> in the callchain.  It will affect a lot more than the dash given to\n> \"git config --file -\".  Fortunately, it does not make it possible\n> for users to make this mistake\n> \n> \t[include]\n>         \tpath = -\n> \n> and scratch their heads, wondering why \"git config\" is not answering\n> until they hit ^D.  But that is _only_ because we check if a file\n> whose name is \"-\" actually exists in the current directory before\n> falling into this codepath (and usually no such file exists).  If\n> such a funnily-named file does exist, we read from that file, not\n> the standard input.  So that \"include\" codepath happens to be safe,\n> but who knows what dragons lie in other codepaths that call this\n> function.\n> \n> I recall that an earlier implementation of \"git diff --no-index\"\n> that made \"-\" read one side to be compared from the standard input\n> had exactly the same issue of comparing filename with \"-\", which we\n> had to fix with code reorganization recently.  I'd prefer to see\n> this update to \"git config --file -\" done the right way from the\n> start.\n\nOkay, reworked version is below. It's slightly more invasive then the\noriginal.\n\n>From 199e6a995bb5228578e66189ef586421a4d8d9ba Mon Sep 17 00:00:00 2001\nFrom: \"Kirill A. Shutemov\" <kirill@shutemov.name>\nDate: Fri, 14 Feb 2014 21:59:39 +0200\nSubject: [PATCH] config: teach \"git config --file -\" to read from the standard\n input\n\nThe patch extends git config --file interface to allow read config from\nstdin.\n\nEditing stdin or setting value in stdin is an error.\n\nSigned-off-by: Kirill A. Shutemov <kirill@shutemov.name>\n---\n builtin/config.c       | 39 ++++++++++++++++++++++++++-------------\n cache.h                |  1 +\n config.c               | 41 +++++++++++++++++++++++++++--------------\n t/t1300-repo-config.sh | 17 +++++++++++++++--\n 4 files changed, 69 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92ebf23f0a9a..625f914c44a0 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -21,6 +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 int use_stdin;\n static const char *given_config_file;\n static const char *given_config_blob;\n static int actions, types;\n@@ -224,7 +225,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\tuse_stdin, given_config_file, given_config_blob,\n \t\t\t\trespect_includes);\n \n \tret = !values.nr;\n@@ -309,7 +310,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\tuse_stdin, given_config_file, given_config_blob,\n \t\t\t\trespect_includes);\n \n \tif (!get_color_found && def_color)\n@@ -339,7 +340,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\tuse_stdin, given_config_file, given_config_blob,\n \t\t\t\trespect_includes);\n \n \tif (get_colorbool_found < 0) {\n@@ -362,8 +363,11 @@ 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 (use_stdin)\n+\t\tdie(\"writing to stdin is not supported\");\n+\n \tif (given_config_blob)\n \t\tdie(\"writing config blobs is not supported\");\n }\n@@ -435,7 +439,8 @@ 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\tuse_stdin, given_config_file, NULL,\n+\t\t\t\trespect_includes);\n \n \tfor_each_string_list_item(item, &values) {\n \t\tstruct urlmatch_current_candidate_value *matched = item->util;\n@@ -476,6 +481,11 @@ 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_file && !strcmp(given_config_file, \"-\")) {\n+\t\tgiven_config_file = NULL;\n+\t\tuse_stdin = 1;\n+\t}\n+\n \tif (use_global_config) {\n \t\tchar *user_config = NULL;\n \t\tchar *xdg_config = NULL;\n@@ -549,6 +559,7 @@ 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    use_stdin,\n \t\t\t\t\t    given_config_file,\n \t\t\t\t\t    given_config_blob,\n \t\t\t\t\t    respect_includes) < 0) {\n@@ -563,6 +574,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tcheck_argc(argc, 0, 0);\n \t\tif (!given_config_file && nongit)\n \t\t\tdie(\"not in a git directory\");\n+\t\tif (use_stdin)\n+\t\t\tdie(\"editing stdin is not supported\");\n \t\tif (given_config_blob)\n \t\t\tdie(\"editing blobs is not supported\");\n \t\tgit_config(git_default_config, NULL);\n@@ -572,7 +585,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 +595,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 +636,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 +646,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 +664,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);\ndiff --git a/cache.h b/cache.h\nindex dc040fb1aa99..538c28a1564a 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1155,6 +1155,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   int use_stdin,\n \t\t\t\t   const char *filename,\n \t\t\t\t   const char *blob_ref,\n \t\t\t\t   int respect_includes);\ndiff --git a/config.c b/config.c\nindex d969a5aefc2b..53dd39f0b9ef 100644\n--- a/config.c\n+++ b/config.c\n@@ -1030,24 +1030,34 @@ 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, const char *filename, FILE *f,\n+\t\t\t       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 = filename;\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+\treturn do_config_from(&top, fn, data);\n+}\n \n-\t\ttop.u.file = f;\n-\t\ttop.name = 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+static int git_config_from_stdin(config_fn_t fn, void *data)\n+{\n+\treturn do_config_from_file(fn, \"<stdin>\", stdin, data);\n+}\n \n-\t\tret = do_config_from(&top, fn, data);\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, f, data);\n \t\tfclose(f);\n \t}\n \treturn ret;\n@@ -1170,6 +1180,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    int use_stdin,\n \t\t\t    const char *filename,\n \t\t\t    const char *blob_ref,\n \t\t\t    int respect_includes)\n@@ -1189,6 +1200,8 @@ 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 (use_stdin)\n+\t\treturn git_config_from_stdin(fn, data);\n \tif (filename)\n \t\treturn git_config_from_file(fn, filename, data);\n \telse if (blob_ref)\n@@ -1203,7 +1216,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, 0, NULL, NULL, 1);\n }\n \n /*\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(\n-- \n Kirill A. Shutemov\n"},{"id":"234808","messageId":"xmqqzjlth1q0.fsf@gitster.dls.corp.google.com","threadId":"35868","inReplyTo":"20140214200435.GA13633@node.dhcp.inet.fi","subject":"Re: [PATCH] config: git_config_from_file(): handle \"-\" filename as stdin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-02-14T20:35:19Z","receivedAt":"2014-02-14T20:35:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kirill A. Shutemov\" <kirill@shutemov.name> writes:\n\n> On Fri, Feb 14, 2014 at 10:27:11AM -0800, Junio C Hamano wrote:\n> ...\n>> I recall that an earlier implementation of \"git diff --no-index\"\n>> that made \"-\" read one side to be compared from the standard input\n>> had exactly the same issue of comparing filename with \"-\", which we\n>> had to fix with code reorganization recently.  I'd prefer to see\n>> this update to \"git config --file -\" done the right way from the\n>> start.\n>\n> Okay, reworked version is below. It's slightly more invasive then the\n> original.\n\nThanks.\n\nIt looks more invasive mostly because you renamed check_blob_write()\nto check_write().  While I think that rename is a right thing to do\n(it used to be \"if we are attempting to write to blob, signal an\nerror\", but we could have called it check_write(), meaning \"we are\nattempting to write; error out if that should not be allowed\"), it\nwould have been much easier to review and comment if this were done\nas a separate clean-up preparatory patch.  It wouldn't have had to\ntouch this many lines, I would think.\n\nAnd \"clean-up existing code as a separate step, without changing the\nbehaviour, before starting to work on a new feature\" is actively\nencouraged around here.\n\n> From 199e6a995bb5228578e66189ef586421a4d8d9ba Mon Sep 17 00:00:00 2001\n> From: \"Kirill A. Shutemov\" <kirill@shutemov.name>\n> Date: Fri, 14 Feb 2014 21:59:39 +0200\n> Subject: [PATCH] config: teach \"git config --file -\" to read from the standard\n>  input\n\nI do not see a need for these four lines in your e-mail.  All the\nnecessary information is already in your e-mail header.  Please drop\nthem, perhaps except for the \"Subject:\" in-body header.\n\nIf you are going to have a discussion and then your proposed patch,\nplease do it this way (without the 8-space indentation I added for\nillustration) using the \"-- >8 --\" scissors line:\n\n        ...\n        Okay, reworked version is below.\n\n        -- >8 --\n        Subject: config: teach \"git config --file -\" to read from the standard input\n\n        Extend \"git config --file\" interface to allow reading from\n        the standard input\n\n        Editing or setting value is an error.\n\n        Signed-off-by: ...\n        ---\n         diffstat and patch here\n        \nThe in-body header \"Subject: \" is there to let you retitle the\ncommit.\n\n> Editing stdin or setting value in stdin is an error.\n\nGood thinking.  Even comes with tests to cover these cases.  Good.\n\n> diff --git a/builtin/config.c b/builtin/config.c\n> index 92ebf23f0a9a..625f914c44a0 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -21,6 +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 int use_stdin;\n>  static const char *given_config_file;\n>  static const char *given_config_blob;\n\nIf we are changing the function signature of git_config_with_options()\nanyway, would it be even a better idea to introduce something like:\n\n\tstruct config_source {\n        \tint use_stdin;\n                const char *config_file;\n                const char *config_blob;\n\t};\n\n        static struct config_source given_config_source;\n\nand have one _fewer_ parameter to the function, instead of adding\none extra parameter?\n\n> diff --git a/config.c b/config.c\n> index d969a5aefc2b..53dd39f0b9ef 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -1030,24 +1030,34 @@ 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, const char *filename, FILE *f,\n> +\t\t\t       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 = filename;\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> +\treturn do_config_from(&top, fn, data);\n> +}\n>  \n> -\t\ttop.u.file = f;\n> -\t\ttop.name = 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> +static int git_config_from_stdin(config_fn_t fn, void *data)\n> +{\n> +\treturn do_config_from_file(fn, \"<stdin>\", stdin, data);\n> +}\n\nSo the above callchain will set top.name to the string \"<stdin>\".\nHow would that interact with the code in handle_path_include() that\nmakes sure that relative config includes are only allowed in file\nwith known location?\n\nOther than that, I didn't spot any issue in this round looks.  It\nseems to be almost there.\n\nThanks.\n"}]}