Skip to content

Git Branching Strategies: Git Flow vs. Trunk-Based Development

The branching model a team picks shapes its release cadence for years — compare both properly before committing to one.

GitGitHubGitLab
2 min readBeginner
Lab time: 15-25 min
RR

Ranjith R

Linux SysAdmin & Cloud Engineer

Published July 1, 2026Updated July 6, 2026
Share
Section 01

Prerequisites

  • Git installed locally
  • Basic git add/commit/push/pull experience
Section 02

What You'll Learn

  • Describe Git Flow's branch roles: main, develop, feature/*, release/*, hotfix/*
  • Describe trunk-based development: short-lived branches merged to main behind feature flags
  • Choose the right model based on release cadence, team size, and CI/CD maturity
  • Perform an interactive rebase to clean up a feature branch before merging
Section 03

Theory

A branching strategy is really a policy for two things: how long a branch lives before merging, and what state main is allowed to be in at any moment. The two dominant models answer both questions almost oppositely.

Git Flow

Git Flow defines fixed branch roles: main always reflects production, develop is the integration branch for the next release, feature/* branches off develop and merges back into it, release/* branches off develop when preparing a release (only bugfixes land here), and hotfix/* branches directly off main for urgent production fixes, merging into both main and develop. It suits products with a scheduled, versioned release cadence.

Trunk-based development

Trunk-based development keeps a single long-lived branch (main/trunk), with feature branches living hours to a couple of days at most before merging back, gated by CI. Work that isn't ready to ship is hidden behind a feature flag rather than kept on a long-lived branch — decoupling "merged" from "released". It suits teams with strong CI/CD and frequent (daily or faster) deployment.

Section 04

Architecture

Git Flow: feature branches integrate via develop, release branches stabilize, hotfixes patch main directly and flow back down

Section 05

Hands-On Lab

Clean up a messy feature branch with interactive rebase before opening a PR:

git checkout feature/login-form
git log --oneline main..HEAD        # see exactly which commits are yours

git rebase -i main
# In the editor, mark commits: pick the first, 'squash' (or 's') the
# "fix typo" / "wip" commits into it, reword the final message

# If main has moved on since you branched, rebase onto the latest first:
git fetch origin
git rebase origin/main

# Force-push required after any rebase (history was rewritten) —
# --force-with-lease refuses if someone else pushed to the branch meanwhile
git push --force-with-lease origin feature/login-form
Section 07

Best Practices

Match the model to your deployment pipeline, not the other way around

If you deploy on every merge to main, Git Flow's develop/release branches add process without benefit — trunk-based is the natural fit. If you ship versioned releases on a schedule, Git Flow's structure genuinely earns its complexity.

Use --force-with-lease, never --force

--force-with-leaserefuses to overwrite a branch if someone else pushed to it since you last fetched, preventing you from silently discarding a teammate's work.
Section 08

Common Mistakes

Long-lived feature branches under a trunk-based label

Calling a branch 'trunk-based' while letting it live for three weeks defeats the entire point — the value comes from small, frequent merges that keep integration conflicts small and CI feedback fast.

Rebasing a shared branch other people have already pulled

Rebasing rewrites commit hashes — anyone who already pulled the old version now has a diverged, conflicting history. Only rebase branches that are exclusively yours (or where the whole team has explicitly agreed to the rewrite).
Section 09

Troubleshooting

Rebase stops with a conflict: git pauses and shows which files conflict — edit them to resolve, then git add the resolved files and git rebase --continue. If it gets too tangled, git rebase --abort returns you to the exact state before the rebase started, with nothing lost.

Force-pushed and now the branch looks broken: git reflog records every position HEAD has been at locally, including before a bad rebase or reset — find the commit hash from before the mistake and git reset --hard <that-sha>to recover it, as long as it hasn't been garbage-collected (reflog entries expire, default 90 days).

Section 11

Cheat Sheet

Branching & Merging

git checkout -b feature/xCreate and switch to a new branch
git switch -c feature/xModern equivalent of checkout -b
git merge feature/xMerge a branch into the current branch
git branch -d feature/xDelete a branch that's been merged
git branch -D feature/xForce-delete a branch, merged or not
git branch -vvList branches with their upstream tracking status
Get the full cheat sheet (with PDF download)
FAQ

Frequently Asked Questions

Yes — the most common path is dropping the develop branch first (merge feature branches straight to main behind flags) while keeping release branches for a transition period, then removing release branches once deployment frequency and CI confidence are high enough to release directly from main.
Section 12

Summary

Git Flow's fixed branch roles suit scheduled, versioned releases; trunk-based development's short-lived branches and feature flags suit frequent, CI-driven deployment. Pick based on your actual release cadence and CI/CD maturity — and remember interactive rebase and reflog are the tools that make cleaning up (and recovering from) either model safe.