Way back in mid-2007, when Rails 1.2 was the new hotness and GitHub was still a year away from crawling out of the primordial internet soup, prolific open source contributor Dr Nic wrote an article titled “8 steps for fixing other people’s code”. It offers excellent general advice, but the workflow details are clearly a product of their time: RubyForge, SVN, and
.patch files feature heavily.
Here in the fantastical future world of 2012, while we still don’t have hoverboards or household nuclear fusion, we do have some great tools that make fixing other people’s code a lot easier. Join me as we rewrite “8 steps” for the modern age!
Open source projects are usually far from perfect. In many cases they started as something the author threw together to solve a problem, then released into the wild just in case it could be useful to someone else. Open source contributions come from regular people — just like you! — who start using the project and run into a bug that annoys them, or get an idea for a feature that would make their life easier.
The experience that prompted me to write this post started with finding a bug in Chronic, a date-parsing library. The issue is described in the pull request I submitted to fix it (pull requests are the final step in this process; we’ll get to them in a bit).
In recent years git and GitHub have emerged as the de facto standard, if not for open source projects in general, then at least for open source Ruby projects. Lucky you! In Dr Nic’s time it was all SVN, uphill both ways.
If your target project has a web site, it will probably link to the official GitHub repository. Failing that, you can search GitHub for the name of the project. This may also turn up other people’s forks of the project — if you’re not sure which one is the original, just pick one and follow the “forked from” links.
Once you’ve found the repository, scroll down to the README and scan it for “How to Contribute” directions. Some projects have a particular process they want contributors to follow, and it might be different from the steps outlined here. Chronic’s instructions are typical and straightforward.
Forking a project creates your own personal copy of the repository that you’re free to modify however you want. It’s integral to the GitHub workflow, and is therefore stupidly easy: Just hit the Fork button.
Copy the Read+Write link from your newly-forked repo and clone it down:
$ git clone firstname.lastname@example.org:grantovich/chronic.git Cloning into 'chronic'...
Then copy the Read-Only link from the original repository (mojombo/chronic in my case) and add it as a “remote” like so:
$ cd chronic $ git remote add upstream git://github.com/mojombo/chronic.git
Many projects use automated tests to ensure that bugs stay fixed and new features don’t introduce new bugs. Running a project’s tests is also a useful sanity check, since no tests should fail on an unmodified clone of the master branch.
Before doing this, you’ll need to install the project’s dependencies — if it has any, it should include a
gem install bundler to install Bundler if you haven’t already, then simply
In most Ruby projects, running “
rake” with no arguments will execute the full test suite. Ensure this doesn’t give you any failures before proceeding.
Come up with a short, descriptive name for a branch that will contain your changes. Here’s my Chronic branch:
$ git checkout -b handle-sm-sd-with-time
Now you’re ready to start coding! Assuming the project has tests, you can do some TDD and start by writing a failing test. Poke around the existing test suite — it likely involves Minitest or Rspec — then write one or more new tests that would succeed if your feature existed or your bug was fixed.
Re-run the test suite to ensure your tests fail (and you didn’t somehow break any other tests). Then, write the code to make them pass! Follow the style of the existing code as much as possible, and resist the temptation to “clean up” or reformat things that aren’t relevant to your change.
Many libraries have Rake tasks applicable to this step.
rake console is a common task that opens an IRB shell with your in-development copy of the library loaded into it. If the library is made available as a gem, there will be a task that builds the gem (e.g.
rake build), and you’ll want to make sure this still works after you change things.
Note that while the changes I made to Chronic were in multiple commits, you should really put both the tests and the code that passes them into a single commit. Any given commit should pass all the tests that exist within it. You can still make multiple commits locally — just squash them later using
git rebase -i origin/master.
If you’ve added a new feature or changed how something works, and the project’s documentation lives in the repository (this is usually true for at least the README file), update it to be consistent with your changes. Documentation never gets enough love, and the owner will probably appreciate your thoughtfulness.
Pull down any changes made to the original repository since you started working, and replay your commits onto the updated code. This ensures that your changes can still be successfully applied to the latest version. It sounds complicated, but the commands are quite simple:
$ git fetch upstream $ git rebase upstream/master
Here’s the other half of the fork-based GitHub workflow. After making changes in your fork, you request that the original author pull those changes into their repository.
First push the branch containing your commits up to GitHub…
$ git push -u
…then hit the Pull Request button on your forked repository.
Set the “head branch” on the right side of the pull request to your new branch.
You’ll be prompted to give your pull request a title (e.g. “Add support for XYZ”) and detailed description. Fill these in, and hit the Send pull request button!
Now comes the hardest part: Waiting. The owners of the repository will be notified of your pull request, and at some point — hopefully — will get around to reviewing it. They might suggest some changes: Make them in your branch and
git push, and the pull request will automatically update with your new commits. If your changes look good, they’ll be officially merged into the project. Success!
After you’ve done this once for a given project, the process is simplified: When you’re ready to contribute again,
git pull upstream master so you’re working with the latest code, and start again from step 4.
While the GitHub web interface is more accessible if you’re not used to the workflow yet, it’s actually possible to do all this without leaving your terminal. Check out the hub tool, and more specifically the commands dealing with forks and pull requests. In fact, my Chronic pull request was originally a bug report that I later “converted” using
Remember these steps are more like guidelines than actual rules. Every project is different, and the owners of the one you’re working on might prefer you do things a bit (or a lot) differently. Be receptive to the feedback you get on your pull requests, even if it seems nitpicky. You’ll be hailed as a model open source citizen in no time!