{"thread":{"id":"66170","subject":"[PATCH] t1402: test forbidden characters in refnames","startedAt":"2026-08-13T20:43:59Z","lastAt":"2026-08-23T14:16:15Z","messageCount":7,"participants":["Nikolaus Schuetz via GitGitGadget","Patrick Steinhardt","Junio C Hamano","Nikolaus Schuetz"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"550578","messageId":"pull.2203.git.1786653837190.gitgitgadget@gmail.com","threadId":"66170","inReplyTo":null,"subject":"[PATCH] t1402: test forbidden characters in refnames","fromName":"Nikolaus Schuetz via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-13T20:43:56Z","receivedAt":"2026-08-13T20:43:59Z","isPatch":true,"body":"From: Nikolaus Schuetz <nikolauspschuetz@gmail.com>\n\ngit-check-ref-format(1) documents that a refname cannot contain a\nspace, tilde, caret, colon, question-mark, asterisk or open-bracket,\nand that it cannot be the single character \"@\".  Of these, only \"?\"\nwas tested as a character embedded in an otherwise-valid refname;\n\"*\" was checked only as a lone character or with --refspec-pattern.\n\nAdd the remaining forbidden characters in that embedded form, and\ncheck that \"@\" alone is rejected even with --allow-onelevel -- where\n\"@\" is otherwise a valid refname component, as \"refs/@\" confirms.\n\nSigned-off-by: Nikolaus Schuetz <nikolauspschuetz@gmail.com>\n---\n    t1402: test forbidden characters in refnames\n    \n    git-check-ref-format(1) documents the characters that a refname may not\n    contain (space, tilde, caret, colon, question-mark, asterisk,\n    open-bracket) and the rule that it may not be the single character \"@\".\n    t1402 only exercised a few of these directly.\n    \n    This adds the remaining forbidden characters in embedded form, and\n    checks that \"@\" alone is rejected even with --allow-onelevel, where \"@\"\n    is otherwise a valid refname component (as \"refs/@\" confirms).\n    \n    Test-only; documents existing behaviour, in the spirit of 919eb8ace\n    (t1402: check for refs ending with a dot).\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2203%2Fnikolauspschuetz%2Fns%2Ft1402-forbidden-characters-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2203/nikolauspschuetz/ns/t1402-forbidden-characters-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/2203\n\n t/t1402-check-ref-format.sh | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh\nindex cabc516ae9..bc1e878a0f 100755\n--- a/t/t1402-check-ref-format.sh\n+++ b/t/t1402-check-ref-format.sh\n@@ -51,12 +51,20 @@ invalid_ref '.refs/foo'\n invalid_ref 'refs/heads/foo.'\n invalid_ref 'heads/foo..bar'\n invalid_ref 'heads/foo?bar'\n+invalid_ref 'heads/foo~bar'\n+invalid_ref 'heads/foo^bar'\n+invalid_ref 'heads/foo:bar'\n+invalid_ref 'heads/foo*bar'\n+invalid_ref 'heads/foo[bar'\n+invalid_ref 'heads/foo bar'\n valid_ref 'foo./bar'\n invalid_ref 'heads/foo.lock'\n invalid_ref 'heads///foo.lock'\n invalid_ref 'foo.lock/bar'\n invalid_ref 'foo.lock///bar'\n valid_ref 'heads/foo@bar'\n+valid_ref 'refs/@'\n+invalid_ref '@' --allow-onelevel\n invalid_ref 'heads/v@{ation'\n invalid_ref 'heads/foo\\bar'\n invalid_ref \"$(printf 'heads/foo\\t')\"\n\nbase-commit: 745601a9a94110d74769ab605ccd4f61339758d2\n-- \ngitgitgadget\n"},{"id":"550800","messageId":"aoWRZhO6BVy7uPLI@pks.im","threadId":"66170","inReplyTo":"pull.2203.git.1786653837190.gitgitgadget@gmail.com","subject":"Re: [PATCH] t1402: test forbidden characters in refnames","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-08-19T11:20:06Z","receivedAt":"2026-08-19T11:20:13Z","isPatch":true,"body":"On Thu, Aug 13, 2026 at 08:43:56PM +0000, Nikolaus Schuetz via GitGitGadget wrote:\n> From: Nikolaus Schuetz <nikolauspschuetz@gmail.com>\n> \n> git-check-ref-format(1) documents that a refname cannot contain a\n> space, tilde, caret, colon, question-mark, asterisk or open-bracket,\n> and that it cannot be the single character \"@\".  Of these, only \"?\"\n> was tested as a character embedded in an otherwise-valid refname;\n> \"*\" was checked only as a lone character or with --refspec-pattern.\n> \n> Add the remaining forbidden characters in that embedded form, and\n> check that \"@\" alone is rejected even with --allow-onelevel -- where\n> \"@\" is otherwise a valid refname component, as \"refs/@\" confirms.\n\nOkay.\n\n> diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh\n> index cabc516ae9..bc1e878a0f 100755\n> --- a/t/t1402-check-ref-format.sh\n> +++ b/t/t1402-check-ref-format.sh\n> @@ -51,12 +51,20 @@ invalid_ref '.refs/foo'\n>  invalid_ref 'refs/heads/foo.'\n>  invalid_ref 'heads/foo..bar'\n>  invalid_ref 'heads/foo?bar'\n> +invalid_ref 'heads/foo~bar'\n> +invalid_ref 'heads/foo^bar'\n> +invalid_ref 'heads/foo:bar'\n> +invalid_ref 'heads/foo*bar'\n> +invalid_ref 'heads/foo[bar'\n> +invalid_ref 'heads/foo bar'\n\nThis feels a tiny bit excessive, but I guess it does not hurt to enforce\nthis property, especially now that it's so easy to add new backends.\n\nOne thing I was briefly wondering is whether we could maybe have a\nsimple loop here, as this feels quite repetitive. We could for example:\n\n    for c in '?' '~' '^' ':' '*' '[' ' '\n    do\n        invalid_ref \"heads/foo${c}bar\"\n    done\n\nBy the way, one weird bit: is it intentional that all of these really\nuse \"heads/something\" instead of \"refs/heads/something\"? I guess it\nultimately doesn't matter.\n\n>  valid_ref 'foo./bar'\n>  invalid_ref 'heads/foo.lock'\n>  invalid_ref 'heads///foo.lock'\n>  invalid_ref 'foo.lock/bar'\n>  invalid_ref 'foo.lock///bar'\n>  valid_ref 'heads/foo@bar'\n> +valid_ref 'refs/@'\n> +invalid_ref '@' --allow-onelevel\n\nThis one certainly is a good addition, as these are quite a bit more\nsubtle.\n\nThanks!\n\nPatrick\n"},{"id":"550841","messageId":"xmqqo6exuagw.fsf@gitster.g","threadId":"66170","inReplyTo":"aoWRZhO6BVy7uPLI@pks.im","subject":"Re: [PATCH] t1402: test forbidden characters in refnames","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-19T20:22:23Z","receivedAt":"2026-08-19T20:22:26Z","isPatch":true,"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Thu, Aug 13, 2026 at 08:43:56PM +0000, Nikolaus Schuetz via GitGitGadget wrote:\n>> From: Nikolaus Schuetz <nikolauspschuetz@gmail.com>\n>> \n>> git-check-ref-format(1) documents that a refname cannot contain a\n>> space, tilde, caret, colon, question-mark, asterisk or open-bracket,\n>> and that it cannot be the single character \"@\".  Of these, only \"?\"\n>> was tested as a character embedded in an otherwise-valid refname;\n>> \"*\" was checked only as a lone character or with --refspec-pattern.\n>> \n>> Add the remaining forbidden characters in that embedded form, and\n>> check that \"@\" alone is rejected even with --allow-onelevel -- where\n>> \"@\" is otherwise a valid refname component, as \"refs/@\" confirms.\n>\n> Okay.\n>\n>> diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh\n>> index cabc516ae9..bc1e878a0f 100755\n>> --- a/t/t1402-check-ref-format.sh\n>> +++ b/t/t1402-check-ref-format.sh\n>> @@ -51,12 +51,20 @@ invalid_ref '.refs/foo'\n>>  invalid_ref 'refs/heads/foo.'\n>>  invalid_ref 'heads/foo..bar'\n>>  invalid_ref 'heads/foo?bar'\n>> +invalid_ref 'heads/foo~bar'\n>> +invalid_ref 'heads/foo^bar'\n>> +invalid_ref 'heads/foo:bar'\n>> +invalid_ref 'heads/foo*bar'\n>> +invalid_ref 'heads/foo[bar'\n>> +invalid_ref 'heads/foo bar'\n>\n> This feels a tiny bit excessive, but I guess it does not hurt to enforce\n> this property, especially now that it's so easy to add new backends.\n\n\"Why would we even care to check these insane cases?\" was my first\nreaction, but I agree with you that these are to protect authors of\nnew backends from stupid mistakes.\n\n> One thing I was briefly wondering is whether we could maybe have a\n> simple loop here, as this feels quite repetitive. We could for example:\n>\n>     for c in '?' '~' '^' ':' '*' '[' ' '\n>     do\n>         invalid_ref \"heads/foo${c}bar\"\n>     done\n\nTrue.  And c does not have to be a single byte. \"..\" can also be\npart of the repertoire.\n\n> By the way, one weird bit: is it intentional that all of these really\n> use \"heads/something\" instead of \"refs/heads/something\"? I guess it\n> ultimately doesn't matter.\n>\n>>  valid_ref 'foo./bar'\n>>  invalid_ref 'heads/foo.lock'\n>>  invalid_ref 'heads///foo.lock'\n>>  invalid_ref 'foo.lock/bar'\n>>  invalid_ref 'foo.lock///bar'\n>>  valid_ref 'heads/foo@bar'\n>> +valid_ref 'refs/@'\n>> +invalid_ref '@' --allow-onelevel\n>\n> This one certainly is a good addition, as these are quite a bit more\n> subtle.\n>\n> Thanks!\n>\n> Patrick\n"},{"id":"550908","messageId":"20260820144648.47267-1-nikolauspschuetz@gmail.com","threadId":"66170","inReplyTo":"xmqqo6exuagw.fsf@gitster.g","subject":"Re: [PATCH] t1402: test forbidden characters in refnames","fromName":"Nikolaus Schuetz","fromEmail":"nikolauspschuetz@gmail.com","sentAt":"2026-08-20T14:46:37Z","receivedAt":"2026-08-20T14:47:19Z","isPatch":true,"body":"> True.  And c does not have to be a single byte. \"..\" can also be\n> part of the repertoire.\n\nAgreed and updated accordingly: forbidden chars are looped over,\nand I folded \"..\" in along with \"\\\" (the same forbidden-char list).\nThe other refname rules enforced by refs.c are well covered,\nso I kept the loop to the embedded forbidden tokens.\n\n> By the way, one weird bit: is it intentional that all of these really\n> use \"heads/something\" instead of \"refs/heads/something\"?\n\nNot intentional -- the file already mixes them (e.g. 'refs/heads/foo.'\nvs 'heads/foo..bar'). check-ref-format validates each component\nregardless of a refs/ prefix, so it doesn't change what's tested; I\nkept 'heads/' to match the neighbours.\n\nThanks,\nNikolaus\n"},{"id":"550954","messageId":"pull.2203.v2.git.1787264417682.gitgitgadget@gmail.com","threadId":"66170","inReplyTo":"pull.2203.git.1786653837190.gitgitgadget@gmail.com","subject":"[PATCH v2] t1402: test forbidden characters in refnames","fromName":"Nikolaus Schuetz via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-20T22:20:17Z","receivedAt":"2026-08-20T22:20:20Z","isPatch":true,"body":"From: Nikolaus Schuetz <nikolauspschuetz@gmail.com>\n\ngit-check-ref-format(1) documents that a refname cannot contain a\nspace, tilde, caret, colon, question-mark, asterisk, open-bracket or\nbackslash, nor the sequence \"..\", and cannot be the single character\n\"@\".  Of these, only \"?\", \"\\\" and \"..\" were tested embedded in an\notherwise-valid refname; \"*\" was checked only as a lone character or\nwith --refspec-pattern.\n\nTest all of them in that embedded form with a single loop, and check\nthat \"@\" alone is rejected even with --allow-onelevel -- where \"@\" is\notherwise a valid refname component, as \"refs/@\" confirms.\n\nSigned-off-by: Nikolaus Schuetz <nikolauspschuetz@gmail.com>\n---\n    t1402: test forbidden characters in refnames\n    \n    git-check-ref-format(1) documents the characters that a refname may not\n    contain (space, tilde, caret, colon, question-mark, asterisk,\n    open-bracket) and the rule that it may not be the single character \"@\".\n    t1402 only exercised a few of these directly.\n    \n    This adds the remaining forbidden characters in embedded form, and\n    checks that \"@\" alone is rejected even with --allow-onelevel, where \"@\"\n    is otherwise a valid refname component (as \"refs/@\" confirms).\n    \n    Test-only; documents existing behaviour, in the spirit of 919eb8ace\n    (t1402: check for refs ending with a dot).\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2203%2Fnikolauspschuetz%2Fns%2Ft1402-forbidden-characters-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2203/nikolauspschuetz/ns/t1402-forbidden-characters-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/2203\n\nRange-diff vs v1:\n\n 1:  f254db5b09 ! 1:  cc013499f9 t1402: test forbidden characters in refnames\n     @@ Commit message\n          t1402: test forbidden characters in refnames\n      \n          git-check-ref-format(1) documents that a refname cannot contain a\n     -    space, tilde, caret, colon, question-mark, asterisk or open-bracket,\n     -    and that it cannot be the single character \"@\".  Of these, only \"?\"\n     -    was tested as a character embedded in an otherwise-valid refname;\n     -    \"*\" was checked only as a lone character or with --refspec-pattern.\n     +    space, tilde, caret, colon, question-mark, asterisk, open-bracket or\n     +    backslash, nor the sequence \"..\", and cannot be the single character\n     +    \"@\".  Of these, only \"?\", \"\\\" and \"..\" were tested embedded in an\n     +    otherwise-valid refname; \"*\" was checked only as a lone character or\n     +    with --refspec-pattern.\n      \n     -    Add the remaining forbidden characters in that embedded form, and\n     -    check that \"@\" alone is rejected even with --allow-onelevel -- where\n     -    \"@\" is otherwise a valid refname component, as \"refs/@\" confirms.\n     +    Test all of them in that embedded form with a single loop, and check\n     +    that \"@\" alone is rejected even with --allow-onelevel -- where \"@\" is\n     +    otherwise a valid refname component, as \"refs/@\" confirms.\n      \n          Signed-off-by: Nikolaus Schuetz <nikolauspschuetz@gmail.com>\n      \n       ## t/t1402-check-ref-format.sh ##\n     -@@ t/t1402-check-ref-format.sh: invalid_ref '.refs/foo'\n     +@@ t/t1402-check-ref-format.sh: invalid_ref 'foo/./bar'\n     + invalid_ref 'foo/bar/.'\n     + invalid_ref '.refs/foo'\n       invalid_ref 'refs/heads/foo.'\n     - invalid_ref 'heads/foo..bar'\n     - invalid_ref 'heads/foo?bar'\n     -+invalid_ref 'heads/foo~bar'\n     -+invalid_ref 'heads/foo^bar'\n     -+invalid_ref 'heads/foo:bar'\n     -+invalid_ref 'heads/foo*bar'\n     -+invalid_ref 'heads/foo[bar'\n     -+invalid_ref 'heads/foo bar'\n     +-invalid_ref 'heads/foo..bar'\n     +-invalid_ref 'heads/foo?bar'\n     ++for c in '?' '~' '^' ':' '*' '[' ' ' '\\' '..'\n     ++do\n     ++\tinvalid_ref \"heads/foo${c}bar\"\n     ++done\n       valid_ref 'foo./bar'\n       invalid_ref 'heads/foo.lock'\n       invalid_ref 'heads///foo.lock'\n     @@ t/t1402-check-ref-format.sh: invalid_ref '.refs/foo'\n      +valid_ref 'refs/@'\n      +invalid_ref '@' --allow-onelevel\n       invalid_ref 'heads/v@{ation'\n     - invalid_ref 'heads/foo\\bar'\n     +-invalid_ref 'heads/foo\\bar'\n       invalid_ref \"$(printf 'heads/foo\\t')\"\n     + invalid_ref \"$(printf 'heads/foo\\177')\"\n     + valid_ref \"$(printf 'heads/fu\\303\\237')\"\n\n\n t/t1402-check-ref-format.sh | 9 ++++++---\n 1 file changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh\nindex cabc516ae9..9dd64662b2 100755\n--- a/t/t1402-check-ref-format.sh\n+++ b/t/t1402-check-ref-format.sh\n@@ -49,16 +49,19 @@ invalid_ref 'foo/./bar'\n invalid_ref 'foo/bar/.'\n invalid_ref '.refs/foo'\n invalid_ref 'refs/heads/foo.'\n-invalid_ref 'heads/foo..bar'\n-invalid_ref 'heads/foo?bar'\n+for c in '?' '~' '^' ':' '*' '[' ' ' '\\' '..'\n+do\n+\tinvalid_ref \"heads/foo${c}bar\"\n+done\n valid_ref 'foo./bar'\n invalid_ref 'heads/foo.lock'\n invalid_ref 'heads///foo.lock'\n invalid_ref 'foo.lock/bar'\n invalid_ref 'foo.lock///bar'\n valid_ref 'heads/foo@bar'\n+valid_ref 'refs/@'\n+invalid_ref '@' --allow-onelevel\n invalid_ref 'heads/v@{ation'\n-invalid_ref 'heads/foo\\bar'\n invalid_ref \"$(printf 'heads/foo\\t')\"\n invalid_ref \"$(printf 'heads/foo\\177')\"\n valid_ref \"$(printf 'heads/fu\\303\\237')\"\n\nbase-commit: 745601a9a94110d74769ab605ccd4f61339758d2\n-- \ngitgitgadget\n"},{"id":"550998","messageId":"xmqq5x13stxt.fsf@gitster.g","threadId":"66170","inReplyTo":"pull.2203.v2.git.1787264417682.gitgitgadget@gmail.com","subject":"Re: [PATCH v2] t1402: test forbidden characters in refnames","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-21T09:29:18Z","receivedAt":"2026-08-21T09:29:22Z","isPatch":true,"body":"\"Nikolaus Schuetz via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n>     This adds the remaining forbidden characters in embedded form, and\n>     checks that \"@\" alone is rejected even with --allow-onelevel, where \"@\"\n>     is otherwise a valid refname component (as \"refs/@\" confirms).\n\nMany funny characters are not allowed between 'foo' and 'bar', but\nare there characters other than dot that are not allowed at the\nbeginning or at the end (e.g., \"refs/heads/foo.\" and \"foo.lock\")?\n\nIOW are we testing exhaustive now?\n\n>  invalid_ref '.refs/foo'\n>  invalid_ref 'refs/heads/foo.'\n> -invalid_ref 'heads/foo..bar'\n> -invalid_ref 'heads/foo?bar'\n> +for c in '?' '~' '^' ':' '*' '[' ' ' '\\' '..'\n> +do\n> +\tinvalid_ref \"heads/foo${c}bar\"\n> +done\n>  valid_ref 'foo./bar'\n>  invalid_ref 'heads/foo.lock'\n>  invalid_ref 'heads///foo.lock'\n"},{"id":"551073","messageId":"20260823141600.74820-1-nikolauspschuetz@gmail.com","threadId":"66170","inReplyTo":"xmqq5x13stxt.fsf@gitster.g","subject":"Re: [PATCH v2] t1402: test forbidden characters in refnames","fromName":"Nikolaus Schuetz","fromEmail":"nikolauspschuetz@gmail.com","sentAt":"2026-08-23T14:15:11Z","receivedAt":"2026-08-23T14:16:15Z","isPatch":true,"body":"> Many funny characters are not allowed between 'foo' and 'bar', but\n> are there characters other than dot that are not allowed at the\n> beginning or at the end (e.g., \"refs/heads/foo.\" and \"foo.lock\")?\n>\n> IOW are we testing exhaustive now?\n\nNo -- dot is the only character with position-specific rules,\nand every other character in the forbidden set is rejected\nanywhere in a component.\n\nThe file already exercises each of those rules on its own -- the\nleading/trailing-dot, \".lock\", empty-component, single-level and\n--normalize cases are all present. So this isn't reaching for\nexhaustiveness; the coverage was already broad, and this just\nfills the untested gap -- of the \"forbidden anywhere\" characters,\nonly \"?\", \"\\\" and \"..\" were tested embedded, so I folded the rest\ninto the loop.\n\nThanks,\nNikolaus\n"}]}