Git Flow Guide — Feature & Hotfix Branches
What this covers
The Gitflow branching model — two long-lived branches (main, develop) plus three kinds of short-lived support branches (feature/*, release/*, hotfix/*) — and the commands to drive each one. It also covers, honestly, when Gitflow is the wrong choice. For everyday git commands see Git
; for running branches in parallel working directories see Git Worktrees
.
When to use it
- Deciding where a change should branch from and merge back to under Gitflow
- Starting a feature, cutting a release, or shipping an emergency hotfix
- Setting up a release-oriented branching model for a versioned product
- Deciding whether Gitflow or a simpler model (trunk-based, GitHub Flow) fits the team
The branches
main— production. Every commit is a released, tagged version.develop— integration branch. Completed features land here first.feature/*— one per unit of work; branches from and merges back todevelop.release/*— branches fromdevelopto stabilise a version; merges to bothmainanddevelop.hotfix/*— branches frommainto fix production; merges to bothmainanddevelop.
You can drive Gitflow with plain git (shown below, works anywhere) or with the
git flow command-line extension, which wraps the same steps.
When NOT to use Gitflow
Worth being honest — Gitflow is often the wrong default for modern web work.
- Continuously-deployed web apps / SaaS: prefer trunk-based development or GitHub Flow — short-lived branches off
main, merge and deploy. Gitflow’s long-liveddevelopand release branches add ceremony a deploy-on-merge pipeline doesn’t need. - Small teams / solo projects: the overhead rarely pays for itself.
- If you deploy
mainmany times a day, thedevelop/releasesplit works against you.
Gitflow earns its keep when you ship discrete, versioned releases (installed software, mobile apps, libraries, on-prem products) and may need to support more than one version at a time.
Setup
# Plain git: create the develop branch off main once
git switch main
git switch -c develop
git push -u origin develop
# Or with the git flow extension (interactive; accept defaults)
git flow init
Feature branches
Branch from develop, merge back to develop. Never branch a feature from main.
# Start
git switch develop
git pull
git switch -c feature/short-description
# ... commit work ...
# Finish — integrate back into develop
git switch develop
git pull
git merge --no-ff feature/short-description
git push
git branch -d feature/short-description
# git flow equivalent
git flow feature start short-description
git flow feature finish short-description
--no-ff keeps the feature’s commits grouped under a merge commit, so the
branch’s shape stays visible in history.
Release branches
Branch from develop when the release scope is frozen. Only stabilisation
commits (version bump, changelog, bug fixes) go here — no new features.
Merge to both main and develop, and tag main.
# Start
git switch develop
git pull
git switch -c release/1.4.0
# ... bump version, update changelog, fix release-blocking bugs ...
# Finish
git switch main
git merge --no-ff release/1.4.0
git tag -a v1.4.0 -m "Release 1.4.0"
git switch develop
git merge --no-ff release/1.4.0 # so fixes made on the release branch return to develop
git push origin main develop --tags
git branch -d release/1.4.0
# git flow equivalent
git flow release start 1.4.0
git flow release finish 1.4.0 # merges to main + develop and tags
Merging the release back into develop is the step people forget — skip it and
the release-branch bug fixes vanish from ongoing development.
Hotfix branches
For an urgent production fix. Branch from main, merge to both main and
develop, and tag main with a new patch version.
# Start from production
git switch main
git pull
git switch -c hotfix/1.4.1
# ... fix the bug ...
# Finish
git switch main
git merge --no-ff hotfix/1.4.1
git tag -a v1.4.1 -m "Hotfix 1.4.1"
git switch develop
git merge --no-ff hotfix/1.4.1
git push origin main develop --tags
git branch -d hotfix/1.4.1
# git flow equivalent
git flow hotfix start 1.4.1
git flow hotfix finish 1.4.1
If a release/* branch is open when you hotfix, merge the hotfix into the
release branch instead of develop (the release branch carries it back to
develop on finish) — otherwise you merge the same fix twice.
Branch reference
| Branch | Branches from | Merges back to | Tagged? |
|---|---|---|---|
feature/* | develop | develop | no |
release/* | develop | main and develop | yes (on main) |
hotfix/* | main | main and develop | yes (on main) |
Safety rules
mainis always releasable. Only mergerelease/*orhotfix/*into it, always tagged.- No features on
release/*orhotfix/*— only stabilisation/fix commits. - Always merge release and hotfix branches back into
develop, or the fixes are lost from ongoing work. - Use
--no-fffor support-branch merges so history shows where each branch integrated. - Tag every
mainmerge with a semantic version — the tag is the released artifact’s identity.
Common mistakes
- A feature branch that lives for weeks. The longer it drifts from
develop, the worse the final merge. Mergedevelopinto it regularly and split the work into smaller features. - New features on the release branch. A release branch is there to stabilise a version; every added feature sends stabilisation back to the start.
- Hotfix straight on
main. Without ahotfix/*branch the fix easily never reachesdevelop, and the bug returns in the next version. - Missing tag. A merge into
mainwithout a tag means nobody can tell which commit is which version. - Fast-forward merges of support branches. Without
--no-ffthe feature’s boundary disappears from history and it cannot be reverted in one commit. - Gitflow “because that is how it is done”. On a website deployed on every merge,
developis just one more branch to keep in sync.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
| Release-branch fix missing from new features | Release branch wasn’t merged back into develop — merge it now |
| Same hotfix applied twice, merge conflict | Hotfix merged to both develop and an open release/* — merge to the release branch only; it carries to develop on finish |
develop far ahead of main, never releasing | Deploy cadence outpaces the model — reconsider trunk-based / GitHub Flow |
git flow command not found | Install the extension (git-flow / git-flow-avh package), or use the plain-git steps above |
Feature accidentally branched from main | Rebase it onto develop: git rebase --onto develop main feature/x |
Frequently asked questions
What is Git Flow?
Git Flow (Gitflow) is a branching model with two permanent branches — main for released versions and develop for integration — and three kinds of temporary branches: feature/*, release/* and hotfix/*. Each kind has a fixed place to branch from and to merge into.
What is the difference between Git Flow and GitHub Flow?
GitHub Flow has a single permanent branch, main: short branch, pull request, merge, deploy. Git Flow adds develop and release branches, which suits products shipped in versions; for a website deployed on every merge it is overhead.
Do I need to install the git flow extension?
No. Commands such as git flow feature start only wrap plain git switch, git merge --no-ff and git tag. The whole model runs on plain git; the extension saves typing and keeps the steps in order.
Where does a hotfix branch from, and where is it merged?
A hotfix branches from main, because it fixes what runs in production. Once fixed, it is merged into main (tagged with the new patch version) and back into develop — or into the open release/* branch, if there is one.
Is Git Flow outdated?
Not outdated, just not a fit everywhere. For continuously deployed websites and SaaS, trunk-based development or GitHub Flow is simpler. For libraries, mobile apps and on-prem software with numbered versions, Git Flow still makes sense.