By John Duff
A little while back, Rodrigo Flores posted to the plataformatec blog, A (successful) git branching model, where he talks about the git workflow they've been using on some projects. I thought this was a great post and decided to do something similar explaining the git workflow that we use at Shopify.
Preface
Git is an incredibly powerful tool that can be used in many different ways. I don't believe there is a 'correct' workflow for using git, just many different options that work for particular situations and people. The workflow that I am going to describe won't work for everyone, and not everyone at Shopify uses git in the same way - you have to modify and massage it to shape your needs and the way you work. I don't consider myself an expert with git, but am comfortable enough with the tool to handle just about everything I might need to do. If there's anything I can't figure out, James MacAulay is our resident git expert in the office and always willing to help out.
Okay, lets get down to business.
Setup
When working on a project each developer and designer first forks the repository that they want to work on. Forking a repository is really simple, Github even has a guide if you need some help with it. A fork is basically your own copy of the repository, that you can change without affecting anyone else. We use GitHub for all of our projects so it makes managing the forks really easy. All the work is done on your fork of the repository and only gets pulled into the main repository after it has been fully tested, code reviewed, etc. We also use the concept of feature branches to make it easy to switch between tasks and to share the work with other colleagues. A branch is kind of like a fork within your own repository, you can have many branches within your forked repository for each of the tasks you're working on. Your checkout of a project should be setup with a couple of remotes and branches to get started.
Remotes:
- origin - This is a remote pointing to your clone of the project and added by default when you do 'git clone'.
- mainline - This is a remote pointing to the main repository for the project. We use this remote to keep up to date and push to the main repository.
Branches:
- production - This is the production branch of the main repository (or mainline). This is the code that is ready to be deployed to production.
- staging - Contains the code that is being run on the staging server (we have two that developers can use). Before a feature is considered 'finished' it must be tested on one of the staging servers, which mirrors the production environment.
- master - Contains completed features that can be deployed.
So how do we set all this up? These couple of git commands should take care of it:
git clone git@github.com:jduff/project.git git remote add mainline git@github.com:Shopify/project.git
Keeping a project up to date is also really easy, you just pull from mainline.
git checkout master git pull --rebase mainline master
I know what you're thinking, what the heck is that 'rebase' doing in there? Well, you don't really need it, but it helps to use it in case you've merged a new feature that you haven't pushed yet. This keeps the history all tidy with the changes you made on top of what is already in master instead of creating an additional "merge" commit when there's a conflict.
Day To Day Usage
So how does all of this work day to day? Here it is, step by step:
git checkout master git checkout -b add_awesome # Feature branches, remember # Do some work, listen to a lightning talk, more work git commit -m "Creating an awesome feature"
Mainline master moves pretty fast so we should keep our feature branch up to date
git checkout master git pull --rebase mainline master git checkout add_awesome git rebase master
Everything is finished, test it out on staging!
git push -f mainline add_awesome:staging # This blows away what is currently being staged, make sure staging isn't already in use!
Staging is cool...code review...high fives all around, ship it!
It's always easier to release a feature if master is up to date and you've rebased your branch. See above for how to 'keep our feature branch up to date'. We also make sure to squash all the commits down as much as possible before merging the feature into master. You can do this with the rebase command:
# Rebase the last 5 commits git rebase -i HEAD~5
Now we can merge the feature into the master branch:
git checkout master git merge add_awesome git push mainline master
And if you want your code to go out to production right away you have a couple more steps:
git checkout master git pull mainline master # Make sure you're up to date with everything git checkout production git merge master git push mainline production # Ask Ops for a deploy
That's about it. It might seem like a lot to get a hang of at the start but it really works well and keeps the main repository clear of merge commits so it's easy to read and revert if required. I personally really like the idea of feature branches and rebasing as often as possible, it makes it super easy to switch tasks and keeps merge conflicts to a minimum. I almost never have conflicts because I rebase a couple of times a day.
A Few More git Tips
I've got a couple more tips that might help you out in your day to day git usage.
# Unstage the last commit git reset HEAD~1 git reset HEAD^ # Same as above # Remove the last commit from history (don't do this if the commit has been pushed to a remote) git reset --hard HEAD~1 # Interactive rebase is awesome! git rebase -i HEAD~4 git rebase -i HEAD^^^^ # Same as above # Change the last commit message, or add staged files to the last commit git commit --amend # Reverses the commit 1b9b50a if it introduced a bug git revert 1b9b50a # Track down a bug, HEAD is bad but 5 commits back it was good git bisect start HEAD HEAD~5
Conclusion
So there you have it, that's how we use git at Shopify. I don't know about everyone else, but once I got going I found this workflow (particularly the feature branches) to work very well. That doesn't mean this is the only way to use git, like I said earlier it is an incredibly powerful tool and you have to find a way that works well for you and your team. I do hope that this might serve as a starting point for your own git workflow and maybe provide a little insight into how we work here at Shopify.
Our tools and the way we use them are constantly evolving so I would love to hear about how you use git to see if we might be able to improve our own workflow. Let us know in the comments or better yet, write you own blog post and drop us the link!
Photo by Paul Hart