Git Flow
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.
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 |