1#!/bin/sh
2
3test_description='test git wire-protocol version 2'
4
5TEST_NO_CREATE_REPO=1
6
7GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
8export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
9
10. ./test-lib.sh
11
12# Test protocol v2 with 'git://' transport
13#
14. "$TEST_DIRECTORY"/lib-git-daemon.sh
15start_git_daemon --export-all --enable=receive-pack
16daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent
17
18test_expect_success 'create repo to be served by git-daemon' '
19	git init "$daemon_parent" &&
20	test_commit -C "$daemon_parent" one
21'
22
23test_expect_success 'list refs with git:// using protocol v2' '
24	test_when_finished "rm -f log" &&
25
26	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
27		ls-remote --symref "$GIT_DAEMON_URL/parent" >actual &&
28
29	# Client requested to use protocol v2
30	grep "ls-remote> .*\\\0\\\0version=2\\\0$" log &&
31	# Server responded using protocol v2
32	grep "ls-remote< version 2" log &&
33
34	git ls-remote --symref "$GIT_DAEMON_URL/parent" >expect &&
35	test_cmp expect actual
36'
37
38test_expect_success 'ref advertisement is filtered with ls-remote using protocol v2' '
39	test_when_finished "rm -f log" &&
40
41	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
42		ls-remote "$GIT_DAEMON_URL/parent" main >actual &&
43
44	cat >expect <<-EOF &&
45	$(git -C "$daemon_parent" rev-parse refs/heads/main)$(printf "\t")refs/heads/main
46	EOF
47
48	test_cmp expect actual
49'
50
51test_expect_success 'clone with git:// using protocol v2' '
52	test_when_finished "rm -f log" &&
53
54	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
55		clone "$GIT_DAEMON_URL/parent" daemon_child &&
56
57	git -C daemon_child log -1 --format=%s >actual &&
58	git -C "$daemon_parent" log -1 --format=%s >expect &&
59	test_cmp expect actual &&
60
61	# Client requested to use protocol v2
62	grep "clone> .*\\\0\\\0version=2\\\0$" log &&
63	# Server responded using protocol v2
64	grep "clone< version 2" log
65'
66
67test_expect_success 'fetch with git:// using protocol v2' '
68	test_when_finished "rm -f log" &&
69
70	test_commit -C "$daemon_parent" two &&
71
72	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
73		fetch &&
74
75	git -C daemon_child log -1 --format=%s origin/main >actual &&
76	git -C "$daemon_parent" log -1 --format=%s >expect &&
77	test_cmp expect actual &&
78
79	# Client requested to use protocol v2
80	grep "fetch> .*\\\0\\\0version=2\\\0$" log &&
81	# Server responded using protocol v2
82	grep "fetch< version 2" log
83'
84
85test_expect_success 'fetch by hash without tag following with protocol v2 does not list refs' '
86	test_when_finished "rm -f log" &&
87
88	test_commit -C "$daemon_parent" two_a &&
89	git -C "$daemon_parent" rev-parse two_a >two_a_hash &&
90
91	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
92		fetch --no-tags origin $(cat two_a_hash) &&
93
94	grep "fetch< version 2" log &&
95	! grep "fetch> command=ls-refs" log
96'
97
98test_expect_success 'pull with git:// using protocol v2' '
99	test_when_finished "rm -f log" &&
100
101	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
102		pull &&
103
104	git -C daemon_child log -1 --format=%s >actual &&
105	git -C "$daemon_parent" log -1 --format=%s >expect &&
106	test_cmp expect actual &&
107
108	# Client requested to use protocol v2
109	grep "fetch> .*\\\0\\\0version=2\\\0$" log &&
110	# Server responded using protocol v2
111	grep "fetch< version 2" log
112'
113
114test_expect_success 'push with git:// and a config of v2 does not request v2' '
115	test_when_finished "rm -f log" &&
116
117	# Till v2 for push is designed, make sure that if a client has
118	# protocol.version configured to use v2, that the client instead falls
119	# back and uses v0.
120
121	test_commit -C daemon_child three &&
122
123	# Push to another branch, as the target repository has the
124	# main branch checked out and we cannot push into it.
125	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
126		push origin HEAD:client_branch &&
127
128	git -C daemon_child log -1 --format=%s >actual &&
129	git -C "$daemon_parent" log -1 --format=%s client_branch >expect &&
130	test_cmp expect actual &&
131
132	# Client requested to use protocol v2
133	! grep "push> .*\\\0\\\0version=2\\\0$" log &&
134	# Server responded using protocol v2
135	! grep "push< version 2" log
136'
137
138stop_git_daemon
139
140# Test protocol v2 with 'file://' transport
141#
142test_expect_success 'create repo to be served by file:// transport' '
143	git init file_parent &&
144	test_commit -C file_parent one
145'
146
147test_expect_success 'list refs with file:// using protocol v2' '
148	test_when_finished "rm -f log" &&
149
150	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
151		ls-remote --symref "file://$(pwd)/file_parent" >actual &&
152
153	# Server responded using protocol v2
154	grep "ls-remote< version 2" log &&
155
156	git ls-remote --symref "file://$(pwd)/file_parent" >expect &&
157	test_cmp expect actual
158'
159
160test_expect_success 'ref advertisement is filtered with ls-remote using protocol v2' '
161	test_when_finished "rm -f log" &&
162
163	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
164		ls-remote "file://$(pwd)/file_parent" main >actual &&
165
166	cat >expect <<-EOF &&
167	$(git -C file_parent rev-parse refs/heads/main)$(printf "\t")refs/heads/main
168	EOF
169
170	test_cmp expect actual
171'
172
173test_expect_success 'server-options are sent when using ls-remote' '
174	test_when_finished "rm -f log" &&
175
176	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
177		ls-remote -o hello -o world "file://$(pwd)/file_parent" main >actual &&
178
179	cat >expect <<-EOF &&
180	$(git -C file_parent rev-parse refs/heads/main)$(printf "\t")refs/heads/main
181	EOF
182
183	test_cmp expect actual &&
184	grep "server-option=hello" log &&
185	grep "server-option=world" log
186'
187
188test_expect_success 'warn if using server-option with ls-remote with legacy protocol' '
189	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
190		ls-remote -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
191
192	test_i18ngrep "see protocol.version in" err &&
193	test_i18ngrep "server options require protocol version 2 or later" err
194'
195
196test_expect_success 'clone with file:// using protocol v2' '
197	test_when_finished "rm -f log" &&
198
199	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
200		clone "file://$(pwd)/file_parent" file_child &&
201
202	git -C file_child log -1 --format=%s >actual &&
203	git -C file_parent log -1 --format=%s >expect &&
204	test_cmp expect actual &&
205
206	# Server responded using protocol v2
207	grep "clone< version 2" log &&
208
209	# Client sent ref-prefixes to filter the ref-advertisement
210	grep "ref-prefix HEAD" log &&
211	grep "ref-prefix refs/heads/" log &&
212	grep "ref-prefix refs/tags/" log
213'
214
215test_expect_success 'clone of empty repo propagates name of default branch' '
216	test_when_finished "rm -rf file_empty_parent file_empty_child" &&
217
218	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
219	git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
220
221	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
222	git -c init.defaultBranch=main -c protocol.version=2 \
223		clone "file://$(pwd)/file_empty_parent" file_empty_child &&
224	grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
225'
226
227test_expect_success '...but not if explicitly forbidden by config' '
228	test_when_finished "rm -rf file_empty_parent file_empty_child" &&
229
230	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
231	git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
232	test_config -C file_empty_parent lsrefs.unborn ignore &&
233
234	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
235	git -c init.defaultBranch=main -c protocol.version=2 \
236		clone "file://$(pwd)/file_empty_parent" file_empty_child &&
237	! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
238'
239
240test_expect_success 'bare clone propagates empty default branch' '
241	test_when_finished "rm -rf file_empty_parent file_empty_child.git" &&
242
243	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
244	git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
245
246	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
247	git -c init.defaultBranch=main -c protocol.version=2 \
248		clone --bare \
249		"file://$(pwd)/file_empty_parent" file_empty_child.git &&
250	grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD
251'
252
253test_expect_success 'fetch with file:// using protocol v2' '
254	test_when_finished "rm -f log" &&
255
256	test_commit -C file_parent two &&
257
258	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
259		fetch origin &&
260
261	git -C file_child log -1 --format=%s origin/main >actual &&
262	git -C file_parent log -1 --format=%s >expect &&
263	test_cmp expect actual &&
264
265	# Server responded using protocol v2
266	grep "fetch< version 2" log
267'
268
269test_expect_success 'ref advertisement is filtered during fetch using protocol v2' '
270	test_when_finished "rm -f log" &&
271
272	test_commit -C file_parent three &&
273	git -C file_parent branch unwanted-branch three &&
274
275	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
276		fetch origin main &&
277
278	git -C file_child log -1 --format=%s origin/main >actual &&
279	git -C file_parent log -1 --format=%s >expect &&
280	test_cmp expect actual &&
281
282	grep "refs/heads/main" log &&
283	! grep "refs/heads/unwanted-branch" log
284'
285
286test_expect_success 'server-options are sent when fetching' '
287	test_when_finished "rm -f log" &&
288
289	test_commit -C file_parent four &&
290
291	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
292		fetch -o hello -o world origin main &&
293
294	git -C file_child log -1 --format=%s origin/main >actual &&
295	git -C file_parent log -1 --format=%s >expect &&
296	test_cmp expect actual &&
297
298	grep "server-option=hello" log &&
299	grep "server-option=world" log
300'
301
302test_expect_success 'warn if using server-option with fetch with legacy protocol' '
303	test_when_finished "rm -rf temp_child" &&
304
305	git init temp_child &&
306
307	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C temp_child -c protocol.version=0 \
308		fetch -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
309
310	test_i18ngrep "see protocol.version in" err &&
311	test_i18ngrep "server options require protocol version 2 or later" err
312'
313
314test_expect_success 'server-options are sent when cloning' '
315	test_when_finished "rm -rf log myclone" &&
316
317	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
318		clone --server-option=hello --server-option=world \
319		"file://$(pwd)/file_parent" myclone &&
320
321	grep "server-option=hello" log &&
322	grep "server-option=world" log
323'
324
325test_expect_success 'warn if using server-option with clone with legacy protocol' '
326	test_when_finished "rm -rf myclone" &&
327
328	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
329		clone --server-option=hello --server-option=world \
330		"file://$(pwd)/file_parent" myclone 2>err &&
331
332	test_i18ngrep "see protocol.version in" err &&
333	test_i18ngrep "server options require protocol version 2 or later" err
334'
335
336test_expect_success 'upload-pack respects config using protocol v2' '
337	git init server &&
338	write_script server/.git/hook <<-\EOF &&
339		touch hookout
340		"$@"
341	EOF
342	test_commit -C server one &&
343
344	test_config_global uploadpack.packobjectshook ./hook &&
345	test_path_is_missing server/.git/hookout &&
346	git -c protocol.version=2 clone "file://$(pwd)/server" client &&
347	test_path_is_file server/.git/hookout
348'
349
350test_expect_success 'setup filter tests' '
351	rm -rf server client &&
352	git init server &&
353
354	# 1 commit to create a file, and 1 commit to modify it
355	test_commit -C server message1 a.txt &&
356	test_commit -C server message2 a.txt &&
357	git -C server config protocol.version 2 &&
358	git -C server config uploadpack.allowfilter 1 &&
359	git -C server config uploadpack.allowanysha1inwant 1 &&
360	git -C server config protocol.version 2
361'
362
363test_expect_success 'partial clone' '
364	GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \
365		clone --filter=blob:none "file://$(pwd)/server" client &&
366	grep "version 2" trace &&
367
368	# Ensure that the old version of the file is missing
369	git -C client rev-list --quiet --objects --missing=print main \
370		>observed.oids &&
371	grep "$(git -C server rev-parse message1:a.txt)" observed.oids &&
372
373	# Ensure that client passes fsck
374	git -C client fsck
375'
376
377test_expect_success 'dynamically fetch missing object' '
378	rm "$(pwd)/trace" &&
379	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
380		cat-file -p $(git -C server rev-parse message1:a.txt) &&
381	grep "version 2" trace
382'
383
384test_expect_success 'when dynamically fetching missing object, do not list refs' '
385	! grep "git> command=ls-refs" trace
386'
387
388test_expect_success 'partial fetch' '
389	rm -rf client "$(pwd)/trace" &&
390	git init client &&
391	SERVER="file://$(pwd)/server" &&
392
393	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
394		fetch --filter=blob:none "$SERVER" main:refs/heads/other &&
395	grep "version 2" trace &&
396
397	# Ensure that the old version of the file is missing
398	git -C client rev-list --quiet --objects --missing=print other \
399		>observed.oids &&
400	grep "$(git -C server rev-parse message1:a.txt)" observed.oids &&
401
402	# Ensure that client passes fsck
403	git -C client fsck
404'
405
406test_expect_success 'do not advertise filter if not configured to do so' '
407	SERVER="file://$(pwd)/server" &&
408
409	rm "$(pwd)/trace" &&
410	git -C server config uploadpack.allowfilter 1 &&
411	GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \
412		ls-remote "$SERVER" &&
413	grep "fetch=.*filter" trace &&
414
415	rm "$(pwd)/trace" &&
416	git -C server config uploadpack.allowfilter 0 &&
417	GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \
418		ls-remote "$SERVER" &&
419	grep "fetch=" trace >fetch_capabilities &&
420	! grep filter fetch_capabilities
421'
422
423test_expect_success 'partial clone warns if filter is not advertised' '
424	rm -rf client &&
425	git -C server config uploadpack.allowfilter 0 &&
426	git -c protocol.version=2 \
427		clone --filter=blob:none "file://$(pwd)/server" client 2>err &&
428	test_i18ngrep "filtering not recognized by server, ignoring" err
429'
430
431test_expect_success 'even with handcrafted request, filter does not work if not advertised' '
432	git -C server config uploadpack.allowfilter 0 &&
433
434	# Custom request that tries to filter even though it is not advertised.
435	test-tool pkt-line pack >in <<-EOF &&
436	command=fetch
437	object-format=$(test_oid algo)
438	0001
439	want $(git -C server rev-parse main)
440	filter blob:none
441	0000
442	EOF
443
444	test_must_fail test-tool -C server serve-v2 --stateless-rpc \
445		<in >/dev/null 2>err &&
446	grep "unexpected line: .filter blob:none." err &&
447
448	# Exercise to ensure that if advertised, filter works
449	git -C server config uploadpack.allowfilter 1 &&
450	test-tool -C server serve-v2 --stateless-rpc <in >/dev/null
451'
452
453test_expect_success 'default refspec is used to filter ref when fetchcing' '
454	test_when_finished "rm -f log" &&
455
456	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
457		fetch origin &&
458
459	git -C file_child log -1 --format=%s three >actual &&
460	git -C file_parent log -1 --format=%s three >expect &&
461	test_cmp expect actual &&
462
463	grep "ref-prefix refs/heads/" log &&
464	grep "ref-prefix refs/tags/" log
465'
466
467test_expect_success 'fetch supports various ways of have lines' '
468	rm -rf server client trace &&
469	git init server &&
470	test_commit -C server dwim &&
471	TREE=$(git -C server rev-parse HEAD^{tree}) &&
472	git -C server tag exact \
473		$(git -C server commit-tree -m a "$TREE") &&
474	git -C server tag dwim-unwanted \
475		$(git -C server commit-tree -m b "$TREE") &&
476	git -C server tag exact-unwanted \
477		$(git -C server commit-tree -m c "$TREE") &&
478	git -C server tag prefix1 \
479		$(git -C server commit-tree -m d "$TREE") &&
480	git -C server tag prefix2 \
481		$(git -C server commit-tree -m e "$TREE") &&
482	git -C server tag fetch-by-sha1 \
483		$(git -C server commit-tree -m f "$TREE") &&
484	git -C server tag completely-unrelated \
485		$(git -C server commit-tree -m g "$TREE") &&
486
487	git init client &&
488	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
489		fetch "file://$(pwd)/server" \
490		dwim \
491		refs/tags/exact \
492		refs/tags/prefix*:refs/tags/prefix* \
493		"$(git -C server rev-parse fetch-by-sha1)" &&
494
495	# Ensure that the appropriate prefixes are sent (using a sample)
496	grep "fetch> ref-prefix dwim" trace &&
497	grep "fetch> ref-prefix refs/heads/dwim" trace &&
498	grep "fetch> ref-prefix refs/tags/prefix" trace &&
499
500	# Ensure that the correct objects are returned
501	git -C client cat-file -e $(git -C server rev-parse dwim) &&
502	git -C client cat-file -e $(git -C server rev-parse exact) &&
503	git -C client cat-file -e $(git -C server rev-parse prefix1) &&
504	git -C client cat-file -e $(git -C server rev-parse prefix2) &&
505	git -C client cat-file -e $(git -C server rev-parse fetch-by-sha1) &&
506	test_must_fail git -C client cat-file -e \
507		$(git -C server rev-parse dwim-unwanted) &&
508	test_must_fail git -C client cat-file -e \
509		$(git -C server rev-parse exact-unwanted) &&
510	test_must_fail git -C client cat-file -e \
511		$(git -C server rev-parse completely-unrelated)
512'
513
514test_expect_success 'fetch supports include-tag and tag following' '
515	rm -rf server client trace &&
516	git init server &&
517
518	test_commit -C server to_fetch &&
519	git -C server tag -a annotated_tag -m message &&
520
521	git init client &&
522	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
523		fetch "$(pwd)/server" to_fetch:to_fetch &&
524
525	grep "fetch> ref-prefix to_fetch" trace &&
526	grep "fetch> ref-prefix refs/tags/" trace &&
527	grep "fetch> include-tag" trace &&
528
529	git -C client cat-file -e $(git -C client rev-parse annotated_tag)
530'
531
532test_expect_success 'upload-pack respects client shallows' '
533	rm -rf server client trace &&
534
535	git init server &&
536	test_commit -C server base &&
537	test_commit -C server client_has &&
538
539	git clone --depth=1 "file://$(pwd)/server" client &&
540
541	# Add extra commits to the client so that the whole fetch takes more
542	# than 1 request (due to negotiation)
543	test_commit_bulk -C client --id=c 32 &&
544
545	git -C server checkout -b newbranch base &&
546	test_commit -C server client_wants &&
547
548	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
549		fetch origin newbranch &&
550	# Ensure that protocol v2 is used
551	grep "fetch< version 2" trace
552'
553
554test_expect_success 'ensure that multiple fetches in same process from a shallow repo works' '
555	rm -rf server client trace &&
556
557	test_create_repo server &&
558	test_commit -C server one &&
559	test_commit -C server two &&
560	test_commit -C server three &&
561	git clone --shallow-exclude two "file://$(pwd)/server" client &&
562
563	git -C server tag -a -m "an annotated tag" twotag two &&
564
565	# Triggers tag following (thus, 2 fetches in one process)
566	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
567		fetch --shallow-exclude one origin &&
568	# Ensure that protocol v2 is used
569	grep "fetch< version 2" trace
570'
571
572test_expect_success 'deepen-relative' '
573	rm -rf server client trace &&
574
575	test_create_repo server &&
576	test_commit -C server one &&
577	test_commit -C server two &&
578	test_commit -C server three &&
579	git clone --depth 1 "file://$(pwd)/server" client &&
580	test_commit -C server four &&
581
582	# Sanity check that only "three" is downloaded
583	git -C client log --pretty=tformat:%s main >actual &&
584	echo three >expected &&
585	test_cmp expected actual &&
586
587	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
588		fetch --deepen=1 origin &&
589	# Ensure that protocol v2 is used
590	grep "fetch< version 2" trace &&
591
592	git -C client log --pretty=tformat:%s origin/main >actual &&
593	cat >expected <<-\EOF &&
594	four
595	three
596	two
597	EOF
598	test_cmp expected actual
599'
600
601setup_negotiate_only () {
602	SERVER="$1"
603	URI="$2"
604
605	rm -rf "$SERVER" client
606
607	git init "$SERVER"
608	test_commit -C "$SERVER" one
609	test_commit -C "$SERVER" two
610
611	git clone "$URI" client
612	test_commit -C client three
613}
614
615test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
616	SERVER="server" &&
617	URI="file://$(pwd)/server" &&
618
619	setup_negotiate_only "$SERVER" "$URI" &&
620
621	cat >err.expect <<-\EOF &&
622	fatal: --negotiate-only needs one or more --negotiate-tip=*
623	EOF
624
625	test_must_fail git -c protocol.version=2 -C client fetch \
626		--negotiate-only \
627		origin 2>err.actual &&
628	test_cmp err.expect err.actual
629'
630
631test_expect_success 'file:// --negotiate-only' '
632	SERVER="server" &&
633	URI="file://$(pwd)/server" &&
634
635	setup_negotiate_only "$SERVER" "$URI" &&
636
637	git -c protocol.version=2 -C client fetch \
638		--no-tags \
639		--negotiate-only \
640		--negotiation-tip=$(git -C client rev-parse HEAD) \
641		origin >out &&
642	COMMON=$(git -C "$SERVER" rev-parse two) &&
643	grep "$COMMON" out
644'
645
646test_expect_success 'file:// --negotiate-only with protocol v0' '
647	SERVER="server" &&
648	URI="file://$(pwd)/server" &&
649
650	setup_negotiate_only "$SERVER" "$URI" &&
651
652	test_must_fail git -c protocol.version=0 -C client fetch \
653		--no-tags \
654		--negotiate-only \
655		--negotiation-tip=$(git -C client rev-parse HEAD) \
656		origin 2>err &&
657	test_i18ngrep "negotiate-only requires protocol v2" err
658'
659
660# Test protocol v2 with 'http://' transport
661#
662. "$TEST_DIRECTORY"/lib-httpd.sh
663start_httpd
664
665test_expect_success 'create repo to be served by http:// transport' '
666	git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
667	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" config http.receivepack true &&
668	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one
669'
670
671test_expect_success 'clone with http:// using protocol v2' '
672	test_when_finished "rm -f log" &&
673
674	GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" git -c protocol.version=2 \
675		clone "$HTTPD_URL/smart/http_parent" http_child &&
676
677	git -C http_child log -1 --format=%s >actual &&
678	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
679	test_cmp expect actual &&
680
681	# Client requested to use protocol v2
682	grep "Git-Protocol: version=2" log &&
683	# Server responded using protocol v2
684	grep "git< version 2" log &&
685	# Verify that the chunked encoding sending codepath is NOT exercised
686	! grep "Send header: Transfer-Encoding: chunked" log
687'
688
689test_expect_success 'clone repository with http:// using protocol v2 with incomplete pktline length' '
690	test_when_finished "rm -f log" &&
691
692	git init "$HTTPD_DOCUMENT_ROOT_PATH/incomplete_length" &&
693	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/incomplete_length" file &&
694
695	test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" git -c protocol.version=2 \
696		clone "$HTTPD_URL/smart/incomplete_length" incomplete_length_child 2>err &&
697
698	# Client requested to use protocol v2
699	grep "Git-Protocol: version=2" log &&
700	# Server responded using protocol v2
701	grep "git< version 2" log &&
702	# Client reported appropriate failure
703	test_i18ngrep "bytes of length header were received" err
704'
705
706test_expect_success 'clone repository with http:// using protocol v2 with incomplete pktline body' '
707	test_when_finished "rm -f log" &&
708
709	git init "$HTTPD_DOCUMENT_ROOT_PATH/incomplete_body" &&
710	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/incomplete_body" file &&
711
712	test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" git -c protocol.version=2 \
713		clone "$HTTPD_URL/smart/incomplete_body" incomplete_body_child 2>err &&
714
715	# Client requested to use protocol v2
716	grep "Git-Protocol: version=2" log &&
717	# Server responded using protocol v2
718	grep "git< version 2" log &&
719	# Client reported appropriate failure
720	test_i18ngrep "bytes of body are still expected" err
721'
722
723test_expect_success 'clone with http:// using protocol v2 and invalid parameters' '
724	test_when_finished "rm -f log" &&
725
726	test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" \
727		git -c protocol.version=2 \
728		clone --shallow-since=20151012 "$HTTPD_URL/smart/http_parent" http_child_invalid &&
729
730	# Client requested to use protocol v2
731	grep "Git-Protocol: version=2" log &&
732	# Server responded using protocol v2
733	grep "git< version 2" log
734'
735
736test_expect_success 'clone big repository with http:// using protocol v2' '
737	test_when_finished "rm -f log" &&
738
739	git init "$HTTPD_DOCUMENT_ROOT_PATH/big" &&
740	# Ensure that the list of wants is greater than http.postbuffer below
741	for i in $(test_seq 1 1500)
742	do
743		# do not use here-doc, because it requires a process
744		# per loop iteration
745		echo "commit refs/heads/too-many-refs-$i" &&
746		echo "committer git <git@example.com> $i +0000" &&
747		echo "data 0" &&
748		echo "M 644 inline bla.txt" &&
749		echo "data 4" &&
750		echo "bla"
751	done | git -C "$HTTPD_DOCUMENT_ROOT_PATH/big" fast-import &&
752
753	GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" git \
754		-c protocol.version=2 -c http.postbuffer=65536 \
755		clone "$HTTPD_URL/smart/big" big_child &&
756
757	# Client requested to use protocol v2
758	grep "Git-Protocol: version=2" log &&
759	# Server responded using protocol v2
760	grep "git< version 2" log &&
761	# Verify that the chunked encoding sending codepath is exercised
762	grep "Send header: Transfer-Encoding: chunked" log
763'
764
765test_expect_success 'fetch with http:// using protocol v2' '
766	test_when_finished "rm -f log" &&
767
768	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
769
770	GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
771		fetch &&
772
773	git -C http_child log -1 --format=%s origin/main >actual &&
774	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
775	test_cmp expect actual &&
776
777	# Server responded using protocol v2
778	grep "git< version 2" log
779'
780
781test_expect_success 'fetch with http:// by hash without tag following with protocol v2 does not list refs' '
782	test_when_finished "rm -f log" &&
783
784	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two_a &&
785	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" rev-parse two_a >two_a_hash &&
786
787	GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
788		fetch --no-tags origin $(cat two_a_hash) &&
789
790	grep "fetch< version 2" log &&
791	! grep "fetch> command=ls-refs" log
792'
793
794test_expect_success 'fetch from namespaced repo respects namespaces' '
795	test_when_finished "rm -f log" &&
796
797	git init "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" &&
798	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" one &&
799	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" two &&
800	git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" \
801		update-ref refs/namespaces/ns/refs/heads/main one &&
802
803	GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
804		fetch "$HTTPD_URL/smart_namespace/nsrepo" \
805		refs/heads/main:refs/heads/theirs &&
806
807	# Server responded using protocol v2
808	grep "fetch< version 2" log &&
809
810	git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" rev-parse one >expect &&
811	git -C http_child rev-parse theirs >actual &&
812	test_cmp expect actual
813'
814
815test_expect_success 'ls-remote with v2 http sends only one POST' '
816	test_when_finished "rm -f log" &&
817
818	git ls-remote "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" >expect &&
819	GIT_TRACE_CURL="$(pwd)/log" git -c protocol.version=2 \
820		ls-remote "$HTTPD_URL/smart/http_parent" >actual &&
821	test_cmp expect actual &&
822
823	grep "Send header: POST" log >posts &&
824	test_line_count = 1 posts
825'
826
827test_expect_success 'push with http:// and a config of v2 does not request v2' '
828	test_when_finished "rm -f log" &&
829	# Till v2 for push is designed, make sure that if a client has
830	# protocol.version configured to use v2, that the client instead falls
831	# back and uses v0.
832
833	test_commit -C http_child three &&
834
835	# Push to another branch, as the target repository has the
836	# main branch checked out and we cannot push into it.
837	GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
838		push origin HEAD:client_branch &&
839
840	git -C http_child log -1 --format=%s >actual &&
841	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s client_branch >expect &&
842	test_cmp expect actual &&
843
844	# Client did not request to use protocol v2
845	! grep "Git-Protocol: version=2" log &&
846	# Server did not respond using protocol v2
847	! grep "git< version 2" log
848'
849
850test_expect_success 'when server sends "ready", expect DELIM' '
851	rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" http_child &&
852
853	git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
854	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one &&
855
856	git clone "$HTTPD_URL/smart/http_parent" http_child &&
857
858	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
859
860	# After "ready" in the acknowledgments section, pretend that a FLUSH
861	# (0000) was sent instead of a DELIM (0001).
862	printf "\$ready = 1 if /ready/; \$ready && s/0001/0000/" \
863		>"$HTTPD_ROOT_PATH/one-time-perl" &&
864
865	test_must_fail git -C http_child -c protocol.version=2 \
866		fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
867	test_i18ngrep "expected packfile to be sent after .ready." err
868'
869
870test_expect_success 'when server does not send "ready", expect FLUSH' '
871	rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" http_child log &&
872
873	git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
874	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one &&
875
876	git clone "$HTTPD_URL/smart/http_parent" http_child &&
877
878	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
879
880	# Create many commits to extend the negotiation phase across multiple
881	# requests, so that the server does not send "ready" in the first
882	# request.
883	test_commit_bulk -C http_child --id=c 32 &&
884
885	# After the acknowledgments section, pretend that a DELIM
886	# (0001) was sent instead of a FLUSH (0000).
887	printf "\$ack = 1 if /acknowledgments/; \$ack && s/0000/0001/" \
888		>"$HTTPD_ROOT_PATH/one-time-perl" &&
889
890	test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \
891		-c protocol.version=2 \
892		fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
893	grep "fetch< .*acknowledgments" log &&
894	! grep "fetch< .*ready" log &&
895	test_i18ngrep "expected no other sections to be sent after no .ready." err
896'
897
898configure_exclusion () {
899	git -C "$1" hash-object "$2" >objh &&
900	git -C "$1" pack-objects "$HTTPD_DOCUMENT_ROOT_PATH/mypack" <objh >packh &&
901	git -C "$1" config --add \
902		"uploadpack.blobpackfileuri" \
903		"$(cat objh) $(cat packh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" &&
904	cat objh
905}
906
907test_expect_success 'part of packfile response provided as URI' '
908	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
909	rm -rf "$P" http_child log &&
910
911	git init "$P" &&
912	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
913
914	echo my-blob >"$P/my-blob" &&
915	git -C "$P" add my-blob &&
916	echo other-blob >"$P/other-blob" &&
917	git -C "$P" add other-blob &&
918	git -C "$P" commit -m x &&
919
920	configure_exclusion "$P" my-blob >h &&
921	configure_exclusion "$P" other-blob >h2 &&
922
923	GIT_TRACE=1 GIT_TRACE_PACKET="$(pwd)/log" GIT_TEST_SIDEBAND_ALL=1 \
924	git -c protocol.version=2 \
925		-c fetch.uriprotocols=http,https \
926		clone "$HTTPD_URL/smart/http_parent" http_child &&
927
928	# Ensure that my-blob and other-blob are in separate packfiles.
929	for idx in http_child/.git/objects/pack/*.idx
930	do
931		git verify-pack --object-format=$(test_oid algo) --verbose $idx >out &&
932		{
933			grep "^[0-9a-f]\{16,\} " out || :
934		} >out.objectlist &&
935		if test_line_count = 1 out.objectlist
936		then
937			if grep $(cat h) out
938			then
939				>hfound
940			fi &&
941			if grep $(cat h2) out
942			then
943				>h2found
944			fi
945		fi
946	done &&
947	test -f hfound &&
948	test -f h2found &&
949
950	# Ensure that there are exactly 3 packfiles with associated .idx
951	ls http_child/.git/objects/pack/*.pack \
952	    http_child/.git/objects/pack/*.idx >filelist &&
953	test_line_count = 6 filelist
954'
955
956test_expect_success 'packfile URIs with fetch instead of clone' '
957	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
958	rm -rf "$P" http_child log &&
959
960	git init "$P" &&
961	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
962
963	echo my-blob >"$P/my-blob" &&
964	git -C "$P" add my-blob &&
965	git -C "$P" commit -m x &&
966
967	configure_exclusion "$P" my-blob >h &&
968
969	git init http_child &&
970
971	GIT_TEST_SIDEBAND_ALL=1 \
972	git -C http_child -c protocol.version=2 \
973		-c fetch.uriprotocols=http,https \
974		fetch "$HTTPD_URL/smart/http_parent"
975'
976
977test_expect_success 'fetching with valid packfile URI but invalid hash fails' '
978	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
979	rm -rf "$P" http_child log &&
980
981	git init "$P" &&
982	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
983
984	echo my-blob >"$P/my-blob" &&
985	git -C "$P" add my-blob &&
986	echo other-blob >"$P/other-blob" &&
987	git -C "$P" add other-blob &&
988	git -C "$P" commit -m x &&
989
990	configure_exclusion "$P" my-blob >h &&
991	# Configure a URL for other-blob. Just reuse the hash of the object as
992	# the hash of the packfile, since the hash does not matter for this
993	# test as long as it is not the hash of the pack, and it is of the
994	# expected length.
995	git -C "$P" hash-object other-blob >objh &&
996	git -C "$P" pack-objects "$HTTPD_DOCUMENT_ROOT_PATH/mypack" <objh >packh &&
997	git -C "$P" config --add \
998		"uploadpack.blobpackfileuri" \
999		"$(cat objh) $(cat objh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" &&
1000
1001	test_must_fail env GIT_TEST_SIDEBAND_ALL=1 \
1002		git -c protocol.version=2 \
1003		-c fetch.uriprotocols=http,https \
1004		clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
1005	test_i18ngrep "pack downloaded from.*does not match expected hash" err
1006'
1007
1008test_expect_success 'packfile-uri with transfer.fsckobjects' '
1009	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
1010	rm -rf "$P" http_child log &&
1011
1012	git init "$P" &&
1013	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
1014
1015	echo my-blob >"$P/my-blob" &&
1016	git -C "$P" add my-blob &&
1017	git -C "$P" commit -m x &&
1018
1019	configure_exclusion "$P" my-blob >h &&
1020
1021	sane_unset GIT_TEST_SIDEBAND_ALL &&
1022	git -c protocol.version=2 -c transfer.fsckobjects=1 \
1023		-c fetch.uriprotocols=http,https \
1024		clone "$HTTPD_URL/smart/http_parent" http_child &&
1025
1026	# Ensure that there are exactly 2 packfiles with associated .idx
1027	ls http_child/.git/objects/pack/*.pack \
1028	    http_child/.git/objects/pack/*.idx >filelist &&
1029	test_line_count = 4 filelist
1030'
1031
1032test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object' '
1033	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
1034	rm -rf "$P" http_child log &&
1035
1036	git init "$P" &&
1037	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
1038
1039	cat >bogus-commit <<-EOF &&
1040	tree $EMPTY_TREE
1041	author Bugs Bunny 1234567890 +0000
1042	committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000
1043
1044	This commit object intentionally broken
1045	EOF
1046	BOGUS=$(git -C "$P" hash-object -t commit -w --stdin <bogus-commit) &&
1047	git -C "$P" branch bogus-branch "$BOGUS" &&
1048
1049	echo my-blob >"$P/my-blob" &&
1050	git -C "$P" add my-blob &&
1051	git -C "$P" commit -m x &&
1052
1053	configure_exclusion "$P" my-blob >h &&
1054
1055	sane_unset GIT_TEST_SIDEBAND_ALL &&
1056	test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
1057		-c fetch.uriprotocols=http,https \
1058		clone "$HTTPD_URL/smart/http_parent" http_child 2>error &&
1059	test_i18ngrep "invalid author/committer line - missing email" error
1060'
1061
1062test_expect_success 'packfile-uri with transfer.fsckobjects succeeds when .gitmodules is separate from tree' '
1063	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
1064	rm -rf "$P" http_child &&
1065
1066	git init "$P" &&
1067	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
1068
1069	echo "[submodule libfoo]" >"$P/.gitmodules" &&
1070	echo "path = include/foo" >>"$P/.gitmodules" &&
1071	echo "url = git://example.com/git/lib.git" >>"$P/.gitmodules" &&
1072	git -C "$P" add .gitmodules &&
1073	git -C "$P" commit -m x &&
1074
1075	configure_exclusion "$P" .gitmodules >h &&
1076
1077	sane_unset GIT_TEST_SIDEBAND_ALL &&
1078	git -c protocol.version=2 -c transfer.fsckobjects=1 \
1079		-c fetch.uriprotocols=http,https \
1080		clone "$HTTPD_URL/smart/http_parent" http_child &&
1081
1082	# Ensure that there are exactly 2 packfiles with associated .idx
1083	ls http_child/.git/objects/pack/*.pack \
1084	    http_child/.git/objects/pack/*.idx >filelist &&
1085	test_line_count = 4 filelist
1086'
1087
1088test_expect_success 'packfile-uri with transfer.fsckobjects fails when .gitmodules separate from tree is invalid' '
1089	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
1090	rm -rf "$P" http_child err &&
1091
1092	git init "$P" &&
1093	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
1094
1095	echo "[submodule \"..\"]" >"$P/.gitmodules" &&
1096	echo "path = include/foo" >>"$P/.gitmodules" &&
1097	echo "url = git://example.com/git/lib.git" >>"$P/.gitmodules" &&
1098	git -C "$P" add .gitmodules &&
1099	git -C "$P" commit -m x &&
1100
1101	configure_exclusion "$P" .gitmodules >h &&
1102
1103	sane_unset GIT_TEST_SIDEBAND_ALL &&
1104	test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
1105		-c fetch.uriprotocols=http,https \
1106		clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
1107	test_i18ngrep "disallowed submodule name" err
1108'
1109
1110test_expect_success 'http:// --negotiate-only' '
1111	SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
1112	URI="$HTTPD_URL/smart/server" &&
1113
1114	setup_negotiate_only "$SERVER" "$URI" &&
1115
1116	git -c protocol.version=2 -C client fetch \
1117		--no-tags \
1118		--negotiate-only \
1119		--negotiation-tip=$(git -C client rev-parse HEAD) \
1120		origin >out &&
1121	COMMON=$(git -C "$SERVER" rev-parse two) &&
1122	grep "$COMMON" out
1123'
1124
1125test_expect_success 'http:// --negotiate-only without wait-for-done support' '
1126	SERVER="server" &&
1127	URI="$HTTPD_URL/one_time_perl/server" &&
1128
1129	setup_negotiate_only "$SERVER" "$URI" &&
1130
1131	echo "s/ wait-for-done/ xxxx-xxx-xxxx/" \
1132		>"$HTTPD_ROOT_PATH/one-time-perl" &&
1133
1134	test_must_fail git -c protocol.version=2 -C client fetch \
1135		--no-tags \
1136		--negotiate-only \
1137		--negotiation-tip=$(git -C client rev-parse HEAD) \
1138		origin 2>err &&
1139	test_i18ngrep "server does not support wait-for-done" err
1140'
1141
1142test_expect_success 'http:// --negotiate-only with protocol v0' '
1143	SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
1144	URI="$HTTPD_URL/smart/server" &&
1145
1146	setup_negotiate_only "$SERVER" "$URI" &&
1147
1148	test_must_fail git -c protocol.version=0 -C client fetch \
1149		--no-tags \
1150		--negotiate-only \
1151		--negotiation-tip=$(git -C client rev-parse HEAD) \
1152		origin 2>err &&
1153	test_i18ngrep "negotiate-only requires protocol v2" err
1154'
1155
1156# DO NOT add non-httpd-specific tests here, because the last part of this
1157# test script is only executed when httpd is available and enabled.
1158
1159test_done
1160