{"thread":{"id":"64128","subject":"[PATCH 0/5] builtin/config: bug fixes for \"get\" subcommand with \"--type=color\"","startedAt":"2025-09-11T13:25:00Z","lastAt":"2025-09-22T16:29:13Z","messageCount":36,"participants":["Patrick Steinhardt","Kristoffer Haugsbakk","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"526121","messageId":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","threadId":"64128","inReplyTo":null,"subject":"[PATCH 0/5] builtin/config: bug fixes for \"get\" subcommand with \"--type=color\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-11T13:24:53Z","receivedAt":"2025-09-11T13:25:00Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis small patch series contains two bug fixes for `git config get\n--type=color`:\n\n  - We restore the behaviour where we can now parse colors without a\n    config key.\n\n  - We stop spawning the pager when the user requests to print ANSI\n    color escape sequences.\n\nFurthermore, the patch series does some lighter refactorings of t1300.\nThat test file still has its fair share of issues, but at least it looks\na bit less dirty now.\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (5):\n      t1300: write test expectations in the test's body\n      t1300: small style fixups\n      builtin/config: do not die in `get_color()`\n      builtin/config: special-case retrieving colors without a key\n      builtin/config: do not spawn pager when printing color codes\n\n builtin/config.c  |  20 +++-\n t/t1300-config.sh | 344 +++++++++++++++++++++++++++---------------------------\n 2 files changed, 185 insertions(+), 179 deletions(-)\n\n\n---\nbase-commit: ab427cd991100e94792fce124b0934135abdea4b\nchange-id: 20250911-pks-config-color-e5b8a213e895\n\n"},{"id":"526122","messageId":"20250911-pks-config-color-v1-1-3a7c79df65b1@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH 1/5] t1300: write test expectations in the test's body","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-11T13:24:54Z","receivedAt":"2025-09-11T13:25:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"There are a bunch of tests in t1300 where we write the test expectation\nhanded over to `test_cmp ()` outside of the test body. This does not\nmatch our modern test style, and there isn't really a reason why this\nwould need to happen outside of the test bodies.\n\nConvert those to instead do so as part of the test itself.\n\nNote that there are two exceptions that we don't convert. In both of\nthese cases the expectation is reused across multiple tests, and thus a\nconversion where we'd move that into the first test that uses the\nexpectation would be invalid. Those are simply left as-is for now.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 290 ++++++++++++++++++++++++------------------------------\n 1 file changed, 129 insertions(+), 161 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f856821839..bde9bda234 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -134,38 +134,39 @@ test_expect_success 'clear default config' '\n \trm -f .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'initial' '\n+\tcat >expect <<EOF &&\n [section]\n \tpenguin = little blue\n EOF\n-test_expect_success 'initial' '\n \tgit config ${mode_set} section.penguin \"little blue\" &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'mixed case' '\n+\tcat >expect <<EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n EOF\n-test_expect_success 'mixed case' '\n \tgit config ${mode_set} Section.Movie BadPhysics &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'similar section' '\n+\tcat >expect <<EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'similar section' '\n \tgit config ${mode_set} Sections.WhatEver Second &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'uppercase section' '\n+\tcat >expect <<EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n@@ -173,7 +174,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'uppercase section' '\n \tgit config ${mode_set} SECTION.UPPERCASE true &&\n \ttest_cmp expect .git/config\n '\n@@ -186,7 +186,8 @@ test_expect_success 'replace with non-match (actually matching)' '\n \tgit config section.penguin \"very blue\" !kingpin\n '\n \n-cat > expect << EOF\n+test_expect_success 'append comments' '\n+\tcat >expect <<EOF &&\n [section]\n \tMovie = BadPhysics\n \tUPPERCASE = true\n@@ -198,8 +199,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-\n-test_expect_success 'append comments' '\n \tgit config --replace-all --comment=\"Pygoscelis papua\" section.penguin gentoo &&\n \tgit config ${mode_set} --comment=\"find fish\" section.disposition peckish &&\n \tgit config ${mode_set} --comment=\"#abc\" section.foo bar &&\n@@ -214,7 +213,9 @@ test_expect_success 'Prohibited LF in comment' '\n \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n '\n \n-test_expect_success 'non-match result' 'test_cmp expect .git/config'\n+test_expect_success 'non-match result' '\n+\ttest_cmp expect .git/config\n+'\n \n test_expect_success 'find mixed-case key by canonical name' '\n \ttest_cmp_config Second sections.whatever\n@@ -265,14 +266,15 @@ test_expect_success 'unset with cont. lines' '\n \tgit config ${mode_unset} beta.baz\n '\n \n-cat > expect <<\\EOF\n-[alpha]\n-bar = foo\n-[beta]\n-foo = bar\n-EOF\n-\n-test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'\n+test_expect_success 'unset with cont. lines is correct' '\n+\tcat >expect <<-\\EOF &&\n+\t[alpha]\n+\tbar = foo\n+\t[beta]\n+\tfoo = bar\n+\tEOF\n+\ttest_cmp expect .git/config\n+'\n \n cat > .git/config << EOF\n [beta] ; silly comment # another comment\n@@ -292,16 +294,15 @@ test_expect_success 'multiple unset' '\n \tgit config ${mode_unset_all} beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'multiple unset is correct' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'multiple unset is correct' '\n \ttest_cmp expect .git/config\n '\n \n@@ -318,37 +319,38 @@ test_expect_success '--replace-all' '\n \tgit config ${mode_replace_all} beta.haha gamma\n '\n \n-cat > expect << EOF\n+test_expect_success 'all replaced' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = gamma\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'all replaced' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = alpha\n [nextSection] noNewline = ouch\n EOF\n-test_expect_success 'really mean test' '\n+\n \tgit config ${mode_set} beta.haha alpha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -356,7 +358,6 @@ noIndent= sillyValue ; 'nother silly comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'really really mean test' '\n \tgit config ${mode_set} nextsection.nonewline wow &&\n \ttest_cmp expect .git/config\n '\n@@ -365,23 +366,24 @@ test_expect_success 'get value' '\n \ttest_cmp_config alpha beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'unset' '\n \tgit config ${mode_unset} beta.haha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar' '\n+\tcat  >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -389,7 +391,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar' '\n \tgit config nextsection.NoNewLine \"wow2 for me\" \"for me$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -415,9 +416,10 @@ test_expect_success 'multi-valued get-all returns all' '\n \ttest_cmp expect actual\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar replace' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -425,7 +427,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow3\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar replace' '\n \tgit config nextsection.nonewline \"wow3\" \"wow$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -438,17 +439,16 @@ test_expect_success 'invalid unset' '\n \ttest_must_fail git config ${mode_unset} somesection.nonewline\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tNoNewLine = wow2 for me\n EOF\n-\n-test_expect_success 'multivar unset' '\n \tcase \"$mode\" in\n \tlegacy)\n \t\tgit config --unset nextsection.nonewline \"wow3$\";;\n@@ -466,9 +466,10 @@ test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n '\n \n-cat > expect << EOF\n+test_expect_success 'hierarchical section value' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -479,19 +480,16 @@ noIndent= sillyValue ; 'nother silly comment\n [Version \"1.2.3eX\"]\n \tAlpha = beta\n EOF\n-\n-test_expect_success 'hierarchical section value' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n-beta.noindent=sillyValue\n-nextsection.nonewline=wow2 for me\n-123456.a123=987\n-version.1.2.3eX.alpha=beta\n-EOF\n-\n test_expect_success 'working --list' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent=sillyValue\n+\tnextsection.nonewline=wow2 for me\n+\t123456.a123=987\n+\tversion.1.2.3eX.alpha=beta\n+\tEOF\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n@@ -500,44 +498,40 @@ test_expect_success '--list without repo produces empty output' '\n \ttest_must_be_empty output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-123456.a123\n-version.1.2.3eX.alpha\n-EOF\n-\n test_expect_success '--name-only --list' '\n+\tcat >expect <<-EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\t123456.a123\n+\tversion.1.2.3eX.alpha\n+\tEOF\n \tgit config ${mode_prefix}list --name-only >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent sillyValue\n-nextsection.nonewline wow2 for me\n-EOF\n-\n test_expect_success '--get-regexp' '\n+\tcat >expect <<-EOF &&\n+\tbeta.noindent sillyValue\n+\tnextsection.nonewline wow2 for me\n+\tEOF\n \tgit config ${mode_get_regexp} in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-EOF\n-\n test_expect_success '--name-only --get-regexp' '\n+\tcat >expect <<-EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\tEOF\n \tgit config ${mode_get_regexp} --name-only in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-wow2 for me\n-wow4 for you\n-EOF\n-\n test_expect_success '--add' '\n+\tcat >expect <<-EOF &&\n+\twow2 for me\n+\twow4 for you\n+\tEOF\n \tgit config --add nextsection.nonewline \"wow4 for you\" &&\n \tgit config ${mode_get_all} nextsection.nonewline > output &&\n \ttest_cmp expect output\n@@ -558,37 +552,32 @@ test_expect_success 'get variable with empty value' '\n \tgit config --get emptyvalue.variable ^$\n '\n \n-echo novalue.variable > expect\n-\n test_expect_success 'get-regexp variable with no value' '\n+\techo novalue.variable >expect &&\n \tgit config ${mode_get_regexp} novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'novalue.variable true' > expect\n-\n test_expect_success 'get-regexp --bool variable with no value' '\n+\techo \"novalue.variable true\" >expect &&\n \tgit config ${mode_get_regexp} --bool novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'emptyvalue.variable ' > expect\n-\n test_expect_success 'get-regexp variable with empty value' '\n+\techo \"emptyvalue.variable \" >expect &&\n \tgit config ${mode_get_regexp} emptyvalue > output &&\n \ttest_cmp expect output\n '\n \n-echo true > expect\n-\n test_expect_success 'get bool variable with no value' '\n+\techo true >expect &&\n \tgit config --bool novalue.variable > output &&\n \ttest_cmp expect output\n '\n \n-echo false > expect\n-\n test_expect_success 'get bool variable with empty value' '\n+\techo false > expect &&\n \tgit config --bool emptyvalue.variable > output &&\n \ttest_cmp expect output\n '\n@@ -604,19 +593,19 @@ cat > .git/config << EOF\n \tc = d\n EOF\n \n-cat > expect << EOF\n+test_expect_success 'new section is partial match of another' '\n+\tcat >expect <<EOF &&\n [a.b]\n \tc = d\n [a]\n \tx = y\n EOF\n-\n-test_expect_success 'new section is partial match of another' '\n \tgit config a.x y &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'new variable inserts into proper section' '\n+\tcat >expect <<EOF &&\n [a.b]\n \tc = d\n [a]\n@@ -625,8 +614,6 @@ cat > expect << EOF\n [b]\n \tx = y\n EOF\n-\n-test_expect_success 'new variable inserts into proper section' '\n \tgit config b.x y &&\n \tgit config a.b c &&\n \ttest_cmp expect .git/config\n@@ -642,11 +629,10 @@ cat > other-config << EOF\n \tbahn = strasse\n EOF\n \n-cat > expect << EOF\n-ein.bahn=strasse\n-EOF\n-\n test_expect_success 'alternative GIT_CONFIG' '\n+\tcat >expect <<-EOF &&\n+\tein.bahn=strasse\n+\tEOF\n \tGIT_CONFIG=other-config git config ${mode_prefix}list >output &&\n \ttest_cmp expect output\n '\n@@ -675,14 +661,13 @@ test_expect_success 'refer config from subdirectory' '\n \ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n '\n \n-cat > expect << EOF\n+test_expect_success '--set in alternative file' '\n+\tcat >expect <<\\EOF &&\n [ein]\n \tbahn = strasse\n [anwohner]\n \tpark = ausweis\n EOF\n-\n-test_expect_success '--set in alternative file' '\n \tgit config --file=other-config anwohner.park ausweis &&\n \ttest_cmp expect other-config\n '\n@@ -730,7 +715,8 @@ test_expect_success 'rename another section' '\n \tgit config ${mode_prefix}rename-section branch.\"1 234 blabl/a\" branch.drei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -740,8 +726,6 @@ cat > expect << EOF\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -753,7 +737,8 @@ test_expect_success 'rename a section with a var on the same line' '\n \tgit config ${mode_prefix}rename-section branch.vier branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -765,8 +750,6 @@ weird\n [branch \"zwei\"]\n \tz = 1\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -816,32 +799,29 @@ test_expect_success 'remove section' '\n \tgit config ${mode_prefix}remove-section branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'section was removed properly' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'section was removed properly' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'section ending' '\n+\tcat >expect <<\\EOF &&\n [gitcvs]\n \tenabled = true\n \tdbname = %Ggitcvs2.%a.%m.sqlite\n [gitcvs \"ext\"]\n \tdbname = %Ggitcvs1.%a.%m.sqlite\n EOF\n-\n-test_expect_success 'section ending' '\n \trm -f .git/config &&\n \tgit config ${mode_set} gitcvs.enabled true &&\n \tgit config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&\n \tgit config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&\n \ttest_cmp expect .git/config\n-\n '\n \n test_expect_success numbers '\n@@ -885,19 +865,17 @@ test_expect_success 'invalid stdin config' '\n \ttest_grep \"bad config line 1 in standard input\" output\n '\n \n-cat > expect << EOF\n-true\n-false\n-true\n-false\n-true\n-false\n-true\n-false\n-EOF\n-\n test_expect_success bool '\n-\n+\tcat >expect <<-\\EOF &&\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\tEOF\n \tgit config ${mode_set} bool.true1 01 &&\n \tgit config ${mode_set} bool.true2 -1 &&\n \tgit config ${mode_set} bool.true3 YeS &&\n@@ -923,7 +901,8 @@ test_expect_success 'invalid bool (set)' '\n \n \ttest_must_fail git config --bool bool.nobool foobar'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --bool' '\n+\tcat >expect<<\\EOF &&\n [bool]\n \ttrue1 = true\n \ttrue2 = true\n@@ -934,9 +913,6 @@ cat > expect <<\\EOF\n \tfalse3 = false\n \tfalse4 = false\n EOF\n-\n-test_expect_success 'set --bool' '\n-\n \trm -f .git/config &&\n \tgit config --bool bool.true1 01 &&\n \tgit config --bool bool.true2 -1 &&\n@@ -948,15 +924,13 @@ test_expect_success 'set --bool' '\n \tgit config --bool bool.false4 FALSE &&\n \ttest_cmp expect .git/config'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --int' '\n+\tcat >expect <<\\EOF &&\n [int]\n \tval1 = 1\n \tval2 = -1\n \tval3 = 5242880\n EOF\n-\n-test_expect_success 'set --int' '\n-\n \trm -f .git/config &&\n \tgit config --int int.val1 01 &&\n \tgit config --int int.val2 -1 &&\n@@ -994,7 +968,8 @@ test_expect_success 'get --bool-or-int' '\n \ttest_cmp expect actual\n '\n \n-cat >expect <<\\EOF\n+test_expect_success 'set --bool-or-int' '\n+\tcat >expect <<\\EOF &&\n [bool]\n \ttrue1 = true\n \tfalse1 = false\n@@ -1005,8 +980,6 @@ cat >expect <<\\EOF\n \tint2 = 1\n \tint3 = -1\n EOF\n-\n-test_expect_success 'set --bool-or-int' '\n \trm -f .git/config &&\n \tgit config --bool-or-int bool.true1 true &&\n \tgit config --bool-or-int bool.false1 false &&\n@@ -1018,14 +991,13 @@ test_expect_success 'set --bool-or-int' '\n \ttest_cmp expect .git/config\n '\n \n-cat >expect <<\\EOF\n+test_expect_success !MINGW 'set --path' '\n+\tcat >expect <<\\EOF &&\n [path]\n \thome = ~/\n \tnormal = /dev/null\n \ttrailingtilde = foo~\n EOF\n-\n-test_expect_success !MINGW 'set --path' '\n \trm -f .git/config &&\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n@@ -1037,25 +1009,23 @@ then\n \ttest_set_prereq HOMEVAR\n fi\n \n-cat >expect <<EOF\n-$HOME/\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success HOMEVAR 'get --path' '\n+\tcat >expect <<-EOF &&\n+\t$HOME/\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \tgit config --get --path path.home > result &&\n \tgit config --get --path path.normal >> result &&\n \tgit config --get --path path.trailingtilde >> result &&\n \ttest_cmp expect result\n '\n \n-cat >expect <<\\EOF\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success !MINGW 'get --path copes with unset $HOME' '\n+\tcat >expect <<-\\EOF &&\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \t(\n \t\tsane_unset HOME &&\n \t\ttest_must_fail git config --get --path path.home \\\n@@ -1112,12 +1082,11 @@ test_expect_success 'get --type=color' '\n \ttest_cmp expect actual\n '\n \n-cat >expect << EOF\n+test_expect_success 'set --type=color' '\n+\tcat >expect <<EOF &&\n [foo]\n \tcolor = red\n EOF\n-\n-test_expect_success 'set --type=color' '\n \trm .git/config &&\n \tgit config --type=color foo.color \"red\" &&\n \ttest_cmp expect .git/config\n@@ -1133,14 +1102,14 @@ test_expect_success 'set --type=color barfs on non-color' '\n \ttest_grep \"cannot parse color\" error\n '\n \n-cat > expect << EOF\n+test_expect_success 'quoting' '\n+\tcat >expect <<EOF &&\n [quote]\n \tleading = \" test\"\n \tending = \"test \"\n \tsemicolon = \"test;test\"\n \thash = \"test#test\"\n EOF\n-test_expect_success 'quoting' '\n \trm -f .git/config &&\n \tgit config ${mode_set} quote.leading \" test\" &&\n \tgit config ${mode_set} quote.ending \"test \" &&\n@@ -1166,13 +1135,12 @@ inued\n inued\"\n EOF\n \n-cat > expect <<\\EOF\n+test_expect_success 'value continued on next line' '\n+\tcat >expect <<\\EOF &&\n section.continued=continued\n section.noncont=not continued\n section.quotecont=cont;inued\n EOF\n-\n-test_expect_success 'value continued on next line' '\n \tgit config ${mode_prefix}list > result &&\n \ttest_cmp expect result\n '\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526123","messageId":"20250911-pks-config-color-v1-2-3a7c79df65b1@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH 2/5] t1300: small style fixups","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-11T13:24:55Z","receivedAt":"2025-09-11T13:25:05Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We have a couple of small style violations in t1300:\n\n  - An empty newline at the start of the test body.\n\n  - The test command is sometimes on the same line as the test name.\n\n  - The closing single-quote is sometimes on the same line as the last\n    command of the test.\n\nFix these.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 34 +++++++++++++++++++++-------------\n 1 file changed, 21 insertions(+), 13 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex bde9bda234..9c405e9532 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -458,9 +458,13 @@ EOF\n \ttest_cmp expect .git/config\n '\n \n-test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'\n+test_expect_success 'invalid key' '\n+\ttest_must_fail git config inval.2key blabla\n+'\n \n-test_expect_success 'correct key' 'git config 123456.a123 987'\n+test_expect_success 'correct key' '\n+\tgit config 123456.a123 987\n+'\n \n test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n@@ -493,6 +497,7 @@ test_expect_success 'working --list' '\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n+\n test_expect_success '--list without repo produces empty output' '\n \tgit --git-dir=nonexistent config ${mode_prefix}list >output &&\n \ttest_must_be_empty output\n@@ -890,16 +895,17 @@ test_expect_success bool '\n \t    git config --bool --get bool.true$i >>result &&\n \t    git config --bool --get bool.false$i >>result || return 1\n \tdone &&\n-\ttest_cmp expect result'\n+\ttest_cmp expect result\n+'\n \n test_expect_success 'invalid bool (--get)' '\n-\n \tgit config ${mode_set} bool.nobool foobar &&\n-\ttest_must_fail git config --bool --get bool.nobool'\n+\ttest_must_fail git config --bool --get bool.nobool\n+'\n \n test_expect_success 'invalid bool (set)' '\n-\n-\ttest_must_fail git config --bool bool.nobool foobar'\n+\ttest_must_fail git config --bool bool.nobool foobar\n+'\n \n test_expect_success 'set --bool' '\n \tcat >expect<<\\EOF &&\n@@ -1002,7 +1008,8 @@ EOF\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n \tgit config --path path.trailingtilde \"foo~\" &&\n-\ttest_cmp expect .git/config'\n+\ttest_cmp expect .git/config\n+'\n \n if test_have_prereq !MINGW && test \"${HOME+set}\"\n then\n@@ -1120,10 +1127,13 @@ EOF\n \n test_expect_success 'key with newline' '\n \ttest_must_fail git config ${mode_get} \"key.with\n-newline\" 123'\n+newline\" 123\n+'\n \n-test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\\\\\n-newline'\n+test_expect_success 'value with newline' '\n+\tgit config ${mode_set} key.sub value.with\\\\\\\n+newline\n+'\n \n cat > .git/config <<\\EOF\n [section]\n@@ -1333,7 +1343,6 @@ test_expect_success 'multiple git -c appends config' '\n '\n \n test_expect_success 'last one wins: two level vars' '\n-\n \t# sec.var and sec.VAR are the same variable, as the first\n \t# and the last level of a configuration variable name is\n \t# case insensitive.\n@@ -1352,7 +1361,6 @@ test_expect_success 'last one wins: two level vars' '\n '\n \n test_expect_success 'last one wins: three level vars' '\n-\n \t# v.a.r and v.A.r are not the same variable, as the middle\n \t# level of a three-level configuration variable name is\n \t# case sensitive.\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526124","messageId":"20250911-pks-config-color-v1-3-3a7c79df65b1@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH 3/5] builtin/config: do not die in `get_color()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-11T13:24:56Z","receivedAt":"2025-09-11T13:25:09Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"When trying to parse an invalid color via `get_color()` we die. We're\nabout to introduce another caller in a subsequent commit though that has\nits own error handling, so dying is a bit drastic there. Furthermore,\nthe only caller that we already have right now already knows to handle\nerrors in other branches that don't call `get_color()`.\n\nConvert the function to instead return an error code to improve its\nflexibility.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 59fb113b07..afd48bfa51 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -547,24 +547,31 @@ static int git_get_color_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static void get_color(const struct config_location_options *opts,\n+static int get_color(const struct config_location_options *opts,\n \t\t      const char *var, const char *def_color)\n {\n \tstruct get_color_config_data data = {\n \t\t.get_color_slot = var,\n \t\t.parsed_color[0] = '\\0',\n \t};\n+\tint ret;\n \n \tconfig_with_options(git_get_color_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n \tif (!data.get_color_found && def_color) {\n-\t\tif (color_parse(def_color, data.parsed_color) < 0)\n-\t\t\tdie(_(\"unable to parse default color value\"));\n+\t\tif (color_parse(def_color, data.parsed_color) < 0) {\n+\t\t\tret = error(_(\"unable to parse default color value\"));\n+\t\t\tgoto out;\n+\t\t}\n \t}\n \n+\tret = 0;\n+\n+out:\n \tfputs(data.parsed_color, stdout);\n+\treturn ret;\n }\n \n struct get_colorbool_config_data {\n@@ -1390,7 +1397,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tget_color(&location_opts, argv[0], argv[1]);\n+\t\tret = get_color(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526125","messageId":"20250911-pks-config-color-v1-4-3a7c79df65b1@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-11T13:24:57Z","receivedAt":"2025-09-11T13:25:12Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Our documentation for git-config(1) has a section where it explains how\nto parse and use colors as Git would configure them. In order to get the\nANSI color escape sequence to reset the colors to normal we recommend\nthe following command:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n\nWhat this command is supposed to do is to not parse any configuration\nkey at all. Instead, it is expected to parse the \"reset\" default value\nand turn it into a proper ANSI color escape sequence.\n\nIt was reported though [1] that this command doesn't work:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n    error: key does not contain a section:\n\nThis error was introduced with 4e51389000 (builtin/config: introduce\n\"get\" subcommand, 2024-05-06), where we introduced the new \"get\"\nsubcommand to retrieve configuration values. The preimage of that commit\nused `git config --get-color \"\" \"reset\"` instead, which still works\nnowadays.\n\nThis use case is really quite specific to parsing colors, as it wouldn't\nmake sense to give git-config(1) a default value and an empty config key\nonly to return that default value unmodified. But with `--type=color` we\ndon't return the value directly, but we instead parse the value into an\nANSI escape sequence.\n\nAs such, we can easily special-case this one use case: if the provided\nconfig key is empty, the user is asking for a color code and the user\nhas provided a value, then we call `get_color()` directly. Do so to\nmake the documented command work as expected.\n\n[1]: <aI+oQvQgnNtC6DVw@szeder.dev>\n\nReported-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  |  2 ++\n t/t1300-config.sh | 11 +++++++++++\n 2 files changed, 13 insertions(+)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex afd48bfa51..f50c11df57 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -923,6 +923,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n+\telse if (display_opts.type == TYPE_COLOR && !strlen(argv[0]) && display_opts.default_value)\n+\t\tret = get_color(&location_opts, \"\", display_opts.default_value);\n \telse\n \t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern,\n \t\t\t\tget_value_flags, flags);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 9c405e9532..40f170cf40 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1084,11 +1084,22 @@ test_expect_success 'get --type=color' '\n \trm .git/config &&\n \tgit config ${mode_set} foo.color \"red\" &&\n \tgit config --get --type=color foo.color >actual.raw &&\n+\tgit config get --type=color foo.color >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \techo \"<RED>\" >expect &&\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'get --type=color with default value only' '\n+\tgit config --get-color \"\" \"red\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual &&\n+\tgit config get --type=color --default=\"red\" \"\" >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<EOF &&\n [foo]\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526126","messageId":"20250911-pks-config-color-v1-5-3a7c79df65b1@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-11T13:24:58Z","receivedAt":"2025-09-11T13:25:15Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"With `git config get --type=color` the user asks us to parse a specific\nconfiguration key and turn the value into an ANSI color escape sequence.\nThe printed string can then for example be used as part of shell scripts\nto reuse the same colors as Git.\n\nRight now though we set up the auto-pager though, which means that the\nstring may instead be written to the pager command. This is of course\nquite nonsensical: there shouldn't be any use case where the color code\nshould end up in the pager instead of in the TTY.\n\nFix this by disabling the pager in case the user is asking us to print\ncolor sequences.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 3 ++-\n t/t1300-config.sh | 9 +++++++++\n 2 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex f50c11df57..6708d91814 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -919,7 +919,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \tlocation_options_init(&location_opts, prefix);\n \tdisplay_options_init(&display_opts);\n \n-\tsetup_auto_pager(\"config\", 1);\n+\tif (display_opts.type != TYPE_COLOR)\n+\t\tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 40f170cf40..26111c175c 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -9,6 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n for mode in legacy subcommands\n do\n@@ -1100,6 +1101,14 @@ test_expect_success 'get --type=color with default value only' '\n \ttest_cmp actual.raw actual-subcommand.raw\n '\n \n+test_expect_success TTY 'get --type=color does not use a pager' '\n+\ttest_config core.pager \"echo foobar\" &&\n+\ttest_terminal git config get --type=color --default=\"red\" \"\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<EOF &&\n [foo]\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526138","messageId":"365d19ca-0a61-44f1-ab31-7e87f47d55e6@app.fastmail.com","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-4-3a7c79df65b1@pks.im","subject":"Re: [PATCH 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-11T16:48:45Z","receivedAt":"2025-09-11T16:49:06Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Thu, Sep 11, 2025, at 15:24, Patrick Steinhardt wrote:\n> Our documentation for git-config(1) has a section where it explains how\n> to parse and use colors as Git would configure them. In order to get the\n\nOkay.  This is simple to find with a `color` search.\n\n> ANSI color escape sequence to reset the colors to normal we recommend\n> the following command:\n>\n>     $ git config get --type=color --default=\"reset\" \"\"\n>\n> What this command is supposed to do is to not parse any configuration\n> key at all.\n\nOr\n\n    This command is not supposed to parse any configuration keys.\n\n> Instead, it is expected to parse the \"reset\" default value\n> and turn it into a proper ANSI color escape sequence.\n>\n> It was reported though [1] that this command doesn't work:\n>\n>     $ git config get --type=color --default=\"reset\" \"\"\n>     error: key does not contain a section:\n>\n> This error was introduced with 4e51389000 (builtin/config: introduce\n\nIMO s/with/in/ ?\n\n> \"get\" subcommand, 2024-05-06), where we introduced the new \"get\"\n\nnit: s/introduced the new/introduced the/\n\n> subcommand to retrieve configuration values. The preimage of that commit\n> used `git config --get-color \"\" \"reset\"` instead, which still works\n> nowadays.\n\nnit: s/still works nowadays/still works/\n\n>\n> This use case is really quite specific to parsing colors, as it wouldn't\n\ns/use case/use-case/\n\n> make sense to give git-config(1) a default value and an empty config key\n> only to return that default value unmodified. But with `--type=color` we\n> don't return the value directly, but we instead parse the value into an\n> ANSI escape sequence.\n\nTwo “but”?  Maybe\n\n    But with `--type=color` we don't return the value directly; we\n    instead parse the value into an ANSI escape sequence.\n\n>\n> As such, we can easily special-case this one use case: if the provided\n\ns/use case/use-case/\n\nLike special-case.\n\n>\n> As such, we can easily special-case this one use case: if the provided\n> config key is empty, the user is asking for a color code and the user\n> has provided a value, then we call `get_color()` directly. Do so to\n> make the documented command work as expected.\n\nIn my opinion this is more difficult to read without an Oxford comma.\nA bullet list could break up the serial comma and the comma used to\nseparate the “then” subclause.\n\n    use-case:\n\n    - if the provided config key is empty;\n\n    - the user is asking for a color code; and\n\n    - the user has provided a value,\n\n    then we ...\n\nIn any case: I think a colon generally means that semicolon will be used\ninstead of serial comma.\n\n>\n> [1]: <aI+oQvQgnNtC6DVw@szeder.dev>\n>\n> Reported-by: SZEDER Gábor <szeder.dev@gmail.com>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n>[snip too technical diff]\n"},{"id":"526139","messageId":"e3ad63d7-7ad6-4a35-b340-0643a49bc143@app.fastmail.com","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-5-3a7c79df65b1@pks.im","subject":"Re: [PATCH 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-11T16:49:20Z","receivedAt":"2025-09-11T16:49:42Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Thu, Sep 11, 2025, at 15:24, Patrick Steinhardt wrote:\n> With `git config get --type=color` the user asks us to parse a specific\n> configuration key and turn the value into an ANSI color escape sequence.\n> The printed string can then for example be used as part of shell scripts\n> to reuse the same colors as Git.\n>\n> Right now though we set up the auto-pager though, which means that the\n\nDouble “though”.\n\n> string may instead be written to the pager command. This is of course\n> quite nonsensical: there shouldn't be any use case where the color code\n\nIMO s/:/;/\n\ns/use case/use-case/\n\n> should end up in the pager instead of in the TTY.\n>\n> Fix this by disabling the pager in case the user is asking us to print\n> color sequences.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n>[snip the diff]\n"},{"id":"526140","messageId":"94eb9052-18e1-4565-8f33-42fdf136e2a4@app.fastmail.com","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-1-3a7c79df65b1@pks.im","subject":"Re: [PATCH 1/5] t1300: write test expectations in the test's body","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-11T16:50:29Z","receivedAt":"2025-09-11T16:50:50Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Thu, Sep 11, 2025, at 15:24, Patrick Steinhardt wrote:\n> There are a bunch of tests in t1300 where we write the test expectation\n> handed over to `test_cmp ()` outside of the test body. This does not\n> match our modern test style, and there isn't really a reason why this\n> would need to happen outside of the test bodies.\n>\n> Convert those to instead do so as part of the test itself.\n>\n> Note that there are two exceptions that we don't convert. In both of\n> these cases the expectation is reused across multiple tests, and thus a\n> conversion where we'd move that into the first test that uses the\n> expectation would be invalid. Those are simply left as-is for now.\n\nThis is just a suggestion (which everything is):\n\n    Note that there are two exceptions that we leave as-is for now since\n    they are reused across tests.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  t/t1300-config.sh | 290 ++++++++++++++++++++++++------------------------------\n>  1 file changed, 129 insertions(+), 161 deletions(-)\n>\n> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n\nFor the following I looked mostly at a color-move etc. diff as well as\nthe commit.\n\n> index f856821839..bde9bda234 100755\n> --- a/t/t1300-config.sh\n> +++ b/t/t1300-config.sh\n> @@ -134,38 +134,39 @@ test_expect_success 'clear default config' '\n>  \trm -f .git/config\n>  '\n>\n> -cat > expect << EOF > +test_expect_success 'initial' '\n> +\tcat >expect <<EOF &&\n>  [section]\n>  \tpenguin = little blue\n>  EOF\n> -test_expect_success 'initial' '\n\nOk.  Correct.  But I see that you are also overall doing a sort of\nnormalization.\n\n• Remove-tabs if it makes sense\n• No variable expansion if it makes sense\n\nAnd here `<<\\EOF` would work too.  Or is that worse style?\n\nI will keep mentioning this throughout the rest.\n\n>  \tgit config ${mode_set} section.penguin \"little blue\" &&\n>  \ttest_cmp expect .git/config\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'mixed case' '\n> +\tcat >expect <<EOF &&\n>  [section]\n>  \tpenguin = little blue\n>  \tMovie = BadPhysics\n>  EOF\n> -test_expect_success 'mixed case' '\n\nOk.  But same as previous.\n\n>  \tgit config ${mode_set} Section.Movie BadPhysics &&\n>  \ttest_cmp expect .git/config\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'similar section' '\n> +\tcat >expect <<EOF &&\n>  [section]\n>  \tpenguin = little blue\n>  \tMovie = BadPhysics\n>  [Sections]\n>  \tWhatEver = Second\n>  EOF\n> -test_expect_success 'similar section' '\n\nDitto.\n\n>  \tgit config ${mode_set} Sections.WhatEver Second &&\n>  \ttest_cmp expect .git/config\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'uppercase section' '\n> +\tcat >expect <<EOF &&\n>  [section]\n>  \tpenguin = little blue\n>  \tMovie = BadPhysics\n> @@ -173,7 +174,6 @@ cat > expect << EOF\n>  [Sections]\n>  \tWhatEver = Second\n>  EOF\n> -test_expect_success 'uppercase section' '\n\nDitto.\n\n>  \tgit config ${mode_set} SECTION.UPPERCASE true &&\n>  \ttest_cmp expect .git/config\n>  '\n> @@ -186,7 +186,8 @@ test_expect_success 'replace with non-match\n> (actually matching)' '\n>  \tgit config section.penguin \"very blue\" !kingpin\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'append comments' '\n> +\tcat >expect <<EOF &&\n>  [section]\n>  \tMovie = BadPhysics\n>  \tUPPERCASE = true\n> @@ -198,8 +199,6 @@ cat > expect << EOF\n>  [Sections]\n>  \tWhatEver = Second\n>  EOF\n> -\n> -test_expect_success 'append comments' '\n\nDitto.\n\n>  \tgit config --replace-all --comment=\"Pygoscelis papua\" section.penguin\n> gentoo &&\n>  \tgit config ${mode_set} --comment=\"find fish\" section.disposition\n> peckish &&\n>  \tgit config ${mode_set} --comment=\"#abc\" section.foo bar &&\n> @@ -214,7 +213,9 @@ test_expect_success 'Prohibited LF in comment' '\n>  \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n>  '\n>\n> -test_expect_success 'non-match result' 'test_cmp expect .git/config'\n> +test_expect_success 'non-match result' '\n> +\ttest_cmp expect .git/config\n> +'\n\nOkay.  You normalize the one line to\n\n    test_expect_success <name> '\n         <body>\n    '\n\nThe same kind of change done in the next patch `t1300: small\nstyle fixups`.\n\n>\n>  test_expect_success 'find mixed-case key by canonical name' '\n>  \ttest_cmp_config Second sections.whatever\n> @@ -265,14 +266,15 @@ test_expect_success 'unset with cont. lines' '\n>  \tgit config ${mode_unset} beta.baz\n>  '\n>\n> -cat > expect <<\\EOF\n> -[alpha]\n> -bar = foo\n> -[beta]\n> -foo = bar\n> -EOF\n> -\n> -test_expect_success 'unset with cont. lines is correct' 'test_cmp\n> expect .git/config'\n> +test_expect_success 'unset with cont. lines is correct' '\n> +\tcat >expect <<-\\EOF &&\n> +\t[alpha]\n> +\tbar = foo\n> +\t[beta]\n> +\tfoo = bar\n> +\tEOF\n> +\ttest_cmp expect .git/config\n> +'\n\nCorrect.  You eliminate tabs and retain `\\`.\n\n>\n>  cat > .git/config << EOF\n>  [beta] ; silly comment # another comment\n> @@ -292,16 +294,15 @@ test_expect_success 'multiple unset' '\n>  \tgit config ${mode_unset_all} beta.haha\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'multiple unset is correct' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n>  [nextSection] noNewline = ouch\n>  EOF\n> -\n> -test_expect_success 'multiple unset is correct' '\n>  \ttest_cmp expect .git/config\n>  '\n\nCorrect.  You replaced `'` with `${SQ}` (I assume “single quote”) which\nis probably good style in general in order to avoid toothpicks.\n\n>\n> @@ -318,37 +319,38 @@ test_expect_success '--replace-all' '\n>  \tgit config ${mode_replace_all} beta.haha gamma\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'all replaced' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n>  \thaha = gamma\n>  [nextSection] noNewline = ouch\n>  EOF\n> -\n> -test_expect_success 'all replaced' '\n>  \ttest_cmp expect .git/config\n>  '\n\nCorrect.\n\n>\n> -cat > expect << EOF\n> +test_expect_success 'really mean test' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n>  \thaha = alpha\n>  [nextSection] noNewline = ouch\n>  EOF\n> -test_expect_success 'really mean test' '\n> +\n\nNew blank line.  That can be dropped.\n\n>  \tgit config ${mode_set} beta.haha alpha &&\n>  \ttest_cmp expect .git/config\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'really really mean test' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n> @@ -356,7 +358,6 @@ noIndent= sillyValue ; 'nother silly comment\n>  [nextSection]\n>  \tnonewline = wow\n>  EOF\n> -test_expect_success 'really really mean test' '\n>  \tgit config ${mode_set} nextsection.nonewline wow &&\n>  \ttest_cmp expect .git/config\n>  '\n\nHuh, the similarity between “really” and “really really” confused me.\nBut correct.\n\n> @@ -365,23 +366,24 @@ test_expect_success 'get value' '\n>  \ttest_cmp_config alpha beta.haha\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'unset' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n>  [nextSection]\n>  \tnonewline = wow\n>  EOF\n> -test_expect_success 'unset' '\n>  \tgit config ${mode_unset} beta.haha &&\n>  \ttest_cmp expect .git/config\n>  '\n\nCorrect.\n\n>\n> -cat > expect << EOF\n> +test_expect_success 'multivar' '\n> +\tcat  >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n> @@ -389,7 +391,6 @@ noIndent= sillyValue ; 'nother silly comment\n>  \tnonewline = wow\n>  \tNoNewLine = wow2 for me\n>  EOF\n> -test_expect_success 'multivar' '\n>  \tgit config nextsection.NoNewLine \"wow2 for me\" \"for me$\" &&\n>  \ttest_cmp expect .git/config\n>  '\n> @@ -415,9 +416,10 @@ test_expect_success 'multi-valued get-all returns all' '\n>  \ttest_cmp expect actual\n>  '\n\nCorrect.\n\n>\n> -cat > expect << EOF\n> +test_expect_success 'multivar replace' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n> @@ -425,7 +427,6 @@ noIndent= sillyValue ; 'nother silly comment\n>  \tnonewline = wow3\n>  \tNoNewLine = wow2 for me\n>  EOF\n> -test_expect_success 'multivar replace' '\n>  \tgit config nextsection.nonewline \"wow3\" \"wow$\" &&\n>  \ttest_cmp expect .git/config\n>  '\n\nCorrect.\n\n> @@ -438,17 +439,16 @@ test_expect_success 'invalid unset' '\n>  \ttest_must_fail git config ${mode_unset} somesection.nonewline\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'multivar unset' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n>  [nextSection]\n>  \tNoNewLine = wow2 for me\n>  EOF\n> -\n> -test_expect_success 'multivar unset' '\n>  \tcase \"$mode\" in\n>  \tlegacy)\n>  \t\tgit config --unset nextsection.nonewline \"wow3$\";;\n\nCorrect.\n\n> @@ -466,9 +466,10 @@ test_expect_success 'hierarchical section' '\n>  \tgit config Version.1.2.3eX.Alpha beta\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'hierarchical section value' '\n> +\tcat >expect <<EOF &&\n>  [beta] ; silly comment # another comment\n> -noIndent= sillyValue ; 'nother silly comment\n> +noIndent= sillyValue ; ${SQ}nother silly comment\n>\n>  # empty line\n>  \t\t; comment\n> @@ -479,19 +480,16 @@ noIndent= sillyValue ; 'nother silly comment\n>  [Version \"1.2.3eX\"]\n>  \tAlpha = beta\n>  EOF\n> -\n> -test_expect_success 'hierarchical section value' '\n>  \ttest_cmp expect .git/config\n>  '\n\nCorrect.\n\n>\n> -cat > expect << EOF\n> -beta.noindent=sillyValue\n> -nextsection.nonewline=wow2 for me\n> -123456.a123=987\n> -version.1.2.3eX.alpha=beta\n> -EOF\n> -\n>  test_expect_success 'working --list' '\n> +\tcat >expect <<-\\EOF &&\n> +\tbeta.noindent=sillyValue\n> +\tnextsection.nonewline=wow2 for me\n> +\t123456.a123=987\n> +\tversion.1.2.3eX.alpha=beta\n> +\tEOF\n>  \tgit config ${mode_prefix}list > output &&\n>  \ttest_cmp expect output\n>  '\n\nCorrect.  Removing tabs now that you can.  Also no variable expansion.\n\n> @@ -500,44 +498,40 @@ test_expect_success '--list without repo produces\n> empty output' '\n>  \ttest_must_be_empty output\n>  '\n>\n> -cat > expect << EOF\n> -beta.noindent\n> -nextsection.nonewline\n> -123456.a123\n> -version.1.2.3eX.alpha\n> -EOF\n> -\n>  test_expect_success '--name-only --list' '\n> +\tcat >expect <<-EOF &&\n> +\tbeta.noindent\n> +\tnextsection.nonewline\n> +\t123456.a123\n> +\tversion.1.2.3eX.alpha\n> +\tEOF\n>  \tgit config ${mode_prefix}list --name-only >output &&\n>  \ttest_cmp expect output\n>  '\n\nCorrect.  But you can use `<<-\\EOF`.\n\n>\n> -cat > expect << EOF\n> -beta.noindent sillyValue\n> -nextsection.nonewline wow2 for me\n> -EOF\n> -\n>  test_expect_success '--get-regexp' '\n> +\tcat >expect <<-EOF &&\n> +\tbeta.noindent sillyValue\n> +\tnextsection.nonewline wow2 for me\n> +\tEOF\n>  \tgit config ${mode_get_regexp} in >output &&\n>  \ttest_cmp expect output\n>  '\n\nDitto.\n\n>\n> -cat > expect << EOF\n> -beta.noindent\n> -nextsection.nonewline\n> -EOF\n> -\n>  test_expect_success '--name-only --get-regexp' '\n> +\tcat >expect <<-EOF &&\n> +\tbeta.noindent\n> +\tnextsection.nonewline\n> +\tEOF\n>  \tgit config ${mode_get_regexp} --name-only in >output &&\n>  \ttest_cmp expect output\n>  '\n\nDitto.\n\n>\n> -cat > expect << EOF\n> -wow2 for me\n> -wow4 for you\n> -EOF\n> -\n>  test_expect_success '--add' '\n> +\tcat >expect <<-EOF &&\n> +\twow2 for me\n> +\twow4 for you\n> +\tEOF\n>  \tgit config --add nextsection.nonewline \"wow4 for you\" &&\n>  \tgit config ${mode_get_all} nextsection.nonewline > output &&\n>  \ttest_cmp expect output\n\nDitto.\n\n> @@ -558,37 +552,32 @@ test_expect_success 'get variable with empty value' '\n>  \tgit config --get emptyvalue.variable ^$\n>  '\n>\n> -echo novalue.variable > expect\n> -\n>  test_expect_success 'get-regexp variable with no value' '\n> +\techo novalue.variable >expect &&\n>  \tgit config ${mode_get_regexp} novalue > output &&\n>  \ttest_cmp expect output\n>  '\n\nCorrect.\n\n>\n> -echo 'novalue.variable true' > expect\n> -\n>  test_expect_success 'get-regexp --bool variable with no value' '\n> +\techo \"novalue.variable true\" >expect &&\n>  \tgit config ${mode_get_regexp} --bool novalue > output &&\n>  \ttest_cmp expect output\n>  '\n\nCorrect.\n\n>\n> -echo 'emptyvalue.variable ' > expect\n> -\n>  test_expect_success 'get-regexp variable with empty value' '\n> +\techo \"emptyvalue.variable \" >expect &&\n>  \tgit config ${mode_get_regexp} emptyvalue > output &&\n>  \ttest_cmp expect output\n>  '\n\nCorrect.\n\n>\n> -echo true > expect\n> -\n>  test_expect_success 'get bool variable with no value' '\n> +\techo true >expect &&\n>  \tgit config --bool novalue.variable > output &&\n>  \ttest_cmp expect output\n>  '\n\nCorrect.\n\n>\n> -echo false > expect\n> -\n>  test_expect_success 'get bool variable with empty value' '\n> +\techo false > expect &&\n>  \tgit config --bool emptyvalue.variable > output &&\n>  \ttest_cmp expect output\n>  '\n\nHere you should use `>expect`.\n\n> @@ -604,19 +593,19 @@ cat > .git/config << EOF\n>  \tc = d\n>  EOF\n>\n> -cat > expect << EOF\n> +test_expect_success 'new section is partial match of another' '\n> +\tcat >expect <<EOF &&\n>  [a.b]\n>  \tc = d\n>  [a]\n>  \tx = y\n>  EOF\n> -\n> -test_expect_success 'new section is partial match of another' '\n>  \tgit config a.x y &&\n>  \ttest_cmp expect .git/config\n>  '\n\nYou can use `<<\\EOF`.\n\n>\n> -cat > expect << EOF\n> +test_expect_success 'new variable inserts into proper section' '\n> +\tcat >expect <<EOF &&\n>  [a.b]\n>  \tc = d\n>  [a]\n> @@ -625,8 +614,6 @@ cat > expect << EOF\n>  [b]\n>  \tx = y\n>  EOF\n> -\n> -test_expect_success 'new variable inserts into proper section' '\n>  \tgit config b.x y &&\n>  \tgit config a.b c &&\n>  \ttest_cmp expect .git/config\n\nCorrect.  You can use `<<\\EOF`.\n\n> @@ -642,11 +629,10 @@ cat > other-config << EOF\n>  \tbahn = strasse\n>  EOF\n>\n> -cat > expect << EOF\n> -ein.bahn=strasse\n> -EOF\n> -\n>  test_expect_success 'alternative GIT_CONFIG' '\n> +\tcat >expect <<-EOF &&\n> +\tein.bahn=strasse\n> +\tEOF\n\nDitto.\n\n>  \tGIT_CONFIG=other-config git config ${mode_prefix}list >output &&\n>  \ttest_cmp expect output\n>  '\n> @@ -675,14 +661,13 @@ test_expect_success 'refer config from subdirectory' '\n>  \ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success '--set in alternative file' '\n> +\tcat >expect <<\\EOF &&\n>  [ein]\n>  \tbahn = strasse\n>  [anwohner]\n>  \tpark = ausweis\n>  EOF\n> -\n> -test_expect_success '--set in alternative file' '\n>  \tgit config --file=other-config anwohner.park ausweis &&\n>  \ttest_cmp expect other-config\n>  '\n\nCorrect.  Here you do `<< EOF` → `<<\\EOF`.\n\n> @@ -730,7 +715,8 @@ test_expect_success 'rename another section' '\n>  \tgit config ${mode_prefix}rename-section branch.\"1 234 blabl/a\" branch.drei\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'rename succeeded' '\n> +\tcat >expect <<\\EOF &&\n>  # Hallo\n>  \t#Bello\n>  [branch \"zwei\"]\n> @@ -740,8 +726,6 @@ cat > expect << EOF\n>  [branch \"drei\"]\n>  weird\n>  EOF\n> -\n> -test_expect_success 'rename succeeded' '\n>  \ttest_cmp expect .git/config\n>  '\n\nDitto.\n\n(That four tests are named `rename succeeded` is a bit disorienting.)\n\n>\n> @@ -753,7 +737,8 @@ test_expect_success 'rename a section with a var on\n> the same line' '\n>  \tgit config ${mode_prefix}rename-section branch.vier branch.zwei\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'rename succeeded' '\n> +\tcat >expect <<\\EOF &&\n>  # Hallo\n>  \t#Bello\n>  [branch \"zwei\"]\n> @@ -765,8 +750,6 @@ weird\n>  [branch \"zwei\"]\n>  \tz = 1\n>  EOF\n> -\n> -test_expect_success 'rename succeeded' '\n>  \ttest_cmp expect .git/config\n>  '\n\nDitto.\n\n>\n> @@ -816,32 +799,29 @@ test_expect_success 'remove section' '\n>  \tgit config ${mode_prefix}remove-section branch.zwei\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'section was removed properly' '\n> +\tcat >expect <<\\EOF &&\n>  # Hallo\n>  \t#Bello\n>  [branch \"drei\"]\n>  weird\n>  EOF\n> -\n> -test_expect_success 'section was removed properly' '\n>  \ttest_cmp expect .git/config\n>  '\n\nDitto.\n\n>\n> -cat > expect << EOF\n> +test_expect_success 'section ending' '\n> +\tcat >expect <<\\EOF &&\n>  [gitcvs]\n>  \tenabled = true\n>  \tdbname = %Ggitcvs2.%a.%m.sqlite\n>  [gitcvs \"ext\"]\n>  \tdbname = %Ggitcvs1.%a.%m.sqlite\n>  EOF\n> -\n> -test_expect_success 'section ending' '\n>  \trm -f .git/config &&\n>  \tgit config ${mode_set} gitcvs.enabled true &&\n>  \tgit config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&\n>  \tgit config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&\n>  \ttest_cmp expect .git/config\n> -\n>  '\n\nDitto.  And normalized blank lines, that’s good.\n\n>\n>  test_expect_success numbers '\n> @@ -885,19 +865,17 @@ test_expect_success 'invalid stdin config' '\n>  \ttest_grep \"bad config line 1 in standard input\" output\n>  '\n>\n> -cat > expect << EOF\n> -true\n> -false\n> -true\n> -false\n> -true\n> -false\n> -true\n> -false\n> -EOF\n> -\n>  test_expect_success bool '\n> -\n> +\tcat >expect <<-\\EOF &&\n> +\ttrue\n> +\tfalse\n> +\ttrue\n> +\tfalse\n> +\ttrue\n> +\tfalse\n> +\ttrue\n> +\tfalse\n> +\tEOF\n>  \tgit config ${mode_set} bool.true1 01 &&\n>  \tgit config ${mode_set} bool.true2 -1 &&\n>  \tgit config ${mode_set} bool.true3 YeS &&\n\nCorrect.\n\n> @@ -923,7 +901,8 @@ test_expect_success 'invalid bool (set)' '\n>\n>  \ttest_must_fail git config --bool bool.nobool foobar'\n>\n> -cat > expect <<\\EOF\n> +test_expect_success 'set --bool' '\n> +\tcat >expect<<\\EOF &&\n>  [bool]\n>  \ttrue1 = true\n>  \ttrue2 = true\n> @@ -934,9 +913,6 @@ cat > expect <<\\EOF\n>  \tfalse3 = false\n>  \tfalse4 = false\n>  EOF\n> -\n> -test_expect_success 'set --bool' '\n> -\n>  \trm -f .git/config &&\n>  \tgit config --bool bool.true1 01 &&\n>  \tgit config --bool bool.true2 -1 &&\n\nCorrect.  But add a space? (>expect<<\\EOF)\n\n> @@ -948,15 +924,13 @@ test_expect_success 'set --bool' '\n>  \tgit config --bool bool.false4 FALSE &&\n>  \ttest_cmp expect .git/config'\n>\n> -cat > expect <<\\EOF\n> +test_expect_success 'set --int' '\n> +\tcat >expect <<\\EOF &&\n>  [int]\n>  \tval1 = 1\n>  \tval2 = -1\n>  \tval3 = 5242880\n>  EOF\n> -\n> -test_expect_success 'set --int' '\n> -\n>  \trm -f .git/config &&\n>  \tgit config --int int.val1 01 &&\n>  \tgit config --int int.val2 -1 &&\n\nCorrect.\n\n> @@ -994,7 +968,8 @@ test_expect_success 'get --bool-or-int' '\n>  \ttest_cmp expect actual\n>  '\n>\n> -cat >expect <<\\EOF\n> +test_expect_success 'set --bool-or-int' '\n> +\tcat >expect <<\\EOF &&\n>  [bool]\n>  \ttrue1 = true\n>  \tfalse1 = false\n> @@ -1005,8 +980,6 @@ cat >expect <<\\EOF\n>  \tint2 = 1\n>  \tint3 = -1\n>  EOF\n> -\n> -test_expect_success 'set --bool-or-int' '\n>  \trm -f .git/config &&\n>  \tgit config --bool-or-int bool.true1 true &&\n>  \tgit config --bool-or-int bool.false1 false &&\n\nCorrect.\n\nI tried with here `<<-\\EOF` but that made something fail. :)\n\n> @@ -1018,14 +991,13 @@ test_expect_success 'set --bool-or-int' '\n>  \ttest_cmp expect .git/config\n>  '\n>\n> -cat >expect <<\\EOF\n> +test_expect_success !MINGW 'set --path' '\n> +\tcat >expect <<\\EOF &&\n>  [path]\n>  \thome = ~/\n>  \tnormal = /dev/null\n>  \ttrailingtilde = foo~\n>  EOF\n> -\n> -test_expect_success !MINGW 'set --path' '\n>  \trm -f .git/config &&\n>  \tgit config --path path.home \"~/\" &&\n>  \tgit config --path path.normal \"/dev/null\" &&\n\nCorrect.\n\n> @@ -1037,25 +1009,23 @@ then\n>  \ttest_set_prereq HOMEVAR\n>  fi\n>\n> -cat >expect <<EOF\n> -$HOME/\n> -/dev/null\n> -foo~\n> -EOF\n> -\n>  test_expect_success HOMEVAR 'get --path' '\n> +\tcat >expect <<-EOF &&\n> +\t$HOME/\n> +\t/dev/null\n> +\tfoo~\n> +\tEOF\n>  \tgit config --get --path path.home > result &&\n>  \tgit config --get --path path.normal >> result &&\n>  \tgit config --get --path path.trailingtilde >> result &&\n>  \ttest_cmp expect result\n>  '\n\nCorrect.\n\n>\n> -cat >expect <<\\EOF\n> -/dev/null\n> -foo~\n> -EOF\n> -\n>  test_expect_success !MINGW 'get --path copes with unset $HOME' '\n> +\tcat >expect <<-\\EOF &&\n> +\t/dev/null\n> +\tfoo~\n> +\tEOF\n>  \t(\n>  \t\tsane_unset HOME &&\n>  \t\ttest_must_fail git config --get --path path.home \\\n\nCorrect.\n\n> @@ -1112,12 +1082,11 @@ test_expect_success 'get --type=color' '\n>  \ttest_cmp expect actual\n>  '\n>\n> -cat >expect << EOF\n> +test_expect_success 'set --type=color' '\n> +\tcat >expect <<EOF &&\n>  [foo]\n>  \tcolor = red\n>  EOF\n> -\n> -test_expect_success 'set --type=color' '\n>  \trm .git/config &&\n>  \tgit config --type=color foo.color \"red\" &&\n>  \ttest_cmp expect .git/config\n\nCorrect.  You can use `<<\\EOF`.\n\n> @@ -1133,14 +1102,14 @@ test_expect_success 'set --type=color barfs on\n> non-color' '\n>  \ttest_grep \"cannot parse color\" error\n>  '\n>\n> -cat > expect << EOF\n> +test_expect_success 'quoting' '\n> +\tcat >expect <<EOF &&\n>  [quote]\n>  \tleading = \" test\"\n>  \tending = \"test \"\n>  \tsemicolon = \"test;test\"\n>  \thash = \"test#test\"\n>  EOF\n> -test_expect_success 'quoting' '\n>  \trm -f .git/config &&\n>  \tgit config ${mode_set} quote.leading \" test\" &&\n>  \tgit config ${mode_set} quote.ending \"test \" &&\n\nCorrect.  You can use `<<\\EOF`.\n\n> @@ -1166,13 +1135,12 @@ inued\n>  inued\"\n>  EOF\n>\n> -cat > expect <<\\EOF\n> +test_expect_success 'value continued on next line' '\n> +\tcat >expect <<\\EOF &&\n>  section.continued=continued\n>  section.noncont=not continued\n>  section.quotecont=cont;inued\n>  EOF\n> -\n> -test_expect_success 'value continued on next line' '\n>  \tgit config ${mode_prefix}list > result &&\n>  \ttest_cmp expect result\n>  '\n\nCorrect.  You can use\n\n\tcat >expect <<-\\EOF &&\n\tsection.continued=continued\n\tsection.noncont=not continued\n\tsection.quotecont=cont;inued\n\tEOF\n\n>\n> --\n> 2.51.0.450.g87641ccf93.dirty\n"},{"id":"526353","messageId":"aMf7XvXasOiUAaxo@pks.im","threadId":"64128","inReplyTo":"94eb9052-18e1-4565-8f33-42fdf136e2a4@app.fastmail.com","subject":"Re: [PATCH 1/5] t1300: write test expectations in the test's body","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T11:41:18Z","receivedAt":"2025-09-15T11:41:24Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Sep 11, 2025 at 06:50:29PM +0200, Kristoffer Haugsbakk wrote:\n> On Thu, Sep 11, 2025, at 15:24, Patrick Steinhardt wrote:\n> > There are a bunch of tests in t1300 where we write the test expectation\n> > handed over to `test_cmp ()` outside of the test body. This does not\n> > match our modern test style, and there isn't really a reason why this\n> > would need to happen outside of the test bodies.\n> >\n> > Convert those to instead do so as part of the test itself.\n> >\n> > Note that there are two exceptions that we don't convert. In both of\n> > these cases the expectation is reused across multiple tests, and thus a\n> > conversion where we'd move that into the first test that uses the\n> > expectation would be invalid. Those are simply left as-is for now.\n> \n> This is just a suggestion (which everything is):\n> \n>     Note that there are two exceptions that we leave as-is for now since\n>     they are reused across tests.\n\nYup, that reads way better.\n\n> > index f856821839..bde9bda234 100755\n> > --- a/t/t1300-config.sh\n> > +++ b/t/t1300-config.sh\n> > @@ -134,38 +134,39 @@ test_expect_success 'clear default config' '\n> >  \trm -f .git/config\n> >  '\n> >\n> > -cat > expect << EOF > +test_expect_success 'initial' '\n> > +\tcat >expect <<EOF &&\n> >  [section]\n> >  \tpenguin = little blue\n> >  EOF\n> > -test_expect_success 'initial' '\n> \n> Ok.  Correct.  But I see that you are also overall doing a sort of\n> normalization.\n> \n> • Remove-tabs if it makes sense\n> • No variable expansion if it makes sense\n> \n> And here `<<\\EOF` would work too.  Or is that worse style?\n> \n> I will keep mentioning this throughout the rest.\n\nNo, it's not. I already did it sometimes, but not consistently. I'll\nmention this in the commit message and adapt as possible.\n\n> > @@ -214,7 +213,9 @@ test_expect_success 'Prohibited LF in comment' '\n> >  \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n> >  '\n> >\n> > -test_expect_success 'non-match result' 'test_cmp expect .git/config'\n> > +test_expect_success 'non-match result' '\n> > +\ttest_cmp expect .git/config\n> > +'\n> \n> Okay.  You normalize the one line to\n> \n>     test_expect_success <name> '\n>          <body>\n>     '\n> \n> The same kind of change done in the next patch `t1300: small\n> style fixups`.\n\nTrue, will move into the next patch.\n\nThanks for going through all of these!\n\nPatrick\n"},{"id":"526354","messageId":"aMf7ZCtK1vr9Djo7@pks.im","threadId":"64128","inReplyTo":"365d19ca-0a61-44f1-ab31-7e87f47d55e6@app.fastmail.com","subject":"Re: [PATCH 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T11:41:24Z","receivedAt":"2025-09-15T11:41:29Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Sep 11, 2025 at 06:48:45PM +0200, Kristoffer Haugsbakk wrote:\n> On Thu, Sep 11, 2025, at 15:24, Patrick Steinhardt wrote:\n> > This use case is really quite specific to parsing colors, as it wouldn't\n> \n> s/use case/use-case/\n\nI used to say \"usecase\", but eventually got corrected by one of our\ntechnical writers at GitLab that this is \"use case\". Wikipedia seems to\nagree [1].\n\n[1]: https://en.wikipedia.org/wiki/Use_case\n\n> > make sense to give git-config(1) a default value and an empty config key\n> > only to return that default value unmodified. But with `--type=color` we\n> > don't return the value directly, but we instead parse the value into an\n> > ANSI escape sequence.\n> \n> Two “but”?  Maybe\n> \n>     But with `--type=color` we don't return the value directly; we\n>     instead parse the value into an ANSI escape sequence.\n\nThat reads nicer, yup.\n\n> > As such, we can easily special-case this one use case: if the provided\n> > config key is empty, the user is asking for a color code and the user\n> > has provided a value, then we call `get_color()` directly. Do so to\n> > make the documented command work as expected.\n> \n> In my opinion this is more difficult to read without an Oxford comma.\n> A bullet list could break up the serial comma and the comma used to\n> separate the “then” subclause.\n> \n>     use-case:\n> \n>     - if the provided config key is empty;\n> \n>     - the user is asking for a color code; and\n> \n>     - the user has provided a value,\n> \n>     then we ...\n> \n> In any case: I think a colon generally means that semicolon will be used\n> instead of serial comma.\n\nYup, good suggestion.\n\nPatrick\n"},{"id":"526359","messageId":"20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH v2 0/5] builtin/config: bug fixes for \"get\" subcommand with \"--type=color\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T12:52:47Z","receivedAt":"2025-09-15T12:52:54Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis small patch series contains two bug fixes for `git config get\n--type=color`:\n\n  - We restore the behaviour where we can now parse colors without a\n    config key.\n\n  - We stop spawning the pager when the user requests to print ANSI\n    color escape sequences.\n\nFurthermore, the patch series does some lighter refactorings of t1300.\nThat test file still has its fair share of issues, but at least it looks\na bit less dirty now.\n\nChanges in v2:\n  - Improve commit messages.\n  - Use \"\\EOF\" and \"-EOF\" in more cases.\n  - Move a style fixup from the first commit into the second commit.\n  - Link to v1: https://lore.kernel.org/r/20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (5):\n      t1300: write test expectations in the test's body\n      t1300: small style fixups\n      builtin/config: do not die in `get_color()`\n      builtin/config: special-case retrieving colors without a key\n      builtin/config: do not spawn pager when printing color codes\n\n builtin/config.c  |  20 +++-\n t/t1300-config.sh | 349 +++++++++++++++++++++++++++---------------------------\n 2 files changed, 187 insertions(+), 182 deletions(-)\n\nRange-diff versus v1:\n\n1:  2920521b26 ! 1:  e58033502e t1300: write test expectations in the test's body\n    @@ Commit message\n         match our modern test style, and there isn't really a reason why this\n         would need to happen outside of the test bodies.\n     \n    -    Convert those to instead do so as part of the test itself.\n    +    Convert those to instead do so as part of the test itself. While at it,\n    +    normalize these tests to use `<<\\EOF` for those that don't use variable\n    +    expansion and `<<-EOF` for those that aren't sensitive to indentation.\n     \n    -    Note that there are two exceptions that we don't convert. In both of\n    -    these cases the expectation is reused across multiple tests, and thus a\n    -    conversion where we'd move that into the first test that uses the\n    -    expectation would be invalid. Those are simply left as-is for now.\n    +    Note that there are two exceptions that we leave as-is for now since\n    +    they are reused across tests.\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n    @@ t/t1300-config.sh: test_expect_success 'clear default config' '\n      \n     -cat > expect << EOF\n     +test_expect_success 'initial' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [section]\n      \tpenguin = little blue\n      EOF\n    @@ t/t1300-config.sh: test_expect_success 'clear default config' '\n      \n     -cat > expect << EOF\n     +test_expect_success 'mixed case' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [section]\n      \tpenguin = little blue\n      \tMovie = BadPhysics\n    @@ t/t1300-config.sh: test_expect_success 'clear default config' '\n      \n     -cat > expect << EOF\n     +test_expect_success 'similar section' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [section]\n      \tpenguin = little blue\n      \tMovie = BadPhysics\n    @@ t/t1300-config.sh: test_expect_success 'clear default config' '\n      \n     -cat > expect << EOF\n     +test_expect_success 'uppercase section' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [section]\n      \tpenguin = little blue\n      \tMovie = BadPhysics\n    @@ t/t1300-config.sh: test_expect_success 'replace with non-match (actually matchin\n      \n     -cat > expect << EOF\n     +test_expect_success 'append comments' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [section]\n      \tMovie = BadPhysics\n      \tUPPERCASE = true\n    @@ t/t1300-config.sh: cat > expect << EOF\n      \tgit config --replace-all --comment=\"Pygoscelis papua\" section.penguin gentoo &&\n      \tgit config ${mode_set} --comment=\"find fish\" section.disposition peckish &&\n      \tgit config ${mode_set} --comment=\"#abc\" section.foo bar &&\n    -@@ t/t1300-config.sh: test_expect_success 'Prohibited LF in comment' '\n    - \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n    - '\n    - \n    --test_expect_success 'non-match result' 'test_cmp expect .git/config'\n    -+test_expect_success 'non-match result' '\n    -+\ttest_cmp expect .git/config\n    -+'\n    - \n    - test_expect_success 'find mixed-case key by canonical name' '\n    - \ttest_cmp_config Second sections.whatever\n     @@ t/t1300-config.sh: test_expect_success 'unset with cont. lines' '\n      \tgit config ${mode_unset} beta.baz\n      '\n    @@ t/t1300-config.sh: test_expect_success '--replace-all' '\n      [nextSection] noNewline = ouch\n      EOF\n     -test_expect_success 'really mean test' '\n    -+\n      \tgit config ${mode_set} beta.haha alpha &&\n      \ttest_cmp expect .git/config\n      '\n    @@ t/t1300-config.sh: test_expect_success '--list without repo produces empty outpu\n     -EOF\n     -\n      test_expect_success '--name-only --list' '\n    -+\tcat >expect <<-EOF &&\n    ++\tcat >expect <<-\\EOF &&\n     +\tbeta.noindent\n     +\tnextsection.nonewline\n     +\t123456.a123\n    @@ t/t1300-config.sh: test_expect_success '--list without repo produces empty outpu\n     -EOF\n     -\n      test_expect_success '--get-regexp' '\n    -+\tcat >expect <<-EOF &&\n    ++\tcat >expect <<-\\EOF &&\n     +\tbeta.noindent sillyValue\n     +\tnextsection.nonewline wow2 for me\n     +\tEOF\n    @@ t/t1300-config.sh: test_expect_success '--list without repo produces empty outpu\n     -EOF\n     -\n      test_expect_success '--name-only --get-regexp' '\n    -+\tcat >expect <<-EOF &&\n    ++\tcat >expect <<-\\EOF &&\n     +\tbeta.noindent\n     +\tnextsection.nonewline\n     +\tEOF\n    @@ t/t1300-config.sh: test_expect_success '--list without repo produces empty outpu\n     -EOF\n     -\n      test_expect_success '--add' '\n    -+\tcat >expect <<-EOF &&\n    ++\tcat >expect <<-\\EOF &&\n     +\twow2 for me\n     +\twow4 for you\n     +\tEOF\n    @@ t/t1300-config.sh: test_expect_success 'get variable with empty value' '\n     -echo false > expect\n     -\n      test_expect_success 'get bool variable with empty value' '\n    -+\techo false > expect &&\n    ++\techo false >expect &&\n      \tgit config --bool emptyvalue.variable > output &&\n      \ttest_cmp expect output\n      '\n    @@ t/t1300-config.sh: cat > .git/config << EOF\n      \n     -cat > expect << EOF\n     +test_expect_success 'new section is partial match of another' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [a.b]\n      \tc = d\n      [a]\n    @@ t/t1300-config.sh: cat > .git/config << EOF\n      \n     -cat > expect << EOF\n     +test_expect_success 'new variable inserts into proper section' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [a.b]\n      \tc = d\n      [a]\n    @@ t/t1300-config.sh: cat > other-config << EOF\n     -EOF\n     -\n      test_expect_success 'alternative GIT_CONFIG' '\n    -+\tcat >expect <<-EOF &&\n    ++\tcat >expect <<-\\EOF &&\n     +\tein.bahn=strasse\n     +\tEOF\n      \tGIT_CONFIG=other-config git config ${mode_prefix}list >output &&\n    @@ t/t1300-config.sh: test_expect_success 'invalid bool (set)' '\n      \n     -cat > expect <<\\EOF\n     +test_expect_success 'set --bool' '\n    -+\tcat >expect<<\\EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [bool]\n      \ttrue1 = true\n      \ttrue2 = true\n    @@ t/t1300-config.sh: test_expect_success 'get --type=color' '\n      \n     -cat >expect << EOF\n     +test_expect_success 'set --type=color' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [foo]\n      \tcolor = red\n      EOF\n    @@ t/t1300-config.sh: test_expect_success 'set --type=color barfs on non-color' '\n      \n     -cat > expect << EOF\n     +test_expect_success 'quoting' '\n    -+\tcat >expect <<EOF &&\n    ++\tcat >expect <<\\EOF &&\n      [quote]\n      \tleading = \" test\"\n      \tending = \"test \"\n    @@ t/t1300-config.sh: inued\n      EOF\n      \n     -cat > expect <<\\EOF\n    -+test_expect_success 'value continued on next line' '\n    -+\tcat >expect <<\\EOF &&\n    - section.continued=continued\n    - section.noncont=not continued\n    - section.quotecont=cont;inued\n    - EOF\n    +-section.continued=continued\n    +-section.noncont=not continued\n    +-section.quotecont=cont;inued\n    +-EOF\n     -\n    --test_expect_success 'value continued on next line' '\n    + test_expect_success 'value continued on next line' '\n    ++\tcat >expect <<-\\EOF &&\n    ++\tsection.continued=continued\n    ++\tsection.noncont=not continued\n    ++\tsection.quotecont=cont;inued\n    ++\tEOF\n      \tgit config ${mode_prefix}list > result &&\n      \ttest_cmp expect result\n      '\n2:  a6a38b98c2 ! 2:  c37605959e t1300: small style fixups\n    @@ Commit message\n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n      ## t/t1300-config.sh ##\n    +@@ t/t1300-config.sh: test_expect_success 'Prohibited LF in comment' '\n    + \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n    + '\n    + \n    +-test_expect_success 'non-match result' 'test_cmp expect .git/config'\n    ++test_expect_success 'non-match result' '\n    ++\ttest_cmp expect .git/config\n    ++'\n    + \n    + test_expect_success 'find mixed-case key by canonical name' '\n    + \ttest_cmp_config Second sections.whatever\n     @@ t/t1300-config.sh: EOF\n      \ttest_cmp expect .git/config\n      '\n    @@ t/t1300-config.sh: test_expect_success bool '\n     +'\n      \n      test_expect_success 'set --bool' '\n    - \tcat >expect<<\\EOF &&\n    + \tcat >expect <<\\EOF &&\n     @@ t/t1300-config.sh: EOF\n      \tgit config --path path.home \"~/\" &&\n      \tgit config --path path.normal \"/dev/null\" &&\n3:  b9fe7190a1 = 3:  d88e317762 builtin/config: do not die in `get_color()`\n4:  1cd6acd0e9 ! 4:  6dfb71e7ce builtin/config: special-case retrieving colors without a key\n    @@ Commit message\n     \n             $ git config get --type=color --default=\"reset\" \"\"\n     \n    -    What this command is supposed to do is to not parse any configuration\n    -    key at all. Instead, it is expected to parse the \"reset\" default value\n    -    and turn it into a proper ANSI color escape sequence.\n    +    This command is not supposed to parse any configuration keys. Instead,\n    +    it is expected to parse the \"reset\" default value and turn it into a\n    +    proper ANSI color escape sequence.\n     \n         It was reported though [1] that this command doesn't work:\n     \n             $ git config get --type=color --default=\"reset\" \"\"\n             error: key does not contain a section:\n     \n    -    This error was introduced with 4e51389000 (builtin/config: introduce\n    -    \"get\" subcommand, 2024-05-06), where we introduced the new \"get\"\n    -    subcommand to retrieve configuration values. The preimage of that commit\n    -    used `git config --get-color \"\" \"reset\"` instead, which still works\n    -    nowadays.\n    +    This error was introduced in 4e51389000 (builtin/config: introduce \"get\"\n    +    subcommand, 2024-05-06), where we introduced the \"get\" subcommand to\n    +    retrieve configuration values. The preimage of that commit used `git\n    +    config --get-color \"\" \"reset\"` instead, which still works.\n     \n         This use case is really quite specific to parsing colors, as it wouldn't\n         make sense to give git-config(1) a default value and an empty config key\n         only to return that default value unmodified. But with `--type=color` we\n    -    don't return the value directly, but we instead parse the value into an\n    -    ANSI escape sequence.\n    +    don't return the value directly; we instead parse the value into an ANSI\n    +    escape sequence.\n     \n    -    As such, we can easily special-case this one use case: if the provided\n    -    config key is empty, the user is asking for a color code and the user\n    -    has provided a value, then we call `get_color()` directly. Do so to\n    -    make the documented command work as expected.\n    +    As such, we can easily special-case this one use case:\n    +\n    +        - If the provided config key is empty;\n    +\n    +        - the user is asking for a color code and the user; and\n    +\n    +        - the user has provided a default value,\n    +\n    +    then we call `get_color()` directly. Do so to make the documented\n    +    command work as expected.\n     \n         [1]: <aI+oQvQgnNtC6DVw@szeder.dev>\n     \n    @@ t/t1300-config.sh: test_expect_success 'get --type=color' '\n     +'\n     +\n      test_expect_success 'set --type=color' '\n    - \tcat >expect <<EOF &&\n    + \tcat >expect <<\\EOF &&\n      [foo]\n5:  46fc98e1ec ! 5:  ad443d744f builtin/config: do not spawn pager when printing color codes\n    @@ Commit message\n         The printed string can then for example be used as part of shell scripts\n         to reuse the same colors as Git.\n     \n    -    Right now though we set up the auto-pager though, which means that the\n    -    string may instead be written to the pager command. This is of course\n    -    quite nonsensical: there shouldn't be any use case where the color code\n    -    should end up in the pager instead of in the TTY.\n    +    Right now though we set up the auto-pager, which means that the string\n    +    may instead be written to the pager command. This is of course quite\n    +    nonsensical; there shouldn't be any use case where the color code should\n    +    end up in the pager instead of in the TTY.\n     \n         Fix this by disabling the pager in case the user is asking us to print\n         color sequences.\n    @@ t/t1300-config.sh: test_expect_success 'get --type=color with default value only\n     +'\n     +\n      test_expect_success 'set --type=color' '\n    - \tcat >expect <<EOF &&\n    + \tcat >expect <<\\EOF &&\n      [foo]\n\n---\nbase-commit: ab427cd991100e94792fce124b0934135abdea4b\nchange-id: 20250911-pks-config-color-e5b8a213e895\n\n"},{"id":"526360","messageId":"20250915-pks-config-color-v2-1-e4290bd8d13c@pks.im","threadId":"64128","inReplyTo":"20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im","subject":"[PATCH v2 1/5] t1300: write test expectations in the test's body","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T12:52:48Z","receivedAt":"2025-09-15T12:52:56Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"There are a bunch of tests in t1300 where we write the test expectation\nhanded over to `test_cmp ()` outside of the test body. This does not\nmatch our modern test style, and there isn't really a reason why this\nwould need to happen outside of the test bodies.\n\nConvert those to instead do so as part of the test itself. While at it,\nnormalize these tests to use `<<\\EOF` for those that don't use variable\nexpansion and `<<-EOF` for those that aren't sensitive to indentation.\n\nNote that there are two exceptions that we leave as-is for now since\nthey are reused across tests.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 291 ++++++++++++++++++++++++------------------------------\n 1 file changed, 128 insertions(+), 163 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f856821839..538f2c9b8a 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -134,38 +134,39 @@ test_expect_success 'clear default config' '\n \trm -f .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'initial' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n EOF\n-test_expect_success 'initial' '\n \tgit config ${mode_set} section.penguin \"little blue\" &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'mixed case' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n EOF\n-test_expect_success 'mixed case' '\n \tgit config ${mode_set} Section.Movie BadPhysics &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'similar section' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'similar section' '\n \tgit config ${mode_set} Sections.WhatEver Second &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'uppercase section' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n@@ -173,7 +174,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'uppercase section' '\n \tgit config ${mode_set} SECTION.UPPERCASE true &&\n \ttest_cmp expect .git/config\n '\n@@ -186,7 +186,8 @@ test_expect_success 'replace with non-match (actually matching)' '\n \tgit config section.penguin \"very blue\" !kingpin\n '\n \n-cat > expect << EOF\n+test_expect_success 'append comments' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tMovie = BadPhysics\n \tUPPERCASE = true\n@@ -198,8 +199,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-\n-test_expect_success 'append comments' '\n \tgit config --replace-all --comment=\"Pygoscelis papua\" section.penguin gentoo &&\n \tgit config ${mode_set} --comment=\"find fish\" section.disposition peckish &&\n \tgit config ${mode_set} --comment=\"#abc\" section.foo bar &&\n@@ -265,14 +264,15 @@ test_expect_success 'unset with cont. lines' '\n \tgit config ${mode_unset} beta.baz\n '\n \n-cat > expect <<\\EOF\n-[alpha]\n-bar = foo\n-[beta]\n-foo = bar\n-EOF\n-\n-test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'\n+test_expect_success 'unset with cont. lines is correct' '\n+\tcat >expect <<-\\EOF &&\n+\t[alpha]\n+\tbar = foo\n+\t[beta]\n+\tfoo = bar\n+\tEOF\n+\ttest_cmp expect .git/config\n+'\n \n cat > .git/config << EOF\n [beta] ; silly comment # another comment\n@@ -292,16 +292,15 @@ test_expect_success 'multiple unset' '\n \tgit config ${mode_unset_all} beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'multiple unset is correct' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'multiple unset is correct' '\n \ttest_cmp expect .git/config\n '\n \n@@ -318,37 +317,37 @@ test_expect_success '--replace-all' '\n \tgit config ${mode_replace_all} beta.haha gamma\n '\n \n-cat > expect << EOF\n+test_expect_success 'all replaced' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = gamma\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'all replaced' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = alpha\n [nextSection] noNewline = ouch\n EOF\n-test_expect_success 'really mean test' '\n \tgit config ${mode_set} beta.haha alpha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -356,7 +355,6 @@ noIndent= sillyValue ; 'nother silly comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'really really mean test' '\n \tgit config ${mode_set} nextsection.nonewline wow &&\n \ttest_cmp expect .git/config\n '\n@@ -365,23 +363,24 @@ test_expect_success 'get value' '\n \ttest_cmp_config alpha beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'unset' '\n \tgit config ${mode_unset} beta.haha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar' '\n+\tcat  >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -389,7 +388,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar' '\n \tgit config nextsection.NoNewLine \"wow2 for me\" \"for me$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -415,9 +413,10 @@ test_expect_success 'multi-valued get-all returns all' '\n \ttest_cmp expect actual\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar replace' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -425,7 +424,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow3\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar replace' '\n \tgit config nextsection.nonewline \"wow3\" \"wow$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -438,17 +436,16 @@ test_expect_success 'invalid unset' '\n \ttest_must_fail git config ${mode_unset} somesection.nonewline\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tNoNewLine = wow2 for me\n EOF\n-\n-test_expect_success 'multivar unset' '\n \tcase \"$mode\" in\n \tlegacy)\n \t\tgit config --unset nextsection.nonewline \"wow3$\";;\n@@ -466,9 +463,10 @@ test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n '\n \n-cat > expect << EOF\n+test_expect_success 'hierarchical section value' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -479,19 +477,16 @@ noIndent= sillyValue ; 'nother silly comment\n [Version \"1.2.3eX\"]\n \tAlpha = beta\n EOF\n-\n-test_expect_success 'hierarchical section value' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n-beta.noindent=sillyValue\n-nextsection.nonewline=wow2 for me\n-123456.a123=987\n-version.1.2.3eX.alpha=beta\n-EOF\n-\n test_expect_success 'working --list' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent=sillyValue\n+\tnextsection.nonewline=wow2 for me\n+\t123456.a123=987\n+\tversion.1.2.3eX.alpha=beta\n+\tEOF\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n@@ -500,44 +495,40 @@ test_expect_success '--list without repo produces empty output' '\n \ttest_must_be_empty output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-123456.a123\n-version.1.2.3eX.alpha\n-EOF\n-\n test_expect_success '--name-only --list' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\t123456.a123\n+\tversion.1.2.3eX.alpha\n+\tEOF\n \tgit config ${mode_prefix}list --name-only >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent sillyValue\n-nextsection.nonewline wow2 for me\n-EOF\n-\n test_expect_success '--get-regexp' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent sillyValue\n+\tnextsection.nonewline wow2 for me\n+\tEOF\n \tgit config ${mode_get_regexp} in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-EOF\n-\n test_expect_success '--name-only --get-regexp' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\tEOF\n \tgit config ${mode_get_regexp} --name-only in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-wow2 for me\n-wow4 for you\n-EOF\n-\n test_expect_success '--add' '\n+\tcat >expect <<-\\EOF &&\n+\twow2 for me\n+\twow4 for you\n+\tEOF\n \tgit config --add nextsection.nonewline \"wow4 for you\" &&\n \tgit config ${mode_get_all} nextsection.nonewline > output &&\n \ttest_cmp expect output\n@@ -558,37 +549,32 @@ test_expect_success 'get variable with empty value' '\n \tgit config --get emptyvalue.variable ^$\n '\n \n-echo novalue.variable > expect\n-\n test_expect_success 'get-regexp variable with no value' '\n+\techo novalue.variable >expect &&\n \tgit config ${mode_get_regexp} novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'novalue.variable true' > expect\n-\n test_expect_success 'get-regexp --bool variable with no value' '\n+\techo \"novalue.variable true\" >expect &&\n \tgit config ${mode_get_regexp} --bool novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'emptyvalue.variable ' > expect\n-\n test_expect_success 'get-regexp variable with empty value' '\n+\techo \"emptyvalue.variable \" >expect &&\n \tgit config ${mode_get_regexp} emptyvalue > output &&\n \ttest_cmp expect output\n '\n \n-echo true > expect\n-\n test_expect_success 'get bool variable with no value' '\n+\techo true >expect &&\n \tgit config --bool novalue.variable > output &&\n \ttest_cmp expect output\n '\n \n-echo false > expect\n-\n test_expect_success 'get bool variable with empty value' '\n+\techo false >expect &&\n \tgit config --bool emptyvalue.variable > output &&\n \ttest_cmp expect output\n '\n@@ -604,19 +590,19 @@ cat > .git/config << EOF\n \tc = d\n EOF\n \n-cat > expect << EOF\n+test_expect_success 'new section is partial match of another' '\n+\tcat >expect <<\\EOF &&\n [a.b]\n \tc = d\n [a]\n \tx = y\n EOF\n-\n-test_expect_success 'new section is partial match of another' '\n \tgit config a.x y &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'new variable inserts into proper section' '\n+\tcat >expect <<\\EOF &&\n [a.b]\n \tc = d\n [a]\n@@ -625,8 +611,6 @@ cat > expect << EOF\n [b]\n \tx = y\n EOF\n-\n-test_expect_success 'new variable inserts into proper section' '\n \tgit config b.x y &&\n \tgit config a.b c &&\n \ttest_cmp expect .git/config\n@@ -642,11 +626,10 @@ cat > other-config << EOF\n \tbahn = strasse\n EOF\n \n-cat > expect << EOF\n-ein.bahn=strasse\n-EOF\n-\n test_expect_success 'alternative GIT_CONFIG' '\n+\tcat >expect <<-\\EOF &&\n+\tein.bahn=strasse\n+\tEOF\n \tGIT_CONFIG=other-config git config ${mode_prefix}list >output &&\n \ttest_cmp expect output\n '\n@@ -675,14 +658,13 @@ test_expect_success 'refer config from subdirectory' '\n \ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n '\n \n-cat > expect << EOF\n+test_expect_success '--set in alternative file' '\n+\tcat >expect <<\\EOF &&\n [ein]\n \tbahn = strasse\n [anwohner]\n \tpark = ausweis\n EOF\n-\n-test_expect_success '--set in alternative file' '\n \tgit config --file=other-config anwohner.park ausweis &&\n \ttest_cmp expect other-config\n '\n@@ -730,7 +712,8 @@ test_expect_success 'rename another section' '\n \tgit config ${mode_prefix}rename-section branch.\"1 234 blabl/a\" branch.drei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -740,8 +723,6 @@ cat > expect << EOF\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -753,7 +734,8 @@ test_expect_success 'rename a section with a var on the same line' '\n \tgit config ${mode_prefix}rename-section branch.vier branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -765,8 +747,6 @@ weird\n [branch \"zwei\"]\n \tz = 1\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -816,32 +796,29 @@ test_expect_success 'remove section' '\n \tgit config ${mode_prefix}remove-section branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'section was removed properly' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'section was removed properly' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'section ending' '\n+\tcat >expect <<\\EOF &&\n [gitcvs]\n \tenabled = true\n \tdbname = %Ggitcvs2.%a.%m.sqlite\n [gitcvs \"ext\"]\n \tdbname = %Ggitcvs1.%a.%m.sqlite\n EOF\n-\n-test_expect_success 'section ending' '\n \trm -f .git/config &&\n \tgit config ${mode_set} gitcvs.enabled true &&\n \tgit config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&\n \tgit config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&\n \ttest_cmp expect .git/config\n-\n '\n \n test_expect_success numbers '\n@@ -885,19 +862,17 @@ test_expect_success 'invalid stdin config' '\n \ttest_grep \"bad config line 1 in standard input\" output\n '\n \n-cat > expect << EOF\n-true\n-false\n-true\n-false\n-true\n-false\n-true\n-false\n-EOF\n-\n test_expect_success bool '\n-\n+\tcat >expect <<-\\EOF &&\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\tEOF\n \tgit config ${mode_set} bool.true1 01 &&\n \tgit config ${mode_set} bool.true2 -1 &&\n \tgit config ${mode_set} bool.true3 YeS &&\n@@ -923,7 +898,8 @@ test_expect_success 'invalid bool (set)' '\n \n \ttest_must_fail git config --bool bool.nobool foobar'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --bool' '\n+\tcat >expect <<\\EOF &&\n [bool]\n \ttrue1 = true\n \ttrue2 = true\n@@ -934,9 +910,6 @@ cat > expect <<\\EOF\n \tfalse3 = false\n \tfalse4 = false\n EOF\n-\n-test_expect_success 'set --bool' '\n-\n \trm -f .git/config &&\n \tgit config --bool bool.true1 01 &&\n \tgit config --bool bool.true2 -1 &&\n@@ -948,15 +921,13 @@ test_expect_success 'set --bool' '\n \tgit config --bool bool.false4 FALSE &&\n \ttest_cmp expect .git/config'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --int' '\n+\tcat >expect <<\\EOF &&\n [int]\n \tval1 = 1\n \tval2 = -1\n \tval3 = 5242880\n EOF\n-\n-test_expect_success 'set --int' '\n-\n \trm -f .git/config &&\n \tgit config --int int.val1 01 &&\n \tgit config --int int.val2 -1 &&\n@@ -994,7 +965,8 @@ test_expect_success 'get --bool-or-int' '\n \ttest_cmp expect actual\n '\n \n-cat >expect <<\\EOF\n+test_expect_success 'set --bool-or-int' '\n+\tcat >expect <<\\EOF &&\n [bool]\n \ttrue1 = true\n \tfalse1 = false\n@@ -1005,8 +977,6 @@ cat >expect <<\\EOF\n \tint2 = 1\n \tint3 = -1\n EOF\n-\n-test_expect_success 'set --bool-or-int' '\n \trm -f .git/config &&\n \tgit config --bool-or-int bool.true1 true &&\n \tgit config --bool-or-int bool.false1 false &&\n@@ -1018,14 +988,13 @@ test_expect_success 'set --bool-or-int' '\n \ttest_cmp expect .git/config\n '\n \n-cat >expect <<\\EOF\n+test_expect_success !MINGW 'set --path' '\n+\tcat >expect <<\\EOF &&\n [path]\n \thome = ~/\n \tnormal = /dev/null\n \ttrailingtilde = foo~\n EOF\n-\n-test_expect_success !MINGW 'set --path' '\n \trm -f .git/config &&\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n@@ -1037,25 +1006,23 @@ then\n \ttest_set_prereq HOMEVAR\n fi\n \n-cat >expect <<EOF\n-$HOME/\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success HOMEVAR 'get --path' '\n+\tcat >expect <<-EOF &&\n+\t$HOME/\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \tgit config --get --path path.home > result &&\n \tgit config --get --path path.normal >> result &&\n \tgit config --get --path path.trailingtilde >> result &&\n \ttest_cmp expect result\n '\n \n-cat >expect <<\\EOF\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success !MINGW 'get --path copes with unset $HOME' '\n+\tcat >expect <<-\\EOF &&\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \t(\n \t\tsane_unset HOME &&\n \t\ttest_must_fail git config --get --path path.home \\\n@@ -1112,12 +1079,11 @@ test_expect_success 'get --type=color' '\n \ttest_cmp expect actual\n '\n \n-cat >expect << EOF\n+test_expect_success 'set --type=color' '\n+\tcat >expect <<\\EOF &&\n [foo]\n \tcolor = red\n EOF\n-\n-test_expect_success 'set --type=color' '\n \trm .git/config &&\n \tgit config --type=color foo.color \"red\" &&\n \ttest_cmp expect .git/config\n@@ -1133,14 +1099,14 @@ test_expect_success 'set --type=color barfs on non-color' '\n \ttest_grep \"cannot parse color\" error\n '\n \n-cat > expect << EOF\n+test_expect_success 'quoting' '\n+\tcat >expect <<\\EOF &&\n [quote]\n \tleading = \" test\"\n \tending = \"test \"\n \tsemicolon = \"test;test\"\n \thash = \"test#test\"\n EOF\n-test_expect_success 'quoting' '\n \trm -f .git/config &&\n \tgit config ${mode_set} quote.leading \" test\" &&\n \tgit config ${mode_set} quote.ending \"test \" &&\n@@ -1166,13 +1132,12 @@ inued\n inued\"\n EOF\n \n-cat > expect <<\\EOF\n-section.continued=continued\n-section.noncont=not continued\n-section.quotecont=cont;inued\n-EOF\n-\n test_expect_success 'value continued on next line' '\n+\tcat >expect <<-\\EOF &&\n+\tsection.continued=continued\n+\tsection.noncont=not continued\n+\tsection.quotecont=cont;inued\n+\tEOF\n \tgit config ${mode_prefix}list > result &&\n \ttest_cmp expect result\n '\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526361","messageId":"20250915-pks-config-color-v2-2-e4290bd8d13c@pks.im","threadId":"64128","inReplyTo":"20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im","subject":"[PATCH v2 2/5] t1300: small style fixups","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T12:52:49Z","receivedAt":"2025-09-15T12:52:59Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We have a couple of small style violations in t1300:\n\n  - An empty newline at the start of the test body.\n\n  - The test command is sometimes on the same line as the test name.\n\n  - The closing single-quote is sometimes on the same line as the last\n    command of the test.\n\nFix these.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 38 ++++++++++++++++++++++++--------------\n 1 file changed, 24 insertions(+), 14 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 538f2c9b8a..6d1015acfd 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -213,7 +213,9 @@ test_expect_success 'Prohibited LF in comment' '\n \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n '\n \n-test_expect_success 'non-match result' 'test_cmp expect .git/config'\n+test_expect_success 'non-match result' '\n+\ttest_cmp expect .git/config\n+'\n \n test_expect_success 'find mixed-case key by canonical name' '\n \ttest_cmp_config Second sections.whatever\n@@ -455,9 +457,13 @@ EOF\n \ttest_cmp expect .git/config\n '\n \n-test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'\n+test_expect_success 'invalid key' '\n+\ttest_must_fail git config inval.2key blabla\n+'\n \n-test_expect_success 'correct key' 'git config 123456.a123 987'\n+test_expect_success 'correct key' '\n+\tgit config 123456.a123 987\n+'\n \n test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n@@ -490,6 +496,7 @@ test_expect_success 'working --list' '\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n+\n test_expect_success '--list without repo produces empty output' '\n \tgit --git-dir=nonexistent config ${mode_prefix}list >output &&\n \ttest_must_be_empty output\n@@ -887,16 +894,17 @@ test_expect_success bool '\n \t    git config --bool --get bool.true$i >>result &&\n \t    git config --bool --get bool.false$i >>result || return 1\n \tdone &&\n-\ttest_cmp expect result'\n+\ttest_cmp expect result\n+'\n \n test_expect_success 'invalid bool (--get)' '\n-\n \tgit config ${mode_set} bool.nobool foobar &&\n-\ttest_must_fail git config --bool --get bool.nobool'\n+\ttest_must_fail git config --bool --get bool.nobool\n+'\n \n test_expect_success 'invalid bool (set)' '\n-\n-\ttest_must_fail git config --bool bool.nobool foobar'\n+\ttest_must_fail git config --bool bool.nobool foobar\n+'\n \n test_expect_success 'set --bool' '\n \tcat >expect <<\\EOF &&\n@@ -999,7 +1007,8 @@ EOF\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n \tgit config --path path.trailingtilde \"foo~\" &&\n-\ttest_cmp expect .git/config'\n+\ttest_cmp expect .git/config\n+'\n \n if test_have_prereq !MINGW && test \"${HOME+set}\"\n then\n@@ -1117,10 +1126,13 @@ EOF\n \n test_expect_success 'key with newline' '\n \ttest_must_fail git config ${mode_get} \"key.with\n-newline\" 123'\n+newline\" 123\n+'\n \n-test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\\\\\n-newline'\n+test_expect_success 'value with newline' '\n+\tgit config ${mode_set} key.sub value.with\\\\\\\n+newline\n+'\n \n cat > .git/config <<\\EOF\n [section]\n@@ -1330,7 +1342,6 @@ test_expect_success 'multiple git -c appends config' '\n '\n \n test_expect_success 'last one wins: two level vars' '\n-\n \t# sec.var and sec.VAR are the same variable, as the first\n \t# and the last level of a configuration variable name is\n \t# case insensitive.\n@@ -1349,7 +1360,6 @@ test_expect_success 'last one wins: two level vars' '\n '\n \n test_expect_success 'last one wins: three level vars' '\n-\n \t# v.a.r and v.A.r are not the same variable, as the middle\n \t# level of a three-level configuration variable name is\n \t# case sensitive.\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526362","messageId":"20250915-pks-config-color-v2-3-e4290bd8d13c@pks.im","threadId":"64128","inReplyTo":"20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im","subject":"[PATCH v2 3/5] builtin/config: do not die in `get_color()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T12:52:50Z","receivedAt":"2025-09-15T12:53:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"When trying to parse an invalid color via `get_color()` we die. We're\nabout to introduce another caller in a subsequent commit though that has\nits own error handling, so dying is a bit drastic there. Furthermore,\nthe only caller that we already have right now already knows to handle\nerrors in other branches that don't call `get_color()`.\n\nConvert the function to instead return an error code to improve its\nflexibility.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 59fb113b07..afd48bfa51 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -547,24 +547,31 @@ static int git_get_color_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static void get_color(const struct config_location_options *opts,\n+static int get_color(const struct config_location_options *opts,\n \t\t      const char *var, const char *def_color)\n {\n \tstruct get_color_config_data data = {\n \t\t.get_color_slot = var,\n \t\t.parsed_color[0] = '\\0',\n \t};\n+\tint ret;\n \n \tconfig_with_options(git_get_color_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n \tif (!data.get_color_found && def_color) {\n-\t\tif (color_parse(def_color, data.parsed_color) < 0)\n-\t\t\tdie(_(\"unable to parse default color value\"));\n+\t\tif (color_parse(def_color, data.parsed_color) < 0) {\n+\t\t\tret = error(_(\"unable to parse default color value\"));\n+\t\t\tgoto out;\n+\t\t}\n \t}\n \n+\tret = 0;\n+\n+out:\n \tfputs(data.parsed_color, stdout);\n+\treturn ret;\n }\n \n struct get_colorbool_config_data {\n@@ -1390,7 +1397,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tget_color(&location_opts, argv[0], argv[1]);\n+\t\tret = get_color(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526363","messageId":"20250915-pks-config-color-v2-4-e4290bd8d13c@pks.im","threadId":"64128","inReplyTo":"20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im","subject":"[PATCH v2 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T12:52:51Z","receivedAt":"2025-09-15T12:53:06Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Our documentation for git-config(1) has a section where it explains how\nto parse and use colors as Git would configure them. In order to get the\nANSI color escape sequence to reset the colors to normal we recommend\nthe following command:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n\nThis command is not supposed to parse any configuration keys. Instead,\nit is expected to parse the \"reset\" default value and turn it into a\nproper ANSI color escape sequence.\n\nIt was reported though [1] that this command doesn't work:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n    error: key does not contain a section:\n\nThis error was introduced in 4e51389000 (builtin/config: introduce \"get\"\nsubcommand, 2024-05-06), where we introduced the \"get\" subcommand to\nretrieve configuration values. The preimage of that commit used `git\nconfig --get-color \"\" \"reset\"` instead, which still works.\n\nThis use case is really quite specific to parsing colors, as it wouldn't\nmake sense to give git-config(1) a default value and an empty config key\nonly to return that default value unmodified. But with `--type=color` we\ndon't return the value directly; we instead parse the value into an ANSI\nescape sequence.\n\nAs such, we can easily special-case this one use case:\n\n    - If the provided config key is empty;\n\n    - the user is asking for a color code and the user; and\n\n    - the user has provided a default value,\n\nthen we call `get_color()` directly. Do so to make the documented\ncommand work as expected.\n\n[1]: <aI+oQvQgnNtC6DVw@szeder.dev>\n\nReported-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  |  2 ++\n t/t1300-config.sh | 11 +++++++++++\n 2 files changed, 13 insertions(+)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex afd48bfa51..f50c11df57 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -923,6 +923,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n+\telse if (display_opts.type == TYPE_COLOR && !strlen(argv[0]) && display_opts.default_value)\n+\t\tret = get_color(&location_opts, \"\", display_opts.default_value);\n \telse\n \t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern,\n \t\t\t\tget_value_flags, flags);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 6d1015acfd..3cf5d17aba 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1083,11 +1083,22 @@ test_expect_success 'get --type=color' '\n \trm .git/config &&\n \tgit config ${mode_set} foo.color \"red\" &&\n \tgit config --get --type=color foo.color >actual.raw &&\n+\tgit config get --type=color foo.color >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \techo \"<RED>\" >expect &&\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'get --type=color with default value only' '\n+\tgit config --get-color \"\" \"red\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual &&\n+\tgit config get --type=color --default=\"red\" \"\" >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<\\EOF &&\n [foo]\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526364","messageId":"20250915-pks-config-color-v2-5-e4290bd8d13c@pks.im","threadId":"64128","inReplyTo":"20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im","subject":"[PATCH v2 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-15T12:52:52Z","receivedAt":"2025-09-15T12:53:09Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"With `git config get --type=color` the user asks us to parse a specific\nconfiguration key and turn the value into an ANSI color escape sequence.\nThe printed string can then for example be used as part of shell scripts\nto reuse the same colors as Git.\n\nRight now though we set up the auto-pager, which means that the string\nmay instead be written to the pager command. This is of course quite\nnonsensical; there shouldn't be any use case where the color code should\nend up in the pager instead of in the TTY.\n\nFix this by disabling the pager in case the user is asking us to print\ncolor sequences.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 3 ++-\n t/t1300-config.sh | 9 +++++++++\n 2 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex f50c11df57..6708d91814 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -919,7 +919,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \tlocation_options_init(&location_opts, prefix);\n \tdisplay_options_init(&display_opts);\n \n-\tsetup_auto_pager(\"config\", 1);\n+\tif (display_opts.type != TYPE_COLOR)\n+\t\tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 3cf5d17aba..358d636379 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -9,6 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n for mode in legacy subcommands\n do\n@@ -1099,6 +1100,14 @@ test_expect_success 'get --type=color with default value only' '\n \ttest_cmp actual.raw actual-subcommand.raw\n '\n \n+test_expect_success TTY 'get --type=color does not use a pager' '\n+\ttest_config core.pager \"echo foobar\" &&\n+\ttest_terminal git config get --type=color --default=\"red\" \"\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<\\EOF &&\n [foo]\n\n-- \n2.51.0.450.g87641ccf93.dirty\n\n"},{"id":"526377","messageId":"xmqqo6rbhbv8.fsf@gitster.g","threadId":"64128","inReplyTo":"20250915-pks-config-color-v2-4-e4290bd8d13c@pks.im","subject":"Re: [PATCH v2 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-15T17:19:39Z","receivedAt":"2025-09-15T17:19:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Our documentation for git-config(1) has a section where it explains how\n> to parse and use colors as Git would configure them. In order to get the\n> ANSI color escape sequence to reset the colors to normal we recommend\n> the following command:\n>\n>     $ git config get --type=color --default=\"reset\" \"\"\n>\n> This command is not supposed to parse any configuration keys. Instead,\n> it is expected to parse the \"reset\" default value and turn it into a\n> proper ANSI color escape sequence.\n>\n> It was reported though [1] that this command doesn't work:\n>\n>     $ git config get --type=color --default=\"reset\" \"\"\n>     error: key does not contain a section:\n>\n> This error was introduced in 4e51389000 (builtin/config: introduce \"get\"\n> subcommand, 2024-05-06), where we introduced the \"get\" subcommand to\n> retrieve configuration values. The preimage of that commit used `git\n> config --get-color \"\" \"reset\"` instead, which still works.\n>\n> This use case is really quite specific to parsing colors, as it wouldn't\n> make sense to give git-config(1) a default value and an empty config key\n> only to return that default value unmodified. But with `--type=color` we\n> don't return the value directly; we instead parse the value into an ANSI\n> escape sequence.\n>\n> As such, we can easily special-case this one use case:\n>\n>     - If the provided config key is empty;\n>\n>     - the user is asking for a color code and the user; and\n>\n>     - the user has provided a default value,\n>\n> then we call `get_color()` directly. Do so to make the documented\n> command work as expected.\n\nYup, the UI is still ugly because this is not config at all X-<.\nOnly because we lack \"I have a color name.  Please parse it into\nANSI sequence\" command, we are abusing the \"git config\" command and\nusing \"--default\" as \"here is that color name I am asking about\"\nargument.  Even if we were to add a more intuitive \"parse --color\"\ncommand, the (ab)use of \"git config\" for this purpose has been with\nus for the many years and cannot be dropped--and under that constraints,\nI agree that this patch is the best we could do.\n\nThanks for working on this.\n"},{"id":"526378","messageId":"xmqqikhjhbgo.fsf@gitster.g","threadId":"64128","inReplyTo":"20250915-pks-config-color-v2-5-e4290bd8d13c@pks.im","subject":"Re: [PATCH v2 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-15T17:28:23Z","receivedAt":"2025-09-15T17:28:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> With `git config get --type=color` the user asks us to parse a specific\n> configuration key and turn the value into an ANSI color escape sequence.\n> The printed string can then for example be used as part of shell scripts\n> to reuse the same colors as Git.\n>\n> Right now though we set up the auto-pager, which means that the string\n> may instead be written to the pager command. This is of course quite\n> nonsensical; there shouldn't be any use case where the color code should\n> end up in the pager instead of in the TTY.\n>\n> Fix this by disabling the pager in case the user is asking us to print\n> color sequences.\n\nI am of two minds.  Part of me obviously agrees that it is more\nstraight forward with this change.  But it may \n\nAn interactive user experimenting while writing their own script\nmight say something like\n\n $ git config --type=color --default=\"reverse red\" n.n\n\nIf the command emitted directly to the terminal, then everything\nthey type from then on will be bloody red, but the pager protects\nthem from such an accident.  Instead, they are forced to say\n\n $ C=$(git config get --type=color --default=\"reverse red\" n.n)\n $ R=$(git config get --type=color --default=\"reset\" n.n)\n $ echo \"So$C Bloody ${R}Red\"\n\nbut these are likely what they would be writing in their script\nanyway, so...\n"},{"id":"526424","messageId":"a8702bc6-154a-439c-b4c2-0de208838775@app.fastmail.com","threadId":"64128","inReplyTo":"xmqqikhjhbgo.fsf@gitster.g","subject":"Re: [PATCH v2 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-16T06:56:32Z","receivedAt":"2025-09-16T06:56:55Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Mon, Sep 15, 2025, at 19:28, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n>[snip]\n>> Fix this by disabling the pager in case the user is asking us to print\n>> color sequences.\n>\n> I am of two minds.  Part of me obviously agrees that it is more\n> straight forward with this change.  But it may \n\nDid you drop something here or is it covered by the next paragraph?\n\n>\n> An interactive user experimenting while writing their own script\n> might say something like\n>\n>  $ git config --type=color --default=\"reverse red\" n.n\n>\n>[snip]\n"},{"id":"526669","messageId":"aMuglu_TtQPht1xP@pks.im","threadId":"64128","inReplyTo":"xmqqikhjhbgo.fsf@gitster.g","subject":"Re: [PATCH v2 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-18T06:03:02Z","receivedAt":"2025-09-18T06:03:08Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Sep 15, 2025 at 10:28:23AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > With `git config get --type=color` the user asks us to parse a specific\n> > configuration key and turn the value into an ANSI color escape sequence.\n> > The printed string can then for example be used as part of shell scripts\n> > to reuse the same colors as Git.\n> >\n> > Right now though we set up the auto-pager, which means that the string\n> > may instead be written to the pager command. This is of course quite\n> > nonsensical; there shouldn't be any use case where the color code should\n> > end up in the pager instead of in the TTY.\n> >\n> > Fix this by disabling the pager in case the user is asking us to print\n> > color sequences.\n> \n> I am of two minds.  Part of me obviously agrees that it is more\n> straight forward with this change.  But it may \n> \n> An interactive user experimenting while writing their own script\n> might say something like\n> \n>  $ git config --type=color --default=\"reverse red\" n.n\n> \n> If the command emitted directly to the terminal, then everything\n> they type from then on will be bloody red, but the pager protects\n> them from such an accident.  Instead, they are forced to say\n> \n>  $ C=$(git config get --type=color --default=\"reverse red\" n.n)\n>  $ R=$(git config get --type=color --default=\"reset\" n.n)\n>  $ echo \"So$C Bloody ${R}Red\"\n> \n> but these are likely what they would be writing in their script\n> anyway, so...\n\nTrue. That being said, I'm mostly trying to emulate the old behaviour\nthat we had in `git config --get-color`. We have the following condition\nthere:\n\n\t/*\n\t * The following actions may produce more than one line of output and\n\t * should therefore be paged.\n\t */\n\tif (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))\n\t\tsetup_auto_pager(\"config\", 1);\n\n`ACTION_GET_COLOR` is not part of this condition, so we wouldn't set up\nthe auto pager there, either. So I think it's sensible to match that\nbehaviour so that the new command really is a drop-in replacement for\nthe old one.\n\nI should probably clarify the commit message.\n\nPatrick\n"},{"id":"526670","messageId":"20250918-pks-config-color-v3-0-08ea618cae26@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH v3 0/5] builtin/config: bug fixes for \"get\" subcommand with \"--type=color\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-18T06:14:18Z","receivedAt":"2025-09-18T06:14:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis small patch series contains two bug fixes for `git config get\n--type=color`:\n\n  - We restore the behaviour where we can now parse colors without a\n    config key.\n\n  - We stop spawning the pager when the user requests to print ANSI\n    color escape sequences.\n\nFurthermore, the patch series does some lighter refactorings of t1300.\nThat test file still has its fair share of issues, but at least it looks\na bit less dirty now.\n\nChanges in v2:\n  - Improve commit messages.\n  - Use \"\\EOF\" and \"-EOF\" in more cases.\n  - Move a style fixup from the first commit into the second commit.\n  - Link to v1: https://lore.kernel.org/r/20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im\n\nChanges in v3:\n  - Provide additional context as part of the commit message for the\n    commit that stops setting up the pager with `--type=color`.\n  - Link to v2: https://lore.kernel.org/r/20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (5):\n      t1300: write test expectations in the test's body\n      t1300: small style fixups\n      builtin/config: do not die in `get_color()`\n      builtin/config: special-case retrieving colors without a key\n      builtin/config: do not spawn pager when printing color codes\n\n builtin/config.c  |  20 +++-\n t/t1300-config.sh | 349 +++++++++++++++++++++++++++---------------------------\n 2 files changed, 187 insertions(+), 182 deletions(-)\n\nRange-diff versus v2:\n\n1:  e61278d7c5 = 1:  fdd1711881 t1300: write test expectations in the test's body\n2:  e6f1ea5283 = 2:  8e1e05d9e1 t1300: small style fixups\n3:  6f8257aeb4 = 3:  1ef6272e64 builtin/config: do not die in `get_color()`\n4:  27a8ab34b0 = 4:  5dcf8c6656 builtin/config: special-case retrieving colors without a key\n5:  259600c32a ! 5:  05d5022c1b builtin/config: do not spawn pager when printing color codes\n    @@ Commit message\n         to reuse the same colors as Git.\n     \n         Right now though we set up the auto-pager, which means that the string\n    -    may instead be written to the pager command. This is of course quite\n    -    nonsensical; there shouldn't be any use case where the color code should\n    -    end up in the pager instead of in the TTY.\n    +    may be written to the pager instead of directly to the terminal. This\n    +    behaviour is problematic for two reasons:\n     \n    -    Fix this by disabling the pager in case the user is asking us to print\n    -    color sequences.\n    +      - Color codes are meant for direct terminal output; writing them into\n    +        a pager does not seem like a sensible thing to do without additional\n    +        text.\n    +\n    +      - It is inconsistent with `git config --get-color`, which never uses a\n    +        pager, despite the fact that we claim `git config get --type=color`\n    +        to be a drop-in replacement in git-config(1).\n    +\n    +    Fix this by disabling the pager when outputting color sequences.\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n\n---\nbase-commit: ab427cd991100e94792fce124b0934135abdea4b\nchange-id: 20250911-pks-config-color-e5b8a213e895\n\n"},{"id":"526671","messageId":"20250918-pks-config-color-v3-1-08ea618cae26@pks.im","threadId":"64128","inReplyTo":"20250918-pks-config-color-v3-0-08ea618cae26@pks.im","subject":"[PATCH v3 1/5] t1300: write test expectations in the test's body","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-18T06:14:19Z","receivedAt":"2025-09-18T06:14:29Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"There are a bunch of tests in t1300 where we write the test expectation\nhanded over to `test_cmp ()` outside of the test body. This does not\nmatch our modern test style, and there isn't really a reason why this\nwould need to happen outside of the test bodies.\n\nConvert those to instead do so as part of the test itself. While at it,\nnormalize these tests to use `<<\\EOF` for those that don't use variable\nexpansion and `<<-EOF` for those that aren't sensitive to indentation.\n\nNote that there are two exceptions that we leave as-is for now since\nthey are reused across tests.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 291 ++++++++++++++++++++++++------------------------------\n 1 file changed, 128 insertions(+), 163 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f856821839..538f2c9b8a 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -134,38 +134,39 @@ test_expect_success 'clear default config' '\n \trm -f .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'initial' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n EOF\n-test_expect_success 'initial' '\n \tgit config ${mode_set} section.penguin \"little blue\" &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'mixed case' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n EOF\n-test_expect_success 'mixed case' '\n \tgit config ${mode_set} Section.Movie BadPhysics &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'similar section' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'similar section' '\n \tgit config ${mode_set} Sections.WhatEver Second &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'uppercase section' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n@@ -173,7 +174,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'uppercase section' '\n \tgit config ${mode_set} SECTION.UPPERCASE true &&\n \ttest_cmp expect .git/config\n '\n@@ -186,7 +186,8 @@ test_expect_success 'replace with non-match (actually matching)' '\n \tgit config section.penguin \"very blue\" !kingpin\n '\n \n-cat > expect << EOF\n+test_expect_success 'append comments' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tMovie = BadPhysics\n \tUPPERCASE = true\n@@ -198,8 +199,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-\n-test_expect_success 'append comments' '\n \tgit config --replace-all --comment=\"Pygoscelis papua\" section.penguin gentoo &&\n \tgit config ${mode_set} --comment=\"find fish\" section.disposition peckish &&\n \tgit config ${mode_set} --comment=\"#abc\" section.foo bar &&\n@@ -265,14 +264,15 @@ test_expect_success 'unset with cont. lines' '\n \tgit config ${mode_unset} beta.baz\n '\n \n-cat > expect <<\\EOF\n-[alpha]\n-bar = foo\n-[beta]\n-foo = bar\n-EOF\n-\n-test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'\n+test_expect_success 'unset with cont. lines is correct' '\n+\tcat >expect <<-\\EOF &&\n+\t[alpha]\n+\tbar = foo\n+\t[beta]\n+\tfoo = bar\n+\tEOF\n+\ttest_cmp expect .git/config\n+'\n \n cat > .git/config << EOF\n [beta] ; silly comment # another comment\n@@ -292,16 +292,15 @@ test_expect_success 'multiple unset' '\n \tgit config ${mode_unset_all} beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'multiple unset is correct' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'multiple unset is correct' '\n \ttest_cmp expect .git/config\n '\n \n@@ -318,37 +317,37 @@ test_expect_success '--replace-all' '\n \tgit config ${mode_replace_all} beta.haha gamma\n '\n \n-cat > expect << EOF\n+test_expect_success 'all replaced' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = gamma\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'all replaced' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = alpha\n [nextSection] noNewline = ouch\n EOF\n-test_expect_success 'really mean test' '\n \tgit config ${mode_set} beta.haha alpha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -356,7 +355,6 @@ noIndent= sillyValue ; 'nother silly comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'really really mean test' '\n \tgit config ${mode_set} nextsection.nonewline wow &&\n \ttest_cmp expect .git/config\n '\n@@ -365,23 +363,24 @@ test_expect_success 'get value' '\n \ttest_cmp_config alpha beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'unset' '\n \tgit config ${mode_unset} beta.haha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar' '\n+\tcat  >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -389,7 +388,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar' '\n \tgit config nextsection.NoNewLine \"wow2 for me\" \"for me$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -415,9 +413,10 @@ test_expect_success 'multi-valued get-all returns all' '\n \ttest_cmp expect actual\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar replace' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -425,7 +424,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow3\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar replace' '\n \tgit config nextsection.nonewline \"wow3\" \"wow$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -438,17 +436,16 @@ test_expect_success 'invalid unset' '\n \ttest_must_fail git config ${mode_unset} somesection.nonewline\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tNoNewLine = wow2 for me\n EOF\n-\n-test_expect_success 'multivar unset' '\n \tcase \"$mode\" in\n \tlegacy)\n \t\tgit config --unset nextsection.nonewline \"wow3$\";;\n@@ -466,9 +463,10 @@ test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n '\n \n-cat > expect << EOF\n+test_expect_success 'hierarchical section value' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -479,19 +477,16 @@ noIndent= sillyValue ; 'nother silly comment\n [Version \"1.2.3eX\"]\n \tAlpha = beta\n EOF\n-\n-test_expect_success 'hierarchical section value' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n-beta.noindent=sillyValue\n-nextsection.nonewline=wow2 for me\n-123456.a123=987\n-version.1.2.3eX.alpha=beta\n-EOF\n-\n test_expect_success 'working --list' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent=sillyValue\n+\tnextsection.nonewline=wow2 for me\n+\t123456.a123=987\n+\tversion.1.2.3eX.alpha=beta\n+\tEOF\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n@@ -500,44 +495,40 @@ test_expect_success '--list without repo produces empty output' '\n \ttest_must_be_empty output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-123456.a123\n-version.1.2.3eX.alpha\n-EOF\n-\n test_expect_success '--name-only --list' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\t123456.a123\n+\tversion.1.2.3eX.alpha\n+\tEOF\n \tgit config ${mode_prefix}list --name-only >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent sillyValue\n-nextsection.nonewline wow2 for me\n-EOF\n-\n test_expect_success '--get-regexp' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent sillyValue\n+\tnextsection.nonewline wow2 for me\n+\tEOF\n \tgit config ${mode_get_regexp} in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-EOF\n-\n test_expect_success '--name-only --get-regexp' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\tEOF\n \tgit config ${mode_get_regexp} --name-only in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-wow2 for me\n-wow4 for you\n-EOF\n-\n test_expect_success '--add' '\n+\tcat >expect <<-\\EOF &&\n+\twow2 for me\n+\twow4 for you\n+\tEOF\n \tgit config --add nextsection.nonewline \"wow4 for you\" &&\n \tgit config ${mode_get_all} nextsection.nonewline > output &&\n \ttest_cmp expect output\n@@ -558,37 +549,32 @@ test_expect_success 'get variable with empty value' '\n \tgit config --get emptyvalue.variable ^$\n '\n \n-echo novalue.variable > expect\n-\n test_expect_success 'get-regexp variable with no value' '\n+\techo novalue.variable >expect &&\n \tgit config ${mode_get_regexp} novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'novalue.variable true' > expect\n-\n test_expect_success 'get-regexp --bool variable with no value' '\n+\techo \"novalue.variable true\" >expect &&\n \tgit config ${mode_get_regexp} --bool novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'emptyvalue.variable ' > expect\n-\n test_expect_success 'get-regexp variable with empty value' '\n+\techo \"emptyvalue.variable \" >expect &&\n \tgit config ${mode_get_regexp} emptyvalue > output &&\n \ttest_cmp expect output\n '\n \n-echo true > expect\n-\n test_expect_success 'get bool variable with no value' '\n+\techo true >expect &&\n \tgit config --bool novalue.variable > output &&\n \ttest_cmp expect output\n '\n \n-echo false > expect\n-\n test_expect_success 'get bool variable with empty value' '\n+\techo false >expect &&\n \tgit config --bool emptyvalue.variable > output &&\n \ttest_cmp expect output\n '\n@@ -604,19 +590,19 @@ cat > .git/config << EOF\n \tc = d\n EOF\n \n-cat > expect << EOF\n+test_expect_success 'new section is partial match of another' '\n+\tcat >expect <<\\EOF &&\n [a.b]\n \tc = d\n [a]\n \tx = y\n EOF\n-\n-test_expect_success 'new section is partial match of another' '\n \tgit config a.x y &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'new variable inserts into proper section' '\n+\tcat >expect <<\\EOF &&\n [a.b]\n \tc = d\n [a]\n@@ -625,8 +611,6 @@ cat > expect << EOF\n [b]\n \tx = y\n EOF\n-\n-test_expect_success 'new variable inserts into proper section' '\n \tgit config b.x y &&\n \tgit config a.b c &&\n \ttest_cmp expect .git/config\n@@ -642,11 +626,10 @@ cat > other-config << EOF\n \tbahn = strasse\n EOF\n \n-cat > expect << EOF\n-ein.bahn=strasse\n-EOF\n-\n test_expect_success 'alternative GIT_CONFIG' '\n+\tcat >expect <<-\\EOF &&\n+\tein.bahn=strasse\n+\tEOF\n \tGIT_CONFIG=other-config git config ${mode_prefix}list >output &&\n \ttest_cmp expect output\n '\n@@ -675,14 +658,13 @@ test_expect_success 'refer config from subdirectory' '\n \ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n '\n \n-cat > expect << EOF\n+test_expect_success '--set in alternative file' '\n+\tcat >expect <<\\EOF &&\n [ein]\n \tbahn = strasse\n [anwohner]\n \tpark = ausweis\n EOF\n-\n-test_expect_success '--set in alternative file' '\n \tgit config --file=other-config anwohner.park ausweis &&\n \ttest_cmp expect other-config\n '\n@@ -730,7 +712,8 @@ test_expect_success 'rename another section' '\n \tgit config ${mode_prefix}rename-section branch.\"1 234 blabl/a\" branch.drei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -740,8 +723,6 @@ cat > expect << EOF\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -753,7 +734,8 @@ test_expect_success 'rename a section with a var on the same line' '\n \tgit config ${mode_prefix}rename-section branch.vier branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -765,8 +747,6 @@ weird\n [branch \"zwei\"]\n \tz = 1\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -816,32 +796,29 @@ test_expect_success 'remove section' '\n \tgit config ${mode_prefix}remove-section branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'section was removed properly' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'section was removed properly' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'section ending' '\n+\tcat >expect <<\\EOF &&\n [gitcvs]\n \tenabled = true\n \tdbname = %Ggitcvs2.%a.%m.sqlite\n [gitcvs \"ext\"]\n \tdbname = %Ggitcvs1.%a.%m.sqlite\n EOF\n-\n-test_expect_success 'section ending' '\n \trm -f .git/config &&\n \tgit config ${mode_set} gitcvs.enabled true &&\n \tgit config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&\n \tgit config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&\n \ttest_cmp expect .git/config\n-\n '\n \n test_expect_success numbers '\n@@ -885,19 +862,17 @@ test_expect_success 'invalid stdin config' '\n \ttest_grep \"bad config line 1 in standard input\" output\n '\n \n-cat > expect << EOF\n-true\n-false\n-true\n-false\n-true\n-false\n-true\n-false\n-EOF\n-\n test_expect_success bool '\n-\n+\tcat >expect <<-\\EOF &&\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\tEOF\n \tgit config ${mode_set} bool.true1 01 &&\n \tgit config ${mode_set} bool.true2 -1 &&\n \tgit config ${mode_set} bool.true3 YeS &&\n@@ -923,7 +898,8 @@ test_expect_success 'invalid bool (set)' '\n \n \ttest_must_fail git config --bool bool.nobool foobar'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --bool' '\n+\tcat >expect <<\\EOF &&\n [bool]\n \ttrue1 = true\n \ttrue2 = true\n@@ -934,9 +910,6 @@ cat > expect <<\\EOF\n \tfalse3 = false\n \tfalse4 = false\n EOF\n-\n-test_expect_success 'set --bool' '\n-\n \trm -f .git/config &&\n \tgit config --bool bool.true1 01 &&\n \tgit config --bool bool.true2 -1 &&\n@@ -948,15 +921,13 @@ test_expect_success 'set --bool' '\n \tgit config --bool bool.false4 FALSE &&\n \ttest_cmp expect .git/config'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --int' '\n+\tcat >expect <<\\EOF &&\n [int]\n \tval1 = 1\n \tval2 = -1\n \tval3 = 5242880\n EOF\n-\n-test_expect_success 'set --int' '\n-\n \trm -f .git/config &&\n \tgit config --int int.val1 01 &&\n \tgit config --int int.val2 -1 &&\n@@ -994,7 +965,8 @@ test_expect_success 'get --bool-or-int' '\n \ttest_cmp expect actual\n '\n \n-cat >expect <<\\EOF\n+test_expect_success 'set --bool-or-int' '\n+\tcat >expect <<\\EOF &&\n [bool]\n \ttrue1 = true\n \tfalse1 = false\n@@ -1005,8 +977,6 @@ cat >expect <<\\EOF\n \tint2 = 1\n \tint3 = -1\n EOF\n-\n-test_expect_success 'set --bool-or-int' '\n \trm -f .git/config &&\n \tgit config --bool-or-int bool.true1 true &&\n \tgit config --bool-or-int bool.false1 false &&\n@@ -1018,14 +988,13 @@ test_expect_success 'set --bool-or-int' '\n \ttest_cmp expect .git/config\n '\n \n-cat >expect <<\\EOF\n+test_expect_success !MINGW 'set --path' '\n+\tcat >expect <<\\EOF &&\n [path]\n \thome = ~/\n \tnormal = /dev/null\n \ttrailingtilde = foo~\n EOF\n-\n-test_expect_success !MINGW 'set --path' '\n \trm -f .git/config &&\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n@@ -1037,25 +1006,23 @@ then\n \ttest_set_prereq HOMEVAR\n fi\n \n-cat >expect <<EOF\n-$HOME/\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success HOMEVAR 'get --path' '\n+\tcat >expect <<-EOF &&\n+\t$HOME/\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \tgit config --get --path path.home > result &&\n \tgit config --get --path path.normal >> result &&\n \tgit config --get --path path.trailingtilde >> result &&\n \ttest_cmp expect result\n '\n \n-cat >expect <<\\EOF\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success !MINGW 'get --path copes with unset $HOME' '\n+\tcat >expect <<-\\EOF &&\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \t(\n \t\tsane_unset HOME &&\n \t\ttest_must_fail git config --get --path path.home \\\n@@ -1112,12 +1079,11 @@ test_expect_success 'get --type=color' '\n \ttest_cmp expect actual\n '\n \n-cat >expect << EOF\n+test_expect_success 'set --type=color' '\n+\tcat >expect <<\\EOF &&\n [foo]\n \tcolor = red\n EOF\n-\n-test_expect_success 'set --type=color' '\n \trm .git/config &&\n \tgit config --type=color foo.color \"red\" &&\n \ttest_cmp expect .git/config\n@@ -1133,14 +1099,14 @@ test_expect_success 'set --type=color barfs on non-color' '\n \ttest_grep \"cannot parse color\" error\n '\n \n-cat > expect << EOF\n+test_expect_success 'quoting' '\n+\tcat >expect <<\\EOF &&\n [quote]\n \tleading = \" test\"\n \tending = \"test \"\n \tsemicolon = \"test;test\"\n \thash = \"test#test\"\n EOF\n-test_expect_success 'quoting' '\n \trm -f .git/config &&\n \tgit config ${mode_set} quote.leading \" test\" &&\n \tgit config ${mode_set} quote.ending \"test \" &&\n@@ -1166,13 +1132,12 @@ inued\n inued\"\n EOF\n \n-cat > expect <<\\EOF\n-section.continued=continued\n-section.noncont=not continued\n-section.quotecont=cont;inued\n-EOF\n-\n test_expect_success 'value continued on next line' '\n+\tcat >expect <<-\\EOF &&\n+\tsection.continued=continued\n+\tsection.noncont=not continued\n+\tsection.quotecont=cont;inued\n+\tEOF\n \tgit config ${mode_prefix}list > result &&\n \ttest_cmp expect result\n '\n\n-- \n2.51.0.534.gc79095c0ca.dirty\n\n"},{"id":"526672","messageId":"20250918-pks-config-color-v3-2-08ea618cae26@pks.im","threadId":"64128","inReplyTo":"20250918-pks-config-color-v3-0-08ea618cae26@pks.im","subject":"[PATCH v3 2/5] t1300: small style fixups","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-18T06:14:20Z","receivedAt":"2025-09-18T06:14:32Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We have a couple of small style violations in t1300:\n\n  - An empty newline at the start of the test body.\n\n  - The test command is sometimes on the same line as the test name.\n\n  - The closing single-quote is sometimes on the same line as the last\n    command of the test.\n\nFix these.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 38 ++++++++++++++++++++++++--------------\n 1 file changed, 24 insertions(+), 14 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 538f2c9b8a..6d1015acfd 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -213,7 +213,9 @@ test_expect_success 'Prohibited LF in comment' '\n \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n '\n \n-test_expect_success 'non-match result' 'test_cmp expect .git/config'\n+test_expect_success 'non-match result' '\n+\ttest_cmp expect .git/config\n+'\n \n test_expect_success 'find mixed-case key by canonical name' '\n \ttest_cmp_config Second sections.whatever\n@@ -455,9 +457,13 @@ EOF\n \ttest_cmp expect .git/config\n '\n \n-test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'\n+test_expect_success 'invalid key' '\n+\ttest_must_fail git config inval.2key blabla\n+'\n \n-test_expect_success 'correct key' 'git config 123456.a123 987'\n+test_expect_success 'correct key' '\n+\tgit config 123456.a123 987\n+'\n \n test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n@@ -490,6 +496,7 @@ test_expect_success 'working --list' '\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n+\n test_expect_success '--list without repo produces empty output' '\n \tgit --git-dir=nonexistent config ${mode_prefix}list >output &&\n \ttest_must_be_empty output\n@@ -887,16 +894,17 @@ test_expect_success bool '\n \t    git config --bool --get bool.true$i >>result &&\n \t    git config --bool --get bool.false$i >>result || return 1\n \tdone &&\n-\ttest_cmp expect result'\n+\ttest_cmp expect result\n+'\n \n test_expect_success 'invalid bool (--get)' '\n-\n \tgit config ${mode_set} bool.nobool foobar &&\n-\ttest_must_fail git config --bool --get bool.nobool'\n+\ttest_must_fail git config --bool --get bool.nobool\n+'\n \n test_expect_success 'invalid bool (set)' '\n-\n-\ttest_must_fail git config --bool bool.nobool foobar'\n+\ttest_must_fail git config --bool bool.nobool foobar\n+'\n \n test_expect_success 'set --bool' '\n \tcat >expect <<\\EOF &&\n@@ -999,7 +1007,8 @@ EOF\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n \tgit config --path path.trailingtilde \"foo~\" &&\n-\ttest_cmp expect .git/config'\n+\ttest_cmp expect .git/config\n+'\n \n if test_have_prereq !MINGW && test \"${HOME+set}\"\n then\n@@ -1117,10 +1126,13 @@ EOF\n \n test_expect_success 'key with newline' '\n \ttest_must_fail git config ${mode_get} \"key.with\n-newline\" 123'\n+newline\" 123\n+'\n \n-test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\\\\\n-newline'\n+test_expect_success 'value with newline' '\n+\tgit config ${mode_set} key.sub value.with\\\\\\\n+newline\n+'\n \n cat > .git/config <<\\EOF\n [section]\n@@ -1330,7 +1342,6 @@ test_expect_success 'multiple git -c appends config' '\n '\n \n test_expect_success 'last one wins: two level vars' '\n-\n \t# sec.var and sec.VAR are the same variable, as the first\n \t# and the last level of a configuration variable name is\n \t# case insensitive.\n@@ -1349,7 +1360,6 @@ test_expect_success 'last one wins: two level vars' '\n '\n \n test_expect_success 'last one wins: three level vars' '\n-\n \t# v.a.r and v.A.r are not the same variable, as the middle\n \t# level of a three-level configuration variable name is\n \t# case sensitive.\n\n-- \n2.51.0.534.gc79095c0ca.dirty\n\n"},{"id":"526673","messageId":"20250918-pks-config-color-v3-3-08ea618cae26@pks.im","threadId":"64128","inReplyTo":"20250918-pks-config-color-v3-0-08ea618cae26@pks.im","subject":"[PATCH v3 3/5] builtin/config: do not die in `get_color()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-18T06:14:21Z","receivedAt":"2025-09-18T06:14:35Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"When trying to parse an invalid color via `get_color()` we die. We're\nabout to introduce another caller in a subsequent commit though that has\nits own error handling, so dying is a bit drastic there. Furthermore,\nthe only caller that we already have right now already knows to handle\nerrors in other branches that don't call `get_color()`.\n\nConvert the function to instead return an error code to improve its\nflexibility.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 59fb113b07..afd48bfa51 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -547,24 +547,31 @@ static int git_get_color_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static void get_color(const struct config_location_options *opts,\n+static int get_color(const struct config_location_options *opts,\n \t\t      const char *var, const char *def_color)\n {\n \tstruct get_color_config_data data = {\n \t\t.get_color_slot = var,\n \t\t.parsed_color[0] = '\\0',\n \t};\n+\tint ret;\n \n \tconfig_with_options(git_get_color_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n \tif (!data.get_color_found && def_color) {\n-\t\tif (color_parse(def_color, data.parsed_color) < 0)\n-\t\t\tdie(_(\"unable to parse default color value\"));\n+\t\tif (color_parse(def_color, data.parsed_color) < 0) {\n+\t\t\tret = error(_(\"unable to parse default color value\"));\n+\t\t\tgoto out;\n+\t\t}\n \t}\n \n+\tret = 0;\n+\n+out:\n \tfputs(data.parsed_color, stdout);\n+\treturn ret;\n }\n \n struct get_colorbool_config_data {\n@@ -1390,7 +1397,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tget_color(&location_opts, argv[0], argv[1]);\n+\t\tret = get_color(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n\n-- \n2.51.0.534.gc79095c0ca.dirty\n\n"},{"id":"526674","messageId":"20250918-pks-config-color-v3-4-08ea618cae26@pks.im","threadId":"64128","inReplyTo":"20250918-pks-config-color-v3-0-08ea618cae26@pks.im","subject":"[PATCH v3 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-18T06:14:22Z","receivedAt":"2025-09-18T06:14:38Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Our documentation for git-config(1) has a section where it explains how\nto parse and use colors as Git would configure them. In order to get the\nANSI color escape sequence to reset the colors to normal we recommend\nthe following command:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n\nThis command is not supposed to parse any configuration keys. Instead,\nit is expected to parse the \"reset\" default value and turn it into a\nproper ANSI color escape sequence.\n\nIt was reported though [1] that this command doesn't work:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n    error: key does not contain a section:\n\nThis error was introduced in 4e51389000 (builtin/config: introduce \"get\"\nsubcommand, 2024-05-06), where we introduced the \"get\" subcommand to\nretrieve configuration values. The preimage of that commit used `git\nconfig --get-color \"\" \"reset\"` instead, which still works.\n\nThis use case is really quite specific to parsing colors, as it wouldn't\nmake sense to give git-config(1) a default value and an empty config key\nonly to return that default value unmodified. But with `--type=color` we\ndon't return the value directly; we instead parse the value into an ANSI\nescape sequence.\n\nAs such, we can easily special-case this one use case:\n\n    - If the provided config key is empty;\n\n    - the user is asking for a color code and the user; and\n\n    - the user has provided a default value,\n\nthen we call `get_color()` directly. Do so to make the documented\ncommand work as expected.\n\n[1]: <aI+oQvQgnNtC6DVw@szeder.dev>\n\nReported-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  |  2 ++\n t/t1300-config.sh | 11 +++++++++++\n 2 files changed, 13 insertions(+)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex afd48bfa51..f50c11df57 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -923,6 +923,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n+\telse if (display_opts.type == TYPE_COLOR && !strlen(argv[0]) && display_opts.default_value)\n+\t\tret = get_color(&location_opts, \"\", display_opts.default_value);\n \telse\n \t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern,\n \t\t\t\tget_value_flags, flags);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 6d1015acfd..3cf5d17aba 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1083,11 +1083,22 @@ test_expect_success 'get --type=color' '\n \trm .git/config &&\n \tgit config ${mode_set} foo.color \"red\" &&\n \tgit config --get --type=color foo.color >actual.raw &&\n+\tgit config get --type=color foo.color >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \techo \"<RED>\" >expect &&\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'get --type=color with default value only' '\n+\tgit config --get-color \"\" \"red\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual &&\n+\tgit config get --type=color --default=\"red\" \"\" >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<\\EOF &&\n [foo]\n\n-- \n2.51.0.534.gc79095c0ca.dirty\n\n"},{"id":"526675","messageId":"20250918-pks-config-color-v3-5-08ea618cae26@pks.im","threadId":"64128","inReplyTo":"20250918-pks-config-color-v3-0-08ea618cae26@pks.im","subject":"[PATCH v3 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-18T06:14:23Z","receivedAt":"2025-09-18T06:14:42Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"With `git config get --type=color` the user asks us to parse a specific\nconfiguration key and turn the value into an ANSI color escape sequence.\nThe printed string can then for example be used as part of shell scripts\nto reuse the same colors as Git.\n\nRight now though we set up the auto-pager, which means that the string\nmay be written to the pager instead of directly to the terminal. This\nbehaviour is problematic for two reasons:\n\n  - Color codes are meant for direct terminal output; writing them into\n    a pager does not seem like a sensible thing to do without additional\n    text.\n\n  - It is inconsistent with `git config --get-color`, which never uses a\n    pager, despite the fact that we claim `git config get --type=color`\n    to be a drop-in replacement in git-config(1).\n\nFix this by disabling the pager when outputting color sequences.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 3 ++-\n t/t1300-config.sh | 9 +++++++++\n 2 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex f50c11df57..6708d91814 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -919,7 +919,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \tlocation_options_init(&location_opts, prefix);\n \tdisplay_options_init(&display_opts);\n \n-\tsetup_auto_pager(\"config\", 1);\n+\tif (display_opts.type != TYPE_COLOR)\n+\t\tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 3cf5d17aba..358d636379 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -9,6 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n for mode in legacy subcommands\n do\n@@ -1099,6 +1100,14 @@ test_expect_success 'get --type=color with default value only' '\n \ttest_cmp actual.raw actual-subcommand.raw\n '\n \n+test_expect_success TTY 'get --type=color does not use a pager' '\n+\ttest_config core.pager \"echo foobar\" &&\n+\ttest_terminal git config get --type=color --default=\"red\" \"\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<\\EOF &&\n [foo]\n\n-- \n2.51.0.534.gc79095c0ca.dirty\n\n"},{"id":"526678","messageId":"xmqqikhg9rwx.fsf@gitster.g","threadId":"64128","inReplyTo":"20250918-pks-config-color-v3-4-08ea618cae26@pks.im","subject":"Re: [PATCH v3 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-18T06:49:18Z","receivedAt":"2025-09-18T06:49:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Our documentation for git-config(1) has a section where it explains how\n> to parse and use colors as Git would configure them. In order to get the\n> ANSI color escape sequence to reset the colors to normal we recommend\n> the following command:\n>\n>     $ git config get --type=color --default=\"reset\" \"\"\n>\n> This command is not supposed to parse any configuration keys. Instead,\n> it is expected to parse the \"reset\" default value and turn it into a\n> proper ANSI color escape sequence.\n>\n> It was reported though [1] that this command doesn't work:\n>\n>     $ git config get --type=color --default=\"reset\" \"\"\n>     error: key does not contain a section:\n>\n> This error was introduced in 4e51389000 (builtin/config: introduce \"get\"\n> subcommand, 2024-05-06), where we introduced the \"get\" subcommand to\n> retrieve configuration values. The preimage of that commit used `git\n> config --get-color \"\" \"reset\"` instead, which still works.\n>\n> This use case is really quite specific to parsing colors, as it wouldn't\n> make sense to give git-config(1) a default value and an empty config key\n> only to return that default value unmodified. But with `--type=color` we\n> don't return the value directly; we instead parse the value into an ANSI\n> escape sequence.\n>\n> As such, we can easily special-case this one use case:\n>\n>     - If the provided config key is empty;\n>\n>     - the user is asking for a color code and the user; and\n\n\"and the user;\" -> \";\" perhaps?\n\n>\n>     - the user has provided a default value,\n>\n> then we call `get_color()` directly. Do so to make the documented\n> command work as expected.\n\nIf we are willing to handle this as a special case anyway, I wonder\nif it can easily be arranged to take this as a(nother) special case.\n\n    $ git config get --type=color --default=\"reset\"\n\nI.e., instead of (or in addition to) \"if the config key is empty\",\nspecial case \"if the config key is not given\", which may be slightly\nmore intuitive.\n\nBut even without it, what is presented is a vast improvement enough\n;-)\n\n\n"},{"id":"526928","messageId":"aNFJanpSY8wd8-b1@pks.im","threadId":"64128","inReplyTo":"xmqqikhg9rwx.fsf@gitster.g","subject":"Re: [PATCH v3 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:04:42Z","receivedAt":"2025-09-22T13:04:48Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Sep 17, 2025 at 11:49:18PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > Our documentation for git-config(1) has a section where it explains how\n> > to parse and use colors as Git would configure them. In order to get the\n> > ANSI color escape sequence to reset the colors to normal we recommend\n> > the following command:\n> >\n> >     $ git config get --type=color --default=\"reset\" \"\"\n> >\n> > This command is not supposed to parse any configuration keys. Instead,\n> > it is expected to parse the \"reset\" default value and turn it into a\n> > proper ANSI color escape sequence.\n> >\n> > It was reported though [1] that this command doesn't work:\n> >\n> >     $ git config get --type=color --default=\"reset\" \"\"\n> >     error: key does not contain a section:\n> >\n> > This error was introduced in 4e51389000 (builtin/config: introduce \"get\"\n> > subcommand, 2024-05-06), where we introduced the \"get\" subcommand to\n> > retrieve configuration values. The preimage of that commit used `git\n> > config --get-color \"\" \"reset\"` instead, which still works.\n> >\n> > This use case is really quite specific to parsing colors, as it wouldn't\n> > make sense to give git-config(1) a default value and an empty config key\n> > only to return that default value unmodified. But with `--type=color` we\n> > don't return the value directly; we instead parse the value into an ANSI\n> > escape sequence.\n> >\n> > As such, we can easily special-case this one use case:\n> >\n> >     - If the provided config key is empty;\n> >\n> >     - the user is asking for a color code and the user; and\n> \n> \"and the user;\" -> \";\" perhaps?\n\nOh, yeah, thanks.\n\n> >     - the user has provided a default value,\n> >\n> > then we call `get_color()` directly. Do so to make the documented\n> > command work as expected.\n> \n> If we are willing to handle this as a special case anyway, I wonder\n> if it can easily be arranged to take this as a(nother) special case.\n> \n>     $ git config get --type=color --default=\"reset\"\n> \n> I.e., instead of (or in addition to) \"if the config key is empty\",\n> special case \"if the config key is not given\", which may be slightly\n> more intuitive.\n> \n> But even without it, what is presented is a vast improvement enough\n> ;-)\n\nWe probably could, yeah. But it starts to become even weirder than it\nalready is, so I'd honestly just leave it as-is for now :) I doubt that\nthere's too many users out there that care about this anyway.\n\nPatrick\n"},{"id":"526929","messageId":"20250922-pks-config-color-v4-0-28b7d2697eed@pks.im","threadId":"64128","inReplyTo":"20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im","subject":"[PATCH v4 0/5] builtin/config: bug fixes for \"get\" subcommand with \"--type=color\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:06:17Z","receivedAt":"2025-09-22T13:06:25Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis small patch series contains two bug fixes for `git config get\n--type=color`:\n\n  - We restore the behaviour where we can now parse colors without a\n    config key.\n\n  - We stop spawning the pager when the user requests to print ANSI\n    color escape sequences.\n\nFurthermore, the patch series does some lighter refactorings of t1300.\nThat test file still has its fair share of issues, but at least it looks\na bit less dirty now.\n\nChanges in v2:\n  - Improve commit messages.\n  - Use \"\\EOF\" and \"-EOF\" in more cases.\n  - Move a style fixup from the first commit into the second commit.\n  - Link to v1: https://lore.kernel.org/r/20250911-pks-config-color-v1-0-3a7c79df65b1@pks.im\n\nChanges in v3:\n  - Provide additional context as part of the commit message for the\n    commit that stops setting up the pager with `--type=color`.\n  - Link to v2: https://lore.kernel.org/r/20250915-pks-config-color-v2-0-e4290bd8d13c@pks.im\n\nChanges in v4:\n  - Fix a commit message grammar bug.\n  - Link to v3: https://lore.kernel.org/r/20250918-pks-config-color-v3-0-08ea618cae26@pks.im\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (5):\n      t1300: write test expectations in the test's body\n      t1300: small style fixups\n      builtin/config: do not die in `get_color()`\n      builtin/config: special-case retrieving colors without a key\n      builtin/config: do not spawn pager when printing color codes\n\n builtin/config.c  |  20 +++-\n t/t1300-config.sh | 349 +++++++++++++++++++++++++++---------------------------\n 2 files changed, 187 insertions(+), 182 deletions(-)\n\nRange-diff versus v3:\n\n1:  e62108edc8 = 1:  8d873d65de t1300: write test expectations in the test's body\n2:  deef91062a = 2:  39eeb35d43 t1300: small style fixups\n3:  58940b2cf9 = 3:  ee22438222 builtin/config: do not die in `get_color()`\n4:  288d51d1c0 ! 4:  44b1d54527 builtin/config: special-case retrieving colors without a key\n    @@ Commit message\n     \n             - If the provided config key is empty;\n     \n    -        - the user is asking for a color code and the user; and\n    +        - the user is asking for a color code; and\n     \n             - the user has provided a default value,\n     \n5:  9a3adf8795 = 5:  b4d72c21f5 builtin/config: do not spawn pager when printing color codes\n\n---\nbase-commit: ab427cd991100e94792fce124b0934135abdea4b\nchange-id: 20250911-pks-config-color-e5b8a213e895\n\n"},{"id":"526930","messageId":"20250922-pks-config-color-v4-1-28b7d2697eed@pks.im","threadId":"64128","inReplyTo":"20250922-pks-config-color-v4-0-28b7d2697eed@pks.im","subject":"[PATCH v4 1/5] t1300: write test expectations in the test's body","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:06:18Z","receivedAt":"2025-09-22T13:06:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"There are a bunch of tests in t1300 where we write the test expectation\nhanded over to `test_cmp ()` outside of the test body. This does not\nmatch our modern test style, and there isn't really a reason why this\nwould need to happen outside of the test bodies.\n\nConvert those to instead do so as part of the test itself. While at it,\nnormalize these tests to use `<<\\EOF` for those that don't use variable\nexpansion and `<<-EOF` for those that aren't sensitive to indentation.\n\nNote that there are two exceptions that we leave as-is for now since\nthey are reused across tests.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 291 ++++++++++++++++++++++++------------------------------\n 1 file changed, 128 insertions(+), 163 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex f856821839..538f2c9b8a 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -134,38 +134,39 @@ test_expect_success 'clear default config' '\n \trm -f .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'initial' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n EOF\n-test_expect_success 'initial' '\n \tgit config ${mode_set} section.penguin \"little blue\" &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'mixed case' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n EOF\n-test_expect_success 'mixed case' '\n \tgit config ${mode_set} Section.Movie BadPhysics &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'similar section' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'similar section' '\n \tgit config ${mode_set} Sections.WhatEver Second &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'uppercase section' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tpenguin = little blue\n \tMovie = BadPhysics\n@@ -173,7 +174,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-test_expect_success 'uppercase section' '\n \tgit config ${mode_set} SECTION.UPPERCASE true &&\n \ttest_cmp expect .git/config\n '\n@@ -186,7 +186,8 @@ test_expect_success 'replace with non-match (actually matching)' '\n \tgit config section.penguin \"very blue\" !kingpin\n '\n \n-cat > expect << EOF\n+test_expect_success 'append comments' '\n+\tcat >expect <<\\EOF &&\n [section]\n \tMovie = BadPhysics\n \tUPPERCASE = true\n@@ -198,8 +199,6 @@ cat > expect << EOF\n [Sections]\n \tWhatEver = Second\n EOF\n-\n-test_expect_success 'append comments' '\n \tgit config --replace-all --comment=\"Pygoscelis papua\" section.penguin gentoo &&\n \tgit config ${mode_set} --comment=\"find fish\" section.disposition peckish &&\n \tgit config ${mode_set} --comment=\"#abc\" section.foo bar &&\n@@ -265,14 +264,15 @@ test_expect_success 'unset with cont. lines' '\n \tgit config ${mode_unset} beta.baz\n '\n \n-cat > expect <<\\EOF\n-[alpha]\n-bar = foo\n-[beta]\n-foo = bar\n-EOF\n-\n-test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'\n+test_expect_success 'unset with cont. lines is correct' '\n+\tcat >expect <<-\\EOF &&\n+\t[alpha]\n+\tbar = foo\n+\t[beta]\n+\tfoo = bar\n+\tEOF\n+\ttest_cmp expect .git/config\n+'\n \n cat > .git/config << EOF\n [beta] ; silly comment # another comment\n@@ -292,16 +292,15 @@ test_expect_success 'multiple unset' '\n \tgit config ${mode_unset_all} beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'multiple unset is correct' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'multiple unset is correct' '\n \ttest_cmp expect .git/config\n '\n \n@@ -318,37 +317,37 @@ test_expect_success '--replace-all' '\n \tgit config ${mode_replace_all} beta.haha gamma\n '\n \n-cat > expect << EOF\n+test_expect_success 'all replaced' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = gamma\n [nextSection] noNewline = ouch\n EOF\n-\n-test_expect_success 'all replaced' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n \thaha = alpha\n [nextSection] noNewline = ouch\n EOF\n-test_expect_success 'really mean test' '\n \tgit config ${mode_set} beta.haha alpha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'really really mean test' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -356,7 +355,6 @@ noIndent= sillyValue ; 'nother silly comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'really really mean test' '\n \tgit config ${mode_set} nextsection.nonewline wow &&\n \ttest_cmp expect .git/config\n '\n@@ -365,23 +363,24 @@ test_expect_success 'get value' '\n \ttest_cmp_config alpha beta.haha\n '\n \n-cat > expect << EOF\n+test_expect_success 'unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tnonewline = wow\n EOF\n-test_expect_success 'unset' '\n \tgit config ${mode_unset} beta.haha &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar' '\n+\tcat  >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -389,7 +388,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar' '\n \tgit config nextsection.NoNewLine \"wow2 for me\" \"for me$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -415,9 +413,10 @@ test_expect_success 'multi-valued get-all returns all' '\n \ttest_cmp expect actual\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar replace' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -425,7 +424,6 @@ noIndent= sillyValue ; 'nother silly comment\n \tnonewline = wow3\n \tNoNewLine = wow2 for me\n EOF\n-test_expect_success 'multivar replace' '\n \tgit config nextsection.nonewline \"wow3\" \"wow$\" &&\n \ttest_cmp expect .git/config\n '\n@@ -438,17 +436,16 @@ test_expect_success 'invalid unset' '\n \ttest_must_fail git config ${mode_unset} somesection.nonewline\n '\n \n-cat > expect << EOF\n+test_expect_success 'multivar unset' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n [nextSection]\n \tNoNewLine = wow2 for me\n EOF\n-\n-test_expect_success 'multivar unset' '\n \tcase \"$mode\" in\n \tlegacy)\n \t\tgit config --unset nextsection.nonewline \"wow3$\";;\n@@ -466,9 +463,10 @@ test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n '\n \n-cat > expect << EOF\n+test_expect_success 'hierarchical section value' '\n+\tcat >expect <<EOF &&\n [beta] ; silly comment # another comment\n-noIndent= sillyValue ; 'nother silly comment\n+noIndent= sillyValue ; ${SQ}nother silly comment\n \n # empty line\n \t\t; comment\n@@ -479,19 +477,16 @@ noIndent= sillyValue ; 'nother silly comment\n [Version \"1.2.3eX\"]\n \tAlpha = beta\n EOF\n-\n-test_expect_success 'hierarchical section value' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n-beta.noindent=sillyValue\n-nextsection.nonewline=wow2 for me\n-123456.a123=987\n-version.1.2.3eX.alpha=beta\n-EOF\n-\n test_expect_success 'working --list' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent=sillyValue\n+\tnextsection.nonewline=wow2 for me\n+\t123456.a123=987\n+\tversion.1.2.3eX.alpha=beta\n+\tEOF\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n@@ -500,44 +495,40 @@ test_expect_success '--list without repo produces empty output' '\n \ttest_must_be_empty output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-123456.a123\n-version.1.2.3eX.alpha\n-EOF\n-\n test_expect_success '--name-only --list' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\t123456.a123\n+\tversion.1.2.3eX.alpha\n+\tEOF\n \tgit config ${mode_prefix}list --name-only >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent sillyValue\n-nextsection.nonewline wow2 for me\n-EOF\n-\n test_expect_success '--get-regexp' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent sillyValue\n+\tnextsection.nonewline wow2 for me\n+\tEOF\n \tgit config ${mode_get_regexp} in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-beta.noindent\n-nextsection.nonewline\n-EOF\n-\n test_expect_success '--name-only --get-regexp' '\n+\tcat >expect <<-\\EOF &&\n+\tbeta.noindent\n+\tnextsection.nonewline\n+\tEOF\n \tgit config ${mode_get_regexp} --name-only in >output &&\n \ttest_cmp expect output\n '\n \n-cat > expect << EOF\n-wow2 for me\n-wow4 for you\n-EOF\n-\n test_expect_success '--add' '\n+\tcat >expect <<-\\EOF &&\n+\twow2 for me\n+\twow4 for you\n+\tEOF\n \tgit config --add nextsection.nonewline \"wow4 for you\" &&\n \tgit config ${mode_get_all} nextsection.nonewline > output &&\n \ttest_cmp expect output\n@@ -558,37 +549,32 @@ test_expect_success 'get variable with empty value' '\n \tgit config --get emptyvalue.variable ^$\n '\n \n-echo novalue.variable > expect\n-\n test_expect_success 'get-regexp variable with no value' '\n+\techo novalue.variable >expect &&\n \tgit config ${mode_get_regexp} novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'novalue.variable true' > expect\n-\n test_expect_success 'get-regexp --bool variable with no value' '\n+\techo \"novalue.variable true\" >expect &&\n \tgit config ${mode_get_regexp} --bool novalue > output &&\n \ttest_cmp expect output\n '\n \n-echo 'emptyvalue.variable ' > expect\n-\n test_expect_success 'get-regexp variable with empty value' '\n+\techo \"emptyvalue.variable \" >expect &&\n \tgit config ${mode_get_regexp} emptyvalue > output &&\n \ttest_cmp expect output\n '\n \n-echo true > expect\n-\n test_expect_success 'get bool variable with no value' '\n+\techo true >expect &&\n \tgit config --bool novalue.variable > output &&\n \ttest_cmp expect output\n '\n \n-echo false > expect\n-\n test_expect_success 'get bool variable with empty value' '\n+\techo false >expect &&\n \tgit config --bool emptyvalue.variable > output &&\n \ttest_cmp expect output\n '\n@@ -604,19 +590,19 @@ cat > .git/config << EOF\n \tc = d\n EOF\n \n-cat > expect << EOF\n+test_expect_success 'new section is partial match of another' '\n+\tcat >expect <<\\EOF &&\n [a.b]\n \tc = d\n [a]\n \tx = y\n EOF\n-\n-test_expect_success 'new section is partial match of another' '\n \tgit config a.x y &&\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'new variable inserts into proper section' '\n+\tcat >expect <<\\EOF &&\n [a.b]\n \tc = d\n [a]\n@@ -625,8 +611,6 @@ cat > expect << EOF\n [b]\n \tx = y\n EOF\n-\n-test_expect_success 'new variable inserts into proper section' '\n \tgit config b.x y &&\n \tgit config a.b c &&\n \ttest_cmp expect .git/config\n@@ -642,11 +626,10 @@ cat > other-config << EOF\n \tbahn = strasse\n EOF\n \n-cat > expect << EOF\n-ein.bahn=strasse\n-EOF\n-\n test_expect_success 'alternative GIT_CONFIG' '\n+\tcat >expect <<-\\EOF &&\n+\tein.bahn=strasse\n+\tEOF\n \tGIT_CONFIG=other-config git config ${mode_prefix}list >output &&\n \ttest_cmp expect output\n '\n@@ -675,14 +658,13 @@ test_expect_success 'refer config from subdirectory' '\n \ttest_cmp_config -C x strasse --file=../other-config --get ein.bahn\n '\n \n-cat > expect << EOF\n+test_expect_success '--set in alternative file' '\n+\tcat >expect <<\\EOF &&\n [ein]\n \tbahn = strasse\n [anwohner]\n \tpark = ausweis\n EOF\n-\n-test_expect_success '--set in alternative file' '\n \tgit config --file=other-config anwohner.park ausweis &&\n \ttest_cmp expect other-config\n '\n@@ -730,7 +712,8 @@ test_expect_success 'rename another section' '\n \tgit config ${mode_prefix}rename-section branch.\"1 234 blabl/a\" branch.drei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -740,8 +723,6 @@ cat > expect << EOF\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -753,7 +734,8 @@ test_expect_success 'rename a section with a var on the same line' '\n \tgit config ${mode_prefix}rename-section branch.vier branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'rename succeeded' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"zwei\"]\n@@ -765,8 +747,6 @@ weird\n [branch \"zwei\"]\n \tz = 1\n EOF\n-\n-test_expect_success 'rename succeeded' '\n \ttest_cmp expect .git/config\n '\n \n@@ -816,32 +796,29 @@ test_expect_success 'remove section' '\n \tgit config ${mode_prefix}remove-section branch.zwei\n '\n \n-cat > expect << EOF\n+test_expect_success 'section was removed properly' '\n+\tcat >expect <<\\EOF &&\n # Hallo\n \t#Bello\n [branch \"drei\"]\n weird\n EOF\n-\n-test_expect_success 'section was removed properly' '\n \ttest_cmp expect .git/config\n '\n \n-cat > expect << EOF\n+test_expect_success 'section ending' '\n+\tcat >expect <<\\EOF &&\n [gitcvs]\n \tenabled = true\n \tdbname = %Ggitcvs2.%a.%m.sqlite\n [gitcvs \"ext\"]\n \tdbname = %Ggitcvs1.%a.%m.sqlite\n EOF\n-\n-test_expect_success 'section ending' '\n \trm -f .git/config &&\n \tgit config ${mode_set} gitcvs.enabled true &&\n \tgit config ${mode_set} gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&\n \tgit config ${mode_set} gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&\n \ttest_cmp expect .git/config\n-\n '\n \n test_expect_success numbers '\n@@ -885,19 +862,17 @@ test_expect_success 'invalid stdin config' '\n \ttest_grep \"bad config line 1 in standard input\" output\n '\n \n-cat > expect << EOF\n-true\n-false\n-true\n-false\n-true\n-false\n-true\n-false\n-EOF\n-\n test_expect_success bool '\n-\n+\tcat >expect <<-\\EOF &&\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\ttrue\n+\tfalse\n+\tEOF\n \tgit config ${mode_set} bool.true1 01 &&\n \tgit config ${mode_set} bool.true2 -1 &&\n \tgit config ${mode_set} bool.true3 YeS &&\n@@ -923,7 +898,8 @@ test_expect_success 'invalid bool (set)' '\n \n \ttest_must_fail git config --bool bool.nobool foobar'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --bool' '\n+\tcat >expect <<\\EOF &&\n [bool]\n \ttrue1 = true\n \ttrue2 = true\n@@ -934,9 +910,6 @@ cat > expect <<\\EOF\n \tfalse3 = false\n \tfalse4 = false\n EOF\n-\n-test_expect_success 'set --bool' '\n-\n \trm -f .git/config &&\n \tgit config --bool bool.true1 01 &&\n \tgit config --bool bool.true2 -1 &&\n@@ -948,15 +921,13 @@ test_expect_success 'set --bool' '\n \tgit config --bool bool.false4 FALSE &&\n \ttest_cmp expect .git/config'\n \n-cat > expect <<\\EOF\n+test_expect_success 'set --int' '\n+\tcat >expect <<\\EOF &&\n [int]\n \tval1 = 1\n \tval2 = -1\n \tval3 = 5242880\n EOF\n-\n-test_expect_success 'set --int' '\n-\n \trm -f .git/config &&\n \tgit config --int int.val1 01 &&\n \tgit config --int int.val2 -1 &&\n@@ -994,7 +965,8 @@ test_expect_success 'get --bool-or-int' '\n \ttest_cmp expect actual\n '\n \n-cat >expect <<\\EOF\n+test_expect_success 'set --bool-or-int' '\n+\tcat >expect <<\\EOF &&\n [bool]\n \ttrue1 = true\n \tfalse1 = false\n@@ -1005,8 +977,6 @@ cat >expect <<\\EOF\n \tint2 = 1\n \tint3 = -1\n EOF\n-\n-test_expect_success 'set --bool-or-int' '\n \trm -f .git/config &&\n \tgit config --bool-or-int bool.true1 true &&\n \tgit config --bool-or-int bool.false1 false &&\n@@ -1018,14 +988,13 @@ test_expect_success 'set --bool-or-int' '\n \ttest_cmp expect .git/config\n '\n \n-cat >expect <<\\EOF\n+test_expect_success !MINGW 'set --path' '\n+\tcat >expect <<\\EOF &&\n [path]\n \thome = ~/\n \tnormal = /dev/null\n \ttrailingtilde = foo~\n EOF\n-\n-test_expect_success !MINGW 'set --path' '\n \trm -f .git/config &&\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n@@ -1037,25 +1006,23 @@ then\n \ttest_set_prereq HOMEVAR\n fi\n \n-cat >expect <<EOF\n-$HOME/\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success HOMEVAR 'get --path' '\n+\tcat >expect <<-EOF &&\n+\t$HOME/\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \tgit config --get --path path.home > result &&\n \tgit config --get --path path.normal >> result &&\n \tgit config --get --path path.trailingtilde >> result &&\n \ttest_cmp expect result\n '\n \n-cat >expect <<\\EOF\n-/dev/null\n-foo~\n-EOF\n-\n test_expect_success !MINGW 'get --path copes with unset $HOME' '\n+\tcat >expect <<-\\EOF &&\n+\t/dev/null\n+\tfoo~\n+\tEOF\n \t(\n \t\tsane_unset HOME &&\n \t\ttest_must_fail git config --get --path path.home \\\n@@ -1112,12 +1079,11 @@ test_expect_success 'get --type=color' '\n \ttest_cmp expect actual\n '\n \n-cat >expect << EOF\n+test_expect_success 'set --type=color' '\n+\tcat >expect <<\\EOF &&\n [foo]\n \tcolor = red\n EOF\n-\n-test_expect_success 'set --type=color' '\n \trm .git/config &&\n \tgit config --type=color foo.color \"red\" &&\n \ttest_cmp expect .git/config\n@@ -1133,14 +1099,14 @@ test_expect_success 'set --type=color barfs on non-color' '\n \ttest_grep \"cannot parse color\" error\n '\n \n-cat > expect << EOF\n+test_expect_success 'quoting' '\n+\tcat >expect <<\\EOF &&\n [quote]\n \tleading = \" test\"\n \tending = \"test \"\n \tsemicolon = \"test;test\"\n \thash = \"test#test\"\n EOF\n-test_expect_success 'quoting' '\n \trm -f .git/config &&\n \tgit config ${mode_set} quote.leading \" test\" &&\n \tgit config ${mode_set} quote.ending \"test \" &&\n@@ -1166,13 +1132,12 @@ inued\n inued\"\n EOF\n \n-cat > expect <<\\EOF\n-section.continued=continued\n-section.noncont=not continued\n-section.quotecont=cont;inued\n-EOF\n-\n test_expect_success 'value continued on next line' '\n+\tcat >expect <<-\\EOF &&\n+\tsection.continued=continued\n+\tsection.noncont=not continued\n+\tsection.quotecont=cont;inued\n+\tEOF\n \tgit config ${mode_prefix}list > result &&\n \ttest_cmp expect result\n '\n\n-- \n2.51.0.536.g15c5d4f767.dirty\n\n"},{"id":"526931","messageId":"20250922-pks-config-color-v4-2-28b7d2697eed@pks.im","threadId":"64128","inReplyTo":"20250922-pks-config-color-v4-0-28b7d2697eed@pks.im","subject":"[PATCH v4 2/5] t1300: small style fixups","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:06:19Z","receivedAt":"2025-09-22T13:06:30Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We have a couple of small style violations in t1300:\n\n  - An empty newline at the start of the test body.\n\n  - The test command is sometimes on the same line as the test name.\n\n  - The closing single-quote is sometimes on the same line as the last\n    command of the test.\n\nFix these.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t1300-config.sh | 38 ++++++++++++++++++++++++--------------\n 1 file changed, 24 insertions(+), 14 deletions(-)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 538f2c9b8a..6d1015acfd 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -213,7 +213,9 @@ test_expect_success 'Prohibited LF in comment' '\n \ttest_must_fail git config ${mode_set} --comment=\"a${LF}b\" section.k v\n '\n \n-test_expect_success 'non-match result' 'test_cmp expect .git/config'\n+test_expect_success 'non-match result' '\n+\ttest_cmp expect .git/config\n+'\n \n test_expect_success 'find mixed-case key by canonical name' '\n \ttest_cmp_config Second sections.whatever\n@@ -455,9 +457,13 @@ EOF\n \ttest_cmp expect .git/config\n '\n \n-test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'\n+test_expect_success 'invalid key' '\n+\ttest_must_fail git config inval.2key blabla\n+'\n \n-test_expect_success 'correct key' 'git config 123456.a123 987'\n+test_expect_success 'correct key' '\n+\tgit config 123456.a123 987\n+'\n \n test_expect_success 'hierarchical section' '\n \tgit config Version.1.2.3eX.Alpha beta\n@@ -490,6 +496,7 @@ test_expect_success 'working --list' '\n \tgit config ${mode_prefix}list > output &&\n \ttest_cmp expect output\n '\n+\n test_expect_success '--list without repo produces empty output' '\n \tgit --git-dir=nonexistent config ${mode_prefix}list >output &&\n \ttest_must_be_empty output\n@@ -887,16 +894,17 @@ test_expect_success bool '\n \t    git config --bool --get bool.true$i >>result &&\n \t    git config --bool --get bool.false$i >>result || return 1\n \tdone &&\n-\ttest_cmp expect result'\n+\ttest_cmp expect result\n+'\n \n test_expect_success 'invalid bool (--get)' '\n-\n \tgit config ${mode_set} bool.nobool foobar &&\n-\ttest_must_fail git config --bool --get bool.nobool'\n+\ttest_must_fail git config --bool --get bool.nobool\n+'\n \n test_expect_success 'invalid bool (set)' '\n-\n-\ttest_must_fail git config --bool bool.nobool foobar'\n+\ttest_must_fail git config --bool bool.nobool foobar\n+'\n \n test_expect_success 'set --bool' '\n \tcat >expect <<\\EOF &&\n@@ -999,7 +1007,8 @@ EOF\n \tgit config --path path.home \"~/\" &&\n \tgit config --path path.normal \"/dev/null\" &&\n \tgit config --path path.trailingtilde \"foo~\" &&\n-\ttest_cmp expect .git/config'\n+\ttest_cmp expect .git/config\n+'\n \n if test_have_prereq !MINGW && test \"${HOME+set}\"\n then\n@@ -1117,10 +1126,13 @@ EOF\n \n test_expect_success 'key with newline' '\n \ttest_must_fail git config ${mode_get} \"key.with\n-newline\" 123'\n+newline\" 123\n+'\n \n-test_expect_success 'value with newline' 'git config ${mode_set} key.sub value.with\\\\\\\n-newline'\n+test_expect_success 'value with newline' '\n+\tgit config ${mode_set} key.sub value.with\\\\\\\n+newline\n+'\n \n cat > .git/config <<\\EOF\n [section]\n@@ -1330,7 +1342,6 @@ test_expect_success 'multiple git -c appends config' '\n '\n \n test_expect_success 'last one wins: two level vars' '\n-\n \t# sec.var and sec.VAR are the same variable, as the first\n \t# and the last level of a configuration variable name is\n \t# case insensitive.\n@@ -1349,7 +1360,6 @@ test_expect_success 'last one wins: two level vars' '\n '\n \n test_expect_success 'last one wins: three level vars' '\n-\n \t# v.a.r and v.A.r are not the same variable, as the middle\n \t# level of a three-level configuration variable name is\n \t# case sensitive.\n\n-- \n2.51.0.536.g15c5d4f767.dirty\n\n"},{"id":"526932","messageId":"20250922-pks-config-color-v4-3-28b7d2697eed@pks.im","threadId":"64128","inReplyTo":"20250922-pks-config-color-v4-0-28b7d2697eed@pks.im","subject":"[PATCH v4 3/5] builtin/config: do not die in `get_color()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:06:20Z","receivedAt":"2025-09-22T13:06:33Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"When trying to parse an invalid color via `get_color()` we die. We're\nabout to introduce another caller in a subsequent commit though that has\nits own error handling, so dying is a bit drastic there. Furthermore,\nthe only caller that we already have right now already knows to handle\nerrors in other branches that don't call `get_color()`.\n\nConvert the function to instead return an error code to improve its\nflexibility.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 59fb113b07..afd48bfa51 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -547,24 +547,31 @@ static int git_get_color_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-static void get_color(const struct config_location_options *opts,\n+static int get_color(const struct config_location_options *opts,\n \t\t      const char *var, const char *def_color)\n {\n \tstruct get_color_config_data data = {\n \t\t.get_color_slot = var,\n \t\t.parsed_color[0] = '\\0',\n \t};\n+\tint ret;\n \n \tconfig_with_options(git_get_color_config, &data,\n \t\t\t    &opts->source, the_repository,\n \t\t\t    &opts->options);\n \n \tif (!data.get_color_found && def_color) {\n-\t\tif (color_parse(def_color, data.parsed_color) < 0)\n-\t\t\tdie(_(\"unable to parse default color value\"));\n+\t\tif (color_parse(def_color, data.parsed_color) < 0) {\n+\t\t\tret = error(_(\"unable to parse default color value\"));\n+\t\t\tgoto out;\n+\t\t}\n \t}\n \n+\tret = 0;\n+\n+out:\n \tfputs(data.parsed_color, stdout);\n+\treturn ret;\n }\n \n struct get_colorbool_config_data {\n@@ -1390,7 +1397,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)\n \t}\n \telse if (actions == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n-\t\tget_color(&location_opts, argv[0], argv[1]);\n+\t\tret = get_color(&location_opts, argv[0], argv[1]);\n \t}\n \telse if (actions == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n\n-- \n2.51.0.536.g15c5d4f767.dirty\n\n"},{"id":"526933","messageId":"20250922-pks-config-color-v4-4-28b7d2697eed@pks.im","threadId":"64128","inReplyTo":"20250922-pks-config-color-v4-0-28b7d2697eed@pks.im","subject":"[PATCH v4 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:06:21Z","receivedAt":"2025-09-22T13:06:35Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Our documentation for git-config(1) has a section where it explains how\nto parse and use colors as Git would configure them. In order to get the\nANSI color escape sequence to reset the colors to normal we recommend\nthe following command:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n\nThis command is not supposed to parse any configuration keys. Instead,\nit is expected to parse the \"reset\" default value and turn it into a\nproper ANSI color escape sequence.\n\nIt was reported though [1] that this command doesn't work:\n\n    $ git config get --type=color --default=\"reset\" \"\"\n    error: key does not contain a section:\n\nThis error was introduced in 4e51389000 (builtin/config: introduce \"get\"\nsubcommand, 2024-05-06), where we introduced the \"get\" subcommand to\nretrieve configuration values. The preimage of that commit used `git\nconfig --get-color \"\" \"reset\"` instead, which still works.\n\nThis use case is really quite specific to parsing colors, as it wouldn't\nmake sense to give git-config(1) a default value and an empty config key\nonly to return that default value unmodified. But with `--type=color` we\ndon't return the value directly; we instead parse the value into an ANSI\nescape sequence.\n\nAs such, we can easily special-case this one use case:\n\n    - If the provided config key is empty;\n\n    - the user is asking for a color code; and\n\n    - the user has provided a default value,\n\nthen we call `get_color()` directly. Do so to make the documented\ncommand work as expected.\n\n[1]: <aI+oQvQgnNtC6DVw@szeder.dev>\n\nReported-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  |  2 ++\n t/t1300-config.sh | 11 +++++++++++\n 2 files changed, 13 insertions(+)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex afd48bfa51..f50c11df57 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -923,6 +923,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\n+\telse if (display_opts.type == TYPE_COLOR && !strlen(argv[0]) && display_opts.default_value)\n+\t\tret = get_color(&location_opts, \"\", display_opts.default_value);\n \telse\n \t\tret = get_value(&location_opts, &display_opts, argv[0], value_pattern,\n \t\t\t\tget_value_flags, flags);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 6d1015acfd..3cf5d17aba 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1083,11 +1083,22 @@ test_expect_success 'get --type=color' '\n \trm .git/config &&\n \tgit config ${mode_set} foo.color \"red\" &&\n \tgit config --get --type=color foo.color >actual.raw &&\n+\tgit config get --type=color foo.color >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \techo \"<RED>\" >expect &&\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'get --type=color with default value only' '\n+\tgit config --get-color \"\" \"red\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual &&\n+\tgit config get --type=color --default=\"red\" \"\" >actual-subcommand.raw &&\n+\ttest_cmp actual.raw actual-subcommand.raw\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<\\EOF &&\n [foo]\n\n-- \n2.51.0.536.g15c5d4f767.dirty\n\n"},{"id":"526934","messageId":"20250922-pks-config-color-v4-5-28b7d2697eed@pks.im","threadId":"64128","inReplyTo":"20250922-pks-config-color-v4-0-28b7d2697eed@pks.im","subject":"[PATCH v4 5/5] builtin/config: do not spawn pager when printing color codes","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:06:22Z","receivedAt":"2025-09-22T13:06:39Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"With `git config get --type=color` the user asks us to parse a specific\nconfiguration key and turn the value into an ANSI color escape sequence.\nThe printed string can then for example be used as part of shell scripts\nto reuse the same colors as Git.\n\nRight now though we set up the auto-pager, which means that the string\nmay be written to the pager instead of directly to the terminal. This\nbehaviour is problematic for two reasons:\n\n  - Color codes are meant for direct terminal output; writing them into\n    a pager does not seem like a sensible thing to do without additional\n    text.\n\n  - It is inconsistent with `git config --get-color`, which never uses a\n    pager, despite the fact that we claim `git config get --type=color`\n    to be a drop-in replacement in git-config(1).\n\nFix this by disabling the pager when outputting color sequences.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/config.c  | 3 ++-\n t/t1300-config.sh | 9 +++++++++\n 2 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex f50c11df57..6708d91814 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -919,7 +919,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix,\n \tlocation_options_init(&location_opts, prefix);\n \tdisplay_options_init(&display_opts);\n \n-\tsetup_auto_pager(\"config\", 1);\n+\tif (display_opts.type != TYPE_COLOR)\n+\t\tsetup_auto_pager(\"config\", 1);\n \n \tif (url)\n \t\tret = get_urlmatch(&location_opts, &display_opts, argv[0], url);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 3cf5d17aba..358d636379 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -9,6 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n for mode in legacy subcommands\n do\n@@ -1099,6 +1100,14 @@ test_expect_success 'get --type=color with default value only' '\n \ttest_cmp actual.raw actual-subcommand.raw\n '\n \n+test_expect_success TTY 'get --type=color does not use a pager' '\n+\ttest_config core.pager \"echo foobar\" &&\n+\ttest_terminal git config get --type=color --default=\"red\" \"\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\techo \"<RED>\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'set --type=color' '\n \tcat >expect <<\\EOF &&\n [foo]\n\n-- \n2.51.0.536.g15c5d4f767.dirty\n\n"},{"id":"526951","messageId":"xmqqo6r2v4bs.fsf@gitster.g","threadId":"64128","inReplyTo":"aNFJanpSY8wd8-b1@pks.im","subject":"Re: [PATCH v3 4/5] builtin/config: special-case retrieving colors without a key","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-22T16:29:11Z","receivedAt":"2025-09-22T16:29:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>> If we are willing to handle this as a special case anyway, I wonder\n>> if it can easily be arranged to take this as a(nother) special case.\n>> \n>>     $ git config get --type=color --default=\"reset\"\n>> \n>> I.e., instead of (or in addition to) \"if the config key is empty\",\n>> special case \"if the config key is not given\", which may be slightly\n>> more intuitive.\n>> \n>> But even without it, what is presented is a vast improvement enough\n>> ;-)\n>\n> We probably could, yeah. But it starts to become even weirder than it\n> already is, so I'd honestly just leave it as-is for now :) I doubt that\n> there's too many users out there that care about this anyway.\n\nYes, unless we can require \"--default\" to be spelled \"--translate-this\"\nonly in that specific use case (which we don't want to), the keyless\ncase would be even more strange than the empty key case, so I am\nhappy with what we see in this series.\n\nThanks.\n"}]}