Git branching strategies: Gitflow, trunk-based and GitHub Flow
On this page
A branching strategy decides which branches live for a long time, where new work starts, and how code reaches production. The right one depends on how often you deploy, how big the team is, and whether you support more than one version.
Start simple. GitHub Flow or a light trunk-based approach works for most teams. Add the extra branches of Gitflow or release branching only when you hit a problem the simpler approach cannot solve.
Strategies at a glance
| Strategy | Long-lived branches | Best for |
|---|---|---|
| Gitflow | main, develop, plus release and hotfix branches | Scheduled releases, teams that need a stable production branch at all times |
| Trunk-based development | main only, with short-lived feature branches | Continuous deployment, teams with strong automated tests |
| GitHub Flow | main only | Small teams shipping often |
| GitLab Flow | Environment branches such as staging and production | Teams that deploy through set environments |
| Release branching | One branch per release | Projects that maintain several versions at once |
Choosing a strategy
Work through these questions:
- How often do you deploy? Daily or several times a day suits trunk-based development or GitHub Flow. Weekly or monthly releases suit Gitflow or release branching.
- How large is the team? Large teams with many parallel workstreams get more from the structure of Gitflow. Small teams move faster with a simpler approach.
- Do you need emergency fixes in production? Then you need a clear path for hotfixes. Gitflow defines one.
- How good is your CI pipeline? Trunk-based development needs fast, thorough automated tests. Without them, merging to
mainall day is risky. - Do you support several production versions? Release branching is built for that.
Gitflow
Gitflow keeps new development apart from released work. It has two long-lived branches:
main(sometimesmaster) holds only production-ready code. Every commit tomainis a release.developis the integration branch. Finished features land here before they are released.
Three kinds of short-lived branch are created as needed and deleted once merged:
- Feature branches start from
develop, hold one feature or change, and merge back intodevelop. - Release branches start from
developwhen a release is being prepared. Bug fixes and version bumps happen here. They merge into bothmainanddevelop. - Hotfix branches start from
mainwhen production needs an urgent fix. They merge into bothmainanddevelop, so the next release keeps the fix.
How a feature moves through Gitflow
- A developer creates a feature branch from
develop. - The work happens on the feature branch: commits, reviews and changes.
- The feature branch merges into
developand is deleted. - When the team is ready to release, someone cuts a release branch from
develop. - Last-minute fixes and the version number change go onto the release branch.
- The release branch merges into
main, which triggers a deploy, and intodevelop, so the fixes are not lost. Then it is deleted.
In Git commands, the feature and release steps look like this:
git switch develop
git switch -c feature/order-export
# ... commit work, open a pull request into develop, merge it
git switch develop
git switch -c release/1.4.0
# ... bump the version, fix release bugs
git switch main
git merge --no-ff release/1.4.0
git tag v1.4.0
git switch develop
git merge --no-ff release/1.4.0
git branch -d release/1.4.0What Gitflow gives you and what it costs
main is always deployable, and development, stabilisation and production each have their own branch. That makes it less likely an unfinished feature ships by accident.
The cost is complexity. The model only works if everyone follows it. Forgetting to merge a hotfix into develop, or cutting a release branch at the wrong time, causes bugs that are hard to trace. Feedback is also slower. Code has to pass through develop and a release branch before it reaches production, which is too slow for teams that deploy often.
Trunk-based development
In trunk-based development, everyone integrates into one long-lived branch, the trunk, usually main. Changes land often, sometimes several times a day, and the trunk stays deployable.
Small teams may commit straight to the trunk. Larger teams usually open very short-lived feature branches that last hours, not days, and merge them through a pull request. No branch lives long enough to drift far from the trunk, so merge conflicts stay small. When one change depends on another that is still in review, stacked pull requests keep each branch small.
Feature flags
Code lands in the trunk before the feature is finished, so you need feature flags (also called feature toggles). A flag lets you deploy code without turning it on for users. When the feature is ready, you switch the flag on. If something goes wrong, you switch it off, with no revert or rollback.
Flags have their own cost. You have to create them, maintain them, and remove them after the feature ships.
Continuous integration
Trunk-based development needs a mature continuous integration (CI) pipeline. Every commit to the trunk must trigger an automated build and test run. If the tests fail, the trunk is broken for the whole team, so the team has to fix it straight away and keep it green.
Pair programming also fits this approach. Two developers working on the same code share context as they go, which means fewer surprises when the change is integrated.
What trunk-based development gives you and what it costs
There is one branch to think about, with no long-lived feature branches to rebase and no release branches to manage. Code reaches production quickly, so bugs show up early, when they are cheap to fix. Branches last hours, so each pull request is small and quick to review.
It is harder to plan a release around a date with a fixed set of features. A model with release branches handles that better.
Trunk-based development is the usual choice for teams that deploy continuously and trust their automated tests. It also copes with growth better than long-lived feature branches, which get more painful as the number of parallel branches rises.
GitHub Flow
GitHub Flow is the simplest option. There is one long-lived branch, main. You create a feature branch, work on it, open a pull request for review, and merge it into main. You deploy from main whenever you are ready. Creating pull requests covers how to present a branch for review.
It suits small teams that ship often. In a large team with many features in progress, merge conflicts become more common, and without a stabilisation branch a coordinated release is hard.
GitLab Flow
GitLab Flow replaces Gitflow's release branches with environment branches such as staging and production. Code moves from main to staging for testing, then to production for release. It is easier to follow than Gitflow, but the history gets messy if nobody keeps the environment branches tidy.
Release branching
Release branching keeps one branch for each release. Fixes are applied to every release branch that is still supported. Use it when you maintain several versions in production at once, such as a library or an installed product.