With Astrid for Android, we’ve transitioned from svn (Google Code) to bzr (Launchpad) to git (Github) within the last two years. As the primary committer, I’ve loved git, but it has caused a lot of trouble for our collaborators. This post is designed to alleviate that pain by (1) explaining some things, and (2) clarifying the workflow.
Think of this article as a list of things to do / not to do (at least when contributing to Astrid, and probably other small open/closed source projects), with a recommended workflow at the end.
Collaborator merging considered harmful
If you’ve made local commits:
[upstream] --- [upstream] --- [you] --- [you]
You almost never want to git merge / pull. In fact, if you don’t have write privileges to your repository, you should probably never use git merge or pull. Those are the wrong commands to use, because you end up with extra “merge” commits.
For example, suppose you have:
--- [you] --- [you] / [upstream] --- [upstream] \ --- [teammate] --- [teammate]
When you perform the merge, you will end up with:
--- [you] --- [you] --- [merge] / / [upstream] / \ / --- [teammate] --- [teammate]
The problem is that whoever is going to merge your code eventually won’t want your merge commits. Ideally, the project’s history should look like this:
[upstream] --- [upstream] --- [teammate] --- [teammate] --- [you] --- [you]
In order for that to happen, you have to make your repository look like that first, by rebasing your changes on top of upstream. In addition, when remote branch gets new commits, you will have to merge every time.
--- [you] --- [you] --- [merge] --- [merge] --- [merge] / / / / [upstream] / / / \ / / / --- [teammate] --- [teammate] --- [.....] --- [.....]
That’s really bad! The best way to avoid this is to follow two principles:
- use rebase instead of merge / pull
- use visualization tools (text or graphical) to figure out your history
To facilitate in the latter point, I use an alias called git lola, which maps to:
lola = log --all --graph --decorate --pretty=oneline --abbrev-commit
It displays every branch with lines that show who is descending from what. In addition, here’s the workflow you should use in general:
Recommended workflow for collaborators
This workflow is intended for situations with one upstream repository that collaborators have read-only access to. This is often called the integration-manager, or benevolent dictatorship workflow.
In the recommended workflow, users will have two repositories. The first is upstream, which is the official repository that everyone works from. The second is origin, which is your fork of the repository that you have commit access to. From github, if you fork a project and then clone it to your hard disk, all of this is set up for you automatically.
Now, you want to start working.
# loads what's new from upstream git fetch upstream # create a new branch from upstream/master. I usually prepend the date # (YYMMDD) for sorting purposes git co -b 110416-new-stuff upstream/master # do some work. git add ... git commit # verify that tests pass! # perform a diff to make sure you know what you're committing git diff upstream/master # are you ready to push? let's push HEAD (current branch) to origin git push origin HEAD # if not, or you're ready for something else, time to create a new branch git co -b 110416-some-more-stuff upstream/master
The last step is very important: send a pull request (either via github or email) that specifies which branch should be pulled. Otherwise, no one will know what you’ve done.
Here, we’ve assumed that there have been no changes upstream that could cause a conflict. That might not always be the case (say you’ve been working on the patch for a while). If you’re on a feature branch, and you want to pull the latest stuff:
# loads what's new from upstream git fetch upstream # rebase your current work on top git rebase upstream/master # you might have conflicts. fix them! * # now you can keep working, or push, as above
(* if you’re interested, here’s a simple batch-resolve script).
Finally, if you ever mess something up, remember the magical “go back in time and fix history” command: git rebase -i. Very powerful!
If you want to read a LONG, comprehensive article, Pro Git has a whole chunk on committer workflows. The workflow described here is equivalent to the Private Managed Team / Public Small Project section of that page.
Questions, comments, concerns?