One thing I hear a lot from teams that aren’t using Git yet is that they’ve checked it out but it feels really complicated. I remember diving into Git at first and feeling overwhelmed, so I can definitely sympathize. There are so many concepts presented, and very often each article you read talks about a different way to use Git to control your source. In this article we’ll talk about the workflow that we use here at Carsonified. We like to keep things simple, and hopefully this will help some teams that are thinking about jumping to Git make the switch.
Why Use Git?
We absolutely love using Git, and that’s a big deal when I think back and remember how many version control systems I’ve had a bad relationship with, like Subversion, CVS, and Visual SourceSafe. For me there are two big contributors to why I love Git – it’s distributed and it makes branching extremely inexpensive.
Git is a distributed version control system. That means that there can be any number of repositories, unlike version control systems like Subversion that have one primary repository on a central server. It seems like having any number of repositories would be chaotic, but in practice most teams have a central server that they sync the repositories in their local project working directories to. Having those repositories on your local computer is a big deal, though, because it means that you can do anything you would normally only be able to do while connected to the central repository without actually connecting to it. That means quick commits because they don’t have to sync with the server and also means being able to create experimental branches even when you don’t have internet access. It’s awesome!
The other thing I love about Git is that branching is cheap. With version control systems I’ve used in the past the teams I’ve worked with have been afraid to create branches because it’s too difficult to merge those branches back into the main branch. With Git that fear is basically gone. You can create a new local branch if you’re making a quick change, or push the branch up to the server if you’ll be working on it for a while, and when you’re done it’s a piece of cake to merge your branch back into your main branch. You’ll see below that we use tons of branches at Carsonified. We’re never scared of what might happen because of all that branching.
While Git is amazing because of these features, it’s got a ton more features and a ton of different ways teams can use it. That can get messy, so at Carsonified we have a few simple rules we use with our repos to keep things easy for everyone on the team, even folks who are a little more shy about Git. Our workflow is completely compatible with GUI tools like Github for Mac and GitX. I know a ton of designers who are Terminal heroes, but I’ve also talked to many who really prefer to work with Git with a desktop app, so this workflow is inclusive for them.
Here are those rules:
1. Write new features in feature branches.
2. Integrate on the master branch.
3. Deploy from the release branch.
We’re not covering the basics of pushing, pulling, fetching, adding, and committing with Git here, but there are some great resources available to help you learn the basics. One great site for learning Git is Git Immersion.
Write Features In Feature Branches
When we’re working on a new feature that’s going to be comprised of a decent amount of code, we create a new branch named after the feature. That keeps the new feature out of the way if we need to put together a quick bug fix and also helps out with keeping different team members from stepping on each other’s toes as they work. I’d normally name a feature branch based on the feature, so for example our recent conversion to HTML5 video was in a branch called “video_workflow” because it included a lot of changes concerning how we publish video. For the sake of example, though, I’ll just call it “feature_branch” here. Here’s how we create that new remote branch:
# create a new feature branch in the remote repo named feature_branch
git push origin master:refs/heads/feature_branch
# make sure we've got everything updated from the remote repo
git fetch origin
# create a local branch named feature_branch that tracks
# the remote feature_branch
git branch --track feature_branch origin/feature_branch
# check out the new branch
git checkout feature_branch
If you’re thinking that’s a lot of commands, you’re right. I use a tool called grb, or Git Remote Branch, that makes working with remote branches a ton easier by wrapping up common git operations. It’s easy to install if you have RubyGems on your computer (if you have a Mac you do):
gem install grb
and now with grb installed you can ditch that huge list of commands above and just type:
grb create feature_branch
Now that you’re on the new branch you can work like you normally would with Git, adding, committing, and pushing code. From time to time it’s helpful to merge any master branch changes that have happened since creating your feature branch into the feature branch. That helps you make sure that you’re taking those changes into consideration as you work. Obviously if nothing has changed on your master branch you won’t need to do this, but there’s no harm in running the command, as it will just tell you that everything’s up to date:
git merge master
Once you’re done creating your new feature, you’ll integrate it back into the master branch.
Integrate On The Master Branch
To integrate the feature branch back into the master branch, you’ll first want to merge your master branch into your feature branch one last time to make sure that everything merges correctly. You merge it just like we did above with:
git merge master
Now you can check out your master branch and merge the feature branch into it. Since we just merged the master branch into the feature branch, it’s guaranteed to merge cleanly back into the master branch.
git checkout master
git merge feature_branch
If you’d like you can also delete the feature branch from your local and remote repositories, since you just finished up the feature and won’t need it anymore.
# this has to be in competition for one of the least intuitive
# commands ever, but it removes the remote branch
git push origin :refs/heads/feature_branch
# remove the local branch
git branch -d feature_branch
This is another spot where grb can help out if you have it installed, by turning those two commands for deleting the branch into one far more intuitive command:
grb rm feature_branch
At this point our master branch includes our new feature and we’re ready to work on another feature or deploy the new code.
Deploy From The Release Branch
I like to have a special branch dedicated to the code that I release. The standard for code getting into that branch is that it’s ready to deploy. That usually means that its tests have been run and are passing, and that the team’s generally happy with the code. You can create your deploy branch just like we did with the feature branch. I usually name my deployment branches “deploy”. Creative, huh?
So let’s say you’ve integrated some code into your master branch and now you’re ready to deploy the code. Here are the commands I use to merge that code into the deployment branch and tag a release:
# checkout the deploy branch
git checkout deploy
# pull the deploy branch, just to be sure everything's up to date
git pull origin deploy
# merge the master branch into the deploy branch
git merge master
# tag the release that we're about to make
git tag -a -m "My comments about this release" [tag_name]
# push it all up to the remote repository
git push --tags origin deploy
# switch back to the master branch,
# since we never do any work on the deploy branch
git checkout master
I usually bundle up the deploy commands into a script that I share within the repository so that it can be run easily by anyone on the team who needs to merge and tag a release. Those scripts usually include some sort of date and time based mechanism for automatically creating the tag name, since version numbers usually don’t make a lot of sense when you’re deploying your application several times a day like we typically do with our web apps.
Once you’ve merged and tagged the release on the deploy branch, you’re ready to deploy it. How you deploy it depends on what tools you’re using and how your team handles deployments. That’s certainly a far bigger topic than we have time for here, but I can say that we use and like Capistrano for our deployments.
It may seem overwhelming if you’re new to git, but once you get the hang of this workflow it’s a piece of cake. It’s not unusual for me to have one major feature branch that I’m working on while a designer is doing some work on another feature branch, and we’ll often have several experimental branches that include work on ideas as well. We almost never run into problems of one person’s commits messing up another’s, and our master branch is always ready for a quick bug fix and deploy, if needed. We’ve really enjoyed this workflow and hope you’ll enjoy it too!