Github pull requests made easy

- mhuffnagle - Element 84

(Featured image from http://octodex.github.com/collabocats/)

One of the things that I like most about Github is how easy it is to contribute back to the projects that I use on a daily basis. Once I figured that out, I started forking repos and issuing pull requests every time I found a problem. In this post, I want to demonstrate a case where I find a problem with a library I’m using, fix it, and submit a pull request back to the maintainer. I promise it’ll be pain-free.

For this example, let’s use a simple (and flawed) Ruby calculator that I’ve written. It’s available at https://github.com/Element84/bad_calculator.

As I’m using the BadCalculator module I find that, when I use the “add” method, it never quite gives me the correct answer. In fact, the result is always one more than it should be.

Step 1: Log an issue

When you find a problem like this, the first and, I’d say most important step, is to log an issue for it. Even if you aren’t the one who ends up fixing the problem, you’ll let the developer know about it, and you’ll also help out other users who run into the same problem.

Filing an issue on Github is really easy. You just go to the project’s page, click over to the “Issues” page, click “New Issue”, and describe the problem you’ve found. It’s a good idea to do a quick search through the existing issues to see if anyone else has reported the same problem. It’s possible that there’s a workaround, or that it’s already been fixed in a future release.

In this case we don’t find an issue for what we’re seeing, so we file our own.

New Github issue

Step 2: Install hub (one time only)

Github is pretty easy to work with. To make it even easier, I’d suggest installing hub. Hub describes itself as, “a command-line wrapper for git that makes you better at GitHub.” The hub page has instructions on how to install it but, as of now, it’s as simple as:


$ curl http://defunkt.io/hub/standalone -sLo ~/bin/hub
$ chmod +x ~/bin/hub

For this to work, make sure that ~/bin exists and is in your path.

The hub page suggests aliasing git to point to hub, but I don’t do that right now. Most of the git repositories that I work with are not on Github, and I don’t want to spin up Ruby every time I run git (especially when working with JRuby).

One other sidenote about hub: it defaults to using the git protocol. A lot of firewalls block git:// access, so hub will fail. Fortunately you can tell hub to use https instead with:

git config --global hub.protocol https

So, with that one-time setup done, let’s squash that bug.

Step 3: Clone the repository

The first step is to clone the repository to your local machine. If we were just using git then we would run:

git clone https://github.com/Element84/bad_calculator.git

Since we have hub installed, though, it’s even easier:


[localhost src]$ hub clone Element84/bad_calculator
Cloning into 'bad_calculator'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (5/5), done.
Unpacking objects: 100% (7/7), done.
remote: Total 7 (delta 0), reused 7 (delta 0)

Step 4: Run the tests

If the project has tests, you should run them and make sure they all pass before you start making changes. bad_calculator does have tests, so let’s make sure they’re all green.


[localhost bad_calculator (master)]$ bundle install
Using minitest (4.7.3)
Using bundler (1.3.5)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
[localhost bad_calculator (master)]$ ./tests.rb
Run options: --seed 46318

# Running tests:

.

Finished tests in 0.000465s, 2150.5376 tests/s, 2150.5376 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

Looks like we’re good to go.

Step 5: Create a feature branch

Most project maintainers ask that pull requests be in a feature branch, so we’ll follow that best practice and create a new branch called “FixAddMethod”.


[localhost bad_calculator (master)]$ git checkout -b FixAddMethod
Switched to a new branch 'FixAddMethod'

Step 6: Add a new test

Before we fix the problem, let’s add a test to make sure that it really is fixed and stays fixed in the future.


def test_add_returns_correct_value
assert_equal 3, BadCalculator.add(1, 2)
end

Now we run the tests again and confirm that the test we just added is catching the problem we’re fixing.


[localhost bad_calculator (FixAddMethod)]$ ./tests.rb
Run options: --seed 55121

# Running tests:

.F

Finished tests in 0.022372s, 89.3975 tests/s, 89.3975 assertions/s.

1) Failure:
TestBadCalculator#test_add_returns_correct_value [./tests.rb:15]:
Expected: 3
Actual: 4

