Contributing to Open Source - grjug/ChuckNorrisJokes GitHub Wiki
Contributing to Open Source Projects
When working on a project where everyone shares and has write access to the same remote repository, everyone can easily keep their repositories in sync without much effort. There is one primary or canonical branch, often called master
, but in our case is called develop
(for more on this, see the git-flow
model). Contributors create feature branches and submit pull requests to the master
or develop
branch, the PR gets merged in, and everyone can simply run git pull
when in the primary branch to update the changes locally. However, in open source, there are often multiple forks of a single canonical repository, each with its own canonical master
or develop
branch. How do we properly keep those in sync?
When working on an open source project we follow a very similar pattern as if it was a shared repository, with a few added rules:
- Always keep the canonical branch in sync with the
upstream
repository. This means you will never submit a pull request to, commit directly on, or merge into your own master branch. You can only ever update the primary branch from theupstream
repository. - Always follow the branching rules (if there are any) and contributing guidelines for the project you are working on.
- Be kind, courteous, and open to comments and suggestions from the maintainers. You are writing the code, but they are the ones who have to maintain it, so it is important that they clearly understand what your code does and why.
- When writing commit messages, be clear, concise, and follow good commit message guidelines.
- When committing, keep your commits clean, focused, and atomic.
- When submitting pull requests, be descriptive about what it does and what problems it solves.
Branching
When we want to write a feature, fix a bug, or write some experimental code, we branch off of develop
or master
(depending on the project's branch model) and name our branch. We can do this by typing the following command into the terminal:
$ git checkout -b feature/add-joke-sharing
This will create a branch called feature/add-joke-sharing
and check it out. You can see this illustrated below:
$ git branch
master
develop
* feature/add-joke-sharing
You can now start changing files and committing your changes. Once you've made a few commits, you can push a copy of your branch to the remote for which you have write access: origin
:
$ git push -u origin feature/add-joke-sharing
This will create a copy of your branch on your fork of the repository, but will not affect the upstream repository at grjug/ChuckNorrisJokes
. In order to do that, we need to make a pull request.
Pull Requests and Forked Repositories
The easiest way to make a pull request is to open up the repository on Github in your browser. If you have a freshly pushed branch, you will see something like this on top of the page:
Here's where it's important to make a distinction between working on a shared repository versus collaborating on an open source project: it's very important you keep your canonical branch in sync with the upstream repository. This means that, unlike working on a single shared repo, where you would submit a pull request to your own repository's master
or develop
branch (or merge them in from the command line), you should submit the pull request to the upstream
repository. Github makes this very easy for us; just click the link to the jrjug/ChuckNorrisJokes
repository at the top where it says "forked from", and you should see something like this:
At this point you can click the green button that says Compare & pull request. Fill in a descriptive name of the pull request and a meaningful explanation in the comment that explains what your pull request is meant to do and how it does it. Well written pull requests are not only more likely to be merged, but they're also helpful for the project maintainer to guide their code review and understand exactly what they're looking at. Once you've filled it out, you can click Create pull request and you'll see a brand new request queued up. If that pull request (or PR) gets merged in, it will become a part of the upstream repository.
Syncing with Upstream
Since our rule above about keeping your canonical branch in sync with the upstream repository is very important, we will never make a change to our own local master
or develop
branch that does not come directly from the upstream repository. Since our pull request was merged in (or other PRs were merged in while we were working), we can update our canonical branch by updating git's knowledge of the upstream repository, then merging its develop
branch into our own:
$ git fetch upstream
$ git checkout develop
$ git merge upstream/develop
$ git push
As you type each of these commands, git will give you some feedback showing whether or not there were changes made. Here's the gist of what's happening:
- Tell git to look for any changes that have been made to our
upstream
remote and cache them locally. - Checkout our canonical branch. Whenever you merge, you should first checkout the branch to which the merge should be applied.
- Merge our
upstream/develop
branch intodevelop
. Note thatupstream/develop
is just another branch, but git treats it a little differently since it knows it is a copy of a remote. To see this in action, you can typegit branch -a
into your terminal and see all the branches git knows about for this repository. - Update our forked
origin
so it stays up to date with everything else.
Committing with Git
One of the most common mistakes users of git make, new and old alike, is reckless commits. There are a few general rules to follow when you start changing code and committing it.
Always review your work before you commit.
Use git add -p
to step through your diff
and add code in chunks. This can be a little awkward at first, but with a little practice, it will become second nature. This tool can be extremely powerful once you grok it. Only add stuff you know belongs in the commit you're planning.
Committing with git add .
or git commit -am
are carpet-bomb techniques, and should generally be avoided. You will almost always end up committing something that doesn't belong in that commit. Doing so is like publishing an article without proofreading it first.
Commits should be atomic.
Keep when you are reviewing your commits and picking changes, try to make sure everything you are adding is specifically related to the goal. It helps to have a commit message in mind when picking chunks to add. For instance, you might have fixed a typo you noticed while working on a feature. Skip that patch when you are adding code and put it in its own commit afterward (or before, if you still have a lot of work to do on your feature). This will make it easy to save that change in case the rest of the feature doesn't end up getting merged.
Make good commit messages.
A good commit message describes the purpose of the content of your commit in 50 characters or less. When phrasing a commit message, think of it in terms of what the commit does. Something like "If merged, this commit will ".
Good
- Add a button for generating random jokes
- Fix typo in network error message
- Allow users to use their name in jokes
Bad
- Update
- Added a button for generating random jokes
- add a bunch of stuff
Additionally, if you want to say more about your commit, you can add an extended commit message. If you're writing your commit message in vim, just add an empty line below the first line (think of that as the subject line), and then start typing below that empty line (think of it as the body of an email). Best practice is to add manual line breaks at around 80 characters in the extended commit message.