Setting up gitweb on Shared Hosting

In my last post I talked about how I was disappointed I wouldn’t be able to post my toy projects on GitHub, since GitHub supports ICE. I did get a few recommendations for other places to host Git repositories, but—at least partly inspired by Tom Ryder’s “Why Not GitHub?”—I decided to take the plunge and set up my own hosting. After all, these aren’t big collaborative projects; they’re “just” projects whose revision history I’m willing to share. In all cases so far, I’m the only author, even.

Here were my requirements:

  • Anybody can clone over HTTP(S) (using the usual git clone)
  • Visiting the repo in a web browser gives some “pretty” way to browse the source

Notably absent: I don’t care about pushing over HTTPS. I can use SSH for pushing. This is all read-only.

Now, I found out that git comes with a CGI script to do this, called gitweb. (You can read “a CGI script” as “an executable that you can drop into your web server to generate content based on requests”.) But the documentation is geared towards people who have control over their whole web server, and right now I’m just on a shared hosting plan. So it took me a while to figure out the configuration to use.

What worked for me

  1. Decide where you want the git repositories. You can make a new subdomain or a new subfolder if you want, but the main thing here is that the server-side repositories are going to be served up by your web server as static content, so it has to be somewhere that your hosting plan lets you put website files. I went with a subfolder.

    [server] % mkdir ~/www/belkadan.com/source
    
  2. Install gitweb.cgi in that directory. According to the Git Book, you’re supposed to grab the latest sources and generate a custom version of the script yourself. What they don’t mention is that pretty much everything you’d customize by generating it yourself is already customizable with a config file. So here: you can download the version of gitweb.cgi that I’m using, and configure it yourself. I generated it from the git sources’ master branch on 2020-01-22 using make bindir=/usr/bin gitweb, saving all my customization for the next step.

    Anyway, the important point is to put gitweb.cgi in with the rest of your stuff. I know normally we’d put it somewhere else and have the web server automatically invoke it when accessing the repository URL, but we may not get that kind of control on a shared hosting plan! And as you’ll see soon, this simplifies the next step.

  3. Configure gitweb with an adjacent gitweb_config.perl file. This is the point where you do have to read through gitweb.cgi to find out what configuration options are available. But there are a few important ones.

    # Since we've put gitweb.cgi in with the repositories,
    # we don't need to hardcode a path to find the repositories!
    use File::Basename;
    $projectroot = dirname(__FILE__);
       
    # My server doesn't actually put git in /usr/bin,
    # so do a PATH search for it instead.
    $GIT = "git";
       
    # Turn off search features, to limit potential CPU usage.
    # Don't want to get the site suspended, or have it lag!
    $feature{'search'}{'default'} = [0];
       
    # Use pretty URLs.
    $feature{'pathinfo'}{'default'} = [1];
    # ...and then fix up a bug where the base URL doesn't have a trailing slash.
    $base_url .= "/";
    
  4. Copy gitweb’s static resources to your web server. I put them in a “static” subfolder of the repository folder, but you can put them anywhere. (They don’t have to be relative to the base URL, either.) Note that the JavaScript file is also generated, so once again you can just download my version. Since all of this comes from the git repository, it’s available under GPLv2.

  5. Configure your web server with a local configuration file. My server is Apache HTTP, and that’s the most likely one for a shared hosting plan that still lets you mess around this much. Since I don’t have access to the main server config, we’ll be using an htaccess file. The idea to use a RewriteRule is adapted from the gitweb man page, but I’ve made one important change: paths that resolve to real directories are still sent to gitweb.cgi. I’m not sure why the original man page version doesn’t do that; everything seems to work fine, including cloning, under this configuration.

    Anyway, if you’re using Apache like me, you can add this to your repository directory under the name .htaccess:

    # Make sure we're set up to run gitweb.
    Options FollowSymlinks ExecCGI
    
    RewriteEngine on
    # Rewrite anything that's not a real file...
    RewriteCond %{REQUEST_FILENAME} !-f
    # ...to be a path under gitweb.cgi.
    RewriteRule ^.*$ gitweb.cgi/$0
    
  6. (for each repository) Set up a bare git repository. You can name it with or without “.git” at the end; I went for “without” so that my URLs were slightly prettier. Note also that I changed the default branch (HEAD) for this project; if you’re using git’s usual “master”, you can skip the git symbolic-ref step.

    [server] % cd ~/www/belkadan.com/source
    [server] % git init --bare ROSE-8
    [server] % git -C ROSE-8 symbolic-ref HEAD refs/heads/dev
    [server] % $EDITOR ROSE-8/description  # This will show up in gitweb
    
  7. (for each repository) Configure the repo to stay clone-ready when updates come in. Basically, git doesn’t normally keep around info for clones to work over plain HTTP, but we can generate it up front whenever there’s a push. We use a post-update hook for that. I’ve also chosen to automatically generate the README.html that gitweb expects from the README.md that’s already in my repository.1

    [server] % echo '#!/bin/sh' > ROSE-8/hooks/post-update
    [server] % echo 'git update-server-info' >> ROSE-8/hooks/post-update
    [server] % echo 'git cat-file blob HEAD:README.md | markdown > README.html' >> ROSE-8/hooks/post-update
    [server] % chmod a+x ROSE-8/hooks/post-update
    
  8. (for each repository) Push to that repository (using SSH).

    [laptop] % cd ROSE-8
    [laptop] % git remote add web ssh://jrose@belkadan.com/~/www/belkadan.com/source/ROSE-8
    [laptop] % git push web dev  # Push whatever branches you want.
    

That’s a lot of steps, and I went through a lot more trial and error to get to this point. But it works! Here’s what the final directory layout looks like:

[server] % ls -AF ~/www/belkadan.com/source
gitweb.cgi*
gitweb_config.perl
.htaccess
ROSE-8/
static/

At this point you can test that both browsing through gitweb and cloning from the command line actually work.

[laptop] % cd $TMPDIR
[laptop] % git clone https://belkadan.com/source/ROSE-8
[laptop] % ls ROSE-8

The good news is that you only have to do steps 6-8 if you want to add another repository. And you can continue tweaking the gitweb configuration from step 3, or the resources from step 4, if you want.

What’s next

So I’ve got my ROSE-8 repository repositories online now, and I’ll do another blog post about that soon. I also don’t love the default gitweb look, and while Stefan Imhoff has done a pretty good job making a GitHub-inspired theme I want to see if I can make something I like better. Later, though.

Meanwhile, you can now clone ROSE-8! From the same URL as the browse URL!