Merge branch 'ab/retire-pcre1'
[git] / t / t5703-upload-pack-ref-in-want.sh
1 #!/bin/sh
2
3 test_description='upload-pack ref-in-want'
4
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8 . ./test-lib.sh
9
10 get_actual_refs () {
11         sed -n -e '/wanted-refs/,/0001/{
12                 /wanted-refs/d
13                 /0001/d
14                 p
15                 }' <out | test-tool pkt-line unpack >actual_refs
16 }
17
18 get_actual_commits () {
19         test-tool pkt-line unpack-sideband <out >o.pack &&
20         git index-pack o.pack &&
21         git verify-pack -v o.idx >objs &&
22         grep commit objs | cut -d" " -f1 | sort >actual_commits
23 }
24
25 check_output () {
26         get_actual_refs &&
27         test_cmp expected_refs actual_refs &&
28         get_actual_commits &&
29         sort expected_commits >sorted_commits &&
30         test_cmp sorted_commits actual_commits
31 }
32
33 write_command () {
34         echo "command=$1"
35
36         if test "$(test_oid algo)" != sha1
37         then
38                 echo "object-format=$(test_oid algo)"
39         fi
40 }
41
42 # c(o/foo) d(o/bar)
43 #        \ /
44 #         b   e(baz)  f(main)
45 #          \__  |  __/
46 #             \ | /
47 #               a
48 test_expect_success 'setup repository' '
49         test_commit a &&
50         git branch -M main &&
51         git checkout -b o/foo &&
52         test_commit b &&
53         test_commit c &&
54         git checkout -b o/bar b &&
55         test_commit d &&
56         git checkout -b baz a &&
57         test_commit e &&
58         git checkout main &&
59         test_commit f
60 '
61
62 test_expect_success 'config controls ref-in-want advertisement' '
63         test-tool serve-v2 --advertise-capabilities >out &&
64         perl -ne "/ref-in-want/ and print" out >out.filter &&
65         test_must_be_empty out.filter &&
66
67         git config uploadpack.allowRefInWant false &&
68         test-tool serve-v2 --advertise-capabilities >out &&
69         perl -ne "/ref-in-want/ and print" out >out.filter &&
70         test_must_be_empty out.filter &&
71
72         git config uploadpack.allowRefInWant true &&
73         test-tool serve-v2 --advertise-capabilities >out &&
74         perl -ne "/ref-in-want/ and print" out >out.filter &&
75         test_file_not_empty out.filter
76 '
77
78 test_expect_success 'invalid want-ref line' '
79         test-tool pkt-line pack >in <<-EOF &&
80         $(write_command fetch)
81         0001
82         no-progress
83         want-ref refs/heads/non-existent
84         done
85         0000
86         EOF
87
88         test_must_fail test-tool serve-v2 --stateless-rpc 2>out <in &&
89         grep "unknown ref" out
90 '
91
92 test_expect_success 'basic want-ref' '
93         oid=$(git rev-parse f) &&
94         cat >expected_refs <<-EOF &&
95         $oid refs/heads/main
96         EOF
97         git rev-parse f >expected_commits &&
98
99         oid=$(git rev-parse a) &&
100         test-tool pkt-line pack >in <<-EOF &&
101         $(write_command fetch)
102         0001
103         no-progress
104         want-ref refs/heads/main
105         have $oid
106         done
107         0000
108         EOF
109
110         test-tool serve-v2 --stateless-rpc >out <in &&
111         check_output
112 '
113
114 test_expect_success 'multiple want-ref lines' '
115         oid_c=$(git rev-parse c) &&
116         oid_d=$(git rev-parse d) &&
117         cat >expected_refs <<-EOF &&
118         $oid_c refs/heads/o/foo
119         $oid_d refs/heads/o/bar
120         EOF
121         git rev-parse c d >expected_commits &&
122
123         oid=$(git rev-parse b) &&
124         test-tool pkt-line pack >in <<-EOF &&
125         $(write_command fetch)
126         0001
127         no-progress
128         want-ref refs/heads/o/foo
129         want-ref refs/heads/o/bar
130         have $oid
131         done
132         0000
133         EOF
134
135         test-tool serve-v2 --stateless-rpc >out <in &&
136         check_output
137 '
138
139 test_expect_success 'mix want and want-ref' '
140         oid=$(git rev-parse f) &&
141         cat >expected_refs <<-EOF &&
142         $oid refs/heads/main
143         EOF
144         git rev-parse e f >expected_commits &&
145
146         test-tool pkt-line pack >in <<-EOF &&
147         $(write_command fetch)
148         0001
149         no-progress
150         want-ref refs/heads/main
151         want $(git rev-parse e)
152         have $(git rev-parse a)
153         done
154         0000
155         EOF
156
157         test-tool serve-v2 --stateless-rpc >out <in &&
158         check_output
159 '
160
161 test_expect_success 'want-ref with ref we already have commit for' '
162         oid=$(git rev-parse c) &&
163         cat >expected_refs <<-EOF &&
164         $oid refs/heads/o/foo
165         EOF
166         >expected_commits &&
167
168         oid=$(git rev-parse c) &&
169         test-tool pkt-line pack >in <<-EOF &&
170         $(write_command fetch)
171         0001
172         no-progress
173         want-ref refs/heads/o/foo
174         have $oid
175         done
176         0000
177         EOF
178
179         test-tool serve-v2 --stateless-rpc >out <in &&
180         check_output
181 '
182
183 REPO="$(pwd)/repo"
184 LOCAL_PRISTINE="$(pwd)/local_pristine"
185
186 # $REPO
187 # c(o/foo) d(o/bar)
188 #        \ /
189 #         b   e(baz)  f(main)
190 #          \__  |  __/
191 #             \ | /
192 #               a
193 #
194 # $LOCAL_PRISTINE
195 #               s32(side)
196 #               |
197 #               .
198 #               .
199 #               |
200 #               a(main)
201 test_expect_success 'setup repos for fetching with ref-in-want tests' '
202         (
203                 git init -b main "$REPO" &&
204                 cd "$REPO" &&
205                 test_commit a &&
206
207                 # Local repo with many commits (so that negotiation will take
208                 # more than 1 request/response pair)
209                 rm -rf "$LOCAL_PRISTINE" &&
210                 git clone "file://$REPO" "$LOCAL_PRISTINE" &&
211                 cd "$LOCAL_PRISTINE" &&
212                 git checkout -b side &&
213                 test_commit_bulk --id=s 33 &&
214
215                 # Add novel commits to upstream
216                 git checkout main &&
217                 cd "$REPO" &&
218                 git checkout -b o/foo &&
219                 test_commit b &&
220                 test_commit c &&
221                 git checkout -b o/bar b &&
222                 test_commit d &&
223                 git checkout -b baz a &&
224                 test_commit e &&
225                 git checkout main &&
226                 test_commit f
227         ) &&
228         git -C "$REPO" config uploadpack.allowRefInWant true &&
229         git -C "$LOCAL_PRISTINE" config protocol.version 2
230 '
231
232 test_expect_success 'fetching with exact OID' '
233         test_when_finished "rm -f log" &&
234
235         rm -rf local &&
236         cp -r "$LOCAL_PRISTINE" local &&
237         oid=$(git -C "$REPO" rev-parse d) &&
238         GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
239                 "$oid":refs/heads/actual &&
240
241         git -C "$REPO" rev-parse "d" >expected &&
242         git -C local rev-parse refs/heads/actual >actual &&
243         test_cmp expected actual &&
244         grep "want $oid" log
245 '
246
247 test_expect_success 'fetching multiple refs' '
248         test_when_finished "rm -f log" &&
249
250         rm -rf local &&
251         cp -r "$LOCAL_PRISTINE" local &&
252         GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin main baz &&
253
254         git -C "$REPO" rev-parse "main" "baz" >expected &&
255         git -C local rev-parse refs/remotes/origin/main refs/remotes/origin/baz >actual &&
256         test_cmp expected actual &&
257         grep "want-ref refs/heads/main" log &&
258         grep "want-ref refs/heads/baz" log
259 '
260
261 test_expect_success 'fetching ref and exact OID' '
262         test_when_finished "rm -f log" &&
263
264         rm -rf local &&
265         cp -r "$LOCAL_PRISTINE" local &&
266         oid=$(git -C "$REPO" rev-parse b) &&
267         GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
268                 main "$oid":refs/heads/actual &&
269
270         git -C "$REPO" rev-parse "main" "b" >expected &&
271         git -C local rev-parse refs/remotes/origin/main refs/heads/actual >actual &&
272         test_cmp expected actual &&
273         grep "want $oid" log &&
274         grep "want-ref refs/heads/main" log
275 '
276
277 test_expect_success 'fetching with wildcard that does not match any refs' '
278         test_when_finished "rm -f log" &&
279
280         rm -rf local &&
281         cp -r "$LOCAL_PRISTINE" local &&
282         git -C local fetch origin refs/heads/none*:refs/heads/* >out &&
283         test_must_be_empty out
284 '
285
286 test_expect_success 'fetching with wildcard that matches multiple refs' '
287         test_when_finished "rm -f log" &&
288
289         rm -rf local &&
290         cp -r "$LOCAL_PRISTINE" local &&
291         GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin refs/heads/o*:refs/heads/o* &&
292
293         git -C "$REPO" rev-parse "o/foo" "o/bar" >expected &&
294         git -C local rev-parse "o/foo" "o/bar" >actual &&
295         test_cmp expected actual &&
296         grep "want-ref refs/heads/o/foo" log &&
297         grep "want-ref refs/heads/o/bar" log
298 '
299
300 . "$TEST_DIRECTORY"/lib-httpd.sh
301 start_httpd
302
303 REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
304 LOCAL_PRISTINE="$(pwd)/local_pristine"
305
306 test_expect_success 'setup repos for change-while-negotiating test' '
307         (
308                 git init -b main "$REPO" &&
309                 cd "$REPO" &&
310                 >.git/git-daemon-export-ok &&
311                 test_commit m1 &&
312                 git tag -d m1 &&
313
314                 # Local repo with many commits (so that negotiation will take
315                 # more than 1 request/response pair)
316                 rm -rf "$LOCAL_PRISTINE" &&
317                 git clone "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" "$LOCAL_PRISTINE" &&
318                 cd "$LOCAL_PRISTINE" &&
319                 git checkout -b side &&
320                 test_commit_bulk --id=s 33 &&
321
322                 # Add novel commits to upstream
323                 git checkout main &&
324                 cd "$REPO" &&
325                 test_commit m2 &&
326                 test_commit m3 &&
327                 git tag -d m2 m3
328         ) &&
329         git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_perl/repo" &&
330         git -C "$LOCAL_PRISTINE" config protocol.version 2
331 '
332
333 inconsistency () {
334         # Simulate that the server initially reports $2 as the ref
335         # corresponding to $1, and after that, $1 as the ref corresponding to
336         # $1. This corresponds to the real-life situation where the server's
337         # repository appears to change during negotiation, for example, when
338         # different servers in a load-balancing arrangement serve (stateless)
339         # RPCs during a single negotiation.
340         oid1=$(git -C "$REPO" rev-parse $1) &&
341         oid2=$(git -C "$REPO" rev-parse $2) &&
342         echo "s/$oid1/$oid2/" >"$HTTPD_ROOT_PATH/one-time-perl"
343 }
344
345 test_expect_success 'server is initially ahead - no ref in want' '
346         git -C "$REPO" config uploadpack.allowRefInWant false &&
347         rm -rf local &&
348         cp -r "$LOCAL_PRISTINE" local &&
349         inconsistency main $(test_oid numeric) &&
350         test_must_fail git -C local fetch 2>err &&
351         test_i18ngrep "fatal: remote error: upload-pack: not our ref" err
352 '
353
354 test_expect_success 'server is initially ahead - ref in want' '
355         git -C "$REPO" config uploadpack.allowRefInWant true &&
356         rm -rf local &&
357         cp -r "$LOCAL_PRISTINE" local &&
358         inconsistency main $(test_oid numeric) &&
359         git -C local fetch &&
360
361         git -C "$REPO" rev-parse --verify main >expected &&
362         git -C local rev-parse --verify refs/remotes/origin/main >actual &&
363         test_cmp expected actual
364 '
365
366 test_expect_success 'server is initially behind - no ref in want' '
367         git -C "$REPO" config uploadpack.allowRefInWant false &&
368         rm -rf local &&
369         cp -r "$LOCAL_PRISTINE" local &&
370         inconsistency main "main^" &&
371         git -C local fetch &&
372
373         git -C "$REPO" rev-parse --verify "main^" >expected &&
374         git -C local rev-parse --verify refs/remotes/origin/main >actual &&
375         test_cmp expected actual
376 '
377
378 test_expect_success 'server is initially behind - ref in want' '
379         git -C "$REPO" config uploadpack.allowRefInWant true &&
380         rm -rf local &&
381         cp -r "$LOCAL_PRISTINE" local &&
382         inconsistency main "main^" &&
383         git -C local fetch &&
384
385         git -C "$REPO" rev-parse --verify "main" >expected &&
386         git -C local rev-parse --verify refs/remotes/origin/main >actual &&
387         test_cmp expected actual
388 '
389
390 test_expect_success 'server loses a ref - ref in want' '
391         git -C "$REPO" config uploadpack.allowRefInWant true &&
392         rm -rf local &&
393         cp -r "$LOCAL_PRISTINE" local &&
394         echo "s/main/rain/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
395         test_must_fail git -C local fetch 2>err &&
396
397         test_i18ngrep "fatal: remote error: unknown ref refs/heads/rain" err
398 '
399
400 # DO NOT add non-httpd-specific tests here, because the last part of this
401 # test script is only executed when httpd is available and enabled.
402
403 test_done