Gitflow is a git workflow model with predictable branching patterns, including developing new features, shipping out releases, and handling hotfixes. Vincent Driessen came up with the original idea in 2010. Eventually helper CLIs came out (which I recommend using) that hide away the complexity of the flow steps. I will write about these in an upcoming post. Although, I find it useful to know what is going on under the hood before committing to a new automation toolset.

This blog post goes through a quick demo of a project using gitflow, and the various steps it goes through, from working on a feature to releasing it into production.

You can clone the repo for this post here https://github.com/joeshub/gitflow-demo, or better yet follow along step by step.

Start a new sample git project

mkdir gitflow-demo && cd gitflow-demo
touch README
git commit -am "first commit"
git remote add origin git@github.com:joeshub/gitflow-demo.git
git push -u origin master

Develop branch

Our Working tree is always the develop branch in gitflow. Create a new develop branch locally and push it up. You should now have 2 branches upstream; master and develop.

git checkout -b develop
git push -u origin develop

Feature/xyz branches

New development in gitflow is always done in a feature branch, based off the develop working tree branch. Feature branches are by convention prefixed with feature and a slash, so the format is feature/foo

git checkout -b feature/foo develop
echo "console.log('foo');" >> foo.js
git commit -am "Adding foo file"

Now let's assume we or another team member wants to simultaneously work on a different feature. We just create a new branch for it that tracks develop.

git checkout -b feature/bar develop
echo "console.log('bar');" >> bar.js
git commit -am "Adding bar file"

Merging features into develop

When ready, we send a pull request for the feature so it can be merged into the develop branch.

For simplicity, here we are going to just merge feature/foo back into develop without sending an PR, and push it back into develop.

git checkout develop
git merge feature/foo
git push

Rebasing from develop into a feature branch

Now let's switch to our feature/bar branch. To get the latest changes that have landed in develop, we need to rebase from there. This will do a rewind/apply/replay so that your changes get added on top of any new updates to develop since you last pulled. You will want to do this rebasing from develop step often while working on a feature branch to avoid messy conflicts.

git checkout feature/bar
git rebase develop
# First, rewinding head to replay your work on top of it...
# Applying: Adding bar file

If you run a git log you will see your change to bar.js is now the last commit in your branch, and the other foo features that have already landed in develop.

git log --pretty=oneline --abbrev-commit
# 8b404a5 (HEAD -> feature/bar) Adding bar file
# 195431c (origin/develop, feature/foo, develop) Adding foo file
# fed3007 (origin/master, master) initial commit

Now we just repeat the same steps as before. We merge and push our feature upstream into develop. Again we are skipping the PR step here for simplicity.

git checkout develop
git merge feature/bar
git push

Releasing code

We always create release branches from develop. They are always prefixed with release/ in gitflow, for example release/1.0. This allows work to continue on develop, while we do a release. We then push our new release branch upstream. Normally a build & deploy step runs which pushes the release to a staging environment for testing and QA.

git checkout -b release/1.0 develop
git push -u origin release/1.0

Fixing issues after a release

Looks like QA found a bug! In this case, bar.js needs to output text in all caps.

To fix bugs found in a release branch, we make edits to the release branch and merge those changes back into the release branch, and normally this goes through a PR step as well. In larger teams, you can also create a new branch to track the bug fix and send a PR to merge this into the release branch.

echo "console.log('BAR');" > bar.js
git commit -am "hot fix bar.js"
git push

After bug fixes are released, we need to make sure to merge the release branch changes into our main develop branch.

git checkout develop
git merge release/1.0
git push

Releasing to production from master

Once a release is tested, it is then released into production. So we will merge our release branch into master as well.

git checkout master
git merge release/1.0
git push

Tagging master allows us to track the release version that's currently out on production. Once we've tagged master we are finally ready to deploy that tag into production.

git tag v1.0.0
git push origin v1.0.0

Hot fixing bugs in production

After our release went out, marketing looked at the ALL CAPS and decided it wasn't very nice. So we need to do a hot fix to our production code.

Using the gitflow naming convention, we create a branch off of master named hotfix/ and the bug being fixed, for example hotfix/fixes-bar-caps.

git checkout -b hotfix/fixes-bar-caps master
echo "console.log('bar');" > bar.js
git commit -am "hotfix removes all caps in bar.js"

Once ready, we will merge the hotfix into master. Then we'll re-tag a release and push that tag to master.

git checkout master
git merge hotfix/fixes-bar-caps
git push
git tag v1.0.1
git push origin v1.0.1

We also need to make sure to merge the hotfix back into our develop branch, to keep it up to date with master.

git checkout develop
git merge hotfix/fixes-bar-caps
git push

Cleaning up branches after a release

After you're done with a release, it's good to clean up branches that have already been merged in. At this point you will have the following branches:

git branch -avv
# develop 00b2d00 [origin/develop] hotfix removes all caps in bar.js
# feature/bar 8b404a5 Adding bar file
# feature/foo 195431c Adding foo file
# hotfix/fixes-bar-caps 00b2d00 hotfix removes all caps in bar.js
# master 00b2d00 [origin/master] hotfix removes all caps in bar.js
# release/1.0 c26f0f3 [origin/release/1.0] hot fix bar.js
# remotes/origin/develop 00b2d00 hotfix removes all caps in bar.js
# remotes/origin/master 00b2d00 hotfix removes all caps in bar.js
# remotes/origin/release/1.0 c26f0f3 hot fix bar.js

To clean up we can delete the feature and hotfix branches.

git branch -d feature/foo
git branch -d feature/bar
git branch -d hotfix/fixes-bar-caps
git branch -avv
# develop 00b2d00 [origin/develop] hotfix removes all caps in bar.js
# master 00b2d00 [origin/master] hotfix removes all caps in bar.js
# release/1.0 c26f0f3 [origin/release/1.0] hot fix bar.js
# remotes/origin/develop 00b2d00 hotfix removes all caps in bar.js
# remotes/origin/master 00b2d00 hotfix removes all caps in bar.js
# remotes/origin/release/1.0 c26f0f3 hot fix bar.js

Automating gitflow with tooling

As I mentioned earlier, while knowing all the inner workings of gitflow is helpful, there are tools built that automate some of these steps. I'll write a quick post on those tools next.


References