Handling hotfixes in GitFlow Jump to heading
In GitFlow, develop carries unreleased work, so a production bug cannot be fixed there and shipped β the fix would drag every unreleased feature along with it. The modelβs answer is a hotfix branch: cut from main, which holds exactly what is in production, fixed, released as a patch version, and then merged back so the fix also reaches the next release. The procedure is simple; the failure modes are in the details. A hotfix merged into main but not develop reappears in the next release. A hotfix merged into develop while a release branch is open skips that release. A hotfix that diverges from develop produces conflicts at merge-back that someone resolves badly under pressure. This page runs the procedure with those pitfalls covered, within GitFlow vs GitHub Flow comparison.
When to use this approach Jump to heading
- Your team uses GitFlow, as set up in setting up GitFlow branches and protections.
- A bug in production needs fixing before the next scheduled release.
- Past hotfixes have gone missing from later releases, or caused painful merges into
develop. - For GitHub Flow or trunk-based models, a hotfix is simply a fast pull request to
main; this page does not apply.
Step 1 β Branch from the production tag Jump to heading
Cut the hotfix branch from main β or, more precisely, from the tag of the version in production. Name it after the patch version it will produce.
git fetch origin --tags
git describe --tags --abbrev=0 origin/main # v2.4.0 β what production runs
git switch -c hotfix/2.4.1 v2.4.0
git push -u origin hotfix/2.4.1 Step 2 β Keep the fix minimal and test it like a release Jump to heading
A hotfix ships to production quickly with less review time than a normal release, so it should contain the smallest change that fixes the bug, plus a regression test. Run the same release pipeline the hotfix will go through β the full suite and staging deploy β on the hotfix branch.
# The fix and its test, nothing else
git add src/billing/totals.py tests/billing/test_credit_notes.py
git commit -m "fix(billing): stop double discount on credit notes (PAY-907)"
git push
gh pr create --base main --title "Hotfix 2.4.1: double discount on credit notes" --fill Resist the temptation to include βwhile we are hereβ fixes. Every extra line is untested in the context of an urgent release.
Step 3 β Merge into main and tag the patch release Jump to heading
Merge the hotfix into main through its pull request, with a merge commit so the hotfix is identifiable in history, then tag the result.
gh pr merge --merge --delete-branch=false
git fetch origin
git tag -s v2.4.1 -m "Release 2.4.1 (hotfix PAY-907)" origin/main
git push origin v2.4.1 Keep the hotfix branch until the merge-back is done; it is the clean source for merging the fix forward.
Step 4 β Merge back to develop, or to the open release Jump to heading
Where the fix goes next depends on whether a release branch is currently open.
# No open release: merge the hotfix into develop
git switch develop && git pull --ff-only
git merge --no-ff hotfix/2.4.1 -m "Merge hotfix/2.4.1 into develop"
git push origin develop
# Open release: merge into the release branch instead
git switch release/2.5 && git merge --no-ff hotfix/2.4.1 && git push If the merge into develop conflicts β because develop has changed the same code β resolve it with care: developβs version is the future, and the fixβs intent must be reapplied to it, as described in resolving cherry-pick conflicts on older branches in reverse.
Step 5 β Verify the fix is everywhere, then delete the branch Jump to heading
Confirm the fix is on main and on the branch that will become the next release, then delete the hotfix branch.
fix=$(git log --format=%H -1 --grep='PAY-907' origin/hotfix/2.4.1)
for b in origin/main origin/develop origin/release/2.5; do
git merge-base --is-ancestor "$fix" "$b" 2>/dev/null && echo "β $b" || echo "β $b missing the fix"
done
git push origin --delete hotfix/2.4.1 β οΈ SAFETY WARNING: Deleting the hotfix branch before the merge-back means the fix exists only on
main, and the next release fromdevelopwill regress. Keep the branch until the verification above shows the fix on every branch that needs it. If deleted too early, the fix is still reachable onmain; mergemainintodevelop, or cherry-pick the fix commit with-x.
Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Can we cherry-pick instead of merging the hotfix back? Jump to heading
Yes, and some teams prefer it for a tidier develop. Use cherry-pick -x so the link is recorded. Merging is the GitFlow default because it records the relationship in the graph and avoids duplicate commits.
What if two hotfixes are needed at once? Jump to heading
Run them sequentially if possible: finish and tag 2.4.1, then cut 2.4.2 from it. Two parallel hotfix branches from the same tag both need merging into main and conflict with each otherβs version bumps.
Should the CI check for missing merge-backs? Jump to heading
Yes. A scheduled git cherry comparison between main and develop, as in the GitFlow setup guide, catches forgotten merge-backs within a day.
Related Jump to heading
- GitFlow vs GitHub Flow Comparison β the parent topic.
- Hotfix Branches That Do Not Drift from Main β the same concern in other models.
- Cherry-Picking Hotfixes Across Release Branches β when several versions need the fix.
- Tagging Pre-Releases and Release Candidates β version tags around hotfixes.