Skip to content

Instantly share code, notes, and snippets.

@sploiselle
Last active August 2, 2017 16:43
Show Gist options
  • Save sploiselle/ab0799eea0f19a0e7e609e4093f436ef to your computer and use it in GitHub Desktop.
Save sploiselle/ab0799eea0f19a0e7e609e4093f436ef to your computer and use it in GitHub Desktop.
So you want to contribute to a repo and need to make a fork...

Contribute with a Forked Repo: How to & Best Practices

If you want to work on someone else's GitHub repo, you'll start with a fork. After forking and cloning the repo locally, though, you're kind of on your own and there's a ton of Git-related work you need to do for a successful pull request.

To help you figure out this complex process, this guide walks you through making your first contribution (all the way to the point where you can start your second).

As you can probably see, it's a lengthy process. So, to keep this as brief as possible, this doc is light on conceptual details and heavy on execution.

Assumptions

The guide assumes you:

  • Have a GitHub account
  • Are familiar with maneuvering around Git (e.g. you know what branches, commits, and pull requests are)
  • Use command line to interact with Git (though the same workflow applies to desktop tools – just the steps are different)

Overview

  1. Talk to the Base Repo Committers
  2. Fork & Clone the Repo
  3. Learn the Repo
  4. Set the Base Repo as Upstream
  5. Create a Branch for Your Work
  6. Do Your Work
  7. Keep up with the Base Repo
  8. Fix Your Commit Messages
  9. Submit Your Pull Request
  10. Close Your Working Branch

Talk to the Base Repo Committers

Foremost, communication is key. Many of us are guilty of being over-eager and running quickly to get something done...only to find someone else already had it under way. Talking to the folks you want to work with can avoid that – and is just good etiquette for working with a team of people.

To get a sense of what work you should be doing, as well as who is doing the work, you can check out a repo's Issues. It's a good place to start having a conversation and find part of the project to work on.

We'll fast forward a little bit though and assume that because you're in this article, there's work that you know you ought to do.

Fork & Clone the Repo

  1. First, the easiest thing to do: fork the project.

  2. In the directory where you want to keep your work, clone the fork (i.e. your repo) to your local machine:

$ git clone https://github.com/your_GitHub_username/path_to_.git
  1. Move into the directory for your local fork:
$ cd baseRepoName

Learn the Repo

Every repo is different. It sounds like nothing but lip service to say, "Learn the repo," but you really have to take the time to understand it (as well as the code). The more time you invest here, the better your work will come across in the end.

Some things to notice:

  • What's the "master" called? Is it master? Is it gh-pages? In this doc, I'm going to refer to it by the variable master but it could be anything.
  • What is in each of the directories?
  • Read the README.md – the kind person who wrote it wrote it for a reason.

Set the Base Repo as Upstream

Because the base repo is 'up stream' from you work, all of its commits should be able to flow downstream to you. Setting this up now makes integrating your work with whatever other activity occurs in the base repo really easy.

$ git remote add --track master upstream https://github.com/base_repo_owner/path_to_.git

Create a Branch for Your Work

Here's a lesson I learned the painful way: treat your forked repo's master as sacred. Don't touch it. It should always mirror the base repo as closely as possible. All of your work belongs in a branch, the same as if you were working on a non-forked repo.

Create a new branch and move into it:

$ git checkout -b newFeature

Obviously, you can call the repo whatever you want but I'll continue using newFeature to refer to your working branch.

In your own branch, you're relatively safe from harm and embarrassment. You can hack away at whatever you want without the fear of ending up in some kind of rat's nest of competing commits with the base repo.

You should also move the branch up to GitHub at this point:

$ git push -u origin newFeature

Do Some Amazing Work

This is the fun part in the middle of all of the meta-work. Relish it. Iterate on something amazing.

Committing

When you're working, you can commit like you normally do without much fear. You're going to end up only sending one commit message at the end of the process, so there's no need to worry.

Pushing

As long as you're in your working branch, you can push just like you normally would:

$ git push

Keep Up with the Base Repo

There is probably activity going on in the base repo while you're off focusing on your work. You should periodically pull in all the commits it's accepted to ensure what you're working on doesn't conflict.

However, to make sure things don't go sideways, it's crucial that you do this in your fork's master and not your working branch.

  1. Move back to the master branch:
$ git checkout master
  1. Pull (i.e. fetch and merge) all of the changes committed to the base repo master:
$ git pull upstream master

Because you were incredibly diligent and worked only in your own branch, there won't be any merge conflicts and you're onto the next step – making sure your contribution keeps working

Review Changes & Test Your Work Against the Base

