Gitflow Explained: The Branching Model Every Dev Should Know

You just joined a new team. You clone the repo, create a branch, push some code... and suddenly your teammate's work is gone. The senior dev sends you a Slack message: "Did you just push to main?"
Sound familiar? This is exactly why teams use branching models — and the most well-known one is Gitflow.
Created by Vincent Driessen in 2010, Gitflow is a structured branching strategy that gives every type of work its own lane. It's not the only model out there, but understanding it will make you a better team developer — even if your team uses something simpler.
What You'll Learn
✅ What Gitflow is and why it exists
✅ The 5 branch types and their purpose
✅ Step-by-step workflow for features, releases, and hotfixes
✅ When to use Gitflow vs. simpler models
✅ Common mistakes and how to avoid them
The Problem Gitflow Solves
Without a Branching Model
Imagine a team of 5 developers, all pushing to main:
# Monday morning
git pull origin main # 3 conflicts from Friday
git push origin main # Breaks the CI pipeline
# Someone deploys broken code to production 💥What goes wrong:
- No clear separation between "in-progress" and "ready" code
- Production hotfixes get tangled with half-done features
- Nobody knows which commit is safe to deploy
- Releases become stressful all-nighters
With Gitflow
Every type of work has its own branch type. Features are isolated. Releases are planned. Hotfixes go straight to production without disrupting ongoing work.
The 5 Branch Types
Gitflow uses two permanent branches and three temporary branch types:
| Branch | Lifetime | Branches From | Merges Into | Purpose |
|---|---|---|---|---|
main | Permanent | — | — | Production-ready code |
develop | Permanent | main | — | Integration branch |
feature/* | Temporary | develop | develop | New features |
release/* | Temporary | develop | main + develop | Release preparation |
hotfix/* | Temporary | main | main + develop | Emergency production fixes |
Let's break each one down.
Permanent Branches
main — The Production Branch
This branch always reflects what's running in production. Every commit on main is a deployable release.
Rules:
- Never commit directly to
main - Only merge from
release/*orhotfix/*branches - Every merge gets a version tag (e.g.,
v1.0,v1.2.1)
# You should never do this:
git checkout main
git commit -m "quick fix" # ❌ Don't!
# Instead, always merge from a proper branch:
git checkout main
git merge --no-ff release/1.0
git tag -a v1.0 -m "Release version 1.0"develop — The Integration Branch
This is where all completed features come together. Think of it as the "next release" staging area.
Rules:
- Features merge here when they're done
- Should always be in a working state (CI passes)
- When
developis stable enough, a release branch is created from it
Temporary Branches
Feature Branches — Building New Things
Every new feature gets its own branch. This keeps your work isolated from everyone else's.
Naming: feature/login, feature/user-profile, feature/payment-api
The complete workflow:
# 1. Create feature branch from develop
git checkout develop
git pull origin develop
git checkout -b feature/user-profile
# 2. Work on your feature (multiple commits are fine)
git add .
git commit -m "Add user profile page layout"
git commit -m "Add avatar upload functionality"
git commit -m "Add profile edit form"
# 3. Keep your branch updated (optional but recommended)
git checkout develop
git pull origin develop
git checkout feature/user-profile
git merge develop # Resolve conflicts if any
# 4. When feature is complete, merge back to develop
git checkout develop
git merge --no-ff feature/user-profile
git push origin develop
# 5. Clean up
git branch -d feature/user-profileWhy
--no-ff? The--no-ff(no fast-forward) flag creates a merge commit even when Git could do a fast-forward. This preserves the history that a feature branch existed, groups all related commits, and makes it easy to revert an entire feature with a singlegit revert.
With fast-forward (default):
* commit 3 (feature work)
* commit 2 (feature work)
* commit 1 (develop)With --no-ff:
* Merge feature/user-profile into develop
|\
| * commit 3 (feature work)
| * commit 2 (feature work)
|/
* commit 1 (develop)The second version clearly shows where the feature started and ended. Much easier to understand!
Release Branches — Preparing for Launch
When develop has all the features planned for the next version, you create a release branch. This is the "freeze" period — no new features, only bug fixes and version bumps.
Naming: release/1.0, release/2.1
The complete workflow:
# 1. Create release branch from develop
git checkout develop
git checkout -b release/1.0
# 2. Bump version number
echo "1.0.0" > VERSION
git commit -am "Bump version to 1.0.0"
# 3. Fix any last-minute bugs (no new features!)
git commit -m "Fix typo in login page"
git commit -m "Fix date formatting bug"
# 4. When ready, merge into main AND tag it
git checkout main
git merge --no-ff release/1.0
git tag -a v1.0 -m "Release version 1.0"
git push origin main --tags
# 5. Also merge back into develop (to include bug fixes)
git checkout develop
git merge --no-ff release/1.0
git push origin develop
# 6. Clean up
git branch -d release/1.0Why merge into both main and develop? The bug fixes you made during the release freeze need to be in develop too. Otherwise, those bugs would reappear in the next release.
Hotfix Branches — Emergency Production Fixes
Production is broken. Users are affected. You need a fix now — but develop has half-finished features that aren't ready to ship.
Hotfix branches let you patch production without touching develop.
Naming: hotfix/1.0.1, hotfix/fix-payment-crash
The complete workflow:
# 1. Create hotfix branch from main (production)
git checkout main
git checkout -b hotfix/1.0.1
# 2. Fix the critical bug
git commit -m "Fix payment processing crash on empty cart"
# 3. Bump patch version
echo "1.0.1" > VERSION
git commit -am "Bump version to 1.0.1"
# 4. Merge into main and tag
git checkout main
git merge --no-ff hotfix/1.0.1
git tag -a v1.0.1 -m "Hotfix: payment crash"
git push origin main --tags
# 5. Merge into develop (so the fix is in future releases too)
git checkout develop
git merge --no-ff hotfix/1.0.1
git push origin develop
# 6. Clean up
git branch -d hotfix/1.0.1Important exception: If a
release/*branch currently exists, merge the hotfix into the release branch instead ofdevelop. The fix will reachdevelopwhen the release is finished.
The Full Picture
Here's how all the branch types work together in a typical release cycle:
Practice: A Real-World Scenario
Let's walk through a realistic sprint to see Gitflow in action.
Week 1: Feature Development
# Alice works on user authentication
git checkout -b feature/auth develop
# ... writes code ...
# Bob works on the dashboard
git checkout -b feature/dashboard develop
# ... writes code ...
# Both merge when done
git checkout develop && git merge --no-ff feature/auth
git checkout develop && git merge --no-ff feature/dashboardWeek 2: Release Preparation
# Team decides develop is ready for v1.0
git checkout -b release/1.0 develop
# QA finds a bug — fix it on the release branch
git commit -m "Fix validation edge case"
# Meanwhile, Charlie starts a new feature for v1.1
git checkout -b feature/search develop
# This doesn't affect the release!Week 2 (Thursday): Emergency Hotfix
# Production payment system crashes!
git checkout -b hotfix/0.9.1 main
git commit -m "Fix null pointer in payment handler"
# Merge hotfix to main immediately
git checkout main && git merge --no-ff hotfix/0.9.1
git tag -a v0.9.1
# Merge into release branch (not develop, since release exists)
git checkout release/1.0 && git merge --no-ff hotfix/0.9.1Week 3: Ship It
# Release 1.0 is ready
git checkout main && git merge --no-ff release/1.0
git tag -a v1.0
# Sync back to develop
git checkout develop && git merge --no-ff release/1.0When Should You Use Gitflow?
Gitflow is not always the right choice. The original author himself added a note in 2020 saying simpler models like GitHub Flow may be better for many teams.
Use Gitflow When
✅ Your software has explicit version numbers (v1.0, v2.0)
✅ You need to support multiple versions in production
✅ You're building desktop apps, libraries, CLIs, or APIs
✅ You have a formal release process with QA stages
✅ Your team has 5+ developers working on parallel features
Use Simpler Models (GitHub Flow / Trunk-Based) When
✅ You deploy continuously (multiple times per day)
✅ You have a single production version (web apps, SaaS)
✅ Your team is small (1–4 developers)
✅ You have strong automated testing and CI/CD
✅ You want minimal branching overhead
Quick Comparison
| Aspect | Gitflow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| Branches | 5 types | 2 (main + feature) | 1 (main + short-lived) |
| Complexity | High | Low | Low |
| Release cycle | Planned releases | Continuous | Continuous |
| Best for | Versioned software | Web apps, SaaS | High-velocity teams |
| Learning curve | Steep | Gentle | Moderate |
Common Mistakes (and How to Avoid Them)
1. Committing Directly to main or develop
# ❌ Bad
git checkout main
git commit -m "quick fix"
# ✅ Good — always use a branch
git checkout -b hotfix/quick-fix main
git commit -m "Fix the issue"
git checkout main && git merge --no-ff hotfix/quick-fix2. Forgetting --no-ff on Merges
# ❌ Bad — loses branch history
git merge feature/login
# ✅ Good — preserves branch history
git merge --no-ff feature/loginPro tip: Configure Git to always use no-ff for merges:
git config --global merge.ff false3. Adding New Features on Release Branches
Release branches are for bug fixes only. If you discover a feature is needed, add it to develop for the next release.
4. Forgetting to Merge Back
Release and hotfix branches must be merged into both main and develop. Forgetting the develop merge means bug fixes are lost in future releases.
5. Long-Lived Feature Branches
Feature branches that live for weeks become merge nightmares. Keep them short:
- Merge
developinto your feature branch regularly - Break large features into smaller, mergeable pieces
- Aim for branches that live 3–5 days, not 3–5 weeks
Setting Up Gitflow
Option 1: Manual (Recommended for Learning)
Just follow the branching conventions manually. This is what we've been doing throughout this guide. It helps you understand exactly what's happening.
Option 2: git-flow Extension
The git-flow CLI tool automates the branching commands:
# Install
brew install git-flow-avh # macOS
apt-get install git-flow # Ubuntu/Debian
# Initialize in your repo
git flow init
# Accept defaults: main for production, develop for development
# Feature workflow
git flow feature start user-profile
# ... work ...
git flow feature finish user-profile
# Release workflow
git flow release start 1.0
# ... bug fixes ...
git flow release finish 1.0
# Hotfix workflow
git flow hotfix start 1.0.1
# ... fix ...
git flow hotfix finish 1.0.1The extension handles the branching, merging, tagging, and cleanup automatically. But I recommend doing it manually first until you're comfortable with the flow.
Gitflow Cheat Sheet
Feature
git checkout -b feature/X develop # Start
# ... work ...
git checkout develop # Finish
git merge --no-ff feature/X
git branch -d feature/XRelease
git checkout -b release/X.Y develop # Start
# ... bug fixes only ...
git checkout main # Finish
git merge --no-ff release/X.Y
git tag -a vX.Y
git checkout develop
git merge --no-ff release/X.Y
git branch -d release/X.YHotfix
git checkout -b hotfix/X.Y.Z main # Start
# ... fix ...
git checkout main # Finish
git merge --no-ff hotfix/X.Y.Z
git tag -a vX.Y.Z
git checkout develop
git merge --no-ff hotfix/X.Y.Z
git branch -d hotfix/X.Y.ZKey Takeaways
- Gitflow separates concerns — production code, development, features, releases, and hotfixes each have their own lane
mainis sacred — it always reflects production. Never commit directly to itdevelopis the hub — all features flow through here before reaching production--no-ffis essential — it preserves branch history and makes reverts easy- Merge back is mandatory — release and hotfix branches merge into both
mainanddevelop - Know when NOT to use it — for continuous delivery web apps, simpler models like GitHub Flow are often better
Understanding Gitflow — even if your team uses a different model — gives you a solid mental framework for how code flows from development to production. It teaches you to think about isolation, integration, and release management — skills that transfer to any branching strategy.
Further Reading
- A Successful Git Branching Model — Vincent Driessen's original article
- GitHub Flow — A simpler alternative for continuous delivery
- Trunk-Based Development — The model preferred by high-velocity teams
- Atlassian Gitflow Workflow — Another great explanation with visuals
📬 Subscribe to Newsletter
Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.
We respect your privacy. Unsubscribe at any time.
💬 Comments
Sign in to leave a comment
We'll never post without your permission.