Pressed Pixels

Things by Leo Baker-Hytch

Simple deployment to GitHub Pages with git worktree

I’ve recently been building single-page apps with React, and using webpack to transform/bundle the JS and CSS into a distributable form. GitHub Pages is the perfect place to host these projects, as deployment is only a git push away, from the gh-pages branch.

At least, it ought to be that simple—but instead I’ve found myself awkwardly copying build output from dist on master to the project root on gh-pages (GitHub gives you no choice but to serve your site from there). That or I’ll add the build output files to .gitignore, and put up with them polluting the project root.

Thankfully, git has a trick up its sleeve to avoid all this: worktree, added in version 2.5. Glossing over the gory details contained in the man page, the command allows you to check out additional branches into subdirectories of your repository’s root, like so:

git worktree add dist gh-pages

Now when you cd into dist, you’ll be on the gh-pages branch. Go back up a level, and you’re back on master. Very convenient.

A few things to watch out for: when running this command, git will complain if dist already exists. This also assumes that you already have a gh-pages branch. If not, worktree add can create it for you by giving it the flag, -b.

If you create a new branch like this, however, it’ll contain all the history of the branch you were on when the command was executed. I prefer an empty history, to be able to clearly see deploys on their own—probably best combined with judicious use of tags, to link back to the history in your development branches.

An empty history requires an orphan branch, created as follows:

git checkout --orphan gh-pages

Somewhat annoyingly, this populates the index with the contents of the branch from which you perform the checkout (and there’s no corresponding --orphan option for the branch command to do what we really want). The workaround is easy though. Just clear the index, make an initial commit on the new branch (an empty one will do), then checkout the previous branch with the flag, --force, to silence warnings about overwriting files that were carried over with the orphan checkout—they’ve not changed. Then the new worktree can be added:

git reset
git commit --allow-empty -m 'Initial commit'
git checkout --force master
git worktree add dist gh-pages

Finally, you can run your build tool of choice (with dist configured as its output directory, of course) and push to production:

rm -rf dist/* && NODE_ENV=production webpack -p
git tag v0.0.1
cd dist
git add --all
git commit -m 'Deploy v0.0.1'
git push
cd ..

You could make deploying easier still by adding a post-commit hook that runs (roughly) the above whenever you create a new tag. But that’s for another post.