Git Branching Strategies (GitFlow/Trunk-based) Cheat Sheet
Comparing common Git branching models — GitFlow, trunk-based development, and GitHub Flow — and when to use each.
GitFlow Branch Types
The canonical branch roles defined by the GitFlow model.
- main/master- Always reflects production-ready code; tagged with release versions
- develop- Integration branch where completed features accumulate before a release
- feature/*- Branched from develop, merged back into develop when complete
- release/*- Branched from develop to stabilize a release, merged into main and develop
- hotfix/*- Branched from main to patch production urgently, merged into main and develop
GitFlow Commands
Typical git-flow CLI workflow (via git-flow extension) for a feature.
git flow feature start checkout-redesign# ... work, commit ...git flow feature finish checkout-redesign# merges into develop and deletes the feature branchgit flow release start 1.4.0git flow release finish 1.4.0# merges into main + develop, tags v1.4.0
Trunk-Based Development Workflow
Short-lived branches merged directly into main, gated by feature flags.
git checkout maingit pullgit checkout -b add-search-endpoint# small change, ideally < 1 day of workgit push -u origin add-search-endpoint# open PR, get review, merge same daygit checkout main && git pull && git branch -d add-search-endpoint
When to Use Each Model
Trade-offs between the major branching strategies.
- GitFlow- Good fit for scheduled/versioned releases (e.g. shipped software, mobile apps with app-store review)
- Trunk-based development- Favors continuous delivery; requires strong CI, feature flags, and small PRs
- GitHub Flow- Simplified model: main is always deployable, feature branches merge via PR directly to main
- Release branches- Even trunk-based teams often cut a release branch at deploy time for hotfix isolation
Feature Flags for Trunk-Based Development
Ship incomplete work to main safely by gating it behind a runtime flag instead of a long-lived branch.
// Merge to main behind a flag, deploy dark, then flip onif (featureFlags.isEnabled('new-checkout-flow', { userId })) { return renderNewCheckout();}return renderLegacyCheckout();// Rollout stages typically live in the flag service, not in code:// 1. off for everyone (merged but dormant)// 2. on for internal/QA cohort// 3. percentage rollout (1% -> 10% -> 50% -> 100%)// 4. flag deleted once code path is the only path
Cutting & Hardening a Release Branch
Even trunk-based teams isolate a release for stabilization and hotfixes without freezing main.
git checkout main && git pullgit checkout -b release/2026.07git push -u origin release/2026.07# Cherry-pick a fix that landed on main after the cutgit checkout release/2026.07git cherry-pick <fix-sha>git push# Tag the release once QA signs offgit tag -a v2026.07.0 -m "Release 2026.07.0"git push origin v2026.07.0# Backport the same cherry-pick to main if it wasn't already theregit checkout main && git cherry-pick <fix-sha>
Keeping a Long-Lived Branch in Sync (GitFlow develop)
Reduce merge-hell on develop by rebasing feature branches frequently instead of merging main into them ad hoc.
# On a feature branch off develop, stay current dailygit fetch origingit rebase origin/develop# When finishing a release in GitFlow, merge --no-ff to keep# a visible release boundary in historygit checkout maingit merge --no-ff release/1.4.0 -m "Release 1.4.0"git checkout developgit merge --no-ff release/1.4.0git branch -d release/1.4.0
Branch Protection & CI Gating
Rules that make a branching strategy actually hold up in a team, not just on paper.
- Required status checks- main/trunk should block merges until CI (tests, lint, type-check) passes; without this, trunk-based development degrades into 'push and pray'
- Required reviews- At least one approval before merge; pair with small PRs so review latency doesn't push developers back toward long-lived branches
- Linear history requirement- GitHub/GitLab setting that forces rebase or squash merges, keeping trunk history readable when many short branches merge per day
- Merge queue- Serializes merges and re-runs CI against the post-merge state, catching integration failures that pass in isolation but fail combined
- Stale branch cleanup- Automated deletion of merged branches; trunk-based teams accumulate hundreds of short branches per week without it
Branching at Scale: Monorepo Considerations
How branching strategy choice interacts with repo topology.
- Monorepo + trunk-based- Common pairing: single main branch, path-based CI triggers, feature flags per team to avoid cross-team blocking
- Polyrepo + GitFlow- Coordinating a release/* branch across many repos is expensive; version pinning or a release-train process usually replaces per-repo GitFlow
- Release trains- Fixed-cadence cutoffs (e.g. every 2 weeks) where whatever is on trunk ships, avoiding the need to track feature-complete release branches
- Stacked PRs- Chain of small dependent branches (each based on the previous) used to keep trunk-based PRs small while still landing large features incrementally
If your team deploys multiple times a day, GitFlow's long-lived develop/release branches usually add overhead without benefit — trunk-based development with feature flags and small, frequent PRs scales better for continuous delivery.