I spent some time this week helping a colleague learn the ins and outs of Git rebase. He mentioned that he could not find a git introduction that summarized the basic workflow you might use when contributing to an open source project on Github. This article attempts to fill the gap.
Note that there are many possible ways to interact with git and github and none of those ways are canonically correct. The steps here will help a newcomer to git or to collaborative editing on git. I outline the steps in short form first, then summarize alternatives below.
- Fork the remote project on github.
- Clone your new fork.
git clone ssh://email@example.com/[yourname]/[reponame]/ cd [reponame]
- Add a shortcut to upstream.
git remote add upstream https://firstname.lastname@example.org/[developername]/[reponame]/
- Optionally choose a branch to hack on.
git checkout development_branch_name
- Create a local copy of that branch.
git checkout -b working_branch_name
- Commit your changes
git add [-p] filenames git commit
Repeat the hack,add,commit steps until your commits look the way you want them to.
- Grab any commits that were added upstream while you were working.
git checkout development_branch_name git pull upstream development_branch_name
- Rebase your work onto to the end of the development branch.
git checkout working_branch_name git rebase development_branch_name
- Push your commits to your github fork for the world to see.
git push origin working_branch_name
- Make a pull request on github. Pick
development_branch_nameas the one you want your code pulled into, and working_branch_name as the one you are pulling from in your repo.
- Oh nos! The upstream devs didn’t like your changes. That’s fine. Repeat the hack/commit step as needed. You may
be able to just append commits to your previous commits. If you need to alter your history (e.g. because they don’t like how one of your commits looks), check the man pages for
git commit --amendand
git rebase -i.
- Once you have the working_branch in a state you like, repeat the remote pull and rebase steps to ensure your commits “look like” they happened at the end of the remote developer’s branch.
- Push your changes to your github fork, forcing an overwrite of any existing changes.
git push -f origin
- Issue another pull request on github; your old pull request will be updated to the new working branch.
- Your code has been accepted upstream, congratulations on your contribution to an important open source project!
- This represents a branch the upstream developer has pushed to their remote repository. The branch chosen really depends on where you want to develop. Some projects do all their development on master. Others push a remote branch named ‘devel’ or branches named after the feature being developed on that branch. Either way, your ultimate goal is to have your commits added to this branch on the remote repository.
- It is possible to do your development on a single branch and rearrange commits as needed. However, branching is free in git, and storing your changes on a different logical branch will really help you identify where everything fits together.
- Each commit should contain one logical change.
A commit message should not contain the word “and”.
If you have been working on two logical changes at the same time, you can index only relevant hunks using
git add -p.
- You’re checking out the original branch here. You don’t want to pull all the foreign commits into the branch you’ve been developing on; that branch should only contain your commits at this time.
This is the same as “moving” all your commits
to the end of development. The idea is to have your commits as a “tail” that all come after the upstream changes. Upstream will love this because they can do a fast-forward merge of your commits. Hopefully
there aren’t any conflicts. If there are, resolve them so that your commits can apply cleanly. You’ll want to review the rebase documentation before continuing. Also, use three way merging, it makes life so much easier.
- There is a philisophical debate as to whether it is acceptable to rewrite public history, as Dieter has discussed in detail. In this case, since you have communicated (via the pull request) an intent to rewrite history, it’s not only acceptable, but desirable.Most upstream developers will consider it good form to edit older commits
so that they reflect what the history “should have” looked like so that people reading your history can understand it.