Managing source with a distributed team

Background

Many months ago we switched from using subversion to using git. A couple of our braver folks pushed this change through in the face of a few complaints from other team members. There is a learning curve. We got through this learning curve more quickly than I expected. Now looking back, I can see that the learning curve has several plateaus followed by steep inclines. In this post, I will describe where we were in our git scm process and where we are now. Hopefully this information will help you more fully embrace using git or at least help you think on and improve your process. If you think your process is better or you see anything in ours that is glaringly wrong or could use a tweak, please post a comment.

Then

After the initial learning curve, we settled into the use-git-like-we-used-svn process. Apart from pull-before-you-push, it was a free for all. What does this kind of process create? Well, a pair might tackle a user story for the client’s next release. The end of the day comes and they want to preserve their work. They make sure the tests pass. Commit and push. Fortunately, the repository did not have anyone else’s changes. They go home comfortable that the code changes will survive if their laptop or pairing station has a catastrophic disk failure.

The catch. The catch is they haven’t completed the feature. Maybe they will complete it tomorrow and the client will accept it before the next release. Then again, it is nearly guaranteed that we will want to deploy a feature release or a maintenance release before some feature is fully implemented.

At that point, we decided to create a remote branch for production and deploy from that branch. This sounded like a good choice. It did help us segregate changes ready for release and changes that were works in process. If you are currently using git and using it more to its full capability, you are probably smirking or cringing as you read this.

We cherry-picked and merged changes from master to production in preparation for deployment. This prep process was sometimes easy and straightforward and sometimes terrifying. Regardless, the final result was a production branch and a master branch that often contained the same code, but had a wildly varying commit history.

The preparation for some releases became excruciating. Even if preparing the release went smoothly, over time, this step in our process became a significant smell that begged to be addressed.

Now

After a bit of research and poking and prodding of our current repository, we have settled on the following process. Assume that there are no local modified files and no staged changes. Assume that the local tracking branches are up-to-date with the remotes that they track.

Bug or Feature

  1. Create a local branch from the production branch.
  2. Write tests and implement features commiting locally along the way.
  3. Clean up the commits using git rebase --interactive. This is helpful in taking several commits and squashing them into a single commit for the feature.
  4. Publish the local feature branch to a remote repository. The grb gem is a handy tool to help you work with remote branches. If we have a particularly urgent bug, we will likely skip this step and stage the fix for review by the client. It is more common though to publish to a remote to share the code with the rest of the team.
  5. Repeat No. 3 and No. 4 until the feature is ready for delivery or the bug is fixed. If the feature is large or the ultimate deliverable is a set of features for a particular release, each pair should interact with the remote in the standard way. The standard way is to git fetch the remote and rebase or merge in any changes before pushing your commits back to the remote.
  6. The feature branch can be rebased to production to keep it up-to-date with other changes. Coordinate the rebase with the team, as they will need to set up a new tracking branch.
  7. With the feature or set of features ready for deliver, we merge the branch down onto a staging branch for deployment to a client acceptance server.
  8. If the features are rejected, we are back to No. 3 and No. 4 above.
  9. After the features are accepted, we prepare it for a deployment by rebasing the local branch against production and then merging that branch back onto production.
  10. If for some reason, we need to work on the branch after it has been rebased, we will git push --force the rebased version to the remote after confirming with the team that no one is in process of making changes.

NOTE: Please only use git push --force if you really know what you are doing!!

It is worth stating that we run the test suite locally and on our CI server throughout this process.

Good/Bad/Lessons learned…

We are using this process on small team distributed projects. Prepping for and deploying releases has become almost trivial. This could simply be improvement that we experience by fixing a problem we caused. In any case, the repository history is much cleaner.

There is definitely a lot to learn in using git in your particular process. We are looking forward to continuing to improve our source control maintenance process.

UPDATE

A couple clarifying points…

  • production is the branch on which we want to keep a clean history. We also use a staging branch to collect features and bug fixes for deployment to the staging server. Your master may well be used as either the staging branch or the production branch. In the process above, we do not commit to master or staging or production directly. We simply merge in the features that are needed for a particular staging or production deployment.
  • Using this process, you will invariable rebase a feature branch to merge it into production prior to deploying a release. At this point, we typically delete the remote feature branch. Any additional work that comes up related to that feature can go through the entire process of obtaining a new branch off of production.