2 tests, 2 assertions, 1 failures, 0 errors, 0 skips

It failed, but that’s what we want to see at this point.

Step 7: Fix the problem

Lucky for us, this is a really easy fix!


[localhost bad_calculator (FixAddMethod)]$ git diff bad_calculator.rb
diff --git a/bad_calculator.rb b/bad_calculator.rb
index c65cda4..5228787 100644
--- a/bad_calculator.rb
+++ b/bad_calculator.rb
@@ -1,7 +1,7 @@
module BadCalculator

def self.add(x,y)
- x + y + 1
+ x + y
end

end

Step 8: Re-run the tests

Running the tests again, we see that they’re all green, including the test that we just added.


[localhost bad_calculator (FixAddMethod)]$ ./tests.rb
Run options: --seed 29028

# Running tests:

..

Finished tests in 0.000470s, 4255.3191 tests/s, 4255.3191 assertions/s.

2 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Step 9: Commit

Make sure to provide a good description, since it will appear in the log for the project you’re contributing to.


[localhost bad_calculator (FixAddMethod)]$ git commit -am "Fixed the add method so that it now returns the correct value."
[FixAddMethod 884783a] Fixed the add method so that it now returns the correct value.
2 files changed, 5 insertions(+), 1 deletion(-)

Step 10: Fork the Github repository

Next, we need to fork the repository on Github so that we can push our changes. Since we’re using hub, we can just run:


[localhost bad_calculator (FixAddMethod)]$ hub fork
Updating mhuffnagle
From https://github.com/mhuffnagle/bad_calculator
* [new branch] master -> mhuffnagle/master
new remote: mhuffnagle

The new remote is named for your username (mhuffnagle in this case).

Step 11: Push the feature branch

We’ll be pushing the feature branch to our forked copy of the repository on Github, which will then be the source of the pull request.


[localhost bad_calculator (FixAddMethod)]$ git push -u mhuffnagle FixAddMethod
Counting objects: 7, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 549 bytes, done.
Total 4 (delta 1), reused 0 (delta 0)
To https://github.com/mhuffnagle/bad_calculator.git
* [new branch] FixAddMethod -> FixAddMethod
Branch FixAddMethod set up to track remote branch FixAddMethod from mhuffnagle by rebasing.

Step 12: Issue a pull request

This step is where hub earn its pay. Everything that we’ve done so far could have been done through the Github website and/or git on the command line. The one thing that you can’t do, though, is attach a pull request to an issue. I’m not sure why Github doesn’t let you specify an issue number when you create a pull request on the site, but they don’t. Fortunately for us, hub makes it really easy. Our issue number is “1″ in this case, so we’ll run the following command to create a pull request and associate it with issue number 1:


[localhost bad_calculator (FixAddMethod)]$ hub pull-request -i 1

https://github.com/Element84/bad_calculator/pull/1

Step 12: Admire your work

Github issue with pull request

That’s all there is to it! If you head over to the issue page that you filed, on Github, you should see your pull request and the code changes that you made. The maintainer of the project will now receive a notification about your change and (hopefully) merge it into master. Congratulations, you’ve given back to the Open Source community!

Summary

To sum up, here are the steps that we just went through:

  1. Create an issue on Github
  2. Setup hub (one time only)
  3. Clone the repository with: git clone Element84/bad_calculator
  4. Run the tests if there are any
  5. Create a feature branch with: git checkout -b FixAddMethod
  6. Fix the problem
  7. Make sure that the tests all pass
  8. Commit your changes with: git commit -am "Fixed the problem"
  9. Fork the repository with: hub fork
  10. Push your feature branch with: git push -u mhuffnagle FixAddMethod
  11. Issue a pull request with: hub pull-request -i 1