Your due diligence is to ensure your final product (i.e. the code in your pull request) actually works with the base repo. To save yourself enormous refactoring at the end, you should test it whenever your work is in a stable place.

  1. Review the incoming commits for potential conflicts. That can be a daunting task depending on the rate of changes, but the more work you frontload here, the fewer surprises you'll have during the next phase – merging and testing.

  2. After your review, go back to your working branch:

$ git checkout newFeature
  1. Merge your updated master with your working branch:
$ git merge master

If there's a gripe, you've got merge conflicts! We're going long enough here, so we'll fast forward a bit, assuming all went well and you're close to submitting your work.

Fix Your Commit Messages (rebase) Before Your PR

Once you're ready to submit your pull request, you should clean up your commits so they don't congest the commit history. Ideally, you want to massage everything down to a single, great, exceptionally clear commit message. To do this, we're going to use Git's rebase tool, which lets you manage your commits.

rebase's interactive mode (which you're going to use) opens a file in vim, showing you all of the commits that are 'behind' the branch or commit you target. From here, you can manage them – things like rewriting the commit message or collapsing many commits into one. Because we're not doing a deep dive here, you're just going to focus on using it with branches and ending up with a single commit for your pull request.

Make sure you're on your working branch:

$ git checkout newFeature

Start rebase:

$ git rebase -i master

You'll get the interactive rebase file, which has following format:

[action] [truncated hash] [commit message]

Here's an example:

pick 27a6a1a commit 1
pick 617eccd commit 2
pick 2c484b5 commit 3

Again, your goal is to end up with a single commit. First, we're going to change all but one instance of pick into fixup, which will let us keep our work but remove our commits.

However, when using rebase you cannot collapse an older commit in a newer commit; you can only collapse newer commits into old ones. (You'll see how that looks in just a second.)

You also want to rewrite the oldest commit message to encapsulate everything you've done, so you're going to change its action from pick to reword.

Assuming you're still in the rebase window...

  1. Press i to enter INSERT MODE.
  2. Change your commit's actions, so the first is reword and the rest are fixup. Here's what the previous example looks like now:
reword 27a6a1a commit 1
fixup 617eccd commit 2
fixup 2c484b5 commit 3

Note that you don't need to actually reword the commit 1 commit message yet – that'll happen in just a sec.

  1. Press esc to exit INSERT MODE.
  2. Type :wq and then press Enter.

You're now in the standard GitHub commit message editor. Come up with something that explains all of the great work you've done and save the changes (same process as before with INSERT MODE).

All of your work is in one branch with one commit (which is ideal), but because you've "lost" a bunch of commits, Git isn't going to let us just push back to newFeature, so you have to force it:

$ git push --force origin newFeature

Good work!

You aren't quite done yet, but I say take a moment to congratulate yourself on getting this far. On top of the actual work you did, you've also navigated the kind of byzantine path to generating a really beautiful pull request through a fork. Way to go.

Submit Your Pull request

Before submitting the PR, almost as a matter of ceremony, you should catch up with the base repo (outlined in Keeping up with the base repo).

Now. The moment when all of your hard work pays off.

Submit the pull request.

Now what?

What happens next depends on the code reviewers. They can either accept your PR, ask you to do some more work, or deny it.

Accepted

If all goes well, the recipient of your pull request will say, "Wow! This is so elegant. The code is brilliant!" and they'll accept it. At that point you can move on to Close Your Working Branch.

Needs work

Often times, this is the first chance for the base repo's collaborators to review the code you've submitted. It's not unusual for there to be additional requests to make things look or work a certain way.

Fortunately, once your branch is connected through a pull request, everything you commit is automatically added to it (which also means you should being mindful of your commits – though you can still rebase them at any time). You'll go through this process until it's accepted or you go onto the next option...

Denied

Sometimes things don't work out and your request is denied (which isn't always as harsh as it sounds). You'll need to close it without having your commits integrated into the base. Fortunately, because you had all of your work separated into another branch, it's quick and easy (skip to the last step in Close Your Working Branch.

Close Your Working Branch

Once your code has (hopefully) been integrated into the base repo, go ahead and pull it in using the upstream remote. (Though it's tempting, don't merge your working branch into your fork's master.)

  1. Move to your master:
$ git checkout master
  1. Fetch and merge all of the base repo master's new commits (which includes your work!):
$ git pull
  1. And the final bit is to remove your branch locally and remotely:
$ git branch -d newFeature
$ git push origin --delete newFeature

Begin Again

If you got all the way here, you did it. Now that you've successfully gone through an entire contribution flow once, push the boulder back down the hill and find another issue to tackle.

Credits & Thanks

If you'd like to improve this guide, either leave a comment below, fork it, or email me at [email protected]. I'd appreciate the help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment