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!
1. Get annoyed by a bug or missing feature
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).
2. Locate the repository and read up
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.
3. Fork the repository
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 git@github.com: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
4. Run the tests
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 Gemfile
. Use gem install bundler
to install
Bundler if you haven’t already, then simply bundle install
.
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.
5. Make your changes
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
.
6. Document your changes
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.
7. Get up-to-date
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
8. Submit a pull request
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!
Final thoughts
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 hub
.
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!