t9904: add tests for the new url-parse builtin

Test git URL parsing, validation and component extraction on all documented git URL schemes and syntaxes. Add IPv6 host coverage in URL form: ssh://[::1]/path ssh://user@[::1]:1234/path git://[::1]:9418/path http://[2001:db8::1]/path https://[2001:db8::1]/path In URL form the brackets are kept in the host component (RFC 3986 syntax for IPv6 literals). Also exercise the bracketed scp short forms that t5601-clone.sh covers via parse_connect_url: [host]:path [host:port]:path [::1]:repo user@[::1]:repo user@[host:port]:path In scp form, brackets are kept for IPv6 literals (two or more inner colons) and stripped for plain hostnames or host:port pairs. Suggested-by: Torsten Bögershausen <tboegi@web.de> Signed-off-by: Matheus Afonso Martins Moreira <matheus@matheusmoreira.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Matheus Afonso Martins Moreira committed May 2, 2026 at 05:28 UTC 0e2149cff1c37c5f9602d515d1d39d9701d15e24
2 files changed +320
t/meson.build
+1
@@ -1114,6 +1114,7 @@ integration_tests = [
1114 't9901-git-web--browse.sh',
1115 't9902-completion.sh',
1116 't9903-bash-prompt.sh',
1117 + 't9904-url-parse.sh',
1118 ]
1119
1120 benchmarks = [
t/t9904-url-parse.sh new
+319
@@ -0,0 +1,319 @@
1 +#!/bin/sh
2 +#
3 +# Copyright (c) 2024 Matheus Afonso Martins Moreira
4 +#
5 +
6 +test_description='git url-parse tests'
7 +
8 +. ./test-lib.sh
9 +
10 +test_expect_success 'git url-parse -- ssh syntax' '
11 + git url-parse "ssh://user@example.com:1234/repository/path" &&
12 + git url-parse "ssh://user@example.com/repository/path" &&
13 + git url-parse "ssh://example.com:1234/repository/path" &&
14 + git url-parse "ssh://example.com/repository/path"
15 +'
16 +
17 +test_expect_success 'git url-parse -- git syntax' '
18 + git url-parse "git://example.com:1234/repository/path" &&
19 + git url-parse "git://example.com/repository/path"
20 +'
21 +
22 +test_expect_success 'git url-parse -- http syntax' '
23 + git url-parse "https://example.com:1234/repository/path" &&
24 + git url-parse "https://example.com/repository/path" &&
25 + git url-parse "http://example.com:1234/repository/path" &&
26 + git url-parse "http://example.com/repository/path"
27 +'
28 +
29 +test_expect_success 'git url-parse -- scp syntax' '
30 + git url-parse "user@example.com:/repository/path" &&
31 + git url-parse "example.com:/repository/path"
32 +'
33 +
34 +test_expect_success 'git url-parse -- username expansion - ssh syntax' '
35 + git url-parse "ssh://user@example.com:1234/~user/repository" &&
36 + git url-parse "ssh://user@example.com/~user/repository" &&
37 + git url-parse "ssh://example.com:1234/~user/repository" &&
38 + git url-parse "ssh://example.com/~user/repository"
39 +'
40 +
41 +test_expect_success 'git url-parse -- username expansion - git syntax' '
42 + git url-parse "git://example.com:1234/~user/repository" &&
43 + git url-parse "git://example.com/~user/repository"
44 +'
45 +
46 +test_expect_success 'git url-parse -- username expansion - scp syntax' '
47 + git url-parse "user@example.com:~user/repository" &&
48 + git url-parse "example.com:~user/repository"
49 +'
50 +
51 +test_expect_success 'git url-parse -- file urls' '
52 + git url-parse "file:///repository/path" &&
53 + git url-parse "file://"
54 +'
55 +
56 +test_expect_success 'git url-parse -c scheme -- ssh syntax' '
57 + test ssh = "$(git url-parse -c scheme "ssh://user@example.com:1234/repository/path")" &&
58 + test ssh = "$(git url-parse -c scheme "ssh://user@example.com/repository/path")" &&
59 + test ssh = "$(git url-parse -c scheme "ssh://example.com:1234/repository/path")" &&
60 + test ssh = "$(git url-parse -c scheme "ssh://example.com/repository/path")"
61 +'
62 +
63 +test_expect_success 'git url-parse -c scheme -- git syntax' '
64 + test git = "$(git url-parse -c scheme "git://example.com:1234/repository/path")" &&
65 + test git = "$(git url-parse -c scheme "git://example.com/repository/path")"
66 +'
67 +
68 +test_expect_success 'git url-parse -c scheme -- http syntax' '
69 + test https = "$(git url-parse -c scheme "https://example.com:1234/repository/path")" &&
70 + test https = "$(git url-parse -c scheme "https://example.com/repository/path")" &&
71 + test http = "$(git url-parse -c scheme "http://example.com:1234/repository/path")" &&
72 + test http = "$(git url-parse -c scheme "http://example.com/repository/path")"
73 +'
74 +
75 +test_expect_success 'git url-parse -c scheme -- scp syntax' '
76 + test ssh = "$(git url-parse -c scheme "user@example.com:/repository/path")" &&
77 + test ssh = "$(git url-parse -c scheme "example.com:/repository/path")"
78 +'
79 +
80 +test_expect_success 'git url-parse -c user -- ssh syntax' '
81 + test user = "$(git url-parse -c user "ssh://user@example.com:1234/repository/path")" &&
82 + test user = "$(git url-parse -c user "ssh://user@example.com/repository/path")" &&
83 + test "" = "$(git url-parse -c user "ssh://example.com:1234/repository/path")" &&
84 + test "" = "$(git url-parse -c user "ssh://example.com/repository/path")"
85 +'
86 +
87 +test_expect_success 'git url-parse -c user -- git syntax' '
88 + test "" = "$(git url-parse -c user "git://example.com:1234/repository/path")" &&
89 + test "" = "$(git url-parse -c user "git://example.com/repository/path")"
90 +'
91 +
92 +test_expect_success 'git url-parse -c user -- http syntax' '
93 + test "" = "$(git url-parse -c user "https://example.com:1234/repository/path")" &&
94 + test "" = "$(git url-parse -c user "https://example.com/repository/path")" &&
95 + test "" = "$(git url-parse -c user "http://example.com:1234/repository/path")" &&
96 + test "" = "$(git url-parse -c user "http://example.com/repository/path")"
97 +'
98 +
99 +test_expect_success 'git url-parse -c user -- scp syntax' '
100 + test user = "$(git url-parse -c user "user@example.com:/repository/path")" &&
101 + test "" = "$(git url-parse -c user "example.com:/repository/path")"
102 +'
103 +
104 +test_expect_success 'git url-parse -c password -- http syntax' '
105 + test secret = "$(git url-parse -c password "https://user:secret@example.com:1234/repository/path")" &&
106 + test secret = "$(git url-parse -c password "http://user:secret@example.com/repository/path")" &&
107 + test "" = "$(git url-parse -c password "https://user@example.com/repository/path")" &&
108 + test "" = "$(git url-parse -c password "https://example.com/repository/path")"
109 +'
110 +
111 +test_expect_success 'git url-parse -c host -- ssh syntax' '
112 + test example.com = "$(git url-parse -c host "ssh://user@example.com:1234/repository/path")" &&
113 + test example.com = "$(git url-parse -c host "ssh://user@example.com/repository/path")" &&
114 + test example.com = "$(git url-parse -c host "ssh://example.com:1234/repository/path")" &&
115 + test example.com = "$(git url-parse -c host "ssh://example.com/repository/path")"
116 +'
117 +
118 +test_expect_success 'git url-parse -c host -- git syntax' '
119 + test example.com = "$(git url-parse -c host "git://example.com:1234/repository/path")" &&
120 + test example.com = "$(git url-parse -c host "git://example.com/repository/path")"
121 +'
122 +
123 +test_expect_success 'git url-parse -c host -- http syntax' '
124 + test example.com = "$(git url-parse -c host "https://example.com:1234/repository/path")" &&
125 + test example.com = "$(git url-parse -c host "https://example.com/repository/path")" &&
126 + test example.com = "$(git url-parse -c host "http://example.com:1234/repository/path")" &&
127 + test example.com = "$(git url-parse -c host "http://example.com/repository/path")"
128 +'
129 +
130 +test_expect_success 'git url-parse -c host -- scp syntax' '
131 + test example.com = "$(git url-parse -c host "user@example.com:/repository/path")" &&
132 + test example.com = "$(git url-parse -c host "example.com:/repository/path")"
133 +'
134 +
135 +test_expect_success 'git url-parse -c port -- ssh syntax' '
136 + test 1234 = "$(git url-parse -c port "ssh://user@example.com:1234/repository/path")" &&
137 + test "" = "$(git url-parse -c port "ssh://user@example.com/repository/path")" &&
138 + test 1234 = "$(git url-parse -c port "ssh://example.com:1234/repository/path")" &&
139 + test "" = "$(git url-parse -c port "ssh://example.com/repository/path")"
140 +'
141 +
142 +test_expect_success 'git url-parse -c port -- git syntax' '
143 + test 1234 = "$(git url-parse -c port "git://example.com:1234/repository/path")" &&
144 + test "" = "$(git url-parse -c port "git://example.com/repository/path")"
145 +'
146 +
147 +test_expect_success 'git url-parse -c port -- http syntax' '
148 + test 1234 = "$(git url-parse -c port "https://example.com:1234/repository/path")" &&
149 + test "" = "$(git url-parse -c port "https://example.com/repository/path")" &&
150 + test 1234 = "$(git url-parse -c port "http://example.com:1234/repository/path")" &&
151 + test "" = "$(git url-parse -c port "http://example.com/repository/path")"
152 +'
153 +
154 +test_expect_success 'git url-parse -c port -- scp syntax' '
155 + test "" = "$(git url-parse -c port "user@example.com:/repository/path")" &&
156 + test "" = "$(git url-parse -c port "example.com:/repository/path")"
157 +'
158 +
159 +test_expect_success 'git url-parse -c path -- ssh syntax' '
160 + test "/repository/path" = "$(git url-parse -c path "ssh://user@example.com:1234/repository/path")" &&
161 + test "/repository/path" = "$(git url-parse -c path "ssh://user@example.com/repository/path")" &&
162 + test "/repository/path" = "$(git url-parse -c path "ssh://example.com:1234/repository/path")" &&
163 + test "/repository/path" = "$(git url-parse -c path "ssh://example.com/repository/path")"
164 +'
165 +
166 +test_expect_success 'git url-parse -c path -- git syntax' '
167 + test "/repository/path" = "$(git url-parse -c path "git://example.com:1234/repository/path")" &&
168 + test "/repository/path" = "$(git url-parse -c path "git://example.com/repository/path")"
169 +'
170 +
171 +test_expect_success 'git url-parse -c path -- http syntax' '
172 + test "/repository/path" = "$(git url-parse -c path "https://example.com:1234/repository/path")" &&
173 + test "/repository/path" = "$(git url-parse -c path "https://example.com/repository/path")" &&
174 + test "/repository/path" = "$(git url-parse -c path "http://example.com:1234/repository/path")" &&
175 + test "/repository/path" = "$(git url-parse -c path "http://example.com/repository/path")"
176 +'
177 +
178 +test_expect_success 'git url-parse -c path -- scp syntax' '
179 + test "/repository/path" = "$(git url-parse -c path "user@example.com:/repository/path")" &&
180 + test "/repository/path" = "$(git url-parse -c path "example.com:/repository/path")"
181 +'
182 +
183 +test_expect_success 'git url-parse -c path -- username expansion - ssh syntax' '
184 + test "~user/repository" = "$(git url-parse -c path "ssh://user@example.com:1234/~user/repository")" &&
185 + test "~user/repository" = "$(git url-parse -c path "ssh://user@example.com/~user/repository")" &&
186 + test "~user/repository" = "$(git url-parse -c path "ssh://example.com:1234/~user/repository")" &&
187 + test "~user/repository" = "$(git url-parse -c path "ssh://example.com/~user/repository")"
188 +'
189 +
190 +test_expect_success 'git url-parse -c path -- username expansion - git syntax' '
191 + test "~user/repository" = "$(git url-parse -c path "git://example.com:1234/~user/repository")" &&
192 + test "~user/repository" = "$(git url-parse -c path "git://example.com/~user/repository")"
193 +'
194 +
195 +test_expect_success 'git url-parse -c path -- username expansion - scp syntax' '
196 + test "~user/repository" = "$(git url-parse -c path "user@example.com:~user/repository")" &&
197 + test "~user/repository" = "$(git url-parse -c path "example.com:~user/repository")"
198 +'
199 +
200 +test_expect_success 'git url-parse -c path -- username expansion strips query and fragment' '
201 + test "~user/repository" = "$(git url-parse -c path "ssh://example.com/~user/repository?query")" &&
202 + test "~user/repository" = "$(git url-parse -c path "ssh://example.com/~user/repository#fragment")" &&
203 + test "~user/repository" = "$(git url-parse -c path "git://example.com/~user/repository?query")" &&
204 + test "~user/repository" = "$(git url-parse -c path "user@example.com:~user/repository?query")"
205 +'
206 +
207 +test_expect_success 'git url-parse -- ssh syntax with IPv6' '
208 + git url-parse "ssh://user@[::1]:1234/repository/path" &&
209 + git url-parse "ssh://user@[::1]/repository/path" &&
210 + git url-parse "ssh://[::1]:1234/repository/path" &&
211 + git url-parse "ssh://[::1]/repository/path" &&
212 + git url-parse "ssh://[2001:db8::1]/repository/path"
213 +'
214 +
215 +test_expect_success 'git url-parse -- git syntax with IPv6' '
216 + git url-parse "git://[::1]:9418/repository/path" &&
217 + git url-parse "git://[::1]/repository/path"
218 +'
219 +
220 +test_expect_success 'git url-parse -- http syntax with IPv6' '
221 + git url-parse "https://[::1]:1234/repository/path" &&
222 + git url-parse "https://[::1]/repository/path" &&
223 + git url-parse "http://[2001:db8::1]/repository/path"
224 +'
225 +
226 +test_expect_success 'git url-parse -c host -- IPv6 in URL form' '
227 + test "[::1]" = "$(git url-parse -c host "ssh://user@[::1]:1234/repository/path")" &&
228 + test "[::1]" = "$(git url-parse -c host "ssh://[::1]/repository/path")" &&
229 + test "[2001:db8::1]" = "$(git url-parse -c host "ssh://[2001:db8::1]/repository/path")" &&
230 + test "[::1]" = "$(git url-parse -c host "git://[::1]/repository/path")" &&
231 + test "[2001:db8::1]" = "$(git url-parse -c host "https://[2001:db8::1]/repository/path")"
232 +'
233 +
234 +test_expect_success 'git url-parse -c port -- IPv6 in URL form' '
235 + test 1234 = "$(git url-parse -c port "ssh://user@[::1]:1234/repository/path")" &&
236 + test "" = "$(git url-parse -c port "ssh://[::1]/repository/path")" &&
237 + test 9418 = "$(git url-parse -c port "git://[::1]:9418/repository/path")"
238 +'
239 +
240 +test_expect_success 'git url-parse -- scp syntax with IPv6' '
241 + git url-parse "[::1]:repository/path" &&
242 + git url-parse "user@[::1]:repository/path" &&
243 + git url-parse "[2001:db8::1]:repo"
244 +'
245 +
246 +test_expect_success 'git url-parse -- scp syntax with bracketed hostname' '
247 + git url-parse "[myhost]:src" &&
248 + git url-parse "user@[myhost]:src"
249 +'
250 +
251 +test_expect_success 'git url-parse -- scp syntax with bracketed host:port' '
252 + git url-parse "[myhost:123]:src" &&
253 + git url-parse "user@[myhost:123]:src"
254 +'
255 +
256 +test_expect_success 'git url-parse -c host -- scp+IPv6' '
257 + test "[::1]" = "$(git url-parse -c host "[::1]:repository/path")" &&
258 + test "[::1]" = "$(git url-parse -c host "user@[::1]:repository/path")" &&
259 + test "[2001:db8::1]" = "$(git url-parse -c host "[2001:db8::1]:repo")"
260 +'
261 +
262 +test_expect_success 'git url-parse -c path -- scp+IPv6' '
263 + test "/repository/path" = "$(git url-parse -c path "[::1]:/repository/path")" &&
264 + test "/repository/path" = "$(git url-parse -c path "[::1]:repository/path")" &&
265 + test "/repo" = "$(git url-parse -c path "[2001:db8::1]:repo")"
266 +'
267 +
268 +test_expect_success 'git url-parse -c host,port,path -- scp [host:port]:src' '
269 + test myhost = "$(git url-parse -c host "[myhost:123]:src")" &&
270 + test 123 = "$(git url-parse -c port "[myhost:123]:src")" &&
271 + test "/src" = "$(git url-parse -c path "[myhost:123]:src")"
272 +'
273 +
274 +test_expect_success 'git url-parse -c host,path -- scp [host]:src' '
275 + test myhost = "$(git url-parse -c host "[myhost]:src")" &&
276 + test "/src" = "$(git url-parse -c path "[myhost]:src")"
277 +'
278 +
279 +test_expect_success 'git url-parse -c user -- scp with user@ and brackets' '
280 + test user = "$(git url-parse -c user "user@[::1]:repo")" &&
281 + test user = "$(git url-parse -c user "user@[myhost:123]:src")" &&
282 + test user = "$(git url-parse -c user "user@[myhost]:src")"
283 +'
284 +
285 +test_expect_success 'git url-parse -- scp+IPv6 with username expansion' '
286 + test "~user/repo" = "$(git url-parse -c path "[::1]:~user/repo")" &&
287 + test "~user/repo" = "$(git url-parse -c path "user@[::1]:~user/repo")"
288 +'
289 +
290 +test_expect_success 'git url-parse fails on invalid URL' '
291 + test_must_fail git url-parse "not a url"
292 +'
293 +
294 +test_expect_success 'git url-parse helpful error for absolute local path' '
295 + test_must_fail git url-parse "/abs/path" 2>err &&
296 + test_grep "is not a URL" err &&
297 + test_grep "file:///" err
298 +'
299 +
300 +test_expect_success 'git url-parse helpful error for relative local path' '
301 + test_must_fail git url-parse "./rel" 2>err &&
302 + test_grep "is not a URL" err &&
303 + test_grep "absolute path" err
304 +'
305 +
306 +test_expect_success 'git url-parse fails on unknown -c component name' '
307 + test_must_fail git url-parse -c bogus "https://example.com/repo"
308 +'
309 +
310 +test_expect_success 'git url-parse fails on URL missing host' '
311 + test_must_fail git url-parse "https://"
312 +'
313 +
314 +test_expect_success 'git url-parse with no URL prints usage' '
315 + test_must_fail git url-parse 2>err &&
316 + test_grep "usage:" err
317 +'
318 +
319 +test_done