WordPress and Git

A few years back, I wrote a post on managing WordPress. For the most part, it’s still valid but I’ve been using git to manage code for quite awhile now, so I thought I’d give a little update.

Thanks to Mark Jaquith, there’s now a reliable, complete git mirror of WordPress. I’ve been using git to manage my WordPress sites for awhile now, but the actual updating of WordPress has always been weird to force into the git loop. My practice has been to keep a stock WordPress git repository that is also a subversion repository (just add *.svn to your .gitignore). I svn switch to a new tagged release, commit the result in git, and then this repository is set up as a remote on all my active WordPress sites, so I ssh into these sites and merge each with this stock WordPress branch.

Now that there’s a good Wordpress mirror on github, the way to take advantage of this may seem obvious: just clone this mirror and then merge from it. However, that has a couple downsides. First, you get messy history: all of the commits you’ve made locally for your site get drowned in WordPress core commits. Second, unless you’re actively developing WordPress core, you only care about official releases (and possibly beta releases). In other words, you want to merge with tags only and you don’t want messy history. So here’s how to get that: instead of cloning Jaquith’s mirror, just add it as a remote and squash the commits.

Step-by-step:

If you’re starting from scratch, create an empty git repo with git init and create an initial commit with something — maybe a local-readme.txt file explaining what you’re doing. (“Squash commit into empty head not supported yet”.) Anway, add the remote:

git remote add wp git://github.com/WordPress/WordPress.git
git fetch -t wp

The first adds the github mirror as a remote named “wp”. The fetch -t will make sure you get all the tags. (Run git tags afterwards to see the list of available tags.)

Next, merge with the latest tag (here, 3.2.1):

git merge --squash --no-commit -s recursive -X theirs tags/3.2.1

The --squash means it takes the hundreds (thousands?) of ancestor commits for that tag and compresses it into a single commit. The -s recursive -X theirs tells git that if there are conflicts, use the new version. (Remember, all you’re doing here is creating a simple local WordPress mirror to merge your actual WordPress sites with.)

The --no-commit tells git to merge but stop just short of actually making the commit. So make sure everything looks good. For example, you can’t squash binary files so you may get conflicts about that. If this happens, just git checkout the latest version explicitly. For example, going from 3.2 to 3.2.1 I had to do the following: git checkout tags/3.2.1 -- wp-admin/images/wp-logo.png

Finally, git commit -m "3.2.1"

Now, as I said above, what I like to do is this: do not use this as your starting point for an actual WordPress instance. Instead you add this repo as a remote (just like we did above) to your site and then you can just do a git pull wp (or whatever you call it) there. That way you only have to do the little dance above once for each release if you’re responsible for multiple WordPress instances.

Quick aside: Why don’t I just put mine on github? I could. Here’s why Jaquith’s mirror an official WordPress mirror is a big deal. Others have done this, but they’ve all had shortcomings: the ones I knew about didn’t include tags, or they were really slow to update, or they simply got abandoned. I’m not blaming any of them. Sure, I could put a “simplified WordPress repo” on github, but I don’t want to commit to updating it instantly every WordPress update forever. And what works for me may not work for you. The process I described above is not difficult and shows how you can take the comprehensive mirror and merge with it to make your life easier in your own way that suites your needs.

Plugins

What about plugins? There’s no git mirror of the WordPress Plugins Directory that I know of, so I usually revert to my subversion hack described above: svn co http://svn.wp-plugins.org/plugin-name/trunk/ . into a directory for the plugin and then commit the results into git. Alternatively, you can use WordPress’ backend plugin updater, which can work just fine, but I inevitably end up having to modify certain plugins and that process simply overwrites the files and you lose your modifications, whereas at least svn up will alert you to conflicts and let you merge your changes into the new version. (Makes me wonder how many out-of-date plugins would be updated if the WordPress Plugin Directory worked more like github — where anyone could fork your plugin and fix it if you won’t…)

Beta Testing

If you want to test beta releases locally before installing them, you can just create a “testing” branch of your WordPress repository (git checkout -b testing) and then instead of merging with a release tag, just merge with whatever branch the development for the next version is happening on. Make sure if you’re going to switch back and forth to backup your database though!

Merging into an existing site

This may sound great for setting up new sites, but what about merging into an existing wordpress site? You’ll get conflicts because git won’t have a common ancestor commit to merge from. So fake one: create a graft file file linking your most recent commit where you updated WordPress (if you’re using git for the first time, just use your first commit) and the SHA1 of the commit for the version you’re currently running in your base WordPress repo. After creating this file, you should see both branches reflected in your git log and when you try to merge to the current commit in your remote branch, it should merge cleanly, as if you’d been merging with it all along.