Design

Git Tutorial: Branches and Workflow

By Scott Danzig, September 09, 2013

Pulling changes, handling merges and conflicts, and building a productive workflow are activities that Git handles in its own productive but unique way.

I'm often tasked with a modification I must make to a shared project hosted as a Github repository as I described. On Github, I have a separate user, "scottdanzig" for my Github activity, which allows clear separation of my personal projects from what I've done that for the magazine. For my examples, I'll refer to a Web application created with Scala and the Play Framework, that provides restaurant listings for your mobile device. Let's say we realized that the listings load very fast, and we can afford to display larger pictures. Here is my preferred workflow:

The first thing I do is change the status of the JIRA story I'm going to work on to "In Progress."

If I don't yet have the project cloned onto my machine, I'll do that first: git clone https://GitHub.com/nymag/listings.git

I'll interactively rebase my larger-pics branch onto my dev branch. This gives me an opportunity to change all my commits to one big commit, to be applied to the latest commit on the dev branch: git rebase -i dev. I write one comprehensive commit message detailing my changes so far, making sure to start with the JIRA story number so people can review the motivation behind the change. It's possible I might want to not combine all my commits yet. If I'm not sure if one of the incremental changes is necessary, I may decide to keep it as a separate commit. This is possible if you leave it as a separate "pick" during the interactive rebasing. Git will give you an opportunity to rewrite the commit description for that commit separately.

Checkout the dev branch: git checkout dev

Merge in my one commit: git merge larger-pics

Push it to Github: git push origin dev

If Git rejects my change, I may need to rebase my dev branch onto origin/dev, and then try again. We're not going to combine any commits, so it doesn't need to be interactive: git rebase origin/dev then again: git push origin dev

Jenkins will detect the commit and kick off a new build. I can log into the Jenkins Web interface and watch the progress of the build. It's possible the build will fail, and other developers will grumble at me until I fix the now broken dev environment. Let's say I did just that.

Next stage is the code review. I'll log into Crucible and advertise my list of commits in the dev branch for others to review. I can make modifications based on their feedback if necessary.

Let's say both Jenkins and my fellow developers are happy. It's time to submit my code to QA. The QA branch is automatically deployed by Jenkins to the QA servers, a pristine environment meant to better reflect what actually is accessed by New YorkMagazine's readers. We have some dedicated QA experts who systematically test my functionality to make sure I didn't unintentionally break something. If there are no QA experts available, QA might be done by another developer if the feature is sufficiently urgent.

I need to update my local QA branch so I can rebase my changes onto it, pushing fast-forward commits. I first type: git pull origin qa

Then I change to my larger-pics branch: git checkout larger-pics

It's time to rebase my commits onto the qa branch, rather than dev, which can be polluted by the works in progress of other developers. I type: git rebase -i qa, creating a combined commit message describing my entire set of changes. I now have a branch that is the same as QA, plus one commit that reflects all of my changes.

I go to the repository on Github and create a pull request, requesting my larger-pics branch be merged into the qa branch.

At this point, it's out of my hands, for the time being. However, the project has a "maintainer" assigned.

The maintainer can first use the Github interface to see the changes. The maintainer can give a last check for the code.

If approved, the maintainer must merge the branch targeted by the pull request to the qa branch. If the commit will have no conflicts, Github's interface is sufficient to merge the change. Otherwise, the maintainer can reject the change, requesting for the original developer of the change to rebase the branch again and resolve the conflict before creating a new pull request. Otherwise, the maintainer can check out the branch locally and resolve the merge, rather than the original developer doing it.

The maintainer commits the merged change and updates the JIRA story to "Submitted to QA."

If QA finds a bug, they will change the JIRA status to "Failed QA." The maintainer will checkout the QA branch and use "git revert" to roll back the change, then will reassign the JIRA ticket back to the original developer.

If QA approves the change however, they will change the JIRA status to "Passed QA."

At regular intervals, a development team will release a set of features that are ready. A release consists of:

A developer merging QA-approved changes from the qa branch to the staging branch.

Members of the team having a last look at the change's functionality in the staging environment.

The developer of a change, after confirming that it works correctly in staging, merges the change into the prod branch before a designated release cutoff time.

The developer changes the status of the JIRA story to "Resolved"

The system administrators deploy a build including the last commit before the cutoff time. For us, this entails a brief period of down-time, so the release is coordinated with the editors and others who potentially will be affected.

Further Thoughts

That's a summary of how I work, and although everything is sensible, it's a bit in flux. These are things which could be changed:

We can get rid of the staging environment, and merge directly from QA. I see the value in this extra level of testing, but I believe four stages is a bit cumbersome.

A project does not necessarily need a maintainer, and if we use Crucible, perhaps not even pull requests. A developer can merge his change directly into the QA branch and submit the story to QA on his/her own. I prefer to have a project maintainer.

We can get rid of Crucible, and just use the code review system in Github. It might not be as feature-filled, but if we use pull requests, it's readily available and could streamline the process. I like Crucible, although it might be worth exploring eliminating this redundancy.

After years of using many other version control systems, Git has proven to be the one that makes the most sense. It's certainly not dependent on a reliable Internet connection. It's fast. It's very flexible. After more than twenty years of professional software development, I conclude Git is an absolutely indispensable tool.

Scott Danzig has been programming for more than 20 years. His personal projects on Github can be found at https://Github.com/sdanzig.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!