Merge branch 'fixes/2.45.1/2.41' into fixes/2.45.1/2.42
[alt-git.git] / t / t5541-http-push-smart.sh
blobd0211cd8bef450e0149dccd037b0f9e1607ef421
1 #!/bin/sh
3 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
6 test_description='test smart pushing over http via http-backend'
7 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
8 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
10 . ./test-lib.sh
12 ROOT_PATH="$PWD"
13 . "$TEST_DIRECTORY"/lib-gpg.sh
14 . "$TEST_DIRECTORY"/lib-httpd.sh
15 . "$TEST_DIRECTORY"/lib-terminal.sh
16 start_httpd
18 test_expect_success 'setup remote repository' '
19 cd "$ROOT_PATH" &&
20 mkdir test_repo &&
21 cd test_repo &&
22 git init &&
23 : >path1 &&
24 git add path1 &&
25 test_tick &&
26 git commit -m initial &&
27 cd - &&
28 git clone --bare test_repo test_repo.git &&
29 cd test_repo.git &&
30 git config http.receivepack true &&
31 git config core.logallrefupdates true &&
32 ORIG_HEAD=$(git rev-parse --verify HEAD) &&
33 cd - &&
34 mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
37 setup_askpass_helper
39 test_expect_success 'clone remote repository' '
40 rm -rf test_repo_clone &&
41 git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
43 cd test_repo_clone && git config push.default matching
47 test_expect_success 'push to remote repository (standard)' '
48 # Clear the log, so that the "used receive-pack service" test below
49 # sees just what we did here.
50 >"$HTTPD_ROOT_PATH"/access.log &&
52 cd "$ROOT_PATH"/test_repo_clone &&
53 : >path2 &&
54 git add path2 &&
55 test_tick &&
56 git commit -m path2 &&
57 HEAD=$(git rev-parse --verify HEAD) &&
58 GIT_TRACE_CURL=true git push -v -v 2>err &&
59 ! grep "Expect: 100-continue" err &&
60 grep "POST git-receive-pack ([0-9]* bytes)" err &&
61 (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
62 test $HEAD = $(git rev-parse --verify HEAD))
65 test_expect_success 'used receive-pack service' '
66 cat >exp <<-\EOF &&
67 GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
68 POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
69 EOF
71 check_access_log exp
74 test_expect_success 'push to remote repository (standard) with sending Accept-Language' '
75 cat >exp <<-\EOF &&
76 => Send header: Accept-Language: ko-KR, *;q=0.9
77 => Send header: Accept-Language: ko-KR, *;q=0.9
78 EOF
80 cd "$ROOT_PATH"/test_repo_clone &&
81 : >path_lang &&
82 git add path_lang &&
83 test_tick &&
84 git commit -m path_lang &&
85 HEAD=$(git rev-parse --verify HEAD) &&
86 GIT_TRACE_CURL=true LANGUAGE="ko_KR.UTF-8" git push -v -v 2>err &&
87 ! grep "Expect: 100-continue" err &&
89 grep "=> Send header: Accept-Language:" err >err.language &&
90 test_cmp exp err.language
93 test_expect_success 'push already up-to-date' '
94 git push
97 test_expect_success 'create and delete remote branch' '
98 cd "$ROOT_PATH"/test_repo_clone &&
99 git checkout -b dev &&
100 : >path3 &&
101 git add path3 &&
102 test_tick &&
103 git commit -m dev &&
104 git push origin dev &&
105 git push origin :dev &&
106 test_must_fail git show-ref --verify refs/remotes/origin/dev
109 test_expect_success 'setup rejected update hook' '
110 test_hook --setup -C "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" update <<-\EOF &&
111 exit 1
114 cat >exp <<-EOF
115 remote: error: hook declined to update refs/heads/dev2
116 To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
117 ! [remote rejected] dev2 -> dev2 (hook declined)
118 error: failed to push some refs to '\''http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'\''
122 test_expect_success 'rejected update prints status' '
123 cd "$ROOT_PATH"/test_repo_clone &&
124 git checkout -b dev2 &&
125 : >path4 &&
126 git add path4 &&
127 test_tick &&
128 git commit -m dev2 &&
129 test_must_fail git push origin dev2 2>act &&
130 sed -e "/^remote: /s/ *$//" <act >cmp &&
131 test_cmp exp cmp
133 rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
135 test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
136 "$ROOT_PATH"/test_repo_clone main success
138 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
139 # create a dissimilarly-named remote ref so that git is unable to match the
140 # two refs (viz. local, remote) unless an explicit refspec is provided.
141 git push origin main:niam &&
143 echo "change changed" > path2 &&
144 git commit -a -m path2 --amend &&
146 # push main too; this ensures there is at least one '"'push'"' command to
147 # the remote helper and triggers interaction with the helper.
148 test_must_fail git push -v origin +main main:niam >output 2>&1'
150 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
151 grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *main -> main (forced update)$" output &&
152 grep "^ ! \[rejected\] *main -> niam (non-fast-forward)$" output
155 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
156 test_i18ngrep "Updates were rejected because" \
157 output
160 test_expect_success 'push (chunked)' '
161 git checkout main &&
162 test_commit commit path3 &&
163 HEAD=$(git rev-parse --verify HEAD) &&
164 test_config http.postbuffer 4 &&
165 git push -v -v origin $BRANCH 2>err &&
166 grep "POST git-receive-pack (chunked)" err &&
167 (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
168 test $HEAD = $(git rev-parse --verify HEAD))
171 ## References of remote: atomic1(1) main(2) collateral(2) other(2)
172 ## References of local : atomic2(2) main(1) collateral(3) other(2) collateral1(3) atomic(1)
173 ## Atomic push : main(1) collateral(3) atomic(1)
174 test_expect_success 'push --atomic also prevents branch creation, reports collateral' '
175 # Setup upstream repo - empty for now
176 d=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&
177 git init --bare "$d" &&
178 test_config -C "$d" http.receivepack true &&
179 up="$HTTPD_URL"/smart/atomic-branches.git &&
181 # Tell "$up" about three branches for now
182 test_commit atomic1 &&
183 test_commit atomic2 &&
184 git branch collateral &&
185 git branch other &&
186 git push "$up" atomic1 main collateral other &&
187 git tag -d atomic1 &&
189 # collateral is a valid push, but should be failed by atomic push
190 git checkout collateral &&
191 test_commit collateral1 &&
193 # Make main incompatible with upstream to provoke atomic
194 git checkout main &&
195 git reset --hard HEAD^ &&
197 # Add a new branch which should be failed by atomic push. This is a
198 # regression case.
199 git branch atomic &&
201 # --atomic should cause entire push to be rejected
202 test_must_fail git push --atomic "$up" main atomic collateral 2>output &&
204 # the new branch should not have been created upstream
205 test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
207 # upstream should still reflect atomic2, the last thing we pushed
208 # successfully
209 git rev-parse atomic2 >expected &&
210 # on main...
211 git -C "$d" rev-parse refs/heads/main >actual &&
212 test_cmp expected actual &&
213 # ...and collateral.
214 git -C "$d" rev-parse refs/heads/collateral >actual &&
215 test_cmp expected actual &&
217 # the failed refs should be indicated to the user
218 grep "^ ! .*rejected.* main -> main" output &&
220 # the collateral failure refs should be indicated to the user
221 grep "^ ! .*rejected.* atomic -> atomic .*atomic push failed" output &&
222 grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output &&
224 # never report what we do not push
225 ! grep "^ ! .*rejected.* atomic1 " output &&
226 ! grep "^ ! .*rejected.* other " output
229 test_expect_success 'push --atomic fails on server-side errors' '
230 # Use previously set up repository
231 d=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&
232 test_config -C "$d" http.receivepack true &&
233 up="$HTTPD_URL"/smart/atomic-branches.git &&
235 # break ref updates for other on the remote site
236 mkdir "$d/refs/heads/other.lock" &&
238 # add the new commit to other
239 git branch -f other collateral &&
241 # --atomic should cause entire push to be rejected
242 test_must_fail git push --atomic "$up" atomic other 2>output &&
244 # the new branch should not have been created upstream
245 test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
247 # upstream should still reflect atomic2, the last thing we pushed
248 # successfully
249 git rev-parse atomic2 >expected &&
250 # ...to other.
251 git -C "$d" rev-parse refs/heads/other >actual &&
252 test_cmp expected actual &&
254 # the new branch should not have been created upstream
255 test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
257 # the failed refs should be indicated to the user
258 grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
260 # the collateral failure refs should be indicated to the user
261 grep "^ ! .*rejected.* atomic -> atomic .*atomic transaction failed" output
264 test_expect_success 'push --all can push to empty repo' '
265 d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
266 git init --bare "$d" &&
267 git --git-dir="$d" config http.receivepack true &&
268 git push --all "$HTTPD_URL"/smart/empty-all.git
271 test_expect_success 'push --mirror can push to empty repo' '
272 d=$HTTPD_DOCUMENT_ROOT_PATH/empty-mirror.git &&
273 git init --bare "$d" &&
274 git --git-dir="$d" config http.receivepack true &&
275 git push --mirror "$HTTPD_URL"/smart/empty-mirror.git
278 test_expect_success 'push --all to repo with alternates' '
279 s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
280 d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-all.git &&
281 git clone --bare --shared "$s" "$d" &&
282 git --git-dir="$d" config http.receivepack true &&
283 git --git-dir="$d" repack -adl &&
284 git push --all "$HTTPD_URL"/smart/alternates-all.git
287 test_expect_success 'push --mirror to repo with alternates' '
288 s=$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git &&
289 d=$HTTPD_DOCUMENT_ROOT_PATH/alternates-mirror.git &&
290 git clone --bare --shared "$s" "$d" &&
291 git --git-dir="$d" config http.receivepack true &&
292 git --git-dir="$d" repack -adl &&
293 git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
296 test_expect_success TTY 'push shows progress when stderr is a tty' '
297 cd "$ROOT_PATH"/test_repo_clone &&
298 test_commit noisy &&
299 test_terminal git push >output 2>&1 &&
300 test_i18ngrep "^Writing objects" output
303 test_expect_success TTY 'push --quiet silences status and progress' '
304 cd "$ROOT_PATH"/test_repo_clone &&
305 test_commit quiet &&
306 test_terminal git push --quiet >output 2>&1 &&
307 test_must_be_empty output
310 test_expect_success TTY 'push --no-progress silences progress but not status' '
311 cd "$ROOT_PATH"/test_repo_clone &&
312 test_commit no-progress &&
313 test_terminal git push --no-progress >output 2>&1 &&
314 test_i18ngrep "^To http" output &&
315 test_i18ngrep ! "^Writing objects" output
318 test_expect_success 'push --progress shows progress to non-tty' '
319 cd "$ROOT_PATH"/test_repo_clone &&
320 test_commit progress &&
321 git push --progress >output 2>&1 &&
322 test_i18ngrep "^To http" output &&
323 test_i18ngrep "^Writing objects" output
326 test_expect_success 'http push gives sane defaults to reflog' '
327 cd "$ROOT_PATH"/test_repo_clone &&
328 test_commit reflog-test &&
329 git push "$HTTPD_URL"/smart/test_repo.git &&
330 git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
331 log -g -1 --format="%gn <%ge>" >actual &&
332 echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
333 test_cmp expect actual
336 test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
337 cd "$ROOT_PATH"/test_repo_clone &&
338 test_commit custom-reflog-test &&
339 git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
340 git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
341 log -g -1 --format="%gn <%ge>" >actual &&
342 echo "Custom User <custom@example.com>" >expect &&
343 test_cmp expect actual
346 test_expect_success 'push over smart http with auth' '
347 cd "$ROOT_PATH/test_repo_clone" &&
348 echo push-auth-test >expect &&
349 test_commit push-auth-test &&
350 set_askpass user@host pass@host &&
351 git push "$HTTPD_URL"/auth/smart/test_repo.git &&
352 git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
353 log -1 --format=%s >actual &&
354 expect_askpass both user@host &&
355 test_cmp expect actual
358 test_expect_success 'push to auth-only-for-push repo' '
359 cd "$ROOT_PATH/test_repo_clone" &&
360 echo push-half-auth >expect &&
361 test_commit push-half-auth &&
362 set_askpass user@host pass@host &&
363 git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
364 git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
365 log -1 --format=%s >actual &&
366 expect_askpass both user@host &&
367 test_cmp expect actual
370 test_expect_success 'create repo without http.receivepack set' '
371 cd "$ROOT_PATH" &&
372 git init half-auth &&
374 cd half-auth &&
375 test_commit one
376 ) &&
377 git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git"
380 test_expect_success 'clone via half-auth-complete does not need password' '
381 cd "$ROOT_PATH" &&
382 set_askpass wrong &&
383 git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \
384 half-auth-clone &&
385 expect_askpass none
388 test_expect_success 'push into half-auth-complete requires password' '
389 cd "$ROOT_PATH/half-auth-clone" &&
390 echo two >expect &&
391 test_commit two &&
392 set_askpass user@host pass@host &&
393 git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
394 git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
395 log -1 --format=%s >actual &&
396 expect_askpass both user@host &&
397 test_cmp expect actual
400 test_expect_success CMDLINE_LIMIT 'push 2000 tags over http' '
401 sha1=$(git rev-parse HEAD) &&
402 test_seq 2000 |
403 sort |
404 sed "s|.*|$sha1 refs/tags/really-long-tag-name-&|" \
405 >.git/packed-refs &&
406 run_with_limited_cmdline git push --mirror
409 test_expect_success GPG 'push with post-receive to inspect certificate' '
410 test_hook -C "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git post-receive <<-\EOF &&
411 # discard the update list
412 cat >/dev/null
413 # record the push certificate
414 if test -n "${GIT_PUSH_CERT-}"
415 then
416 git cat-file blob $GIT_PUSH_CERT >../push-cert
417 fi &&
418 cat >../push-cert-status <<E_O_F
419 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
420 KEY=${GIT_PUSH_CERT_KEY-nokey}
421 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
422 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
423 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
424 E_O_F
427 cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
428 git config receive.certnonceseed sekrit &&
429 git config receive.certnonceslop 30
430 ) &&
431 cd "$ROOT_PATH/test_repo_clone" &&
432 test_commit cert-test &&
433 git push --signed "$HTTPD_URL/smart/test_repo.git" &&
435 cd "$HTTPD_DOCUMENT_ROOT_PATH" &&
436 cat <<-\EOF &&
437 SIGNER=C O Mitter <committer@example.com>
438 KEY=13B6F51ECDDE430D
439 STATUS=G
440 NONCE_STATUS=OK
442 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" push-cert
443 ) >expect &&
444 test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH/push-cert-status"
447 test_expect_success 'push status output scrubs password' '
448 cd "$ROOT_PATH/test_repo_clone" &&
449 git push --porcelain \
450 "$HTTPD_URL_USER_PASS/smart/test_repo.git" \
451 +HEAD:scrub >status &&
452 # should have been scrubbed down to vanilla URL
453 grep "^To $HTTPD_URL/smart/test_repo.git" status
456 test_expect_success 'clone/fetch scrubs password from reflogs' '
457 cd "$ROOT_PATH" &&
458 git clone "$HTTPD_URL_USER_PASS/smart/test_repo.git" \
459 reflog-test &&
460 cd reflog-test &&
461 test_commit prepare-for-force-fetch &&
462 git switch -c away &&
463 git fetch "$HTTPD_URL_USER_PASS/smart/test_repo.git" \
464 +main:main &&
465 # should have been scrubbed down to vanilla URL
466 git log -g main >reflog &&
467 grep "$HTTPD_URL" reflog &&
468 ! grep "$HTTPD_URL_USER_PASS" reflog
471 test_expect_success 'Non-ASCII branch name can be used with --force-with-lease' '
472 cd "$ROOT_PATH" &&
473 git clone "$HTTPD_URL_USER_PASS/smart/test_repo.git" non-ascii &&
474 cd non-ascii &&
475 git checkout -b rama-de-árbol &&
476 test_commit F &&
477 git push --force-with-lease origin rama-de-árbol &&
478 git ls-remote origin refs/heads/rama-de-árbol >actual &&
479 git ls-remote . refs/heads/rama-de-árbol >expect &&
480 test_cmp expect actual &&
481 git push --delete --force-with-lease origin rama-de-árbol &&
482 git ls-remote origin refs/heads/rama-de-árbol >actual &&
483 test_must_be_empty actual
486 test_expect_success 'colorize errors/hints' '
487 cd "$ROOT_PATH"/test_repo_clone &&
488 test_must_fail git -c color.transport=always -c color.advice=always \
489 -c color.push=always \
490 push origin origin/main^:main 2>act &&
491 test_decode_color <act >decoded &&
492 test_i18ngrep "<RED>.*rejected.*<RESET>" decoded &&
493 test_i18ngrep "<RED>error: failed to push some refs" decoded &&
494 test_i18ngrep "<YELLOW>hint: " decoded &&
495 test_i18ngrep ! "^hint: " decoded
498 test_expect_success 'report error server does not provide ref status' '
499 git init "$HTTPD_DOCUMENT_ROOT_PATH/no_report" &&
500 git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" config http.receivepack true &&
501 test_must_fail git push --porcelain \
502 $HTTPD_URL_USER_PASS/smart/no_report \
503 HEAD:refs/tags/will-fail >actual &&
504 test_must_fail git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" \
505 rev-parse --verify refs/tags/will-fail &&
506 cat >expect <<-EOF &&
507 To $HTTPD_URL/smart/no_report
508 ! HEAD:refs/tags/will-fail [remote failure] (remote failed to report status)
509 Done
511 test_cmp expect actual
514 test_done