Oli.jp

Articles…

GitHub Pages Workflow and deleting git’s master branch

Over at HTML5 Doctor we’ve decided to mirror Mark Pilgrim’s “Dive into HTML5” book, and help update it. Jonathan Neal created an up-to-date mirror (with improvements) on GitHub, so I thought we’d give hosting via GitHub Pages a go. Using a CNAME setting we can map the subdomain diveinto.html5doctor.com to GitHub, which serves the content in a special gh-pages branch. It’s working great.

gh-pages workflow

Setting up GitHub Pages can appear a bit scary. Here’s how GitHub’s documentation suggests setting up the gh-pages branch:

$ cd /path/to/fancypants
$ git symbolic-ref HEAD refs/heads/gh-pages
$ rm .git/index
$ git clean -fdx
$ echo "My GitHub Page" > index.html
$ git add .
$ git commit -a -m "First pages commit"
$ git push origin gh-pages

This makes a new gh-pages branch with nothing in it, then adds a file and pushes it to GitHub. Now you have two branches with differing content:

  • master — your project’s code
  • gh-pages — your project’s website, hosted by GitHub using GitHub Pages

In Quick tip: git checkout specific files from another branch, Nicolas Gallagher covers how to add or update files on gh-pages from the master branch (this assumes you’re working in master):

$ git add .
$ git status // to see what changes are going to be committed
$ git commit -m "Some descriptive commit message"
$ git push // push the master branch changes to GitHub
$ git checkout gh-pages // go to the gh-pages branch
$ git checkout master -- file1.ext file2.ext file3.ext // add/update file1-3 with changes from master branch
$ git commit -m "Update file1-3 from master"
$ git push // push the gh-pages branch changes to GitHub Pages
$ git checkout master // return to the master branch

Lea Verou generally mirrors master branch changes to gh-pages, and covers the workflow in Easily keep gh-pages in sync with master. Do this by replacing the git checkout master and second git commit commands with git rebase master:

$ git add .
$ git status // to see what changes are going to be committed
$ git commit -m 'Some descriptive commit message'
$ git push // push the master branch changes to GitHub
$ git checkout gh-pages // go to the gh-pages branch
$ git rebase master // bring gh-pages up to date with master
$ git push // push the gh-pages branch changes to GitHub Pages
$ git checkout master // return to the master branch

By rebasing, all commits on the master branch (and their commit messages) are applied to the gh-pages branch.

Using a post-commit hook

Paul Irish contributed this post-commit hook snippet for automating Lea’s workflow (save as .git/hooks/post-commit in your Git repo):

#!/bin/sh
git checkout gh-pages
git rebase master
git checkout master

This lets you replace the last five steps of Lea’s workflow with just git push --all. Nice!

Merging via git push

Nicolas Gallagher came across another way to merge master into gh-pages in GitHub’s Help on remotes — you can push your local master branch to the gh-pages branch on GitHub:

git push -f origin master:gh-pages

This would replace the last four steps of Lea’s workflow. Your master branch needs to be a mirror or subset of the remote gh-pages branch, and it means if you’ve got the gh-pages branch locally it’ll now be behind GitHub’s version. However, using this method you can essentially ignore (or not even have) the gh-pages branch locally.

The -f (force) makes the push happen even if the gh-pages branch is newer (avoiding a “non-fast-forward updates were rejected” error). When using this method you shouldn’t be working on gh-pages at all, so the only time this will happen is if someone else pushes changes ahead of you. In that case you can merge those changes into your local master and push the gh-pages branch again. This is the shell script for force-pushing that Move the Web Forward uses.

Setting up GitHub Pages using your current content

However I wanted to have everything in gh-pages and no master branch, as the project page basically is the project. Ideally I’d just rename the master branch to gh-pages (possibly $ git remote rename master gh-pages), but I worried this would cause problems with GitHub. Being lazy I just branched instead.

$ cd /path/to/diveintohtml5
$ git checkout -b gh-pages
$ git push origin gh-pages

This worked fine even with CNAME-based serving, but a typical git pull failed with this error:

$ git pull
You asked me to pull without telling me which branch you
want to merge with, and 'branch.gh-pages.merge' in
your configuration file does not tell me, either. Please
specify which branch you want to use on the command line and
try again (e.g. 'git pull <repository> <refspec>').
See git-pull(1) for details.

If you often merge with the same branch, you may want to
use something like the following in your configuration file:

  [branch "gh-pages"]
  remote = <nickname>
  merge = <remote-ref>

  [remote "<nickname>"]
  url = <url>
  fetch = <refspec>

See git-config(1) for details.

I fixed this by editing the repo’s .git/config file and adding:

[branch "gh-pages"]
  remote = origin
  merge = refs/heads/gh-pages

As Ryan Fitzer confirms in Easy Syncing of GitHub Pages (detailing what he did for a “default” setup), I could have avoided this by following the GitHub instructions, and replacing echo "My GitHub Page" > index.html with git merge master.

Deleting the master branch on GitHub

This step could be messy, so I double-checked with GitHub support first (thanks Petros!). There are three things you need to do:

  1. Make the gh-pages branch your default branch on GitHub: Repository Administration > Options > Set “Default Branch” setting to something other than master
  2. Delete the master branch on GitHub using $ git push origin :master (which I think just sends a null branch to replace the remote master branch)
  3. Delete the master branch in your local repo (once you’ve checked everything is working!) using $ git branch -d master

After doing that I’ve ended up with a single gh-pages branch on GitHub, and everything (apart from html5doctor.github.com/diveintohtml5) is working as expected. I’m not sure why the default GitHub Pages address fails, but given we’re using CNAME to serve on a different domain it’s not a problem for us.

GitHub support also had this good advice:

First of all every time you are not sure about something, you can try doing it after you have cloned and fetched everything locally as a backup. If something goes wrong, you can push your repository again.

Conclusion

I hope GitHub adds admin options for choosing which branch to use for GitHub Pages (so you could choose master), and for adding CNAME information (adding a file to the repo is inelegant). I also hope they update the GitHub Pages documentation to cover the above setups.

In the meantime, if you have a repo you want to serve via gh-pages using CNAME (not using the github.com URL), and you don’t have any separation between the master and gh-pages branches, moving your repo from master to gh-pages might be a good way to simplify things.

If you have any feedback or comments, contact me via Twitter (@boblet) or Google+ (Oli Studholme).