Git for Static Sites

I often find myself creating static websites, where I want to push the source to Github, and publish the result somewhere, quickly and cheaply.

One example is presentations, which I write in org-mode and then publish using reveal.js. Another is Elm sites, when I build the site in Elm and need to host the generated HTML/JavaScript, without necessarily wanting a custom backend.

So, here's how I do it. Follow along and you'll learn a practical trick for publishing, and some git-fu to boot...

Publish on Github

The first thing to know is that those generous souls at Github will publish your static sites for free. If you create any repo with a branch called gh-pages, they'll publish it that branch on <your-username>.github.io/<repo-name> a few minutes later.

So you could just create a brand new repository, commit your compiled-down site to it, and rename master to gh-pages.

That would do the job, but we can improve things.

Orphan Branches

Keeping two separate-but-related repos, one for the source and one for the output, gets old quickly. Especially for small projects. Ideally I'd like two separate project trees managed in the same .git directory, to keep everything together. And there's something that feels correct about managing the two facets of the project in one place. Doing exactly that is very easy:

git checkout --orphan gh-pages

When you checkout an 'orphan' branch, git creates an entirely new branch without any parent commits (hence the name, 'orphan'). You can add anything you want to that branch, and it's separate from the main history, but stored in the same .git directory.

After creating an orphan branch you can clear out the current directory - safe in the knowledge that it's all still saved on master - and add any new content you want. The orphan branch has its own timeline and own files, but it's housed within the same git repo.

Run git push -u origin gh-pages the first time you want to push, and from then on git push will send master, publishing your source, and gh-pages, publishing your site.

We just need one more piece to make this work really smoothly.

Worktrees

The only problem with the orphan branch approach is that the workflow typically goes:

  • Checkout master.
  • Build the site to /dist.
  • Checkout gh-pages
  • Copy the contents of /dist up to the top level.
  • Commit and push.

It's fine, but the file-copying is a little unwieldy. The solution, that makes this whole approach flow smoothly, is using git worktrees - a new feature in git 2.5.

With worktrees, you can have two completely separate working directories, backed by the same git repo. It's a feature I use when I want to flip between active-branches without stashing all the time. (For example, when you're blocked halfway through feature A, so you want to crack on with feature B, but you know you'll need to flip back and forth.)

For our purposes though, worktrees allow us to build directly to our gh-pages branch. We start a worktree off like so:

$ pwd
/.../my-project
$ git worktree add ../my-project-dist gh-pages

Now we have two directories side-by-side:

. - /my-project       # Checked out as master
|
. - /my-project-dist  # Checked out as gh-pages

Now we just alter our build script to build into ../my-project-dist and the setup's complete. Any commits in either directory get stored on the same repo. And pushes, pulls or fetches will synchronise exactly as you'd hope.

To Recap

Why?

  • You want to manage a source and a release repo.
  • With easy publishing of the release repo.
  • With the minimum of admin.
  • With a guarantee that if you've got one, you've got the other.
  • With a guarantee that if you've backed-up one, you've backed-up the other.

How

# Create the orphan gh-pages branch
$ git checkout --orphan gh-pages

# Create at least one commit.
...

# Connect local gh-pages to the remote version.
$ git push -u origin gh-pages

# Head back to master.
$ git checkout master

# Check out the orphan gh-pages branch in its own directory.
$ git worktree add ../my-project-dist gh-pages


# Finally, from now on, to publish both the source and the release it's just.
$ git push