summary refs log tree commit diff
path: root/www/git.causal.agency/cgit
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2022-02-14 22:44:50 -0500
committerJune McEnroe <june@causal.agency>2022-02-14 22:44:50 -0500
commit0f2436b6f949b6ed6d887df7849fd4626843735a (patch)
treec9ec947d216c117b31973b7c8f0527852f62d3a1 /www/git.causal.agency/cgit
parentNarrow filter (diff)
downloadsrc-0f2436b6f949b6ed6d887df7849fd4626843735a.tar.gz
src-0f2436b6f949b6ed6d887df7849fd4626843735a.zip
Remove cgit
My fork of cgit is now <https://git.causal.agency/cgit-pink/>.
Diffstat (limited to 'www/git.causal.agency/cgit')
-rw-r--r--www/git.causal.agency/cgit/.gitignore13
-rw-r--r--www/git.causal.agency/cgit/.mailmap10
-rw-r--r--www/git.causal.agency/cgit/AUTHORS13
-rw-r--r--www/git.causal.agency/cgit/COPYING339
-rw-r--r--www/git.causal.agency/cgit/Makefile168
-rw-r--r--www/git.causal.agency/cgit/README86
-rw-r--r--www/git.causal.agency/cgit/cache.c475
-rw-r--r--www/git.causal.agency/cgit/cache.h37
-rw-r--r--www/git.causal.agency/cgit/cgit.c1105
-rw-r--r--www/git.causal.agency/cgit/cgit.css877
-rw-r--r--www/git.causal.agency/cgit/cgit.h397
-rw-r--r--www/git.causal.agency/cgit/cgit.mk114
-rw-r--r--www/git.causal.agency/cgit/cgit.pngbin1366 -> 0 bytes
-rw-r--r--www/git.causal.agency/cgit/cgitrc.5.txt977
-rw-r--r--www/git.causal.agency/cgit/cmd.c208
-rw-r--r--www/git.causal.agency/cgit/cmd.h16
-rw-r--r--www/git.causal.agency/cgit/configfile.c90
-rw-r--r--www/git.causal.agency/cgit/configfile.h10
-rwxr-xr-xwww/git.causal.agency/cgit/contrib/hooks/post-receive.agefile19
-rw-r--r--www/git.causal.agency/cgit/filter.c222
-rwxr-xr-xwww/git.causal.agency/cgit/filters/about-formatting.sh27
-rwxr-xr-xwww/git.causal.agency/cgit/filters/commit-links.sh28
-rwxr-xr-xwww/git.causal.agency/cgit/filters/email-gravatar.py36
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/man2html4
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/md2html304
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/rst2html2
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/txt2html4
-rwxr-xr-xwww/git.causal.agency/cgit/filters/syntax-highlighting.py55
-rwxr-xr-xwww/git.causal.agency/cgit/filters/syntax-highlighting.sh121
-rwxr-xr-xwww/git.causal.agency/cgit/gen-version.sh20
-rw-r--r--www/git.causal.agency/cgit/html.c344
-rw-r--r--www/git.causal.agency/cgit/html.h37
-rw-r--r--www/git.causal.agency/cgit/parsing.c223
-rw-r--r--www/git.causal.agency/cgit/robots.txt4
-rw-r--r--www/git.causal.agency/cgit/scan-tree.c268
-rw-r--r--www/git.causal.agency/cgit/scan-tree.h2
-rw-r--r--www/git.causal.agency/cgit/shared.c579
-rw-r--r--www/git.causal.agency/cgit/tests/.gitignore2
-rw-r--r--www/git.causal.agency/cgit/tests/Makefile17
-rwxr-xr-xwww/git.causal.agency/cgit/tests/filters/dump.sh4
-rwxr-xr-xwww/git.causal.agency/cgit/tests/setup.sh161
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh45
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0010-validate-html.sh40
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0020-validate-cache.sh78
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0101-index.sh17
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0102-summary.sh25
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0103-log.sh24
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0104-tree.sh32
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0105-commit.sh36
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0106-diff.sh19
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0107-snapshot.sh205
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0108-patch.sh62
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0109-gitconfig.sh48
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0110-rawdiff.sh42
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0111-filter.sh43
-rwxr-xr-xwww/git.causal.agency/cgit/tests/valgrind/bin/cgit12
-rw-r--r--www/git.causal.agency/cgit/ui-atom.c158
-rw-r--r--www/git.causal.agency/cgit/ui-atom.h6
-rw-r--r--www/git.causal.agency/cgit/ui-blame.c306
-rw-r--r--www/git.causal.agency/cgit/ui-blame.h6
-rw-r--r--www/git.causal.agency/cgit/ui-blob.c182
-rw-r--r--www/git.causal.agency/cgit/ui-blob.h8
-rw-r--r--www/git.causal.agency/cgit/ui-clone.c126
-rw-r--r--www/git.causal.agency/cgit/ui-clone.h8
-rw-r--r--www/git.causal.agency/cgit/ui-commit.c148
-rw-r--r--www/git.causal.agency/cgit/ui-commit.h6
-rw-r--r--www/git.causal.agency/cgit/ui-diff.c505
-rw-r--r--www/git.causal.agency/cgit/ui-diff.h15
-rw-r--r--www/git.causal.agency/cgit/ui-log.c555
-rw-r--r--www/git.causal.agency/cgit/ui-log.h9
-rw-r--r--www/git.causal.agency/cgit/ui-patch.c98
-rw-r--r--www/git.causal.agency/cgit/ui-patch.h7
-rw-r--r--www/git.causal.agency/cgit/ui-plain.c207
-rw-r--r--www/git.causal.agency/cgit/ui-plain.h6
-rw-r--r--www/git.causal.agency/cgit/ui-refs.c219
-rw-r--r--www/git.causal.agency/cgit/ui-refs.h8
-rw-r--r--www/git.causal.agency/cgit/ui-repolist.c381
-rw-r--r--www/git.causal.agency/cgit/ui-repolist.h7
-rw-r--r--www/git.causal.agency/cgit/ui-shared.c1241
-rw-r--r--www/git.causal.agency/cgit/ui-shared.h87
-rw-r--r--www/git.causal.agency/cgit/ui-snapshot.c319
-rw-r--r--www/git.causal.agency/cgit/ui-snapshot.h7
-rw-r--r--www/git.causal.agency/cgit/ui-ssdiff.c420
-rw-r--r--www/git.causal.agency/cgit/ui-ssdiff.h25
-rw-r--r--www/git.causal.agency/cgit/ui-stats.c426
-rw-r--r--www/git.causal.agency/cgit/ui-stats.h28
-rw-r--r--www/git.causal.agency/cgit/ui-summary.c151
-rw-r--r--www/git.causal.agency/cgit/ui-summary.h7
-rw-r--r--www/git.causal.agency/cgit/ui-tag.c120
-rw-r--r--www/git.causal.agency/cgit/ui-tag.h6
-rw-r--r--www/git.causal.agency/cgit/ui-tree.c411
-rw-r--r--www/git.causal.agency/cgit/ui-tree.h6
92 files changed, 0 insertions, 14371 deletions
diff --git a/www/git.causal.agency/cgit/.gitignore b/www/git.causal.agency/cgit/.gitignore
deleted file mode 100644
index bca98fcf..00000000
--- a/www/git.causal.agency/cgit/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
-# Files I don't care to see in git-status/commit
-/cgit
-/git
-cgit.conf
-CGIT-CFLAGS
-VERSION
-cgitrc.5
-cgitrc.5.fo
-cgitrc.5.html
-cgitrc.5.pdf
-cgitrc.5.xml
-*.o
-*.d
diff --git a/www/git.causal.agency/cgit/.mailmap b/www/git.causal.agency/cgit/.mailmap
deleted file mode 100644
index 03b54796..00000000
--- a/www/git.causal.agency/cgit/.mailmap
+++ /dev/null
@@ -1,10 +0,0 @@
-Florian Pritz <bluewind@xinu.at> <bluewind@xssn.at>
-Harley Laue <losinggeneration@gmail.com> <losinggeneration@aim.com>
-John Keeping <john@keeping.me.uk> <john@metanate.com>
-Lars Hjemli <hjemli@gmail.com> <larsh@hal-2004.(none)>
-Lars Hjemli <hjemli@gmail.com> <larsh@hatman.(none)>
-Lars Hjemli <hjemli@gmail.com> <larsh@slackbox.hjemli.net>
-Lars Hjemli <hjemli@gmail.com> <larsh@slaptop.hjemli.net>
-Lukas Fleischer <lfleischer@lfos.de> <cgit@cryptocrack.de>
-Lukas Fleischer <lfleischer@lfos.de> <info@cryptocrack.de>
-Stefan Bühler <source@stbuehler.de> <lighttpd@stbuehler.de>
diff --git a/www/git.causal.agency/cgit/AUTHORS b/www/git.causal.agency/cgit/AUTHORS
deleted file mode 100644
index 031de338..00000000
--- a/www/git.causal.agency/cgit/AUTHORS
+++ /dev/null
@@ -1,13 +0,0 @@
-Maintainer:
-	Jason A. Donenfeld <Jason@zx2c4.com>
-
-Contributors:
-	Jason A. Donenfeld <Jason@zx2c4.com>
-	Lukas Fleischer <cgit@cryptocrack.de>
-	Johan Herland <johan@herland.net>
-	Lars Hjemli <hjemli@gmail.com>
-	Ferry Huberts <ferry.huberts@pelagic.nl>
-	John Keeping <john@keeping.me.uk>
-
-Previous Maintainer:
-	Lars Hjemli <hjemli@gmail.com>
diff --git a/www/git.causal.agency/cgit/COPYING b/www/git.causal.agency/cgit/COPYING
deleted file mode 100644
index d159169d..00000000
--- a/www/git.causal.agency/cgit/COPYING
+++ /dev/null
@@ -1,339 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                            NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
diff --git a/www/git.causal.agency/cgit/Makefile b/www/git.causal.agency/cgit/Makefile
deleted file mode 100644
index e5fb0675..00000000
--- a/www/git.causal.agency/cgit/Makefile
+++ /dev/null
@@ -1,168 +0,0 @@
-all::
-
-CGIT_VERSION = causal agency
-CGIT_SCRIPT_NAME = cgit.cgi
-CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
-CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
-CGIT_CONFIG = /etc/cgitrc
-CACHE_ROOT = /var/cache/cgit
-prefix = /usr/local
-libdir = $(prefix)/lib
-filterdir = $(libdir)/cgit/filters
-docdir = $(prefix)/share/doc/cgit
-htmldir = $(docdir)
-pdfdir = $(docdir)
-mandir = $(prefix)/share/man
-SHA1_HEADER = <openssl/sha.h>
-GIT_VER = 2.32.0
-GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.gz
-INSTALL = install
-COPYTREE = cp -r
-MAN5_TXT = $(wildcard *.5.txt)
-MAN_TXT  = $(MAN5_TXT)
-DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT))
-DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
-DOC_PDF  = $(patsubst %.txt,%.pdf,$(MAN_TXT))
-
-ASCIIDOC = asciidoc
-ASCIIDOC_EXTRA =
-ASCIIDOC_HTML = xhtml11
-ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA)
-TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
-
-# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
-# do not support the 'size specifiers' introduced by C99, namely ll, hh,
-# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
-# some C compilers supported these specifiers prior to C99 as an extension.
-#
-# Define HAVE_LINUX_SENDFILE to use sendfile()
-
-#-include config.mak
-
--include git/config.mak.uname
-#
-# Let the user override the above settings.
-#
--include cgit.conf
-
-export CGIT_VERSION CGIT_SCRIPT_NAME CGIT_SCRIPT_PATH CGIT_DATA_PATH CGIT_CONFIG CACHE_ROOT
-
-#
-# Define a way to invoke make in subdirs quietly, shamelessly ripped
-# from git.git
-#
-QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
-QUIET_SUBDIR1  =
-
-ifneq ($(findstring w,$(MAKEFLAGS)),w)
-PRINT_DIR = --no-print-directory
-else # "make -w"
-NO_SUBDIR = :
-endif
-
-ifndef V
-	QUIET_SUBDIR0  = +@subdir=
-	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
-			 $(MAKE) $(PRINT_DIR) -C $$subdir
-	QUIET_TAGS     = @echo '   ' TAGS $@;
-	export V
-endif
-
-.SUFFIXES:
-
-all:: cgit
-
-cgit:
-	$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk ../cgit $(EXTRA_GIT_TARGETS) NO_CURL=1
-
-sparse:
-	$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk NO_CURL=1 cgit-sparse
-
-test:
-	@$(MAKE) --no-print-directory cgit EXTRA_GIT_TARGETS=all
-	$(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
-
-install: all
-	$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH)
-	$(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
-	$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
-	$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
-	$(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
-	$(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
-	$(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
-	$(COPYTREE) filters/* $(DESTDIR)$(filterdir)
-
-install-doc: install-man install-html install-pdf
-
-install-man: doc-man
-	$(INSTALL) -m 0755 -d $(DESTDIR)$(mandir)/man5
-	$(INSTALL) -m 0644 $(DOC_MAN5) $(DESTDIR)$(mandir)/man5
-
-install-html: doc-html
-	$(INSTALL) -m 0755 -d $(DESTDIR)$(htmldir)
-	$(INSTALL) -m 0644 $(DOC_HTML) $(DESTDIR)$(htmldir)
-
-install-pdf: doc-pdf
-	$(INSTALL) -m 0755 -d $(DESTDIR)$(pdfdir)
-	$(INSTALL) -m 0644 $(DOC_PDF) $(DESTDIR)$(pdfdir)
-
-uninstall:
-	rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
-	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
-	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
-
-uninstall-doc: uninstall-man uninstall-html uninstall-pdf
-
-uninstall-man:
-	@for i in $(DOC_MAN5); do \
-	    rm -fv $(DESTDIR)$(mandir)/man5/$$i; \
-	done
-
-uninstall-html:
-	@for i in $(DOC_HTML); do \
-	    rm -fv $(DESTDIR)$(htmldir)/$$i; \
-	done
-
-uninstall-pdf:
-	@for i in $(DOC_PDF); do \
-	    rm -fv $(DESTDIR)$(pdfdir)/$$i; \
-	done
-
-doc: doc-man doc-html doc-pdf
-doc-man: doc-man5
-doc-man5: $(DOC_MAN5)
-doc-html: $(DOC_HTML)
-doc-pdf: $(DOC_PDF)
-
-%.5 : %.5.txt
-	a2x -f manpage $<
-
-$(DOC_HTML): %.html : %.txt
-	$(TXT_TO_HTML) -o $@+ $< && \
-	mv $@+ $@
-
-$(DOC_PDF): %.pdf : %.txt
-	a2x -f pdf cgitrc.5.txt
-
-clean: clean-doc
-	$(RM) cgit VERSION CGIT-CFLAGS *.o tags
-	$(RM) -r .deps
-
-cleanall: clean
-	$(MAKE) -C git clean
-
-clean-doc:
-	$(RM) cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
-
-get-git:
-	curl -L $(GIT_URL) | tar -xzf - && rm -rf git && mv git-$(GIT_VER) git
-
-tags:
-	$(QUIET_TAGS)find . -name '*.[ch]' | xargs ctags
-
-.PHONY: all cgit git get-git
-.PHONY: clean clean-doc cleanall
-.PHONY: doc doc-html doc-man doc-pdf
-.PHONY: install install-doc install-html install-man install-pdf
-.PHONY: tags test
-.PHONY: uninstall uninstall-doc uninstall-html uninstall-man uninstall-pdf
diff --git a/www/git.causal.agency/cgit/README b/www/git.causal.agency/cgit/README
deleted file mode 100644
index 371cf21f..00000000
--- a/www/git.causal.agency/cgit/README
+++ /dev/null
@@ -1,86 +0,0 @@
-cgit - CGI for Git
-==================
-
-This is an attempt to create a fast web interface for the Git SCM, using a
-built-in cache to decrease server I/O pressure.
-
-Installation
-------------
-
-Building cgit involves building a proper version of Git. How to do this
-depends on how you obtained the cgit sources:
-
-a) If you're working in a cloned cgit repository, you first need to
-initialize and update the Git submodule:
-
-    $ git submodule init     # register the Git submodule in .git/config
-    $ $EDITOR .git/config    # if you want to specify a different url for git
-    $ git submodule update   # clone/fetch and checkout correct git version
-
-b) If you're building from a cgit tarball, you can download a proper git
-version like this:
-
-    $ make get-git
-
-When either a) or b) has been performed, you can build and install cgit like
-this:
-
-    $ make
-    $ sudo make install
-
-This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You
-can configure this location (and a few other things) by providing a `cgit.conf`
-file (see the Makefile for details).
-
-
-Dependencies
-------------
-
-* libzip
-* libcrypto (OpenSSL)
-* libssl (OpenSSL)
-
-Apache configuration
---------------------
-
-A new `Directory` section must probably be added for cgit, possibly something
-like this:
-
-    <Directory "/var/www/htdocs/cgit/">
-        AllowOverride None
-        Options +ExecCGI
-        Order allow,deny
-        Allow from all
-    </Directory>
-
-
-Runtime configuration
----------------------
-
-The file `/etc/cgitrc` is read by cgit before handling a request. In addition
-to runtime parameters, this file may also contain a list of repositories
-displayed by cgit (see `cgitrc.5.txt` for further details).
-
-The cache
----------
-
-When cgit is invoked it looks for a cache file matching the request and
-returns it to the client. If no such cache file exists (or if it has expired),
-the content for the request is written into the proper cache file before the
-file is returned.
-
-If the cache file has expired but cgit is unable to obtain a lock for it, the
-stale cache file is returned to the client. This is done to favour page
-throughput over page freshness.
-
-The generated content contains the complete response to the client, including
-the HTTP headers `Modified` and `Expires`.
-
-Online presence
----------------
-
-* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/>
-
-* Patches, bug reports, discussions and support should go to the cgit
-  mailing list: <cgit@lists.zx2c4.com>. To sign up, visit
-  <https://lists.zx2c4.com/mailman/listinfo/cgit>
diff --git a/www/git.causal.agency/cgit/cache.c b/www/git.causal.agency/cgit/cache.c
deleted file mode 100644
index 578b73b0..00000000
--- a/www/git.causal.agency/cgit/cache.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/* cache.c: cache management
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- *
- *
- * The cache is just a directory structure where each file is a cache slot,
- * and each filename is based on the hash of some key (e.g. the cgit url).
- * Each file contains the full key followed by the cached content for that
- * key.
- *
- */
-
-#include "cgit.h"
-#include "cache.h"
-#include "html.h"
-#ifdef HAVE_LINUX_SENDFILE
-#include <sys/sendfile.h>
-#endif
-
-#define CACHE_BUFSIZE (1024 * 4)
-
-struct cache_slot {
-	const char *key;
-	size_t keylen;
-	int ttl;
-	cache_fill_fn fn;
-	int cache_fd;
-	int lock_fd;
-	int stdout_fd;
-	const char *cache_name;
-	const char *lock_name;
-	int match;
-	struct stat cache_st;
-	int bufsize;
-	char buf[CACHE_BUFSIZE];
-};
-
-/* Open an existing cache slot and fill the cache buffer with
- * (part of) the content of the cache file. Return 0 on success
- * and errno otherwise.
- */
-static int open_slot(struct cache_slot *slot)
-{
-	char *bufz;
-	ssize_t bufkeylen = -1;
-
-	slot->cache_fd = open(slot->cache_name, O_RDONLY);
-	if (slot->cache_fd == -1)
-		return errno;
-
-	if (fstat(slot->cache_fd, &slot->cache_st))
-		return errno;
-
-	slot->bufsize = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
-	if (slot->bufsize < 0)
-		return errno;
-
-	bufz = memchr(slot->buf, 0, slot->bufsize);
-	if (bufz)
-		bufkeylen = bufz - slot->buf;
-
-	if (slot->key)
-		slot->match = bufkeylen == slot->keylen &&
-		    !memcmp(slot->key, slot->buf, bufkeylen + 1);
-
-	return 0;
-}
-
-/* Close the active cache slot */
-static int close_slot(struct cache_slot *slot)
-{
-	int err = 0;
-	if (slot->cache_fd > 0) {
-		if (close(slot->cache_fd))
-			err = errno;
-		else
-			slot->cache_fd = -1;
-	}
-	return err;
-}
-
-/* Print the content of the active cache slot (but skip the key). */
-static int print_slot(struct cache_slot *slot)
-{
-#ifdef HAVE_LINUX_SENDFILE
-	off_t start_off;
-	int ret;
-
-	start_off = slot->keylen + 1;
-
-	do {
-		ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off,
-				slot->cache_st.st_size - start_off);
-		if (ret < 0) {
-			if (errno == EAGAIN || errno == EINTR)
-				continue;
-			return errno;
-		}
-		return 0;
-	} while (1);
-#else
-	ssize_t i, j;
-
-	i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
-	if (i != slot->keylen + 1)
-		return errno;
-
-	do {
-		i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
-		if (i > 0)
-			j = xwrite(STDOUT_FILENO, slot->buf, i);
-	} while (i > 0 && j == i);
-
-	if (i < 0 || j != i)
-		return errno;
-	else
-		return 0;
-#endif
-}
-
-/* Check if the slot has expired */
-static int is_expired(struct cache_slot *slot)
-{
-	if (slot->ttl < 0)
-		return 0;
-	else
-		return slot->cache_st.st_mtime + slot->ttl * 60 < time(NULL);
-}
-
-/* Check if the slot has been modified since we opened it.
- * NB: If stat() fails, we pretend the file is modified.
- */
-static int is_modified(struct cache_slot *slot)
-{
-	struct stat st;
-
-	if (stat(slot->cache_name, &st))
-		return 1;
-	return (st.st_ino != slot->cache_st.st_ino ||
-		st.st_mtime != slot->cache_st.st_mtime ||
-		st.st_size != slot->cache_st.st_size);
-}
-
-/* Close an open lockfile */
-static int close_lock(struct cache_slot *slot)
-{
-	int err = 0;
-	if (slot->lock_fd > 0) {
-		if (close(slot->lock_fd))
-			err = errno;
-		else
-			slot->lock_fd = -1;
-	}
-	return err;
-}
-
-/* Create a lockfile used to store the generated content for a cache
- * slot, and write the slot key + \0 into it.
- * Returns 0 on success and errno otherwise.
- */
-static int lock_slot(struct cache_slot *slot)
-{
-	struct flock lock = {
-		.l_type = F_WRLCK,
-		.l_whence = SEEK_SET,
-		.l_start = 0,
-		.l_len = 0,
-	};
-
-	slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT,
-			     S_IRUSR | S_IWUSR);
-	if (slot->lock_fd == -1)
-		return errno;
-	if (fcntl(slot->lock_fd, F_SETLK, &lock) < 0) {
-		int saved_errno = errno;
-		close(slot->lock_fd);
-		slot->lock_fd = -1;
-		return saved_errno;
-	}
-	if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0)
-		return errno;
-	return 0;
-}
-
-/* Release the current lockfile. If `replace_old_slot` is set the
- * lockfile replaces the old cache slot, otherwise the lockfile is
- * just deleted.
- */
-static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
-{
-	int err;
-
-	if (replace_old_slot)
-		err = rename(slot->lock_name, slot->cache_name);
-	else
-		err = unlink(slot->lock_name);
-
-	/* Restore stdout and close the temporary FD. */
-	if (slot->stdout_fd >= 0) {
-		dup2(slot->stdout_fd, STDOUT_FILENO);
-		close(slot->stdout_fd);
-		slot->stdout_fd = -1;
-	}
-
-	if (err)
-		return errno;
-
-	return 0;
-}
-
-/* Generate the content for the current cache slot by redirecting
- * stdout to the lock-fd and invoking the callback function
- */
-static int fill_slot(struct cache_slot *slot)
-{
-	/* Preserve stdout */
-	slot->stdout_fd = dup(STDOUT_FILENO);
-	if (slot->stdout_fd == -1)
-		return errno;
-
-	/* Redirect stdout to lockfile */
-	if (dup2(slot->lock_fd, STDOUT_FILENO) == -1)
-		return errno;
-
-	/* Generate cache content */
-	slot->fn();
-
-	/* Make sure any buffered data is flushed to the file */
-	if (fflush(stdout))
-		return errno;
-
-	/* update stat info */
-	if (fstat(slot->lock_fd, &slot->cache_st))
-		return errno;
-
-	return 0;
-}
-
-/* Crude implementation of 32-bit FNV-1 hash algorithm,
- * see http://www.isthe.com/chongo/tech/comp/fnv/ for details
- * about the magic numbers.
- */
-#define FNV_OFFSET 0x811c9dc5
-#define FNV_PRIME  0x01000193
-
-unsigned long hash_str(const char *str)
-{
-	unsigned long h = FNV_OFFSET;
-	unsigned char *s = (unsigned char *)str;
-
-	if (!s)
-		return h;
-
-	while (*s) {
-		h *= FNV_PRIME;
-		h ^= *s++;
-	}
-	return h;
-}
-
-static int process_slot(struct cache_slot *slot)
-{
-	int err;
-
-	/*
-	 * Make sure any buffered data is flushed before we redirect,
-	 * do sendfile(2) or write(2)
-	 */
-	if (fflush(stdout))
-		return errno;
-
-	err = open_slot(slot);
-	if (!err && slot->match) {
-		if (is_expired(slot)) {
-			if (!lock_slot(slot)) {
-				/* If the cachefile has been replaced between
-				 * `open_slot` and `lock_slot`, we'll just
-				 * serve the stale content from the original
-				 * cachefile. This way we avoid pruning the
-				 * newly generated slot. The same code-path
-				 * is chosen if fill_slot() fails for some
-				 * reason.
-				 *
-				 * TODO? check if the new slot contains the
-				 * same key as the old one, since we would
-				 * prefer to serve the newest content.
-				 * This will require us to open yet another
-				 * file-descriptor and read and compare the
-				 * key from the new file, so for now we're
-				 * lazy and just ignore the new file.
-				 */
-				if (is_modified(slot) || fill_slot(slot)) {
-					unlock_slot(slot, 0);
-					close_lock(slot);
-				} else {
-					close_slot(slot);
-					unlock_slot(slot, 1);
-					slot->cache_fd = slot->lock_fd;
-				}
-			}
-		}
-		if ((err = print_slot(slot)) != 0) {
-			cache_log("[cgit] error printing cache %s: %s (%d)\n",
-				  slot->cache_name,
-				  strerror(err),
-				  err);
-		}
-		close_slot(slot);
-		return err;
-	}
-
-	/* If the cache slot does not exist (or its key doesn't match the
-	 * current key), lets try to create a new cache slot for this
-	 * request. If this fails (for whatever reason), lets just generate
-	 * the content without caching it and fool the caller to believe
-	 * everything worked out (but print a warning on stdout).
-	 */
-
-	close_slot(slot);
-	if ((err = lock_slot(slot)) != 0) {
-		cache_log("[cgit] Unable to lock slot %s: %s (%d)\n",
-			  slot->lock_name, strerror(err), err);
-		slot->fn();
-		return 0;
-	}
-
-	if ((err = fill_slot(slot)) != 0) {
-		cache_log("[cgit] Unable to fill slot %s: %s (%d)\n",
-			  slot->lock_name, strerror(err), err);
-		unlock_slot(slot, 0);
-		close_lock(slot);
-		slot->fn();
-		return 0;
-	}
-	// We've got a valid cache slot in the lock file, which
-	// is about to replace the old cache slot. But if we
-	// release the lockfile and then try to open the new cache
-	// slot, we might get a race condition with a concurrent
-	// writer for the same cache slot (with a different key).
-	// Lets avoid such a race by just printing the content of
-	// the lock file.
-	slot->cache_fd = slot->lock_fd;
-	unlock_slot(slot, 1);
-	if ((err = print_slot(slot)) != 0) {
-		cache_log("[cgit] error printing cache %s: %s (%d)\n",
-			  slot->cache_name,
-			  strerror(err),
-			  err);
-	}
-	close_slot(slot);
-	return err;
-}
-
-/* Print cached content to stdout, generate the content if necessary. */
-int cache_process(int size, const char *path, const char *key, int ttl,
-		  cache_fill_fn fn)
-{
-	unsigned long hash;
-	int i;
-	struct strbuf filename = STRBUF_INIT;
-	struct strbuf lockname = STRBUF_INIT;
-	struct cache_slot slot;
-	int result;
-
-	/* If the cache is disabled, just generate the content */
-	if (size <= 0 || ttl == 0) {
-		fn();
-		return 0;
-	}
-
-	/* Verify input, calculate filenames */
-	if (!path) {
-		cache_log("[cgit] Cache path not specified, caching is disabled\n");
-		fn();
-		return 0;
-	}
-	if (!key)
-		key = "";
-	hash = hash_str(key) % size;
-	strbuf_addstr(&filename, path);
-	strbuf_ensure_end(&filename, '/');
-	for (i = 0; i < 8; i++) {
-		strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf));
-		hash >>= 4;
-	}
-	strbuf_addbuf(&lockname, &filename);
-	strbuf_addstr(&lockname, ".lock");
-	slot.fn = fn;
-	slot.ttl = ttl;
-	slot.stdout_fd = -1;
-	slot.cache_name = filename.buf;
-	slot.lock_name = lockname.buf;
-	slot.key = key;
-	slot.keylen = strlen(key);
-	result = process_slot(&slot);
-
-	strbuf_release(&filename);
-	strbuf_release(&lockname);
-	return result;
-}
-
-/* Return a strftime formatted date/time
- * NB: the result from this function is to shared memory
- */
-static char *sprintftime(const char *format, time_t time)
-{
-	static char buf[64];
-	struct tm tm;
-
-	if (!time)
-		return NULL;
-	gmtime_r(&time, &tm);
-	strftime(buf, sizeof(buf)-1, format, &tm);
-	return buf;
-}
-
-int cache_ls(const char *path)
-{
-	DIR *dir;
-	struct dirent *ent;
-	int err = 0;
-	struct cache_slot slot = { NULL };
-	struct strbuf fullname = STRBUF_INIT;
-	size_t prefixlen;
-
-	if (!path) {
-		cache_log("[cgit] cache path not specified\n");
-		return -1;
-	}
-	dir = opendir(path);
-	if (!dir) {
-		err = errno;
-		cache_log("[cgit] unable to open path %s: %s (%d)\n",
-			  path, strerror(err), err);
-		return err;
-	}
-	strbuf_addstr(&fullname, path);
-	strbuf_ensure_end(&fullname, '/');
-	prefixlen = fullname.len;
-	while ((ent = readdir(dir)) != NULL) {
-		if (strlen(ent->d_name) != 8)
-			continue;
-		strbuf_setlen(&fullname, prefixlen);
-		strbuf_addstr(&fullname, ent->d_name);
-		slot.cache_name = fullname.buf;
-		if ((err = open_slot(&slot)) != 0) {
-			cache_log("[cgit] unable to open path %s: %s (%d)\n",
-				  fullname.buf, strerror(err), err);
-			continue;
-		}
-		htmlf("%s %s %10"PRIuMAX" %s\n",
-		      fullname.buf,
-		      sprintftime("%Y-%m-%d %H:%M:%S",
-				  slot.cache_st.st_mtime),
-		      (uintmax_t)slot.cache_st.st_size,
-		      slot.buf);
-		close_slot(&slot);
-	}
-	closedir(dir);
-	strbuf_release(&fullname);
-	return 0;
-}
-
-/* Print a message to stdout */
-void cache_log(const char *format, ...)
-{
-	va_list args;
-	va_start(args, format);
-	vfprintf(stderr, format, args);
-	va_end(args);
-}
-
diff --git a/www/git.causal.agency/cgit/cache.h b/www/git.causal.agency/cgit/cache.h
deleted file mode 100644
index 470da4fc..00000000
--- a/www/git.causal.agency/cgit/cache.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Since git has it's own cache.h which we include,
- * lets test on CGIT_CACHE_H to avoid confusion
- */
-
-#ifndef CGIT_CACHE_H
-#define CGIT_CACHE_H
-
-typedef void (*cache_fill_fn)(void);
-
-
-/* Print cached content to stdout, generate the content if necessary.
- *
- * Parameters
- *   size    max number of cache files
- *   path    directory used to store cache files
- *   key     the key used to lookup cache files
- *   ttl     max cache time in seconds for this key
- *   fn      content generator function for this key
- *
- * Return value
- *   0 indicates success, everything else is an error
- */
-extern int cache_process(int size, const char *path, const char *key, int ttl,
-			 cache_fill_fn fn);
-
-
-/* List info about all cache entries on stdout */
-extern int cache_ls(const char *path);
-
-/* Print a message to stdout */
-__attribute__((format (printf,1,2)))
-extern void cache_log(const char *format, ...);
-
-extern unsigned long hash_str(const char *str);
-
-#endif /* CGIT_CACHE_H */
diff --git a/www/git.causal.agency/cgit/cgit.c b/www/git.causal.agency/cgit/cgit.c
deleted file mode 100644
index 642cac77..00000000
--- a/www/git.causal.agency/cgit/cgit.c
+++ /dev/null
@@ -1,1105 +0,0 @@
-/* cgit.c: cgi for the git scm
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "cache.h"
-#include "cmd.h"
-#include "configfile.h"
-#include "html.h"
-#include "ui-shared.h"
-#include "ui-stats.h"
-#include "ui-blob.h"
-#include "ui-summary.h"
-#include "scan-tree.h"
-
-const char *cgit_version = CGIT_VERSION;
-
-__attribute__((constructor))
-static void constructor_environment()
-{
-	/* Do not look in /etc/ for gitconfig and gitattributes. */
-	setenv("GIT_CONFIG_NOSYSTEM", "1", 1);
-	setenv("GIT_ATTR_NOSYSTEM", "1", 1);
-	unsetenv("HOME");
-	unsetenv("XDG_CONFIG_HOME");
-}
-
-static void add_mimetype(const char *name, const char *value)
-{
-	struct string_list_item *item;
-
-	item = string_list_insert(&ctx.cfg.mimetypes, name);
-	item->util = xstrdup(value);
-}
-
-static void process_cached_repolist(const char *path);
-
-static void repo_config(struct cgit_repo *repo, const char *name, const char *value)
-{
-	const char *path;
-	struct string_list_item *item;
-
-	if (!strcmp(name, "name"))
-		repo->name = xstrdup(value);
-	else if (!strcmp(name, "clone-url"))
-		repo->clone_url = xstrdup(value);
-	else if (!strcmp(name, "desc"))
-		repo->desc = xstrdup(value);
-	else if (!strcmp(name, "owner"))
-		repo->owner = xstrdup(value);
-	else if (!strcmp(name, "homepage"))
-		repo->homepage = xstrdup(value);
-	else if (!strcmp(name, "defbranch"))
-		repo->defbranch = xstrdup(value);
-	else if (!strcmp(name, "extra-head-content"))
-		repo->extra_head_content = xstrdup(value);
-	else if (!strcmp(name, "snapshots"))
-		repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
-	else if (!strcmp(name, "enable-blame"))
-		repo->enable_blame = atoi(value);
-	else if (!strcmp(name, "enable-commit-graph"))
-		repo->enable_commit_graph = atoi(value);
-	else if (!strcmp(name, "enable-log-filecount"))
-		repo->enable_log_filecount = atoi(value);
-	else if (!strcmp(name, "enable-log-linecount"))
-		repo->enable_log_linecount = atoi(value);
-	else if (!strcmp(name, "enable-remote-branches"))
-		repo->enable_remote_branches = atoi(value);
-	else if (!strcmp(name, "enable-subject-links"))
-		repo->enable_subject_links = atoi(value);
-	else if (!strcmp(name, "enable-html-serving"))
-		repo->enable_html_serving = atoi(value);
-	else if (!strcmp(name, "branch-sort")) {
-		if (!strcmp(value, "age"))
-			repo->branch_sort = 1;
-		if (!strcmp(value, "name"))
-			repo->branch_sort = 0;
-	} else if (!strcmp(name, "commit-sort")) {
-		if (!strcmp(value, "date"))
-			repo->commit_sort = 1;
-		if (!strcmp(value, "topo"))
-			repo->commit_sort = 2;
-	} else if (!strcmp(name, "max-stats"))
-		repo->max_stats = cgit_find_stats_period(value, NULL);
-	else if (!strcmp(name, "module-link"))
-		repo->module_link= xstrdup(value);
-	else if (skip_prefix(name, "module-link.", &path)) {
-		item = string_list_append(&repo->submodules, xstrdup(path));
-		item->util = xstrdup(value);
-	} else if (!strcmp(name, "section"))
-		repo->section = xstrdup(value);
-	else if (!strcmp(name, "snapshot-prefix"))
-		repo->snapshot_prefix = xstrdup(value);
-	else if (!strcmp(name, "readme") && value != NULL) {
-		if (repo->readme.items == ctx.cfg.readme.items)
-			memset(&repo->readme, 0, sizeof(repo->readme));
-		string_list_append(&repo->readme, xstrdup(value));
-	} else if (!strcmp(name, "logo") && value != NULL)
-		repo->logo = xstrdup(value);
-	else if (!strcmp(name, "logo-link") && value != NULL)
-		repo->logo_link = xstrdup(value);
-	else if (!strcmp(name, "hide"))
-		repo->hide = atoi(value);
-	else if (!strcmp(name, "ignore"))
-		repo->ignore = atoi(value);
-	else if (ctx.cfg.enable_filter_overrides) {
-		if (!strcmp(name, "about-filter"))
-			repo->about_filter = cgit_new_filter(value, ABOUT);
-		else if (!strcmp(name, "commit-filter"))
-			repo->commit_filter = cgit_new_filter(value, COMMIT);
-		else if (!strcmp(name, "source-filter"))
-			repo->source_filter = cgit_new_filter(value, SOURCE);
-		else if (!strcmp(name, "email-filter"))
-			repo->email_filter = cgit_new_filter(value, EMAIL);
-		else if (!strcmp(name, "owner-filter"))
-			repo->owner_filter = cgit_new_filter(value, OWNER);
-	}
-}
-
-static void config_cb(const char *name, const char *value)
-{
-	const char *arg;
-
-	if (!strcmp(name, "section"))
-		ctx.cfg.section = xstrdup(value);
-	else if (!strcmp(name, "repo.url"))
-		ctx.repo = cgit_add_repo(value);
-	else if (ctx.repo && !strcmp(name, "repo.path"))
-		ctx.repo->path = trim_end(value, '/');
-	else if (ctx.repo && skip_prefix(name, "repo.", &arg))
-		repo_config(ctx.repo, arg, value);
-	else if (!strcmp(name, "readme"))
-		string_list_append(&ctx.cfg.readme, xstrdup(value));
-	else if (!strcmp(name, "root-title"))
-		ctx.cfg.root_title = xstrdup(value);
-	else if (!strcmp(name, "root-desc"))
-		ctx.cfg.root_desc = xstrdup(value);
-	else if (!strcmp(name, "root-readme"))
-		ctx.cfg.root_readme = xstrdup(value);
-	else if (!strcmp(name, "css"))
-		ctx.cfg.css = xstrdup(value);
-	else if (!strcmp(name, "favicon"))
-		ctx.cfg.favicon = xstrdup(value);
-	else if (!strcmp(name, "footer"))
-		ctx.cfg.footer = xstrdup(value);
-	else if (!strcmp(name, "head-include"))
-		ctx.cfg.head_include = xstrdup(value);
-	else if (!strcmp(name, "header"))
-		ctx.cfg.header = xstrdup(value);
-	else if (!strcmp(name, "logo"))
-		ctx.cfg.logo = xstrdup(value);
-	else if (!strcmp(name, "logo-link"))
-		ctx.cfg.logo_link = xstrdup(value);
-	else if (!strcmp(name, "module-link"))
-		ctx.cfg.module_link = xstrdup(value);
-	else if (!strcmp(name, "strict-export"))
-		ctx.cfg.strict_export = xstrdup(value);
-	else if (!strcmp(name, "virtual-root"))
-		ctx.cfg.virtual_root = ensure_end(value, '/');
-	else if (!strcmp(name, "noplainemail"))
-		ctx.cfg.noplainemail = atoi(value);
-	else if (!strcmp(name, "noheader"))
-		ctx.cfg.noheader = atoi(value);
-	else if (!strcmp(name, "snapshots"))
-		ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
-	else if (!strcmp(name, "enable-filter-overrides"))
-		ctx.cfg.enable_filter_overrides = atoi(value);
-	else if (!strcmp(name, "enable-follow-links"))
-		ctx.cfg.enable_follow_links = atoi(value);
-	else if (!strcmp(name, "enable-http-clone"))
-		ctx.cfg.enable_http_clone = atoi(value);
-	else if (!strcmp(name, "enable-index-links"))
-		ctx.cfg.enable_index_links = atoi(value);
-	else if (!strcmp(name, "enable-index-owner"))
-		ctx.cfg.enable_index_owner = atoi(value);
-	else if (!strcmp(name, "enable-blame"))
-		ctx.cfg.enable_blame = atoi(value);
-	else if (!strcmp(name, "enable-commit-graph"))
-		ctx.cfg.enable_commit_graph = atoi(value);
-	else if (!strcmp(name, "enable-log-filecount"))
-		ctx.cfg.enable_log_filecount = atoi(value);
-	else if (!strcmp(name, "enable-log-linecount"))
-		ctx.cfg.enable_log_linecount = atoi(value);
-	else if (!strcmp(name, "enable-remote-branches"))
-		ctx.cfg.enable_remote_branches = atoi(value);
-	else if (!strcmp(name, "enable-subject-links"))
-		ctx.cfg.enable_subject_links = atoi(value);
-	else if (!strcmp(name, "enable-html-serving"))
-		ctx.cfg.enable_html_serving = atoi(value);
-	else if (!strcmp(name, "enable-tree-linenumbers"))
-		ctx.cfg.enable_tree_linenumbers = atoi(value);
-	else if (!strcmp(name, "enable-git-config"))
-		ctx.cfg.enable_git_config = atoi(value);
-	else if (!strcmp(name, "max-stats"))
-		ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
-	else if (!strcmp(name, "cache-size"))
-		ctx.cfg.cache_size = atoi(value);
-	else if (!strcmp(name, "cache-root"))
-		ctx.cfg.cache_root = xstrdup(expand_macros(value));
-	else if (!strcmp(name, "cache-root-ttl"))
-		ctx.cfg.cache_root_ttl = atoi(value);
-	else if (!strcmp(name, "cache-repo-ttl"))
-		ctx.cfg.cache_repo_ttl = atoi(value);
-	else if (!strcmp(name, "cache-scanrc-ttl"))
-		ctx.cfg.cache_scanrc_ttl = atoi(value);
-	else if (!strcmp(name, "cache-static-ttl"))
-		ctx.cfg.cache_static_ttl = atoi(value);
-	else if (!strcmp(name, "cache-dynamic-ttl"))
-		ctx.cfg.cache_dynamic_ttl = atoi(value);
-	else if (!strcmp(name, "cache-about-ttl"))
-		ctx.cfg.cache_about_ttl = atoi(value);
-	else if (!strcmp(name, "cache-snapshot-ttl"))
-		ctx.cfg.cache_snapshot_ttl = atoi(value);
-	else if (!strcmp(name, "case-sensitive-sort"))
-		ctx.cfg.case_sensitive_sort = atoi(value);
-	else if (!strcmp(name, "about-filter"))
-		ctx.cfg.about_filter = cgit_new_filter(value, ABOUT);
-	else if (!strcmp(name, "commit-filter"))
-		ctx.cfg.commit_filter = cgit_new_filter(value, COMMIT);
-	else if (!strcmp(name, "email-filter"))
-		ctx.cfg.email_filter = cgit_new_filter(value, EMAIL);
-	else if (!strcmp(name, "owner-filter"))
-		ctx.cfg.owner_filter = cgit_new_filter(value, OWNER);
-	else if (!strcmp(name, "auth-filter"))
-		ctx.cfg.auth_filter = cgit_new_filter(value, AUTH);
-	else if (!strcmp(name, "embedded"))
-		ctx.cfg.embedded = atoi(value);
-	else if (!strcmp(name, "max-atom-items"))
-		ctx.cfg.max_atom_items = atoi(value);
-	else if (!strcmp(name, "max-message-length"))
-		ctx.cfg.max_msg_len = atoi(value);
-	else if (!strcmp(name, "max-repodesc-length"))
-		ctx.cfg.max_repodesc_len = atoi(value);
-	else if (!strcmp(name, "max-blob-size"))
-		ctx.cfg.max_blob_size = atoi(value);
-	else if (!strcmp(name, "max-repo-count"))
-		ctx.cfg.max_repo_count = atoi(value);
-	else if (!strcmp(name, "max-commit-count"))
-		ctx.cfg.max_commit_count = atoi(value);
-	else if (!strcmp(name, "project-list"))
-		ctx.cfg.project_list = xstrdup(expand_macros(value));
-	else if (!strcmp(name, "scan-path"))
-		if (ctx.cfg.cache_size)
-			process_cached_repolist(expand_macros(value));
-		else if (ctx.cfg.project_list)
-			scan_projects(expand_macros(value),
-				      ctx.cfg.project_list, repo_config);
-		else
-			scan_tree(expand_macros(value), repo_config);
-	else if (!strcmp(name, "scan-hidden-path"))
-		ctx.cfg.scan_hidden_path = atoi(value);
-	else if (!strcmp(name, "section-from-path"))
-		ctx.cfg.section_from_path = atoi(value);
-	else if (!strcmp(name, "repository-sort"))
-		ctx.cfg.repository_sort = xstrdup(value);
-	else if (!strcmp(name, "section-sort"))
-		ctx.cfg.section_sort = atoi(value);
-	else if (!strcmp(name, "source-filter"))
-		ctx.cfg.source_filter = cgit_new_filter(value, SOURCE);
-	else if (!strcmp(name, "summary-log"))
-		ctx.cfg.summary_log = atoi(value);
-	else if (!strcmp(name, "summary-branches"))
-		ctx.cfg.summary_branches = atoi(value);
-	else if (!strcmp(name, "summary-tags"))
-		ctx.cfg.summary_tags = atoi(value);
-	else if (!strcmp(name, "side-by-side-diffs"))
-		ctx.cfg.difftype = atoi(value) ? DIFF_SSDIFF : DIFF_UNIFIED;
-	else if (!strcmp(name, "agefile"))
-		ctx.cfg.agefile = xstrdup(value);
-	else if (!strcmp(name, "mimetype-file"))
-		ctx.cfg.mimetype_file = xstrdup(value);
-	else if (!strcmp(name, "renamelimit"))
-		ctx.cfg.renamelimit = atoi(value);
-	else if (!strcmp(name, "remove-suffix"))
-		ctx.cfg.remove_suffix = atoi(value);
-	else if (!strcmp(name, "robots"))
-		ctx.cfg.robots = xstrdup(value);
-	else if (!strcmp(name, "clone-prefix"))
-		ctx.cfg.clone_prefix = xstrdup(value);
-	else if (!strcmp(name, "clone-url"))
-		ctx.cfg.clone_url = xstrdup(value);
-	else if (!strcmp(name, "local-time"))
-		ctx.cfg.local_time = atoi(value);
-	else if (!strcmp(name, "commit-sort")) {
-		if (!strcmp(value, "date"))
-			ctx.cfg.commit_sort = 1;
-		if (!strcmp(value, "topo"))
-			ctx.cfg.commit_sort = 2;
-	} else if (!strcmp(name, "branch-sort")) {
-		if (!strcmp(value, "age"))
-			ctx.cfg.branch_sort = 1;
-		if (!strcmp(value, "name"))
-			ctx.cfg.branch_sort = 0;
-	} else if (skip_prefix(name, "mimetype.", &arg))
-		add_mimetype(arg, value);
-	else if (!strcmp(name, "include"))
-		parse_configfile(expand_macros(value), config_cb);
-}
-
-static void querystring_cb(const char *name, const char *value)
-{
-	if (!value)
-		value = "";
-
-	if (!strcmp(name,"r")) {
-		ctx.qry.repo = xstrdup(value);
-		ctx.repo = cgit_get_repoinfo(value);
-	} else if (!strcmp(name, "p")) {
-		ctx.qry.page = xstrdup(value);
-	} else if (!strcmp(name, "url")) {
-		if (*value == '/')
-			value++;
-		ctx.qry.url = xstrdup(value);
-		cgit_parse_url(value);
-	} else if (!strcmp(name, "qt")) {
-		ctx.qry.grep = xstrdup(value);
-	} else if (!strcmp(name, "q")) {
-		ctx.qry.search = xstrdup(value);
-	} else if (!strcmp(name, "h")) {
-		ctx.qry.head = xstrdup(value);
-		ctx.qry.has_symref = 1;
-	} else if (!strcmp(name, "id")) {
-		ctx.qry.oid = xstrdup(value);
-		ctx.qry.has_oid = 1;
-	} else if (!strcmp(name, "id2")) {
-		ctx.qry.oid2 = xstrdup(value);
-		ctx.qry.has_oid = 1;
-	} else if (!strcmp(name, "ofs")) {
-		ctx.qry.ofs = atoi(value);
-	} else if (!strcmp(name, "path")) {
-		ctx.qry.path = trim_end(value, '/');
-	} else if (!strcmp(name, "name")) {
-		ctx.qry.name = xstrdup(value);
-	} else if (!strcmp(name, "s")) {
-		ctx.qry.sort = xstrdup(value);
-	} else if (!strcmp(name, "showmsg")) {
-		ctx.qry.showmsg = atoi(value);
-	} else if (!strcmp(name, "period")) {
-		ctx.qry.period = xstrdup(value);
-	} else if (!strcmp(name, "dt")) {
-		ctx.qry.difftype = atoi(value);
-		ctx.qry.has_difftype = 1;
-	} else if (!strcmp(name, "ss")) {
-		/* No longer generated, but there may be links out there. */
-		ctx.qry.difftype = atoi(value) ? DIFF_SSDIFF : DIFF_UNIFIED;
-		ctx.qry.has_difftype = 1;
-	} else if (!strcmp(name, "all")) {
-		ctx.qry.show_all = atoi(value);
-	} else if (!strcmp(name, "context")) {
-		ctx.qry.context = atoi(value);
-	} else if (!strcmp(name, "ignorews")) {
-		ctx.qry.ignorews = atoi(value);
-	} else if (!strcmp(name, "follow")) {
-		ctx.qry.follow = atoi(value);
-	}
-}
-
-static void prepare_context(void)
-{
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.cfg.agefile = "info/web/last-modified";
-	ctx.cfg.cache_size = 0;
-	ctx.cfg.cache_max_create_time = 5;
-	ctx.cfg.cache_root = CGIT_CACHE_ROOT;
-	ctx.cfg.cache_about_ttl = 15;
-	ctx.cfg.cache_snapshot_ttl = 5;
-	ctx.cfg.cache_repo_ttl = 5;
-	ctx.cfg.cache_root_ttl = 5;
-	ctx.cfg.cache_scanrc_ttl = 15;
-	ctx.cfg.cache_dynamic_ttl = 5;
-	ctx.cfg.cache_static_ttl = -1;
-	ctx.cfg.case_sensitive_sort = 1;
-	ctx.cfg.branch_sort = 0;
-	ctx.cfg.commit_sort = 0;
-	ctx.cfg.css = "/cgit.css";
-	ctx.cfg.logo = "/cgit.png";
-	ctx.cfg.favicon = NULL;
-	ctx.cfg.local_time = 0;
-	ctx.cfg.enable_http_clone = 1;
-	ctx.cfg.enable_index_owner = 1;
-	ctx.cfg.enable_tree_linenumbers = 1;
-	ctx.cfg.enable_git_config = 0;
-	ctx.cfg.max_repo_count = 50;
-	ctx.cfg.max_commit_count = 50;
-	ctx.cfg.max_lock_attempts = 5;
-	ctx.cfg.max_msg_len = 80;
-	ctx.cfg.max_repodesc_len = 80;
-	ctx.cfg.max_blob_size = 0;
-	ctx.cfg.max_stats = 0;
-	ctx.cfg.project_list = NULL;
-	ctx.cfg.renamelimit = -1;
-	ctx.cfg.remove_suffix = 0;
-	ctx.cfg.robots = "index, nofollow";
-	ctx.cfg.root_title = "Git repository browser";
-	ctx.cfg.root_desc = "a fast webinterface for the git dscm";
-	ctx.cfg.scan_hidden_path = 0;
-	ctx.cfg.script_name = CGIT_SCRIPT_NAME;
-	ctx.cfg.section = "";
-	ctx.cfg.repository_sort = "name";
-	ctx.cfg.section_sort = 1;
-	ctx.cfg.summary_branches = 10;
-	ctx.cfg.summary_log = 10;
-	ctx.cfg.summary_tags = 10;
-	ctx.cfg.max_atom_items = 10;
-	ctx.cfg.difftype = DIFF_UNIFIED;
-	ctx.env.cgit_config = getenv("CGIT_CONFIG");
-	ctx.env.http_host = getenv("HTTP_HOST");
-	ctx.env.https = getenv("HTTPS");
-	ctx.env.no_http = getenv("NO_HTTP");
-	ctx.env.path_info = getenv("PATH_INFO");
-	ctx.env.query_string = getenv("QUERY_STRING");
-	ctx.env.request_method = getenv("REQUEST_METHOD");
-	ctx.env.script_name = getenv("SCRIPT_NAME");
-	ctx.env.server_name = getenv("SERVER_NAME");
-	ctx.env.server_port = getenv("SERVER_PORT");
-	ctx.env.http_cookie = getenv("HTTP_COOKIE");
-	ctx.env.http_referer = getenv("HTTP_REFERER");
-	ctx.env.content_length = getenv("CONTENT_LENGTH") ? strtoul(getenv("CONTENT_LENGTH"), NULL, 10) : 0;
-	ctx.env.authenticated = 0;
-	ctx.page.mimetype = "text/html";
-	ctx.page.charset = PAGE_ENCODING;
-	ctx.page.filename = NULL;
-	ctx.page.size = 0;
-	ctx.page.modified = time(NULL);
-	ctx.page.expires = ctx.page.modified;
-	ctx.page.etag = NULL;
-	string_list_init(&ctx.cfg.mimetypes, 1);
-	if (ctx.env.script_name)
-		ctx.cfg.script_name = xstrdup(ctx.env.script_name);
-	if (ctx.env.query_string)
-		ctx.qry.raw = xstrdup(ctx.env.query_string);
-	if (!ctx.env.cgit_config)
-		ctx.env.cgit_config = CGIT_CONFIG;
-}
-
-struct refmatch {
-	char *req_ref;
-	char *first_ref;
-	int match;
-};
-
-static int find_current_ref(const char *refname, const struct object_id *oid,
-			    int flags, void *cb_data)
-{
-	struct refmatch *info;
-
-	info = (struct refmatch *)cb_data;
-	if (!strcmp(refname, info->req_ref))
-		info->match = 1;
-	if (!info->first_ref)
-		info->first_ref = xstrdup(refname);
-	return info->match;
-}
-
-static void free_refmatch_inner(struct refmatch *info)
-{
-	if (info->first_ref)
-		free(info->first_ref);
-}
-
-static char *find_default_branch(struct cgit_repo *repo)
-{
-	struct refmatch info;
-	char *ref;
-
-	info.req_ref = repo->defbranch;
-	info.first_ref = NULL;
-	info.match = 0;
-	for_each_branch_ref(find_current_ref, &info);
-	if (info.match)
-		ref = info.req_ref;
-	else
-		ref = info.first_ref;
-	if (ref)
-		ref = xstrdup(ref);
-	free_refmatch_inner(&info);
-
-	return ref;
-}
-
-static char *guess_defbranch(void)
-{
-	const char *ref, *refname;
-	struct object_id oid;
-
-	ref = resolve_ref_unsafe("HEAD", 0, &oid, NULL);
-	if (!ref || !skip_prefix(ref, "refs/heads/", &refname))
-		return "master";
-	return xstrdup(refname);
-}
-
-/* The caller must free filename and ref after calling this. */
-static inline void parse_readme(const char *readme, char **filename, char **ref, struct cgit_repo *repo)
-{
-	const char *colon;
-
-	*filename = NULL;
-	*ref = NULL;
-
-	if (!readme || !readme[0])
-		return;
-
-	/* Check if the readme is tracked in the git repo. */
-	colon = strchr(readme, ':');
-	if (colon && strlen(colon) > 1) {
-		/* If it starts with a colon, we want to use
-		 * the default branch */
-		if (colon == readme && repo->defbranch)
-			*ref = xstrdup(repo->defbranch);
-		else
-			*ref = xstrndup(readme, colon - readme);
-		readme = colon + 1;
-	}
-
-	/* Prepend repo path to relative readme path unless tracked. */
-	if (!(*ref) && readme[0] != '/')
-		*filename = fmtalloc("%s/%s", repo->path, readme);
-	else
-		*filename = xstrdup(readme);
-}
-static void choose_readme(struct cgit_repo *repo)
-{
-	int found;
-	char *filename, *ref;
-	struct string_list_item *entry;
-
-	if (!repo->readme.nr)
-		return;
-
-	found = 0;
-	for_each_string_list_item(entry, &repo->readme) {
-		parse_readme(entry->string, &filename, &ref, repo);
-		if (!filename) {
-			free(filename);
-			free(ref);
-			continue;
-		}
-		if (ref) {
-			if (cgit_ref_path_exists(filename, ref, 1)) {
-				found = 1;
-				break;
-			}
-		}
-		else if (!access(filename, R_OK)) {
-			found = 1;
-			break;
-		}
-		free(filename);
-		free(ref);
-	}
-	repo->readme.strdup_strings = 1;
-	string_list_clear(&repo->readme, 0);
-	repo->readme.strdup_strings = 0;
-	if (found)
-		string_list_append(&repo->readme, filename)->util = ref;
-}
-
-static void print_no_repo_clone_urls(const char *url)
-{
-        html("<tr><td><a rel='vcs-git' href='");
-        html_url_path(url);
-        html("' title='");
-        html_attr(ctx.repo->name);
-        html(" Git repository'>");
-        html_txt(url);
-        html("</a></td></tr>\n");
-}
-
-static void prepare_repo_env(int *nongit)
-{
-	/* The path to the git repository. */
-	setenv("GIT_DIR", ctx.repo->path, 1);
-
-	/* Setup the git directory and initialize the notes system. Both of these
-	 * load local configuration from the git repository, so we do them both while
-	 * the HOME variables are unset. */
-	setup_git_directory_gently(nongit);
-	load_display_notes(NULL);
-}
-
-static int prepare_repo_cmd(int nongit)
-{
-	struct object_id oid;
-	int rc;
-
-	if (nongit) {
-		const char *name = ctx.repo->name;
-		rc = errno;
-		ctx.page.title = fmtalloc("%s - %s", ctx.cfg.root_title,
-						"config error");
-		ctx.repo = NULL;
-		cgit_print_http_headers();
-		cgit_print_docstart();
-		cgit_print_pageheader();
-		cgit_print_error("Failed to open %s: %s", name,
-				 rc ? strerror(rc) : "Not a valid git repository");
-		cgit_print_docend();
-		return 1;
-	}
-	ctx.page.title = fmtalloc("%s - %s", ctx.repo->name, ctx.repo->desc);
-
-	if (!ctx.repo->defbranch)
-		ctx.repo->defbranch = guess_defbranch();
-
-	if (!ctx.qry.head) {
-		ctx.qry.nohead = 1;
-		ctx.qry.head = find_default_branch(ctx.repo);
-	}
-
-	if (!ctx.qry.head) {
-		cgit_print_http_headers();
-		cgit_print_docstart();
-		cgit_print_pageheader();
-		cgit_print_error("Repository seems to be empty");
-		if (!strcmp(ctx.qry.page, "summary")) {
-			html("<table class='list'><tr class='nohover'><td>&nbsp;</td></tr><tr class='nohover'><th class='left'>Clone</th></tr>\n");
-			cgit_prepare_repo_env(ctx.repo);
-			cgit_add_clone_urls(print_no_repo_clone_urls);
-			html("</table>\n");
-		}
-		cgit_print_docend();
-		return 1;
-	}
-
-	if (get_oid(ctx.qry.head, &oid)) {
-		char *old_head = ctx.qry.head;
-		ctx.qry.head = xstrdup(ctx.repo->defbranch);
-		cgit_print_error_page(404, "Not found",
-				"Invalid branch: %s", old_head);
-		free(old_head);
-		return 1;
-	}
-	string_list_sort(&ctx.repo->submodules);
-	cgit_prepare_repo_env(ctx.repo);
-	choose_readme(ctx.repo);
-	return 0;
-}
-
-static inline void open_auth_filter(const char *function)
-{
-	cgit_open_filter(ctx.cfg.auth_filter, function,
-		ctx.env.http_cookie ? ctx.env.http_cookie : "",
-		ctx.env.request_method ? ctx.env.request_method : "",
-		ctx.env.query_string ? ctx.env.query_string : "",
-		ctx.env.http_referer ? ctx.env.http_referer : "",
-		ctx.env.path_info ? ctx.env.path_info : "",
-		ctx.env.http_host ? ctx.env.http_host : "",
-		ctx.env.https ? ctx.env.https : "",
-		ctx.qry.repo ? ctx.qry.repo : "",
-		ctx.qry.page ? ctx.qry.page : "",
-		cgit_currentfullurl(),
-		cgit_loginurl());
-}
-
-/* We intentionally keep this rather small, instead of looping and
- * feeding it to the filter a couple bytes at a time. This way, the
- * filter itself does not need to handle any denial of service or
- * buffer bloat issues. If this winds up being too small, people
- * will complain on the mailing list, and we'll increase it as needed. */
-#define MAX_AUTHENTICATION_POST_BYTES 4096
-/* The filter is expected to spit out "Status: " and all headers. */
-static inline void authenticate_post(void)
-{
-	char buffer[MAX_AUTHENTICATION_POST_BYTES];
-	ssize_t len;
-
-	open_auth_filter("authenticate-post");
-	len = ctx.env.content_length;
-	if (len > MAX_AUTHENTICATION_POST_BYTES)
-		len = MAX_AUTHENTICATION_POST_BYTES;
-	if ((len = read(STDIN_FILENO, buffer, len)) < 0)
-		die_errno("Could not read POST from stdin");
-	if (fwrite(buffer, 1, len, stdout) < len)
-		die_errno("Could not write POST to stdout");
-	cgit_close_filter(ctx.cfg.auth_filter);
-	exit(0);
-}
-
-static inline void authenticate_cookie(void)
-{
-	/* If we don't have an auth_filter, consider all cookies valid, and thus return early. */
-	if (!ctx.cfg.auth_filter) {
-		ctx.env.authenticated = 1;
-		return;
-	}
-
-	/* If we're having something POST'd to /login, we're authenticating POST,
-	 * instead of the cookie, so call authenticate_post and bail out early.
-	 * This pattern here should match /?p=login with POST. */
-	if (ctx.env.request_method && ctx.qry.page && !ctx.repo && \
-	    !strcmp(ctx.env.request_method, "POST") && !strcmp(ctx.qry.page, "login")) {
-		authenticate_post();
-		return;
-	}
-
-	/* If we've made it this far, we're authenticating the cookie for real, so do that. */
-	open_auth_filter("authenticate-cookie");
-	ctx.env.authenticated = cgit_close_filter(ctx.cfg.auth_filter);
-}
-
-static void process_request(void)
-{
-	struct cgit_cmd *cmd;
-	int nongit = 0;
-
-	/* If we're not yet authenticated, no matter what page we're on,
-	 * display the authentication body from the auth_filter. This should
-	 * never be cached. */
-	if (!ctx.env.authenticated) {
-		ctx.page.title = "Authentication Required";
-		cgit_print_http_headers();
-		cgit_print_docstart();
-		cgit_print_pageheader();
-		open_auth_filter("body");
-		cgit_close_filter(ctx.cfg.auth_filter);
-		cgit_print_docend();
-		return;
-	}
-
-	if (ctx.repo)
-		prepare_repo_env(&nongit);
-
-	cmd = cgit_get_cmd();
-	if (!cmd) {
-		ctx.page.title = "cgit error";
-		cgit_print_error_page(404, "Not found", "Invalid request");
-		return;
-	}
-
-	if (!ctx.cfg.enable_http_clone && cmd->is_clone) {
-		ctx.page.title = "cgit error";
-		cgit_print_error_page(404, "Not found", "Invalid request");
-		return;
-	}
-
-	if (cmd->want_repo && !ctx.repo) {
-		cgit_print_error_page(400, "Bad request",
-				"No repository selected");
-		return;
-	}
-
-	/* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual"
-	 * in-project path limit to be made available at ctx.qry.vpath.
-	 * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL).
-	 */
-	ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL;
-
-	if (ctx.repo && prepare_repo_cmd(nongit))
-		return;
-
-	cmd->fn();
-}
-
-static int cmp_repos(const void *a, const void *b)
-{
-	const struct cgit_repo *ra = a, *rb = b;
-	return strcmp(ra->url, rb->url);
-}
-
-static char *build_snapshot_setting(int bitmap)
-{
-	const struct cgit_snapshot_format *f;
-	struct strbuf result = STRBUF_INIT;
-
-	for (f = cgit_snapshot_formats; f->suffix; f++) {
-		if (cgit_snapshot_format_bit(f) & bitmap) {
-			if (result.len)
-				strbuf_addch(&result, ' ');
-			strbuf_addstr(&result, f->suffix);
-		}
-	}
-	return strbuf_detach(&result, NULL);
-}
-
-static char *get_first_line(char *txt)
-{
-	char *t = xstrdup(txt);
-	char *p = strchr(t, '\n');
-	if (p)
-		*p = '\0';
-	return t;
-}
-
-static void print_repo(FILE *f, struct cgit_repo *repo)
-{
-	struct string_list_item *item;
-	fprintf(f, "repo.url=%s\n", repo->url);
-	fprintf(f, "repo.name=%s\n", repo->name);
-	fprintf(f, "repo.path=%s\n", repo->path);
-	if (repo->owner)
-		fprintf(f, "repo.owner=%s\n", repo->owner);
-	if (repo->desc) {
-		char *tmp = get_first_line(repo->desc);
-		fprintf(f, "repo.desc=%s\n", tmp);
-		free(tmp);
-	}
-	for_each_string_list_item(item, &repo->readme) {
-		if (item->util)
-			fprintf(f, "repo.readme=%s:%s\n", (char *)item->util, item->string);
-		else
-			fprintf(f, "repo.readme=%s\n", item->string);
-	}
-	if (repo->defbranch)
-		fprintf(f, "repo.defbranch=%s\n", repo->defbranch);
-	if (repo->extra_head_content)
-		fprintf(f, "repo.extra-head-content=%s\n", repo->extra_head_content);
-	if (repo->module_link)
-		fprintf(f, "repo.module-link=%s\n", repo->module_link);
-	if (repo->section)
-		fprintf(f, "repo.section=%s\n", repo->section);
-	if (repo->homepage)
-		fprintf(f, "repo.homepage=%s\n", repo->homepage);
-	if (repo->clone_url)
-		fprintf(f, "repo.clone-url=%s\n", repo->clone_url);
-	fprintf(f, "repo.enable-blame=%d\n",
-	        repo->enable_blame);
-	fprintf(f, "repo.enable-commit-graph=%d\n",
-	        repo->enable_commit_graph);
-	fprintf(f, "repo.enable-log-filecount=%d\n",
-	        repo->enable_log_filecount);
-	fprintf(f, "repo.enable-log-linecount=%d\n",
-	        repo->enable_log_linecount);
-	if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter)
-		cgit_fprintf_filter(repo->about_filter, f, "repo.about-filter=");
-	if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter)
-		cgit_fprintf_filter(repo->commit_filter, f, "repo.commit-filter=");
-	if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
-		cgit_fprintf_filter(repo->source_filter, f, "repo.source-filter=");
-	if (repo->email_filter && repo->email_filter != ctx.cfg.email_filter)
-		cgit_fprintf_filter(repo->email_filter, f, "repo.email-filter=");
-	if (repo->owner_filter && repo->owner_filter != ctx.cfg.owner_filter)
-		cgit_fprintf_filter(repo->owner_filter, f, "repo.owner-filter=");
-	if (repo->snapshots != ctx.cfg.snapshots) {
-		char *tmp = build_snapshot_setting(repo->snapshots);
-		fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : "");
-		free(tmp);
-	}
-	if (repo->snapshot_prefix)
-		fprintf(f, "repo.snapshot-prefix=%s\n", repo->snapshot_prefix);
-	if (repo->max_stats != ctx.cfg.max_stats)
-		fprintf(f, "repo.max-stats=%s\n",
-		        cgit_find_stats_periodname(repo->max_stats));
-	if (repo->logo)
-		fprintf(f, "repo.logo=%s\n", repo->logo);
-	if (repo->logo_link)
-		fprintf(f, "repo.logo-link=%s\n", repo->logo_link);
-	fprintf(f, "repo.enable-remote-branches=%d\n", repo->enable_remote_branches);
-	fprintf(f, "repo.enable-subject-links=%d\n", repo->enable_subject_links);
-	fprintf(f, "repo.enable-html-serving=%d\n", repo->enable_html_serving);
-	if (repo->branch_sort == 1)
-		fprintf(f, "repo.branch-sort=age\n");
-	if (repo->commit_sort) {
-		if (repo->commit_sort == 1)
-			fprintf(f, "repo.commit-sort=date\n");
-		else if (repo->commit_sort == 2)
-			fprintf(f, "repo.commit-sort=topo\n");
-	}
-	fprintf(f, "repo.hide=%d\n", repo->hide);
-	fprintf(f, "repo.ignore=%d\n", repo->ignore);
-	fprintf(f, "\n");
-}
-
-static void print_repolist(FILE *f, struct cgit_repolist *list, int start)
-{
-	int i;
-
-	for (i = start; i < list->count; i++)
-		print_repo(f, &list->repos[i]);
-}
-
-/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
- * and return 0 on success.
- */
-static int generate_cached_repolist(const char *path, const char *cached_rc)
-{
-	struct strbuf locked_rc = STRBUF_INIT;
-	int result = 0;
-	int idx;
-	FILE *f;
-
-	strbuf_addf(&locked_rc, "%s.lock", cached_rc);
-	f = fopen(locked_rc.buf, "wx");
-	if (!f) {
-		/* Inform about the error unless the lockfile already existed,
-		 * since that only means we've got concurrent requests.
-		 */
-		result = errno;
-		if (result != EEXIST)
-			fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
-				locked_rc.buf, strerror(result), result);
-		goto out;
-	}
-	idx = cgit_repolist.count;
-	if (ctx.cfg.project_list)
-		scan_projects(path, ctx.cfg.project_list, repo_config);
-	else
-		scan_tree(path, repo_config);
-	print_repolist(f, &cgit_repolist, idx);
-	if (rename(locked_rc.buf, cached_rc))
-		fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
-			locked_rc.buf, cached_rc, strerror(errno), errno);
-	fclose(f);
-out:
-	strbuf_release(&locked_rc);
-	return result;
-}
-
-static void process_cached_repolist(const char *path)
-{
-	struct stat st;
-	struct strbuf cached_rc = STRBUF_INIT;
-	time_t age;
-	unsigned long hash;
-
-	hash = hash_str(path);
-	if (ctx.cfg.project_list)
-		hash += hash_str(ctx.cfg.project_list);
-	strbuf_addf(&cached_rc, "%s/rc-%8lx", ctx.cfg.cache_root, hash);
-
-	if (stat(cached_rc.buf, &st)) {
-		/* Nothing is cached, we need to scan without forking. And
-		 * if we fail to generate a cached repolist, we need to
-		 * invoke scan_tree manually.
-		 */
-		if (generate_cached_repolist(path, cached_rc.buf)) {
-			if (ctx.cfg.project_list)
-				scan_projects(path, ctx.cfg.project_list,
-					      repo_config);
-			else
-				scan_tree(path, repo_config);
-		}
-		goto out;
-	}
-
-	parse_configfile(cached_rc.buf, config_cb);
-
-	/* If the cached configfile hasn't expired, lets exit now */
-	age = time(NULL) - st.st_mtime;
-	if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
-		goto out;
-
-	/* The cached repolist has been parsed, but it was old. So lets
-	 * rescan the specified path and generate a new cached repolist
-	 * in a child-process to avoid latency for the current request.
-	 */
-	if (fork())
-		goto out;
-
-	exit(generate_cached_repolist(path, cached_rc.buf));
-out:
-	strbuf_release(&cached_rc);
-}
-
-static void cgit_parse_args(int argc, const char **argv)
-{
-	int i;
-	const char *arg;
-	int scan = 0;
-
-	for (i = 1; i < argc; i++) {
-		if (!strcmp(argv[i], "--version")) {
-			printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION);
-#ifndef HAVE_LINUX_SENDFILE
-			printf("[-] ");
-#else
-			printf("[+] ");
-#endif
-			printf("Linux sendfile() usage\n");
-
-			exit(0);
-		}
-		if (skip_prefix(argv[i], "--cache=", &arg)) {
-			ctx.cfg.cache_root = xstrdup(arg);
-		} else if (!strcmp(argv[i], "--nohttp")) {
-			ctx.env.no_http = "1";
-		} else if (skip_prefix(argv[i], "--query=", &arg)) {
-			ctx.qry.raw = xstrdup(arg);
-		} else if (skip_prefix(argv[i], "--repo=", &arg)) {
-			ctx.qry.repo = xstrdup(arg);
-		} else if (skip_prefix(argv[i], "--page=", &arg)) {
-			ctx.qry.page = xstrdup(arg);
-		} else if (skip_prefix(argv[i], "--head=", &arg)) {
-			ctx.qry.head = xstrdup(arg);
-			ctx.qry.has_symref = 1;
-		} else if (skip_prefix(argv[i], "--oid=", &arg)) {
-			ctx.qry.oid = xstrdup(arg);
-			ctx.qry.has_oid = 1;
-		} else if (skip_prefix(argv[i], "--ofs=", &arg)) {
-			ctx.qry.ofs = atoi(arg);
-		} else if (skip_prefix(argv[i], "--scan-tree=", &arg) ||
-		           skip_prefix(argv[i], "--scan-path=", &arg)) {
-			/*
-			 * HACK: The global snapshot bit mask defines the set
-			 * of allowed snapshot formats, but the config file
-			 * hasn't been parsed yet so the mask is currently 0.
-			 * By setting all bits high before scanning we make
-			 * sure that any in-repo cgitrc snapshot setting is
-			 * respected by scan_tree().
-			 *
-			 * NOTE: We assume that there aren't more than 8
-			 * different snapshot formats supported by cgit...
-			 */
-			ctx.cfg.snapshots = 0xFF;
-			scan++;
-			scan_tree(arg, repo_config);
-		}
-	}
-	if (scan) {
-		qsort(cgit_repolist.repos, cgit_repolist.count,
-			sizeof(struct cgit_repo), cmp_repos);
-		print_repolist(stdout, &cgit_repolist, 0);
-		exit(0);
-	}
-}
-
-static int calc_ttl(void)
-{
-	if (!ctx.repo)
-		return ctx.cfg.cache_root_ttl;
-
-	if (!ctx.qry.page)
-		return ctx.cfg.cache_repo_ttl;
-
-	if (!strcmp(ctx.qry.page, "about"))
-		return ctx.cfg.cache_about_ttl;
-
-	if (!strcmp(ctx.qry.page, "snapshot"))
-		return ctx.cfg.cache_snapshot_ttl;
-
-	if (ctx.qry.has_oid)
-		return ctx.cfg.cache_static_ttl;
-
-	if (ctx.qry.has_symref)
-		return ctx.cfg.cache_dynamic_ttl;
-
-	return ctx.cfg.cache_repo_ttl;
-}
-
-int cmd_main(int argc, const char **argv)
-{
-	const char *path;
-	int err, ttl;
-
-	atexit(cgit_cleanup_filters);
-
-	prepare_context();
-	cgit_repolist.length = 0;
-	cgit_repolist.count = 0;
-	cgit_repolist.repos = NULL;
-
-	cgit_parse_args(argc, argv);
-	parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
-	ctx.repo = NULL;
-	http_parse_querystring(ctx.qry.raw, querystring_cb);
-
-	/* If virtual-root isn't specified in cgitrc, lets pretend
-	 * that virtual-root equals SCRIPT_NAME, minus any possibly
-	 * trailing slashes.
-	 */
-	if (!ctx.cfg.virtual_root && ctx.cfg.script_name)
-		ctx.cfg.virtual_root = ensure_end(ctx.cfg.script_name, '/');
-
-	/* If no url parameter is specified on the querystring, lets
-	 * use PATH_INFO as url. This allows cgit to work with virtual
-	 * urls without the need for rewriterules in the webserver (as
-	 * long as PATH_INFO is included in the cache lookup key).
-	 */
-	path = ctx.env.path_info;
-	if (!ctx.qry.url && path) {
-		if (path[0] == '/')
-			path++;
-		ctx.qry.url = xstrdup(path);
-		if (ctx.qry.raw) {
-			char *newqry = fmtalloc("%s?%s", path, ctx.qry.raw);
-			free(ctx.qry.raw);
-			ctx.qry.raw = newqry;
-		} else
-			ctx.qry.raw = xstrdup(ctx.qry.url);
-		cgit_parse_url(ctx.qry.url);
-	}
-
-	/* Before we go any further, we set ctx.env.authenticated by checking to see
-	 * if the supplied cookie is valid. All cookies are valid if there is no
-	 * auth_filter. If there is an auth_filter, the filter decides. */
-	authenticate_cookie();
-
-	ttl = calc_ttl();
-	if (ttl < 0)
-		ctx.page.expires += 10 * 365 * 24 * 60 * 60; /* 10 years */
-	else
-		ctx.page.expires += ttl * 60;
-	if (!ctx.env.authenticated || (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")))
-		ctx.cfg.cache_size = 0;
-	err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
-			    ctx.qry.raw, ttl, process_request);
-	cgit_cleanup_filters();
-	if (err)
-		cgit_print_error("Error processing page: %s (%d)",
-				 strerror(err), err);
-	return err;
-}
diff --git a/www/git.causal.agency/cgit/cgit.css b/www/git.causal.agency/cgit/cgit.css
deleted file mode 100644
index f3dbb7a9..00000000
--- a/www/git.causal.agency/cgit/cgit.css
+++ /dev/null
@@ -1,877 +0,0 @@
-div#cgit {
-	padding: 0em;
-	margin: 0em;
-	font-family: sans-serif;
-	font-size: 10pt;
-	color: #333;
-	background: white;
-	padding: 4px;
-}
-
-div#cgit a {
-	color: blue;
-	text-decoration: none;
-}
-
-div#cgit a:hover {
-	text-decoration: underline;
-}
-
-div#cgit table {
-	border-collapse: collapse;
-}
-
-div#cgit table#header {
-	width: 100%;
-	margin-bottom: 1em;
-}
-
-div#cgit table#header td.logo {
-	width: 96px;
-	vertical-align: top;
-}
-
-div#cgit table#header td.main {
-	font-size: 250%;
-	padding-left: 10px;
-	white-space: nowrap;
-}
-
-div#cgit table#header td.main a {
-	color: #000;
-}
-
-div#cgit table#header td.form {
-	text-align: right;
-	vertical-align: bottom;
-	padding-right: 1em;
-	padding-bottom: 2px;
-	white-space: nowrap;
-}
-
-div#cgit table#header td.form form,
-div#cgit table#header td.form input,
-div#cgit table#header td.form select {
-	font-size: 90%;
-}
-
-div#cgit table#header td.sub {
-	color: #777;
-	border-top: solid 1px #ccc;
-	padding-left: 10px;
-}
-
-div#cgit table.tabs {
-	border-bottom: solid 3px #ccc;
-	border-collapse: collapse;
-	margin-top: 2em;
-	margin-bottom: 0px;
-	width: 100%;
-}
-
-div#cgit table.tabs td {
-	padding: 0px 1em;
-	vertical-align: bottom;
-}
-
-div#cgit table.tabs td a {
-	padding: 2px 0.25em;
-	color: #777;
-	font-size: 110%;
-}
-
-div#cgit table.tabs td a.active {
-	color: #000;
-	background-color: #ccc;
-}
-
-div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after {
-	content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhcJDQY+gm2TAAAAHWlUWHRDb21tZW50AAAAAABDcmVhdGVkIHdpdGggR0lNUGQuZQcAAABbSURBVAhbY2BABs4MU4CwhYHBh2Erww4wrGFQZHjI8B8IgUIscJWyDHcggltQhI4zGDCcRwhChPggHIggP1QoAVmQkSETrGoHsiAEsACtBYN0oDAMbgU6EBcAAL2eHUt4XUU4AAAAAElFTkSuQmCC);
-	opacity: 0.5;
-	margin: 0 0 0 5px;
-}
-
-div#cgit table.tabs td.form {
-	text-align: right;
-}
-
-div#cgit table.tabs td.form form {
-	padding-bottom: 2px;
-	font-size: 90%;
-	white-space: nowrap;
-}
-
-div#cgit table.tabs td.form input,
-div#cgit table.tabs td.form select {
-	font-size: 90%;
-}
-
-div#cgit div.path {
-	margin: 0px;
-	padding: 5px 2em 2px 2em;
-	color: #000;
-	background-color: #eee;
-}
-
-div#cgit div.content {
-	margin: 0px;
-	padding: 2em;
-	border-bottom: solid 3px #ccc;
-}
-
-
-div#cgit table.list {
-	width: 100%;
-	border: none;
-	border-collapse: collapse;
-}
-
-div#cgit table.list tr {
-	background: white;
-}
-
-div#cgit table.list tr.logheader {
-	background: #eee;
-}
-
-div#cgit table.list tr:nth-child(even) {
-	background: #f7f7f7;
-}
-
-div#cgit table.list tr:nth-child(odd) {
-	background: white;
-}
-
-div#cgit table.list tr:hover {
-	background: #eee;
-}
-
-div#cgit table.list tr.nohover {
-	background: white;
-}
-
-div#cgit table.list tr.nohover:hover {
-	background: white;
-}
-
-div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
-	background: #f7f7f7;
-}
-
-div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) {
-	background: white;
-}
-
-div#cgit table.list th {
-	font-weight: bold;
-	/* color: #888;
-	border-top: dashed 1px #888;
-	border-bottom: dashed 1px #888;
-	*/
-	padding: 0.1em 0.5em 0.05em 0.5em;
-	vertical-align: baseline;
-}
-
-div#cgit table.list td {
-	border: none;
-	padding: 0.1em 0.5em 0.1em 0.5em;
-}
-
-div#cgit table.list td.commitgraph {
-	font-family: monospace;
-	white-space: pre;
-}
-
-div#cgit table.list td.commitgraph .column1 {
-	color: #a00;
-}
-
-div#cgit table.list td.commitgraph .column2 {
-	color: #0a0;
-}
-
-div#cgit table.list td.commitgraph .column3 {
-	color: #aa0;
-}
-
-div#cgit table.list td.commitgraph .column4 {
-	color: #00a;
-}
-
-div#cgit table.list td.commitgraph .column5 {
-	color: #a0a;
-}
-
-div#cgit table.list td.commitgraph .column6 {
-	color: #0aa;
-}
-
-div#cgit table.list td.logsubject {
-	font-family: monospace;
-	font-weight: bold;
-}
-
-div#cgit table.list td.logmsg {
-	font-family: monospace;
-	white-space: pre;
-	padding: 0 0.5em;
-}
-
-div#cgit table.list td a {
-	color: black;
-}
-
-div#cgit table.list td a.ls-dir {
-	font-weight: bold;
-	color: #00f;
-}
-
-div#cgit table.list td a:hover {
-	color: #00f;
-}
-
-div#cgit img {
-	border: none;
-}
-
-div#cgit input#switch-btn {
-	margin: 2px 0px 0px 0px;
-}
-
-div#cgit td#sidebar input.txt {
-	width: 100%;
-	margin: 2px 0px 0px 0px;
-}
-
-div#cgit table#grid {
-	margin: 0px;
-}
-
-div#cgit td#content {
-	vertical-align: top;
-	padding: 1em 2em 1em 1em;
-	border: none;
-}
-
-div#cgit div#summary {
-	vertical-align: top;
-	margin-bottom: 1em;
-}
-
-div#cgit table#downloads {
-	float: right;
-	border-collapse: collapse;
-	border: solid 1px #777;
-	margin-left: 0.5em;
-	margin-bottom: 0.5em;
-}
-
-div#cgit table#downloads th {
-	background-color: #ccc;
-}
-
-div#cgit div#blob {
-	border: solid 1px black;
-}
-
-div#cgit div.error {
-	color: red;
-	font-weight: bold;
-	margin: 1em 2em;
-}
-
-div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit .ls-mod {
-	font-family: monospace;
-}
-
-div#cgit td.ls-size {
-	text-align: right;
-	font-family: monospace;
-	width: 10em;
-}
-
-div#cgit td.ls-mode {
-	font-family: monospace;
-	width: 10em;
-}
-
-div#cgit table.blob {
-	margin-top: 0.5em;
-	border-top: solid 1px black;
-}
-
-div#cgit table.blob td.hashes,
-div#cgit table.blob td.lines {
-	margin: 0; padding: 0 0 0 0.5em;
-	vertical-align: top;
-	color: black;
-}
-
-div#cgit table.blob td.linenumbers {
-	margin: 0; padding: 0 0.5em 0 0.5em;
-	vertical-align: top;
-	text-align: right;
-	border-right: 1px solid gray;
-}
-
-div#cgit table.blob pre {
-	padding: 0; margin: 0;
-}
-
-div#cgit table.blob td.linenumbers a,
-div#cgit table.ssdiff td.lineno a {
-	color: gray;
-	text-align: right;
-	text-decoration: none;
-}
-
-div#cgit table.blob td.linenumbers a:hover,
-div#cgit table.ssdiff td.lineno a:hover {
-	color: black;
-}
-
-div#cgit table.blame td.hashes,
-div#cgit table.blame td.lines,
-div#cgit table.blame td.linenumbers {
-	padding: 0;
-}
-
-div#cgit table.blame td.hashes div.alt,
-div#cgit table.blame td.lines div.alt {
-	padding: 0 0.5em 0 0.5em;
-}
-
-div#cgit table.blame td.linenumbers div.alt {
-	padding: 0 0.5em 0 0;
-}
-
-div#cgit table.blame div.alt:nth-child(even) {
-	background: #eee;
-}
-
-div#cgit table.blame div.alt:nth-child(odd) {
-	background: white;
-}
-
-div#cgit table.blame td.lines > div {
-	position: relative;
-}
-
-div#cgit table.blame td.lines > div > pre {
-	padding: 0 0 0 0.5em;
-	position: absolute;
-	top: 0;
-}
-
-div#cgit table.bin-blob {
-	margin-top: 0.5em;
-	border: solid 1px black;
-}
-
-div#cgit table.bin-blob th {
-	font-family: monospace;
-	white-space: pre;
-	border: solid 1px #777;
-	padding: 0.5em 1em;
-}
-
-div#cgit table.bin-blob td {
-	font-family: monospace;
-	white-space: pre;
-	border-left: solid 1px #777;
-	padding: 0em 1em;
-}
-
-div#cgit table.nowrap td {
-	white-space: nowrap;
-}
-
-div#cgit table.commit-info {
-	border-collapse: collapse;
-	margin-top: 1.5em;
-}
-
-div#cgit div.cgit-panel {
-	float: right;
-	margin-top: 1.5em;
-}
-
-div#cgit div.cgit-panel table {
-	border-collapse: collapse;
-	border: solid 1px #aaa;
-	background-color: #eee;
-}
-
-div#cgit div.cgit-panel th {
-	text-align: center;
-}
-
-div#cgit div.cgit-panel td {
-	padding: 0.25em 0.5em;
-}
-
-div#cgit div.cgit-panel td.label {
-	padding-right: 0.5em;
-}
-
-div#cgit div.cgit-panel td.ctrl {
-	padding-left: 0.5em;
-}
-
-div#cgit table.commit-info th {
-	text-align: left;
-	font-weight: normal;
-	padding: 0.1em 1em 0.1em 0.1em;
-	vertical-align: top;
-}
-
-div#cgit table.commit-info td {
-	font-weight: normal;
-	padding: 0.1em 1em 0.1em 0.1em;
-}
-
-div#cgit div.commit-subject {
-	font-weight: bold;
-	font-size: 125%;
-	margin: 1.5em 0em 0.5em 0em;
-	padding: 0em;
-}
-
-div#cgit div.notes-header {
-	font-weight: bold;
-	padding-top: 1.5em;
-}
-
-div#cgit div.notes {
-	white-space: pre;
-	font-family: monospace;
-	border: solid 1px #ee9;
-	background-color: #ffd;
-	padding: 0.3em 2em 0.3em 1em;
-	float: left;
-}
-
-div#cgit div.notes-footer {
-	clear: left;
-}
-
-div#cgit div.diffstat-header {
-	font-weight: bold;
-	padding-top: 1.5em;
-}
-
-div#cgit table.diffstat {
-	border-collapse: collapse;
-	border: solid 1px #aaa;
-	background-color: #eee;
-}
-
-div#cgit table.diffstat th {
-	font-weight: normal;
-	text-align: left;
-	text-decoration: underline;
-	padding: 0.1em 1em 0.1em 0.1em;
-	font-size: 100%;
-}
-
-div#cgit table.diffstat td {
-	padding: 0.2em 0.2em 0.1em 0.1em;
-	font-size: 100%;
-	border: none;
-}
-
-div#cgit table.diffstat td.mode {
-	white-space: nowrap;
-}
-
-div#cgit table.diffstat td span.modechange {
-	padding-left: 1em;
-	color: red;
-}
-
-div#cgit table.diffstat td.add a {
-	color: green;
-}
-
-div#cgit table.diffstat td.del a {
-	color: red;
-}
-
-div#cgit table.diffstat td.upd a {
-	color: blue;
-}
-
-div#cgit table.diffstat td.graph {
-	width: 500px;
-	vertical-align: middle;
-}
-
-div#cgit table.diffstat td.graph table {
-	border: none;
-}
-
-div#cgit table.diffstat td.graph td {
-	padding: 0px;
-	border: 0px;
-	height: 7pt;
-}
-
-div#cgit table.diffstat td.graph td.add {
-	background-color: #5c5;
-}
-
-div#cgit table.diffstat td.graph td.rem {
-	background-color: #c55;
-}
-
-div#cgit div.diffstat-summary {
-	color: #888;
-	padding-top: 0.5em;
-}
-
-div#cgit table.diff {
-	width: 100%;
-}
-
-div#cgit table.diff td span.head {
-	font-weight: bold;
-	color: black;
-}
-
-div#cgit table.diff td span.hunk {
-	color: #009;
-}
-
-div#cgit table.diff td span.add {
-	color: green;
-}
-
-div#cgit table.diff td span.del {
-	color: red;
-}
-
-div#cgit .oid {
-	font-family: monospace;
-	font-size: 90%;
-}
-
-div#cgit .left {
-	text-align: left;
-}
-
-div#cgit .right {
-	text-align: right;
-}
-
-div#cgit table.list td.reposection {
-	font-style: italic;
-	color: #888;
-}
-
-div#cgit a.button {
-	font-size: 80%;
-}
-
-div#cgit a.primary {
-	font-size: 100%;
-}
-
-div#cgit a.secondary {
-	font-size: 90%;
-}
-
-div#cgit td.toplevel-repo {
-
-}
-
-div#cgit table.list td.sublevel-repo {
-	padding-left: 1.5em;
-}
-
-div#cgit ul.pager {
-	list-style-type: none;
-	text-align: center;
-	margin: 1em 0em 0em 0em;
-	padding: 0;
-}
-
-div#cgit ul.pager li {
-	display: inline-block;
-	margin: 0.25em 0.5em;
-}
-
-div#cgit ul.pager a {
-	color: #777;
-}
-
-div#cgit ul.pager .current {
-	font-weight: bold;
-}
-
-div#cgit span.age-mins {
-	font-weight: bold;
-	color: #080;
-}
-
-div#cgit span.age-hours {
-	color: #080;
-}
-
-div#cgit span.age-days {
-	color: #040;
-}
-
-div#cgit span.age-weeks {
-	color: #444;
-}
-
-div#cgit span.age-months {
-	color: #888;
-}
-
-div#cgit span.age-years {
-	color: #bbb;
-}
-
-div#cgit span.insertions {
-	color: #080;
-}
-
-div#cgit span.deletions {
-	color: #800;
-}
-
-div#cgit div.footer {
-	margin-top: 0.5em;
-	text-align: center;
-	font-size: 80%;
-	color: #ccc;
-}
-
-div#cgit div.footer a {
-	color: #ccc;
-	text-decoration: none;
-}
-
-div#cgit div.footer a:hover {
-	text-decoration: underline;
-}
-
-div#cgit a.branch-deco {
-	color: #000;
-	padding: 0px 0.25em;
-	background-color: #88ff88;
-	border: solid 1px #007700;
-}
-
-div#cgit a.tag-deco {
-	color: #000;
-	padding: 0px 0.25em;
-	background-color: #ffff88;
-	border: solid 1px #777700;
-}
-
-div#cgit a.tag-annotated-deco {
-	color: #000;
-	padding: 0px 0.25em;
-	background-color: #ffcc88;
-	border: solid 1px #777700;
-}
-
-div#cgit a.remote-deco {
-	color: #000;
-	padding: 0px 0.25em;
-	background-color: #ccccff;
-	border: solid 1px #000077;
-}
-
-div#cgit a.deco {
-	color: #000;
-	padding: 0px 0.25em;
-	background-color: #ff8888;
-	border: solid 1px #770000;
-}
-
-div#cgit div.commit-subject a.branch-deco,
-div#cgit div.commit-subject a.tag-deco,
-div#cgit div.commit-subject a.tag-annotated-deco,
-div#cgit div.commit-subject a.remote-deco,
-div#cgit div.commit-subject a.deco {
-	font-size: 75%;
-}
-
-div#cgit table.stats {
-	border: solid 1px black;
-	border-collapse: collapse;
-}
-
-div#cgit table.stats th {
-	text-align: left;
-	padding: 1px 0.5em;
-	background-color: #eee;
-	border: solid 1px black;
-}
-
-div#cgit table.stats td {
-	text-align: right;
-	padding: 1px 0.5em;
-	border: solid 1px black;
-}
-
-div#cgit table.stats td.total {
-	font-weight: bold;
-	text-align: left;
-}
-
-div#cgit table.stats td.sum {
-	color: #c00;
-	font-weight: bold;
-/*	background-color: #eee; */
-}
-
-div#cgit table.stats td.left {
-	text-align: left;
-}
-
-div#cgit table.vgraph {
-	border-collapse: separate;
-	border: solid 1px black;
-	height: 200px;
-}
-
-div#cgit table.vgraph th {
-	background-color: #eee;
-	font-weight: bold;
-	border: solid 1px white;
-	padding: 1px 0.5em;
-}
-
-div#cgit table.vgraph td {
-	vertical-align: bottom;
-	padding: 0px 10px;
-}
-
-div#cgit table.vgraph div.bar {
-	background-color: #eee;
-}
-
-div#cgit table.hgraph {
-	border: solid 1px black;
-	width: 800px;
-}
-
-div#cgit table.hgraph th {
-	background-color: #eee;
-	font-weight: bold;
-	border: solid 1px black;
-	padding: 1px 0.5em;
-}
-
-div#cgit table.hgraph td {
-	vertical-align: middle;
-	padding: 2px 2px;
-}
-
-div#cgit table.hgraph div.bar {
-	background-color: #eee;
-	height: 1em;
-}
-
-div#cgit table.ssdiff {
-	width: 100%;
-}
-
-div#cgit table.ssdiff td {
-	font-size: 75%;
-	font-family: monospace;
-	white-space: pre;
-	padding: 1px 4px 1px 4px;
-	border-left: solid 1px #aaa;
-	border-right: solid 1px #aaa;
-}
-
-div#cgit table.ssdiff td.add {
-	color: black;
-	background: #cfc;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.add_dark {
-	color: black;
-	background: #aca;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff span.add {
-	background: #cfc;
-	font-weight: bold;
-}
-
-div#cgit table.ssdiff td.del {
-	color: black;
-	background: #fcc;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.del_dark {
-	color: black;
-	background: #caa;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff span.del {
-	background: #fcc;
-	font-weight: bold;
-}
-
-div#cgit table.ssdiff td.changed {
-	color: black;
-	background: #ffc;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.changed_dark {
-	color: black;
-	background: #cca;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.lineno {
-	color: black;
-	background: #eee;
-	text-align: right;
-	width: 3em;
-	min-width: 3em;
-}
-
-div#cgit table.ssdiff td.hunk {
-	color: black;
-	background: #ccf;
-	border-top: solid 1px #aaa;
-	border-bottom: solid 1px #aaa;
-}
-
-div#cgit table.ssdiff td.head {
-	border-top: solid 1px #aaa;
-	border-bottom: solid 1px #aaa;
-}
-
-div#cgit table.ssdiff td.head div.head {
-	font-weight: bold;
-	color: black;
-}
-
-div#cgit table.ssdiff td.foot {
-	border-top: solid 1px #aaa;
-	border-left: none;
-	border-right: none;
-	border-bottom: none;
-}
-
-div#cgit table.ssdiff td.space {
-	border: none;
-}
-
-div#cgit table.ssdiff td.space div {
-	min-height: 3em;
-}
diff --git a/www/git.causal.agency/cgit/cgit.h b/www/git.causal.agency/cgit/cgit.h
deleted file mode 100644
index 72fcd849..00000000
--- a/www/git.causal.agency/cgit/cgit.h
+++ /dev/null
@@ -1,397 +0,0 @@
-#ifndef CGIT_H
-#define CGIT_H
-
-
-#include <git-compat-util.h>
-#include <stdbool.h>
-
-#include <cache.h>
-#include <grep.h>
-#include <object.h>
-#include <object-store.h>
-#include <tree.h>
-#include <commit.h>
-#include <tag.h>
-#include <diff.h>
-#include <diffcore.h>
-#include <strvec.h>
-#include <refs.h>
-#include <revision.h>
-#include <log-tree.h>
-#include <archive.h>
-#include <string-list.h>
-#include <xdiff-interface.h>
-#include <xdiff/xdiff.h>
-#include <utf8.h>
-#include <notes.h>
-#include <graph.h>
-
-/* Add isgraph(x) to Git's sane ctype support (see git-compat-util.h) */
-#undef isgraph
-#define isgraph(x) (isprint((x)) && !isspace((x)))
-
-
-/*
- * Limits used for relative dates
- */
-#define TM_MIN    60
-#define TM_HOUR  (TM_MIN * 60)
-#define TM_DAY   (TM_HOUR * 24)
-#define TM_WEEK  (TM_DAY * 7)
-#define TM_YEAR  (TM_DAY * 365)
-#define TM_MONTH (TM_YEAR / 12.0)
-
-
-/*
- * Default encoding
- */
-#define PAGE_ENCODING "UTF-8"
-
-#define BIT(x)	(1U << (x))
-
-typedef void (*configfn)(const char *name, const char *value);
-typedef void (*filepair_fn)(struct diff_filepair *pair);
-typedef void (*linediff_fn)(char *line, int len);
-
-typedef enum {
-	DIFF_UNIFIED, DIFF_SSDIFF, DIFF_STATONLY
-} diff_type;
-
-typedef enum {
-	ABOUT, COMMIT, SOURCE, EMAIL, AUTH, OWNER
-} filter_type;
-
-struct cgit_filter {
-	int (*open)(struct cgit_filter *, va_list ap);
-	int (*close)(struct cgit_filter *);
-	void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix);
-	void (*cleanup)(struct cgit_filter *);
-	int argument_count;
-};
-
-struct cgit_exec_filter {
-	struct cgit_filter base;
-	char *cmd;
-	char **argv;
-	int old_stdout;
-	int pid;
-};
-
-struct cgit_repo {
-	char *url;
-	char *name;
-	char *path;
-	char *desc;
-	char *extra_head_content;
-	char *owner;
-	char *homepage;
-	char *defbranch;
-	char *module_link;
-	struct string_list readme;
-	char *section;
-	char *clone_url;
-	char *logo;
-	char *logo_link;
-	char *snapshot_prefix;
-	int snapshots;
-	int enable_blame;
-	int enable_commit_graph;
-	int enable_log_filecount;
-	int enable_log_linecount;
-	int enable_remote_branches;
-	int enable_subject_links;
-	int enable_html_serving;
-	int max_stats;
-	int branch_sort;
-	int commit_sort;
-	time_t mtime;
-	struct cgit_filter *about_filter;
-	struct cgit_filter *commit_filter;
-	struct cgit_filter *source_filter;
-	struct cgit_filter *email_filter;
-	struct cgit_filter *owner_filter;
-	struct string_list submodules;
-	int hide;
-	int ignore;
-};
-
-typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
-	      const char *value);
-
-struct cgit_repolist {
-	int length;
-	int count;
-	struct cgit_repo *repos;
-};
-
-struct commitinfo {
-	struct commit *commit;
-	char *author;
-	char *author_email;
-	unsigned long author_date;
-	int author_tz;
-	char *committer;
-	char *committer_email;
-	unsigned long committer_date;
-	int committer_tz;
-	char *subject;
-	char *msg;
-	char *msg_encoding;
-};
-
-struct taginfo {
-	char *tagger;
-	char *tagger_email;
-	unsigned long tagger_date;
-	int tagger_tz;
-	char *msg;
-};
-
-struct refinfo {
-	const char *refname;
-	struct object *object;
-	union {
-		struct taginfo *tag;
-		struct commitinfo *commit;
-	};
-};
-
-struct reflist {
-	struct refinfo **refs;
-	int alloc;
-	int count;
-};
-
-struct cgit_query {
-	int has_symref;
-	int has_oid;
-	int has_difftype;
-	char *raw;
-	char *repo;
-	char *page;
-	char *search;
-	char *grep;
-	char *head;
-	char *oid;
-	char *oid2;
-	char *path;
-	char *name;
-	char *url;
-	char *period;
-	int   ofs;
-	int nohead;
-	char *sort;
-	int showmsg;
-	diff_type difftype;
-	int show_all;
-	int context;
-	int ignorews;
-	int follow;
-	char *vpath;
-};
-
-struct cgit_config {
-	char *agefile;
-	char *cache_root;
-	char *clone_prefix;
-	char *clone_url;
-	char *css;
-	char *favicon;
-	char *footer;
-	char *head_include;
-	char *header;
-	char *logo;
-	char *logo_link;
-	char *mimetype_file;
-	char *module_link;
-	char *project_list;
-	struct string_list readme;
-	char *robots;
-	char *root_title;
-	char *root_desc;
-	char *root_readme;
-	char *script_name;
-	char *section;
-	char *repository_sort;
-	char *virtual_root;	/* Always ends with '/'. */
-	char *strict_export;
-	int cache_size;
-	int cache_dynamic_ttl;
-	int cache_max_create_time;
-	int cache_repo_ttl;
-	int cache_root_ttl;
-	int cache_scanrc_ttl;
-	int cache_static_ttl;
-	int cache_about_ttl;
-	int cache_snapshot_ttl;
-	int case_sensitive_sort;
-	int embedded;
-	int enable_filter_overrides;
-	int enable_follow_links;
-	int enable_http_clone;
-	int enable_index_links;
-	int enable_index_owner;
-	int enable_blame;
-	int enable_commit_graph;
-	int enable_log_filecount;
-	int enable_log_linecount;
-	int enable_remote_branches;
-	int enable_subject_links;
-	int enable_html_serving;
-	int enable_tree_linenumbers;
-	int enable_git_config;
-	int local_time;
-	int max_atom_items;
-	int max_repo_count;
-	int max_commit_count;
-	int max_lock_attempts;
-	int max_msg_len;
-	int max_repodesc_len;
-	int max_blob_size;
-	int max_stats;
-	int noplainemail;
-	int noheader;
-	int renamelimit;
-	int remove_suffix;
-	int scan_hidden_path;
-	int section_from_path;
-	int snapshots;
-	int section_sort;
-	int summary_branches;
-	int summary_log;
-	int summary_tags;
-	diff_type difftype;
-	int branch_sort;
-	int commit_sort;
-	struct string_list mimetypes;
-	struct cgit_filter *about_filter;
-	struct cgit_filter *commit_filter;
-	struct cgit_filter *source_filter;
-	struct cgit_filter *email_filter;
-	struct cgit_filter *owner_filter;
-	struct cgit_filter *auth_filter;
-};
-
-struct cgit_page {
-	time_t modified;
-	time_t expires;
-	size_t size;
-	const char *mimetype;
-	const char *charset;
-	const char *filename;
-	const char *etag;
-	const char *title;
-	int status;
-	const char *statusmsg;
-};
-
-struct cgit_environment {
-	const char *cgit_config;
-	const char *http_host;
-	const char *https;
-	const char *no_http;
-	const char *path_info;
-	const char *query_string;
-	const char *request_method;
-	const char *script_name;
-	const char *server_name;
-	const char *server_port;
-	const char *http_cookie;
-	const char *http_referer;
-	unsigned int content_length;
-	int authenticated;
-};
-
-struct cgit_context {
-	struct cgit_environment env;
-	struct cgit_query qry;
-	struct cgit_config cfg;
-	struct cgit_repo *repo;
-	struct cgit_page page;
-};
-
-typedef int (*write_archive_fn_t)(const char *, const char *);
-
-struct cgit_snapshot_format {
-	const char *suffix;
-	const char *mimetype;
-	write_archive_fn_t write_func;
-};
-
-extern const char *cgit_version;
-
-extern struct cgit_repolist cgit_repolist;
-extern struct cgit_context ctx;
-extern const struct cgit_snapshot_format cgit_snapshot_formats[];
-
-extern char *cgit_default_repo_desc;
-extern struct cgit_repo *cgit_add_repo(const char *url);
-extern struct cgit_repo *cgit_get_repoinfo(const char *url);
-extern void cgit_repo_config_cb(const char *name, const char *value);
-
-extern int chk_zero(int result, char *msg);
-extern int chk_positive(int result, char *msg);
-extern int chk_non_negative(int result, char *msg);
-
-extern char *trim_end(const char *str, char c);
-extern char *ensure_end(const char *str, char c);
-
-extern void strbuf_ensure_end(struct strbuf *sb, char c);
-
-extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
-extern void cgit_free_reflist_inner(struct reflist *list);
-extern int cgit_refs_cb(const char *refname, const struct object_id *oid,
-			int flags, void *cb_data);
-
-extern void cgit_free_commitinfo(struct commitinfo *info);
-extern void cgit_free_taginfo(struct taginfo *info);
-
-void cgit_diff_tree_cb(struct diff_queue_struct *q,
-		       struct diff_options *options, void *data);
-
-extern int cgit_diff_files(const struct object_id *old_oid,
-			   const struct object_id *new_oid,
-			   unsigned long *old_size, unsigned long *new_size,
-			   int *binary, int context, int ignorews,
-			   linediff_fn fn);
-
-extern void cgit_diff_tree(const struct object_id *old_oid,
-			   const struct object_id *new_oid,
-			   filepair_fn fn, const char *prefix, int ignorews);
-
-extern void cgit_diff_commit(struct commit *commit, filepair_fn fn,
-			     const char *prefix);
-
-__attribute__((format (printf,1,2)))
-extern char *fmt(const char *format,...);
-
-__attribute__((format (printf,1,2)))
-extern char *fmtalloc(const char *format,...);
-
-extern struct commitinfo *cgit_parse_commit(struct commit *commit);
-extern struct taginfo *cgit_parse_tag(struct tag *tag);
-extern void cgit_parse_url(const char *url);
-
-extern const char *cgit_repobasename(const char *reponame);
-
-extern int cgit_parse_snapshots_mask(const char *str);
-extern const struct object_id *cgit_snapshot_get_sig(const char *ref,
-						     const struct cgit_snapshot_format *f);
-extern const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f);
-
-extern int cgit_open_filter(struct cgit_filter *filter, ...);
-extern int cgit_close_filter(struct cgit_filter *filter);
-extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix);
-extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
-extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
-extern void cgit_cleanup_filters(void);
-
-extern void cgit_prepare_repo_env(struct cgit_repo * repo);
-
-extern int readfile(const char *path, char **buf, size_t *size);
-
-extern char *expand_macros(const char *txt);
-
-extern char *get_mimetype_for_filename(const char *filename);
-
-#endif /* CGIT_H */
diff --git a/www/git.causal.agency/cgit/cgit.mk b/www/git.causal.agency/cgit/cgit.mk
deleted file mode 100644
index 5b9ed5be..00000000
--- a/www/git.causal.agency/cgit/cgit.mk
+++ /dev/null
@@ -1,114 +0,0 @@
-# This Makefile is run in the "git" directory in order to re-use Git's
-# build variables and operating system detection.  Hence all files in
-# CGit's directory must be prefixed with "../".
-include Makefile
-
-CGIT_PREFIX = ../
-
--include $(CGIT_PREFIX)cgit.conf
-
-# The CGIT_* variables are inherited when this file is called from the
-# main Makefile - they are defined there.
-
-$(CGIT_PREFIX)VERSION: force-version
-	@cd $(CGIT_PREFIX) && '$(SHELL_PATH_SQ)' ./gen-version.sh "$(CGIT_VERSION)"
--include $(CGIT_PREFIX)VERSION
-.PHONY: force-version
-
-# CGIT_CFLAGS is a separate variable so that we can track it separately
-# and avoid rebuilding all of Git when these variables change.
-CGIT_CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
-CGIT_CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
-CGIT_CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
-
-PKG_CONFIG ?= pkg-config
-
-ifdef NO_C99_FORMAT
-	CFLAGS += -DNO_C99_FORMAT
-endif
-
-# Add -ldl to linker flags on systems that commonly use GNU libc.
-ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD))
-	CGIT_LIBS += -ldl
-endif
-
-# glibc 2.1+ offers sendfile which the most common C library on Linux
-ifeq ($(uname_S),Linux)
-	HAVE_LINUX_SENDFILE = YesPlease
-endif
-
-ifdef HAVE_LINUX_SENDFILE
-	CGIT_CFLAGS += -DHAVE_LINUX_SENDFILE
-endif
-
-CGIT_OBJ_NAMES += cgit.o
-CGIT_OBJ_NAMES += cache.o
-CGIT_OBJ_NAMES += cmd.o
-CGIT_OBJ_NAMES += configfile.o
-CGIT_OBJ_NAMES += filter.o
-CGIT_OBJ_NAMES += html.o
-CGIT_OBJ_NAMES += parsing.o
-CGIT_OBJ_NAMES += scan-tree.o
-CGIT_OBJ_NAMES += shared.o
-CGIT_OBJ_NAMES += ui-atom.o
-CGIT_OBJ_NAMES += ui-blame.o
-CGIT_OBJ_NAMES += ui-blob.o
-CGIT_OBJ_NAMES += ui-clone.o
-CGIT_OBJ_NAMES += ui-commit.o
-CGIT_OBJ_NAMES += ui-diff.o
-CGIT_OBJ_NAMES += ui-log.o
-CGIT_OBJ_NAMES += ui-patch.o
-CGIT_OBJ_NAMES += ui-plain.o
-CGIT_OBJ_NAMES += ui-refs.o
-CGIT_OBJ_NAMES += ui-repolist.o
-CGIT_OBJ_NAMES += ui-shared.o
-CGIT_OBJ_NAMES += ui-snapshot.o
-CGIT_OBJ_NAMES += ui-ssdiff.o
-CGIT_OBJ_NAMES += ui-stats.o
-CGIT_OBJ_NAMES += ui-summary.o
-CGIT_OBJ_NAMES += ui-tag.o
-CGIT_OBJ_NAMES += ui-tree.o
-
-CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))
-
-# Only cgit.c reference CGIT_VERSION so we only rebuild its objects when the
-# version changes.
-CGIT_VERSION_OBJS := $(addprefix $(CGIT_PREFIX),cgit.o cgit.sp)
-$(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
-$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
-	-DCGIT_VERSION='"$(CGIT_VERSION)"'
-
-# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
-# handled by that and we must handle them ourselves.
-cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d)
-cgit_dep_files_present := $(wildcard $(cgit_dep_files))
-ifneq ($(cgit_dep_files_present),)
-include $(cgit_dep_files_present)
-endif
-
-ifeq ($(wildcard $(CGIT_PREFIX).depend),)
-missing_dep_dirs += $(CGIT_PREFIX).depend
-endif
-
-$(CGIT_PREFIX).depend:
-	@mkdir -p $@
-
-$(CGIT_PREFIX)CGIT-CFLAGS: FORCE
-	@FLAGS='$(subst ','\'',$(CGIT_CFLAGS))'; \
-	    if test x"$$FLAGS" != x"`cat ../CGIT-CFLAGS 2>/dev/null`" ; then \
-		echo 1>&2 "    * new CGit build flags"; \
-		echo "$$FLAGS" >$(CGIT_PREFIX)CGIT-CFLAGS; \
-            fi
-
-$(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
-	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<
-
-$(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
-	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
-
-CGIT_SP_OBJS := $(patsubst %.o,%.sp,$(CGIT_OBJS))
-
-$(CGIT_SP_OBJS): %.sp: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS FORCE
-	$(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $(SPARSE_FLAGS) $<
-
-cgit-sparse: $(CGIT_SP_OBJS)
diff --git a/www/git.causal.agency/cgit/cgit.png b/www/git.causal.agency/cgit/cgit.png
deleted file mode 100644
index 425528ee..00000000
--- a/www/git.causal.agency/cgit/cgit.png
+++ /dev/null
Binary files differdiff --git a/www/git.causal.agency/cgit/cgitrc.5.txt b/www/git.causal.agency/cgit/cgitrc.5.txt
deleted file mode 100644
index 7dd644a9..00000000
--- a/www/git.causal.agency/cgit/cgitrc.5.txt
+++ /dev/null
@@ -1,977 +0,0 @@
-:man source:   cgit
-:man manual:   cgit
-
-CGITRC(5)
-========
-
-
-NAME
-----
-cgitrc - runtime configuration for cgit
-
-
-SYNOPSIS
---------
-Cgitrc contains all runtime settings for cgit, including the list of git
-repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank
-lines, and lines starting with '#', are ignored.
-
-
-LOCATION
---------
-The default location of cgitrc, defined at compile time, is /etc/cgitrc. At
-runtime, cgit will consult the environment variable CGIT_CONFIG and, if
-defined, use its value instead.
-
-
-GLOBAL SETTINGS
----------------
-about-filter::
-	Specifies a command which will be invoked to format the content of
-	about pages (both top-level and for each repository). The command will
-	get the content of the about-file on its STDIN, the name of the file
-	as the first argument, and the STDOUT from the command will be
-	included verbatim on the about page. Default value: none. See
-	also: "FILTER API".
-
-agefile::
-	Specifies a path, relative to each repository path, which can be used
-	to specify the date and time of the youngest commit in the repository.
-	The first line in the file is used as input to the "parse_date"
-	function in libgit. Recommended timestamp-format is "yyyy-mm-dd
-	hh:mm:ss". You may want to generate this file from a post-receive
-	hook. Default value: "info/web/last-modified".
-
-auth-filter::
-	Specifies a command that will be invoked for authenticating repository
-	access. Receives quite a few arguments, and data on both stdin and
-	stdout for authentication processing. Details follow later in this
-	document. If no auth-filter is specified, no authentication is
-	performed. Default value: none. See also: "FILTER API".
-
-branch-sort::
-	Flag which, when set to "age", enables date ordering in the branch ref
-	list, and when set to "name" enables ordering by branch name. Default
-	value: "name".
-
-cache-about-ttl::
-	Number which specifies the time-to-live, in minutes, for the cached
-	version of the repository about page. See also: "CACHE". Default
-	value: "15".
-
-cache-dynamic-ttl::
-	Number which specifies the time-to-live, in minutes, for the cached
-	version of repository pages accessed without a fixed SHA1. See also:
-	"CACHE". Default value: "5".
-
-cache-repo-ttl::
-	Number which specifies the time-to-live, in minutes, for the cached
-	version of the repository summary page. See also: "CACHE". Default
-	value: "5".
-
-cache-root::
-	Path used to store the cgit cache entries. Default value:
-	"/var/cache/cgit". See also: "MACRO EXPANSION".
-
-cache-root-ttl::
-	Number which specifies the time-to-live, in minutes, for the cached
-	version of the repository index page. See also: "CACHE". Default
-	value: "5".
-
-cache-scanrc-ttl::
-	Number which specifies the time-to-live, in minutes, for the result
-	of scanning a path for git repositories. See also: "CACHE". Default
-	value: "15".
-
-case-sensitive-sort::
-	Sort items in the repo list case sensitively. Default value: "1".
-	See also: repository-sort, section-sort.
-
-cache-size::
-	The maximum number of entries in the cgit cache. When set to "0",
-	caching is disabled. See also: "CACHE". Default value: "0"
-
-cache-snapshot-ttl::
-	Number which specifies the time-to-live, in minutes, for the cached
-	version of snapshots. See also: "CACHE". Default value: "5".
-
-cache-static-ttl::
-	Number which specifies the time-to-live, in minutes, for the cached
-	version of repository pages accessed with a fixed SHA1. See also:
-	"CACHE". Default value: -1".
-
-clone-prefix::
-	Space-separated list of common prefixes which, when combined with a
-	repository url, generates valid clone urls for the repository. This
-	setting is only used if `repo.clone-url` is unspecified. Default value:
-	none.
-
-clone-url::
-	Space-separated list of clone-url templates. This setting is only
-	used if `repo.clone-url` is unspecified. Default value: none. See
-	also: "MACRO EXPANSION", "FILTER API".
-
-commit-filter::
-	Specifies a command which will be invoked to format commit messages.
-	The command will get the message on its STDIN, and the STDOUT from the
-	command will be included verbatim as the commit message, i.e. this can
-	be used to implement bugtracker integration. Default value: none.
-	See also: "FILTER API".
-
-commit-sort::
-	Flag which, when set to "date", enables strict date ordering in the
-	commit log, and when set to "topo" enables strict topological
-	ordering. If unset, the default ordering of "git log" is used. Default
-	value: unset.
-
-css::
-	Url which specifies the css document to include in all cgit pages.
-	Default value: "/cgit.css".
-
-email-filter::
-	Specifies a command which will be invoked to format names and email
-	address of committers, authors, and taggers, as represented in various
-	places throughout the cgit interface. This command will receive an
-	email address and an origin page string as its command line arguments,
-	and the text to format on STDIN. It is to write the formatted text back
-	out onto STDOUT. Default value: none. See also: "FILTER API".
-
-embedded::
-	Flag which, when set to "1", will make cgit generate a html fragment
-	suitable for embedding in other html pages. Default value: none. See
-	also: "noheader".
-
-enable-blame::
-	Flag which, when set to "1", will allow cgit to provide a "blame" page
-	for files, and will make it generate links to that page in appropriate
-	places. Default value: "0".
-
-enable-commit-graph::
-	Flag which, when set to "1", will make cgit print an ASCII-art commit
-	history graph to the left of the commit messages in the repository
-	log page. Default value: "0".
-
-enable-filter-overrides::
-	Flag which, when set to "1", allows all filter settings to be
-	overridden in repository-specific cgitrc files. Default value: none.
-
-enable-follow-links::
-	Flag which, when set to "1", allows users to follow a file in the log
-	view.  Default value: "0".
-
-enable-git-config::
-	Flag which, when set to "1", will allow cgit to use git config to set
-	any repo specific settings. This option is used in conjunction with
-	"scan-path", and must be defined prior, to augment repo-specific
-	settings. The keys gitweb.owner, gitweb.category, gitweb.description,
-	and gitweb.homepage will map to the cgit keys repo.owner, repo.section,
-	repo.desc, and repo.homepage respectively. All git config keys that begin
-	with "cgit." will be mapped to the corresponding "repo." key in cgit.
-	Default value: "0". See also: scan-path, section-from-path.
-
-enable-http-clone::
-	If set to "1", cgit will act as a dumb HTTP endpoint for git clones.
-	You can add "http://$HTTP_HOST$SCRIPT_NAME/$CGIT_REPO_URL" to clone-url
-	to expose this feature. If you use an alternate way of serving git
-	repositories, you may wish to disable this. Default value: "1".
-
-enable-html-serving::
-	Flag which, when set to "1", will allow the /plain handler to serve
-	mimetype headers that result in the file being treated as HTML by the
-	browser. When set to "0", such file types are returned instead as
-	text/plain or application/octet-stream. Default value: "0". See also:
-	"repo.enable-html-serving".
-
-enable-index-links::
-	Flag which, when set to "1", will make cgit generate extra links for
-	each repo in the repository index (specifically, to the "summary",
-	"commit" and "tree" pages). Default value: "0".
-
-enable-index-owner::
-	Flag which, when set to "1", will make cgit display the owner of
-	each repo in the repository index. Default value: "1".
-
-enable-log-filecount::
-	Flag which, when set to "1", will make cgit print the number of
-	modified files for each commit on the repository log page. Default
-	value: "0".
-
-enable-log-linecount::
-	Flag which, when set to "1", will make cgit print the number of added
-	and removed lines for each commit on the repository log page. Default
-	value: "0".
-
-enable-remote-branches::
-	Flag which, when set to "1", will make cgit display remote branches
-	in the summary and refs views. Default value: "0". See also:
-	"repo.enable-remote-branches".
-
-enable-subject-links::
-	Flag which, when set to "1", will make cgit use the subject of the
-	parent commit as link text when generating links to parent commits
-	in commit view. Default value: "0". See also:
-	"repo.enable-subject-links".
-
-enable-tree-linenumbers::
-	Flag which, when set to "1", will make cgit generate linenumber links
-	for plaintext blobs printed in the tree view. Default value: "1".
-
-favicon::
-	Url used as link to a shortcut icon for cgit. It is suggested to use
-	the value "/favicon.ico" since certain browsers will ignore other
-	values. Default value: none.
-
-footer::
-	The content of the file specified with this option will be included
-	verbatim at the bottom of all pages (i.e. it replaces the standard
-	"generated by..." message. Default value: none.
-
-head-include::
-	The content of the file specified with this option will be included
-	verbatim in the html HEAD section on all pages. Default value: none.
-
-header::
-	The content of the file specified with this option will be included
-	verbatim at the top of all pages. Default value: none.
-
-include::
-	Name of a configfile to include before the rest of the current config-
-	file is parsed. Default value: none. See also: "MACRO EXPANSION".
-
-local-time::
-	Flag which, if set to "1", makes cgit print commit and tag times in the
-	servers timezone. Default value: "0".
-
-logo::
-	Url which specifies the source of an image which will be used as a logo
-	on all cgit pages. Default value: "/cgit.png".
-
-logo-link::
-	Url loaded when clicking on the cgit logo image. If unspecified the
-	calculated url of the repository index page will be used. Default
-	value: none.
-
-max-atom-items::
-	Specifies the number of items to display in atom feeds view. Default
-	value: "10".
-
-max-blob-size::
-	Specifies the maximum size of a blob to display HTML for in KBytes.
-	Default value: "0" (limit disabled).
-
-max-commit-count::
-	Specifies the number of entries to list per page in "log" view. Default
-	value: "50".
-
-max-message-length::
-	Specifies the maximum number of commit message characters to display in
-	"log" view. Default value: "80".
-
-max-repo-count::
-	Specifies the number of entries to list per page on the	repository
-	index page. Default value: "50".
-
-max-repodesc-length::
-	Specifies the maximum number of repo description characters to display
-	on the repository index page. Default value: "80".
-
-max-stats::
-	Set the default maximum statistics period. Valid values are "week",
-	"month", "quarter" and "year". If unspecified, statistics are
-	disabled. Default value: none. See also: "repo.max-stats".
-
-mimetype.<ext>::
-	Set the mimetype for the specified filename extension. This is used
-	by the `plain` command when returning blob content.
-
-mimetype-file::
-	Specifies the file to use for automatic mimetype lookup. If specified
-	then this field is used as a fallback when no "mimetype.<ext>" match is
-	found. If unspecified then no such lookup is performed. The typical file
-	to use on a Linux system is /etc/mime.types. The format of the file must
-	comply to:
-	- a comment line is an empty line or a line starting with a hash (#),
-	  optionally preceded by whitespace
-	- a non-comment line starts with the mimetype (like image/png), followed
-	  by one or more file extensions (like jpg), all separated by whitespace
-	Default value: none. See also: "mimetype.<ext>".
-
-module-link::
-	Text which will be used as the formatstring for a hyperlink when a
-	submodule is printed in a directory listing. The arguments for the
-	formatstring are the path and SHA1 of the submodule commit. Default
-	value: none.
-
-noplainemail::
-	If set to "1" showing full author email addresses will be disabled.
-	Default value: "0".
-
-noheader::
-	Flag which, when set to "1", will make cgit omit the standard header
-	on all pages. Default value: none. See also: "embedded".
-
-owner-filter::
-	Specifies a command which will be invoked to format the Owner
-	column of the main page.  The command will get the owner on STDIN,
-	and the STDOUT from the command will be included verbatim in the
-	table.  This can be used to link to additional context such as an
-	owners home page.  When active this filter is used instead of the
-	default owner query url.  Default value: none.
-	See also: "FILTER API".
-
-project-list::
-	A list of subdirectories inside of scan-path, relative to it, that
-	should loaded as git repositories. This must be defined prior to
-	scan-path. Default value: none. See also: scan-path, "MACRO
-	EXPANSION".
-
-readme::
-	Text which will be used as default value for "repo.readme". Multiple
-	config keys may be specified, and cgit will use the first found file
-	in this list. This is useful in conjunction with scan-path. Default
-	value: none. See also: scan-path, repo.readme.
-
-remove-suffix::
-	If set to "1" and scan-path is enabled, if any repositories are found
-	with a suffix of ".git", this suffix will be removed for the url and
-	name. This must be defined prior to scan-path. Default value: "0".
-	See also: scan-path.
-
-renamelimit::
-	Maximum number of files to consider when detecting renames. The value
-	 "-1" uses the compiletime value in git (for further info, look at
-	  `man git-diff`). Default value: "-1".
-
-repository-sort::
-	The way in which repositories in each section are sorted. Valid values
-	are "name" for sorting by the repo name or "age" for sorting by the
-	most recently updated repository. Default value: "name". See also:
-	section, case-sensitive-sort, section-sort.
-
-robots::
-	Text used as content for the "robots" meta-tag. Default value:
-	"index, nofollow".
-
-root-desc::
-	Text printed below the heading on the repository index page. Default
-	value: "a fast webinterface for the git dscm".
-
-root-readme::
-	The content of the file specified with this option will be included
-	verbatim below the "about" link on the repository index page. Default
-	value: none.
-
-root-title::
-	Text printed as heading on the repository index page. Default value:
-	"Git Repository Browser".
-
-scan-hidden-path::
-	If set to "1" and scan-path is enabled, scan-path will recurse into
-	directories whose name starts with a period ('.'). Otherwise,
-	scan-path will stay away from such directories (considered as
-	"hidden"). Note that this does not apply to the ".git" directory in
-	non-bare repos. This must be defined prior to scan-path.
-	Default value: 0. See also: scan-path.
-
-scan-path::
-	A path which will be scanned for repositories. If caching is enabled,
-	the result will be cached as a cgitrc include-file in the cache
-	directory. If project-list has been defined prior to scan-path,
-	scan-path loads only the directories listed in the file pointed to by
-	project-list. Be advised that only the global settings taken
-	before the scan-path directive will be applied to each repository.
-	Default value: none. See also: cache-scanrc-ttl, project-list,
-	"MACRO EXPANSION".
-
-section::
-	The name of the current repository section - all repositories defined
-	after this option will inherit the current section name. Default value:
-	none.
-
-section-sort::
-	Flag which, when set to "1", will sort the sections on the repository
-	listing by name. Set this flag to "0" if the order in the cgitrc file should
-	be preserved. Default value: "1". See also: section,
-	case-sensitive-sort, repository-sort.
-
-section-from-path::
-	A number which, if defined prior to scan-path, specifies how many
-	path elements from each repo path to use as a default section name.
-	If negative, cgit will discard the specified number of path elements
-	above the repo directory. Default value: "0".
-
-side-by-side-diffs::
-	If set to "1" shows side-by-side diffs instead of unidiffs per
-	default. Default value: "0".
-
-snapshots::
-	Text which specifies the default set of snapshot formats that cgit
-	generates links for. The value is a space-separated list of zero or
-	more of the values "tar", "tar.gz", "tar.bz2", "tar.lz", "tar.xz",
-	"tar.zst" and "zip". The special value "all" enables all snapshot
-	formats. Default value: none.
-	All compressors use default settings. Some settings can be influenced
-	with environment variables, for example set ZSTD_CLEVEL=10 in web
-	server environment for higher (but slower) zstd compression.
-
-source-filter::
-	Specifies a command which will be invoked to format plaintext blobs
-	in the tree view. The command will get the blob content on its STDIN
-	and the name of the blob as its only command line argument. The STDOUT
-	from the command will be included verbatim as the blob contents, i.e.
-	this can be used to implement e.g. syntax highlighting. Default value:
-	none. See also: "FILTER API".
-
-summary-branches::
-	Specifies the number of branches to display in the repository "summary"
-	view. Default value: "10".
-
-summary-log::
-	Specifies the number of log entries to display in the repository
-	"summary" view. Default value: "10".
-
-summary-tags::
-	Specifies the number of tags to display in the repository "summary"
-	view. Default value: "10".
-
-strict-export::
-	Filename which, if specified, needs to be present within the repository
-	for cgit to allow access to that repository. This can be used to emulate
-	gitweb's EXPORT_OK and STRICT_EXPORT functionality and limit cgit's
-	repositories to match those exported by git-daemon. This option must
-	be defined prior to scan-path.
-
-virtual-root::
-	Url which, if specified, will be used as root for all cgit links. It
-	will also cause cgit to generate 'virtual urls', i.e. urls like
-	'/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
-	value: none.
-	NOTE: cgit has recently learned how to use PATH_INFO to achieve the
-	same kind of virtual urls, so this option will probably be deprecated.
-
-
-REPOSITORY SETTINGS
--------------------
-repo.about-filter::
-	Override the default about-filter. Default value: none. See also:
-	"enable-filter-overrides". See also: "FILTER API".
-
-repo.branch-sort::
-	Flag which, when set to "age", enables date ordering in the branch ref
-	list, and when set to "name" enables ordering by branch name. Default
-	value: "name".
-
-repo.clone-url::
-	A list of space-separated urls which can be used to clone this repo.
-	Default value: none. See also: "MACRO EXPANSION".
-
-repo.commit-filter::
-	Override the default commit-filter. Default value: none. See also:
-	"enable-filter-overrides". See also: "FILTER API".
-
-repo.commit-sort::
-	Flag which, when set to "date", enables strict date ordering in the
-	commit log, and when set to "topo" enables strict topological
-	ordering. If unset, the default ordering of "git log" is used. Default
-	value: unset.
-
-repo.defbranch::
-	The name of the default branch for this repository. If no such branch
-	exists in the repository, the first branch name (when sorted) is used
-	as default instead. Default value: branch pointed to by HEAD, or
-	"master" if there is no suitable HEAD.
-
-repo.desc::
-	The value to show as repository description. Default value: none.
-
-repo.email-filter::
-	Override the default email-filter. Default value: none. See also:
-	"enable-filter-overrides". See also: "FILTER API".
-
-repo.enable-blame::
-	A flag which can be used to disable the global setting
-	`enable-blame'. Default value: none.
-
-repo.enable-commit-graph::
-	A flag which can be used to disable the global setting
-	`enable-commit-graph'. Default value: none.
-
-repo.enable-html-serving::
-	A flag which can be used to override the global setting
-	`enable-html-serving`. Default value: none.
-
-repo.enable-log-filecount::
-	A flag which can be used to disable the global setting
-	`enable-log-filecount'. Default value: none.
-
-repo.enable-log-linecount::
-	A flag which can be used to disable the global setting
-	`enable-log-linecount'. Default value: none.
-
-repo.enable-remote-branches::
-	Flag which, when set to "1", will make cgit display remote branches
-	in the summary and refs views. Default value: <enable-remote-branches>.
-
-repo.enable-subject-links::
-	A flag which can be used to override the global setting
-	`enable-subject-links'. Default value: none.
-
-repo.extra-head-content::
-	This value will be added verbatim to the head section of each page
-	displayed for this repo. Default value: none.
-
-repo.hide::
-	Flag which, when set to "1", hides the repository from the repository
-	index. The repository can still be accessed by providing a direct path.
-	Default value: "0". See also: "repo.ignore".
-
-repo.homepage::
-	The value to show as repository homepage. Default value: none.
-
-repo.ignore::
-	Flag which, when set to "1", ignores the repository. The repository
-	is not shown in the index and cannot be accessed by providing a direct
-	path. Default value: "0". See also: "repo.hide".
-
-repo.logo::
-	Url which specifies the source of an image which will be used as a logo
-	on this repo's pages. Default value: global logo.
-
-repo.logo-link::
-	Url loaded when clicking on the cgit logo image. If unspecified the
-	calculated url of the repository index page will be used. Default
-	value: global logo-link.
-
-repo.module-link::
-	Text which will be used as the formatstring for a hyperlink when a
-	submodule is printed in a directory listing. The arguments for the
-	formatstring are the path and SHA1 of the submodule commit. Default
-	value: <module-link>
-
-repo.module-link.<path>::
-	Text which will be used as the formatstring for a hyperlink when a
-	submodule with the specified subdirectory path is printed in a
-	directory listing. The only argument for the formatstring is the SHA1
-	of the submodule commit. Default value: none.
-
-repo.max-stats::
-	Override the default maximum statistics period. Valid values are equal
-	to the values specified for the global "max-stats" setting. Default
-	value: none.
-
-repo.name::
-	The value to show as repository name. Default value: <repo.url>.
-
-repo.owner::
-	A value used to identify the owner of the repository. Default value:
-	none.
-
-repo.owner-filter::
-	Override the default owner-filter. Default value: none. See also:
-	"enable-filter-overrides". See also: "FILTER API".
-
-repo.path::
-	An absolute path to the repository directory. For non-bare repositories
-	this is the .git-directory. Default value: none.
-
-repo.readme::
-	A path (relative to <repo.path>) which specifies a file to include
-	verbatim as the "About" page for this repo. You may also specify a
-	git refspec by head or by hash by prepending the refspec followed by
-	a colon. For example, "master:docs/readme.mkd". If the value begins
-	with a colon, i.e. ":docs/readme.rst", the default branch of the
-	repository will be used. Sharing any file will expose that entire
-	directory tree to the "/about/PATH" endpoints, so be sure that there
-	are no non-public files located in the same directory as the readme
-	file. Default value: <readme>.
-
-repo.section::
-	Override the current section name for this repository. Default value:
-	none.
-
-repo.snapshots::
-	A mask of snapshot formats for this repo that cgit generates links for,
-	restricted by the global "snapshots" setting. Default value:
-	<snapshots>.
-
-repo.snapshot-prefix::
-	Prefix to use for snapshot links instead of the repository basename.
-	For example, the "linux-stable" repository may wish to set this to
-	"linux" so that snapshots are in the format "linux-3.15.4" instead
-	of "linux-stable-3.15.4".  Default value: <empty> meaning to use
-	the repository basename.
-
-repo.source-filter::
-	Override the default source-filter. Default value: none. See also:
-	"enable-filter-overrides". See also: "FILTER API".
-
-repo.url::
-	The relative url used to access the repository. This must be the first
-	setting specified for each repo. Default value: none.
-
-
-REPOSITORY-SPECIFIC CGITRC FILE
--------------------------------
-When the option "scan-path" is used to auto-discover git repositories, cgit
-will try to parse the file "cgitrc" within any found repository. Such a
-repo-specific config file may contain any of the repo-specific options
-described above, except "repo.url" and "repo.path". Additionally, the "filter"
-options are only acknowledged in repo-specific config files when
-"enable-filter-overrides" is set to "1".
-
-Note: the "repo." prefix is dropped from the option names in repo-specific
-config files, e.g. "repo.desc" becomes "desc".
-
-
-FILTER API
-----------
-By default, filters are separate processes that are executed each time they
-are needed.  Alternative technologies may be used by prefixing the filter
-specification with the relevant string; available values are:
-
-'exec:'::
-	The default "one process per filter" mode.
-
-
-Parameters are provided to filters as follows.
-
-about filter::
-	This filter is given a single parameter: the filename of the source
-	file to filter. The filter can use the filename to determine (for
-	example) the type of syntax to follow when formatting the readme file.
-	The about text that is to be filtered is available on standard input
-	and the filtered text is expected on standard output.
-
-auth filter::
-	The authentication filter receives 12 parameters:
-	  - filter action, explained below, which specifies which action the
-	    filter is called for
-	  - http cookie
-	  - http method
-	  - http referer
-	  - http path
-	  - http https flag
-	  - cgit repo
-	  - cgit page
-	  - cgit url
-	  - cgit login url
-	When the filter action is "body", this filter must write to output the
-	HTML for displaying the login form, which POSTs to the login url. When
-	the filter action is "authenticate-cookie", this filter must validate
-	the http cookie and return a 0 if it is invalid or 1 if it is invalid,
-	in the exit code / close function. If the filter action is
-	"authenticate-post", this filter receives POST'd parameters on
-	standard input, and should write a complete CGI response, preferably
-	with a 302 redirect, and write to output one or more "Set-Cookie"
-	HTTP headers, each followed by a newline.
-
-commit filter::
-	This filter is given no arguments. The commit message text that is to
-	be filtered is available on standard input and the filtered text is
-	expected on standard output.
-
-email filter::
-	This filter is given two parameters: the email address of the relevant
-	author and a string indicating the originating page. The filter will
-	then receive the text string to format on standard input and is
-	expected to write to standard output the formatted text to be included
-	in the page.
-
-owner filter::
-	This filter is given no arguments.  The owner text is available on
-	standard input and the filter is expected to write to standard
-	output.  The output is included in the Owner column.
-
-source filter::
-	This filter is given a single parameter: the filename of the source
-	file to filter. The filter can use the filename to determine (for
-	example) the syntax highlighting mode. The contents of the source
-	file that is to be filtered is available on standard input and the
-	filtered contents is expected on standard output.
-
-
-All filters are handed the following environment variables:
-
-- CGIT_REPO_URL (from repo.url)
-- CGIT_REPO_NAME (from repo.name)
-- CGIT_REPO_PATH (from repo.path)
-- CGIT_REPO_OWNER (from repo.owner)
-- CGIT_REPO_DEFBRANCH (from repo.defbranch)
-- CGIT_REPO_SECTION (from repo.section)
-- CGIT_REPO_CLONE_URL (from repo.clone-url)
-
-If a setting is not defined for a repository and the corresponding global
-setting is also not defined (if applicable), then the corresponding
-environment variable will be unset.
-
-
-MACRO EXPANSION
----------------
-The following cgitrc options support a simple macro expansion feature,
-where tokens prefixed with "$" are replaced with the value of a similarly
-named environment variable:
-
-- cache-root
-- include
-- project-list
-- scan-path
-
-Macro expansion will also happen on the content of $CGIT_CONFIG, if
-defined.
-
-One usage of this feature is virtual hosting, which in its simplest form
-can be accomplished by adding the following line to /etc/cgitrc:
-
-	include=/etc/cgitrc.d/$HTTP_HOST
-
-The following options are expanded during request processing, and support
-the environment variables defined in "FILTER API":
-
-- clone-url
-- repo.clone-url
-
-
-CACHE
------
-
-All cache ttl values are in minutes. Negative ttl values indicate that a page
-type will never expire, and thus the first time a URL is accessed, the result
-will be cached indefinitely, even if the underlying git repository changes.
-Conversely, when a ttl value is zero, the cache is disabled for that
-particular page type, and the page type is never cached.
-
-SIGNATURES
-----------
-
-Cgit can host .asc signatures corresponding to various snapshot formats,
-through use of git notes. For example, the following command may be used to
-add a signature to a .tar.xz archive:
-
-    git notes --ref=refs/notes/signatures/tar.xz add -C "$(
-	gpg --output - --armor --detach-sign cgit-1.1.tar.xz |
-	git hash-object -w --stdin
-    )" v1.1
-
-If it is instead desirable to attach a signature of the underlying .tar, this
-will be linked, as a special case, beside a .tar.* link that does not have its
-own signature. For example, a signature of a tarball of the latest tag might
-be added with a similar command:
-
-    tag="$(git describe --abbrev=0)"
-    git notes --ref=refs/notes/signatures/tar add -C "$(
-        git archive --format tar --prefix "cgit-${tag#v}/" "$tag" |
-        gpg --output - --armor --detach-sign |
-        git hash-object -w --stdin
-    )" "$tag"
-
-Since git-archive(1) is expected to produce stable output between versions,
-this allows one to generate a long-term signature of the contents of a given
-tag.
-
-EXAMPLE CGITRC FILE
--------------------
-
-....
-# Enable caching of up to 1000 output entries
-cache-size=1000
-
-
-# Specify some default clone urls using macro expansion
-clone-url=git://foo.org/$CGIT_REPO_URL git@foo.org:$CGIT_REPO_URL
-
-# Specify the css url
-css=/css/cgit.css
-
-
-# Show owner on index page
-enable-index-owner=1
-
-
-# Allow http transport git clone
-enable-http-clone=1
-
-
-# Show extra links for each repository on the index page
-enable-index-links=1
-
-
-# Enable blame page and create links to it from tree page
-enable-blame=1
-
-
-# Enable ASCII art commit history graph on the log pages
-enable-commit-graph=1
-
-
-# Show number of affected files per commit on the log pages
-enable-log-filecount=1
-
-
-# Show number of added/removed lines per commit on the log pages
-enable-log-linecount=1
-
-
-# Sort branches by date
-branch-sort=age
-
-
-# Add a cgit favicon
-favicon=/favicon.ico
-
-
-# Use a custom logo
-logo=/img/mylogo.png
-
-
-# Enable statistics per week, month and quarter
-max-stats=quarter
-
-
-# Set the title and heading of the repository index page
-root-title=example.com git repositories
-
-
-# Set a subheading for the repository index page
-root-desc=tracking the foobar development
-
-
-# Include some more info about example.com on the index page
-root-readme=/var/www/htdocs/about.html
-
-
-# Allow download of tar.gz, tar.bz2 and zip-files
-snapshots=tar.gz tar.bz2 zip
-
-
-##
-## List of common mimetypes
-##
-
-mimetype.gif=image/gif
-mimetype.html=text/html
-mimetype.jpg=image/jpeg
-mimetype.jpeg=image/jpeg
-mimetype.pdf=application/pdf
-mimetype.png=image/png
-mimetype.svg=image/svg+xml
-
-
-# Highlight source code with python pygments-based highlighter
-source-filter=/var/www/cgit/filters/syntax-highlighting.py
-
-# Format markdown, restructuredtext, manpages, text files, and html files
-# through the right converters
-about-filter=/var/www/cgit/filters/about-formatting.sh
-
-##
-## Search for these files in the root of the default branch of repositories
-## for coming up with the about page:
-##
-readme=:README.md
-readme=:readme.md
-readme=:README.mkd
-readme=:readme.mkd
-readme=:README.rst
-readme=:readme.rst
-readme=:README.html
-readme=:readme.html
-readme=:README.htm
-readme=:readme.htm
-readme=:README.txt
-readme=:readme.txt
-readme=:README
-readme=:readme
-readme=:INSTALL.md
-readme=:install.md
-readme=:INSTALL.mkd
-readme=:install.mkd
-readme=:INSTALL.rst
-readme=:install.rst
-readme=:INSTALL.html
-readme=:install.html
-readme=:INSTALL.htm
-readme=:install.htm
-readme=:INSTALL.txt
-readme=:install.txt
-readme=:INSTALL
-readme=:install
-
-
-##
-## List of repositories.
-## PS: Any repositories listed when section is unset will not be
-##     displayed under a section heading
-## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
-##      and included like this:
-##        include=/etc/cgitrepos
-##
-
-
-repo.url=foo
-repo.path=/pub/git/foo.git
-repo.desc=the master foo repository
-repo.owner=fooman@example.com
-repo.readme=info/web/about.html
-
-
-repo.url=bar
-repo.path=/pub/git/bar.git
-repo.desc=the bars for your foo
-repo.owner=barman@example.com
-repo.readme=info/web/about.html
-
-
-# The next repositories will be displayed under the 'extras' heading
-section=extras
-
-
-repo.url=baz
-repo.path=/pub/git/baz.git
-repo.desc=a set of extensions for bar users
-
-repo.url=wiz
-repo.path=/pub/git/wiz.git
-repo.desc=the wizard of foo
-
-
-# Add some mirrored repositories
-section=mirrors
-
-
-repo.url=git
-repo.path=/pub/git/git.git
-repo.desc=the dscm
-
-
-repo.url=linux
-repo.path=/pub/git/linux.git
-repo.desc=the kernel
-
-# Disable adhoc downloads of this repo
-repo.snapshots=0
-
-# Disable line-counts for this repo
-repo.enable-log-linecount=0
-
-# Restrict the max statistics period for this repo
-repo.max-stats=month
-....
-
-
-BUGS
-----
-Comments currently cannot appear on the same line as a setting; the comment
-will be included as part of the value. E.g. this line:
-
-	robots=index  # allow indexing
-
-will generate the following html element:
-
-	<meta name='robots' content='index  # allow indexing'/>
-
-
-
-AUTHOR
-------
-Lars Hjemli <hjemli@gmail.com>
-Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/www/git.causal.agency/cgit/cmd.c b/www/git.causal.agency/cgit/cmd.c
deleted file mode 100644
index 0eb75b1d..00000000
--- a/www/git.causal.agency/cgit/cmd.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/* cmd.c: the cgit command dispatcher
- *
- * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "cmd.h"
-#include "cache.h"
-#include "ui-shared.h"
-#include "ui-atom.h"
-#include "ui-blame.h"
-#include "ui-blob.h"
-#include "ui-clone.h"
-#include "ui-commit.h"
-#include "ui-diff.h"
-#include "ui-log.h"
-#include "ui-patch.h"
-#include "ui-plain.h"
-#include "ui-refs.h"
-#include "ui-repolist.h"
-#include "ui-snapshot.h"
-#include "ui-stats.h"
-#include "ui-summary.h"
-#include "ui-tag.h"
-#include "ui-tree.h"
-
-static void HEAD_fn(void)
-{
-	cgit_clone_head();
-}
-
-static void atom_fn(void)
-{
-	cgit_print_atom(ctx.qry.head, ctx.qry.path, ctx.cfg.max_atom_items);
-}
-
-static void about_fn(void)
-{
-	if (ctx.repo) {
-		size_t path_info_len = ctx.env.path_info ? strlen(ctx.env.path_info) : 0;
-		if (!ctx.qry.path &&
-		    ctx.qry.url[strlen(ctx.qry.url) - 1] != '/' &&
-		    (!path_info_len || ctx.env.path_info[path_info_len - 1] != '/')) {
-			char *currenturl = cgit_currenturl();
-			char *redirect = fmtalloc("%s/", currenturl);
-			cgit_redirect(redirect, true);
-			free(currenturl);
-			free(redirect);
-		} else if (ctx.repo->readme.nr)
-			cgit_print_repo_readme(ctx.qry.path);
-		else if (ctx.repo->homepage)
-			cgit_redirect(ctx.repo->homepage, false);
-		else {
-			char *currenturl = cgit_currenturl();
-			char *redirect = fmtalloc("%s../", currenturl);
-			cgit_redirect(redirect, false);
-			free(currenturl);
-			free(redirect);
-		}
-	} else
-		cgit_print_site_readme();
-}
-
-static void blame_fn(void)
-{
-	if (ctx.repo->enable_blame)
-		cgit_print_blame();
-	else
-		cgit_print_error_page(403, "Forbidden", "Blame is disabled");
-}
-
-static void blob_fn(void)
-{
-	cgit_print_blob(ctx.qry.oid, ctx.qry.path, ctx.qry.head, 0);
-}
-
-static void commit_fn(void)
-{
-	cgit_print_commit(ctx.qry.oid, ctx.qry.path);
-}
-
-static void diff_fn(void)
-{
-	cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 0);
-}
-
-static void rawdiff_fn(void)
-{
-	cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 1);
-}
-
-static void info_fn(void)
-{
-	cgit_clone_info();
-}
-
-static void log_fn(void)
-{
-	cgit_print_log(ctx.qry.oid, ctx.qry.ofs, ctx.cfg.max_commit_count,
-		       ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1,
-		       ctx.repo->enable_commit_graph,
-		       ctx.repo->commit_sort);
-}
-
-static void ls_cache_fn(void)
-{
-	ctx.page.mimetype = "text/plain";
-	ctx.page.filename = "ls-cache.txt";
-	cgit_print_http_headers();
-	cache_ls(ctx.cfg.cache_root);
-}
-
-static void objects_fn(void)
-{
-	cgit_clone_objects();
-}
-
-static void repolist_fn(void)
-{
-	cgit_print_repolist();
-}
-
-static void patch_fn(void)
-{
-	cgit_print_patch(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path);
-}
-
-static void plain_fn(void)
-{
-	cgit_print_plain();
-}
-
-static void refs_fn(void)
-{
-	cgit_print_refs();
-}
-
-static void snapshot_fn(void)
-{
-	cgit_print_snapshot(ctx.qry.head, ctx.qry.oid, ctx.qry.path,
-			    ctx.qry.nohead);
-}
-
-static void stats_fn(void)
-{
-	cgit_show_stats();
-}
-
-static void summary_fn(void)
-{
-	cgit_print_summary();
-}
-
-static void tag_fn(void)
-{
-	cgit_print_tag(ctx.qry.oid);
-}
-
-static void tree_fn(void)
-{
-	cgit_print_tree(ctx.qry.oid, ctx.qry.path);
-}
-
-#define def_cmd(name, want_repo, want_vpath, is_clone) \
-	{#name, name##_fn, want_repo, want_vpath, is_clone}
-
-struct cgit_cmd *cgit_get_cmd(void)
-{
-	static struct cgit_cmd cmds[] = {
-		def_cmd(HEAD, 1, 0, 1),
-		def_cmd(atom, 1, 0, 0),
-		def_cmd(about, 0, 0, 0),
-		def_cmd(blame, 1, 1, 0),
-		def_cmd(blob, 1, 0, 0),
-		def_cmd(commit, 1, 1, 0),
-		def_cmd(diff, 1, 1, 0),
-		def_cmd(info, 1, 0, 1),
-		def_cmd(log, 1, 1, 0),
-		def_cmd(ls_cache, 0, 0, 0),
-		def_cmd(objects, 1, 0, 1),
-		def_cmd(patch, 1, 1, 0),
-		def_cmd(plain, 1, 0, 0),
-		def_cmd(rawdiff, 1, 1, 0),
-		def_cmd(refs, 1, 0, 0),
-		def_cmd(repolist, 0, 0, 0),
-		def_cmd(snapshot, 1, 0, 0),
-		def_cmd(stats, 1, 1, 0),
-		def_cmd(summary, 1, 0, 0),
-		def_cmd(tag, 1, 0, 0),
-		def_cmd(tree, 1, 1, 0),
-	};
-	int i;
-
-	if (ctx.qry.page == NULL) {
-		if (ctx.repo)
-			ctx.qry.page = "summary";
-		else
-			ctx.qry.page = "repolist";
-	}
-
-	for (i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
-		if (!strcmp(ctx.qry.page, cmds[i].name))
-			return &cmds[i];
-	return NULL;
-}
diff --git a/www/git.causal.agency/cgit/cmd.h b/www/git.causal.agency/cgit/cmd.h
deleted file mode 100644
index 6249b1d8..00000000
--- a/www/git.causal.agency/cgit/cmd.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef CMD_H
-#define CMD_H
-
-typedef void (*cgit_cmd_fn)(void);
-
-struct cgit_cmd {
-	const char *name;
-	cgit_cmd_fn fn;
-	unsigned int want_repo:1,
-		want_vpath:1,
-		is_clone:1;
-};
-
-extern struct cgit_cmd *cgit_get_cmd(void);
-
-#endif /* CMD_H */
diff --git a/www/git.causal.agency/cgit/configfile.c b/www/git.causal.agency/cgit/configfile.c
deleted file mode 100644
index e0391091..00000000
--- a/www/git.causal.agency/cgit/configfile.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/* configfile.c: parsing of config files
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include <git-compat-util.h>
-#include "configfile.h"
-
-static int next_char(FILE *f)
-{
-	int c = fgetc(f);
-	if (c == '\r') {
-		c = fgetc(f);
-		if (c != '\n') {
-			ungetc(c, f);
-			c = '\r';
-		}
-	}
-	return c;
-}
-
-static void skip_line(FILE *f)
-{
-	int c;
-
-	while ((c = next_char(f)) && c != '\n' && c != EOF)
-		;
-}
-
-static int read_config_line(FILE *f, struct strbuf *name, struct strbuf *value)
-{
-	int c = next_char(f);
-
-	strbuf_reset(name);
-	strbuf_reset(value);
-
-	/* Skip comments and preceding spaces. */
-	for(;;) {
-		if (c == EOF)
-			return 0;
-		else if (c == '#' || c == ';')
-			skip_line(f);
-		else if (!isspace(c))
-			break;
-		c = next_char(f);
-	}
-
-	/* Read variable name. */
-	while (c != '=') {
-		if (c == '\n' || c == EOF)
-			return 0;
-		strbuf_addch(name, c);
-		c = next_char(f);
-	}
-
-	/* Read variable value. */
-	c = next_char(f);
-	while (c != '\n' && c != EOF) {
-		strbuf_addch(value, c);
-		c = next_char(f);
-	}
-
-	return 1;
-}
-
-int parse_configfile(const char *filename, configfile_value_fn fn)
-{
-	static int nesting;
-	struct strbuf name = STRBUF_INIT;
-	struct strbuf value = STRBUF_INIT;
-	FILE *f;
-
-	/* cancel deeply nested include-commands */
-	if (nesting > 8)
-		return -1;
-	if (!(f = fopen(filename, "r")))
-		return -1;
-	nesting++;
-	while (read_config_line(f, &name, &value))
-		fn(name.buf, value.buf);
-	nesting--;
-	fclose(f);
-	strbuf_release(&name);
-	strbuf_release(&value);
-	return 0;
-}
-
diff --git a/www/git.causal.agency/cgit/configfile.h b/www/git.causal.agency/cgit/configfile.h
deleted file mode 100644
index af7ca197..00000000
--- a/www/git.causal.agency/cgit/configfile.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef CONFIGFILE_H
-#define CONFIGFILE_H
-
-#include "cgit.h"
-
-typedef void (*configfile_value_fn)(const char *name, const char *value);
-
-extern int parse_configfile(const char *filename, configfile_value_fn fn);
-
-#endif /* CONFIGFILE_H */
diff --git a/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile b/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile
deleted file mode 100755
index 2f72ae9c..00000000
--- a/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-#
-# An example hook to update the "agefile" for CGit's idle time calculation.
-#
-# This hook assumes that you are using the default agefile location of
-# "info/web/last-modified".  If you change the value in your cgitrc then you
-# must also change it here.
-#
-# To install the hook, copy (or link) it to the file "hooks/post-receive" in
-# each of your repositories.
-#
-
-agefile="$(git rev-parse --git-dir)"/info/web/last-modified
-
-mkdir -p "$(dirname "$agefile")" &&
-git for-each-ref \
-	--sort=-authordate --count=1 \
-	--format='%(authordate:iso8601)' \
-	>"$agefile"
diff --git a/www/git.causal.agency/cgit/filter.c b/www/git.causal.agency/cgit/filter.c
deleted file mode 100644
index 2b6c838e..00000000
--- a/www/git.causal.agency/cgit/filter.c
+++ /dev/null
@@ -1,222 +0,0 @@
-/* filter.c: filter framework functions
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "html.h"
-
-static inline void reap_filter(struct cgit_filter *filter)
-{
-	if (filter && filter->cleanup)
-		filter->cleanup(filter);
-}
-
-void cgit_cleanup_filters(void)
-{
-	int i;
-	reap_filter(ctx.cfg.about_filter);
-	reap_filter(ctx.cfg.commit_filter);
-	reap_filter(ctx.cfg.source_filter);
-	reap_filter(ctx.cfg.email_filter);
-	reap_filter(ctx.cfg.owner_filter);
-	reap_filter(ctx.cfg.auth_filter);
-	for (i = 0; i < cgit_repolist.count; ++i) {
-		reap_filter(cgit_repolist.repos[i].about_filter);
-		reap_filter(cgit_repolist.repos[i].commit_filter);
-		reap_filter(cgit_repolist.repos[i].source_filter);
-		reap_filter(cgit_repolist.repos[i].email_filter);
-		reap_filter(cgit_repolist.repos[i].owner_filter);
-	}
-}
-
-static int open_exec_filter(struct cgit_filter *base, va_list ap)
-{
-	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
-	int pipe_fh[2];
-	int i;
-
-	for (i = 0; i < filter->base.argument_count; i++)
-		filter->argv[i + 1] = va_arg(ap, char *);
-
-	chk_zero(fflush(stdout), "unable to flush STDOUT");
-	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
-		"Unable to duplicate STDOUT");
-	chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
-	filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
-	if (filter->pid == 0) {
-		close(pipe_fh[1]);
-		chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO),
-			"Unable to use pipe as STDIN");
-		execvp(filter->cmd, filter->argv);
-		die_errno("Unable to exec subprocess %s", filter->cmd);
-	}
-	close(pipe_fh[0]);
-	chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO),
-		"Unable to use pipe as STDOUT");
-	close(pipe_fh[1]);
-	return 0;
-}
-
-static int close_exec_filter(struct cgit_filter *base)
-{
-	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
-	int i, exit_status = 0;
-
-	chk_zero(fflush(stdout), "unable to flush STDOUT");
-	chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
-		"Unable to restore STDOUT");
-	close(filter->old_stdout);
-	if (filter->pid < 0)
-		goto done;
-	waitpid(filter->pid, &exit_status, 0);
-	if (WIFEXITED(exit_status))
-		goto done;
-	die("Subprocess %s exited abnormally", filter->cmd);
-
-done:
-	for (i = 0; i < filter->base.argument_count; i++)
-		filter->argv[i + 1] = NULL;
-	return WEXITSTATUS(exit_status);
-
-}
-
-static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix)
-{
-	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
-	fprintf(f, "%sexec:%s\n", prefix, filter->cmd);
-}
-
-static void cleanup_exec_filter(struct cgit_filter *base)
-{
-	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
-	if (filter->argv) {
-		free(filter->argv);
-		filter->argv = NULL;
-	}
-	if (filter->cmd) {
-		free(filter->cmd);
-		filter->cmd = NULL;
-	}
-}
-
-static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count)
-{
-	struct cgit_exec_filter *f;
-	int args_size = 0;
-
-	f = xmalloc(sizeof(*f));
-	/* We leave argv for now and assign it below. */
-	cgit_exec_filter_init(f, xstrdup(cmd), NULL);
-	f->base.argument_count = argument_count;
-	args_size = (2 + argument_count) * sizeof(char *);
-	f->argv = xmalloc(args_size);
-	memset(f->argv, 0, args_size);
-	f->argv[0] = f->cmd;
-	return &f->base;
-}
-
-void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
-{
-	memset(filter, 0, sizeof(*filter));
-	filter->base.open = open_exec_filter;
-	filter->base.close = close_exec_filter;
-	filter->base.fprintf = fprintf_exec_filter;
-	filter->base.cleanup = cleanup_exec_filter;
-	filter->cmd = cmd;
-	filter->argv = argv;
-	/* The argument count for open_filter is zero by default, unless called from new_filter, above. */
-	filter->base.argument_count = 0;
-}
-
-int cgit_open_filter(struct cgit_filter *filter, ...)
-{
-	int result;
-	va_list ap;
-	if (!filter)
-		return 0;
-	va_start(ap, filter);
-	result = filter->open(filter, ap);
-	va_end(ap);
-	return result;
-}
-
-int cgit_close_filter(struct cgit_filter *filter)
-{
-	if (!filter)
-		return 0;
-	return filter->close(filter);
-}
-
-void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
-{
-	filter->fprintf(filter, f, prefix);
-}
-
-
-
-static const struct {
-	const char *prefix;
-	struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
-} filter_specs[] = {
-	{ "exec", new_exec_filter },
-};
-
-struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
-{
-	char *colon;
-	int i;
-	size_t len;
-	int argument_count;
-
-	if (!cmd || !cmd[0])
-		return NULL;
-
-	colon = strchr(cmd, ':');
-	len = colon - cmd;
-	/*
-	 * In case we're running on Windows, don't allow a single letter before
-	 * the colon.
-	 */
-	if (len == 1)
-		colon = NULL;
-
-	switch (filtertype) {
-		case AUTH:
-			argument_count = 12;
-			break;
-
-		case EMAIL:
-			argument_count = 2;
-			break;
-
-		case OWNER:
-			argument_count = 0;
-			break;
-
-		case SOURCE:
-		case ABOUT:
-			argument_count = 1;
-			break;
-
-		case COMMIT:
-		default:
-			argument_count = 0;
-			break;
-	}
-
-	/* If no prefix is given, exec filter is the default. */
-	if (!colon)
-		return new_exec_filter(cmd, argument_count);
-
-	for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
-		if (len == strlen(filter_specs[i].prefix) &&
-		    !strncmp(filter_specs[i].prefix, cmd, len))
-			return filter_specs[i].ctor(colon + 1, argument_count);
-	}
-
-	die("Invalid filter type: %.*s", (int) len, cmd);
-}
diff --git a/www/git.causal.agency/cgit/filters/about-formatting.sh b/www/git.causal.agency/cgit/filters/about-formatting.sh
deleted file mode 100755
index 85daf9c2..00000000
--- a/www/git.causal.agency/cgit/filters/about-formatting.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-
-# This may be used with the about-filter or repo.about-filter setting in cgitrc.
-# It passes formatting of about pages to differing programs, depending on the usage.
-
-# Markdown support requires python and markdown-python.
-# RestructuredText support requires python and docutils.
-# Man page support requires groff.
-
-# The following environment variables can be used to retrieve the configuration
-# of the repository for which this script is called:
-# CGIT_REPO_URL        ( = repo.url       setting )
-# CGIT_REPO_NAME       ( = repo.name      setting )
-# CGIT_REPO_PATH       ( = repo.path      setting )
-# CGIT_REPO_OWNER      ( = repo.owner     setting )
-# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
-# CGIT_REPO_SECTION    ( = section        setting )
-# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
-
-cd "$(dirname $0)/html-converters/"
-case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in
-	*.markdown|*.mdown|*.md|*.mkd) exec ./md2html; ;;
-	*.rst) exec ./rst2html; ;;
-	*.[1-9]) exec ./man2html; ;;
-	*.htm|*.html) exec cat; ;;
-	*.txt|*) exec ./txt2html; ;;
-esac
diff --git a/www/git.causal.agency/cgit/filters/commit-links.sh b/www/git.causal.agency/cgit/filters/commit-links.sh
deleted file mode 100755
index 796ac308..00000000
--- a/www/git.causal.agency/cgit/filters/commit-links.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-# This script can be used to generate links in commit messages.
-#
-# To use this script, refer to this file with either the commit-filter or the
-# repo.commit-filter options in cgitrc.
-#
-# The following environment variables can be used to retrieve the configuration
-# of the repository for which this script is called:
-# CGIT_REPO_URL        ( = repo.url       setting )
-# CGIT_REPO_NAME       ( = repo.name      setting )
-# CGIT_REPO_PATH       ( = repo.path      setting )
-# CGIT_REPO_OWNER      ( = repo.owner     setting )
-# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
-# CGIT_REPO_SECTION    ( = section        setting )
-# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
-#
-
-regex=''
-
-# This expression generates links to commits referenced by their SHA1.
-regex=$regex'
-s|\b([0-9a-fA-F]{7,64})\b|<a href="./?id=\1">\1</a>|g'
-
-# This expression generates links to a fictional bugtracker.
-regex=$regex'
-s|#([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g'
-
-sed -re "$regex"
diff --git a/www/git.causal.agency/cgit/filters/email-gravatar.py b/www/git.causal.agency/cgit/filters/email-gravatar.py
deleted file mode 100755
index 012113c5..00000000
--- a/www/git.causal.agency/cgit/filters/email-gravatar.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env python3
-
-# This script may be used with the email-filter or repo.email-filter settings in cgitrc.
-#
-# The following environment variables can be used to retrieve the configuration
-# of the repository for which this script is called:
-# CGIT_REPO_URL        ( = repo.url       setting )
-# CGIT_REPO_NAME       ( = repo.name      setting )
-# CGIT_REPO_PATH       ( = repo.path      setting )
-# CGIT_REPO_OWNER      ( = repo.owner     setting )
-# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
-# CGIT_REPO_SECTION    ( = section        setting )
-# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
-#
-# It receives an email address on argv[1] and text on stdin. It prints
-# to stdout that text prepended by a gravatar at 10pt.
-
-import sys
-import hashlib
-import codecs
-
-email = sys.argv[1].lower().strip()
-if email[0] == '<':
-        email = email[1:]
-if email[-1] == '>':
-        email = email[0:-1]
-
-page = sys.argv[2]
-
-sys.stdin = codecs.getreader("utf-8")(sys.stdin.detach())
-sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
-
-md5 = hashlib.md5(email.encode()).hexdigest()
-text = sys.stdin.read().strip()
-
-print("<img src='//www.gravatar.com/avatar/" + md5 + "?s=13&amp;d=retro' width='13' height='13' alt='Gravatar' /> " + text)
diff --git a/www/git.causal.agency/cgit/filters/html-converters/man2html b/www/git.causal.agency/cgit/filters/html-converters/man2html
deleted file mode 100755
index 0ef78841..00000000
--- a/www/git.causal.agency/cgit/filters/html-converters/man2html
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-echo "<div style=\"font-family: monospace\">"
-groff -mandoc -T html -P -r -P -l | egrep -v '(<html>|<head>|<meta|<title>|</title>|</head>|<body>|</body>|</html>|<!DOCTYPE|"http://www.w3.org)'
-echo "</div>"
diff --git a/www/git.causal.agency/cgit/filters/html-converters/md2html b/www/git.causal.agency/cgit/filters/html-converters/md2html
deleted file mode 100755
index 59f43a84..00000000
--- a/www/git.causal.agency/cgit/filters/html-converters/md2html
+++ /dev/null
@@ -1,304 +0,0 @@
-#!/usr/bin/env python3
-import markdown
-import sys
-import io
-from pygments.formatters import HtmlFormatter
-from markdown.extensions.toc import TocExtension
-sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
-sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
-sys.stdout.write('''
-<style>
-.markdown-body {
-    font-size: 14px;
-    line-height: 1.6;
-    overflow: hidden;
-}
-.markdown-body>*:first-child {
-    margin-top: 0 !important;
-}
-.markdown-body>*:last-child {
-    margin-bottom: 0 !important;
-}
-.markdown-body a.absent {
-    color: #c00;
-}
-.markdown-body a.anchor {
-    display: block;
-    padding-left: 30px;
-    margin-left: -30px;
-    cursor: pointer;
-    position: absolute;
-    top: 0;
-    left: 0;
-    bottom: 0;
-}
-.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
-    margin: 20px 0 10px;
-    padding: 0;
-    font-weight: bold;
-    -webkit-font-smoothing: antialiased;
-    cursor: text;
-    position: relative;
-}
-.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
-    display: none;
-    color: #000;
-}
-.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
-    text-decoration: none;
-    line-height: 1;
-    padding-left: 0;
-    margin-left: -22px;
-    top: 15%;
-}
-.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
-    display: inline-block;
-}
-div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink {
-    color: black;
-}
-.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
-    font-size: inherit;
-}
-.markdown-body h1 {
-    font-size: 28px;
-    color: #000;
-}
-.markdown-body h2 {
-    font-size: 24px;
-    border-bottom: 1px solid #ccc;
-    color: #000;
-}
-.markdown-body h3 {
-    font-size: 18px;
-}
-.markdown-body h4 {
-    font-size: 16px;
-}
-.markdown-body h5 {
-    font-size: 14px;
-}
-.markdown-body h6 {
-    color: #777;
-    font-size: 14px;
-}
-.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
-    margin: 15px 0;
-}
-.markdown-body hr {
-    border: 2px solid #ccc;
-}
-.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
-    margin-top: 0;
-    padding-top: 0;
-}
-.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
-    margin-top: 0;
-    padding-top: 0;
-}
-.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p {
-    margin-top: 0;
-}
-.markdown-body li p.first {
-    display: inline-block;
-}
-.markdown-body ul, .markdown-body ol {
-    padding-left: 30px;
-}
-.markdown-body ul.no-list, .markdown-body ol.no-list {
-    list-style-type: none;
-    padding: 0;
-}
-.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type {
-    margin-top: 0px;
-}
-.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type {
-    margin-bottom: 0;
-}
-.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {
-    margin-bottom: 0;
-}
-.markdown-body dl {
-    padding: 0;
-}
-.markdown-body dl dt {
-    font-size: 14px;
-    font-weight: bold;
-    font-style: italic;
-    padding: 0;
-    margin: 15px 0 5px;
-}
-.markdown-body dl dt:first-child {
-    padding: 0;
-}
-.markdown-body dl dt>:first-child {
-    margin-top: 0px;
-}
-.markdown-body dl dt>:last-child {
-    margin-bottom: 0px;
-}
-.markdown-body dl dd {
-    margin: 0 0 15px;
-    padding: 0 15px;
-}
-.markdown-body dl dd>:first-child {
-    margin-top: 0px;
-}
-.markdown-body dl dd>:last-child {
-    margin-bottom: 0px;
-}
-.markdown-body blockquote {
-    border-left: 4px solid #DDD;
-    padding: 0 15px;
-    color: #777;
-}
-.markdown-body blockquote>:first-child {
-    margin-top: 0px;
-}
-.markdown-body blockquote>:last-child {
-    margin-bottom: 0px;
-}
-.markdown-body table th {
-    font-weight: bold;
-}
-.markdown-body table th, .markdown-body table td {
-    border: 1px solid #ccc;
-    padding: 6px 13px;
-}
-.markdown-body table tr {
-    border-top: 1px solid #ccc;
-    background-color: #fff;
-}
-.markdown-body table tr:nth-child(2n) {
-    background-color: #f8f8f8;
-}
-.markdown-body img {
-    max-width: 100%;
-    -moz-box-sizing: border-box;
-    box-sizing: border-box;
-}
-.markdown-body span.frame {
-    display: block;
-    overflow: hidden;
-}
-.markdown-body span.frame>span {
-    border: 1px solid #ddd;
-    display: block;
-    float: left;
-    overflow: hidden;
-    margin: 13px 0 0;
-    padding: 7px;
-    width: auto;
-}
-.markdown-body span.frame span img {
-    display: block;
-    float: left;
-}
-.markdown-body span.frame span span {
-    clear: both;
-    color: #333;
-    display: block;
-    padding: 5px 0 0;
-}
-.markdown-body span.align-center {
-    display: block;
-    overflow: hidden;
-    clear: both;
-}
-.markdown-body span.align-center>span {
-    display: block;
-    overflow: hidden;
-    margin: 13px auto 0;
-    text-align: center;
-}
-.markdown-body span.align-center span img {
-    margin: 0 auto;
-    text-align: center;
-}
-.markdown-body span.align-right {
-    display: block;
-    overflow: hidden;
-    clear: both;
-}
-.markdown-body span.align-right>span {
-    display: block;
-    overflow: hidden;
-    margin: 13px 0 0;
-    text-align: right;
-}
-.markdown-body span.align-right span img {
-    margin: 0;
-    text-align: right;
-}
-.markdown-body span.float-left {
-    display: block;
-    margin-right: 13px;
-    overflow: hidden;
-    float: left;
-}
-.markdown-body span.float-left span {
-    margin: 13px 0 0;
-}
-.markdown-body span.float-right {
-    display: block;
-    margin-left: 13px;
-    overflow: hidden;
-    float: right;
-}
-.markdown-body span.float-right>span {
-    display: block;
-    overflow: hidden;
-    margin: 13px auto 0;
-    text-align: right;
-}
-.markdown-body code, .markdown-body tt {
-    margin: 0 2px;
-    padding: 0px 5px;
-    border: 1px solid #eaeaea;
-    background-color: #f8f8f8;
-    border-radius: 3px;
-}
-.markdown-body code {
-    white-space: nowrap;
-}
-.markdown-body pre>code {
-    margin: 0;
-    padding: 0;
-    white-space: pre;
-    border: none;
-    background: transparent;
-}
-.markdown-body .highlight pre, .markdown-body pre {
-    background-color: #f8f8f8;
-    border: 1px solid #ccc;
-    font-size: 13px;
-    line-height: 19px;
-    overflow: auto;
-    padding: 6px 10px;
-    border-radius: 3px;
-}
-.markdown-body pre code, .markdown-body pre tt {
-    margin: 0;
-    padding: 0;
-    background-color: transparent;
-    border: none;
-}
-''')
-sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
-sys.stdout.write('''
-</style>   
-''')
-sys.stdout.write("<div class='markdown-body'>")
-sys.stdout.flush()
-# Note: you may want to run this through bleach for sanitization
-markdown.markdownFromFile(
-	output_format="html5",
-	extensions=[
-		"markdown.extensions.fenced_code",
-		"markdown.extensions.codehilite",
-		"markdown.extensions.tables",
-		"markdown.extensions.sane_lists",
-		TocExtension(anchorlink=True)],
-	extension_configs={
-		"markdown.extensions.codehilite":{"css_class":"highlight"}})
-sys.stdout.write("</div>")
diff --git a/www/git.causal.agency/cgit/filters/html-converters/rst2html b/www/git.causal.agency/cgit/filters/html-converters/rst2html
deleted file mode 100755
index 02d90f81..00000000
--- a/www/git.causal.agency/cgit/filters/html-converters/rst2html
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-exec rst2html.py --template <(echo -e "%(stylesheet)s\n%(body_pre_docinfo)s\n%(docinfo)s\n%(body)s")
diff --git a/www/git.causal.agency/cgit/filters/html-converters/txt2html b/www/git.causal.agency/cgit/filters/html-converters/txt2html
deleted file mode 100755
index 495eeceb..00000000
--- a/www/git.causal.agency/cgit/filters/html-converters/txt2html
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-echo "<pre>"
-sed "s|&|\\&amp;|g;s|'|\\&apos;|g;s|\"|\\&quot;|g;s|<|\\&lt;|g;s|>|\\&gt;|g"
-echo "</pre>"
diff --git a/www/git.causal.agency/cgit/filters/syntax-highlighting.py b/www/git.causal.agency/cgit/filters/syntax-highlighting.py
deleted file mode 100755
index e912594c..00000000
--- a/www/git.causal.agency/cgit/filters/syntax-highlighting.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python3
-
-# This script uses Pygments and Python3. You must have both installed
-# for this to work.
-#
-# http://pygments.org/
-# http://python.org/
-#
-# It may be used with the source-filter or repo.source-filter settings
-# in cgitrc.
-#
-# The following environment variables can be used to retrieve the
-# configuration of the repository for which this script is called:
-# CGIT_REPO_URL        ( = repo.url       setting )
-# CGIT_REPO_NAME       ( = repo.name      setting )
-# CGIT_REPO_PATH       ( = repo.path      setting )
-# CGIT_REPO_OWNER      ( = repo.owner     setting )
-# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
-# CGIT_REPO_SECTION    ( = section        setting )
-# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
-
-
-import sys
-import io
-from pygments import highlight
-from pygments.util import ClassNotFound
-from pygments.lexers import TextLexer
-from pygments.lexers import guess_lexer
-from pygments.lexers import guess_lexer_for_filename
-from pygments.formatters import HtmlFormatter
-
-
-sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace')
-sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
-data = sys.stdin.read()
-filename = sys.argv[1]
-formatter = HtmlFormatter(style='pastie', nobackground=True)
-
-try:
-	lexer = guess_lexer_for_filename(filename, data)
-except ClassNotFound:
-	# check if there is any shebang
-	if data[0:2] == '#!':
-		lexer = guess_lexer(data)
-	else:
-		lexer = TextLexer()
-except TypeError:
-	lexer = TextLexer()
-
-# highlight! :-)
-# printout pygments' css definitions as well
-sys.stdout.write('<style>')
-sys.stdout.write(formatter.get_style_defs('.highlight'))
-sys.stdout.write('</style>')
-sys.stdout.write(highlight(data, lexer, formatter, outfile=None))
diff --git a/www/git.causal.agency/cgit/filters/syntax-highlighting.sh b/www/git.causal.agency/cgit/filters/syntax-highlighting.sh
deleted file mode 100755
index 840bc34f..00000000
--- a/www/git.causal.agency/cgit/filters/syntax-highlighting.sh
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/bin/sh
-# This script can be used to implement syntax highlighting in the cgit
-# tree-view by referring to this file with the source-filter or repo.source-
-# filter options in cgitrc.
-#
-# This script requires a shell supporting the ${var##pattern} syntax.
-# It is supported by at least dash and bash, however busybox environments
-# might have to use an external call to sed instead.
-#
-# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
-# highlighting, so you'll probably want something like the following included
-# in your css file:
-#
-# Style definition file generated by highlight 2.4.8, http://www.andre-simon.de/
-#
-# table.blob .num  { color:#2928ff; }
-# table.blob .esc  { color:#ff00ff; }
-# table.blob .str  { color:#ff0000; }
-# table.blob .dstr { color:#818100; }
-# table.blob .slc  { color:#838183; font-style:italic; }
-# table.blob .com  { color:#838183; font-style:italic; }
-# table.blob .dir  { color:#008200; }
-# table.blob .sym  { color:#000000; }
-# table.blob .kwa  { color:#000000; font-weight:bold; }
-# table.blob .kwb  { color:#830000; }
-# table.blob .kwc  { color:#000000; font-weight:bold; }
-# table.blob .kwd  { color:#010181; }
-#
-#
-# Style definition file generated by highlight 2.6.14, http://www.andre-simon.de/
-#
-# body.hl  { background-color:#ffffff; }
-# pre.hl   { color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';}
-# .hl.num  { color:#2928ff; }
-# .hl.esc  { color:#ff00ff; }
-# .hl.str  { color:#ff0000; }
-# .hl.dstr { color:#818100; }
-# .hl.slc  { color:#838183; font-style:italic; }
-# .hl.com  { color:#838183; font-style:italic; }
-# .hl.dir  { color:#008200; }
-# .hl.sym  { color:#000000; }
-# .hl.line { color:#555555; }
-# .hl.mark { background-color:#ffffbb;}
-# .hl.kwa  { color:#000000; font-weight:bold; }
-# .hl.kwb  { color:#830000; }
-# .hl.kwc  { color:#000000; font-weight:bold; }
-# .hl.kwd  { color:#010181; }
-#
-#
-# Style definition file generated by highlight 3.8, http://www.andre-simon.de/
-#
-# body.hl { background-color:#e0eaee; }
-# pre.hl  { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New';}
-# .hl.num { color:#b07e00; }
-# .hl.esc { color:#ff00ff; }
-# .hl.str { color:#bf0303; }
-# .hl.pps { color:#818100; }
-# .hl.slc { color:#838183; font-style:italic; }
-# .hl.com { color:#838183; font-style:italic; }
-# .hl.ppc { color:#008200; }
-# .hl.opt { color:#000000; }
-# .hl.lin { color:#555555; }
-# .hl.kwa { color:#000000; font-weight:bold; }
-# .hl.kwb { color:#0057ae; }
-# .hl.kwc { color:#000000; font-weight:bold; }
-# .hl.kwd { color:#010181; }
-#
-#
-# Style definition file generated by highlight 3.13, http://www.andre-simon.de/
-#
-# body.hl { background-color:#e0eaee; }
-# pre.hl  { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New',monospace;}
-# .hl.num { color:#b07e00; }
-# .hl.esc { color:#ff00ff; }
-# .hl.str { color:#bf0303; }
-# .hl.pps { color:#818100; }
-# .hl.slc { color:#838183; font-style:italic; }
-# .hl.com { color:#838183; font-style:italic; }
-# .hl.ppc { color:#008200; }
-# .hl.opt { color:#000000; }
-# .hl.ipl { color:#0057ae; }
-# .hl.lin { color:#555555; }
-# .hl.kwa { color:#000000; font-weight:bold; }
-# .hl.kwb { color:#0057ae; }
-# .hl.kwc { color:#000000; font-weight:bold; }
-# .hl.kwd { color:#010181; }
-#
-#
-# The following environment variables can be used to retrieve the configuration
-# of the repository for which this script is called:
-# CGIT_REPO_URL        ( = repo.url       setting )
-# CGIT_REPO_NAME       ( = repo.name      setting )
-# CGIT_REPO_PATH       ( = repo.path      setting )
-# CGIT_REPO_OWNER      ( = repo.owner     setting )
-# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
-# CGIT_REPO_SECTION    ( = section        setting )
-# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
-#
-
-# store filename and extension in local vars
-BASENAME="$1"
-EXTENSION="${BASENAME##*.}"
-
-[ "${BASENAME}" = "${EXTENSION}" ] && EXTENSION=txt
-[ -z "${EXTENSION}" ] && EXTENSION=txt
-
-# map Makefile and Makefile.* to .mk
-[ "${BASENAME%%.*}" = "Makefile" ] && EXTENSION=mk
-
-# highlight versions 2 and 3 have different commandline options. Specifically,
-# the -X option that is used for version 2 is replaced by the -O xhtml option
-# for version 3.
-#
-# Version 2 can be found (for example) on EPEL 5, while version 3 can be
-# found (for example) on EPEL 6.
-#
-# This is for version 2
-exec highlight --force -f -I -X -S "$EXTENSION" 2>/dev/null
-
-# This is for version 3
-#exec highlight --force -f -I -O xhtml -S "$EXTENSION" 2>/dev/null
diff --git a/www/git.causal.agency/cgit/gen-version.sh b/www/git.causal.agency/cgit/gen-version.sh
deleted file mode 100755
index 80cf49af..00000000
--- a/www/git.causal.agency/cgit/gen-version.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh
-
-# Get version-info specified in Makefile
-V=$1
-
-# Use `git describe` to get current version if we're inside a git repo
-if test "$(git rev-parse --git-dir 2>/dev/null)" = '.git'
-then
-	V=$(git describe --abbrev=4 HEAD 2>/dev/null)
-fi
-
-new="CGIT_VERSION = $V"
-old=$(cat VERSION 2>/dev/null)
-
-# Exit if VERSION is uptodate
-test "$old" = "$new" && exit 0
-
-# Update VERSION with new version-info
-echo "$new" > VERSION
-cat VERSION
diff --git a/www/git.causal.agency/cgit/html.c b/www/git.causal.agency/cgit/html.c
deleted file mode 100644
index cefcf5e7..00000000
--- a/www/git.causal.agency/cgit/html.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/* html.c: helper functions for html output
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "html.h"
-#include "url.h"
-
-/* Percent-encoding of each character, except: a-zA-Z0-9!$()*,./:;@- */
-static const char* url_escape_table[256] = {
-	"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
-	"%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
-	"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
-	"%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
-	"%20", NULL,  "%22", "%23", NULL,  "%25", "%26", "%27",
-	NULL,  NULL,  NULL,  "%2b", NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  NULL,  "%3c", "%3d", "%3e", "%3f",
-	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  NULL,  "%5c", NULL,  "%5e", NULL,
-	"%60", NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
-	NULL,  NULL,  NULL,  "%7b", "%7c", "%7d", NULL,  "%7f",
-	"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
-	"%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
-	"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
-	"%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
-	"%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
-	"%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
-	"%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
-	"%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
-	"%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
-	"%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
-	"%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
-	"%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
-	"%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
-	"%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
-	"%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
-	"%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
-};
-
-char *fmt(const char *format, ...)
-{
-	static char buf[8][1024];
-	static int bufidx;
-	int len;
-	va_list args;
-
-	bufidx++;
-	bufidx &= 7;
-
-	va_start(args, format);
-	len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
-	va_end(args);
-	if (len > sizeof(buf[bufidx])) {
-		fprintf(stderr, "[html.c] string truncated: %s\n", format);
-		exit(1);
-	}
-	return buf[bufidx];
-}
-
-char *fmtalloc(const char *format, ...)
-{
-	struct strbuf sb = STRBUF_INIT;
-	va_list args;
-
-	va_start(args, format);
-	strbuf_vaddf(&sb, format, args);
-	va_end(args);
-
-	return strbuf_detach(&sb, NULL);
-}
-
-void html_raw(const char *data, size_t size)
-{
-	if (fwrite(data, 1, size, stdout) != size)
-		die_errno("write error on html output");
-}
-
-void html(const char *txt)
-{
-	html_raw(txt, strlen(txt));
-}
-
-void htmlf(const char *format, ...)
-{
-	va_list args;
-	struct strbuf buf = STRBUF_INIT;
-
-	va_start(args, format);
-	strbuf_vaddf(&buf, format, args);
-	va_end(args);
-	html(buf.buf);
-	strbuf_release(&buf);
-}
-
-void html_txtf(const char *format, ...)
-{
-	va_list args;
-
-	va_start(args, format);
-	html_vtxtf(format, args);
-	va_end(args);
-}
-
-void html_vtxtf(const char *format, va_list ap)
-{
-	va_list cp;
-	struct strbuf buf = STRBUF_INIT;
-
-	va_copy(cp, ap);
-	strbuf_vaddf(&buf, format, cp);
-	va_end(cp);
-	html_txt(buf.buf);
-	strbuf_release(&buf);
-}
-
-void html_txt(const char *txt)
-{
-	if (txt)
-		html_ntxt(txt, strlen(txt));
-}
-
-ssize_t html_ntxt(const char *txt, size_t len)
-{
-	const char *t = txt;
-	ssize_t slen;
-
-	if (len > SSIZE_MAX)
-		return -1;
-
-	slen = (ssize_t) len;
-	while (t && *t && slen--) {
-		int c = *t;
-		if (c == '<' || c == '>' || c == '&') {
-			html_raw(txt, t - txt);
-			if (c == '>')
-				html("&gt;");
-			else if (c == '<')
-				html("&lt;");
-			else if (c == '&')
-				html("&amp;");
-			txt = t + 1;
-		}
-		t++;
-	}
-	if (t != txt)
-		html_raw(txt, t - txt);
-	return slen;
-}
-
-void html_attrf(const char *fmt, ...)
-{
-	va_list ap;
-	struct strbuf sb = STRBUF_INIT;
-
-	va_start(ap, fmt);
-	strbuf_vaddf(&sb, fmt, ap);
-	va_end(ap);
-
-	html_attr(sb.buf);
-	strbuf_release(&sb);
-}
-
-void html_attr(const char *txt)
-{
-	const char *t = txt;
-	while (t && *t) {
-		int c = *t;
-		if (c == '<' || c == '>' || c == '\'' || c == '\"' || c == '&') {
-			html_raw(txt, t - txt);
-			if (c == '>')
-				html("&gt;");
-			else if (c == '<')
-				html("&lt;");
-			else if (c == '\'')
-				html("&#x27;");
-			else if (c == '"')
-				html("&quot;");
-			else if (c == '&')
-				html("&amp;");
-			txt = t + 1;
-		}
-		t++;
-	}
-	if (t != txt)
-		html(txt);
-}
-
-void html_url_path(const char *txt)
-{
-	const char *t = txt;
-	while (t && *t) {
-		unsigned char c = *t;
-		const char *e = url_escape_table[c];
-		if (e && c != '+' && c != '&') {
-			html_raw(txt, t - txt);
-			html(e);
-			txt = t + 1;
-		}
-		t++;
-	}
-	if (t != txt)
-		html(txt);
-}
-
-void html_url_arg(const char *txt)
-{
-	const char *t = txt;
-	while (t && *t) {
-		unsigned char c = *t;
-		const char *e = url_escape_table[c];
-		if (c == ' ')
-			e = "+";
-		if (e) {
-			html_raw(txt, t - txt);
-			html(e);
-			txt = t + 1;
-		}
-		t++;
-	}
-	if (t != txt)
-		html(txt);
-}
-
-void html_header_arg_in_quotes(const char *txt)
-{
-	const char *t = txt;
-	while (t && *t) {
-		unsigned char c = *t;
-		const char *e = NULL;
-		if (c == '\\')
-			e = "\\\\";
-		else if (c == '\r')
-			e = "\\r";
-		else if (c == '\n')
-			e = "\\n";
-		else if (c == '"')
-			e = "\\\"";
-		if (e) {
-			html_raw(txt, t - txt);
-			html(e);
-			txt = t + 1;
-		}
-		t++;
-	}
-	if (t != txt)
-		html(txt);
-
-}
-
-void html_hidden(const char *name, const char *value)
-{
-	html("<input type='hidden' name='");
-	html_attr(name);
-	html("' value='");
-	html_attr(value);
-	html("'/>");
-}
-
-void html_option(const char *value, const char *text, const char *selected_value)
-{
-	html("<option value='");
-	html_attr(value);
-	html("'");
-	if (selected_value && !strcmp(selected_value, value))
-		html(" selected='selected'");
-	html(">");
-	html_txt(text);
-	html("</option>\n");
-}
-
-void html_intoption(int value, const char *text, int selected_value)
-{
-	htmlf("<option value='%d'%s>", value,
-	      value == selected_value ? " selected='selected'" : "");
-	html_txt(text);
-	html("</option>");
-}
-
-void html_link_open(const char *url, const char *title, const char *class)
-{
-	html("<a href='");
-	html_attr(url);
-	if (title) {
-		html("' title='");
-		html_attr(title);
-	}
-	if (class) {
-		html("' class='");
-		html_attr(class);
-	}
-	html("'>");
-}
-
-void html_link_close(void)
-{
-	html("</a>");
-}
-
-void html_fileperm(unsigned short mode)
-{
-	htmlf("%c%c%c", (mode & 4 ? 'r' : '-'),
-	      (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-'));
-}
-
-int html_include(const char *filename)
-{
-	FILE *f;
-	char buf[4096];
-	size_t len;
-
-	if (!(f = fopen(filename, "r"))) {
-		fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n",
-			filename, strerror(errno), errno);
-		return -1;
-	}
-	while ((len = fread(buf, 1, 4096, f)) > 0)
-		html_raw(buf, len);
-	fclose(f);
-	return 0;
-}
-
-void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value))
-{
-	const char *t = txt;
-
-	while (t && *t) {
-		char *name = url_decode_parameter_name(&t);
-		if (*name) {
-			char *value = url_decode_parameter_value(&t);
-			fn(name, value);
-			free(value);
-		}
-		free(name);
-	}
-}
diff --git a/www/git.causal.agency/cgit/html.h b/www/git.causal.agency/cgit/html.h
deleted file mode 100644
index fa4de775..00000000
--- a/www/git.causal.agency/cgit/html.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef HTML_H
-#define HTML_H
-
-#include "cgit.h"
-
-extern void html_raw(const char *txt, size_t size);
-extern void html(const char *txt);
-
-__attribute__((format (printf,1,2)))
-extern void htmlf(const char *format,...);
-
-__attribute__((format (printf,1,2)))
-extern void html_txtf(const char *format,...);
-
-__attribute__((format (printf,1,0)))
-extern void html_vtxtf(const char *format, va_list ap);
-
-__attribute__((format (printf,1,2)))
-extern void html_attrf(const char *format,...);
-
-extern void html_txt(const char *txt);
-extern ssize_t html_ntxt(const char *txt, size_t len);
-extern void html_attr(const char *txt);
-extern void html_url_path(const char *txt);
-extern void html_url_arg(const char *txt);
-extern void html_header_arg_in_quotes(const char *txt);
-extern void html_hidden(const char *name, const char *value);
-extern void html_option(const char *value, const char *text, const char *selected_value);
-extern void html_intoption(int value, const char *text, int selected_value);
-extern void html_link_open(const char *url, const char *title, const char *class);
-extern void html_link_close(void);
-extern void html_fileperm(unsigned short mode);
-extern int html_include(const char *filename);
-
-extern void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value));
-
-#endif /* HTML_H */
diff --git a/www/git.causal.agency/cgit/parsing.c b/www/git.causal.agency/cgit/parsing.c
deleted file mode 100644
index 72b59b3c..00000000
--- a/www/git.causal.agency/cgit/parsing.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/* parsing.c: parsing of config files
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-
-/*
- * url syntax: [repo ['/' cmd [ '/' path]]]
- *   repo: any valid repo url, may contain '/'
- *   cmd:  log | commit | diff | tree | view | blob | snapshot
- *   path: any valid path, may contain '/'
- *
- */
-void cgit_parse_url(const char *url)
-{
-	char *c, *cmd, *p;
-	struct cgit_repo *repo;
-
-	if (!url || url[0] == '\0')
-		return;
-
-	ctx.qry.page = NULL;
-	ctx.repo = cgit_get_repoinfo(url);
-	if (ctx.repo) {
-		ctx.qry.repo = ctx.repo->url;
-		return;
-	}
-
-	cmd = NULL;
-	c = strchr(url, '/');
-	while (c) {
-		c[0] = '\0';
-		repo = cgit_get_repoinfo(url);
-		if (repo) {
-			ctx.repo = repo;
-			cmd = c;
-		}
-		c[0] = '/';
-		c = strchr(c + 1, '/');
-	}
-
-	if (ctx.repo) {
-		ctx.qry.repo = ctx.repo->url;
-		p = strchr(cmd + 1, '/');
-		if (p) {
-			p[0] = '\0';
-			if (p[1])
-				ctx.qry.path = trim_end(p + 1, '/');
-		}
-		if (cmd[1])
-			ctx.qry.page = xstrdup(cmd + 1);
-	}
-}
-
-static char *substr(const char *head, const char *tail)
-{
-	char *buf;
-
-	if (tail < head)
-		return xstrdup("");
-	buf = xmalloc(tail - head + 1);
-	strlcpy(buf, head, tail - head + 1);
-	return buf;
-}
-
-static void parse_user(const char *t, char **name, char **email, unsigned long *date, int *tz)
-{
-	struct ident_split ident;
-	unsigned email_len;
-
-	if (!split_ident_line(&ident, t, strchrnul(t, '\n') - t)) {
-		*name = substr(ident.name_begin, ident.name_end);
-
-		email_len = ident.mail_end - ident.mail_begin;
-		*email = xmalloc(strlen("<") + email_len + strlen(">") + 1);
-		xsnprintf(*email, email_len + 3, "<%.*s>", email_len, ident.mail_begin);
-
-		if (ident.date_begin)
-			*date = strtoul(ident.date_begin, NULL, 10);
-		if (ident.tz_begin)
-			*tz = atoi(ident.tz_begin);
-	}
-}
-
-#ifdef NO_ICONV
-#define reencode(a, b, c)
-#else
-static const char *reencode(char **txt, const char *src_enc, const char *dst_enc)
-{
-	char *tmp;
-
-	if (!txt)
-		return NULL;
-
-	if (!*txt || !src_enc || !dst_enc)
-		return *txt;
-
-	/* no encoding needed if src_enc equals dst_enc */
-	if (!strcasecmp(src_enc, dst_enc))
-		return *txt;
-
-	tmp = reencode_string(*txt, dst_enc, src_enc);
-	if (tmp) {
-		free(*txt);
-		*txt = tmp;
-	}
-	return *txt;
-}
-#endif
-
-static const char *next_header_line(const char *p)
-{
-	p = strchr(p, '\n');
-	if (!p)
-		return NULL;
-	return p + 1;
-}
-
-static int end_of_header(const char *p)
-{
-	return !p || (*p == '\n');
-}
-
-struct commitinfo *cgit_parse_commit(struct commit *commit)
-{
-	struct commitinfo *ret;
-	const char *p = repo_get_commit_buffer(the_repository, commit, NULL);
-	const char *t;
-
-	ret = xcalloc(1, sizeof(struct commitinfo));
-	ret->commit = commit;
-
-	if (!p)
-		return ret;
-
-	if (!skip_prefix(p, "tree ", &p))
-		die("Bad commit: %s", oid_to_hex(&commit->object.oid));
-	p += the_hash_algo->hexsz + 1;
-
-	while (skip_prefix(p, "parent ", &p))
-		p += the_hash_algo->hexsz + 1;
-
-	if (p && skip_prefix(p, "author ", &p)) {
-		parse_user(p, &ret->author, &ret->author_email,
-			&ret->author_date, &ret->author_tz);
-		p = next_header_line(p);
-	}
-
-	if (p && skip_prefix(p, "committer ", &p)) {
-		parse_user(p, &ret->committer, &ret->committer_email,
-			&ret->committer_date, &ret->committer_tz);
-		p = next_header_line(p);
-	}
-
-	if (p && skip_prefix(p, "encoding ", &p)) {
-		t = strchr(p, '\n');
-		if (t) {
-			ret->msg_encoding = substr(p, t + 1);
-			p = t + 1;
-		}
-	}
-
-	if (!ret->msg_encoding)
-		ret->msg_encoding = xstrdup("UTF-8");
-
-	while (!end_of_header(p))
-		p = next_header_line(p);
-	while (p && *p == '\n')
-		p++;
-	if (!p)
-		return ret;
-
-	t = strchrnul(p, '\n');
-	ret->subject = substr(p, t);
-	while (*t == '\n')
-		t++;
-	ret->msg = xstrdup(t);
-
-	reencode(&ret->author, ret->msg_encoding, PAGE_ENCODING);
-	reencode(&ret->author_email, ret->msg_encoding, PAGE_ENCODING);
-	reencode(&ret->committer, ret->msg_encoding, PAGE_ENCODING);
-	reencode(&ret->committer_email, ret->msg_encoding, PAGE_ENCODING);
-	reencode(&ret->subject, ret->msg_encoding, PAGE_ENCODING);
-	reencode(&ret->msg, ret->msg_encoding, PAGE_ENCODING);
-
-	return ret;
-}
-
-struct taginfo *cgit_parse_tag(struct tag *tag)
-{
-	void *data;
-	enum object_type type;
-	unsigned long size;
-	const char *p;
-	struct taginfo *ret = NULL;
-
-	data = read_object_file(&tag->object.oid, &type, &size);
-	if (!data || type != OBJ_TAG)
-		goto cleanup;
-
-	ret = xcalloc(1, sizeof(struct taginfo));
-
-	for (p = data; !end_of_header(p); p = next_header_line(p)) {
-		if (skip_prefix(p, "tagger ", &p)) {
-			parse_user(p, &ret->tagger, &ret->tagger_email,
-				&ret->tagger_date, &ret->tagger_tz);
-		}
-	}
-
-	while (p && *p == '\n')
-		p++;
-
-	if (p && *p)
-		ret->msg = xstrdup(p);
-
-cleanup:
-	free(data);
-	return ret;
-}
diff --git a/www/git.causal.agency/cgit/robots.txt b/www/git.causal.agency/cgit/robots.txt
deleted file mode 100644
index 1b33266d..00000000
--- a/www/git.causal.agency/cgit/robots.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-User-agent: *
-Disallow: /*/snapshot/*
-Disallow: /*/blame/*
-Allow: /
diff --git a/www/git.causal.agency/cgit/scan-tree.c b/www/git.causal.agency/cgit/scan-tree.c
deleted file mode 100644
index 1e3b43de..00000000
--- a/www/git.causal.agency/cgit/scan-tree.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/* scan-tree.c
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "scan-tree.h"
-#include "configfile.h"
-#include "html.h"
-#include <config.h>
-
-/* return 1 if path contains a objects/ directory and a HEAD file */
-static int is_git_dir(const char *path)
-{
-	struct stat st;
-	struct strbuf pathbuf = STRBUF_INIT;
-	int result = 0;
-
-	strbuf_addf(&pathbuf, "%s/objects", path);
-	if (stat(pathbuf.buf, &st)) {
-		if (errno != ENOENT)
-			fprintf(stderr, "Error checking path %s: %s (%d)\n",
-				path, strerror(errno), errno);
-		goto out;
-	}
-	if (!S_ISDIR(st.st_mode))
-		goto out;
-
-	strbuf_reset(&pathbuf);
-	strbuf_addf(&pathbuf, "%s/HEAD", path);
-	if (stat(pathbuf.buf, &st)) {
-		if (errno != ENOENT)
-			fprintf(stderr, "Error checking path %s: %s (%d)\n",
-				path, strerror(errno), errno);
-		goto out;
-	}
-	if (!S_ISREG(st.st_mode))
-		goto out;
-
-	result = 1;
-out:
-	strbuf_release(&pathbuf);
-	return result;
-}
-
-static struct cgit_repo *repo;
-static repo_config_fn config_fn;
-
-static void scan_tree_repo_config(const char *name, const char *value)
-{
-	config_fn(repo, name, value);
-}
-
-static int gitconfig_config(const char *key, const char *value, void *cb)
-{
-	const char *name;
-
-	if (!strcmp(key, "gitweb.owner"))
-		config_fn(repo, "owner", value);
-	else if (!strcmp(key, "gitweb.description"))
-		config_fn(repo, "desc", value);
-	else if (!strcmp(key, "gitweb.category"))
-		config_fn(repo, "section", value);
-	else if (!strcmp(key, "gitweb.homepage"))
-		config_fn(repo, "homepage", value);
-	else if (skip_prefix(key, "cgit.", &name))
-		config_fn(repo, name, value);
-
-	return 0;
-}
-
-static char *xstrrchr(char *s, char *from, int c)
-{
-	while (from >= s && *from != c)
-		from--;
-	return from < s ? NULL : from;
-}
-
-static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
-{
-	struct stat st;
-	struct passwd *pwd;
-	size_t pathlen;
-	struct strbuf rel = STRBUF_INIT;
-	char *p, *slash;
-	int n;
-	size_t size;
-
-	if (stat(path->buf, &st)) {
-		fprintf(stderr, "Error accessing %s: %s (%d)\n",
-			path->buf, strerror(errno), errno);
-		return;
-	}
-
-	strbuf_addch(path, '/');
-	pathlen = path->len;
-
-	if (ctx.cfg.strict_export) {
-		strbuf_addstr(path, ctx.cfg.strict_export);
-		if(stat(path->buf, &st))
-			return;
-		strbuf_setlen(path, pathlen);
-	}
-
-	strbuf_addstr(path, "noweb");
-	if (!stat(path->buf, &st))
-		return;
-	strbuf_setlen(path, pathlen);
-
-	if (!starts_with(path->buf, base))
-		strbuf_addbuf(&rel, path);
-	else
-		strbuf_addstr(&rel, path->buf + strlen(base) + 1);
-
-	if (!strcmp(rel.buf + rel.len - 5, "/.git"))
-		strbuf_setlen(&rel, rel.len - 5);
-	else if (rel.len && rel.buf[rel.len - 1] == '/')
-		strbuf_setlen(&rel, rel.len - 1);
-
-	repo = cgit_add_repo(rel.buf);
-	config_fn = fn;
-	if (ctx.cfg.enable_git_config) {
-		strbuf_addstr(path, "config");
-		git_config_from_file(gitconfig_config, path->buf, NULL);
-		strbuf_setlen(path, pathlen);
-	}
-
-	if (ctx.cfg.remove_suffix) {
-		size_t urllen;
-		strip_suffix(repo->url, ".git", &urllen);
-		strip_suffix_mem(repo->url, &urllen, "/");
-		repo->url[urllen] = '\0';
-	}
-	repo->path = xstrdup(path->buf);
-	while (!repo->owner) {
-		if ((pwd = getpwuid(st.st_uid)) == NULL) {
-			break;
-		}
-		if (pwd->pw_gecos)
-			if ((p = strchr(pwd->pw_gecos, ',')))
-				*p = '\0';
-		repo->owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
-	}
-
-	if (repo->desc == cgit_default_repo_desc || !repo->desc) {
-		strbuf_addstr(path, "description");
-		if (!stat(path->buf, &st))
-			readfile(path->buf, &repo->desc, &size);
-		strbuf_setlen(path, pathlen);
-	}
-
-	if (ctx.cfg.section_from_path) {
-		n = ctx.cfg.section_from_path;
-		if (n > 0) {
-			slash = rel.buf - 1;
-			while (slash && n && (slash = strchr(slash + 1, '/')))
-				n--;
-		} else {
-			slash = rel.buf + rel.len;
-			while (slash && n && (slash = xstrrchr(rel.buf, slash - 1, '/')))
-				n++;
-		}
-		if (slash && !n) {
-			*slash = '\0';
-			repo->section = xstrdup(rel.buf);
-			*slash = '/';
-			if (starts_with(repo->name, repo->section)) {
-				repo->name += strlen(repo->section);
-				if (*repo->name == '/')
-					repo->name++;
-			}
-		}
-	}
-
-	strbuf_addstr(path, "cgitrc");
-	if (!stat(path->buf, &st))
-		parse_configfile(path->buf, &scan_tree_repo_config);
-
-	strbuf_release(&rel);
-}
-
-static void scan_path(const char *base, const char *path, repo_config_fn fn)
-{
-	DIR *dir = opendir(path);
-	struct dirent *ent;
-	struct strbuf pathbuf = STRBUF_INIT;
-	size_t pathlen = strlen(path);
-	struct stat st;
-
-	if (!dir) {
-		fprintf(stderr, "Error opening directory %s: %s (%d)\n",
-			path, strerror(errno), errno);
-		return;
-	}
-
-	strbuf_add(&pathbuf, path, strlen(path));
-	if (is_git_dir(pathbuf.buf)) {
-		add_repo(base, &pathbuf, fn);
-		goto end;
-	}
-	strbuf_addstr(&pathbuf, "/.git");
-	if (is_git_dir(pathbuf.buf)) {
-		add_repo(base, &pathbuf, fn);
-		goto end;
-	}
-	/*
-	 * Add one because we don't want to lose the trailing '/' when we
-	 * reset the length of pathbuf in the loop below.
-	 */
-	pathlen++;
-	while ((ent = readdir(dir)) != NULL) {
-		if (ent->d_name[0] == '.') {
-			if (ent->d_name[1] == '\0')
-				continue;
-			if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
-				continue;
-			if (!ctx.cfg.scan_hidden_path)
-				continue;
-		}
-		strbuf_setlen(&pathbuf, pathlen);
-		strbuf_addstr(&pathbuf, ent->d_name);
-		if (stat(pathbuf.buf, &st)) {
-			fprintf(stderr, "Error checking path %s: %s (%d)\n",
-				pathbuf.buf, strerror(errno), errno);
-			continue;
-		}
-		if (S_ISDIR(st.st_mode))
-			scan_path(base, pathbuf.buf, fn);
-	}
-end:
-	strbuf_release(&pathbuf);
-	closedir(dir);
-}
-
-void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
-{
-	struct strbuf line = STRBUF_INIT;
-	FILE *projects;
-	int err;
-
-	projects = fopen(projectsfile, "r");
-	if (!projects) {
-		fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
-			projectsfile, strerror(errno), errno);
-		return;
-	}
-	while (strbuf_getline(&line, projects) != EOF) {
-		if (!line.len)
-			continue;
-		strbuf_insert(&line, 0, "/", 1);
-		strbuf_insert(&line, 0, path, strlen(path));
-		scan_path(path, line.buf, fn);
-	}
-	if ((err = ferror(projects))) {
-		fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
-			projectsfile, strerror(err), err);
-	}
-	fclose(projects);
-	strbuf_release(&line);
-}
-
-void scan_tree(const char *path, repo_config_fn fn)
-{
-	scan_path(path, path, fn);
-}
diff --git a/www/git.causal.agency/cgit/scan-tree.h b/www/git.causal.agency/cgit/scan-tree.h
deleted file mode 100644
index 1afbd4bb..00000000
--- a/www/git.causal.agency/cgit/scan-tree.h
+++ /dev/null
@@ -1,2 +0,0 @@
-extern void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn);
-extern void scan_tree(const char *path, repo_config_fn fn);
diff --git a/www/git.causal.agency/cgit/shared.c b/www/git.causal.agency/cgit/shared.c
deleted file mode 100644
index 8115469a..00000000
--- a/www/git.causal.agency/cgit/shared.c
+++ /dev/null
@@ -1,579 +0,0 @@
-/* shared.c: global vars + some callback functions
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-
-struct cgit_repolist cgit_repolist;
-struct cgit_context ctx;
-
-int chk_zero(int result, char *msg)
-{
-	if (result != 0)
-		die_errno("%s", msg);
-	return result;
-}
-
-int chk_positive(int result, char *msg)
-{
-	if (result <= 0)
-		die_errno("%s", msg);
-	return result;
-}
-
-int chk_non_negative(int result, char *msg)
-{
-	if (result < 0)
-		die_errno("%s", msg);
-	return result;
-}
-
-char *cgit_default_repo_desc = "[no description]";
-struct cgit_repo *cgit_add_repo(const char *url)
-{
-	struct cgit_repo *ret;
-
-	if (++cgit_repolist.count > cgit_repolist.length) {
-		if (cgit_repolist.length == 0)
-			cgit_repolist.length = 8;
-		else
-			cgit_repolist.length *= 2;
-		cgit_repolist.repos = xrealloc(cgit_repolist.repos,
-					       cgit_repolist.length *
-					       sizeof(struct cgit_repo));
-	}
-
-	ret = &cgit_repolist.repos[cgit_repolist.count-1];
-	memset(ret, 0, sizeof(struct cgit_repo));
-	ret->url = trim_end(url, '/');
-	ret->name = ret->url;
-	ret->path = NULL;
-	ret->desc = cgit_default_repo_desc;
-	ret->extra_head_content = NULL;
-	ret->owner = NULL;
-	ret->homepage = NULL;
-	ret->section = ctx.cfg.section;
-	ret->snapshots = ctx.cfg.snapshots;
-	ret->enable_blame = ctx.cfg.enable_blame;
-	ret->enable_commit_graph = ctx.cfg.enable_commit_graph;
-	ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
-	ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
-	ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
-	ret->enable_subject_links = ctx.cfg.enable_subject_links;
-	ret->enable_html_serving = ctx.cfg.enable_html_serving;
-	ret->max_stats = ctx.cfg.max_stats;
-	ret->branch_sort = ctx.cfg.branch_sort;
-	ret->commit_sort = ctx.cfg.commit_sort;
-	ret->module_link = ctx.cfg.module_link;
-	ret->readme = ctx.cfg.readme;
-	ret->mtime = -1;
-	ret->about_filter = ctx.cfg.about_filter;
-	ret->commit_filter = ctx.cfg.commit_filter;
-	ret->source_filter = ctx.cfg.source_filter;
-	ret->email_filter = ctx.cfg.email_filter;
-	ret->owner_filter = ctx.cfg.owner_filter;
-	ret->clone_url = ctx.cfg.clone_url;
-	ret->submodules.strdup_strings = 1;
-	ret->hide = ret->ignore = 0;
-	return ret;
-}
-
-struct cgit_repo *cgit_get_repoinfo(const char *url)
-{
-	int i;
-	struct cgit_repo *repo;
-
-	for (i = 0; i < cgit_repolist.count; i++) {
-		repo = &cgit_repolist.repos[i];
-		if (repo->ignore)
-			continue;
-		if (!strcmp(repo->url, url))
-			return repo;
-	}
-	return NULL;
-}
-
-void cgit_free_commitinfo(struct commitinfo *info)
-{
-	free(info->author);
-	free(info->author_email);
-	free(info->committer);
-	free(info->committer_email);
-	free(info->subject);
-	free(info->msg);
-	free(info->msg_encoding);
-	free(info);
-}
-
-char *trim_end(const char *str, char c)
-{
-	int len;
-
-	if (str == NULL)
-		return NULL;
-	len = strlen(str);
-	while (len > 0 && str[len - 1] == c)
-		len--;
-	if (len == 0)
-		return NULL;
-	return xstrndup(str, len);
-}
-
-char *ensure_end(const char *str, char c)
-{
-	size_t len = strlen(str);
-	char *result;
-
-	if (len && str[len - 1] == c)
-		return xstrndup(str, len);
-
-	result = xmalloc(len + 2);
-	memcpy(result, str, len);
-	result[len] = '/';
-	result[len + 1] = '\0';
-	return result;
-}
-
-void strbuf_ensure_end(struct strbuf *sb, char c)
-{
-	if (!sb->len || sb->buf[sb->len - 1] != c)
-		strbuf_addch(sb, c);
-}
-
-void cgit_add_ref(struct reflist *list, struct refinfo *ref)
-{
-	size_t size;
-
-	if (list->count >= list->alloc) {
-		list->alloc += (list->alloc ? list->alloc : 4);
-		size = list->alloc * sizeof(struct refinfo *);
-		list->refs = xrealloc(list->refs, size);
-	}
-	list->refs[list->count++] = ref;
-}
-
-static struct refinfo *cgit_mk_refinfo(const char *refname, const struct object_id *oid)
-{
-	struct refinfo *ref;
-
-	ref = xmalloc(sizeof (struct refinfo));
-	ref->refname = xstrdup(refname);
-	ref->object = parse_object(the_repository, oid);
-	switch (ref->object->type) {
-	case OBJ_TAG:
-		ref->tag = cgit_parse_tag((struct tag *)ref->object);
-		break;
-	case OBJ_COMMIT:
-		ref->commit = cgit_parse_commit((struct commit *)ref->object);
-		break;
-	}
-	return ref;
-}
-
-void cgit_free_taginfo(struct taginfo *tag)
-{
-	if (tag->tagger)
-		free(tag->tagger);
-	if (tag->tagger_email)
-		free(tag->tagger_email);
-	if (tag->msg)
-		free(tag->msg);
-	free(tag);
-}
-
-static void cgit_free_refinfo(struct refinfo *ref)
-{
-	if (ref->refname)
-		free((char *)ref->refname);
-	switch (ref->object->type) {
-	case OBJ_TAG:
-		cgit_free_taginfo(ref->tag);
-		break;
-	case OBJ_COMMIT:
-		cgit_free_commitinfo(ref->commit);
-		break;
-	}
-	free(ref);
-}
-
-void cgit_free_reflist_inner(struct reflist *list)
-{
-	int i;
-
-	for (i = 0; i < list->count; i++) {
-		cgit_free_refinfo(list->refs[i]);
-	}
-	free(list->refs);
-}
-
-int cgit_refs_cb(const char *refname, const struct object_id *oid, int flags,
-		  void *cb_data)
-{
-	struct reflist *list = (struct reflist *)cb_data;
-	struct refinfo *info = cgit_mk_refinfo(refname, oid);
-
-	if (info)
-		cgit_add_ref(list, info);
-	return 0;
-}
-
-void cgit_diff_tree_cb(struct diff_queue_struct *q,
-		       struct diff_options *options, void *data)
-{
-	int i;
-
-	for (i = 0; i < q->nr; i++) {
-		if (q->queue[i]->status == 'U')
-			continue;
-		((filepair_fn)data)(q->queue[i]);
-	}
-}
-
-static int load_mmfile(mmfile_t *file, const struct object_id *oid)
-{
-	enum object_type type;
-
-	if (is_null_oid(oid)) {
-		file->ptr = (char *)"";
-		file->size = 0;
-	} else {
-		file->ptr = read_object_file(oid, &type,
-		                           (unsigned long *)&file->size);
-	}
-	return 1;
-}
-
-/*
- * Receive diff-buffers from xdiff and concatenate them as
- * needed across multiple callbacks.
- *
- * This is basically a copy of xdiff-interface.c/xdiff_outf(),
- * ripped from git and modified to use globals instead of
- * a special callback-struct.
- */
-static char *diffbuf = NULL;
-static int buflen = 0;
-
-static int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
-{
-	int i;
-
-	for (i = 0; i < nbuf; i++) {
-		if (mb[i].ptr[mb[i].size-1] != '\n') {
-			/* Incomplete line */
-			diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
-			memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
-			buflen += mb[i].size;
-			continue;
-		}
-
-		/* we have a complete line */
-		if (!diffbuf) {
-			((linediff_fn)priv)(mb[i].ptr, mb[i].size);
-			continue;
-		}
-		diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
-		memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
-		((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
-		free(diffbuf);
-		diffbuf = NULL;
-		buflen = 0;
-	}
-	if (diffbuf) {
-		((linediff_fn)priv)(diffbuf, buflen);
-		free(diffbuf);
-		diffbuf = NULL;
-		buflen = 0;
-	}
-	return 0;
-}
-
-int cgit_diff_files(const struct object_id *old_oid,
-		    const struct object_id *new_oid, unsigned long *old_size,
-		    unsigned long *new_size, int *binary, int context,
-		    int ignorews, linediff_fn fn)
-{
-	mmfile_t file1, file2;
-	xpparam_t diff_params;
-	xdemitconf_t emit_params;
-	xdemitcb_t emit_cb;
-
-	if (!load_mmfile(&file1, old_oid) || !load_mmfile(&file2, new_oid))
-		return 1;
-
-	*old_size = file1.size;
-	*new_size = file2.size;
-
-	if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
-	    (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
-		*binary = 1;
-		if (file1.size)
-			free(file1.ptr);
-		if (file2.size)
-			free(file2.ptr);
-		return 0;
-	}
-
-	memset(&diff_params, 0, sizeof(diff_params));
-	memset(&emit_params, 0, sizeof(emit_params));
-	memset(&emit_cb, 0, sizeof(emit_cb));
-	diff_params.flags = XDF_NEED_MINIMAL;
-	if (ignorews)
-		diff_params.flags |= XDF_IGNORE_WHITESPACE;
-	emit_params.ctxlen = context > 0 ? context : 3;
-	emit_params.flags = XDL_EMIT_FUNCNAMES;
-	emit_cb.out_line = filediff_cb;
-	emit_cb.priv = fn;
-	xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
-	if (file1.size)
-		free(file1.ptr);
-	if (file2.size)
-		free(file2.ptr);
-	return 0;
-}
-
-void cgit_diff_tree(const struct object_id *old_oid,
-		    const struct object_id *new_oid,
-		    filepair_fn fn, const char *prefix, int ignorews)
-{
-	struct diff_options opt;
-	struct pathspec_item item;
-
-	memset(&item, 0, sizeof(item));
-	diff_setup(&opt);
-	opt.output_format = DIFF_FORMAT_CALLBACK;
-	opt.detect_rename = 1;
-	opt.rename_limit = ctx.cfg.renamelimit;
-	opt.flags.recursive = 1;
-	if (ignorews)
-		DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
-	opt.format_callback = cgit_diff_tree_cb;
-	opt.format_callback_data = fn;
-	if (prefix) {
-		item.match = xstrdup(prefix);
-		item.len = strlen(prefix);
-		opt.pathspec.nr = 1;
-		opt.pathspec.items = &item;
-	}
-	diff_setup_done(&opt);
-
-	if (old_oid && !is_null_oid(old_oid))
-		diff_tree_oid(old_oid, new_oid, "", &opt);
-	else
-		diff_root_tree_oid(new_oid, "", &opt);
-	diffcore_std(&opt);
-	diff_flush(&opt);
-
-	free(item.match);
-}
-
-void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix)
-{
-	const struct object_id *old_oid = NULL;
-
-	if (commit->parents)
-		old_oid = &commit->parents->item->object.oid;
-	cgit_diff_tree(old_oid, &commit->object.oid, fn, prefix,
-		       ctx.qry.ignorews);
-}
-
-int cgit_parse_snapshots_mask(const char *str)
-{
-	struct string_list tokens = STRING_LIST_INIT_DUP;
-	struct string_list_item *item;
-	const struct cgit_snapshot_format *f;
-	int rv = 0;
-
-	/* favor legacy setting */
-	if (atoi(str))
-		return 1;
-
-	if (strcmp(str, "all") == 0)
-		return INT_MAX;
-
-	string_list_split(&tokens, str, ' ', -1);
-	string_list_remove_empty_items(&tokens, 0);
-
-	for_each_string_list_item(item, &tokens) {
-		for (f = cgit_snapshot_formats; f->suffix; f++) {
-			if (!strcmp(item->string, f->suffix) ||
-			    !strcmp(item->string, f->suffix + 1)) {
-				rv |= cgit_snapshot_format_bit(f);
-				break;
-			}
-		}
-	}
-
-	string_list_clear(&tokens, 0);
-	return rv;
-}
-
-typedef struct {
-	char * name;
-	char * value;
-} cgit_env_var;
-
-void cgit_prepare_repo_env(struct cgit_repo * repo)
-{
-	cgit_env_var env_vars[] = {
-		{ .name = "CGIT_REPO_URL", .value = repo->url },
-		{ .name = "CGIT_REPO_NAME", .value = repo->name },
-		{ .name = "CGIT_REPO_PATH", .value = repo->path },
-		{ .name = "CGIT_REPO_OWNER", .value = repo->owner },
-		{ .name = "CGIT_REPO_DEFBRANCH", .value = repo->defbranch },
-		{ .name = "CGIT_REPO_SECTION", .value = repo->section },
-		{ .name = "CGIT_REPO_CLONE_URL", .value = repo->clone_url }
-	};
-	int env_var_count = ARRAY_SIZE(env_vars);
-	cgit_env_var *p, *q;
-	static char *warn = "cgit warning: failed to set env: %s=%s\n";
-
-	p = env_vars;
-	q = p + env_var_count;
-	for (; p < q; p++)
-		if (p->value && setenv(p->name, p->value, 1))
-			fprintf(stderr, warn, p->name, p->value);
-}
-
-/* Read the content of the specified file into a newly allocated buffer,
- * zeroterminate the buffer and return 0 on success, errno otherwise.
- */
-int readfile(const char *path, char **buf, size_t *size)
-{
-	int fd, e;
-	struct stat st;
-
-	fd = open(path, O_RDONLY);
-	if (fd == -1)
-		return errno;
-	if (fstat(fd, &st)) {
-		e = errno;
-		close(fd);
-		return e;
-	}
-	if (!S_ISREG(st.st_mode)) {
-		close(fd);
-		return EISDIR;
-	}
-	*buf = xmalloc(st.st_size + 1);
-	*size = read_in_full(fd, *buf, st.st_size);
-	e = errno;
-	(*buf)[*size] = '\0';
-	close(fd);
-	return (*size == st.st_size ? 0 : e);
-}
-
-static int is_token_char(char c)
-{
-	return isalnum(c) || c == '_';
-}
-
-/* Replace name with getenv(name), return pointer to zero-terminating char
- */
-static char *expand_macro(char *name, int maxlength)
-{
-	char *value;
-	size_t len;
-
-	len = 0;
-	value = getenv(name);
-	if (value) {
-		len = strlen(value) + 1;
-		if (len > maxlength)
-			len = maxlength;
-		strlcpy(name, value, len);
-		--len;
-	}
-	return name + len;
-}
-
-#define EXPBUFSIZE (1024 * 8)
-
-/* Replace all tokens prefixed by '$' in the specified text with the
- * value of the named environment variable.
- * NB: the return value is a static buffer, i.e. it must be strdup'd
- * by the caller.
- */
-char *expand_macros(const char *txt)
-{
-	static char result[EXPBUFSIZE];
-	char *p, *start;
-	int len;
-
-	p = result;
-	start = NULL;
-	while (p < result + EXPBUFSIZE - 1 && txt && *txt) {
-		*p = *txt;
-		if (start) {
-			if (!is_token_char(*txt)) {
-				if (p - start > 0) {
-					*p = '\0';
-					len = result + EXPBUFSIZE - start - 1;
-					p = expand_macro(start, len) - 1;
-				}
-				start = NULL;
-				txt--;
-			}
-			p++;
-			txt++;
-			continue;
-		}
-		if (*txt == '$') {
-			start = p;
-			txt++;
-			continue;
-		}
-		p++;
-		txt++;
-	}
-	*p = '\0';
-	if (start && p - start > 0) {
-		len = result + EXPBUFSIZE - start - 1;
-		p = expand_macro(start, len);
-		*p = '\0';
-	}
-	return result;
-}
-
-char *get_mimetype_for_filename(const char *filename)
-{
-	char *ext, *mimetype, *token, line[1024], *saveptr;
-	FILE *file;
-	struct string_list_item *mime;
-
-	if (!filename)
-		return NULL;
-
-	ext = strrchr(filename, '.');
-	if (!ext)
-		return NULL;
-	++ext;
-	if (!ext[0])
-		return NULL;
-	mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
-	if (mime)
-		return xstrdup(mime->util);
-
-	if (!ctx.cfg.mimetype_file)
-		return NULL;
-	file = fopen(ctx.cfg.mimetype_file, "r");
-	if (!file)
-		return NULL;
-	while (fgets(line, sizeof(line), file)) {
-		if (!line[0] || line[0] == '#')
-			continue;
-		mimetype = strtok_r(line, " \t\r\n", &saveptr);
-		while ((token = strtok_r(NULL, " \t\r\n", &saveptr))) {
-			if (!strcasecmp(ext, token)) {
-				fclose(file);
-				return xstrdup(mimetype);
-			}
-		}
-	}
-	fclose(file);
-	return NULL;
-}
diff --git a/www/git.causal.agency/cgit/tests/.gitignore b/www/git.causal.agency/cgit/tests/.gitignore
deleted file mode 100644
index 3fd2e965..00000000
--- a/www/git.causal.agency/cgit/tests/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-trash\ directory.t*
-test-results
diff --git a/www/git.causal.agency/cgit/tests/Makefile b/www/git.causal.agency/cgit/tests/Makefile
deleted file mode 100644
index 65e11173..00000000
--- a/www/git.causal.agency/cgit/tests/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-include ../git/config.mak.uname
--include ../cgit.conf
-
-SHELL_PATH ?= $(SHELL)
-SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
-
-T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
-
-all: $(T)
-
-$(T):
-	@'$(SHELL_PATH_SQ)' $@ $(CGIT_TEST_OPTS)
-
-clean:
-	$(RM) -rf trash
-
-.PHONY: $(T) clean
diff --git a/www/git.causal.agency/cgit/tests/filters/dump.sh b/www/git.causal.agency/cgit/tests/filters/dump.sh
deleted file mode 100755
index da6f7a1b..00000000
--- a/www/git.causal.agency/cgit/tests/filters/dump.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-[ "$#" -gt 0 ] && printf "%s " "$*"
-tr '[:lower:]' '[:upper:]'
diff --git a/www/git.causal.agency/cgit/tests/setup.sh b/www/git.causal.agency/cgit/tests/setup.sh
deleted file mode 100755
index 31e7d5bb..00000000
--- a/www/git.causal.agency/cgit/tests/setup.sh
+++ /dev/null
@@ -1,161 +0,0 @@
-# This file should be sourced by all test-scripts
-#
-# Main functions:
-#   prepare_tests(description) - setup for testing, i.e. create repos+config
-#   run_test(description, script) - run one test, i.e. eval script
-#
-# Helper functions
-#   cgit_query(querystring) - call cgit with the specified querystring
-#   cgit_url(url) - call cgit with the specified virtual url
-#
-# Example script:
-#
-# . setup.sh
-# prepare_tests "html validation"
-# run_test 'repo index' 'cgit_url "/" | tidy -e'
-# run_test 'repo summary' 'cgit_url "/foo" | tidy -e'
-
-# We don't want to run Git commands through Valgrind, so we filter out the
-# --valgrind option here and handle it ourselves.  We copy the arguments
-# assuming that none contain a newline, although other whitespace is
-# preserved.
-LF='
-'
-test_argv=
-
-while test $# != 0
-do
-	case "$1" in
-	--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
-		cgit_valgrind=t
-		test_argv="$test_argv${LF}--verbose"
-		;;
-	*)
-		test_argv="$test_argv$LF$1"
-		;;
-	esac
-	shift
-done
-
-OLDIFS=$IFS
-IFS=$LF
-set -- $test_argv
-IFS=$OLDIFS
-
-: ${TEST_DIRECTORY=$(pwd)/../git/t}
-: ${TEST_OUTPUT_DIRECTORY=$(pwd)}
-TEST_NO_CREATE_REPO=YesPlease
-. "$TEST_DIRECTORY"/test-lib.sh
-
-# Prepend the directory containing cgit to PATH.
-if test -n "$cgit_valgrind"
-then
-	GIT_VALGRIND="$TEST_DIRECTORY/valgrind"
-	CGIT_VALGRIND=$(cd ../valgrind && pwd)
-	PATH="$CGIT_VALGRIND/bin:$PATH"
-	export GIT_VALGRIND CGIT_VALGRIND
-else
-	PATH="$(pwd)/../..:$PATH"
-fi
-
-FILTER_DIRECTORY=$(cd ../filters && pwd)
-
-mkrepo() {
-	name=$1
-	count=$2
-	test_create_repo "$name"
-	(
-		cd "$name"
-		n=1
-		while test $n -le $count
-		do
-			echo $n >file-$n
-			git add file-$n
-			git commit -m "commit $n"
-			n=$(expr $n + 1)
-		done
-		case "$3" in
-		testplus)
-			echo "hello" >a+b
-			git add a+b
-			git commit -m "add a+b"
-			git branch "1+2"
-			;;
-		commit-graph)
-			git commit-graph write
-			;;
-		esac
-	)
-}
-
-setup_repos()
-{
-	rm -rf cache
-	mkdir -p cache
-	mkrepo repos/foo 5 >/dev/null
-	mkrepo repos/bar 50 commit-graph >/dev/null
-	mkrepo repos/foo+bar 10 testplus >/dev/null
-	mkrepo "repos/with space" 2 >/dev/null
-	mkrepo repos/filter 5 testplus >/dev/null
-	cat >cgitrc <<EOF
-virtual-root=/
-cache-root=$PWD/cache
-
-cache-size=1021
-snapshots=tar.gz tar.bz tar.lz tar.xz tar.zst zip
-enable-log-filecount=1
-enable-log-linecount=1
-summary-log=5
-summary-branches=5
-summary-tags=5
-clone-url=git://example.org/\$CGIT_REPO_URL.git
-enable-filter-overrides=1
-
-repo.url=foo
-repo.path=$PWD/repos/foo/.git
-# Do not specify a description for this repo, as it then will be assigned
-# the constant value "[no description]" (which actually used to cause a
-# segfault).
-
-repo.url=bar
-repo.path=$PWD/repos/bar/.git
-repo.desc=the bar repo
-
-repo.url=foo+bar
-repo.path=$PWD/repos/foo+bar/.git
-repo.desc=the foo+bar repo
-
-repo.url=with space
-repo.path=$PWD/repos/with space/.git
-repo.desc=spaced repo
-
-repo.url=filter-exec
-repo.path=$PWD/repos/filter/.git
-repo.desc=filtered repo
-repo.about-filter=exec:$FILTER_DIRECTORY/dump.sh
-repo.commit-filter=exec:$FILTER_DIRECTORY/dump.sh
-repo.email-filter=exec:$FILTER_DIRECTORY/dump.sh
-repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh
-repo.readme=master:a+b
-EOF
-}
-
-cgit_query()
-{
-	CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="$1" cgit
-}
-
-cgit_url()
-{
-	CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="url=$1" cgit
-}
-
-strip_headers() {
-	while read -r line
-	do
-		test -z "$line" && break
-	done
-	cat
-}
-
-test -z "$CGIT_TEST_NO_CREATE_REPOS" && setup_repos
diff --git a/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
deleted file mode 100755
index dd84fe3f..00000000
--- a/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/sh
-
-if [ "${CGIT_TEST_NO_GIT_VERSION}" = "YesPlease" ]; then
-	exit 0
-fi
-
-test_description='Check Git version is correct'
-CGIT_TEST_NO_CREATE_REPOS=YesPlease
-. ./setup.sh
-
-test_expect_success 'extract Git version from Makefile' '
-	sed -n -e "/^GIT_VER[ 	]*=/ {
-		s/^GIT_VER[ 	]*=[ 	]*//
-		p
-	}" ../../Makefile >makefile_version
-'
-
-# Note that Git's GIT-VERSION-GEN script applies "s/-/./g" to the version
-# string to produce the internal version in the GIT-VERSION-FILE, so we
-# must apply the same transformation to the version in the Makefile before
-# comparing them.
-test_expect_success 'test Git version matches Makefile' '
-	( cat ../../git/GIT-VERSION-FILE || echo "No GIT-VERSION-FILE" ) |
-	sed -e "s/GIT_VERSION[ 	]*=[ 	]*//" -e "s/\\.dirty$//" >git_version &&
-	sed -e "s/-/./g" makefile_version >makefile_git_version &&
-	test_cmp git_version makefile_git_version
-'
-
-test_expect_success 'test submodule version matches Makefile' '
-	if ! test -e ../../git/.git
-	then
-		echo "git/ is not a Git repository" >&2
-	else
-		(
-			cd ../.. &&
-			sm_oid=$(git ls-files --stage -- git |
-				sed -e "s/^[0-9]* \\([0-9a-f]*\\) [0-9]	.*$/\\1/") &&
-			cd git &&
-			git describe --match "v[0-9]*" $sm_oid
-		) | sed -e "s/^v//" -e "s/-/./" >sm_version &&
-		test_cmp sm_version makefile_version
-	fi
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0010-validate-html.sh b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
deleted file mode 100755
index ca08d69d..00000000
--- a/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/sh
-
-test_description='Validate html with tidy'
-. ./setup.sh
-
-
-test_url()
-{
-	tidy_opt="-eq"
-	test -z "$NO_TIDY_WARNINGS" || tidy_opt+=" --show-warnings no"
-	cgit_url "$1" >tidy-$test_count.tmp || return
-	sed -e "1,4d" tidy-$test_count.tmp >tidy-$test_count || return
-	"$tidy" $tidy_opt tidy-$test_count
-	rc=$?
-
-	# tidy returns with exitcode 1 on warnings, 2 on error
-	if test $rc = 2
-	then
-		false
-	else
-		:
-	fi
-}
-
-tidy=`which tidy 2>/dev/null`
-test -n "$tidy" || {
-	skip_all='Skipping html validation tests: tidy not found'
-	test_done
-	exit
-}
-
-test_expect_success 'index page' 'test_url ""'
-test_expect_success 'foo' 'test_url "foo"'
-test_expect_success 'foo/log' 'test_url "foo/log"'
-test_expect_success 'foo/tree' 'test_url "foo/tree"'
-test_expect_success 'foo/tree/file-1' 'test_url "foo/tree/file-1"'
-test_expect_success 'foo/commit' 'test_url "foo/commit"'
-test_expect_success 'foo/diff' 'test_url "foo/diff"'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
deleted file mode 100755
index 657765d8..00000000
--- a/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/sh
-
-test_description='Validate cache'
-. ./setup.sh
-
-test_expect_success 'verify cache-size=0' '
-
-	rm -f cache/* &&
-	sed -e "s/cache-size=1021$/cache-size=0/" cgitrc >cgitrc.tmp &&
-	mv -f cgitrc.tmp cgitrc &&
-	cgit_url "" &&
-	cgit_url "foo" &&
-	cgit_url "foo/refs" &&
-	cgit_url "foo/tree" &&
-	cgit_url "foo/log" &&
-	cgit_url "foo/diff" &&
-	cgit_url "foo/patch" &&
-	cgit_url "bar" &&
-	cgit_url "bar/refs" &&
-	cgit_url "bar/tree" &&
-	cgit_url "bar/log" &&
-	cgit_url "bar/diff" &&
-	cgit_url "bar/patch" &&
-	ls cache >output &&
-	test_line_count = 0 output
-'
-
-test_expect_success 'verify cache-size=1' '
-
-	rm -f cache/* &&
-	sed -e "s/cache-size=0$/cache-size=1/" cgitrc >cgitrc.tmp &&
-	mv -f cgitrc.tmp cgitrc &&
-	cgit_url "" &&
-	cgit_url "foo" &&
-	cgit_url "foo/refs" &&
-	cgit_url "foo/tree" &&
-	cgit_url "foo/log" &&
-	cgit_url "foo/diff" &&
-	cgit_url "foo/patch" &&
-	cgit_url "bar" &&
-	cgit_url "bar/refs" &&
-	cgit_url "bar/tree" &&
-	cgit_url "bar/log" &&
-	cgit_url "bar/diff" &&
-	cgit_url "bar/patch" &&
-	ls cache >output &&
-	test_line_count = 1 output
-'
-
-test_expect_success 'verify cache-size=1021' '
-
-	rm -f cache/* &&
-	sed -e "s/cache-size=1$/cache-size=1021/" cgitrc >cgitrc.tmp &&
-	mv -f cgitrc.tmp cgitrc &&
-	cgit_url "" &&
-	cgit_url "foo" &&
-	cgit_url "foo/refs" &&
-	cgit_url "foo/tree" &&
-	cgit_url "foo/log" &&
-	cgit_url "foo/diff" &&
-	cgit_url "foo/patch" &&
-	cgit_url "bar" &&
-	cgit_url "bar/refs" &&
-	cgit_url "bar/tree" &&
-	cgit_url "bar/log" &&
-	cgit_url "bar/diff" &&
-	cgit_url "bar/patch" &&
-	ls cache >output &&
-	test_line_count = 13 output &&
-	cgit_url "foo/ls_cache" >output.full &&
-	strip_headers <output.full >output &&
-	test_line_count = 13 output &&
-	# Check that ls_cache output is cached correctly
-	cgit_url "foo/ls_cache" >output.second &&
-	test_cmp output.full output.second
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0101-index.sh b/www/git.causal.agency/cgit/tests/t0101-index.sh
deleted file mode 100755
index 82ef9b04..00000000
--- a/www/git.causal.agency/cgit/tests/t0101-index.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on index page'
-. ./setup.sh
-
-test_expect_success 'generate index page' 'cgit_url "" >tmp'
-test_expect_success 'find foo repo' 'grep "foo" tmp'
-test_expect_success 'find foo description' 'grep "\[no description\]" tmp'
-test_expect_success 'find bar repo' 'grep "bar" tmp'
-test_expect_success 'find bar description' 'grep "the bar repo" tmp'
-test_expect_success 'find foo+bar repo' 'grep ">foo+bar<" tmp'
-test_expect_success 'verify foo+bar link' 'grep "/foo+bar/" tmp'
-test_expect_success 'verify "with%20space" link' 'grep "/with%20space/" tmp'
-test_expect_success 'no tree-link' '! grep "foo/tree" tmp'
-test_expect_success 'no log-link' '! grep "foo/log" tmp'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0102-summary.sh b/www/git.causal.agency/cgit/tests/t0102-summary.sh
deleted file mode 100755
index b8864cb1..00000000
--- a/www/git.causal.agency/cgit/tests/t0102-summary.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on summary page'
-. ./setup.sh
-
-test_expect_success 'generate foo summary' 'cgit_url "foo" >tmp'
-test_expect_success 'find commit 1' 'grep "commit 1" tmp'
-test_expect_success 'find commit 5' 'grep "commit 5" tmp'
-test_expect_success 'find branch master' 'grep "master" tmp'
-test_expect_success 'no tags' '! grep "tags" tmp'
-test_expect_success 'clone-url expanded correctly' '
-	grep "git://example.org/foo.git" tmp
-'
-
-test_expect_success 'generate bar summary' 'cgit_url "bar" >tmp'
-test_expect_success 'no commit 45' '! grep "commit 45" tmp'
-test_expect_success 'find commit 46' 'grep "commit 46" tmp'
-test_expect_success 'find commit 50' 'grep "commit 50" tmp'
-test_expect_success 'find branch master' 'grep "master" tmp'
-test_expect_success 'no tags' '! grep "tags" tmp'
-test_expect_success 'clone-url expanded correctly' '
-	grep "git://example.org/bar.git" tmp
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0103-log.sh b/www/git.causal.agency/cgit/tests/t0103-log.sh
deleted file mode 100755
index bdf1435a..00000000
--- a/www/git.causal.agency/cgit/tests/t0103-log.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on log page'
-. ./setup.sh
-
-test_expect_success 'generate foo/log' 'cgit_url "foo/log" >tmp'
-test_expect_success 'find commit 1' 'grep "commit 1" tmp'
-test_expect_success 'find commit 5' 'grep "commit 5" tmp'
-
-test_expect_success 'generate bar/log' 'cgit_url "bar/log" >tmp'
-test_expect_success 'find commit 1' 'grep "commit 1" tmp'
-test_expect_success 'find commit 50' 'grep "commit 50" tmp'
-
-test_expect_success 'generate "with%20space/log?qt=grep&q=commit+1"' '
-	cgit_url "with+space/log&qt=grep&q=commit+1" >tmp
-'
-test_expect_success 'find commit 1' 'grep "commit 1" tmp'
-test_expect_success 'find link with %20 in path' 'grep "/with%20space/log/?qt=grep" tmp'
-test_expect_success 'find link with + in arg' 'grep "/log/?qt=grep&amp;q=commit+1" tmp'
-test_expect_success 'no links with space in path' '! grep "href=./with space/" tmp'
-test_expect_success 'no links with space in arg' '! grep "q=commit 1" tmp'
-test_expect_success 'commit 2 is not visible' '! grep "commit 2" tmp'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0104-tree.sh b/www/git.causal.agency/cgit/tests/t0104-tree.sh
deleted file mode 100755
index 2e140f59..00000000
--- a/www/git.causal.agency/cgit/tests/t0104-tree.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on tree page'
-. ./setup.sh
-
-test_expect_success 'generate bar/tree' 'cgit_url "bar/tree" >tmp'
-test_expect_success 'find file-1' 'grep "file-1" tmp'
-test_expect_success 'find file-50' 'grep "file-50" tmp'
-
-test_expect_success 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >tmp'
-
-test_expect_success 'find line 1' '
-	grep "<a id=.n1. href=.#n1.>1</a>" tmp
-'
-
-test_expect_success 'no line 2' '
-	! grep "<a id=.n2. href=.#n2.>2</a>" tmp
-'
-
-test_expect_success 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >tmp'
-
-test_expect_success 'verify a+b link' '
-	grep "/foo+bar/tree/a+b" tmp
-'
-
-test_expect_success 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >tmp'
-
-test_expect_success 'verify a+b?h=1+2 link' '
-	grep "/foo+bar/tree/a+b?h=1%2b2" tmp
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0105-commit.sh b/www/git.causal.agency/cgit/tests/t0105-commit.sh
deleted file mode 100755
index cfed1e7d..00000000
--- a/www/git.causal.agency/cgit/tests/t0105-commit.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on commit page'
-. ./setup.sh
-
-test_expect_success 'generate foo/commit' 'cgit_url "foo/commit" >tmp'
-test_expect_success 'find tree link' 'grep "<a href=./foo/tree/.>" tmp'
-test_expect_success 'find parent link' 'grep -E "<a href=./foo/commit/\?id=.+>" tmp'
-
-test_expect_success 'find commit subject' '
-	grep "<div class=.commit-subject.>commit 5<" tmp
-'
-
-test_expect_success 'find commit msg' 'grep "<pre class=.commit-msg.></pre>" tmp'
-test_expect_success 'find diffstat' 'grep "<table summary=.diffstat. class=.diffstat.>" tmp'
-
-test_expect_success 'find diff summary' '
-	grep "1 files changed, 1 insertions, 0 deletions" tmp
-'
-
-test_expect_success 'get root commit' '
-	root=$(cd repos/foo && git rev-list --reverse HEAD | head -1) &&
-	cgit_url "foo/commit&id=$root" >tmp &&
-	grep "</html>" tmp
-'
-
-test_expect_success 'root commit contains diffstat' '
-	grep "<a href=./foo/diff/file-1.id=[0-9a-f]\{40,64\}.>file-1</a>" tmp
-'
-
-test_expect_success 'root commit contains diff' '
-	grep ">diff --git a/file-1 b/file-1" tmp &&
-	grep "<span class=.add.>+1</span>" tmp
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0106-diff.sh b/www/git.causal.agency/cgit/tests/t0106-diff.sh
deleted file mode 100755
index 62a0a74a..00000000
--- a/www/git.causal.agency/cgit/tests/t0106-diff.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on diff page'
-. ./setup.sh
-
-test_expect_success 'generate foo/diff' 'cgit_url "foo/diff" >tmp'
-test_expect_success 'find diff header' 'grep "a/file-5 b/file-5" tmp'
-test_expect_success 'find blob link' 'grep "<a href=./foo/tree/file-5?id=" tmp'
-test_expect_success 'find added file' 'grep "new file mode 100644" tmp'
-
-test_expect_success 'find hunk header' '
-	grep "<span class=.hunk.>@@ -0,0 +1 @@</span>" tmp
-'
-
-test_expect_success 'find added line' '
-	grep "<span class=.add.>+5</span>" tmp
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0107-snapshot.sh b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
deleted file mode 100755
index 0811ec40..00000000
--- a/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/bin/sh
-
-test_description='Verify snapshot'
-. ./setup.sh
-
-test_expect_success 'get foo/snapshot/master.tar.gz' '
-	cgit_url "foo/snapshot/master.tar.gz" >tmp
-'
-
-test_expect_success 'check html headers' '
-	head -n 1 tmp |
-	grep "Content-Type: application/x-gzip" &&
-
-	head -n 2 tmp |
-	grep "Content-Disposition: inline; filename=.master.tar.gz."
-'
-
-test_expect_success 'strip off the header lines' '
-	strip_headers <tmp >master.tar.gz
-'
-
-test_expect_success 'verify gzip format' '
-	gunzip --test master.tar.gz
-'
-
-test_expect_success 'untar' '
-	rm -rf master &&
-	gzip -dc master.tar.gz | tar -xf -
-'
-
-test_expect_success 'count files' '
-	ls master/ >output &&
-	test_line_count = 5 output
-'
-
-test_expect_success 'verify untarred file-5' '
-	grep "^5$" master/file-5 &&
-	test_line_count = 1 master/file-5
-'
-
-if test -n "$(which lzip 2>/dev/null)"; then
-	test_set_prereq LZIP
-else
-	say 'Skipping LZIP validation tests: lzip not found'
-fi
-
-test_expect_success LZIP 'get foo/snapshot/master.tar.lz' '
-	cgit_url "foo/snapshot/master.tar.lz" >tmp
-'
-
-test_expect_success LZIP 'check html headers' '
-	head -n 1 tmp |
-	grep "Content-Type: application/x-lzip" &&
-
-	head -n 2 tmp |
-	grep "Content-Disposition: inline; filename=.master.tar.lz."
-'
-
-test_expect_success LZIP 'strip off the header lines' '
-	strip_headers <tmp >master.tar.lz
-'
-
-test_expect_success LZIP 'verify lzip format' '
-	lzip --test master.tar.lz
-'
-
-test_expect_success LZIP 'untar' '
-	rm -rf master &&
-	lzip -dc master.tar.lz | tar -xf -
-'
-
-test_expect_success LZIP 'count files' '
-	ls master/ >output &&
-	test_line_count = 5 output
-'
-
-test_expect_success LZIP 'verify untarred file-5' '
-	grep "^5$" master/file-5 &&
-	test_line_count = 1 master/file-5
-'
-
-if test -n "$(which xz 2>/dev/null)"; then
-	test_set_prereq XZ
-else
-	say 'Skipping XZ validation tests: xz not found'
-fi
-
-test_expect_success XZ 'get foo/snapshot/master.tar.xz' '
-	cgit_url "foo/snapshot/master.tar.xz" >tmp
-'
-
-test_expect_success XZ 'check html headers' '
-	head -n 1 tmp |
-	grep "Content-Type: application/x-xz" &&
-
-	head -n 2 tmp |
-	grep "Content-Disposition: inline; filename=.master.tar.xz."
-'
-
-test_expect_success XZ 'strip off the header lines' '
-	strip_headers <tmp >master.tar.xz
-'
-
-test_expect_success XZ 'verify xz format' '
-	xz --test master.tar.xz
-'
-
-test_expect_success XZ 'untar' '
-	rm -rf master &&
-	xz -dc master.tar.xz | tar -xf -
-'
-
-test_expect_success XZ 'count files' '
-	ls master/ >output &&
-	test_line_count = 5 output
-'
-
-test_expect_success XZ 'verify untarred file-5' '
-	grep "^5$" master/file-5 &&
-	test_line_count = 1 master/file-5
-'
-
-if test -n "$(which zstd 2>/dev/null)"; then
-	test_set_prereq ZSTD
-else
-	say 'Skipping ZSTD validation tests: zstd not found'
-fi
-
-test_expect_success ZSTD 'get foo/snapshot/master.tar.zst' '
-	cgit_url "foo/snapshot/master.tar.zst" >tmp
-'
-
-test_expect_success ZSTD 'check html headers' '
-	head -n 1 tmp |
-	grep "Content-Type: application/x-zstd" &&
-
-	head -n 2 tmp |
-	grep "Content-Disposition: inline; filename=.master.tar.zst."
-'
-
-test_expect_success ZSTD 'strip off the header lines' '
-	strip_headers <tmp >master.tar.zst
-'
-
-test_expect_success ZSTD 'verify zstd format' '
-	zstd --test master.tar.zst
-'
-
-test_expect_success ZSTD 'untar' '
-	rm -rf master &&
-	zstd -dc master.tar.zst | tar -xf -
-'
-
-test_expect_success ZSTD 'count files' '
-	ls master/ >output &&
-	test_line_count = 5 output
-'
-
-test_expect_success ZSTD 'verify untarred file-5' '
-	grep "^5$" master/file-5 &&
-	test_line_count = 1 master/file-5
-'
-
-test_expect_success 'get foo/snapshot/master.zip' '
-	cgit_url "foo/snapshot/master.zip" >tmp
-'
-
-test_expect_success 'check HTML headers (zip)' '
-	head -n 1 tmp |
-	grep "Content-Type: application/x-zip" &&
-
-	head -n 2 tmp |
-	grep "Content-Disposition: inline; filename=.master.zip."
-'
-
-test_expect_success 'strip off the header lines (zip)' '
-	strip_headers <tmp >master.zip
-'
-
-if test -n "$(which unzip 2>/dev/null)"; then
-	test_set_prereq UNZIP
-else
-	say 'Skipping ZIP validation tests: unzip not found'
-fi
-
-test_expect_success UNZIP 'verify zip format' '
-	unzip -t master.zip
-'
-
-test_expect_success UNZIP 'unzip' '
-	rm -rf master &&
-	unzip master.zip
-'
-
-test_expect_success UNZIP 'count files (zip)' '
-	ls master/ >output &&
-	test_line_count = 5 output
-'
-
-test_expect_success UNZIP 'verify unzipped file-5' '
-	grep "^5$" master/file-5 &&
-	test_line_count = 1 master/file-5
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0108-patch.sh b/www/git.causal.agency/cgit/tests/t0108-patch.sh
deleted file mode 100755
index 013d6802..00000000
--- a/www/git.causal.agency/cgit/tests/t0108-patch.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on patch page'
-. ./setup.sh
-
-test_expect_success 'generate foo/patch' '
-	cgit_query "url=foo/patch" >tmp
-'
-
-test_expect_success 'find `From:` line' '
-	grep "^From: " tmp
-'
-
-test_expect_success 'find `Date:` line' '
-	grep "^Date: " tmp
-'
-
-test_expect_success 'find `Subject:` line' '
-	grep "^Subject: commit 5" tmp
-'
-
-test_expect_success 'find `cgit` signature' '
-	tail -2 tmp | head -1 | grep "^cgit"
-'
-
-test_expect_success 'compare with output of git-format-patch(1)' '
-	CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) &&
-	git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD^ >tmp2 &&
-	strip_headers <tmp >tmp_ &&
-	test_cmp tmp_ tmp2
-'
-
-test_expect_success 'find initial commit' '
-	root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
-'
-
-test_expect_success 'generate patch for initial commit' '
-	cgit_query "url=foo/patch&id=$root" >tmp
-'
-
-test_expect_success 'find `cgit` signature' '
-	tail -2 tmp | head -1 | grep "^cgit"
-'
-
-test_expect_success 'generate patches for multiple commits' '
-	id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
-	id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) &&
-	cgit_query "url=foo/patch&id=$id&id2=$id2" >tmp
-'
-
-test_expect_success 'find `cgit` signature' '
-	tail -2 tmp | head -1 | grep "^cgit"
-'
-
-test_expect_success 'compare with output of git-format-patch(1)' '
-	CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) &&
-	git --git-dir="$PWD/repos/foo/.git" format-patch -N --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD~3..HEAD >tmp2 &&
-	strip_headers <tmp >tmp_ &&
-	test_cmp tmp_ tmp2
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
deleted file mode 100755
index 189ef281..00000000
--- a/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/sh
-
-test_description='Ensure that git does not access $HOME'
-. ./setup.sh
-
-test -n "$(which strace 2>/dev/null)" || {
-	skip_all='Skipping access validation tests: strace not found'
-	test_done
-	exit
-}
-
-strace true 2>/dev/null || {
-	skip_all='Skipping access validation tests: strace not functional'
-	test_done
-	exit
-}
-
-test_no_home_access () {
-	non_existent_path="/path/to/some/place/that/does/not/possibly/exist"
-	while test -d "$non_existent_path"; do
-		non_existent_path="$non_existent_path/$(date +%N)"
-	done &&
-	strace \
-		-E HOME="$non_existent_path" \
-		-E CGIT_CONFIG="$PWD/cgitrc" \
-		-E QUERY_STRING="url=$1" \
-		-e access -f -o strace.out cgit &&
-	! grep "$non_existent_path" strace.out
-}
-
-test_no_home_access_success() {
-	test_expect_success "do not access \$HOME: $1" "
-		test_no_home_access '$1'
-	"
-}
-
-test_no_home_access_success
-test_no_home_access_success foo
-test_no_home_access_success foo/refs
-test_no_home_access_success foo/log
-test_no_home_access_success foo/tree
-test_no_home_access_success foo/tree/file-1
-test_no_home_access_success foo/commit
-test_no_home_access_success foo/diff
-test_no_home_access_success foo/patch
-test_no_home_access_success foo/snapshot/master.tar.gz
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
deleted file mode 100755
index 66fa7d5d..00000000
--- a/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/sh
-
-test_description='Check content on rawdiff page'
-. ./setup.sh
-
-test_expect_success 'generate foo/rawdiff' '
-	cgit_query "url=foo/rawdiff" >tmp
-'
-
-test_expect_success 'compare with output of git-diff(1)' '
-	git --git-dir="$PWD/repos/foo/.git" diff HEAD^.. >tmp2 &&
-	sed "1,4d" tmp >tmp_ &&
-	cmp tmp_ tmp2
-'
-
-test_expect_success 'find initial commit' '
-	root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
-'
-
-test_expect_success 'generate diff for initial commit' '
-	cgit_query "url=foo/rawdiff&id=$root" >tmp
-'
-
-test_expect_success 'compare with output of git-diff-tree(1)' '
-	git --git-dir="$PWD/repos/foo/.git" diff-tree -p --no-commit-id --root "$root" >tmp2 &&
-	sed "1,4d" tmp >tmp_ &&
-	cmp tmp_ tmp2
-'
-
-test_expect_success 'generate diff for multiple commits' '
-	id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
-	id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) &&
-	cgit_query "url=foo/rawdiff&id=$id&id2=$id2" >tmp
-'
-
-test_expect_success 'compare with output of git-diff(1)' '
-	git --git-dir="$PWD/repos/foo/.git" diff HEAD~3..HEAD >tmp2 &&
-	sed "1,4d" tmp >tmp_ &&
-	cmp tmp_ tmp2
-'
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/t0111-filter.sh b/www/git.causal.agency/cgit/tests/t0111-filter.sh
deleted file mode 100755
index e5d35750..00000000
--- a/www/git.causal.agency/cgit/tests/t0111-filter.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/sh
-
-test_description='Check filtered content'
-. ./setup.sh
-
-prefixes="exec"
-
-for prefix in $prefixes
-do
-	test_expect_success "generate filter-$prefix/tree/a%2bb" "
-		cgit_url 'filter-$prefix/tree/a%2bb' >tmp
-	"
-
-	test_expect_success "check whether the $prefix source filter works" '
-		grep "<code>a+b HELLO$" tmp
-	'
-
-	test_expect_success "generate filter-$prefix/about/" "
-		cgit_url 'filter-$prefix/about/' >tmp
-	"
-
-	test_expect_success "check whether the $prefix about filter works" '
-		grep "<div id='"'"'summary'"'"'>a+b HELLO$" tmp
-	'
-
-	test_expect_success "generate filter-$prefix/commit/" "
-		cgit_url 'filter-$prefix/commit/' >tmp
-	"
-
-	test_expect_success "check whether the $prefix commit filter works" '
-		grep "<div class='"'"'commit-subject'"'"'>ADD A+B" tmp
-	'
-
-	test_expect_success "check whether the $prefix email filter works for authors" '
-		grep "<author@example.com> commit A U THOR &LT;AUTHOR@EXAMPLE.COM&GT;" tmp
-	'
-
-	test_expect_success "check whether the $prefix email filter works for committers" '
-		grep "<committer@example.com> commit C O MITTER &LT;COMMITTER@EXAMPLE.COM&GT;" tmp
-	'
-done
-
-test_done
diff --git a/www/git.causal.agency/cgit/tests/valgrind/bin/cgit b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
deleted file mode 100755
index dcdfbe53..00000000
--- a/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-# Note that we currently use Git's suppression file and there are variables
-# $GIT_VALGRIND and $CGIT_VALGRIND which point to different places.
-exec valgrind -q --error-exitcode=126 \
-	--suppressions="$GIT_VALGRIND/default.supp" \
-	--gen-suppressions=all \
-	--leak-check=no \
-	--track-origins=yes \
-	--log-fd=4 \
-	--input-fd=4 \
-	"$CGIT_VALGRIND/../../cgit" "$@"
diff --git a/www/git.causal.agency/cgit/ui-atom.c b/www/git.causal.agency/cgit/ui-atom.c
deleted file mode 100644
index 8329e01a..00000000
--- a/www/git.causal.agency/cgit/ui-atom.c
+++ /dev/null
@@ -1,158 +0,0 @@
-/* ui-atom.c: functions for atom feeds
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-atom.h"
-#include "html.h"
-#include "ui-shared.h"
-
-static void add_entry(struct commit *commit, const char *host)
-{
-	char delim = '&';
-	char *hex;
-	char *mail, *t, *t2;
-	struct commitinfo *info;
-
-	info = cgit_parse_commit(commit);
-	hex = oid_to_hex(&commit->object.oid);
-	html("<entry>\n");
-	html("<title>");
-	html_txt(info->subject);
-	html("</title>\n");
-	html("<updated>");
-	html_txt(show_date(info->committer_date, 0,
-                    date_mode_from_type(DATE_ISO8601_STRICT)));
-	html("</updated>\n");
-	html("<author>\n");
-	if (info->author) {
-		html("<name>");
-		html_txt(info->author);
-		html("</name>\n");
-	}
-	if (info->author_email && !ctx.cfg.noplainemail) {
-		mail = xstrdup(info->author_email);
-		t = strchr(mail, '<');
-		if (t)
-			t++;
-		else
-			t = mail;
-		t2 = strchr(t, '>');
-		if (t2)
-			*t2 = '\0';
-		html("<email>");
-		html_txt(t);
-		html("</email>\n");
-		free(mail);
-	}
-	html("</author>\n");
-	html("<published>");
-	html_txt(show_date(info->author_date, 0,
-                    date_mode_from_type(DATE_ISO8601_STRICT)));
-	html("</published>\n");
-	if (host) {
-		char *pageurl;
-		html("<link rel='alternate' type='text/html' href='");
-		html(cgit_httpscheme());
-		html_attr(host);
-		pageurl = cgit_pageurl(ctx.repo->url, "commit", NULL);
-		html_attr(pageurl);
-		if (ctx.cfg.virtual_root)
-			delim = '?';
-		html_attrf("%cid=%s", delim, hex);
-		html("'/>\n");
-		free(pageurl);
-	}
-	html("<id>");
-	html_txtf("urn:%s:%s", the_hash_algo->name, hex);
-	html("</id>\n");
-	html("<content type='text'>\n");
-	html_txt(info->msg);
-	html("</content>\n");
-	html("</entry>\n");
-	cgit_free_commitinfo(info);
-}
-
-
-void cgit_print_atom(char *tip, const char *path, int max_count)
-{
-	char *host;
-	const char *argv[] = {NULL, tip, NULL, NULL, NULL};
-	struct commit *commit;
-	struct rev_info rev;
-	int argc = 2;
-	int first = 1;
-
-	if (ctx.qry.show_all)
-		argv[1] = "--all";
-	else if (!tip)
-		argv[1] = ctx.qry.head;
-
-	if (path) {
-		argv[argc++] = "--";
-		argv[argc++] = path;
-	}
-
-	init_revisions(&rev, NULL);
-	rev.abbrev = DEFAULT_ABBREV;
-	rev.commit_format = CMIT_FMT_DEFAULT;
-	rev.verbose_header = 1;
-	rev.show_root_diff = 0;
-	rev.max_count = max_count;
-	setup_revisions(argc, argv, &rev, NULL);
-	prepare_revision_walk(&rev);
-
-	host = cgit_hosturl();
-	ctx.page.mimetype = "text/xml";
-	ctx.page.charset = "utf-8";
-	cgit_print_http_headers();
-	html("<feed xmlns='http://www.w3.org/2005/Atom'>\n");
-	html("<title>");
-	html_txt(ctx.repo->name);
-	if (path) {
-		html("/");
-		html_txt(path);
-	}
-	if (tip && !ctx.qry.show_all) {
-		html(", branch ");
-		html_txt(tip);
-	}
-	html("</title>\n");
-	html("<subtitle>");
-	html_txt(ctx.repo->desc);
-	html("</subtitle>\n");
-	if (host) {
-		char *fullurl = cgit_currentfullurl();
-		char *repourl = cgit_repourl(ctx.repo->url);
-		html("<id>");
-		html_txtf("%s%s%s", cgit_httpscheme(), host, fullurl);
-		html("</id>\n");
-		html("<link rel='self' href='");
-		html_attrf("%s%s%s", cgit_httpscheme(), host, fullurl);
-		html("'/>\n");
-		html("<link rel='alternate' type='text/html' href='");
-		html_attrf("%s%s%s", cgit_httpscheme(), host, repourl);
-		html("'/>\n");
-		free(fullurl);
-		free(repourl);
-	}
-	while ((commit = get_revision(&rev)) != NULL) {
-		if (first) {
-			html("<updated>");
-			html_txt(show_date(commit->date, 0,
-				date_mode_from_type(DATE_ISO8601_STRICT)));
-			html("</updated>\n");
-			first = 0;
-		}
-		add_entry(commit, host);
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
-	html("</feed>\n");
-	free(host);
-}
diff --git a/www/git.causal.agency/cgit/ui-atom.h b/www/git.causal.agency/cgit/ui-atom.h
deleted file mode 100644
index dda953bb..00000000
--- a/www/git.causal.agency/cgit/ui-atom.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef UI_ATOM_H
-#define UI_ATOM_H
-
-extern void cgit_print_atom(char *tip, const char *path, int max_count);
-
-#endif
diff --git a/www/git.causal.agency/cgit/ui-blame.c b/www/git.causal.agency/cgit/ui-blame.c
deleted file mode 100644
index 4adec2b9..00000000
--- a/www/git.causal.agency/cgit/ui-blame.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/* ui-blame.c: functions for blame output
- *
- * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-blame.h"
-#include "html.h"
-#include "ui-shared.h"
-#include "strvec.h"
-#include "blame.h"
-
-
-static char *emit_suspect_detail(struct blame_origin *suspect)
-{
-	struct commitinfo *info;
-	struct strbuf detail = STRBUF_INIT;
-
-	info = cgit_parse_commit(suspect->commit);
-
-	strbuf_addf(&detail, "author  %s", info->author);
-	if (!ctx.cfg.noplainemail)
-		strbuf_addf(&detail, " %s", info->author_email);
-	strbuf_addf(&detail, "  %s\n",
-		    show_date(info->author_date, info->author_tz,
-				    cgit_date_mode(DATE_ISO8601)));
-
-	strbuf_addf(&detail, "committer  %s", info->committer);
-	if (!ctx.cfg.noplainemail)
-		strbuf_addf(&detail, " %s", info->committer_email);
-	strbuf_addf(&detail, "  %s\n\n",
-		    show_date(info->committer_date, info->committer_tz,
-				    cgit_date_mode(DATE_ISO8601)));
-
-	strbuf_addstr(&detail, info->subject);
-
-	cgit_free_commitinfo(info);
-	return strbuf_detach(&detail, NULL);
-}
-
-static void emit_blame_entry_hash(struct blame_entry *ent)
-{
-	struct blame_origin *suspect = ent->suspect;
-	struct object_id *oid = &suspect->commit->object.oid;
-	unsigned long line = 0;
-
-	char *detail = emit_suspect_detail(suspect);
-	html("<span class='oid'>");
-	cgit_commit_link(find_unique_abbrev(oid, DEFAULT_ABBREV), detail,
-			 NULL, ctx.qry.head, oid_to_hex(oid), suspect->path);
-	html("</span>");
-	free(detail);
-
-	while (line++ < ent->num_lines)
-		html("\n");
-}
-
-static void emit_blame_entry_linenumber(struct blame_entry *ent)
-{
-	const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
-
-	unsigned long lineno = ent->lno;
-	while (lineno < ent->lno + ent->num_lines)
-		htmlf(numberfmt, ++lineno);
-}
-
-static void emit_blame_entry_line_background(struct blame_scoreboard *sb,
-					     struct blame_entry *ent)
-{
-	unsigned long line;
-	size_t len, maxlen = 2;
-	const char* pos, *endpos;
-
-	for (line = ent->lno; line < ent->lno + ent->num_lines; line++) {
-		html("\n");
-		pos = blame_nth_line(sb, line);
-		endpos = blame_nth_line(sb, line + 1);
-		len = 0;
-		while (pos < endpos) {
-			len++;
-			if (*pos++ == '\t')
-				len = (len + 7) & ~7;
-		}
-		if (len > maxlen)
-			maxlen = len;
-	}
-
-	for (len = 0; len < maxlen - 1; len++)
-		html(" ");
-}
-
-struct walk_tree_context {
-	char *curr_rev;
-	int match_baselen;
-	int state;
-};
-
-static void print_object(const struct object_id *oid, const char *path,
-			 const char *basename, const char *rev)
-{
-	enum object_type type;
-	char *buf;
-	unsigned long size;
-	struct strvec rev_argv = STRVEC_INIT;
-	struct rev_info revs;
-	struct blame_scoreboard sb;
-	struct blame_origin *o;
-	struct blame_entry *ent = NULL;
-
-	type = oid_object_info(the_repository, oid, &size);
-	if (type == OBJ_BAD) {
-		cgit_print_error_page(404, "Not found", "Bad object name: %s",
-				      oid_to_hex(oid));
-		return;
-	}
-
-	buf = read_object_file(oid, &type, &size);
-	if (!buf) {
-		cgit_print_error_page(500, "Internal server error",
-			"Error reading object %s", oid_to_hex(oid));
-		return;
-	}
-
-	strvec_push(&rev_argv, "blame");
-	strvec_push(&rev_argv, rev);
-	init_revisions(&revs, NULL);
-	revs.diffopt.flags.allow_textconv = 1;
-	setup_revisions(rev_argv.nr, rev_argv.v, &revs, NULL);
-	init_scoreboard(&sb);
-	sb.revs = &revs;
-	sb.repo = the_repository;
-	sb.path = path;
-	setup_scoreboard(&sb, &o);
-	o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o);
-	prio_queue_put(&sb.commits, o->commit);
-	blame_origin_decref(o);
-	sb.ent = NULL;
-	sb.path = path;
-	assign_blame(&sb, 0);
-	blame_sort_final(&sb);
-	blame_coalesce(&sb);
-
-	cgit_set_title_from_path(path);
-
-	cgit_print_layout_start();
-	htmlf("blob: %s (", oid_to_hex(oid));
-	cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path);
-	html(") (");
-	cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path);
-	html(")\n");
-
-	if (buffer_is_binary(buf, size)) {
-		html("<div class='error'>blob is binary.</div>");
-		goto cleanup;
-	}
-	if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
-		htmlf("<div class='error'>blob size (%ldKB)"
-		      " exceeds display size limit (%dKB).</div>",
-		      size / 1024, ctx.cfg.max_blob_size);
-		goto cleanup;
-	}
-
-	html("<table class='blame blob'>\n<tr>\n");
-
-	/* Commit hashes */
-	html("<td class='hashes'>");
-	for (ent = sb.ent; ent; ent = ent->next) {
-		html("<div class='alt'><pre>");
-		emit_blame_entry_hash(ent);
-		html("</pre></div>");
-	}
-	html("</td>\n");
-
-	/* Line numbers */
-	if (ctx.cfg.enable_tree_linenumbers) {
-		html("<td class='linenumbers'>");
-		for (ent = sb.ent; ent; ent = ent->next) {
-			html("<div class='alt'><pre>");
-			emit_blame_entry_linenumber(ent);
-			html("</pre></div>");
-		}
-		html("</td>\n");
-	}
-
-	html("<td class='lines'><div>");
-
-	/* Colored bars behind lines */
-	html("<div>");
-	for (ent = sb.ent; ent; ) {
-		struct blame_entry *e = ent->next;
-		html("<div class='alt'><pre>");
-		emit_blame_entry_line_background(&sb, ent);
-		html("</pre></div>");
-		free(ent);
-		ent = e;
-	}
-	html("</div>");
-
-	free((void *)sb.final_buf);
-
-	/* Lines */
-	html("<pre><code>");
-	if (ctx.repo->source_filter) {
-		char *filter_arg = xstrdup(basename);
-		cgit_open_filter(ctx.repo->source_filter, filter_arg);
-		html_raw(buf, size);
-		cgit_close_filter(ctx.repo->source_filter);
-		free(filter_arg);
-	} else {
-		html_txt(buf);
-	}
-	html("</code></pre>");
-
-	html("</div></td>\n");
-
-	html("</tr>\n</table>\n");
-
-	cgit_print_layout_end();
-
-cleanup:
-	free(buf);
-}
-
-static int walk_tree(const struct object_id *oid, struct strbuf *base,
-		     const char *pathname, unsigned mode, void *cbdata)
-{
-	struct walk_tree_context *walk_tree_ctx = cbdata;
-
-	if (base->len == walk_tree_ctx->match_baselen) {
-		if (S_ISREG(mode)) {
-			struct strbuf buffer = STRBUF_INIT;
-			strbuf_addbuf(&buffer, base);
-			strbuf_addstr(&buffer, pathname);
-			print_object(oid, buffer.buf, pathname,
-				     walk_tree_ctx->curr_rev);
-			strbuf_release(&buffer);
-			walk_tree_ctx->state = 1;
-		} else if (S_ISDIR(mode)) {
-			walk_tree_ctx->state = 2;
-		}
-	} else if (base->len < INT_MAX
-			&& (int)base->len > walk_tree_ctx->match_baselen) {
-		walk_tree_ctx->state = 2;
-	} else if (S_ISDIR(mode)) {
-		return READ_TREE_RECURSIVE;
-	}
-	return 0;
-}
-
-static int basedir_len(const char *path)
-{
-	char *p = strrchr(path, '/');
-	if (p)
-		return p - path + 1;
-	return 0;
-}
-
-void cgit_print_blame(void)
-{
-	const char *rev = ctx.qry.oid;
-	struct object_id oid;
-	struct commit *commit;
-	struct pathspec_item path_items = {
-		.match = ctx.qry.path,
-		.len = ctx.qry.path ? strlen(ctx.qry.path) : 0
-	};
-	struct pathspec paths = {
-		.nr = 1,
-		.items = &path_items
-	};
-	struct walk_tree_context walk_tree_ctx = {
-		.state = 0
-	};
-
-	if (!rev)
-		rev = ctx.qry.head;
-
-	if (get_oid(rev, &oid)) {
-		cgit_print_error_page(404, "Not found",
-			"Invalid revision name: %s", rev);
-		return;
-	}
-	commit = lookup_commit_reference(the_repository, &oid);
-	if (!commit || parse_commit(commit)) {
-		cgit_print_error_page(404, "Not found",
-			"Invalid commit reference: %s", rev);
-		return;
-	}
-
-	walk_tree_ctx.curr_rev = xstrdup(rev);
-	walk_tree_ctx.match_baselen = (path_items.match) ?
-				       basedir_len(path_items.match) : -1;
-
-	read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
-		  &paths, walk_tree, &walk_tree_ctx);
-	if (!walk_tree_ctx.state)
-		cgit_print_error_page(404, "Not found", "Not found");
-	else if (walk_tree_ctx.state == 2)
-		cgit_print_error_page(404, "No blame for folders",
-			"Blame is not available for folders.");
-
-	free(walk_tree_ctx.curr_rev);
-}
diff --git a/www/git.causal.agency/cgit/ui-blame.h b/www/git.causal.agency/cgit/ui-blame.h
deleted file mode 100644
index 5b97e035..00000000
--- a/www/git.causal.agency/cgit/ui-blame.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef UI_BLAME_H
-#define UI_BLAME_H
-
-extern void cgit_print_blame(void);
-
-#endif /* UI_BLAME_H */
diff --git a/www/git.causal.agency/cgit/ui-blob.c b/www/git.causal.agency/cgit/ui-blob.c
deleted file mode 100644
index c10ae42e..00000000
--- a/www/git.causal.agency/cgit/ui-blob.c
+++ /dev/null
@@ -1,182 +0,0 @@
-/* ui-blob.c: show blob content
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-blob.h"
-#include "html.h"
-#include "ui-shared.h"
-
-struct walk_tree_context {
-	const char *match_path;
-	struct object_id *matched_oid;
-	unsigned int found_path:1;
-	unsigned int file_only:1;
-};
-
-static int walk_tree(const struct object_id *oid, struct strbuf *base,
-		const char *pathname, unsigned mode, void *cbdata)
-{
-	struct walk_tree_context *walk_tree_ctx = cbdata;
-
-	if (walk_tree_ctx->file_only && !S_ISREG(mode))
-		return READ_TREE_RECURSIVE;
-	if (strncmp(base->buf, walk_tree_ctx->match_path, base->len)
-		|| strcmp(walk_tree_ctx->match_path + base->len, pathname))
-		return READ_TREE_RECURSIVE;
-	oidcpy(walk_tree_ctx->matched_oid, oid);
-	walk_tree_ctx->found_path = 1;
-	return 0;
-}
-
-int cgit_ref_path_exists(const char *path, const char *ref, int file_only)
-{
-	struct object_id oid;
-	unsigned long size;
-	struct pathspec_item path_items = {
-		.match = xstrdup(path),
-		.len = strlen(path)
-	};
-	struct pathspec paths = {
-		.nr = 1,
-		.items = &path_items
-	};
-	struct walk_tree_context walk_tree_ctx = {
-		.match_path = path,
-		.matched_oid = &oid,
-		.found_path = 0,
-		.file_only = file_only
-	};
-
-	if (get_oid(ref, &oid))
-		goto done;
-	if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT)
-		goto done;
-	read_tree(the_repository,
-		  repo_get_commit_tree(the_repository, lookup_commit_reference(the_repository, &oid)),
-		  &paths, walk_tree, &walk_tree_ctx);
-
-done:
-	free(path_items.match);
-	return walk_tree_ctx.found_path;
-}
-
-int cgit_print_file(char *path, const char *head, int file_only)
-{
-	struct object_id oid;
-	enum object_type type;
-	char *buf;
-	unsigned long size;
-	struct commit *commit;
-	struct pathspec_item path_items = {
-		.match = path,
-		.len = strlen(path)
-	};
-	struct pathspec paths = {
-		.nr = 1,
-		.items = &path_items
-	};
-	struct walk_tree_context walk_tree_ctx = {
-		.match_path = path,
-		.matched_oid = &oid,
-		.found_path = 0,
-		.file_only = file_only
-	};
-
-	if (get_oid(head, &oid))
-		return -1;
-	type = oid_object_info(the_repository, &oid, &size);
-	if (type == OBJ_COMMIT) {
-		commit = lookup_commit_reference(the_repository, &oid);
-		read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
-			  &paths, walk_tree, &walk_tree_ctx);
-		if (!walk_tree_ctx.found_path)
-			return -1;
-		type = oid_object_info(the_repository, &oid, &size);
-	}
-	if (type == OBJ_BAD)
-		return -1;
-	buf = read_object_file(&oid, &type, &size);
-	if (!buf)
-		return -1;
-	buf[size] = '\0';
-	html_raw(buf, size);
-	free(buf);
-	return 0;
-}
-
-void cgit_print_blob(const char *hex, char *path, const char *head, int file_only)
-{
-	struct object_id oid;
-	enum object_type type;
-	char *buf;
-	unsigned long size;
-	struct commit *commit;
-	struct pathspec_item path_items = {
-		.match = path,
-		.len = path ? strlen(path) : 0
-	};
-	struct pathspec paths = {
-		.nr = 1,
-		.items = &path_items
-	};
-	struct walk_tree_context walk_tree_ctx = {
-		.match_path = path,
-		.matched_oid = &oid,
-		.found_path = 0,
-		.file_only = file_only
-	};
-
-	if (hex) {
-		if (get_oid_hex(hex, &oid)) {
-			cgit_print_error_page(400, "Bad request",
-					"Bad hex value: %s", hex);
-			return;
-		}
-	} else {
-		if (get_oid(head, &oid)) {
-			cgit_print_error_page(404, "Not found",
-					"Bad ref: %s", head);
-			return;
-		}
-	}
-
-	type = oid_object_info(the_repository, &oid, &size);
-
-	if ((!hex) && type == OBJ_COMMIT && path) {
-		commit = lookup_commit_reference(the_repository, &oid);
-		read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
-			  &paths, walk_tree, &walk_tree_ctx);
-		type = oid_object_info(the_repository, &oid, &size);
-	}
-
-	if (type == OBJ_BAD) {
-		cgit_print_error_page(404, "Not found",
-				"Bad object name: %s", hex);
-		return;
-	}
-
-	buf = read_object_file(&oid, &type, &size);
-	if (!buf) {
-		cgit_print_error_page(500, "Internal server error",
-				"Error reading object %s", hex);
-		return;
-	}
-
-	buf[size] = '\0';
-	if (buffer_is_binary(buf, size))
-		ctx.page.mimetype = "application/octet-stream";
-	else
-		ctx.page.mimetype = "text/plain";
-	ctx.page.filename = path;
-
-	html("X-Content-Type-Options: nosniff\n");
-	html("Content-Security-Policy: default-src 'none'\n");
-	cgit_print_http_headers();
-	html_raw(buf, size);
-	free(buf);
-}
diff --git a/www/git.causal.agency/cgit/ui-blob.h b/www/git.causal.agency/cgit/ui-blob.h
deleted file mode 100644
index 16847b20..00000000
--- a/www/git.causal.agency/cgit/ui-blob.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef UI_BLOB_H
-#define UI_BLOB_H
-
-extern int cgit_ref_path_exists(const char *path, const char *ref, int file_only);
-extern int cgit_print_file(char *path, const char *head, int file_only);
-extern void cgit_print_blob(const char *hex, char *path, const char *head, int file_only);
-
-#endif /* UI_BLOB_H */
diff --git a/www/git.causal.agency/cgit/ui-clone.c b/www/git.causal.agency/cgit/ui-clone.c
deleted file mode 100644
index 5dccb639..00000000
--- a/www/git.causal.agency/cgit/ui-clone.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* ui-clone.c: functions for http cloning, based on
- * git's http-backend.c by Shawn O. Pearce
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-clone.h"
-#include "html.h"
-#include "ui-shared.h"
-#include "packfile.h"
-#include "object-store.h"
-
-static int print_ref_info(const char *refname, const struct object_id *oid,
-                          int flags, void *cb_data)
-{
-	struct object *obj;
-
-	if (!(obj = parse_object(the_repository, oid)))
-		return 0;
-
-	htmlf("%s\t%s\n", oid_to_hex(oid), refname);
-	if (obj->type == OBJ_TAG) {
-		if (!(obj = deref_tag(the_repository, obj, refname, 0)))
-			return 0;
-		htmlf("%s\t%s^{}\n", oid_to_hex(&obj->oid), refname);
-	}
-	return 0;
-}
-
-static void print_pack_info(void)
-{
-	struct packed_git *pack;
-	char *offset;
-
-	ctx.page.mimetype = "text/plain";
-	ctx.page.filename = "objects/info/packs";
-	cgit_print_http_headers();
-	reprepare_packed_git(the_repository);
-	for (pack = get_packed_git(the_repository); pack; pack = pack->next) {
-		if (pack->pack_local) {
-			offset = strrchr(pack->pack_name, '/');
-			if (offset && offset[1] != '\0')
-				++offset;
-			else
-				offset = pack->pack_name;
-			htmlf("P %s\n", offset);
-		}
-	}
-}
-
-static void send_file(const char *path)
-{
-	struct stat st;
-
-	if (stat(path, &st)) {
-		switch (errno) {
-		case ENOENT:
-			cgit_print_error_page(404, "Not found", "Not found");
-			break;
-		case EACCES:
-			cgit_print_error_page(403, "Forbidden", "Forbidden");
-			break;
-		default:
-			cgit_print_error_page(400, "Bad request", "Bad request");
-		}
-		return;
-	}
-	ctx.page.mimetype = "application/octet-stream";
-	ctx.page.filename = path;
-	skip_prefix(path, ctx.repo->path, &ctx.page.filename);
-	skip_prefix(ctx.page.filename, "/", &ctx.page.filename);
-	cgit_print_http_headers();
-	html_include(path);
-}
-
-void cgit_clone_info(void)
-{
-	if (!ctx.qry.path || strcmp(ctx.qry.path, "refs")) {
-		cgit_print_error_page(400, "Bad request", "Bad request");
-		return;
-	}
-
-	ctx.page.mimetype = "text/plain";
-	ctx.page.filename = "info/refs";
-	cgit_print_http_headers();
-	for_each_ref(print_ref_info, NULL);
-}
-
-void cgit_clone_objects(void)
-{
-	char *p;
-
-	if (!ctx.qry.path)
-		goto err;
-
-	if (!strcmp(ctx.qry.path, "info/packs")) {
-		print_pack_info();
-		return;
-	}
-
-	/* Avoid directory traversal by forbidding "..", but also work around
-	 * other funny business by just specifying a fairly strict format. For
-	 * example, now we don't have to stress out about the Cygwin port.
-	 */
-	for (p = ctx.qry.path; *p; ++p) {
-		if (*p == '.' && *(p + 1) == '.')
-			goto err;
-		if (!isalnum(*p) && *p != '/' && *p != '.' && *p != '-')
-			goto err;
-	}
-
-	send_file(git_path("objects/%s", ctx.qry.path));
-	return;
-
-err:
-	cgit_print_error_page(400, "Bad request", "Bad request");
-}
-
-void cgit_clone_head(void)
-{
-	send_file(git_path("%s", "HEAD"));
-}
diff --git a/www/git.causal.agency/cgit/ui-clone.h b/www/git.causal.agency/cgit/ui-clone.h
deleted file mode 100644
index 3e460a3d..00000000
--- a/www/git.causal.agency/cgit/ui-clone.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef UI_CLONE_H
-#define UI_CLONE_H
-
-void cgit_clone_info(void);
-void cgit_clone_objects(void);
-void cgit_clone_head(void);
-
-#endif /* UI_CLONE_H */
diff --git a/www/git.causal.agency/cgit/ui-commit.c b/www/git.causal.agency/cgit/ui-commit.c
deleted file mode 100644
index b49259e6..00000000
--- a/www/git.causal.agency/cgit/ui-commit.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/* ui-commit.c: generate commit view
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-commit.h"
-#include "html.h"
-#include "ui-shared.h"
-#include "ui-diff.h"
-#include "ui-log.h"
-
-void cgit_print_commit(char *hex, const char *prefix)
-{
-	struct commit *commit, *parent;
-	struct commitinfo *info, *parent_info;
-	struct commit_list *p;
-	struct strbuf notes = STRBUF_INIT;
-	struct object_id oid;
-	char *tmp, *tmp2;
-	int parents = 0;
-
-	if (!hex)
-		hex = ctx.qry.head;
-
-	if (get_oid(hex, &oid)) {
-		cgit_print_error_page(400, "Bad request",
-				"Bad object id: %s", hex);
-		return;
-	}
-	commit = lookup_commit_reference(the_repository, &oid);
-	if (!commit) {
-		cgit_print_error_page(404, "Not found",
-				"Bad commit reference: %s", hex);
-		return;
-	}
-	info = cgit_parse_commit(commit);
-
-	format_display_notes(&oid, &notes, PAGE_ENCODING, 1);
-
-	load_ref_decorations(NULL, DECORATE_FULL_REFS);
-
-	ctx.page.title = fmtalloc("%s - %s", info->subject, ctx.page.title);
-	cgit_print_layout_start();
-	cgit_print_diff_ctrls();
-	html("<table summary='commit info' class='commit-info'>\n");
-	html("<tr><th>author</th><td>");
-	cgit_open_filter(ctx.repo->email_filter, info->author_email, "commit");
-	html_txt(info->author);
-	if (!ctx.cfg.noplainemail) {
-		html(" ");
-		html_txt(info->author_email);
-	}
-	cgit_close_filter(ctx.repo->email_filter);
-	html("</td><td class='right'>");
-	html_txt(show_date(info->author_date, info->author_tz,
-				cgit_date_mode(DATE_ISO8601)));
-	html("</td></tr>\n");
-	html("<tr><th>committer</th><td>");
-	cgit_open_filter(ctx.repo->email_filter, info->committer_email, "commit");
-	html_txt(info->committer);
-	if (!ctx.cfg.noplainemail) {
-		html(" ");
-		html_txt(info->committer_email);
-	}
-	cgit_close_filter(ctx.repo->email_filter);
-	html("</td><td class='right'>");
-	html_txt(show_date(info->committer_date, info->committer_tz,
-				cgit_date_mode(DATE_ISO8601)));
-	html("</td></tr>\n");
-	html("<tr><th>commit</th><td colspan='2' class='oid'>");
-	tmp = oid_to_hex(&commit->object.oid);
-	cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix);
-	html(" (");
-	cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix);
-	html(")</td></tr>\n");
-	html("<tr><th>tree</th><td colspan='2' class='oid'>");
-	tmp = xstrdup(hex);
-	cgit_tree_link(oid_to_hex(get_commit_tree_oid(commit)), NULL, NULL,
-		       ctx.qry.head, tmp, NULL);
-	if (prefix) {
-		html(" /");
-		cgit_tree_link(prefix, NULL, NULL, ctx.qry.head, tmp, prefix);
-	}
-	free(tmp);
-	html("</td></tr>\n");
-	for (p = commit->parents; p; p = p->next) {
-		parent = lookup_commit_reference(the_repository, &p->item->object.oid);
-		if (!parent) {
-			html("<tr><td colspan='3'>");
-			cgit_print_error("Error reading parent commit");
-			html("</td></tr>");
-			continue;
-		}
-		html("<tr><th>parent</th>"
-		     "<td colspan='2' class='oid'>");
-		tmp = tmp2 = oid_to_hex(&p->item->object.oid);
-		if (ctx.repo->enable_subject_links) {
-			parent_info = cgit_parse_commit(parent);
-			tmp2 = parent_info->subject;
-		}
-		cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix);
-		html(" (");
-		cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
-			       oid_to_hex(&p->item->object.oid), prefix);
-		html(")</td></tr>");
-		parents++;
-	}
-	if (ctx.repo->snapshots) {
-		html("<tr><th>download</th><td colspan='2' class='oid'>");
-		cgit_print_snapshot_links(ctx.repo, hex, "<br/>");
-		html("</td></tr>");
-	}
-	html("</table>\n");
-	html("<div class='commit-subject'>");
-	cgit_open_filter(ctx.repo->commit_filter);
-	html_txt(info->subject);
-	cgit_close_filter(ctx.repo->commit_filter);
-	show_commit_decorations(commit);
-	html("</div>");
-	html("<pre class='commit-msg'>");
-	cgit_open_filter(ctx.repo->commit_filter);
-	html_txt(info->msg);
-	cgit_close_filter(ctx.repo->commit_filter);
-	html("</pre>");
-	if (notes.len != 0) {
-		html("<div class='notes-header'>Notes</div>");
-		html("<div class='notes'>");
-		cgit_open_filter(ctx.repo->commit_filter);
-		html_txt(notes.buf);
-		cgit_close_filter(ctx.repo->commit_filter);
-		html("</div>");
-		html("<div class='notes-footer'></div>");
-	}
-	if (parents < 3) {
-		if (parents)
-			tmp = oid_to_hex(&commit->parents->item->object.oid);
-		else
-			tmp = NULL;
-		cgit_print_diff(ctx.qry.oid, tmp, prefix, 0, 0);
-	}
-	strbuf_release(&notes);
-	cgit_free_commitinfo(info);
-	cgit_print_layout_end();
-}
diff --git a/www/git.causal.agency/cgit/ui-commit.h b/www/git.causal.agency/cgit/ui-commit.h
deleted file mode 100644
index 8198b4ba..00000000
--- a/www/git.causal.agency/cgit/ui-commit.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef UI_COMMIT_H
-#define UI_COMMIT_H
-
-extern void cgit_print_commit(char *hex, const char *prefix);
-
-#endif /* UI_COMMIT_H */
diff --git a/www/git.causal.agency/cgit/ui-diff.c b/www/git.causal.agency/cgit/ui-diff.c
deleted file mode 100644
index 2a64ae8f..00000000
--- a/www/git.causal.agency/cgit/ui-diff.c
+++ /dev/null
@@ -1,505 +0,0 @@
-/* ui-diff.c: show diff between two blobs
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-diff.h"
-#include "html.h"
-#include "ui-shared.h"
-#include "ui-ssdiff.h"
-
-struct object_id old_rev_oid[1];
-struct object_id new_rev_oid[1];
-
-static int files, slots;
-static int total_adds, total_rems, max_changes;
-static int lines_added, lines_removed;
-
-static struct fileinfo {
-	char status;
-	struct object_id old_oid[1];
-	struct object_id new_oid[1];
-	unsigned short old_mode;
-	unsigned short new_mode;
-	char *old_path;
-	char *new_path;
-	unsigned int added;
-	unsigned int removed;
-	unsigned long old_size;
-	unsigned long new_size;
-	unsigned int binary:1;
-} *items;
-
-static int use_ssdiff = 0;
-static struct diff_filepair *current_filepair;
-static const char *current_prefix;
-
-struct diff_filespec *cgit_get_current_old_file(void)
-{
-	return current_filepair->one;
-}
-
-struct diff_filespec *cgit_get_current_new_file(void)
-{
-	return current_filepair->two;
-}
-
-static void print_fileinfo(struct fileinfo *info)
-{
-	char *class;
-
-	switch (info->status) {
-	case DIFF_STATUS_ADDED:
-		class = "add";
-		break;
-	case DIFF_STATUS_COPIED:
-		class = "cpy";
-		break;
-	case DIFF_STATUS_DELETED:
-		class = "del";
-		break;
-	case DIFF_STATUS_MODIFIED:
-		class = "upd";
-		break;
-	case DIFF_STATUS_RENAMED:
-		class = "mov";
-		break;
-	case DIFF_STATUS_TYPE_CHANGED:
-		class = "typ";
-		break;
-	case DIFF_STATUS_UNKNOWN:
-		class = "unk";
-		break;
-	case DIFF_STATUS_UNMERGED:
-		class = "stg";
-		break;
-	default:
-		die("bug: unhandled diff status %c", info->status);
-	}
-
-	html("<tr>");
-	html("<td class='mode'>");
-	if (is_null_oid(info->new_oid)) {
-		cgit_print_filemode(info->old_mode);
-	} else {
-		cgit_print_filemode(info->new_mode);
-	}
-
-	if (info->old_mode != info->new_mode &&
-	    !is_null_oid(info->old_oid) &&
-	    !is_null_oid(info->new_oid)) {
-		html("<span class='modechange'>[");
-		cgit_print_filemode(info->old_mode);
-		html("]</span>");
-	}
-	htmlf("</td><td class='%s'>", class);
-	cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
-		       ctx.qry.oid2, info->new_path);
-	if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) {
-		htmlf(" (%s from ",
-		      info->status == DIFF_STATUS_COPIED ? "copied" : "renamed");
-		html_txt(info->old_path);
-		html(")");
-	}
-	html("</td><td class='right'>");
-	if (info->binary) {
-		htmlf("bin</td><td class='graph'>%ld -> %ld bytes",
-		      info->old_size, info->new_size);
-		return;
-	}
-	htmlf("%d", info->added + info->removed);
-	html("</td><td class='graph'>");
-	htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
-	htmlf("<td class='add' style='width: %.1f%%;'/>",
-	      info->added * 100.0 / max_changes);
-	htmlf("<td class='rem' style='width: %.1f%%;'/>",
-	      info->removed * 100.0 / max_changes);
-	htmlf("<td class='none' style='width: %.1f%%;'/>",
-	      (max_changes - info->removed - info->added) * 100.0 / max_changes);
-	html("</tr></table></td></tr>\n");
-}
-
-static void count_diff_lines(char *line, int len)
-{
-	if (line && (len > 0)) {
-		if (line[0] == '+')
-			lines_added++;
-		else if (line[0] == '-')
-			lines_removed++;
-	}
-}
-
-static int show_filepair(struct diff_filepair *pair)
-{
-	/* Always show if we have no limiting prefix. */
-	if (!current_prefix)
-		return 1;
-
-	/* Show if either path in the pair begins with the prefix. */
-	if (starts_with(pair->one->path, current_prefix) ||
-	    starts_with(pair->two->path, current_prefix))
-		return 1;
-
-	/* Otherwise we don't want to show this filepair. */
-	return 0;
-}
-
-static void inspect_filepair(struct diff_filepair *pair)
-{
-	int binary = 0;
-	unsigned long old_size = 0;
-	unsigned long new_size = 0;
-
-	if (!show_filepair(pair))
-		return;
-
-	files++;
-	lines_added = 0;
-	lines_removed = 0;
-	cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, &new_size,
-			&binary, 0, ctx.qry.ignorews, count_diff_lines);
-	if (files >= slots) {
-		if (slots == 0)
-			slots = 4;
-		else
-			slots = slots * 2;
-		items = xrealloc(items, slots * sizeof(struct fileinfo));
-	}
-	items[files-1].status = pair->status;
-	oidcpy(items[files-1].old_oid, &pair->one->oid);
-	oidcpy(items[files-1].new_oid, &pair->two->oid);
-	items[files-1].old_mode = pair->one->mode;
-	items[files-1].new_mode = pair->two->mode;
-	items[files-1].old_path = xstrdup(pair->one->path);
-	items[files-1].new_path = xstrdup(pair->two->path);
-	items[files-1].added = lines_added;
-	items[files-1].removed = lines_removed;
-	items[files-1].old_size = old_size;
-	items[files-1].new_size = new_size;
-	items[files-1].binary = binary;
-	if (lines_added + lines_removed > max_changes)
-		max_changes = lines_added + lines_removed;
-	total_adds += lines_added;
-	total_rems += lines_removed;
-}
-
-static void cgit_print_diffstat(const struct object_id *old_oid,
-				const struct object_id *new_oid,
-				const char *prefix)
-{
-	int i;
-
-	html("<div class='diffstat-header'>");
-	cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.oid,
-		       ctx.qry.oid2, NULL);
-	if (prefix) {
-		html(" (limited to '");
-		html_txt(prefix);
-		html("')");
-	}
-	html("</div>");
-	html("<table summary='diffstat' class='diffstat'>");
-	max_changes = 0;
-	cgit_diff_tree(old_oid, new_oid, inspect_filepair, prefix,
-		       ctx.qry.ignorews);
-	for (i = 0; i<files; i++)
-		print_fileinfo(&items[i]);
-	html("</table>");
-	html("<div class='diffstat-summary'>");
-	htmlf("%d files changed, %d insertions, %d deletions",
-	      files, total_adds, total_rems);
-	html("</div>");
-}
-
-
-/*
- * print a single line returned from xdiff
- */
-static void print_line(char *line, int len)
-{
-	char *class = "ctx";
-	char c = line[len-1];
-
-	if (line[0] == '+')
-		class = "add";
-	else if (line[0] == '-')
-		class = "del";
-	else if (line[0] == '@')
-		class = "hunk";
-
-	htmlf("<span class='%s'>", class);
-	line[len-1] = '\0';
-	html_txt(line);
-	line[len-1] = c;
-	html("</span>\n");
-}
-
-static void header(const struct object_id *oid1, char *path1, int mode1,
-		   const struct object_id *oid2, char *path2, int mode2)
-{
-	char *abbrev1, *abbrev2;
-	int subproject;
-
-	subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
-	html("<span class='head'>");
-	html("diff --git a/");
-	html_txt(path1);
-	html(" b/");
-	html_txt(path2);
-	html("\n");
-
-	if (mode1 == 0)
-		htmlf("new file mode %.6o\n", mode2);
-
-	if (mode2 == 0)
-		htmlf("deleted file mode %.6o\n", mode1);
-
-	if (!subproject) {
-		abbrev1 = xstrdup(find_unique_abbrev(oid1, DEFAULT_ABBREV));
-		abbrev2 = xstrdup(find_unique_abbrev(oid2, DEFAULT_ABBREV));
-		htmlf("index %s..%s", abbrev1, abbrev2);
-		free(abbrev1);
-		free(abbrev2);
-		if (mode1 != 0 && mode2 != 0) {
-			htmlf(" %.6o", mode1);
-			if (mode2 != mode1)
-				htmlf("..%.6o", mode2);
-		}
-		html("\n");
-		if (is_null_oid(oid1)) {
-			path1 = "dev/null";
-			html("--- /");
-		} else
-			html("--- a/");
-		if (mode1 != 0)
-			cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
-				       oid_to_hex(old_rev_oid), path1);
-		else
-			html_txt(path1);
-		html("\n");
-		if (is_null_oid(oid2)) {
-			path2 = "dev/null";
-			html("+++ /");
-		} else
-			html("+++ b/");
-		if (mode2 != 0)
-			cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
-				       oid_to_hex(new_rev_oid), path2);
-		else
-			html_txt(path2);
-		html("\n");
-	}
-	html("</span>");
-}
-
-static void filepair_cb(struct diff_filepair *pair)
-{
-	unsigned long old_size = 0;
-	unsigned long new_size = 0;
-	int binary = 0;
-	linediff_fn print_line_fn = print_line;
-
-	if (!show_filepair(pair))
-		return;
-
-	current_filepair = pair;
-	if (use_ssdiff) {
-		cgit_ssdiff_header_begin();
-		print_line_fn = cgit_ssdiff_line_cb;
-	}
-	header(&pair->one->oid, pair->one->path, pair->one->mode,
-	       &pair->two->oid, pair->two->path, pair->two->mode);
-	if (use_ssdiff)
-		cgit_ssdiff_header_end();
-	if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
-		if (S_ISGITLINK(pair->one->mode))
-			print_line_fn(fmt("-Subproject %s", oid_to_hex(&pair->one->oid)), 52);
-		if (S_ISGITLINK(pair->two->mode))
-			print_line_fn(fmt("+Subproject %s", oid_to_hex(&pair->two->oid)), 52);
-		if (use_ssdiff)
-			cgit_ssdiff_footer();
-		return;
-	}
-	if (cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
-			    &new_size, &binary, ctx.qry.context,
-			    ctx.qry.ignorews, print_line_fn))
-		cgit_print_error("Error running diff");
-	if (binary) {
-		if (use_ssdiff)
-			html("<tr><td colspan='4'>Binary files differ</td></tr>");
-		else
-			html("Binary files differ");
-	}
-	if (use_ssdiff)
-		cgit_ssdiff_footer();
-}
-
-void cgit_print_diff_ctrls(void)
-{
-	int i, curr;
-
-	html("<div class='cgit-panel'>");
-	html("<b>diff options</b>");
-	html("<form method='get'>");
-	cgit_add_hidden_formfields(1, 0, ctx.qry.page);
-	html("<table>");
-	html("<tr><td colspan='2'/></tr>");
-	html("<tr>");
-	html("<td class='label'>context:</td>");
-	html("<td class='ctrl'>");
-	html("<select name='context' onchange='this.form.submit();'>");
-	curr = ctx.qry.context;
-	if (!curr)
-		curr = 3;
-	for (i = 1; i <= 10; i++)
-		html_intoption(i, fmt("%d", i), curr);
-	for (i = 15; i <= 40; i += 5)
-		html_intoption(i, fmt("%d", i), curr);
-	html("</select>");
-	html("</td>");
-	html("</tr><tr>");
-	html("<td class='label'>space:</td>");
-	html("<td class='ctrl'>");
-	html("<select name='ignorews' onchange='this.form.submit();'>");
-	html_intoption(0, "include", ctx.qry.ignorews);
-	html_intoption(1, "ignore", ctx.qry.ignorews);
-	html("</select>");
-	html("</td>");
-	html("</tr><tr>");
-	html("<td class='label'>mode:</td>");
-	html("<td class='ctrl'>");
-	html("<select name='dt' onchange='this.form.submit();'>");
-	curr = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
-	html_intoption(0, "unified", curr);
-	html_intoption(1, "ssdiff", curr);
-	html_intoption(2, "stat only", curr);
-	html("</select></td></tr>");
-	html("<tr><td/><td class='ctrl'>");
-	html("<noscript><input type='submit' value='reload'/></noscript>");
-	html("</td></tr></table>");
-	html("</form>");
-	html("</div>");
-}
-
-void cgit_print_diff(const char *new_rev, const char *old_rev,
-		     const char *prefix, int show_ctrls, int raw)
-{
-	struct commit *commit, *commit2;
-	const struct object_id *old_tree_oid, *new_tree_oid;
-	diff_type difftype;
-
-	/*
-	 * If "follow" is set then the diff machinery needs to examine the
-	 * entire commit to detect renames so we must limit the paths in our
-	 * own callbacks and not pass the prefix to the diff machinery.
-	 */
-	if (ctx.qry.follow && ctx.cfg.enable_follow_links) {
-		current_prefix = prefix;
-		prefix = "";
-	} else {
-		current_prefix = NULL;
-	}
-
-	if (!new_rev)
-		new_rev = ctx.qry.head;
-	if (get_oid(new_rev, new_rev_oid)) {
-		cgit_print_error_page(404, "Not found",
-			"Bad object name: %s", new_rev);
-		return;
-	}
-	commit = lookup_commit_reference(the_repository, new_rev_oid);
-	if (!commit || parse_commit(commit)) {
-		cgit_print_error_page(404, "Not found",
-			"Bad commit: %s", oid_to_hex(new_rev_oid));
-		return;
-	}
-	new_tree_oid = get_commit_tree_oid(commit);
-
-	if (old_rev) {
-		if (get_oid(old_rev, old_rev_oid)) {
-			cgit_print_error_page(404, "Not found",
-				"Bad object name: %s", old_rev);
-			return;
-		}
-	} else if (commit->parents && commit->parents->item) {
-		oidcpy(old_rev_oid, &commit->parents->item->object.oid);
-	} else {
-		oidclr(old_rev_oid);
-	}
-
-	if (!is_null_oid(old_rev_oid)) {
-		commit2 = lookup_commit_reference(the_repository, old_rev_oid);
-		if (!commit2 || parse_commit(commit2)) {
-			cgit_print_error_page(404, "Not found",
-				"Bad commit: %s", oid_to_hex(old_rev_oid));
-			return;
-		}
-		old_tree_oid = get_commit_tree_oid(commit2);
-	} else {
-		old_tree_oid = NULL;
-	}
-
-	if (raw) {
-		struct diff_options diffopt;
-
-		diff_setup(&diffopt);
-		diffopt.output_format = DIFF_FORMAT_PATCH;
-		diffopt.flags.recursive = 1;
-		diff_setup_done(&diffopt);
-
-		ctx.page.mimetype = "text/plain";
-		cgit_print_http_headers();
-		if (old_tree_oid) {
-			diff_tree_oid(old_tree_oid, new_tree_oid, "",
-				       &diffopt);
-		} else {
-			diff_root_tree_oid(new_tree_oid, "", &diffopt);
-		}
-		diffcore_std(&diffopt);
-		diff_flush(&diffopt);
-
-		return;
-	}
-
-	difftype = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
-	use_ssdiff = difftype == DIFF_SSDIFF;
-
-	if (show_ctrls) {
-		cgit_print_layout_start();
-		cgit_print_diff_ctrls();
-	}
-
-	/*
-	 * Clicking on a link to a file in the diff stat should show a diff
-	 * of the file, showing the diff stat limited to a single file is
-	 * pretty useless.  All links from this point on will be to
-	 * individual files, so we simply reset the difftype in the query
-	 * here to avoid propagating DIFF_STATONLY to the individual files.
-	 */
-	if (difftype == DIFF_STATONLY)
-		ctx.qry.difftype = ctx.cfg.difftype;
-
-	cgit_print_diffstat(old_rev_oid, new_rev_oid, prefix);
-
-	if (difftype == DIFF_STATONLY)
-		return;
-
-	if (use_ssdiff) {
-		html("<table summary='ssdiff' class='ssdiff'>");
-	} else {
-		html("<table summary='diff' class='diff'>");
-		html("<tr><td><pre>");
-	}
-	cgit_diff_tree(old_rev_oid, new_rev_oid, filepair_cb, prefix,
-		       ctx.qry.ignorews);
-	if (!use_ssdiff)
-		html("</pre></td></tr>");
-	html("</table>");
-
-	if (show_ctrls)
-		cgit_print_layout_end();
-}
diff --git a/www/git.causal.agency/cgit/ui-diff.h b/www/git.causal.agency/cgit/ui-diff.h
deleted file mode 100644
index 39264a16..00000000
--- a/www/git.causal.agency/cgit/ui-diff.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef UI_DIFF_H
-#define UI_DIFF_H
-
-extern void cgit_print_diff_ctrls(void);
-
-extern void cgit_print_diff(const char *new_hex, const char *old_hex,
-			    const char *prefix, int show_ctrls, int raw);
-
-extern struct diff_filespec *cgit_get_current_old_file(void);
-extern struct diff_filespec *cgit_get_current_new_file(void);
-
-extern struct object_id old_rev_oid[1];
-extern struct object_id new_rev_oid[1];
-
-#endif /* UI_DIFF_H */
diff --git a/www/git.causal.agency/cgit/ui-log.c b/www/git.causal.agency/cgit/ui-log.c
deleted file mode 100644
index b443ca73..00000000
--- a/www/git.causal.agency/cgit/ui-log.c
+++ /dev/null
@@ -1,555 +0,0 @@
-/* ui-log.c: functions for log output
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-log.h"
-#include "html.h"
-#include "ui-shared.h"
-#include "strvec.h"
-
-static int files, add_lines, rem_lines, lines_counted;
-
-/*
- * The list of available column colors in the commit graph.
- */
-static const char *column_colors_html[] = {
-	"<span class='column1'>",
-	"<span class='column2'>",
-	"<span class='column3'>",
-	"<span class='column4'>",
-	"<span class='column5'>",
-	"<span class='column6'>",
-	"</span>",
-};
-
-#define COLUMN_COLORS_HTML_MAX (ARRAY_SIZE(column_colors_html) - 1)
-
-static void count_lines(char *line, int size)
-{
-	if (size <= 0)
-		return;
-
-	if (line[0] == '+')
-		add_lines++;
-
-	else if (line[0] == '-')
-		rem_lines++;
-}
-
-static void inspect_files(struct diff_filepair *pair)
-{
-	unsigned long old_size = 0;
-	unsigned long new_size = 0;
-	int binary = 0;
-
-	files++;
-	if (ctx.repo->enable_log_linecount)
-		cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
-				&new_size, &binary, 0, ctx.qry.ignorews,
-				count_lines);
-}
-
-void show_commit_decorations(struct commit *commit)
-{
-	const struct name_decoration *deco;
-	static char buf[1024];
-
-	buf[sizeof(buf) - 1] = 0;
-	deco = get_name_decoration(&commit->object);
-	if (!deco)
-		return;
-	html("<span class='decoration'>");
-	while (deco) {
-		struct object_id oid_tag, peeled;
-		int is_annotated = 0;
-
-		strlcpy(buf, prettify_refname(deco->name), sizeof(buf));
-		switch(deco->type) {
-		case DECORATION_NONE:
-			/* If the git-core doesn't recognize it,
-			 * don't display anything. */
-			break;
-		case DECORATION_REF_LOCAL:
-			html(" ");
-			cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
-				ctx.qry.vpath, 0, NULL, NULL,
-				ctx.qry.showmsg, 0);
-			break;
-		case DECORATION_REF_TAG:
-			html(" ");
-			if (!read_ref(deco->name, &oid_tag) && !peel_iterated_oid(&oid_tag, &peeled))
-				is_annotated = !oideq(&oid_tag, &peeled);
-			cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf);
-			break;
-		case DECORATION_REF_REMOTE:
-			if (!ctx.repo->enable_remote_branches)
-				break;
-			html(" ");
-			cgit_log_link(buf, NULL, "remote-deco", NULL,
-				oid_to_hex(&commit->object.oid),
-				ctx.qry.vpath, 0, NULL, NULL,
-				ctx.qry.showmsg, 0);
-			break;
-		default:
-			html(" ");
-			cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
-					oid_to_hex(&commit->object.oid),
-					ctx.qry.vpath);
-			break;
-		}
-		deco = deco->next;
-	}
-	html("</span>");
-}
-
-static void handle_rename(struct diff_filepair *pair)
-{
-	/*
-	 * After we have seen a rename, we generate links to the previous
-	 * name of the file so that commit & diff views get fed the path
-	 * that is correct for the commit they are showing, avoiding the
-	 * need to walk the entire history leading back to every commit we
-	 * show in order detect renames.
-	 */
-	if (0 != strcmp(ctx.qry.vpath, pair->two->path)) {
-		free(ctx.qry.vpath);
-		ctx.qry.vpath = xstrdup(pair->two->path);
-	}
-	inspect_files(pair);
-}
-
-static int show_commit(struct commit *commit, struct rev_info *revs)
-{
-	struct commit_list *parents = commit->parents;
-	struct commit *parent;
-	int found = 0, saved_fmt;
-	struct diff_flags saved_flags = revs->diffopt.flags;
-
-	/* Always show if we're not in "follow" mode with a single file. */
-	if (!ctx.qry.follow)
-		return 1;
-
-	/*
-	 * In "follow" mode, we don't show merges.  This is consistent with
-	 * "git log --follow -- <file>".
-	 */
-	if (parents && parents->next)
-		return 0;
-
-	/*
-	 * If this is the root commit, do what rev_info tells us.
-	 */
-	if (!parents)
-		return revs->show_root_diff;
-
-	/* When we get here we have precisely one parent. */
-	parent = parents->item;
-	/* If we can't parse the commit, let print_commit() report an error. */
-	if (parse_commit(parent))
-		return 1;
-
-	files = 0;
-	add_lines = 0;
-	rem_lines = 0;
-
-	revs->diffopt.flags.recursive = 1;
-	diff_tree_oid(get_commit_tree_oid(parent),
-		      get_commit_tree_oid(commit),
-		      "", &revs->diffopt);
-	diffcore_std(&revs->diffopt);
-
-	found = !diff_queue_is_empty();
-	saved_fmt = revs->diffopt.output_format;
-	revs->diffopt.output_format = DIFF_FORMAT_CALLBACK;
-	revs->diffopt.format_callback = cgit_diff_tree_cb;
-	revs->diffopt.format_callback_data = handle_rename;
-	diff_flush(&revs->diffopt);
-	revs->diffopt.output_format = saved_fmt;
-	revs->diffopt.flags = saved_flags;
-
-	lines_counted = 1;
-	return found;
-}
-
-static void print_commit(struct commit *commit, struct rev_info *revs)
-{
-	struct commitinfo *info;
-	int columns = revs->graph ? 4 : 3;
-	struct strbuf graphbuf = STRBUF_INIT;
-	struct strbuf msgbuf = STRBUF_INIT;
-
-	if (ctx.repo->enable_log_filecount)
-		columns++;
-	if (ctx.repo->enable_log_linecount)
-		columns++;
-
-	if (revs->graph) {
-		/* Advance graph until current commit */
-		while (!graph_next_line(revs->graph, &graphbuf)) {
-			/* Print graph segment in otherwise empty table row */
-			html("<tr class='nohover'><td class='commitgraph'>");
-			html(graphbuf.buf);
-			htmlf("</td><td colspan='%d' /></tr>\n", columns);
-			strbuf_setlen(&graphbuf, 0);
-		}
-		/* Current commit's graph segment is now ready in graphbuf */
-	}
-
-	info = cgit_parse_commit(commit);
-	htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : "");
-
-	if (revs->graph) {
-		/* Print graph segment for current commit */
-		html("<td class='commitgraph'>");
-		html(graphbuf.buf);
-		html("</td>");
-		strbuf_setlen(&graphbuf, 0);
-	}
-	else {
-		html("<td>");
-		cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
-		html("</td>");
-	}
-
-	htmlf("<td%s>", ctx.qry.showmsg ? " class='logsubject'" : "");
-	if (ctx.qry.showmsg) {
-		/* line-wrap long commit subjects instead of truncating them */
-		size_t subject_len = strlen(info->subject);
-
-		if (subject_len > ctx.cfg.max_msg_len &&
-		    ctx.cfg.max_msg_len >= 15) {
-			/* symbol for signaling line-wrap (in PAGE_ENCODING) */
-			const char wrap_symbol[] = { ' ', 0xE2, 0x86, 0xB5, 0 };
-			int i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
-
-			/* Rewind i to preceding space character */
-			while (i > 0 && !isspace(info->subject[i]))
-				--i;
-			if (!i) /* Oops, zero spaces. Reset i */
-				i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
-
-			/* add remainder starting at i to msgbuf */
-			strbuf_add(&msgbuf, info->subject + i, subject_len - i);
-			strbuf_trim(&msgbuf);
-			strbuf_add(&msgbuf, "\n\n", 2);
-
-			/* Place wrap_symbol at position i in info->subject */
-			strlcpy(info->subject + i, wrap_symbol, subject_len - i + 1);
-		}
-	}
-	cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
-			 oid_to_hex(&commit->object.oid), ctx.qry.vpath);
-	show_commit_decorations(commit);
-	html("</td><td>");
-	cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
-	html_txt(info->author);
-	cgit_close_filter(ctx.repo->email_filter);
-
-	if (revs->graph) {
-		html("</td><td>");
-		cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
-	}
-
-	if (!lines_counted && (ctx.repo->enable_log_filecount ||
-			       ctx.repo->enable_log_linecount)) {
-		files = 0;
-		add_lines = 0;
-		rem_lines = 0;
-		cgit_diff_commit(commit, inspect_files, ctx.qry.vpath);
-	}
-
-	if (ctx.repo->enable_log_filecount)
-		htmlf("</td><td>%d", files);
-	if (ctx.repo->enable_log_linecount)
-		htmlf("</td><td><span class='deletions'>-%d</span>/"
-			"<span class='insertions'>+%d</span>", rem_lines, add_lines);
-
-	html("</td></tr>\n");
-
-	if ((revs->graph && !graph_is_commit_finished(revs->graph))
-			|| ctx.qry.showmsg) { /* Print a second table row */
-		html("<tr class='nohover-highlight'>");
-
-		if (ctx.qry.showmsg) {
-			/* Concatenate commit message + notes in msgbuf */
-			if (info->msg && *(info->msg)) {
-				strbuf_addstr(&msgbuf, info->msg);
-				strbuf_addch(&msgbuf, '\n');
-			}
-			format_display_notes(&commit->object.oid,
-					     &msgbuf, PAGE_ENCODING, 0);
-			strbuf_addch(&msgbuf, '\n');
-			strbuf_ltrim(&msgbuf);
-		}
-
-		if (revs->graph) {
-			int lines = 0;
-
-			/* Calculate graph padding */
-			if (ctx.qry.showmsg) {
-				/* Count #lines in commit message + notes */
-				const char *p = msgbuf.buf;
-				lines = 1;
-				while ((p = strchr(p, '\n'))) {
-					p++;
-					lines++;
-				}
-			}
-
-			/* Print graph padding */
-			html("<td class='commitgraph'>");
-			while (lines > 0 || !graph_is_commit_finished(revs->graph)) {
-				if (graphbuf.len)
-					html("\n");
-				strbuf_setlen(&graphbuf, 0);
-				graph_next_line(revs->graph, &graphbuf);
-				html(graphbuf.buf);
-				lines--;
-			}
-			html("</td>\n");
-		}
-		else
-			html("<td/>"); /* Empty 'Age' column */
-
-		/* Print msgbuf into remainder of table row */
-		htmlf("<td colspan='%d'%s>\n", columns - (revs->graph ? 1 : 0),
-			ctx.qry.showmsg ? " class='logmsg'" : "");
-		html_txt(msgbuf.buf);
-		html("</td></tr>\n");
-	}
-
-	strbuf_release(&msgbuf);
-	strbuf_release(&graphbuf);
-	cgit_free_commitinfo(info);
-}
-
-static const char *disambiguate_ref(const char *ref, int *must_free_result)
-{
-	struct object_id oid;
-	struct strbuf longref = STRBUF_INIT;
-
-	strbuf_addf(&longref, "refs/heads/%s", ref);
-	if (get_oid(longref.buf, &oid) == 0) {
-		*must_free_result = 1;
-		return strbuf_detach(&longref, NULL);
-	}
-
-	*must_free_result = 0;
-	strbuf_release(&longref);
-	return ref;
-}
-
-static char *next_token(char **src)
-{
-	char *result;
-
-	if (!src || !*src)
-		return NULL;
-	while (isspace(**src))
-		(*src)++;
-	if (!**src)
-		return NULL;
-	result = *src;
-	while (**src) {
-		if (isspace(**src)) {
-			**src = '\0';
-			(*src)++;
-			break;
-		}
-		(*src)++;
-	}
-	return result;
-}
-
-void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
-		    const char *path, int pager, int commit_graph, int commit_sort)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strvec rev_argv = STRVEC_INIT;
-	int i, columns = commit_graph ? 4 : 3;
-	int must_free_tip = 0;
-
-	/* rev_argv.argv[0] will be ignored by setup_revisions */
-	strvec_push(&rev_argv, "log_rev_setup");
-
-	if (!tip)
-		tip = ctx.qry.head;
-	tip = disambiguate_ref(tip, &must_free_tip);
-	strvec_push(&rev_argv, tip);
-
-	if (grep && pattern && *pattern) {
-		pattern = xstrdup(pattern);
-		if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
-		    !strcmp(grep, "committer")) {
-			strvec_pushf(&rev_argv, "--%s=%s", grep, pattern);
-		} else if (!strcmp(grep, "range")) {
-			char *arg;
-			/* Split the pattern at whitespace and add each token
-			 * as a revision expression. Do not accept other
-			 * rev-list options. Also, replace the previously
-			 * pushed tip (it's no longer relevant).
-			 */
-			strvec_pop(&rev_argv);
-			while ((arg = next_token(&pattern))) {
-				if (*arg == '-') {
-					fprintf(stderr, "Bad range expr: %s\n",
-						arg);
-					break;
-				}
-				strvec_push(&rev_argv, arg);
-			}
-		}
-	}
-
-	if (!path || !ctx.cfg.enable_follow_links) {
-		/*
-		 * If we don't have a path, "follow" is a no-op so make sure
-		 * the variable is set to false to avoid needing to check
-		 * both this and whether we have a path everywhere.
-		 */
-		ctx.qry.follow = 0;
-	}
-
-	if (commit_graph && !ctx.qry.follow) {
-		strvec_push(&rev_argv, "--graph");
-		strvec_push(&rev_argv, "--color");
-		graph_set_column_colors(column_colors_html,
-					COLUMN_COLORS_HTML_MAX);
-	}
-
-	if (commit_sort == 1)
-		strvec_push(&rev_argv, "--date-order");
-	else if (commit_sort == 2)
-		strvec_push(&rev_argv, "--topo-order");
-
-	if (path && ctx.qry.follow)
-		strvec_push(&rev_argv, "--follow");
-	strvec_push(&rev_argv, "--");
-	if (path)
-		strvec_push(&rev_argv, path);
-
-	init_revisions(&rev, NULL);
-	rev.abbrev = DEFAULT_ABBREV;
-	rev.commit_format = CMIT_FMT_DEFAULT;
-	rev.verbose_header = 1;
-	rev.show_root_diff = 0;
-	rev.ignore_missing = 1;
-	rev.simplify_history = 1;
-	setup_revisions(rev_argv.nr, rev_argv.v, &rev, NULL);
-	load_ref_decorations(NULL, DECORATE_FULL_REFS);
-	rev.show_decorations = 1;
-	rev.grep_filter.ignore_case = 1;
-
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.rename_limit = ctx.cfg.renamelimit;
-	if (ctx.qry.ignorews)
-		DIFF_XDL_SET(&rev.diffopt, IGNORE_WHITESPACE);
-
-	compile_grep_patterns(&rev.grep_filter);
-	prepare_revision_walk(&rev);
-
-	if (pager) {
-		cgit_print_layout_start();
-		html("<table class='list nowrap'>");
-	}
-
-	html("<tr class='nohover'>");
-	if (commit_graph)
-		html("<th></th>");
-	else
-		html("<th class='left'>Age</th>");
-	html("<th class='left'>Commit message");
-	if (pager) {
-		html(" (");
-		cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
-			      NULL, ctx.qry.head, ctx.qry.oid,
-			      ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep,
-			      ctx.qry.search, ctx.qry.showmsg ? 0 : 1,
-			      ctx.qry.follow);
-		html(")");
-	}
-	html("</th><th class='left'>Author</th>");
-	if (rev.graph)
-		html("<th class='left'>Age</th>");
-	if (ctx.repo->enable_log_filecount) {
-		html("<th class='left'>Files</th>");
-		columns++;
-	}
-	if (ctx.repo->enable_log_linecount) {
-		html("<th class='left'>Lines</th>");
-		columns++;
-	}
-	html("</tr>\n");
-
-	if (ofs<0)
-		ofs = 0;
-
-	for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) {
-		if (show_commit(commit, &rev))
-			i++;
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
-
-	for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; /* nop */) {
-		/*
-		 * In "follow" mode, we must count the files and lines the
-		 * first time we invoke diff on a given commit, and we need
-		 * to do that to see if the commit touches the path we care
-		 * about, so we do it in show_commit.  Hence we must clear
-		 * lines_counted here.
-		 *
-		 * This has the side effect of avoiding running diff twice
-		 * when we are both following renames and showing file
-		 * and/or line counts.
-		 */
-		lines_counted = 0;
-		if (show_commit(commit, &rev)) {
-			i++;
-			print_commit(commit, &rev);
-		}
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
-	if (pager) {
-		html("</table><ul class='pager'>");
-		if (ofs > 0) {
-			html("<li>");
-			cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
-				      ctx.qry.oid, ctx.qry.vpath,
-				      ofs - cnt, ctx.qry.grep,
-				      ctx.qry.search, ctx.qry.showmsg,
-				      ctx.qry.follow);
-			html("</li>");
-		}
-		if ((commit = get_revision(&rev)) != NULL) {
-			html("<li>");
-			cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
-				      ctx.qry.oid, ctx.qry.vpath,
-				      ofs + cnt, ctx.qry.grep,
-				      ctx.qry.search, ctx.qry.showmsg,
-				      ctx.qry.follow);
-			html("</li>");
-		}
-		html("</ul>");
-		cgit_print_layout_end();
-	} else if ((commit = get_revision(&rev)) != NULL) {
-		htmlf("<tr class='nohover'><td colspan='%d'>", columns);
-		cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL,
-			      ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg,
-			      ctx.qry.follow);
-		html("</td></tr>\n");
-	}
-
-	/* If we allocated tip then it is safe to cast away const. */
-	if (must_free_tip)
-		free((char*) tip);
-}
diff --git a/www/git.causal.agency/cgit/ui-log.h b/www/git.causal.agency/cgit/ui-log.h
deleted file mode 100644
index 325607cd..00000000
--- a/www/git.causal.agency/cgit/ui-log.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef UI_LOG_H
-#define UI_LOG_H
-
-extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
-			   char *pattern, const char *path, int pager,
-			   int commit_graph, int commit_sort);
-extern void show_commit_decorations(struct commit *commit);
-
-#endif /* UI_LOG_H */
diff --git a/www/git.causal.agency/cgit/ui-patch.c b/www/git.causal.agency/cgit/ui-patch.c
deleted file mode 100644
index 4ac03cbe..00000000
--- a/www/git.causal.agency/cgit/ui-patch.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* ui-patch.c: generate patch view
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-patch.h"
-#include "html.h"
-#include "ui-shared.h"
-
-/* two commit hashes with two dots in between and termination */
-#define REV_RANGE_LEN 2 * GIT_MAX_HEXSZ + 3
-
-void cgit_print_patch(const char *new_rev, const char *old_rev,
-		      const char *prefix)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct object_id new_rev_oid, old_rev_oid;
-	char rev_range[REV_RANGE_LEN];
-	const char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range, "--", prefix, NULL };
-	int rev_argc = ARRAY_SIZE(rev_argv) - 1;
-	char *patchname;
-
-	if (!prefix)
-		rev_argc--;
-
-	if (!new_rev)
-		new_rev = ctx.qry.head;
-
-	if (get_oid(new_rev, &new_rev_oid)) {
-		cgit_print_error_page(404, "Not found",
-				"Bad object id: %s", new_rev);
-		return;
-	}
-	commit = lookup_commit_reference(the_repository, &new_rev_oid);
-	if (!commit) {
-		cgit_print_error_page(404, "Not found",
-				"Bad commit reference: %s", new_rev);
-		return;
-	}
-
-	if (old_rev) {
-		if (get_oid(old_rev, &old_rev_oid)) {
-			cgit_print_error_page(404, "Not found",
-					"Bad object id: %s", old_rev);
-			return;
-		}
-		if (!lookup_commit_reference(the_repository, &old_rev_oid)) {
-			cgit_print_error_page(404, "Not found",
-					"Bad commit reference: %s", old_rev);
-			return;
-		}
-	} else if (commit->parents && commit->parents->item) {
-		oidcpy(&old_rev_oid, &commit->parents->item->object.oid);
-	} else {
-		oidclr(&old_rev_oid);
-	}
-
-	if (is_null_oid(&old_rev_oid)) {
-		memcpy(rev_range, oid_to_hex(&new_rev_oid), the_hash_algo->hexsz + 1);
-	} else {
-		xsnprintf(rev_range, REV_RANGE_LEN, "%s..%s", oid_to_hex(&old_rev_oid),
-			oid_to_hex(&new_rev_oid));
-	}
-
-	patchname = fmt("%s.patch", rev_range);
-	ctx.page.mimetype = "text/plain";
-	ctx.page.filename = patchname;
-	cgit_print_http_headers();
-
-	if (ctx.cfg.noplainemail) {
-		rev_argv[2] = "--format=format:From %H Mon Sep 17 00:00:00 "
-			      "2001%nFrom: %an%nDate: %aD%n%w(78,0,1)Subject: "
-			      "%s%n%n%w(0)%b";
-	}
-
-	init_revisions(&rev, NULL);
-	rev.abbrev = DEFAULT_ABBREV;
-	rev.verbose_header = 1;
-	rev.diff = 1;
-	rev.show_root_diff = 1;
-	rev.max_parents = 1;
-	rev.diffopt.output_format |= DIFF_FORMAT_DIFFSTAT |
-			DIFF_FORMAT_PATCH | DIFF_FORMAT_SUMMARY;
-	if (prefix)
-		rev.diffopt.stat_sep = fmt("(limited to '%s')\n\n", prefix);
-	setup_revisions(rev_argc, rev_argv, &rev, NULL);
-	prepare_revision_walk(&rev);
-
-	while ((commit = get_revision(&rev)) != NULL) {
-		log_tree_commit(&rev, commit);
-		printf("-- \ncgit %s\n\n", cgit_version);
-	}
-}
diff --git a/www/git.causal.agency/cgit/ui-patch.h b/www/git.causal.agency/cgit/ui-patch.h
deleted file mode 100644
index 7a6cacd5..00000000
--- a/www/git.causal.agency/cgit/ui-patch.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef UI_PATCH_H
-#define UI_PATCH_H
-
-extern void cgit_print_patch(const char *new_rev, const char *old_rev,
-			     const char *prefix);
-
-#endif /* UI_PATCH_H */
diff --git a/www/git.causal.agency/cgit/ui-plain.c b/www/git.causal.agency/cgit/ui-plain.c
deleted file mode 100644
index 65a205fa..00000000
--- a/www/git.causal.agency/cgit/ui-plain.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/* ui-plain.c: functions for output of plain blobs by path
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-plain.h"
-#include "html.h"
-#include "ui-shared.h"
-
-struct walk_tree_context {
-	int match_baselen;
-	int match;
-};
-
-static int print_object(const struct object_id *oid, const char *path)
-{
-	enum object_type type;
-	char *buf, *mimetype;
-	unsigned long size;
-
-	type = oid_object_info(the_repository, oid, &size);
-	if (type == OBJ_BAD) {
-		cgit_print_error_page(404, "Not found", "Not found");
-		return 0;
-	}
-
-	buf = read_object_file(oid, &type, &size);
-	if (!buf) {
-		cgit_print_error_page(404, "Not found", "Not found");
-		return 0;
-	}
-
-	mimetype = get_mimetype_for_filename(path);
-	ctx.page.mimetype = mimetype;
-
-	if (!ctx.repo->enable_html_serving) {
-		html("X-Content-Type-Options: nosniff\n");
-		html("Content-Security-Policy: default-src 'none'\n");
-		if (mimetype) {
-			/* Built-in white list allows PDF and everything that isn't text/ and application/ */
-			if ((!strncmp(mimetype, "text/", 5) || !strncmp(mimetype, "application/", 12)) && strcmp(mimetype, "application/pdf"))
-				ctx.page.mimetype = NULL;
-		}
-	}
-
-	if (!ctx.page.mimetype) {
-		if (buffer_is_binary(buf, size)) {
-			ctx.page.mimetype = "application/octet-stream";
-			ctx.page.charset = NULL;
-		} else {
-			ctx.page.mimetype = "text/plain";
-		}
-	}
-	ctx.page.filename = path;
-	ctx.page.size = size;
-	ctx.page.etag = oid_to_hex(oid);
-	cgit_print_http_headers();
-	html_raw(buf, size);
-	free(mimetype);
-	free(buf);
-	return 1;
-}
-
-static char *buildpath(const char *base, int baselen, const char *path)
-{
-	if (path[0])
-		return fmtalloc("%.*s%s/", baselen, base, path);
-	else
-		return fmtalloc("%.*s/", baselen, base);
-}
-
-static void print_dir(const struct object_id *oid, const char *base,
-		      int baselen, const char *path)
-{
-	char *fullpath, *slash;
-	size_t len;
-
-	fullpath = buildpath(base, baselen, path);
-	slash = (fullpath[0] == '/' ? "" : "/");
-	ctx.page.etag = oid_to_hex(oid);
-	cgit_print_http_headers();
-	htmlf("<html><head><title>%s", slash);
-	html_txt(fullpath);
-	htmlf("</title></head>\n<body>\n<h2>%s", slash);
-	html_txt(fullpath);
-	html("</h2>\n<ul>\n");
-	len = strlen(fullpath);
-	if (len > 1) {
-		fullpath[len - 1] = 0;
-		slash = strrchr(fullpath, '/');
-		if (slash)
-			*(slash + 1) = 0;
-		else {
-			free(fullpath);
-			fullpath = NULL;
-		}
-		html("<li>");
-		cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.oid,
-				fullpath);
-		html("</li>\n");
-	}
-	free(fullpath);
-}
-
-static void print_dir_entry(const struct object_id *oid, const char *base,
-			    int baselen, const char *path, unsigned mode)
-{
-	char *fullpath;
-
-	fullpath = buildpath(base, baselen, path);
-	if (!S_ISDIR(mode) && !S_ISGITLINK(mode))
-		fullpath[strlen(fullpath) - 1] = 0;
-	html("  <li>");
-	if (S_ISGITLINK(mode)) {
-		cgit_submodule_link(NULL, fullpath, oid_to_hex(oid));
-	} else
-		cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
-				fullpath);
-	html("</li>\n");
-	free(fullpath);
-}
-
-static void print_dir_tail(void)
-{
-	html(" </ul>\n</body></html>\n");
-}
-
-static int walk_tree(const struct object_id *oid, struct strbuf *base,
-		const char *pathname, unsigned mode, void *cbdata)
-{
-	struct walk_tree_context *walk_tree_ctx = cbdata;
-
-	if (base->len == walk_tree_ctx->match_baselen) {
-		if (S_ISREG(mode) || S_ISLNK(mode)) {
-			if (print_object(oid, pathname))
-				walk_tree_ctx->match = 1;
-		} else if (S_ISDIR(mode)) {
-			print_dir(oid, base->buf, base->len, pathname);
-			walk_tree_ctx->match = 2;
-			return READ_TREE_RECURSIVE;
-		}
-	} else if (base->len < INT_MAX && (int)base->len > walk_tree_ctx->match_baselen) {
-		print_dir_entry(oid, base->buf, base->len, pathname, mode);
-		walk_tree_ctx->match = 2;
-	} else if (S_ISDIR(mode)) {
-		return READ_TREE_RECURSIVE;
-	}
-
-	return 0;
-}
-
-static int basedir_len(const char *path)
-{
-	char *p = strrchr(path, '/');
-	if (p)
-		return p - path + 1;
-	return 0;
-}
-
-void cgit_print_plain(void)
-{
-	const char *rev = ctx.qry.oid;
-	struct object_id oid;
-	struct commit *commit;
-	struct pathspec_item path_items = {
-		.match = ctx.qry.path,
-		.len = ctx.qry.path ? strlen(ctx.qry.path) : 0
-	};
-	struct pathspec paths = {
-		.nr = 1,
-		.items = &path_items
-	};
-	struct walk_tree_context walk_tree_ctx = {
-		.match = 0
-	};
-
-	if (!rev)
-		rev = ctx.qry.head;
-
-	if (get_oid(rev, &oid)) {
-		cgit_print_error_page(404, "Not found", "Not found");
-		return;
-	}
-	commit = lookup_commit_reference(the_repository, &oid);
-	if (!commit || parse_commit(commit)) {
-		cgit_print_error_page(404, "Not found", "Not found");
-		return;
-	}
-	if (!path_items.match) {
-		path_items.match = "";
-		walk_tree_ctx.match_baselen = -1;
-		print_dir(get_commit_tree_oid(commit), "", 0, "");
-		walk_tree_ctx.match = 2;
-	}
-	else
-		walk_tree_ctx.match_baselen = basedir_len(path_items.match);
-	read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
-		  &paths, walk_tree, &walk_tree_ctx);
-	if (!walk_tree_ctx.match)
-		cgit_print_error_page(404, "Not found", "Not found");
-	else if (walk_tree_ctx.match == 2)
-		print_dir_tail();
-}
diff --git a/www/git.causal.agency/cgit/ui-plain.h b/www/git.causal.agency/cgit/ui-plain.h
deleted file mode 100644
index 5bff07b8..00000000
--- a/www/git.causal.agency/cgit/ui-plain.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef UI_PLAIN_H
-#define UI_PLAIN_H
-
-extern void cgit_print_plain(void);
-
-#endif /* UI_PLAIN_H */
diff --git a/www/git.causal.agency/cgit/ui-refs.c b/www/git.causal.agency/cgit/ui-refs.c
deleted file mode 100644
index 456f610d..00000000
--- a/www/git.causal.agency/cgit/ui-refs.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/* ui-refs.c: browse symbolic refs
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-refs.h"
-#include "html.h"
-#include "ui-shared.h"
-
-static inline int cmp_age(int age1, int age2)
-{
-	/* age1 and age2 are assumed to be non-negative */
-	return age2 - age1;
-}
-
-static int cmp_ref_name(const void *a, const void *b)
-{
-	struct refinfo *r1 = *(struct refinfo **)a;
-	struct refinfo *r2 = *(struct refinfo **)b;
-
-	return strcmp(r1->refname, r2->refname);
-}
-
-static int cmp_branch_age(const void *a, const void *b)
-{
-	struct refinfo *r1 = *(struct refinfo **)a;
-	struct refinfo *r2 = *(struct refinfo **)b;
-
-	return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
-}
-
-static int get_ref_age(struct refinfo *ref)
-{
-	if (!ref->object)
-		return 0;
-	switch (ref->object->type) {
-	case OBJ_TAG:
-		return ref->tag ? ref->tag->tagger_date : 0;
-	case OBJ_COMMIT:
-		return ref->commit ? ref->commit->committer_date : 0;
-	}
-	return 0;
-}
-
-static int cmp_tag_age(const void *a, const void *b)
-{
-	struct refinfo *r1 = *(struct refinfo **)a;
-	struct refinfo *r2 = *(struct refinfo **)b;
-
-	return cmp_age(get_ref_age(r1), get_ref_age(r2));
-}
-
-static int print_branch(struct refinfo *ref)
-{
-	struct commitinfo *info = ref->commit;
-	char *name = (char *)ref->refname;
-
-	if (!info)
-		return 1;
-	html("<tr><td>");
-	cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL,
-		      ctx.qry.showmsg, 0);
-	html("</td><td>");
-
-	if (ref->object->type == OBJ_COMMIT) {
-		cgit_commit_link(info->subject, NULL, NULL, name, NULL, NULL);
-		html("</td><td>");
-		cgit_open_filter(ctx.repo->email_filter, info->author_email, "refs");
-		html_txt(info->author);
-		cgit_close_filter(ctx.repo->email_filter);
-		html("</td><td colspan='2'>");
-		cgit_print_age(info->committer_date, info->committer_tz, -1);
-	} else {
-		html("</td><td></td><td>");
-		cgit_object_link(ref->object);
-	}
-	html("</td></tr>\n");
-	return 0;
-}
-
-static void print_tag_header(void)
-{
-	html("<tr class='nohover'><th class='left'>Tag</th>"
-	     "<th class='left'>Download</th>"
-	     "<th class='left'>Author</th>"
-	     "<th class='left' colspan='2'>Age</th></tr>\n");
-}
-
-static int print_tag(struct refinfo *ref)
-{
-	struct tag *tag = NULL;
-	struct taginfo *info = NULL;
-	char *name = (char *)ref->refname;
-	struct object *obj = ref->object;
-
-	if (obj->type == OBJ_TAG) {
-		tag = (struct tag *)obj;
-		obj = tag->tagged;
-		info = ref->tag;
-		if (!info)
-			return 1;
-	}
-
-	html("<tr><td>");
-	cgit_tag_link(name, NULL, NULL, name);
-	html("</td><td>");
-	if (ctx.repo->snapshots && (obj->type == OBJ_COMMIT))
-		cgit_print_snapshot_links(ctx.repo, name, "&nbsp;&nbsp;");
-	else
-		cgit_object_link(obj);
-	html("</td><td>");
-	if (info) {
-		if (info->tagger) {
-			cgit_open_filter(ctx.repo->email_filter, info->tagger_email, "refs");
-			html_txt(info->tagger);
-			cgit_close_filter(ctx.repo->email_filter);
-		}
-	} else if (ref->object->type == OBJ_COMMIT) {
-		cgit_open_filter(ctx.repo->email_filter, ref->commit->author_email, "refs");
-		html_txt(ref->commit->author);
-		cgit_close_filter(ctx.repo->email_filter);
-	}
-	html("</td><td colspan='2'>");
-	if (info) {
-		if (info->tagger_date > 0)
-			cgit_print_age(info->tagger_date, info->tagger_tz, -1);
-	} else if (ref->object->type == OBJ_COMMIT) {
-		cgit_print_age(ref->commit->commit->date, 0, -1);
-	}
-	html("</td></tr>\n");
-
-	return 0;
-}
-
-static void print_refs_link(const char *path)
-{
-	html("<tr class='nohover'><td colspan='5'>");
-	cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path);
-	html("</td></tr>");
-}
-
-void cgit_print_branches(int maxcount)
-{
-	struct reflist list;
-	int i;
-
-	html("<tr class='nohover'><th class='left'>Branch</th>"
-	     "<th class='left'>Commit message</th>"
-	     "<th class='left'>Author</th>"
-	     "<th class='left' colspan='2'>Age</th></tr>\n");
-
-	list.refs = NULL;
-	list.alloc = list.count = 0;
-	for_each_branch_ref(cgit_refs_cb, &list);
-	if (ctx.repo->enable_remote_branches)
-		for_each_remote_ref(cgit_refs_cb, &list);
-
-	if (maxcount == 0 || maxcount > list.count)
-		maxcount = list.count;
-
-	qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
-	if (ctx.repo->branch_sort == 0)
-		qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
-
-	for (i = 0; i < maxcount; i++)
-		print_branch(list.refs[i]);
-
-	if (maxcount < list.count)
-		print_refs_link("heads");
-
-	cgit_free_reflist_inner(&list);
-}
-
-void cgit_print_tags(int maxcount)
-{
-	struct reflist list;
-	int i;
-
-	list.refs = NULL;
-	list.alloc = list.count = 0;
-	for_each_tag_ref(cgit_refs_cb, &list);
-	if (list.count == 0)
-		return;
-	qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
-	if (!maxcount)
-		maxcount = list.count;
-	else if (maxcount > list.count)
-		maxcount = list.count;
-	print_tag_header();
-	for (i = 0; i < maxcount; i++)
-		print_tag(list.refs[i]);
-
-	if (maxcount < list.count)
-		print_refs_link("tags");
-
-	cgit_free_reflist_inner(&list);
-}
-
-void cgit_print_refs(void)
-{
-	cgit_print_layout_start();
-	html("<table class='list nowrap'>");
-
-	if (ctx.qry.path && starts_with(ctx.qry.path, "heads"))
-		cgit_print_branches(0);
-	else if (ctx.qry.path && starts_with(ctx.qry.path, "tags"))
-		cgit_print_tags(0);
-	else {
-		cgit_print_branches(0);
-		html("<tr class='nohover'><td colspan='5'>&nbsp;</td></tr>");
-		cgit_print_tags(0);
-	}
-	html("</table>");
-	cgit_print_layout_end();
-}
diff --git a/www/git.causal.agency/cgit/ui-refs.h b/www/git.causal.agency/cgit/ui-refs.h
deleted file mode 100644
index 1d4a54a2..00000000
--- a/www/git.causal.agency/cgit/ui-refs.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef UI_REFS_H
-#define UI_REFS_H
-
-extern void cgit_print_branches(int maxcount);
-extern void cgit_print_tags(int maxcount);
-extern void cgit_print_refs(void);
-
-#endif /* UI_REFS_H */
diff --git a/www/git.causal.agency/cgit/ui-repolist.c b/www/git.causal.agency/cgit/ui-repolist.c
deleted file mode 100644
index 97b11c5f..00000000
--- a/www/git.causal.agency/cgit/ui-repolist.c
+++ /dev/null
@@ -1,381 +0,0 @@
-/* ui-repolist.c: functions for generating the repolist page
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-repolist.h"
-#include "html.h"
-#include "ui-shared.h"
-
-static time_t read_agefile(const char *path)
-{
-	time_t result;
-	size_t size;
-	char *buf = NULL;
-	struct strbuf date_buf = STRBUF_INIT;
-
-	if (readfile(path, &buf, &size)) {
-		free(buf);
-		return 0;
-	}
-
-	if (parse_date(buf, &date_buf) == 0)
-		result = strtoul(date_buf.buf, NULL, 10);
-	else
-		result = 0;
-	free(buf);
-	strbuf_release(&date_buf);
-	return result;
-}
-
-static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
-{
-	struct strbuf path = STRBUF_INIT;
-	struct stat s;
-	struct cgit_repo *r = (struct cgit_repo *)repo;
-
-	if (repo->mtime != -1) {
-		*mtime = repo->mtime;
-		return 1;
-	}
-	strbuf_addf(&path, "%s/%s", repo->path, ctx.cfg.agefile);
-	if (stat(path.buf, &s) == 0) {
-		*mtime = read_agefile(path.buf);
-		if (*mtime) {
-			r->mtime = *mtime;
-			goto end;
-		}
-	}
-
-	strbuf_reset(&path);
-	strbuf_addf(&path, "%s/refs/heads/%s", repo->path,
-		    repo->defbranch ? repo->defbranch : "master");
-	if (stat(path.buf, &s) == 0) {
-		*mtime = s.st_mtime;
-		r->mtime = *mtime;
-		goto end;
-	}
-
-	strbuf_reset(&path);
-	strbuf_addf(&path, "%s/%s", repo->path, "packed-refs");
-	if (stat(path.buf, &s) == 0) {
-		*mtime = s.st_mtime;
-		r->mtime = *mtime;
-		goto end;
-	}
-
-	*mtime = 0;
-	r->mtime = *mtime;
-end:
-	strbuf_release(&path);
-	return (r->mtime != 0);
-}
-
-static void print_modtime(struct cgit_repo *repo)
-{
-	time_t t;
-	if (get_repo_modtime(repo, &t))
-		cgit_print_age(t, 0, -1);
-}
-
-static int is_match(struct cgit_repo *repo)
-{
-	if (!ctx.qry.search)
-		return 1;
-	if (repo->url && strcasestr(repo->url, ctx.qry.search))
-		return 1;
-	if (repo->name && strcasestr(repo->name, ctx.qry.search))
-		return 1;
-	if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
-		return 1;
-	if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
-		return 1;
-	return 0;
-}
-
-static int is_in_url(struct cgit_repo *repo)
-{
-	if (!ctx.qry.url)
-		return 1;
-	if (repo->url && starts_with(repo->url, ctx.qry.url))
-		return 1;
-	return 0;
-}
-
-static int is_visible(struct cgit_repo *repo)
-{
-	if (repo->hide || repo->ignore)
-		return 0;
-	if (!(is_match(repo) && is_in_url(repo)))
-		return 0;
-	return 1;
-}
-
-static int any_repos_visible(void)
-{
-	int i;
-
-	for (i = 0; i < cgit_repolist.count; i++) {
-		if (is_visible(&cgit_repolist.repos[i]))
-			return 1;
-	}
-	return 0;
-}
-
-static void print_sort_header(const char *title, const char *sort)
-{
-	char *currenturl = cgit_currenturl();
-	html("<th class='left'><a href='");
-	html_attr(currenturl);
-	htmlf("?s=%s", sort);
-	if (ctx.qry.search) {
-		html("&amp;q=");
-		html_url_arg(ctx.qry.search);
-	}
-	htmlf("'>%s</a></th>", title);
-	free(currenturl);
-}
-
-static void print_header(void)
-{
-	html("<tr class='nohover'>");
-	print_sort_header("Name", "name");
-	print_sort_header("Description", "desc");
-	if (ctx.cfg.enable_index_owner)
-		print_sort_header("Owner", "owner");
-	print_sort_header("Idle", "idle");
-	if (ctx.cfg.enable_index_links)
-		html("<th class='left'>Links</th>");
-	html("</tr>\n");
-}
-
-
-static void print_pager(int items, int pagelen, char *search, char *sort)
-{
-	int i, ofs;
-	char *class = NULL;
-	html("<ul class='pager'>");
-	for (i = 0, ofs = 0; ofs < items; i++, ofs = i * pagelen) {
-		class = (ctx.qry.ofs == ofs) ? "current" : NULL;
-		html("<li>");
-		cgit_index_link(fmt("[%d]", i + 1), fmt("Page %d", i + 1),
-				class, search, sort, ofs, 0);
-		html("</li>");
-	}
-	html("</ul>");
-}
-
-static int cmp(const char *s1, const char *s2)
-{
-	if (s1 && s2) {
-		if (ctx.cfg.case_sensitive_sort)
-			return strcmp(s1, s2);
-		else
-			return strcasecmp(s1, s2);
-	}
-	if (s1 && !s2)
-		return -1;
-	if (s2 && !s1)
-		return 1;
-	return 0;
-}
-
-static int sort_name(const void *a, const void *b)
-{
-	const struct cgit_repo *r1 = a;
-	const struct cgit_repo *r2 = b;
-
-	return cmp(r1->name, r2->name);
-}
-
-static int sort_desc(const void *a, const void *b)
-{
-	const struct cgit_repo *r1 = a;
-	const struct cgit_repo *r2 = b;
-
-	return cmp(r1->desc, r2->desc);
-}
-
-static int sort_owner(const void *a, const void *b)
-{
-	const struct cgit_repo *r1 = a;
-	const struct cgit_repo *r2 = b;
-
-	return cmp(r1->owner, r2->owner);
-}
-
-static int sort_idle(const void *a, const void *b)
-{
-	const struct cgit_repo *r1 = a;
-	const struct cgit_repo *r2 = b;
-	time_t t1, t2;
-
-	t1 = t2 = 0;
-	get_repo_modtime(r1, &t1);
-	get_repo_modtime(r2, &t2);
-	return t2 - t1;
-}
-
-static int sort_section(const void *a, const void *b)
-{
-	const struct cgit_repo *r1 = a;
-	const struct cgit_repo *r2 = b;
-	int result;
-
-	result = cmp(r1->section, r2->section);
-	if (!result) {
-		if (!strcmp(ctx.cfg.repository_sort, "age"))
-			result = sort_idle(r1, r2);
-		if (!result)
-			result = cmp(r1->name, r2->name);
-	}
-	return result;
-}
-
-struct sortcolumn {
-	const char *name;
-	int (*fn)(const void *a, const void *b);
-};
-
-static const struct sortcolumn sortcolumn[] = {
-	{"section", sort_section},
-	{"name", sort_name},
-	{"desc", sort_desc},
-	{"owner", sort_owner},
-	{"idle", sort_idle},
-	{NULL, NULL}
-};
-
-static int sort_repolist(char *field)
-{
-	const struct sortcolumn *column;
-
-	for (column = &sortcolumn[0]; column->name; column++) {
-		if (strcmp(field, column->name))
-			continue;
-		qsort(cgit_repolist.repos, cgit_repolist.count,
-			sizeof(struct cgit_repo), column->fn);
-		return 1;
-	}
-	return 0;
-}
-
-
-void cgit_print_repolist(void)
-{
-	int i, columns = 3, hits = 0, header = 0;
-	char *last_section = NULL;
-	char *section;
-	char *repourl;
-	int sorted = 0;
-
-	if (!any_repos_visible()) {
-		cgit_print_error_page(404, "Not found", "No repositories found");
-		return;
-	}
-
-	if (ctx.cfg.enable_index_links)
-		++columns;
-	if (ctx.cfg.enable_index_owner)
-		++columns;
-
-	ctx.page.title = ctx.cfg.root_title;
-	cgit_print_http_headers();
-	cgit_print_docstart();
-	cgit_print_pageheader();
-
-	if (ctx.qry.sort)
-		sorted = sort_repolist(ctx.qry.sort);
-	else if (ctx.cfg.section_sort)
-		sort_repolist("section");
-
-	html("<table summary='repository list' class='list nowrap'>");
-	for (i = 0; i < cgit_repolist.count; i++) {
-		ctx.repo = &cgit_repolist.repos[i];
-		if (!is_visible(ctx.repo))
-			continue;
-		hits++;
-		if (hits <= ctx.qry.ofs)
-			continue;
-		if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
-			continue;
-		if (!header++)
-			print_header();
-		section = ctx.repo->section;
-		if (section && !strcmp(section, ""))
-			section = NULL;
-		if (!sorted &&
-		    ((last_section == NULL && section != NULL) ||
-		    (last_section != NULL && section == NULL) ||
-		    (last_section != NULL && section != NULL &&
-		     strcmp(section, last_section)))) {
-			htmlf("<tr class='nohover-highlight'><td colspan='%d' class='reposection'>",
-			      columns);
-			html_txt(section);
-			html("</td></tr>");
-			last_section = section;
-		}
-		htmlf("<tr><td class='%s'>",
-		      !sorted && section ? "sublevel-repo" : "toplevel-repo");
-		cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
-		html("</td><td>");
-		repourl = cgit_repourl(ctx.repo->url);
-		html_link_open(repourl, NULL, NULL);
-		free(repourl);
-		if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0)
-			html("...");
-		html_link_close();
-		html("</td><td>");
-		if (ctx.cfg.enable_index_owner) {
-			if (ctx.repo->owner_filter) {
-				cgit_open_filter(ctx.repo->owner_filter);
-				html_txt(ctx.repo->owner);
-				cgit_close_filter(ctx.repo->owner_filter);
-			} else {
-				char *currenturl = cgit_currenturl();
-				html("<a href='");
-				html_attr(currenturl);
-				html("?q=");
-				html_url_arg(ctx.repo->owner);
-				html("'>");
-				html_txt(ctx.repo->owner);
-				html("</a>");
-				free(currenturl);
-			}
-			html("</td><td>");
-		}
-		print_modtime(ctx.repo);
-		html("</td>");
-		if (ctx.cfg.enable_index_links) {
-			html("<td>");
-			cgit_summary_link("summary", NULL, "button", NULL);
-			html(" ");
-			cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
-				      0, NULL, NULL, ctx.qry.showmsg, 0);
-			html(" ");
-			cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
-			html("</td>");
-		}
-		html("</tr>\n");
-	}
-	html("</table>");
-	if (hits > ctx.cfg.max_repo_count)
-		print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search, ctx.qry.sort);
-	cgit_print_docend();
-}
-
-void cgit_print_site_readme(void)
-{
-	cgit_print_layout_start();
-	if (!ctx.cfg.root_readme)
-		goto done;
-	cgit_open_filter(ctx.cfg.about_filter, ctx.cfg.root_readme);
-	html_include(ctx.cfg.root_readme);
-	cgit_close_filter(ctx.cfg.about_filter);
-done:
-	cgit_print_layout_end();
-}
diff --git a/www/git.causal.agency/cgit/ui-repolist.h b/www/git.causal.agency/cgit/ui-repolist.h
deleted file mode 100644
index 1b6b3227..00000000
--- a/www/git.causal.agency/cgit/ui-repolist.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef UI_REPOLIST_H
-#define UI_REPOLIST_H
-
-extern void cgit_print_repolist(void);
-extern void cgit_print_site_readme(void);
-
-#endif /* UI_REPOLIST_H */
diff --git a/www/git.causal.agency/cgit/ui-shared.c b/www/git.causal.agency/cgit/ui-shared.c
deleted file mode 100644
index dfaf5952..00000000
--- a/www/git.causal.agency/cgit/ui-shared.c
+++ /dev/null
@@ -1,1241 +0,0 @@
-/* ui-shared.c: common web output functions
- *
- * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-shared.h"
-#include "cmd.h"
-#include "html.h"
-#include "version.h"
-
-static const char cgit_doctype[] =
-"<!DOCTYPE html>\n";
-
-static char *http_date(time_t t)
-{
-	static char day[][4] =
-		{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
-	static char month[][4] =
-		{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-		 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
-	struct tm tm;
-	gmtime_r(&t, &tm);
-	return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm.tm_wday],
-		   tm.tm_mday, month[tm.tm_mon], 1900 + tm.tm_year,
-		   tm.tm_hour, tm.tm_min, tm.tm_sec);
-}
-
-void cgit_print_error(const char *fmt, ...)
-{
-	va_list ap;
-	va_start(ap, fmt);
-	cgit_vprint_error(fmt, ap);
-	va_end(ap);
-}
-
-void cgit_vprint_error(const char *fmt, va_list ap)
-{
-	va_list cp;
-	html("<div class='error'>");
-	va_copy(cp, ap);
-	html_vtxtf(fmt, cp);
-	va_end(cp);
-	html("</div>\n");
-}
-
-const char *cgit_httpscheme(void)
-{
-	if (ctx.env.https && !strcmp(ctx.env.https, "on"))
-		return "https://";
-	else
-		return "http://";
-}
-
-char *cgit_hosturl(void)
-{
-	if (ctx.env.http_host)
-		return xstrdup(ctx.env.http_host);
-	if (!ctx.env.server_name)
-		return NULL;
-	if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
-		return xstrdup(ctx.env.server_name);
-	return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port);
-}
-
-char *cgit_currenturl(void)
-{
-	const char *root = cgit_rooturl();
-
-	if (!ctx.qry.url)
-		return xstrdup(root);
-	if (root[0] && root[strlen(root) - 1] == '/')
-		return fmtalloc("%s%s", root, ctx.qry.url);
-	return fmtalloc("%s/%s", root, ctx.qry.url);
-}
-
-char *cgit_currentfullurl(void)
-{
-	const char *root = cgit_rooturl();
-	const char *orig_query = ctx.env.query_string ? ctx.env.query_string : "";
-	size_t len = strlen(orig_query);
-	char *query = xmalloc(len + 2), *start_url, *ret;
-
-	/* Remove all url=... parts from query string */
-	memcpy(query + 1, orig_query, len + 1);
-	query[0] = '?';
-	start_url = query;
-	while ((start_url = strstr(start_url, "url=")) != NULL) {
-		if (start_url[-1] == '?' || start_url[-1] == '&') {
-			const char *end_url = strchr(start_url, '&');
-			if (end_url)
-				memmove(start_url, end_url + 1, strlen(end_url));
-			else
-				start_url[0] = '\0';
-		} else
-			++start_url;
-	}
-	if (!query[1])
-		query[0] = '\0';
-
-	if (!ctx.qry.url)
-		ret = fmtalloc("%s%s", root, query);
-	else if (root[0] && root[strlen(root) - 1] == '/')
-		ret = fmtalloc("%s%s%s", root, ctx.qry.url, query);
-	else
-		ret = fmtalloc("%s/%s%s", root, ctx.qry.url, query);
-	free(query);
-	return ret;
-}
-
-const char *cgit_rooturl(void)
-{
-	if (ctx.cfg.virtual_root)
-		return ctx.cfg.virtual_root;
-	else
-		return ctx.cfg.script_name;
-}
-
-const char *cgit_loginurl(void)
-{
-	static const char *login_url;
-	if (!login_url)
-		login_url = fmtalloc("%s?p=login", cgit_rooturl());
-	return login_url;
-}
-
-char *cgit_repourl(const char *reponame)
-{
-	if (ctx.cfg.virtual_root)
-		return fmtalloc("%s%s/", ctx.cfg.virtual_root, reponame);
-	else
-		return fmtalloc("?r=%s", reponame);
-}
-
-char *cgit_fileurl(const char *reponame, const char *pagename,
-		   const char *filename, const char *query)
-{
-	struct strbuf sb = STRBUF_INIT;
-	char *delim;
-
-	if (ctx.cfg.virtual_root) {
-		strbuf_addf(&sb, "%s%s/%s/%s", ctx.cfg.virtual_root, reponame,
-			    pagename, (filename ? filename:""));
-		delim = "?";
-	} else {
-		strbuf_addf(&sb, "?url=%s/%s/%s", reponame, pagename,
-			    (filename ? filename : ""));
-		delim = "&amp;";
-	}
-	if (query)
-		strbuf_addf(&sb, "%s%s", delim, query);
-	return strbuf_detach(&sb, NULL);
-}
-
-char *cgit_pageurl(const char *reponame, const char *pagename,
-		   const char *query)
-{
-	return cgit_fileurl(reponame, pagename, NULL, query);
-}
-
-const char *cgit_repobasename(const char *reponame)
-{
-	/* I assume we don't need to store more than one repo basename */
-	static char rvbuf[1024];
-	int p;
-	const char *rv;
-	size_t len;
-
-	len = strlcpy(rvbuf, reponame, sizeof(rvbuf));
-	if (len >= sizeof(rvbuf))
-		die("cgit_repobasename: truncated repository name '%s'", reponame);
-	p = len - 1;
-	/* strip trailing slashes */
-	while (p && rvbuf[p] == '/')
-		rvbuf[p--] = '\0';
-	/* strip trailing .git */
-	if (p >= 3 && starts_with(&rvbuf[p-3], ".git")) {
-		p -= 3;
-		rvbuf[p--] = '\0';
-	}
-	/* strip more trailing slashes if any */
-	while (p && rvbuf[p] == '/')
-		rvbuf[p--] = '\0';
-	/* find last slash in the remaining string */
-	rv = strrchr(rvbuf, '/');
-	if (rv)
-		return ++rv;
-	return rvbuf;
-}
-
-const char *cgit_snapshot_prefix(const struct cgit_repo *repo)
-{
-	if (repo->snapshot_prefix)
-		return repo->snapshot_prefix;
-
-	return cgit_repobasename(repo->url);
-}
-
-static void site_url(const char *page, const char *search, const char *sort, int ofs, int always_root)
-{
-	char *delim = "?";
-
-	if (always_root || page)
-		html_attr(cgit_rooturl());
-	else {
-		char *currenturl = cgit_currenturl();
-		html_attr(currenturl);
-		free(currenturl);
-	}
-
-	if (page) {
-		htmlf("?p=%s", page);
-		delim = "&amp;";
-	}
-	if (search) {
-		html(delim);
-		html("q=");
-		html_attr(search);
-		delim = "&amp;";
-	}
-	if (sort) {
-		html(delim);
-		html("s=");
-		html_attr(sort);
-		delim = "&amp;";
-	}
-	if (ofs) {
-		html(delim);
-		htmlf("ofs=%d", ofs);
-	}
-}
-
-static void site_link(const char *page, const char *name, const char *title,
-		      const char *class, const char *search, const char *sort, int ofs, int always_root)
-{
-	html("<a");
-	if (title) {
-		html(" title='");
-		html_attr(title);
-		html("'");
-	}
-	if (class) {
-		html(" class='");
-		html_attr(class);
-		html("'");
-	}
-	html(" href='");
-	site_url(page, search, sort, ofs, always_root);
-	html("'>");
-	html_txt(name);
-	html("</a>");
-}
-
-void cgit_index_link(const char *name, const char *title, const char *class,
-		     const char *pattern, const char *sort, int ofs, int always_root)
-{
-	site_link(NULL, name, title, class, pattern, sort, ofs, always_root);
-}
-
-static char *repolink(const char *title, const char *class, const char *page,
-		      const char *head, const char *path)
-{
-	char *delim = "?";
-
-	html("<a");
-	if (title) {
-		html(" title='");
-		html_attr(title);
-		html("'");
-	}
-	if (class) {
-		html(" class='");
-		html_attr(class);
-		html("'");
-	}
-	html(" href='");
-	if (ctx.cfg.virtual_root) {
-		html_url_path(ctx.cfg.virtual_root);
-		html_url_path(ctx.repo->url);
-		if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
-			html("/");
-		if (page) {
-			html_url_path(page);
-			html("/");
-			if (path)
-				html_url_path(path);
-		}
-	} else {
-		html_url_path(ctx.cfg.script_name);
-		html("?url=");
-		html_url_arg(ctx.repo->url);
-		if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
-			html("/");
-		if (page) {
-			html_url_arg(page);
-			html("/");
-			if (path)
-				html_url_arg(path);
-		}
-		delim = "&amp;";
-	}
-	if (head && ctx.repo->defbranch && strcmp(head, ctx.repo->defbranch)) {
-		html(delim);
-		html("h=");
-		html_url_arg(head);
-		delim = "&amp;";
-	}
-	return fmt("%s", delim);
-}
-
-static void reporevlink(const char *page, const char *name, const char *title,
-			const char *class, const char *head, const char *rev,
-			const char *path)
-{
-	char *delim;
-
-	delim = repolink(title, class, page, head, path);
-	if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) {
-		html(delim);
-		html("id=");
-		html_url_arg(rev);
-	}
-	html("'>");
-	html_txt(name);
-	html("</a>");
-}
-
-void cgit_summary_link(const char *name, const char *title, const char *class,
-		       const char *head)
-{
-	reporevlink(NULL, name, title, class, head, NULL, NULL);
-}
-
-void cgit_tag_link(const char *name, const char *title, const char *class,
-		   const char *tag)
-{
-	reporevlink("tag", name, title, class, tag, NULL, NULL);
-}
-
-void cgit_tree_link(const char *name, const char *title, const char *class,
-		    const char *head, const char *rev, const char *path)
-{
-	reporevlink("tree", name, title, class, head, rev, path);
-}
-
-void cgit_plain_link(const char *name, const char *title, const char *class,
-		     const char *head, const char *rev, const char *path)
-{
-	reporevlink("plain", name, title, class, head, rev, path);
-}
-
-void cgit_blame_link(const char *name, const char *title, const char *class,
-		     const char *head, const char *rev, const char *path)
-{
-	reporevlink("blame", name, title, class, head, rev, path);
-}
-
-void cgit_log_link(const char *name, const char *title, const char *class,
-		   const char *head, const char *rev, const char *path,
-		   int ofs, const char *grep, const char *pattern, int showmsg,
-		   int follow)
-{
-	char *delim;
-
-	delim = repolink(title, class, "log", head, path);
-	if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) {
-		html(delim);
-		html("id=");
-		html_url_arg(rev);
-		delim = "&amp;";
-	}
-	if (grep && pattern) {
-		html(delim);
-		html("qt=");
-		html_url_arg(grep);
-		delim = "&amp;";
-		html(delim);
-		html("q=");
-		html_url_arg(pattern);
-	}
-	if (ofs > 0) {
-		html(delim);
-		html("ofs=");
-		htmlf("%d", ofs);
-		delim = "&amp;";
-	}
-	if (showmsg) {
-		html(delim);
-		html("showmsg=1");
-		delim = "&amp;";
-	}
-	if (follow) {
-		html(delim);
-		html("follow=1");
-	}
-	html("'>");
-	html_txt(name);
-	html("</a>");
-}
-
-void cgit_commit_link(const char *name, const char *title, const char *class,
-		      const char *head, const char *rev, const char *path)
-{
-	char *delim;
-
-	delim = repolink(title, class, "commit", head, path);
-	if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) {
-		html(delim);
-		html("id=");
-		html_url_arg(rev);
-		delim = "&amp;";
-	}
-	if (ctx.qry.difftype) {
-		html(delim);
-		htmlf("dt=%d", ctx.qry.difftype);
-		delim = "&amp;";
-	}
-	if (ctx.qry.context > 0 && ctx.qry.context != 3) {
-		html(delim);
-		html("context=");
-		htmlf("%d", ctx.qry.context);
-		delim = "&amp;";
-	}
-	if (ctx.qry.ignorews) {
-		html(delim);
-		html("ignorews=1");
-		delim = "&amp;";
-	}
-	if (ctx.qry.follow) {
-		html(delim);
-		html("follow=1");
-	}
-	html("'>");
-	if (name[0] != '\0') {
-		if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
-			html_ntxt(name, ctx.cfg.max_msg_len - 3);
-			html("...");
-		} else
-			html_txt(name);
-	} else
-		html_txt("(no commit message)");
-	html("</a>");
-}
-
-void cgit_refs_link(const char *name, const char *title, const char *class,
-		    const char *head, const char *rev, const char *path)
-{
-	reporevlink("refs", name, title, class, head, rev, path);
-}
-
-void cgit_snapshot_link(const char *name, const char *title, const char *class,
-			const char *head, const char *rev,
-			const char *archivename)
-{
-	reporevlink("snapshot", name, title, class, head, rev, archivename);
-}
-
-void cgit_diff_link(const char *name, const char *title, const char *class,
-		    const char *head, const char *new_rev, const char *old_rev,
-		    const char *path)
-{
-	char *delim;
-
-	delim = repolink(title, class, "diff", head, path);
-	if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) {
-		html(delim);
-		html("id=");
-		html_url_arg(new_rev);
-		delim = "&amp;";
-	}
-	if (old_rev) {
-		html(delim);
-		html("id2=");
-		html_url_arg(old_rev);
-		delim = "&amp;";
-	}
-	if (ctx.qry.difftype) {
-		html(delim);
-		htmlf("dt=%d", ctx.qry.difftype);
-		delim = "&amp;";
-	}
-	if (ctx.qry.context > 0 && ctx.qry.context != 3) {
-		html(delim);
-		html("context=");
-		htmlf("%d", ctx.qry.context);
-		delim = "&amp;";
-	}
-	if (ctx.qry.ignorews) {
-		html(delim);
-		html("ignorews=1");
-		delim = "&amp;";
-	}
-	if (ctx.qry.follow) {
-		html(delim);
-		html("follow=1");
-	}
-	html("'>");
-	html_txt(name);
-	html("</a>");
-}
-
-void cgit_patch_link(const char *name, const char *title, const char *class,
-		     const char *head, const char *rev, const char *path)
-{
-	reporevlink("patch", name, title, class, head, rev, path);
-}
-
-void cgit_stats_link(const char *name, const char *title, const char *class,
-		     const char *head, const char *path)
-{
-	reporevlink("stats", name, title, class, head, NULL, path);
-}
-
-static void cgit_self_link(char *name, const char *title, const char *class)
-{
-	if (!strcmp(ctx.qry.page, "repolist"))
-		cgit_index_link(name, title, class, ctx.qry.search, ctx.qry.sort,
-				ctx.qry.ofs, 1);
-	else if (!strcmp(ctx.qry.page, "summary"))
-		cgit_summary_link(name, title, class, ctx.qry.head);
-	else if (!strcmp(ctx.qry.page, "tag"))
-		cgit_tag_link(name, title, class, ctx.qry.has_oid ?
-			       ctx.qry.oid : ctx.qry.head);
-	else if (!strcmp(ctx.qry.page, "tree"))
-		cgit_tree_link(name, title, class, ctx.qry.head,
-			       ctx.qry.has_oid ? ctx.qry.oid : NULL,
-			       ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "plain"))
-		cgit_plain_link(name, title, class, ctx.qry.head,
-				ctx.qry.has_oid ? ctx.qry.oid : NULL,
-				ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "blame"))
-		cgit_blame_link(name, title, class, ctx.qry.head,
-				ctx.qry.has_oid ? ctx.qry.oid : NULL,
-				ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "log"))
-		cgit_log_link(name, title, class, ctx.qry.head,
-			      ctx.qry.has_oid ? ctx.qry.oid : NULL,
-			      ctx.qry.path, ctx.qry.ofs,
-			      ctx.qry.grep, ctx.qry.search,
-			      ctx.qry.showmsg, ctx.qry.follow);
-	else if (!strcmp(ctx.qry.page, "commit"))
-		cgit_commit_link(name, title, class, ctx.qry.head,
-				 ctx.qry.has_oid ? ctx.qry.oid : NULL,
-				 ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "patch"))
-		cgit_patch_link(name, title, class, ctx.qry.head,
-				ctx.qry.has_oid ? ctx.qry.oid : NULL,
-				ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "refs"))
-		cgit_refs_link(name, title, class, ctx.qry.head,
-			       ctx.qry.has_oid ? ctx.qry.oid : NULL,
-			       ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "snapshot"))
-		cgit_snapshot_link(name, title, class, ctx.qry.head,
-				   ctx.qry.has_oid ? ctx.qry.oid : NULL,
-				   ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "diff"))
-		cgit_diff_link(name, title, class, ctx.qry.head,
-			       ctx.qry.oid, ctx.qry.oid2,
-			       ctx.qry.path);
-	else if (!strcmp(ctx.qry.page, "stats"))
-		cgit_stats_link(name, title, class, ctx.qry.head,
-				ctx.qry.path);
-	else {
-		/* Don't known how to make link for this page */
-		repolink(title, class, ctx.qry.page, ctx.qry.head, ctx.qry.path);
-		html("><!-- cgit_self_link() doesn't know how to make link for page '");
-		html_txt(ctx.qry.page);
-		html("' -->");
-		html_txt(name);
-		html("</a>");
-	}
-}
-
-void cgit_object_link(struct object *obj)
-{
-	char *page, *shortrev, *fullrev, *name;
-
-	fullrev = oid_to_hex(&obj->oid);
-	shortrev = xstrdup(fullrev);
-	shortrev[10] = '\0';
-	if (obj->type == OBJ_COMMIT) {
-		cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
-				 ctx.qry.head, fullrev, NULL);
-		return;
-	} else if (obj->type == OBJ_TREE)
-		page = "tree";
-	else if (obj->type == OBJ_TAG)
-		page = "tag";
-	else
-		page = "blob";
-	name = fmt("%s %s...", type_name(obj->type), shortrev);
-	reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
-}
-
-static struct string_list_item *lookup_path(struct string_list *list,
-					    const char *path)
-{
-	struct string_list_item *item;
-
-	while (path && path[0]) {
-		if ((item = string_list_lookup(list, path)))
-			return item;
-		if (!(path = strchr(path, '/')))
-			break;
-		path++;
-	}
-	return NULL;
-}
-
-void cgit_submodule_link(const char *class, char *path, const char *rev)
-{
-	struct string_list *list;
-	struct string_list_item *item;
-	char tail, *dir;
-	size_t len;
-
-	len = 0;
-	tail = 0;
-	list = &ctx.repo->submodules;
-	item = lookup_path(list, path);
-	if (!item) {
-		len = strlen(path);
-		tail = path[len - 1];
-		if (tail == '/') {
-			path[len - 1] = 0;
-			item = lookup_path(list, path);
-		}
-	}
-	if (item || ctx.repo->module_link) {
-		html("<a ");
-		if (class)
-			htmlf("class='%s' ", class);
-		html("href='");
-		if (item) {
-			html_attrf(item->util, rev);
-		} else {
-			dir = strrchr(path, '/');
-			if (dir)
-				dir++;
-			else
-				dir = path;
-			html_attrf(ctx.repo->module_link, dir, rev);
-		}
-		html("'>");
-		html_txt(path);
-		html("</a>");
-	} else {
-		html("<span");
-		if (class)
-			htmlf(" class='%s'", class);
-		html(">");
-		html_txt(path);
-		html("</span>");
-	}
-	html_txtf(" @ %.7s", rev);
-	if (item && tail)
-		path[len - 1] = tail;
-}
-
-const struct date_mode *cgit_date_mode(enum date_mode_type type)
-{
-	static struct date_mode mode;
-	mode.type = type;
-	mode.local = ctx.cfg.local_time;
-	return &mode;
-}
-
-static void print_rel_date(time_t t, int tz, double value,
-	const char *class, const char *suffix)
-{
-	htmlf("<span class='%s' title='", class);
-	html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601)));
-	htmlf("'>%.0f %s</span>", value, suffix);
-}
-
-void cgit_print_age(time_t t, int tz, time_t max_relative)
-{
-	time_t now, secs;
-
-	if (!t)
-		return;
-	time(&now);
-	secs = now - t;
-	if (secs < 0)
-		secs = 0;
-
-	if (secs > max_relative && max_relative >= 0) {
-		html("<span title='");
-		html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601)));
-		html("'>");
-		html_txt(show_date(t, tz, cgit_date_mode(DATE_SHORT)));
-		html("</span>");
-		return;
-	}
-
-	if (secs < TM_HOUR * 2) {
-		print_rel_date(t, tz, secs * 1.0 / TM_MIN, "age-mins", "min.");
-		return;
-	}
-	if (secs < TM_DAY * 2) {
-		print_rel_date(t, tz, secs * 1.0 / TM_HOUR, "age-hours", "hours");
-		return;
-	}
-	if (secs < TM_WEEK * 2) {
-		print_rel_date(t, tz, secs * 1.0 / TM_DAY, "age-days", "days");
-		return;
-	}
-	if (secs < TM_MONTH * 2) {
-		print_rel_date(t, tz, secs * 1.0 / TM_WEEK, "age-weeks", "weeks");
-		return;
-	}
-	if (secs < TM_YEAR * 2) {
-		print_rel_date(t, tz, secs * 1.0 / TM_MONTH, "age-months", "months");
-		return;
-	}
-	print_rel_date(t, tz, secs * 1.0 / TM_YEAR, "age-years", "years");
-}
-
-void cgit_print_http_headers(void)
-{
-	if (ctx.env.no_http && !strcmp(ctx.env.no_http, "1"))
-		return;
-
-	if (ctx.page.status)
-		htmlf("Status: %d %s\n", ctx.page.status, ctx.page.statusmsg);
-	if (ctx.page.mimetype && ctx.page.charset)
-		htmlf("Content-Type: %s; charset=%s\n", ctx.page.mimetype,
-		      ctx.page.charset);
-	else if (ctx.page.mimetype)
-		htmlf("Content-Type: %s\n", ctx.page.mimetype);
-	if (ctx.page.size)
-		htmlf("Content-Length: %zd\n", ctx.page.size);
-	if (ctx.page.filename) {
-		html("Content-Disposition: inline; filename=\"");
-		html_header_arg_in_quotes(ctx.page.filename);
-		html("\"\n");
-	}
-	if (!ctx.env.authenticated)
-		html("Cache-Control: no-cache, no-store\n");
-	htmlf("Last-Modified: %s\n", http_date(ctx.page.modified));
-	htmlf("Expires: %s\n", http_date(ctx.page.expires));
-	if (ctx.page.etag)
-		htmlf("ETag: \"%s\"\n", ctx.page.etag);
-	html("\n");
-	if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
-		exit(0);
-}
-
-void cgit_redirect(const char *url, bool permanent)
-{
-	htmlf("Status: %d %s\n", permanent ? 301 : 302, permanent ? "Moved" : "Found");
-	html("Location: ");
-	html_url_path(url);
-	html("\n\n");
-}
-
-static void print_rel_vcs_link(const char *url)
-{
-	html("<link rel='vcs-git' href='");
-	html_attr(url);
-	html("' title='");
-	html_attr(ctx.repo->name);
-	html(" Git repository'/>\n");
-}
-
-void cgit_print_docstart(void)
-{
-	char *host = cgit_hosturl();
-
-	if (ctx.cfg.embedded) {
-		if (ctx.cfg.header)
-			html_include(ctx.cfg.header);
-		return;
-	}
-
-	html(cgit_doctype);
-	html("<html lang='en'>\n");
-	html("<head>\n");
-	html("<title>");
-	html_txt(ctx.page.title);
-	html("</title>\n");
-	htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
-	if (ctx.cfg.robots && *ctx.cfg.robots)
-		htmlf("<meta name='robots' content='%s'/>\n", ctx.cfg.robots);
-	html("<link rel='stylesheet' type='text/css' href='");
-	html_attr(ctx.cfg.css);
-	html("'/>\n");
-	if (ctx.cfg.favicon) {
-		html("<link rel='shortcut icon' href='");
-		html_attr(ctx.cfg.favicon);
-		html("'/>\n");
-	}
-	if (host && ctx.repo && ctx.qry.head) {
-		char *fileurl;
-		struct strbuf sb = STRBUF_INIT;
-		strbuf_addf(&sb, "h=%s", ctx.qry.head);
-
-		html("<link rel='alternate' title='Atom feed' href='");
-		html(cgit_httpscheme());
-		html_attr(host);
-		fileurl = cgit_fileurl(ctx.repo->url, "atom", ctx.qry.vpath,
-				       sb.buf);
-		html_attr(fileurl);
-		html("' type='application/atom+xml'/>\n");
-		strbuf_release(&sb);
-		free(fileurl);
-	}
-	if (ctx.repo)
-		cgit_add_clone_urls(print_rel_vcs_link);
-	if (ctx.cfg.head_include)
-		html_include(ctx.cfg.head_include);
-	if (ctx.repo && ctx.repo->extra_head_content)
-		html(ctx.repo->extra_head_content);
-	html("</head>\n");
-	html("<body>\n");
-	if (ctx.cfg.header)
-		html_include(ctx.cfg.header);
-	free(host);
-}
-
-void cgit_print_docend(void)
-{
-	html("</div> <!-- class=content -->\n");
-	if (ctx.cfg.embedded) {
-		html("</div> <!-- id=cgit -->\n");
-		if (ctx.cfg.footer)
-			html_include(ctx.cfg.footer);
-		return;
-	}
-	if (ctx.cfg.footer)
-		html_include(ctx.cfg.footer);
-	else {
-		htmlf("<div class='footer'>generated by <a href='https://git.causal.agency/src/log/www/git.causal.agency/cgit'>cgit %s</a> "
-			"(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string);
-		html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)));
-		html("</div>\n");
-	}
-	html("</div> <!-- id=cgit -->\n");
-	html("</body>\n</html>\n");
-}
-
-void cgit_print_error_page(int code, const char *msg, const char *fmt, ...)
-{
-	va_list ap;
-	ctx.page.expires = ctx.cfg.cache_dynamic_ttl;
-	ctx.page.status = code;
-	ctx.page.statusmsg = msg;
-	cgit_print_layout_start();
-	va_start(ap, fmt);
-	cgit_vprint_error(fmt, ap);
-	va_end(ap);
-	cgit_print_layout_end();
-}
-
-void cgit_print_layout_start(void)
-{
-	cgit_print_http_headers();
-	cgit_print_docstart();
-	cgit_print_pageheader();
-}
-
-void cgit_print_layout_end(void)
-{
-	cgit_print_docend();
-}
-
-static void add_clone_urls(void (*fn)(const char *), char *txt, char *suffix)
-{
-	struct strbuf **url_list = strbuf_split_str(txt, ' ', 0);
-	int i;
-
-	for (i = 0; url_list[i]; i++) {
-		strbuf_rtrim(url_list[i]);
-		if (url_list[i]->len == 0)
-			continue;
-		if (suffix && *suffix)
-			strbuf_addf(url_list[i], "/%s", suffix);
-		fn(url_list[i]->buf);
-	}
-
-	strbuf_list_free(url_list);
-}
-
-void cgit_add_clone_urls(void (*fn)(const char *))
-{
-	if (ctx.repo->clone_url)
-		add_clone_urls(fn, expand_macros(ctx.repo->clone_url), NULL);
-	else if (ctx.cfg.clone_prefix)
-		add_clone_urls(fn, ctx.cfg.clone_prefix, ctx.repo->url);
-}
-
-static int print_this_commit_option(void)
-{
-	struct object_id oid;
-	if (!ctx.qry.head || get_oid(ctx.qry.head, &oid))
-		return 1;
-	html_option(oid_to_hex(&oid), "this commit", ctx.qry.head);
-	return 0;
-}
-
-static int print_branch_option(const char *refname, const struct object_id *oid,
-			       int flags, void *cb_data)
-{
-	char *name = (char *)refname;
-	html_option(name, name, ctx.qry.head);
-	return 0;
-}
-
-void cgit_add_hidden_formfields(int incl_head, int incl_search,
-				const char *page)
-{
-	if (!ctx.cfg.virtual_root) {
-		struct strbuf url = STRBUF_INIT;
-
-		strbuf_addf(&url, "%s/%s", ctx.qry.repo, page);
-		if (ctx.qry.vpath)
-			strbuf_addf(&url, "/%s", ctx.qry.vpath);
-		html_hidden("url", url.buf);
-		strbuf_release(&url);
-	}
-
-	if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
-	    strcmp(ctx.qry.head, ctx.repo->defbranch))
-		html_hidden("h", ctx.qry.head);
-
-	if (ctx.qry.oid)
-		html_hidden("id", ctx.qry.oid);
-	if (ctx.qry.oid2)
-		html_hidden("id2", ctx.qry.oid2);
-	if (ctx.qry.showmsg)
-		html_hidden("showmsg", "1");
-
-	if (incl_search) {
-		if (ctx.qry.grep)
-			html_hidden("qt", ctx.qry.grep);
-		if (ctx.qry.search)
-			html_hidden("q", ctx.qry.search);
-	}
-}
-
-static const char *hc(const char *page)
-{
-	if (!ctx.qry.page)
-		return NULL;
-
-	return strcmp(ctx.qry.page, page) ? NULL : "active";
-}
-
-static void cgit_print_path_crumbs(char *path)
-{
-	char *old_path = ctx.qry.path;
-	char *p = path, *q, *end = path + strlen(path);
-	int levels = 0;
-
-	ctx.qry.path = NULL;
-	cgit_self_link("root", NULL, NULL);
-	ctx.qry.path = p = path;
-	while (p < end) {
-		if (!(q = strchr(p, '/')) || levels > 15)
-			q = end;
-		*q = '\0';
-		html_txt("/");
-		cgit_self_link(p, NULL, NULL);
-		if (q < end)
-			*q = '/';
-		p = q + 1;
-		++levels;
-	}
-	ctx.qry.path = old_path;
-}
-
-static void print_header(void)
-{
-	char *logo = NULL, *logo_link = NULL;
-
-	html("<table id='header'>\n");
-	html("<tr>\n");
-
-	if (ctx.repo && ctx.repo->logo && *ctx.repo->logo)
-		logo = ctx.repo->logo;
-	else
-		logo = ctx.cfg.logo;
-	if (ctx.repo && ctx.repo->logo_link && *ctx.repo->logo_link)
-		logo_link = ctx.repo->logo_link;
-	else
-		logo_link = ctx.cfg.logo_link;
-	if (logo && *logo) {
-		html("<td class='logo' rowspan='2'><a href='");
-		if (logo_link && *logo_link)
-			html_attr(logo_link);
-		else
-			html_attr(cgit_rooturl());
-		html("'><img src='");
-		html_attr(logo);
-		html("' alt='cgit logo'/></a></td>\n");
-	}
-
-	html("<td class='main'>");
-	if (ctx.repo) {
-		cgit_index_link("index", NULL, NULL, NULL, NULL, 0, 1);
-		html(" : ");
-		cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
-		if (ctx.env.authenticated) {
-			html("</td><td class='form'>");
-			html("<form method='get'>\n");
-			cgit_add_hidden_formfields(0, 1, ctx.qry.page);
-			html("<select name='h' onchange='this.form.submit();'>\n");
-			print_this_commit_option();
-			html("<optgroup label='branches'>");
-			for_each_branch_ref(print_branch_option, ctx.qry.head);
-			if (ctx.repo->enable_remote_branches)
-				for_each_remote_ref(print_branch_option, ctx.qry.head);
-			html("</optgroup>");
-			html("</select> ");
-			html("<input type='submit' value='switch'/>");
-			html("</form>");
-		}
-	} else
-		html_txt(ctx.cfg.root_title);
-	html("</td></tr>\n");
-
-	html("<tr><td class='sub'>");
-	if (ctx.repo) {
-		html_txt(ctx.repo->desc);
-		html("</td><td class='sub right'>");
-		if (ctx.repo->owner_filter) {
-			cgit_open_filter(ctx.repo->owner_filter);
-			html_txt(ctx.repo->owner);
-			cgit_close_filter(ctx.repo->owner_filter);
-		} else {
-			html_txt(ctx.repo->owner);
-		}
-	} else {
-		if (ctx.cfg.root_desc)
-			html_txt(ctx.cfg.root_desc);
-	}
-	html("</td></tr></table>\n");
-}
-
-void cgit_print_pageheader(void)
-{
-	html("<div id='cgit'>");
-	if (!ctx.env.authenticated || !ctx.cfg.noheader)
-		print_header();
-
-	html("<table class='tabs'><tr><td>\n");
-	if (ctx.env.authenticated && ctx.repo) {
-		if (ctx.repo->readme.nr) {
-			reporevlink("about", "about", NULL,
-				    hc("about"), ctx.qry.head, NULL,
-				    NULL);
-			html(" ");
-		}
-		cgit_summary_link("summary", NULL, hc("summary"),
-				  ctx.qry.head);
-		html(" ");
-		cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head,
-			       ctx.qry.oid, NULL);
-		html(" ");
-		cgit_log_link("log", NULL, hc("log"), ctx.qry.head,
-			      NULL, ctx.qry.vpath, 0, NULL, NULL,
-			      ctx.qry.showmsg, ctx.qry.follow);
-		html(" ");
-		if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))
-			cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,
-				        ctx.qry.oid, ctx.qry.vpath);
-		else
-			cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
-				       ctx.qry.oid, ctx.qry.vpath);
-		html(" ");
-		cgit_commit_link("commit", NULL, hc("commit"),
-				 ctx.qry.head, ctx.qry.oid, ctx.qry.vpath);
-		html(" ");
-		cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
-			       ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath);
-		if (ctx.repo->max_stats) {
-			html(" ");
-			cgit_stats_link("stats", NULL, hc("stats"),
-					ctx.qry.head, ctx.qry.vpath);
-		}
-		if (ctx.repo->homepage) {
-			html(" <a href='");
-			html_attr(ctx.repo->homepage);
-			html("'>homepage</a>");
-		}
-		html("</td><td class='form'>");
-		html("<form class='right' method='get' action='");
-		if (ctx.cfg.virtual_root) {
-			char *fileurl = cgit_fileurl(ctx.qry.repo, "log",
-						   ctx.qry.vpath, NULL);
-			html_url_path(fileurl);
-			free(fileurl);
-		}
-		html("'>\n");
-		cgit_add_hidden_formfields(1, 0, "log");
-		html("<select name='qt'>\n");
-		html_option("grep", "log msg", ctx.qry.grep);
-		html_option("author", "author", ctx.qry.grep);
-		html_option("committer", "committer", ctx.qry.grep);
-		html_option("range", "range", ctx.qry.grep);
-		html("</select>\n");
-		html("<input class='txt' type='search' size='10' name='q' value='");
-		html_attr(ctx.qry.search);
-		html("'/>\n");
-		html("<input type='submit' value='search'/>\n");
-		html("</form>\n");
-	} else if (ctx.env.authenticated) {
-		char *currenturl = cgit_currenturl();
-		site_link(NULL, "index", NULL, hc("repolist"), NULL, NULL, 0, 1);
-		if (ctx.cfg.root_readme)
-			site_link("about", "about", NULL, hc("about"),
-				  NULL, NULL, 0, 1);
-		html("</td><td class='form'>");
-		html("<form method='get' action='");
-		html_attr(currenturl);
-		html("'>\n");
-		html("<input type='search' name='q' size='10' value='");
-		html_attr(ctx.qry.search);
-		html("'/>\n");
-		html("<input type='submit' value='search'/>\n");
-		html("</form>");
-		free(currenturl);
-	}
-	html("</td></tr></table>\n");
-	if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) {
-		html("<div class='path'>");
-		html("path: ");
-		cgit_print_path_crumbs(ctx.qry.vpath);
-		if (ctx.cfg.enable_follow_links && !strcmp(ctx.qry.page, "log")) {
-			html(" (");
-			ctx.qry.follow = !ctx.qry.follow;
-			cgit_self_link(ctx.qry.follow ? "follow" : "unfollow",
-					NULL, NULL);
-			ctx.qry.follow = !ctx.qry.follow;
-			html(")");
-		}
-		html("</div>");
-	}
-	html("<div class='content'>");
-}
-
-void cgit_print_filemode(unsigned short mode)
-{
-	if (S_ISDIR(mode))
-		html("d");
-	else if (S_ISLNK(mode))
-		html("l");
-	else if (S_ISGITLINK(mode))
-		html("m");
-	else
-		html("-");
-	html_fileperm(mode >> 6);
-	html_fileperm(mode >> 3);
-	html_fileperm(mode);
-}
-
-void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base,
-				  const char *ref)
-{
-	struct object_id oid;
-
-	/*
-	 * Prettify snapshot names by stripping leading "v" or "V" if the tag
-	 * name starts with {v,V}[0-9] and the prettify mapping is injective,
-	 * i.e. each stripped tag can be inverted without ambiguities.
-	 */
-	if (get_oid(fmt("refs/tags/%s", ref), &oid) == 0 &&
-	    (ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]) &&
-	    ((get_oid(fmt("refs/tags/%s", ref + 1), &oid) == 0) +
-	     (get_oid(fmt("refs/tags/v%s", ref + 1), &oid) == 0) +
-	     (get_oid(fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1))
-		ref++;
-
-	strbuf_addf(filename, "%s-%s", base, ref);
-}
-
-void cgit_print_snapshot_links(const struct cgit_repo *repo, const char *ref,
-			       const char *separator)
-{
-	const struct cgit_snapshot_format *f;
-	struct strbuf filename = STRBUF_INIT;
-	const char *basename;
-	size_t prefixlen;
-
-	basename = cgit_snapshot_prefix(repo);
-	if (starts_with(ref, basename))
-		strbuf_addstr(&filename, ref);
-	else
-		cgit_compose_snapshot_prefix(&filename, basename, ref);
-
-	prefixlen = filename.len;
-	for (f = cgit_snapshot_formats; f->suffix; f++) {
-		if (!(repo->snapshots & cgit_snapshot_format_bit(f)))
-			continue;
-		strbuf_setlen(&filename, prefixlen);
-		strbuf_addstr(&filename, f->suffix);
-		cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL,
-				   filename.buf);
-		if (cgit_snapshot_get_sig(ref, f)) {
-			strbuf_addstr(&filename, ".asc");
-			html(" (");
-			cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
-					   filename.buf);
-			html(")");
-		} else if (starts_with(f->suffix, ".tar") && cgit_snapshot_get_sig(ref, &cgit_snapshot_formats[0])) {
-			strbuf_setlen(&filename, strlen(filename.buf) - strlen(f->suffix));
-			strbuf_addstr(&filename, ".tar.asc");
-			html(" (");
-			cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
-					   filename.buf);
-			html(")");
-		}
-		html(separator);
-	}
-	strbuf_release(&filename);
-}
-
-void cgit_set_title_from_path(const char *path)
-{
-	struct strbuf sb = STRBUF_INIT;
-	const char *slash, *last_slash;
-
-	if (!path)
-		return;
-
-	last_slash = path + strlen(path);
-	for (slash = last_slash; slash > path; --slash) {
-		if (*slash != '/') continue;
-		strbuf_add(&sb, slash + 1, last_slash - slash - 1);
-		strbuf_addstr(&sb, " \xc2\xab ");
-		last_slash = slash;
-	}
-	strbuf_add(&sb, path, last_slash - path);
-	strbuf_addf(&sb, " - %s", ctx.page.title);
-	ctx.page.title = strbuf_detach(&sb, NULL);
-}
diff --git a/www/git.causal.agency/cgit/ui-shared.h b/www/git.causal.agency/cgit/ui-shared.h
deleted file mode 100644
index 6964873a..00000000
--- a/www/git.causal.agency/cgit/ui-shared.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef UI_SHARED_H
-#define UI_SHARED_H
-
-extern const char *cgit_httpscheme(void);
-extern char *cgit_hosturl(void);
-extern const char *cgit_rooturl(void);
-extern char *cgit_currenturl(void);
-extern char *cgit_currentfullurl(void);
-extern const char *cgit_loginurl(void);
-extern char *cgit_repourl(const char *reponame);
-extern char *cgit_fileurl(const char *reponame, const char *pagename,
-			  const char *filename, const char *query);
-extern char *cgit_pageurl(const char *reponame, const char *pagename,
-			  const char *query);
-
-extern void cgit_add_clone_urls(void (*fn)(const char *));
-
-extern void cgit_index_link(const char *name, const char *title,
-			    const char *class, const char *pattern, const char *sort, int ofs, int always_root);
-extern void cgit_summary_link(const char *name, const char *title,
-			      const char *class, const char *head);
-extern void cgit_tag_link(const char *name, const char *title,
-			  const char *class, const char *tag);
-extern void cgit_tree_link(const char *name, const char *title,
-			   const char *class, const char *head,
-			   const char *rev, const char *path);
-extern void cgit_plain_link(const char *name, const char *title,
-			    const char *class, const char *head,
-			    const char *rev, const char *path);
-extern void cgit_blame_link(const char *name, const char *title,
-			    const char *class, const char *head,
-			    const char *rev, const char *path);
-extern void cgit_log_link(const char *name, const char *title,
-			  const char *class, const char *head, const char *rev,
-			  const char *path, int ofs, const char *grep,
-			  const char *pattern, int showmsg, int follow);
-extern void cgit_commit_link(const char *name, const char *title,
-			     const char *class, const char *head,
-			     const char *rev, const char *path);
-extern void cgit_patch_link(const char *name, const char *title,
-			    const char *class, const char *head,
-			    const char *rev, const char *path);
-extern void cgit_refs_link(const char *name, const char *title,
-			   const char *class, const char *head,
-			   const char *rev, const char *path);
-extern void cgit_snapshot_link(const char *name, const char *title,
-			       const char *class, const char *head,
-			       const char *rev, const char *archivename);
-extern void cgit_diff_link(const char *name, const char *title,
-			   const char *class, const char *head,
-			   const char *new_rev, const char *old_rev,
-			   const char *path);
-extern void cgit_stats_link(const char *name, const char *title,
-			    const char *class, const char *head,
-			    const char *path);
-extern void cgit_object_link(struct object *obj);
-
-extern void cgit_submodule_link(const char *class, char *path,
-				const char *rev);
-
-extern void cgit_print_layout_start(void);
-extern void cgit_print_layout_end(void);
-
-__attribute__((format (printf,1,2)))
-extern void cgit_print_error(const char *fmt, ...);
-__attribute__((format (printf,1,0)))
-extern void cgit_vprint_error(const char *fmt, va_list ap);
-extern const struct date_mode *cgit_date_mode(enum date_mode_type type);
-extern void cgit_print_age(time_t t, int tz, time_t max_relative);
-extern void cgit_print_http_headers(void);
-extern void cgit_redirect(const char *url, bool permanent);
-extern void cgit_print_docstart(void);
-extern void cgit_print_docend(void);
-__attribute__((format (printf,3,4)))
-extern void cgit_print_error_page(int code, const char *msg, const char *fmt, ...);
-extern void cgit_print_pageheader(void);
-extern void cgit_print_filemode(unsigned short mode);
-extern void cgit_compose_snapshot_prefix(struct strbuf *filename,
-					 const char *base, const char *ref);
-extern void cgit_print_snapshot_links(const struct cgit_repo *repo,
-				      const char *ref, const char *separator);
-extern const char *cgit_snapshot_prefix(const struct cgit_repo *repo);
-extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
-				       const char *page);
-
-extern void cgit_set_title_from_path(const char *path);
-#endif /* UI_SHARED_H */
diff --git a/www/git.causal.agency/cgit/ui-snapshot.c b/www/git.causal.agency/cgit/ui-snapshot.c
deleted file mode 100644
index 28013935..00000000
--- a/www/git.causal.agency/cgit/ui-snapshot.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/* ui-snapshot.c: generate snapshot of a commit
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-snapshot.h"
-#include "html.h"
-#include "ui-shared.h"
-
-static int write_archive_type(const char *format, const char *hex, const char *prefix)
-{
-	struct strvec argv = STRVEC_INIT;
-	const char **nargv;
-	int result;
-	strvec_push(&argv, "snapshot");
-	strvec_push(&argv, format);
-	if (prefix) {
-		struct strbuf buf = STRBUF_INIT;
-		strbuf_addstr(&buf, prefix);
-		strbuf_addch(&buf, '/');
-		strvec_push(&argv, "--prefix");
-		strvec_push(&argv, buf.buf);
-		strbuf_release(&buf);
-	}
-	strvec_push(&argv, hex);
-	/*
-	 * Now we need to copy the pointers to arguments into a new
-	 * structure because write_archive will rearrange its arguments
-	 * which may result in duplicated/missing entries causing leaks
-	 * or double-frees in strvec_clear.
-	 */
-	nargv = xmalloc(sizeof(char *) * (argv.nr + 1));
-	/* strvec guarantees a trailing NULL entry. */
-	memcpy(nargv, argv.v, sizeof(char *) * (argv.nr + 1));
-
-	if (fflush(stdout))
-		return errno;
-
-	result = write_archive(argv.nr, nargv, NULL, the_repository, NULL, 0);
-	strvec_clear(&argv);
-	free(nargv);
-	return result;
-}
-
-static int write_tar_archive(const char *hex, const char *prefix)
-{
-	return write_archive_type("--format=tar", hex, prefix);
-}
-
-static int write_zip_archive(const char *hex, const char *prefix)
-{
-	return write_archive_type("--format=zip", hex, prefix);
-}
-
-static int write_compressed_tar_archive(const char *hex,
-					const char *prefix,
-					char *filter_argv[])
-{
-	int rv;
-	struct cgit_exec_filter f;
-	cgit_exec_filter_init(&f, filter_argv[0], filter_argv);
-
-	cgit_open_filter(&f.base);
-	rv = write_tar_archive(hex, prefix);
-	cgit_close_filter(&f.base);
-	return rv;
-}
-
-static int write_tar_gzip_archive(const char *hex, const char *prefix)
-{
-	char *argv[] = { "gzip", "-n", NULL };
-	return write_compressed_tar_archive(hex, prefix, argv);
-}
-
-static int write_tar_bzip2_archive(const char *hex, const char *prefix)
-{
-	char *argv[] = { "bzip2", NULL };
-	return write_compressed_tar_archive(hex, prefix, argv);
-}
-
-static int write_tar_lzip_archive(const char *hex, const char *prefix)
-{
-	char *argv[] = { "lzip", NULL };
-	return write_compressed_tar_archive(hex, prefix, argv);
-}
-
-static int write_tar_xz_archive(const char *hex, const char *prefix)
-{
-	char *argv[] = { "xz", NULL };
-	return write_compressed_tar_archive(hex, prefix, argv);
-}
-
-static int write_tar_zstd_archive(const char *hex, const char *prefix)
-{
-	char *argv[] = { "zstd", "-T0", NULL };
-	return write_compressed_tar_archive(hex, prefix, argv);
-}
-
-const struct cgit_snapshot_format cgit_snapshot_formats[] = {
-	/* .tar must remain the 0 index */
-	{ ".tar",	"application/x-tar",	write_tar_archive	},
-	{ ".tar.gz",	"application/x-gzip",	write_tar_gzip_archive	},
-	{ ".tar.bz2",	"application/x-bzip2",	write_tar_bzip2_archive	},
-	{ ".tar.lz",	"application/x-lzip",	write_tar_lzip_archive	},
-	{ ".tar.xz",	"application/x-xz",	write_tar_xz_archive	},
-	{ ".tar.zst",	"application/x-zstd",	write_tar_zstd_archive	},
-	{ ".zip",	"application/x-zip",	write_zip_archive	},
-	{ NULL }
-};
-
-static struct notes_tree snapshot_sig_notes[ARRAY_SIZE(cgit_snapshot_formats)];
-
-const struct object_id *cgit_snapshot_get_sig(const char *ref,
-					      const struct cgit_snapshot_format *f)
-{
-	struct notes_tree *tree;
-	struct object_id oid;
-
-	if (get_oid(ref, &oid))
-		return NULL;
-
-	tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]];
-	if (!tree->initialized) {
-		struct strbuf notes_ref = STRBUF_INIT;
-
-		strbuf_addf(&notes_ref, "refs/notes/signatures/%s",
-			    f->suffix + 1);
-
-		init_notes(tree, notes_ref.buf, combine_notes_ignore, 0);
-		strbuf_release(&notes_ref);
-	}
-
-	return get_note(tree, &oid);
-}
-
-static const struct cgit_snapshot_format *get_format(const char *filename)
-{
-	const struct cgit_snapshot_format *fmt;
-
-	for (fmt = cgit_snapshot_formats; fmt->suffix; fmt++) {
-		if (ends_with(filename, fmt->suffix))
-			return fmt;
-	}
-	return NULL;
-}
-
-const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f)
-{
-	return BIT(f - &cgit_snapshot_formats[0]);
-}
-
-static int make_snapshot(const struct cgit_snapshot_format *format,
-			 const char *hex, const char *prefix,
-			 const char *filename)
-{
-	struct object_id oid;
-
-	if (get_oid(hex, &oid)) {
-		cgit_print_error_page(404, "Not found",
-				"Bad object id: %s", hex);
-		return 1;
-	}
-	if (!lookup_commit_reference(the_repository, &oid)) {
-		cgit_print_error_page(400, "Bad request",
-				"Not a commit reference: %s", hex);
-		return 1;
-	}
-	ctx.page.etag = oid_to_hex(&oid);
-	ctx.page.mimetype = xstrdup(format->mimetype);
-	ctx.page.filename = xstrdup(filename);
-	cgit_print_http_headers();
-	init_archivers();
-	format->write_func(hex, prefix);
-	return 0;
-}
-
-static int write_sig(const struct cgit_snapshot_format *format,
-		     const char *hex, const char *archive,
-		     const char *filename)
-{
-	const struct object_id *note = cgit_snapshot_get_sig(hex, format);
-	enum object_type type;
-	unsigned long size;
-	char *buf;
-
-	if (!note) {
-		cgit_print_error_page(404, "Not found",
-				"No signature for %s", archive);
-		return 0;
-	}
-
-	buf = read_object_file(note, &type, &size);
-	if (!buf) {
-		cgit_print_error_page(404, "Not found", "Not found");
-		return 0;
-	}
-
-	html("X-Content-Type-Options: nosniff\n");
-	html("Content-Security-Policy: default-src 'none'\n");
-	ctx.page.etag = oid_to_hex(note);
-	ctx.page.mimetype = xstrdup("application/pgp-signature");
-	ctx.page.filename = xstrdup(filename);
-	cgit_print_http_headers();
-
-	html_raw(buf, size);
-	free(buf);
-	return 0;
-}
-
-/* Try to guess the requested revision from the requested snapshot name.
- * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become
- * "cgit-0.7.2". If this is a valid commit object name we've got a winner.
- * Otherwise, if the snapshot name has a prefix matching the result from
- * repo_basename(), we strip the basename and any following '-' and '_'
- * characters ("cgit-0.7.2" -> "0.7.2") and check the resulting name once
- * more. If this still isn't a valid commit object name, we check if pre-
- * pending a 'v' or a 'V' to the remaining snapshot name ("0.7.2" ->
- * "v0.7.2") gives us something valid.
- */
-static const char *get_ref_from_filename(const struct cgit_repo *repo,
-					 const char *filename,
-					 const struct cgit_snapshot_format *format)
-{
-	const char *reponame;
-	struct object_id oid;
-	struct strbuf snapshot = STRBUF_INIT;
-	int result = 1;
-
-	strbuf_addstr(&snapshot, filename);
-	strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix));
-
-	if (get_oid(snapshot.buf, &oid) == 0)
-		goto out;
-
-	reponame = cgit_snapshot_prefix(repo);
-	if (starts_with(snapshot.buf, reponame)) {
-		const char *new_start = snapshot.buf;
-		new_start += strlen(reponame);
-		while (new_start && (*new_start == '-' || *new_start == '_'))
-			new_start++;
-		strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0);
-	}
-
-	if (get_oid(snapshot.buf, &oid) == 0)
-		goto out;
-
-	strbuf_insert(&snapshot, 0, "v", 1);
-	if (get_oid(snapshot.buf, &oid) == 0)
-		goto out;
-
-	strbuf_splice(&snapshot, 0, 1, "V", 1);
-	if (get_oid(snapshot.buf, &oid) == 0)
-		goto out;
-
-	result = 0;
-	strbuf_release(&snapshot);
-
-out:
-	return result ? strbuf_detach(&snapshot, NULL) : NULL;
-}
-
-void cgit_print_snapshot(const char *head, const char *hex,
-			 const char *filename, int dwim)
-{
-	const struct cgit_snapshot_format* f;
-	const char *sig_filename = NULL;
-	char *adj_filename = NULL;
-	char *prefix = NULL;
-
-	if (!filename) {
-		cgit_print_error_page(400, "Bad request",
-				"No snapshot name specified");
-		return;
-	}
-
-	if (ends_with(filename, ".asc")) {
-		sig_filename = filename;
-
-		/* Strip ".asc" from filename for common format processing */
-		adj_filename = xstrdup(filename);
-		adj_filename[strlen(adj_filename) - 4] = '\0';
-		filename = adj_filename;
-	}
-
-	f = get_format(filename);
-	if (!f || (!sig_filename && !(ctx.repo->snapshots & cgit_snapshot_format_bit(f)))) {
-		cgit_print_error_page(400, "Bad request",
-				"Unsupported snapshot format: %s", filename);
-		return;
-	}
-
-	if (!hex && dwim) {
-		hex = get_ref_from_filename(ctx.repo, filename, f);
-		if (hex == NULL) {
-			cgit_print_error_page(404, "Not found", "Not found");
-			return;
-		}
-		prefix = xstrdup(filename);
-		prefix[strlen(filename) - strlen(f->suffix)] = '\0';
-	}
-
-	if (!hex)
-		hex = head;
-
-	if (!prefix)
-		prefix = xstrdup(cgit_snapshot_prefix(ctx.repo));
-
-	if (sig_filename)
-		write_sig(f, hex, filename, sig_filename);
-	else
-		make_snapshot(f, hex, prefix, filename);
-
-	free(prefix);
-	free(adj_filename);
-}
diff --git a/www/git.causal.agency/cgit/ui-snapshot.h b/www/git.causal.agency/cgit/ui-snapshot.h
deleted file mode 100644
index a8deec36..00000000
--- a/www/git.causal.agency/cgit/ui-snapshot.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef UI_SNAPSHOT_H
-#define UI_SNAPSHOT_H
-
-extern void cgit_print_snapshot(const char *head, const char *hex,
-				const char *filename, int dwim);
-
-#endif /* UI_SNAPSHOT_H */
diff --git a/www/git.causal.agency/cgit/ui-ssdiff.c b/www/git.causal.agency/cgit/ui-ssdiff.c
deleted file mode 100644
index af8bc9e0..00000000
--- a/www/git.causal.agency/cgit/ui-ssdiff.c
+++ /dev/null
@@ -1,420 +0,0 @@
-#include "cgit.h"
-#include "ui-ssdiff.h"
-#include "html.h"
-#include "ui-shared.h"
-#include "ui-diff.h"
-
-extern int use_ssdiff;
-
-static int current_old_line, current_new_line;
-static int **L = NULL;
-
-struct deferred_lines {
-	int line_no;
-	char *line;
-	struct deferred_lines *next;
-};
-
-static struct deferred_lines *deferred_old, *deferred_old_last;
-static struct deferred_lines *deferred_new, *deferred_new_last;
-
-static void create_or_reset_lcs_table(void)
-{
-	int i;
-
-	if (L != NULL) {
-		memset(*L, 0, sizeof(int) * MAX_SSDIFF_SIZE);
-		return;
-	}
-
-	// xcalloc will die if we ran out of memory;
-	// not very helpful for debugging
-	L = (int**)xcalloc(MAX_SSDIFF_M, sizeof(int *));
-	*L = (int*)xcalloc(MAX_SSDIFF_SIZE, sizeof(int));
-
-	for (i = 1; i < MAX_SSDIFF_M; i++) {
-		L[i] = *L + i * MAX_SSDIFF_N;
-	}
-}
-
-static char *longest_common_subsequence(char *A, char *B)
-{
-	int i, j, ri;
-	int m = strlen(A);
-	int n = strlen(B);
-	int tmp1, tmp2;
-	int lcs_length;
-	char *result;
-
-	// We bail if the lines are too long
-	if (m >= MAX_SSDIFF_M || n >= MAX_SSDIFF_N)
-		return NULL;
-
-	create_or_reset_lcs_table();
-
-	for (i = m; i >= 0; i--) {
-		for (j = n; j >= 0; j--) {
-			if (A[i] == '\0' || B[j] == '\0') {
-				L[i][j] = 0;
-			} else if (A[i] == B[j]) {
-				L[i][j] = 1 + L[i + 1][j + 1];
-			} else {
-				tmp1 = L[i + 1][j];
-				tmp2 = L[i][j + 1];
-				L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
-			}
-		}
-	}
-
-	lcs_length = L[0][0];
-	result = xmalloc(lcs_length + 2);
-	memset(result, 0, sizeof(*result) * (lcs_length + 2));
-
-	ri = 0;
-	i = 0;
-	j = 0;
-	while (i < m && j < n) {
-		if (A[i] == B[j]) {
-			result[ri] = A[i];
-			ri += 1;
-			i += 1;
-			j += 1;
-		} else if (L[i + 1][j] >= L[i][j + 1]) {
-			i += 1;
-		} else {
-			j += 1;
-		}
-	}
-
-	return result;
-}
-
-static int line_from_hunk(char *line, char type)
-{
-	char *buf1, *buf2;
-	int len, res;
-
-	buf1 = strchr(line, type);
-	if (buf1 == NULL)
-		return 0;
-	buf1 += 1;
-	buf2 = strchr(buf1, ',');
-	if (buf2 == NULL)
-		return 0;
-	len = buf2 - buf1;
-	buf2 = xmalloc(len + 1);
-	strlcpy(buf2, buf1, len + 1);
-	res = atoi(buf2);
-	free(buf2);
-	return res;
-}
-
-static char *replace_tabs(char *line)
-{
-	char *prev_buf = line;
-	char *cur_buf;
-	size_t linelen = strlen(line);
-	int n_tabs = 0;
-	int i;
-	char *result;
-	size_t result_len;
-
-	if (linelen == 0) {
-		result = xmalloc(1);
-		result[0] = '\0';
-		return result;
-	}
-
-	for (i = 0; i < linelen; i++) {
-		if (line[i] == '\t')
-			n_tabs += 1;
-	}
-	result_len = linelen + n_tabs * 8;
-	result = xmalloc(result_len + 1);
-	result[0] = '\0';
-
-	for (;;) {
-		cur_buf = strchr(prev_buf, '\t');
-		if (!cur_buf) {
-			linelen = strlen(result);
-			strlcpy(&result[linelen], prev_buf, result_len - linelen + 1);
-			break;
-		} else {
-			linelen = strlen(result);
-			strlcpy(&result[linelen], prev_buf, cur_buf - prev_buf + 1);
-			linelen = strlen(result);
-			memset(&result[linelen], ' ', 8 - (linelen % 8));
-			result[linelen + 8 - (linelen % 8)] = '\0';
-		}
-		prev_buf = cur_buf + 1;
-	}
-	return result;
-}
-
-static int calc_deferred_lines(struct deferred_lines *start)
-{
-	struct deferred_lines *item = start;
-	int result = 0;
-	while (item) {
-		result += 1;
-		item = item->next;
-	}
-	return result;
-}
-
-static void deferred_old_add(char *line, int line_no)
-{
-	struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
-	item->line = xstrdup(line);
-	item->line_no = line_no;
-	item->next = NULL;
-	if (deferred_old) {
-		deferred_old_last->next = item;
-		deferred_old_last = item;
-	} else {
-		deferred_old = deferred_old_last = item;
-	}
-}
-
-static void deferred_new_add(char *line, int line_no)
-{
-	struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
-	item->line = xstrdup(line);
-	item->line_no = line_no;
-	item->next = NULL;
-	if (deferred_new) {
-		deferred_new_last->next = item;
-		deferred_new_last = item;
-	} else {
-		deferred_new = deferred_new_last = item;
-	}
-}
-
-static void print_part_with_lcs(char *class, char *line, char *lcs)
-{
-	int line_len = strlen(line);
-	int i, j;
-	char c[2] = " ";
-	int same = 1;
-
-	j = 0;
-	for (i = 0; i < line_len; i++) {
-		c[0] = line[i];
-		if (same) {
-			if (line[i] == lcs[j])
-				j += 1;
-			else {
-				same = 0;
-				htmlf("<span class='%s'>", class);
-			}
-		} else if (line[i] == lcs[j]) {
-			same = 1;
-			html("</span>");
-			j += 1;
-		}
-		html_txt(c);
-	}
-	if (!same)
-		html("</span>");
-}
-
-static void print_ssdiff_line(char *class,
-			      int old_line_no,
-			      char *old_line,
-			      int new_line_no,
-			      char *new_line, int individual_chars)
-{
-	char *lcs = NULL;
-
-	if (old_line)
-		old_line = replace_tabs(old_line + 1);
-	if (new_line)
-		new_line = replace_tabs(new_line + 1);
-	if (individual_chars && old_line && new_line)
-		lcs = longest_common_subsequence(old_line, new_line);
-	html("<tr>\n");
-	if (old_line_no > 0) {
-		struct diff_filespec *old_file = cgit_get_current_old_file();
-		char *lineno_str = fmt("n%d", old_line_no);
-		char *id_str = fmt("id=%s#%s", is_null_oid(&old_file->oid)?"HEAD":oid_to_hex(old_rev_oid), lineno_str);
-		char *fileurl = cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str);
-		html("<td class='lineno'><a href='");
-		html(fileurl);
-		htmlf("'>%s</a>", lineno_str + 1);
-		html("</td>");
-		htmlf("<td class='%s'>", class);
-		free(fileurl);
-	} else if (old_line)
-		htmlf("<td class='lineno'></td><td class='%s'>", class);
-	else
-		htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
-	if (old_line) {
-		if (lcs)
-			print_part_with_lcs("del", old_line, lcs);
-		else
-			html_txt(old_line);
-	}
-
-	html("</td>\n");
-	if (new_line_no > 0) {
-		struct diff_filespec *new_file = cgit_get_current_new_file();
-		char *lineno_str = fmt("n%d", new_line_no);
-		char *id_str = fmt("id=%s#%s", is_null_oid(&new_file->oid)?"HEAD":oid_to_hex(new_rev_oid), lineno_str);
-		char *fileurl = cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str);
-		html("<td class='lineno'><a href='");
-		html(fileurl);
-		htmlf("'>%s</a>", lineno_str + 1);
-		html("</td>");
-		htmlf("<td class='%s'>", class);
-		free(fileurl);
-	} else if (new_line)
-		htmlf("<td class='lineno'></td><td class='%s'>", class);
-	else
-		htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
-	if (new_line) {
-		if (lcs)
-			print_part_with_lcs("add", new_line, lcs);
-		else
-			html_txt(new_line);
-	}
-
-	html("</td></tr>");
-	if (lcs)
-		free(lcs);
-	if (new_line)
-		free(new_line);
-	if (old_line)
-		free(old_line);
-}
-
-static void print_deferred_old_lines(void)
-{
-	struct deferred_lines *iter_old, *tmp;
-	iter_old = deferred_old;
-	while (iter_old) {
-		print_ssdiff_line("del", iter_old->line_no,
-				  iter_old->line, -1, NULL, 0);
-		tmp = iter_old->next;
-		free(iter_old);
-		iter_old = tmp;
-	}
-}
-
-static void print_deferred_new_lines(void)
-{
-	struct deferred_lines *iter_new, *tmp;
-	iter_new = deferred_new;
-	while (iter_new) {
-		print_ssdiff_line("add", -1, NULL,
-				  iter_new->line_no, iter_new->line, 0);
-		tmp = iter_new->next;
-		free(iter_new);
-		iter_new = tmp;
-	}
-}
-
-static void print_deferred_changed_lines(void)
-{
-	struct deferred_lines *iter_old, *iter_new, *tmp;
-	int n_old_lines = calc_deferred_lines(deferred_old);
-	int n_new_lines = calc_deferred_lines(deferred_new);
-	int individual_chars = (n_old_lines == n_new_lines ? 1 : 0);
-
-	iter_old = deferred_old;
-	iter_new = deferred_new;
-	while (iter_old || iter_new) {
-		if (iter_old && iter_new)
-			print_ssdiff_line("changed", iter_old->line_no,
-					  iter_old->line,
-					  iter_new->line_no, iter_new->line,
-					  individual_chars);
-		else if (iter_old)
-			print_ssdiff_line("changed", iter_old->line_no,
-					  iter_old->line, -1, NULL, 0);
-		else if (iter_new)
-			print_ssdiff_line("changed", -1, NULL,
-					  iter_new->line_no, iter_new->line, 0);
-		if (iter_old) {
-			tmp = iter_old->next;
-			free(iter_old);
-			iter_old = tmp;
-		}
-
-		if (iter_new) {
-			tmp = iter_new->next;
-			free(iter_new);
-			iter_new = tmp;
-		}
-	}
-}
-
-void cgit_ssdiff_print_deferred_lines(void)
-{
-	if (!deferred_old && !deferred_new)
-		return;
-	if (deferred_old && !deferred_new)
-		print_deferred_old_lines();
-	else if (!deferred_old && deferred_new)
-		print_deferred_new_lines();
-	else
-		print_deferred_changed_lines();
-	deferred_old = deferred_old_last = NULL;
-	deferred_new = deferred_new_last = NULL;
-}
-
-/*
- * print a single line returned from xdiff
- */
-void cgit_ssdiff_line_cb(char *line, int len)
-{
-	char c = line[len - 1];
-	line[len - 1] = '\0';
-	if (line[0] == '@') {
-		current_old_line = line_from_hunk(line, '-');
-		current_new_line = line_from_hunk(line, '+');
-	}
-
-	if (line[0] == ' ') {
-		if (deferred_old || deferred_new)
-			cgit_ssdiff_print_deferred_lines();
-		print_ssdiff_line("ctx", current_old_line, line,
-				  current_new_line, line, 0);
-		current_old_line += 1;
-		current_new_line += 1;
-	} else if (line[0] == '+') {
-		deferred_new_add(line, current_new_line);
-		current_new_line += 1;
-	} else if (line[0] == '-') {
-		deferred_old_add(line, current_old_line);
-		current_old_line += 1;
-	} else if (line[0] == '@') {
-		html("<tr><td colspan='4' class='hunk'>");
-		html_txt(line);
-		html("</td></tr>");
-	} else {
-		html("<tr><td colspan='4' class='ctx'>");
-		html_txt(line);
-		html("</td></tr>");
-	}
-	line[len - 1] = c;
-}
-
-void cgit_ssdiff_header_begin(void)
-{
-	current_old_line = -1;
-	current_new_line = -1;
-	html("<tr><td class='space' colspan='4'><div></div></td></tr>");
-	html("<tr><td class='head' colspan='4'>");
-}
-
-void cgit_ssdiff_header_end(void)
-{
-	html("</td></tr>");
-}
-
-void cgit_ssdiff_footer(void)
-{
-	if (deferred_old || deferred_new)
-		cgit_ssdiff_print_deferred_lines();
-	html("<tr><td class='foot' colspan='4'></td></tr>");
-}
diff --git a/www/git.causal.agency/cgit/ui-ssdiff.h b/www/git.causal.agency/cgit/ui-ssdiff.h
deleted file mode 100644
index 11f27144..00000000
--- a/www/git.causal.agency/cgit/ui-ssdiff.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef UI_SSDIFF_H
-#define UI_SSDIFF_H
-
-/*
- * ssdiff line limits
- */
-#ifndef MAX_SSDIFF_M
-#define MAX_SSDIFF_M 128
-#endif
-
-#ifndef MAX_SSDIFF_N
-#define MAX_SSDIFF_N 128
-#endif
-#define MAX_SSDIFF_SIZE ((MAX_SSDIFF_M) * (MAX_SSDIFF_N))
-
-extern void cgit_ssdiff_print_deferred_lines(void);
-
-extern void cgit_ssdiff_line_cb(char *line, int len);
-
-extern void cgit_ssdiff_header_begin(void);
-extern void cgit_ssdiff_header_end(void);
-
-extern void cgit_ssdiff_footer(void);
-
-#endif /* UI_SSDIFF_H */
diff --git a/www/git.causal.agency/cgit/ui-stats.c b/www/git.causal.agency/cgit/ui-stats.c
deleted file mode 100644
index 09b3625e..00000000
--- a/www/git.causal.agency/cgit/ui-stats.c
+++ /dev/null
@@ -1,426 +0,0 @@
-#include "cgit.h"
-#include "ui-stats.h"
-#include "html.h"
-#include "ui-shared.h"
-
-struct authorstat {
-	long total;
-	struct string_list list;
-};
-
-#define DAY_SECS (60 * 60 * 24)
-#define WEEK_SECS (DAY_SECS * 7)
-
-static void trunc_week(struct tm *tm)
-{
-	time_t t = timegm(tm);
-	t -= ((tm->tm_wday + 6) % 7) * DAY_SECS;
-	gmtime_r(&t, tm);
-}
-
-static void dec_week(struct tm *tm)
-{
-	time_t t = timegm(tm);
-	t -= WEEK_SECS;
-	gmtime_r(&t, tm);
-}
-
-static void inc_week(struct tm *tm)
-{
-	time_t t = timegm(tm);
-	t += WEEK_SECS;
-	gmtime_r(&t, tm);
-}
-
-static char *pretty_week(struct tm *tm)
-{
-	static char buf[10];
-
-	strftime(buf, sizeof(buf), "W%V %G", tm);
-	return buf;
-}
-
-static void trunc_month(struct tm *tm)
-{
-	tm->tm_mday = 1;
-}
-
-static void dec_month(struct tm *tm)
-{
-	tm->tm_mon--;
-	if (tm->tm_mon < 0) {
-		tm->tm_year--;
-		tm->tm_mon = 11;
-	}
-}
-
-static void inc_month(struct tm *tm)
-{
-	tm->tm_mon++;
-	if (tm->tm_mon > 11) {
-		tm->tm_year++;
-		tm->tm_mon = 0;
-	}
-}
-
-static char *pretty_month(struct tm *tm)
-{
-	static const char *months[] = {
-		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-	};
-	return fmt("%s %d", months[tm->tm_mon], tm->tm_year + 1900);
-}
-
-static void trunc_quarter(struct tm *tm)
-{
-	trunc_month(tm);
-	while (tm->tm_mon % 3 != 0)
-		dec_month(tm);
-}
-
-static void dec_quarter(struct tm *tm)
-{
-	dec_month(tm);
-	dec_month(tm);
-	dec_month(tm);
-}
-
-static void inc_quarter(struct tm *tm)
-{
-	inc_month(tm);
-	inc_month(tm);
-	inc_month(tm);
-}
-
-static char *pretty_quarter(struct tm *tm)
-{
-	return fmt("Q%d %d", tm->tm_mon / 3 + 1, tm->tm_year + 1900);
-}
-
-static void trunc_year(struct tm *tm)
-{
-	trunc_month(tm);
-	tm->tm_mon = 0;
-}
-
-static void dec_year(struct tm *tm)
-{
-	tm->tm_year--;
-}
-
-static void inc_year(struct tm *tm)
-{
-	tm->tm_year++;
-}
-
-static char *pretty_year(struct tm *tm)
-{
-	return fmt("%d", tm->tm_year + 1900);
-}
-
-static const struct cgit_period periods[] = {
-	{'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week},
-	{'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month},
-	{'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter},
-	{'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year},
-};
-
-/* Given a period code or name, return a period index (1, 2, 3 or 4)
- * and update the period pointer to the correcsponding struct.
- * If no matching code is found, return 0.
- */
-int cgit_find_stats_period(const char *expr, const struct cgit_period **period)
-{
-	int i;
-	char code = '\0';
-
-	if (!expr)
-		return 0;
-
-	if (strlen(expr) == 1)
-		code = expr[0];
-
-	for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++)
-		if (periods[i].code == code || !strcmp(periods[i].name, expr)) {
-			if (period)
-				*period = &periods[i];
-			return i + 1;
-		}
-	return 0;
-}
-
-const char *cgit_find_stats_periodname(int idx)
-{
-	if (idx > 0 && idx < 4)
-		return periods[idx - 1].name;
-	else
-		return "";
-}
-
-static void add_commit(struct string_list *authors, struct commit *commit,
-	const struct cgit_period *period)
-{
-	struct commitinfo *info;
-	struct string_list_item *author, *item;
-	struct authorstat *authorstat;
-	struct string_list *items;
-	char *tmp;
-	struct tm date;
-	time_t t;
-	uintptr_t *counter;
-
-	info = cgit_parse_commit(commit);
-	tmp = xstrdup(info->author);
-	author = string_list_insert(authors, tmp);
-	if (!author->util)
-		author->util = xcalloc(1, sizeof(struct authorstat));
-	else
-		free(tmp);
-	authorstat = author->util;
-	items = &authorstat->list;
-	t = info->committer_date;
-	gmtime_r(&t, &date);
-	period->trunc(&date);
-	tmp = xstrdup(period->pretty(&date));
-	item = string_list_insert(items, tmp);
-	counter = (uintptr_t *)&item->util;
-	if (*counter)
-		free(tmp);
-	(*counter)++;
-
-	authorstat->total++;
-	cgit_free_commitinfo(info);
-}
-
-static int cmp_total_commits(const void *a1, const void *a2)
-{
-	const struct string_list_item *i1 = a1;
-	const struct string_list_item *i2 = a2;
-	const struct authorstat *auth1 = i1->util;
-	const struct authorstat *auth2 = i2->util;
-
-	return auth2->total - auth1->total;
-}
-
-/* Walk the commit DAG and collect number of commits per author per
- * timeperiod into a nested string_list collection.
- */
-static struct string_list collect_stats(const struct cgit_period *period)
-{
-	struct string_list authors;
-	struct rev_info rev;
-	struct commit *commit;
-	const char *argv[] = {NULL, ctx.qry.head, NULL, NULL, NULL, NULL};
-	int argc = 3;
-	time_t now;
-	long i;
-	struct tm tm;
-	char tmp[11];
-
-	time(&now);
-	gmtime_r(&now, &tm);
-	period->trunc(&tm);
-	for (i = 1; i < period->count; i++)
-		period->dec(&tm);
-	strftime(tmp, sizeof(tmp), "%Y-%m-%d", &tm);
-	argv[2] = xstrdup(fmt("--since=%s", tmp));
-	if (ctx.qry.path) {
-		argv[3] = "--";
-		argv[4] = ctx.qry.path;
-		argc += 2;
-	}
-	init_revisions(&rev, NULL);
-	rev.abbrev = DEFAULT_ABBREV;
-	rev.commit_format = CMIT_FMT_DEFAULT;
-	rev.max_parents = 1;
-	rev.verbose_header = 1;
-	rev.show_root_diff = 0;
-	setup_revisions(argc, argv, &rev, NULL);
-	prepare_revision_walk(&rev);
-	memset(&authors, 0, sizeof(authors));
-	while ((commit = get_revision(&rev)) != NULL) {
-		add_commit(&authors, commit, period);
-		free_commit_buffer(the_repository->parsed_objects, commit);
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
-	return authors;
-}
-
-static void print_combined_authorrow(struct string_list *authors, int from,
-				     int to, const char *name,
-				     const char *leftclass,
-				     const char *centerclass,
-				     const char *rightclass,
-				     const struct cgit_period *period)
-{
-	struct string_list_item *author;
-	struct authorstat *authorstat;
-	struct string_list *items;
-	struct string_list_item *date;
-	time_t now;
-	long i, j, total, subtotal;
-	struct tm tm;
-	char *tmp;
-
-	time(&now);
-	gmtime_r(&now, &tm);
-	period->trunc(&tm);
-	for (i = 1; i < period->count; i++)
-		period->dec(&tm);
-
-	total = 0;
-	htmlf("<tr><td class='%s'>%s</td>", leftclass,
-		fmt(name, to - from + 1));
-	for (j = 0; j < period->count; j++) {
-		tmp = period->pretty(&tm);
-		period->inc(&tm);
-		subtotal = 0;
-		for (i = from; i <= to; i++) {
-			author = &authors->items[i];
-			authorstat = author->util;
-			items = &authorstat->list;
-			date = string_list_lookup(items, tmp);
-			if (date)
-				subtotal += (uintptr_t)date->util;
-		}
-		htmlf("<td class='%s'>%ld</td>", centerclass, subtotal);
-		total += subtotal;
-	}
-	htmlf("<td class='%s'>%ld</td></tr>", rightclass, total);
-}
-
-static void print_authors(struct string_list *authors, int top,
-			  const struct cgit_period *period)
-{
-	struct string_list_item *author;
-	struct authorstat *authorstat;
-	struct string_list *items;
-	struct string_list_item *date;
-	time_t now;
-	long i, j, total;
-	struct tm tm;
-	char *tmp;
-
-	time(&now);
-	gmtime_r(&now, &tm);
-	period->trunc(&tm);
-	for (i = 1; i < period->count; i++)
-		period->dec(&tm);
-
-	html("<table class='stats'><tr><th>Author</th>");
-	for (j = 0; j < period->count; j++) {
-		tmp = period->pretty(&tm);
-		htmlf("<th>%s</th>", tmp);
-		period->inc(&tm);
-	}
-	html("<th>Total</th></tr>\n");
-
-	if (top <= 0 || top > authors->nr)
-		top = authors->nr;
-
-	for (i = 0; i < top; i++) {
-		author = &authors->items[i];
-		html("<tr><td class='left'>");
-		html_txt(author->string);
-		html("</td>");
-		authorstat = author->util;
-		items = &authorstat->list;
-		total = 0;
-		for (j = 0; j < period->count; j++)
-			period->dec(&tm);
-		for (j = 0; j < period->count; j++) {
-			tmp = period->pretty(&tm);
-			period->inc(&tm);
-			date = string_list_lookup(items, tmp);
-			if (!date)
-				html("<td>0</td>");
-			else {
-				htmlf("<td>%lu</td>", (uintptr_t)date->util);
-				total += (uintptr_t)date->util;
-			}
-		}
-		htmlf("<td class='sum'>%ld</td></tr>", total);
-	}
-
-	if (top < authors->nr)
-		print_combined_authorrow(authors, top, authors->nr - 1,
-			"Others (%ld)", "left", "", "sum", period);
-
-	print_combined_authorrow(authors, 0, authors->nr - 1, "Total",
-		"total", "sum", "sum", period);
-	html("</table>");
-}
-
-/* Create a sorted string_list with one entry per author. The util-field
- * for each author is another string_list which is used to calculate the
- * number of commits per time-interval.
- */
-void cgit_show_stats(void)
-{
-	struct string_list authors;
-	const struct cgit_period *period;
-	int top, i;
-	const char *code = "w";
-
-	if (ctx.qry.period)
-		code = ctx.qry.period;
-
-	i = cgit_find_stats_period(code, &period);
-	if (!i) {
-		cgit_print_error_page(404, "Not found",
-			"Unknown statistics type: %c", code[0]);
-		return;
-	}
-	if (i > ctx.repo->max_stats) {
-		cgit_print_error_page(400, "Bad request",
-			"Statistics type disabled: %s", period->name);
-		return;
-	}
-	authors = collect_stats(period);
-	qsort(authors.items, authors.nr, sizeof(struct string_list_item),
-		cmp_total_commits);
-
-	top = ctx.qry.ofs;
-	if (!top)
-		top = 10;
-
-	cgit_print_layout_start();
-	html("<div class='cgit-panel'>");
-	html("<b>stat options</b>");
-	html("<form method='get'>");
-	cgit_add_hidden_formfields(1, 0, "stats");
-	html("<table><tr><td colspan='2'/></tr>");
-	if (ctx.repo->max_stats > 1) {
-		html("<tr><td class='label'>Period:</td>");
-		html("<td class='ctrl'><select name='period' onchange='this.form.submit();'>");
-		for (i = 0; i < ctx.repo->max_stats; i++)
-			html_option(fmt("%c", periods[i].code),
-				    periods[i].name, fmt("%c", period->code));
-		html("</select></td></tr>");
-	}
-	html("<tr><td class='label'>Authors:</td>");
-	html("<td class='ctrl'><select name='ofs' onchange='this.form.submit();'>");
-	html_intoption(10, "10", top);
-	html_intoption(25, "25", top);
-	html_intoption(50, "50", top);
-	html_intoption(100, "100", top);
-	html_intoption(-1, "all", top);
-	html("</select></td></tr>");
-	html("<tr><td/><td class='ctrl'>");
-	html("<noscript><input type='submit' value='Reload'/></noscript>");
-	html("</td></tr></table>");
-	html("</form>");
-	html("</div>");
-	htmlf("<h2>Commits per author per %s", period->name);
-	if (ctx.qry.path) {
-		html(" (path '");
-		html_txt(ctx.qry.path);
-		html("')");
-	}
-	html("</h2>");
-	print_authors(&authors, top, period);
-	cgit_print_layout_end();
-}
-
diff --git a/www/git.causal.agency/cgit/ui-stats.h b/www/git.causal.agency/cgit/ui-stats.h
deleted file mode 100644
index 0e61b03d..00000000
--- a/www/git.causal.agency/cgit/ui-stats.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef UI_STATS_H
-#define UI_STATS_H
-
-#include "cgit.h"
-
-struct cgit_period {
-	const char code;
-	const char *name;
-	int max_periods;
-	int count;
-
-	/* Convert a tm value to the first day in the period */
-	void (*trunc)(struct tm *tm);
-
-	/* Update tm value to start of next/previous period */
-	void (*dec)(struct tm *tm);
-	void (*inc)(struct tm *tm);
-
-	/* Pretty-print a tm value */
-	char *(*pretty)(struct tm *tm);
-};
-
-extern int cgit_find_stats_period(const char *expr, const struct cgit_period **period);
-extern const char *cgit_find_stats_periodname(int idx);
-
-extern void cgit_show_stats(void);
-
-#endif /* UI_STATS_H */
diff --git a/www/git.causal.agency/cgit/ui-summary.c b/www/git.causal.agency/cgit/ui-summary.c
deleted file mode 100644
index df7f739a..00000000
--- a/www/git.causal.agency/cgit/ui-summary.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/* ui-summary.c: functions for generating repo summary page
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-summary.h"
-#include "html.h"
-#include "ui-blob.h"
-#include "ui-log.h"
-#include "ui-plain.h"
-#include "ui-refs.h"
-#include "ui-shared.h"
-
-static int urls;
-
-static void print_url(const char *url)
-{
-	int columns = 3;
-
-	if (ctx.repo->enable_log_filecount)
-		columns++;
-	if (ctx.repo->enable_log_linecount)
-		columns++;
-
-	if (urls++ == 0) {
-		htmlf("<tr class='nohover'><td colspan='%d'>&nbsp;</td></tr>", columns);
-		htmlf("<tr class='nohover'><th class='left' colspan='%d'>Clone</th></tr>\n", columns);
-	}
-
-	htmlf("<tr><td colspan='%d'><a rel='vcs-git' href='", columns);
-	html_url_path(url);
-	html("' title='");
-	html_attr(ctx.repo->name);
-	html(" Git repository'>");
-	html_txt(url);
-	html("</a></td></tr>\n");
-}
-
-void cgit_print_summary(void)
-{
-	int columns = 3;
-
-	if (ctx.repo->enable_log_filecount)
-		columns++;
-	if (ctx.repo->enable_log_linecount)
-		columns++;
-
-	cgit_print_layout_start();
-	html("<table summary='repository info' class='list nowrap'>");
-	cgit_print_branches(ctx.cfg.summary_branches);
-	htmlf("<tr class='nohover'><td colspan='%d'>&nbsp;</td></tr>", columns);
-	cgit_print_tags(ctx.cfg.summary_tags);
-	if (ctx.cfg.summary_log > 0) {
-		htmlf("<tr class='nohover'><td colspan='%d'>&nbsp;</td></tr>", columns);
-		cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
-			       NULL, NULL, 0, 0, 0);
-	}
-	urls = 0;
-	cgit_add_clone_urls(print_url);
-	html("</table>");
-	cgit_print_layout_end();
-}
-
-/* The caller must free the return value. */
-static char* append_readme_path(const char *filename, const char *ref, const char *path)
-{
-	char *file, *base_dir, *full_path, *resolved_base = NULL, *resolved_full = NULL;
-	/* If a subpath is specified for the about page, make it relative
-	 * to the directory containing the configured readme. */
-
-	file = xstrdup(filename);
-	base_dir = dirname(file);
-	if (!strcmp(base_dir, ".") || !strcmp(base_dir, "..")) {
-		if (!ref) {
-			free(file);
-			return NULL;
-		}
-		full_path = xstrdup(path);
-	} else
-		full_path = fmtalloc("%s/%s", base_dir, path);
-
-	if (!ref) {
-		resolved_base = realpath(base_dir, NULL);
-		resolved_full = realpath(full_path, NULL);
-		if (!resolved_base || !resolved_full || !starts_with(resolved_full, resolved_base)) {
-			free(full_path);
-			full_path = NULL;
-		}
-	}
-
-	free(file);
-	free(resolved_base);
-	free(resolved_full);
-
-	return full_path;
-}
-
-void cgit_print_repo_readme(const char *path)
-{
-	char *filename, *ref, *mimetype;
-	int free_filename = 0;
-
-	mimetype = get_mimetype_for_filename(path);
-	if (mimetype && (!strncmp(mimetype, "image/", 6) || !strncmp(mimetype, "video/", 6))) {
-		ctx.page.mimetype = mimetype;
-		ctx.page.charset = NULL;
-		cgit_print_plain();
-		free(mimetype);
-		return;
-	}
-	free(mimetype);
-
-	if (path)
-		ctx.page.title = fmtalloc("%s - %s", path, ctx.page.title);
-
-	cgit_print_layout_start();
-	if (ctx.repo->readme.nr == 0)
-		goto done;
-
-	filename = ctx.repo->readme.items[0].string;
-	ref = ctx.repo->readme.items[0].util;
-
-	if (path) {
-		free_filename = 1;
-		filename = append_readme_path(filename, ref, path);
-		if (!filename)
-			goto done;
-	}
-
-	/* Print the calculated readme, either from the git repo or from the
-	 * filesystem, while applying the about-filter.
-	 */
-	html("<div id='summary'>");
-	cgit_open_filter(ctx.repo->about_filter, filename);
-	if (ref)
-		cgit_print_file(filename, ref, 1);
-	else
-		html_include(filename);
-	cgit_close_filter(ctx.repo->about_filter);
-
-	html("</div>");
-	if (free_filename)
-		free(filename);
-
-done:
-	cgit_print_layout_end();
-}
diff --git a/www/git.causal.agency/cgit/ui-summary.h b/www/git.causal.agency/cgit/ui-summary.h
deleted file mode 100644
index cba696af..00000000
--- a/www/git.causal.agency/cgit/ui-summary.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef UI_SUMMARY_H
-#define UI_SUMMARY_H
-
-extern void cgit_print_summary(void);
-extern void cgit_print_repo_readme(const char *path);
-
-#endif /* UI_SUMMARY_H */
diff --git a/www/git.causal.agency/cgit/ui-tag.c b/www/git.causal.agency/cgit/ui-tag.c
deleted file mode 100644
index 05952429..00000000
--- a/www/git.causal.agency/cgit/ui-tag.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* ui-tag.c: display a tag
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-tag.h"
-#include "html.h"
-#include "ui-shared.h"
-
-static void print_tag_content(char *buf)
-{
-	char *p;
-
-	if (!buf)
-		return;
-
-	html("<div class='commit-subject'>");
-	p = strchr(buf, '\n');
-	if (p)
-		*p = '\0';
-	html_txt(buf);
-	html("</div>");
-	if (p) {
-		html("<pre class='commit-msg'>");
-		html_txt(++p);
-		html("</pre>");
-	}
-}
-
-static void print_download_links(char *revname)
-{
-	html("<tr><th>download</th><td class='oid'>");
-	cgit_print_snapshot_links(ctx.repo, revname, "<br/>");
-	html("</td></tr>");
-}
-
-void cgit_print_tag(char *revname)
-{
-	struct strbuf fullref = STRBUF_INIT;
-	struct object_id oid;
-	struct object *obj;
-
-	if (!revname)
-		revname = ctx.qry.head;
-
-	strbuf_addf(&fullref, "refs/tags/%s", revname);
-	if (get_oid(fullref.buf, &oid)) {
-		cgit_print_error_page(404, "Not found",
-			"Bad tag reference: %s", revname);
-		goto cleanup;
-	}
-	obj = parse_object(the_repository, &oid);
-	if (!obj) {
-		cgit_print_error_page(500, "Internal server error",
-			"Bad object id: %s", oid_to_hex(&oid));
-		goto cleanup;
-	}
-	if (obj->type == OBJ_TAG) {
-		struct tag *tag;
-		struct taginfo *info;
-
-		tag = lookup_tag(the_repository, &oid);
-		if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
-			cgit_print_error_page(500, "Internal server error",
-				"Bad tag object: %s", revname);
-			goto cleanup;
-		}
-		cgit_print_layout_start();
-		html("<table class='commit-info'>\n");
-		html("<tr><td>tag name</td><td>");
-		html_txt(revname);
-		htmlf(" (%s)</td></tr>\n", oid_to_hex(&oid));
-		if (info->tagger_date > 0) {
-			html("<tr><td>tag date</td><td>");
-			html_txt(show_date(info->tagger_date, info->tagger_tz,
-						cgit_date_mode(DATE_ISO8601)));
-			html("</td></tr>\n");
-		}
-		if (info->tagger) {
-			html("<tr><td>tagged by</td><td>");
-			cgit_open_filter(ctx.repo->email_filter, info->tagger_email, "tag");
-			html_txt(info->tagger);
-			if (info->tagger_email && !ctx.cfg.noplainemail) {
-				html(" ");
-				html_txt(info->tagger_email);
-			}
-			cgit_close_filter(ctx.repo->email_filter);
-			html("</td></tr>\n");
-		}
-		html("<tr><td>tagged object</td><td class='oid'>");
-		cgit_object_link(tag->tagged);
-		html("</td></tr>\n");
-		if (ctx.repo->snapshots)
-			print_download_links(revname);
-		html("</table>\n");
-		print_tag_content(info->msg);
-		cgit_print_layout_end();
-		cgit_free_taginfo(info);
-	} else {
-		cgit_print_layout_start();
-		html("<table class='commit-info'>\n");
-		html("<tr><td>tag name</td><td>");
-		html_txt(revname);
-		html("</td></tr>\n");
-		html("<tr><td>tagged object</td><td class='oid'>");
-		cgit_object_link(obj);
-		html("</td></tr>\n");
-		if (ctx.repo->snapshots)
-			print_download_links(revname);
-		html("</table>\n");
-		cgit_print_layout_end();
-	}
-
-cleanup:
-	strbuf_release(&fullref);
-}
diff --git a/www/git.causal.agency/cgit/ui-tag.h b/www/git.causal.agency/cgit/ui-tag.h
deleted file mode 100644
index d295cdcd..00000000
--- a/www/git.causal.agency/cgit/ui-tag.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef UI_TAG_H
-#define UI_TAG_H
-
-extern void cgit_print_tag(char *revname);
-
-#endif /* UI_TAG_H */
diff --git a/www/git.causal.agency/cgit/ui-tree.c b/www/git.causal.agency/cgit/ui-tree.c
deleted file mode 100644
index 21e0b884..00000000
--- a/www/git.causal.agency/cgit/ui-tree.c
+++ /dev/null
@@ -1,411 +0,0 @@
-/* ui-tree.c: functions for tree output
- *
- * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-#include "ui-tree.h"
-#include "html.h"
-#include "ui-shared.h"
-
-struct walk_tree_context {
-	char *curr_rev;
-	char *match_path;
-	int state;
-};
-
-static void print_text_buffer(const char *name, char *buf, unsigned long size)
-{
-	unsigned long lineno, idx;
-	const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
-
-	html("<table summary='blob content' class='blob'>\n");
-
-	if (ctx.cfg.enable_tree_linenumbers) {
-		html("<tr><td class='linenumbers'><pre>");
-		idx = 0;
-		lineno = 0;
-
-		if (size) {
-			htmlf(numberfmt, ++lineno);
-			while (idx < size - 1) { // skip absolute last newline
-				if (buf[idx] == '\n')
-					htmlf(numberfmt, ++lineno);
-				idx++;
-			}
-		}
-		html("</pre></td>\n");
-	}
-	else {
-		html("<tr>\n");
-	}
-
-	if (ctx.repo->source_filter) {
-		char *filter_arg = xstrdup(name);
-		html("<td class='lines'><pre><code>");
-		cgit_open_filter(ctx.repo->source_filter, filter_arg);
-		html_raw(buf, size);
-		cgit_close_filter(ctx.repo->source_filter);
-		free(filter_arg);
-		html("</code></pre></td></tr></table>\n");
-		return;
-	}
-
-	html("<td class='lines'><pre><code>");
-	html_txt(buf);
-	html("</code></pre></td></tr></table>\n");
-}
-
-#define ROWLEN 32
-
-static void print_binary_buffer(char *buf, unsigned long size)
-{
-	unsigned long ofs, idx;
-	static char ascii[ROWLEN + 1];
-
-	html("<table summary='blob content' class='bin-blob'>\n");
-	html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>");
-	for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) {
-		htmlf("<tr><td class='right'>%04lx</td><td class='hex'>", ofs);
-		for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
-			htmlf("%*s%02x",
-			      idx == 16 ? 4 : 1, "",
-			      buf[idx] & 0xff);
-		html(" </td><td class='hex'>");
-		for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
-			ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.';
-		ascii[idx] = '\0';
-		html_txt(ascii);
-		html("</td></tr>\n");
-	}
-	html("</table>\n");
-}
-
-static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev)
-{
-	enum object_type type;
-	char *buf;
-	unsigned long size;
-	int is_binary;
-
-	type = oid_object_info(the_repository, oid, &size);
-	if (type == OBJ_BAD) {
-		cgit_print_error_page(404, "Not found",
-			"Bad object name: %s", oid_to_hex(oid));
-		return;
-	}
-
-	buf = read_object_file(oid, &type, &size);
-	if (!buf) {
-		cgit_print_error_page(500, "Internal server error",
-			"Error reading object %s", oid_to_hex(oid));
-		return;
-	}
-	is_binary = buffer_is_binary(buf, size);
-
-	cgit_set_title_from_path(path);
-
-	cgit_print_layout_start();
-	htmlf("blob: %s (", oid_to_hex(oid));
-	cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
-		        rev, path);
-	if (ctx.repo->enable_blame && !is_binary) {
-		html(") (");
-		cgit_blame_link("blame", NULL, NULL, ctx.qry.head,
-			        rev, path);
-	}
-	html(")\n");
-
-	if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
-		htmlf("<div class='error'>blob size (%ldKB) exceeds display size limit (%dKB).</div>",
-				size / 1024, ctx.cfg.max_blob_size);
-		return;
-	}
-
-	if (is_binary)
-		print_binary_buffer(buf, size);
-	else
-		print_text_buffer(basename, buf, size);
-
-	free(buf);
-}
-
-struct single_tree_ctx {
-	struct strbuf *path;
-	struct object_id oid;
-	char *name;
-	size_t count;
-};
-
-static int single_tree_cb(const struct object_id *oid, struct strbuf *base,
-			  const char *pathname, unsigned mode, void *cbdata)
-{
-	struct single_tree_ctx *ctx = cbdata;
-
-	if (++ctx->count > 1)
-		return -1;
-
-	if (!S_ISDIR(mode)) {
-		ctx->count = 2;
-		return -1;
-	}
-
-	ctx->name = xstrdup(pathname);
-	oidcpy(&ctx->oid, oid);
-	strbuf_addf(ctx->path, "/%s", pathname);
-	return 0;
-}
-
-static void write_tree_link(const struct object_id *oid, char *name,
-			    char *rev, struct strbuf *fullpath)
-{
-	size_t initial_length = fullpath->len;
-	struct tree *tree;
-	struct single_tree_ctx tree_ctx = {
-		.path = fullpath,
-		.count = 1,
-	};
-	struct pathspec paths = {
-		.nr = 0
-	};
-
-	oidcpy(&tree_ctx.oid, oid);
-
-	while (tree_ctx.count == 1) {
-		cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev,
-			       fullpath->buf);
-
-		tree = lookup_tree(the_repository, &tree_ctx.oid);
-		if (!tree)
-			return;
-
-		free(tree_ctx.name);
-		tree_ctx.name = NULL;
-		tree_ctx.count = 0;
-
-		read_tree(the_repository, tree, &paths, single_tree_cb, &tree_ctx);
-
-		if (tree_ctx.count != 1)
-			break;
-
-		html(" / ");
-		name = tree_ctx.name;
-	}
-
-	strbuf_setlen(fullpath, initial_length);
-}
-
-static int ls_item(const struct object_id *oid, struct strbuf *base,
-		const char *pathname, unsigned mode, void *cbdata)
-{
-	struct walk_tree_context *walk_tree_ctx = cbdata;
-	char *name;
-	struct strbuf fullpath = STRBUF_INIT;
-	struct strbuf linkpath = STRBUF_INIT;
-	struct strbuf class = STRBUF_INIT;
-	enum object_type type;
-	unsigned long size = 0;
-	char *buf;
-
-	name = xstrdup(pathname);
-	strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "",
-		    ctx.qry.path ? "/" : "", name);
-
-	if (!S_ISGITLINK(mode)) {
-		type = oid_object_info(the_repository, oid, &size);
-		if (type == OBJ_BAD) {
-			htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
-			      name,
-			      oid_to_hex(oid));
-			goto cleanup;
-		}
-	}
-
-	html("<tr><td class='ls-mode'>");
-	cgit_print_filemode(mode);
-	html("</td><td>");
-	if (S_ISGITLINK(mode)) {
-		cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid));
-	} else if (S_ISDIR(mode)) {
-		write_tree_link(oid, name, walk_tree_ctx->curr_rev,
-				&fullpath);
-	} else {
-		char *ext = strrchr(name, '.');
-		strbuf_addstr(&class, "ls-blob");
-		if (ext)
-			strbuf_addf(&class, " %s", ext + 1);
-		cgit_tree_link(name, NULL, class.buf, ctx.qry.head,
-			       walk_tree_ctx->curr_rev, fullpath.buf);
-	}
-	if (S_ISLNK(mode)) {
-		html(" -> ");
-		buf = read_object_file(oid, &type, &size);
-		if (!buf) {
-			htmlf("Error reading object: %s", oid_to_hex(oid));
-			goto cleanup;
-		}
-		strbuf_addbuf(&linkpath, &fullpath);
-		strbuf_addf(&linkpath, "/../%s", buf);
-		strbuf_normalize_path(&linkpath);
-		cgit_tree_link(buf, NULL, class.buf, ctx.qry.head,
-			walk_tree_ctx->curr_rev, linkpath.buf);
-		free(buf);
-		strbuf_release(&linkpath);
-	}
-	htmlf("</td><td class='ls-size'>%li</td>", size);
-
-	html("<td>");
-	cgit_log_link("log", NULL, "button", ctx.qry.head,
-		      walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL,
-		      ctx.qry.showmsg, 0);
-	if (ctx.repo->max_stats) {
-		html(" ");
-		cgit_stats_link("stats", NULL, "button", ctx.qry.head,
-				fullpath.buf);
-	}
-	if (!S_ISGITLINK(mode)) {
-		html(" ");
-		cgit_plain_link("plain", NULL, "button", ctx.qry.head,
-				walk_tree_ctx->curr_rev, fullpath.buf);
-	}
-	if (!S_ISDIR(mode) && ctx.repo->enable_blame) {
-		html(" ");
-		cgit_blame_link("blame", NULL, "button", ctx.qry.head,
-				walk_tree_ctx->curr_rev, fullpath.buf);
-	}
-	html("</td></tr>\n");
-
-cleanup:
-	free(name);
-	strbuf_release(&fullpath);
-	strbuf_release(&class);
-	return 0;
-}
-
-static void ls_head(void)
-{
-	cgit_print_layout_start();
-	html("<table summary='tree listing' class='list'>\n");
-	html("<tr class='nohover'>");
-	html("<th class='left'>Mode</th>");
-	html("<th class='left'>Name</th>");
-	html("<th class='right'>Size</th>");
-	html("<th/>");
-	html("</tr>\n");
-}
-
-static void ls_tail(void)
-{
-	html("</table>\n");
-	cgit_print_layout_end();
-}
-
-static void ls_tree(const struct object_id *oid, const char *path, struct walk_tree_context *walk_tree_ctx)
-{
-	struct tree *tree;
-	struct pathspec paths = {
-		.nr = 0
-	};
-
-	tree = parse_tree_indirect(oid);
-	if (!tree) {
-		cgit_print_error_page(404, "Not found",
-			"Not a tree object: %s", oid_to_hex(oid));
-		return;
-	}
-
-	ls_head();
-	read_tree(the_repository, tree, &paths, ls_item, walk_tree_ctx);
-	ls_tail();
-}
-
-
-static int walk_tree(const struct object_id *oid, struct strbuf *base,
-		const char *pathname, unsigned mode, void *cbdata)
-{
-	struct walk_tree_context *walk_tree_ctx = cbdata;
-
-	if (walk_tree_ctx->state == 0) {
-		struct strbuf buffer = STRBUF_INIT;
-
-		strbuf_addbuf(&buffer, base);
-		strbuf_addstr(&buffer, pathname);
-		if (strcmp(walk_tree_ctx->match_path, buffer.buf))
-			return READ_TREE_RECURSIVE;
-
-		if (S_ISDIR(mode)) {
-			walk_tree_ctx->state = 1;
-			cgit_set_title_from_path(buffer.buf);
-			strbuf_release(&buffer);
-			ls_head();
-			return READ_TREE_RECURSIVE;
-		} else {
-			walk_tree_ctx->state = 2;
-			print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev);
-			strbuf_release(&buffer);
-			return 0;
-		}
-	}
-	ls_item(oid, base, pathname, mode, walk_tree_ctx);
-	return 0;
-}
-
-/*
- * Show a tree or a blob
- *   rev:  the commit pointing at the root tree object
- *   path: path to tree or blob
- */
-void cgit_print_tree(const char *rev, char *path)
-{
-	struct object_id oid;
-	struct commit *commit;
-	struct pathspec_item path_items = {
-		.match = path,
-		.len = path ? strlen(path) : 0
-	};
-	struct pathspec paths = {
-		.nr = path ? 1 : 0,
-		.items = &path_items
-	};
-	struct walk_tree_context walk_tree_ctx = {
-		.match_path = path,
-		.state = 0
-	};
-
-	if (!rev)
-		rev = ctx.qry.head;
-
-	if (get_oid(rev, &oid)) {
-		cgit_print_error_page(404, "Not found",
-			"Invalid revision name: %s", rev);
-		return;
-	}
-	commit = lookup_commit_reference(the_repository, &oid);
-	if (!commit || parse_commit(commit)) {
-		cgit_print_error_page(404, "Not found",
-			"Invalid commit reference: %s", rev);
-		return;
-	}
-
-	walk_tree_ctx.curr_rev = xstrdup(rev);
-
-	if (path == NULL) {
-		ls_tree(get_commit_tree_oid(commit), NULL, &walk_tree_ctx);
-		goto cleanup;
-	}
-
-	read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
-		  &paths, walk_tree, &walk_tree_ctx);
-	if (walk_tree_ctx.state == 1)
-		ls_tail();
-	else if (walk_tree_ctx.state == 2)
-		cgit_print_layout_end();
-	else
-		cgit_print_error_page(404, "Not found", "Path not found");
-
-cleanup:
-	free(walk_tree_ctx.curr_rev);
-}
diff --git a/www/git.causal.agency/cgit/ui-tree.h b/www/git.causal.agency/cgit/ui-tree.h
deleted file mode 100644
index bbd34e35..00000000
--- a/www/git.causal.agency/cgit/ui-tree.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef UI_TREE_H
-#define UI_TREE_H
-
-extern void cgit_print_tree(const char *rev, char *path);
-
-#endif /* UI_TREE_H */