{"thread":{"id":"66055","subject":"[PATCH 0/3] config: support scp-style --url","startedAt":"2026-07-24T00:40:56Z","lastAt":"2026-07-28T10:51:39Z","messageCount":6,"participants":["Fabian Pottbäcker","Ben Knoble"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"548846","messageId":"20260724004011.41795-1-fpottbaecker+git@mailbox.org","threadId":"66055","inReplyTo":null,"subject":"[PATCH 0/3] config: support scp-style --url","fromName":"Fabian Pottbäcker","fromEmail":"fpottbaecker+git@mailbox.org","sentAt":"2026-07-24T00:40:08Z","receivedAt":"2026-07-24T00:40:56Z","isPatch":true,"body":"Almost all git commands support the same set of URL formats, which includes the\nscp-style shortcut for ssh. This format is the default format of multiple large\ngit services, presented to users when pressing a clone button. The config --url\noption was a notable exception. This is only relevant for scripting, it only\naffects the config get command.\n\nThis series consists of three commits: the first adds URL default port\nnormalization to FTP and SSH, the second adds some git-config tests for --url\nwhich seemed missing (present for --get-urlmatch), and the third uses the\nrecently added `url_parse` to support scp-style URLs in --url.\n\nUses in scripting of this change include smaller tools to automatically\nconfigure some settings based on some remote, like setting up author info based\non a user git config value scoped to ssh://service.com, which is useful\nbecause git hosters often offer an email obfuscation/forwarding feature and this\nwould enable easier management of identities with multiple accounts (like work\nand private).\n\nThis could of course be more useful with adjustments to `fmt_ident` to support\nthis, with appropriate config options (what remote, copy values to repo config\nor use globals automatically, ...). Which would have been somewhat out of scope\nfor this and requiring some more intricate changes I did not feel comfortable\nwith yet.\n\n\nFabian Pottbäcker (3):\n  urlmatch: normalize ssh and ftp default ports\n  t1300: cover --url for some --get-urlmatch tests\n  config: use url_parse for --url\n\n Documentation/git-config.adoc           |  5 +++\n builtin/config.c                        |  2 +-\n t/t1300-config.sh                       | 58 +++++++++++++++++++++++++\n t/unit-tests/u-urlmatch-normalization.c |  9 ++++\n urlmatch.c                              | 16 +++++--\n 5 files changed, 85 insertions(+), 5 deletions(-)\n\n\nbase-commit: 9a0c4701dcd5725c4184599322b52933ff5005ca\n-- \n2.50.1 (Apple Git-155)\n\n"},{"id":"548847","messageId":"20260724004011.41795-2-fpottbaecker+git@mailbox.org","threadId":"66055","inReplyTo":"20260724004011.41795-1-fpottbaecker+git@mailbox.org","subject":"[PATCH 1/3] urlmatch: normalize ssh and ftp default ports","fromName":"Fabian Pottbäcker","fromEmail":"fpottbaecker+git@mailbox.org","sentAt":"2026-07-24T00:40:09Z","receivedAt":"2026-07-24T00:40:57Z","isPatch":true,"body":"These protocols are still supported by git and have well known\ndefault ports. This leaves FTPS, which does not have one default\nport.\n\nSigned-off-by: Fabian Pottbäcker <fpottbaecker+git@mailbox.org>\n---\n t/unit-tests/u-urlmatch-normalization.c |  9 +++++++++\n urlmatch.c                              | 16 ++++++++++++----\n 2 files changed, 21 insertions(+), 4 deletions(-)\n\ndiff --git a/t/unit-tests/u-urlmatch-normalization.c b/t/unit-tests/u-urlmatch-normalization.c\nindex 3595d893a2..1808e5e51f 100644\n--- a/t/unit-tests/u-urlmatch-normalization.c\n+++ b/t/unit-tests/u-urlmatch-normalization.c\n@@ -141,9 +141,18 @@ void test_urlmatch_normalization__port_normalization(void)\n \tcheck_normalized_url(\"http://x:80\", \"http://x/\");\n \tcheck_normalized_url(\"http://x:080\", \"http://x/\");\n \tcheck_normalized_url(\"http://x:000000080\", \"http://x/\");\n+\tcheck_normalized_url(\"https://x:8443\", \"https://x:8443/\");\n \tcheck_normalized_url(\"https://x:443\", \"https://x/\");\n \tcheck_normalized_url(\"https://x:0443\", \"https://x/\");\n \tcheck_normalized_url(\"https://x:000000443\", \"https://x/\");\n+\tcheck_normalized_url(\"ftp://x:2121\", \"ftp://x:2121/\");\n+\tcheck_normalized_url(\"ftp://x:21\", \"ftp://x/\");\n+\tcheck_normalized_url(\"ftp://x:021\", \"ftp://x/\");\n+\tcheck_normalized_url(\"ftp://x:00000021\", \"ftp://x/\");\n+\tcheck_normalized_url(\"ssh://x:2222\", \"ssh://x:2222/\");\n+\tcheck_normalized_url(\"ssh://x:22\", \"ssh://x/\");\n+\tcheck_normalized_url(\"ssh://x:022\", \"ssh://x/\");\n+\tcheck_normalized_url(\"ssh://x:00000022\", \"ssh://x/\");\n }\n \n void test_urlmatch_normalization__general_escape(void)\ndiff --git a/urlmatch.c b/urlmatch.c\nindex 20bc2d009c..0c2ddf2e40 100644\n--- a/urlmatch.c\n+++ b/urlmatch.c\n@@ -274,12 +274,20 @@ static char *url_normalize_1(const char *url, struct url_info *out_info, bool al\n \t\tif (url == slash_ptr) {\n \t\t\t/* Skip \":\" port with no number, it's same as default */\n \t\t} else if (slash_ptr - url == 2 &&\n-\t\t\t   starts_with(norm.buf, \"http:\") &&\n-\t\t\t   !strncmp(url, \"80\", 2)) {\n+\t\t\t    starts_with(norm.buf, \"ftp:\") &&\n+\t\t\t    !strncmp(url, \"21\", 2)) {\n+\t\t\t/* Skip http :21 as it's the default */\n+\t\t} else if (slash_ptr - url == 2 &&\n+\t\t\t    starts_with(norm.buf, \"ssh:\") &&\n+\t\t\t    !strncmp(url, \"22\", 2)) {\n+\t\t\t/* Skip http :22 as it's the default */\n+\t\t} else if (slash_ptr - url == 2 &&\n+\t\t\t    starts_with(norm.buf, \"http:\") &&\n+\t\t\t    !strncmp(url, \"80\", 2)) {\n \t\t\t/* Skip http :80 as it's the default */\n \t\t} else if (slash_ptr - url == 3 &&\n-\t\t\t   starts_with(norm.buf, \"https:\") &&\n-\t\t\t   !strncmp(url, \"443\", 3)) {\n+\t\t\t    starts_with(norm.buf, \"https:\") &&\n+\t\t\t    !strncmp(url, \"443\", 3)) {\n \t\t\t/* Skip https :443 as it's the default */\n \t\t} else {\n \t\t\t/*\n-- \n2.50.1 (Apple Git-155)\n\n"},{"id":"548848","messageId":"20260724004011.41795-3-fpottbaecker+git@mailbox.org","threadId":"66055","inReplyTo":"20260724004011.41795-1-fpottbaecker+git@mailbox.org","subject":"[PATCH 2/3] t1300: cover --url for some --get-urlmatch tests","fromName":"Fabian Pottbäcker","fromEmail":"fpottbaecker+git@mailbox.org","sentAt":"2026-07-24T00:40:10Z","receivedAt":"2026-07-24T00:40:58Z","isPatch":true,"body":"Signed-off-by: Fabian Pottbäcker <fpottbaecker+git@mailbox.org>\n---\n t/t1300-config.sh | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex b99f782d5d..8310fe6a65 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -2028,22 +2028,32 @@ test_expect_success 'urlmatch with wildcard' '\n \n \ttest_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&\n \ttest_must_be_empty actual &&\n+\ttest_expect_code 1 git config get --bool --url=https://good.example.com doesnt.exist >actual &&\n+\ttest_must_be_empty actual &&\n \n \techo true >expect &&\n \tgit config --bool --get-urlmatch http.SSLverify https://example.com >actual &&\n \ttest_cmp expect actual &&\n+\tgit config get --bool --url=https://example.com http.SSLverify >actual &&\n+\ttest_cmp expect actual &&\n \n \techo true >expect &&\n \tgit config --bool --get-urlmatch http.SSLverify https://good-example.com >actual &&\n \ttest_cmp expect actual &&\n+\tgit config get --bool --url=https://good-example.com http.SSLverify >actual &&\n+\ttest_cmp expect actual &&\n \n \techo true >expect &&\n \tgit config --bool --get-urlmatch http.sslverify https://deep.nested.example.com >actual &&\n \ttest_cmp expect actual &&\n+\tgit config get --bool --url=https://deep.nested.example.com http.sslverify >actual &&\n+\ttest_cmp expect actual &&\n \n \techo false >expect &&\n \tgit config --bool --get-urlmatch http.sslverify https://good.example.com >actual &&\n \ttest_cmp expect actual &&\n+\tgit config get --bool --url=https://good.example.com http.sslverify >actual &&\n+\ttest_cmp expect actual &&\n \n \t{\n \t\techo http.cookiefile /tmp/cookie.txt &&\n@@ -2051,9 +2061,13 @@ test_expect_success 'urlmatch with wildcard' '\n \t} >expect &&\n \tgit config --get-urlmatch HTTP https://good.example.com >actual &&\n \ttest_cmp expect actual &&\n+\tgit config get --url=https://good.example.com HTTP >actual &&\n+\ttest_cmp expect actual &&\n \n \techo http.sslverify >expect &&\n \tgit config --get-urlmatch HTTP https://more.example.com.au >actual &&\n+\ttest_cmp expect actual &&\n+\tgit config get --url=https://more.example.com.au HTTP >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.50.1 (Apple Git-155)\n\n"},{"id":"548849","messageId":"20260724004011.41795-4-fpottbaecker+git@mailbox.org","threadId":"66055","inReplyTo":"20260724004011.41795-1-fpottbaecker+git@mailbox.org","subject":"[PATCH 3/3] config: use url_parse for --url","fromName":"Fabian Pottbäcker","fromEmail":"fpottbaecker+git@mailbox.org","sentAt":"2026-07-24T00:40:11Z","receivedAt":"2026-07-24T00:40:59Z","isPatch":true,"body":"This enables the use of the scp-style URL syntax for this option and\nthe depracated --get-urlmatch. Since URL matching was primarily used\nfor http(s) transports previously (with the potential exception of\npromisor.acceptFromServerUrl), this has little internal effect and\nmostly brings the behaviour of this URL argument in line with other\ncommands.\n\nSigned-off-by: Fabian Pottbäcker <fpottbaecker+git@mailbox.org>\n---\n Documentation/git-config.adoc |  5 ++++\n builtin/config.c              |  2 +-\n t/t1300-config.sh             | 44 +++++++++++++++++++++++++++++++++++\n 3 files changed, 50 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc\nindex 57af010ade..a38d68512e 100644\n--- a/Documentation/git-config.adoc\n+++ b/Documentation/git-config.adoc\n@@ -143,6 +143,11 @@ permitted).\n \t<section>.<key> is used as a fallback).  When given just the\n \t<section> as name, do so for all the keys in the section and\n \tlist them.  Returns error code 1 if no value is found.\n++\n+This option supports all URL formats (see linkgit:git-fetch[1]),\n+unlike the config file URL syntax explained in the `http.<url>.*`\n+options, which requires an explicitly specified scheme and does\n+not support the scp-style URL syntax.\n \n --global::\n \tFor writing options: write to global `~/.gitconfig` file\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 8d8ec0beea..b5488a9497 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -870,7 +870,7 @@ static int get_urlmatch(const struct config_location_options *opts,\n \tconfig.cascade_fn = NULL;\n \tconfig.cb = &values;\n \n-\tif (!url_normalize(url, &config.url))\n+\tif (!url_parse(url, &config.url))\n \t\tdie(\"%s\", config.url.err);\n \n \tconfig.section = section = xstrdup_tolower(var);\ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex 8310fe6a65..477e030cf3 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -1907,6 +1907,50 @@ test_expect_success 'urlmatch' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'urlmatch with scp syntax' '\n+\tcat >.git/config <<-\\EOF &&\n+\t[section]\n+\t\tflag = false\n+\t[section \"ssh://example.com\"]\n+\t\tflag = true\n+\t\tkey = value\n+\tEOF\n+\n+\techo \"fatal: invalid URL scheme name or missing '\"'\"'://'\"'\"' suffix\" >expect_err &&\n+\ttest_expect_code 128 git config --bool --get-urlmatch section.flag example.com >actual 2>error &&\n+\ttest_cmp expect_err error &&\n+\ttest_must_be_empty actual &&\n+\ttest_expect_code 128 git config get --url=example.com --bool section.flag >actual 2>error &&\n+\ttest_cmp expect_err error &&\n+\ttest_must_be_empty actual &&\n+\n+\ttest_expect_code 1 git config --bool --get-urlmatch doesnt.exist example.com: >actual &&\n+\ttest_must_be_empty actual &&\n+\ttest_expect_code 1 git config get --url=example.com: --bool doesnt.exist >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\techo true >expect &&\n+\tgit config --bool --get-urlmatch section.flag git@example.com:path >actual &&\n+\ttest_cmp expect actual &&\n+\tgit config get --bool --url=git@example.com:path section.flag >actual &&\n+\ttest_cmp expect actual &&\n+\n+\techo false >expect &&\n+\tgit config --bool --get-urlmatch section.flag https://example.com >actual &&\n+\ttest_cmp expect actual &&\n+\tgit config get --bool --url=https://example.com section.flag >actual &&\n+\ttest_cmp expect actual &&\n+\n+\t{\n+\t\techo section.flag true &&\n+\t\techo section.key value\n+\t} >expect &&\n+\tgit config --get-urlmatch section git@example.com:path >actual &&\n+\ttest_cmp expect actual &&\n+\tgit config get --url=git@example.com:path section >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'urlmatch with --show-scope' '\n \tcat >.git/config <<-\\EOF &&\n \t[http \"https://weak.example.com\"]\n-- \n2.50.1 (Apple Git-155)\n\n"},{"id":"548890","messageId":"DE62DFBE-ED4C-4EF5-8F4D-FAAB3519F86A@gmail.com","threadId":"66055","inReplyTo":"20260724004011.41795-2-fpottbaecker+git@mailbox.org","subject":"Re: [PATCH 1/3] urlmatch: normalize ssh and ftp default ports","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-07-24T11:35:57Z","receivedAt":"2026-07-24T11:36:10Z","isPatch":true,"body":"\n> Le 24 juil. 2026 à 09:41, Fabian Pottbäcker <fpottbaecker+git@mailbox.org> a écrit :\n> \n> ﻿These protocols are still supported by git and have well known\n> default ports. This leaves FTPS, which does not have one default\n> port.\n> \n> Signed-off-by: Fabian Pottbäcker <fpottbaecker+git@mailbox.org>\n> ---\n> t/unit-tests/u-urlmatch-normalization.c |  9 +++++++++\n> urlmatch.c                              | 16 ++++++++++++----\n> 2 files changed, 21 insertions(+), 4 deletions(-)\n> \n\n[snip]\n\n> diff --git a/urlmatch.c b/urlmatch.c\n> index 20bc2d009c..0c2ddf2e40 100644\n> --- a/urlmatch.c\n> +++ b/urlmatch.c\n> @@ -274,12 +274,20 @@ static char *url_normalize_1(const char *url, struct url_info *out_info, bool al\n>        if (url == slash_ptr) {\n>            /* Skip \":\" port with no number, it's same as default */\n>        } else if (slash_ptr - url == 2 &&\n> -               starts_with(norm.buf, \"http:\") &&\n> -               !strncmp(url, \"80\", 2)) {\n> +                starts_with(norm.buf, \"ftp:\") &&\n> +                !strncmp(url, \"21\", 2)) {\n> +            /* Skip http :21 as it's the default */\n> +        } else if (slash_ptr - url == 2 &&\n> +                starts_with(norm.buf, \"ssh:\") &&\n> +                !strncmp(url, \"22\", 2)) {\n> +            /* Skip http :22 as it's the default */\n> +        } else if (slash_ptr - url == 2 &&\n> +                starts_with(norm.buf, \"http:\") &&\n> +                !strncmp(url, \"80\", 2)) {\n>            /* Skip http :80 as it's the default */\n>        } else if (slash_ptr - url == 3 &&\n> -               starts_with(norm.buf, \"https:\") &&\n> -               !strncmp(url, \"443\", 3)) {\n> +                starts_with(norm.buf, \"https:\") &&\n> +                !strncmp(url, \"443\", 3)) {\n>            /* Skip https :443 as it's the default */\n>        } else {\n>            /*\n> --\n> 2.50.1 (Apple Git-155)\n\nJust offhand, not commenting on the rest of the series : the comments look wrong for ftp, ssh to me. "},{"id":"549120","messageId":"32323233-CE82-421A-94D2-5902C4382F8D@mailbox.org","threadId":"66055","inReplyTo":"DE62DFBE-ED4C-4EF5-8F4D-FAAB3519F86A@gmail.com","subject":"Re: [PATCH 1/3] urlmatch: normalize ssh and ftp default ports","fromName":"Fabian Pottbäcker","fromEmail":"fpottbaecker+git@mailbox.org","sentAt":"2026-07-28T10:51:18Z","receivedAt":"2026-07-28T10:51:39Z","isPatch":true,"body":"> On 24. Jul 2026, at 13:35, Ben Knoble <ben.knoble@gmail.com> wrote:\n> \n> Just offhand, not commenting on the rest of the series : the comments look wrong for ftp, ssh to me.\n\nRight, I messed up the continuation indent here (I wanted to make it in line with the rest of the file).\nLined up for a v2, but it is minor so I would await further comments for now (see https://gitlab.com/fpottbaecker/git/-/compare/config_url_scp_syntax_v1..config_url_scp_syntax).\n\nAlso, since I noticed this as a convenience thing gitgitgadget does:\n\nFetch-It-Via: git fetch https://gitlab.com/fpottbaecker/git.git config_url_scp_syntax_v1"}]}