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
- Check out the
git checkout dev
- Update my
devbranch with the latest from the remote repository:
git pull origin dev
- Create and checkout a branch off
git checkout -b larger-pics
- Make my modifications and test as much as I can, staging and committing my changes after successfully testing each piece of the new functionality.
- Then update my
devbranch again, so when I merge back, hopefully it's a fast-forward merge:
git pull origin dev
- I'll interactively rebase my
larger-picsbranch 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
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
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
devbranch 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.
- If I think it might be a while before I'm able to fix my change, I'll use "
git revert <SHA code>" to undo the commit. Either way, I'll again checkout my
git rebase dev, then make changes,
git pull origin dev,
git rebase dev,
git checkout dev,
git merge larger-pics,
git push origin dev. Let's say Jenkins gives me the thumbs up now.
- 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 York Magazine'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
git checkout larger-pics
- It's time to rebase my commits onto the
qabranch, 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 add my branch to the remote repository:
git push -u origin larger-pics
- I go to the repository on Github and create a pull request, requesting my
larger-picsbranch be merged into the
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
qabranch 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
prodbranch 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.
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.