#!/bin/sh # The patch page serves mail ready patches, compared line for line against # git format-patch for a single commit, the root commit, a range and a # renamed file, once the CGI headers are stripped. Further checks cover the # headers, max-patch-count, a path, a merge in a range, the signature and the # format used when addresses are hidden. test_description='Check content on patch page' . ./setup.sh # The signature sits on the second-to-last line, above the trailing blank, # and names the version only when enable-version-info asks for it. check_cgit_signature() { tail -n 2 tmp | head -n 1 | grep -x -F "cgit${1:+ $1}" } test_expect_success 'a commit is patched as git format-patch writes it' ' cgit_ok_url "foo/patch" >tmp && git -C repos/foo format-patch --subject-prefix="" \ --signature="cgit" --stdout HEAD^ >expect && strip_headers actual && test_cmp expect actual ' # A commit is named as the range from its parent, which the root commit lacks. test_expect_success 'the patch is plain text named after the commit' ' id=$(git -C repos/foo rev-parse HEAD) && parent=$(git -C repos/foo rev-parse HEAD~1) && cgit_ok_url "foo/patch" >tmp && sed "/^$/q" tmp >headers && grep "^Content-Type: text/plain" headers && grep "^Content-Disposition: inline; filename=\"$parent..$id.patch\"$" headers ' test_expect_success 'the root commit is patched whole' ' root=$(git -C repos/foo rev-list --max-parents=0 HEAD) && cgit_ok_url "foo/patch&id=$root" >tmp && git -C repos/foo format-patch --root -1 --subject-prefix="" \ --signature="cgit" --stdout "$root" >expect && strip_headers actual && test_cmp expect actual && sed "/^$/q" tmp >headers && grep "^Content-Disposition: inline; filename=\"$root.patch\"$" headers ' test_expect_success 'a range matches git format-patch and names the range' ' id=$(git -C repos/foo rev-parse HEAD) && id2=$(git -C repos/foo rev-parse HEAD~3) && cgit_ok_url "foo/patch&id=$id&id2=$id2" >tmp && git -C repos/foo format-patch --subject-prefix="" \ -N --signature="cgit" --stdout HEAD~3..HEAD >expect && strip_headers actual && test_cmp expect actual && sed "/^$/q" tmp >headers && grep "^Content-Disposition: inline; filename=\"$id2..$id.patch\"$" headers ' # git cuts the walk short before it reverses it, so the newest commits are # the ones kept. test_expect_success 'max-patch-count keeps the newest commits of a range' ' root=$(git -C repos/foo rev-list --max-parents=0 HEAD) && cgitrc_repo patchcountrc foo max-patch-count=2 && cgit_ok patchcountrc "url=foo/patch&id2=$root" >tmp && grep "^Subject: " tmp >actual && test_write_lines "Subject: commit 4" "Subject: commit 5" >expect && test_cmp expect actual ' # The range below holds one commit more than the default bound. test_expect_success 'max-patch-count defaults to 50 and 0 lifts it' ' mkrepo repos/many 52 >/dev/null && root=$(git -C repos/many rev-list --max-parents=0 HEAD) && cgitrc_repo manyrc many && cgit_ok manyrc "url=many/patch&id2=$root" >tmp && test $(grep -c "^From [0-9a-f]* Mon Sep 17 00:00:00 2001$" tmp) -eq 50 && cgitrc_repo manyrc many max-patch-count=0 && cgit_ok manyrc "url=many/patch&id2=$root" >tmp && test $(grep -c "^From [0-9a-f]* Mon Sep 17 00:00:00 2001$" tmp) -eq 51 ' test_expect_success 'a path limits a range to the commits touching it' ' id2=$(git -C repos/foo rev-parse HEAD~3) && cgit_ok_url "foo/patch/file-4&id2=$id2" >tmp && grep "^Subject: " tmp >actual && test_write_lines "Subject: commit 4" >expect && test_cmp expect actual && grep "^(limited to .file-4.)$" tmp && ! grep "file-5" tmp ' # A merge has no single patch to give, so it drops out of a range, signature # and all, while the commits it brought in stay. test_expect_success 'a merge drops out of a range' ' mkrepo repos/merged 1 && ( cd repos/merged && git checkout -b side && echo side >side.txt && git add side.txt && git commit -m side && git checkout - && echo main >main.txt && git add main.txt && git commit -m main && git merge --no-ff -m merge side ) && root=$(git -C repos/merged rev-list --max-parents=0 HEAD) && cgitrc_repo mergedrc merged && cgit_ok mergedrc "url=merged/patch&id2=$root" >tmp && git -C repos/merged format-patch --subject-prefix="" \ -N --signature="cgit" --stdout "$root..HEAD" >expect && strip_headers actual && test_cmp expect actual && grep "^Subject: side$" actual && grep "^Subject: main$" actual ' # The version comes from the build, less the v of the tag, as git writes its # own. test_expect_success 'the signature names the version when asked to' ' version=$(sed -n "s/^CGIT_VERSION = v\{0,1\}//p" "$TEST_OUTPUT_DIRECTORY/../build/VERSION") && test -n "$version" && cgitrc_copy versionrc enable-version-info=1 && cgit_ok versionrc "url=foo/patch" >tmp && check_cgit_signature "$version" ' # Without plain email the patch is written from a format of cgit's own, whose # From line carries the name alone. test_expect_success 'enable-plain-email=0 leaves the address off the patch' ' cgitrc_copy plainrc enable-plain-email=0 && cgit_ok plainrc "url=foo/patch" >tmp && grep "^From: A U Thor$" tmp && grep "^Subject: commit 5$" tmp && ! grep "author@example.com" tmp ' # The old name of a file renamed in the commit joins the limit, so the patch # carries the rename rather than a file added whole. It matches git given both # names, but for the note naming the limit. test_expect_success 'a renamed file is patched as the rename' ' mkrepo_renamed repos/moved && cgitrc_repo movedrc moved && cgit_ok movedrc "url=moved/patch/new.txt" >tmp && git -C repos/moved format-patch -1 -M --subject-prefix="" \ --signature="cgit" --stdout HEAD -- new.txt old.txt >expect && strip_headers body && sed "/^(limited to .new.txt.)$/,/^$/d" body >actual && test_cmp expect actual && grep "^similarity index 100%$" actual && grep "^rename from old.txt$" actual ' test_done