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...
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.
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.
The only problem with the orphan branch approach is that the workflow typically goes:
master
./dist
.gh-pages
/dist
up to the top level.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.
# 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