Git Flow Branching Strategy for Small Teams: A Guide

A practical Git Flow branching strategy for small teams: feature, release and hotfix branches, pull request rules, and when to use trunk-based instead.

A Git Flow branching strategy gives every developer on a team the same rules for where work starts, where it is reviewed and how it reaches production. As a team lead, the most common source of broken deployments I see is not bad code. It is unclear branching: someone deploys develop by accident, a hotfix is made on production and lost in the next release, or two features that were never tested together go live on the same day.

In this guide I will explain how my team uses Git Flow day to day: the branches and what each one is for, the exact commands for features, releases and hotfixes, the pull request rules that make it work, the mistakes we made along the way, and when a simpler model like GitHub Flow or trunk-based development is the better choice.

Prerequisites

  • Git 2.40 or newer installed locally
  • A shared remote on GitHub, GitLab or Bitbucket with branch protection available
  • A CI pipeline that runs tests on pull requests
  • At least one staging environment that deploys from develop or release branches

The Git Flow branches explained

Git Flow uses two long-lived branches and three kinds of short-lived ones. The long-lived branches never get deleted; the short-lived ones are merged and removed.

  • main always matches production. Every commit on main is a release and gets a version tag.
  • develop is the integration branch. It holds finished features waiting for the next release, and it deploys to staging.
  • feature/* branches come from develop and go back into develop through a pull request.
  • release/* branches come from develop when you freeze a version. Only bug fixes, version bumps and changelog updates go here.
  • hotfix/* branches come from main to fix production urgently, and are merged into both main and develop.

Branch naming conventions

Consistent names let CI apply rules automatically and let anyone understand a branch at a glance. We include the ticket number so the branch, pull request and issue tracker link together.

feature/CRM-142-lead-scoring
feature/CRM-157-export-csv
release/2.4.0
hotfix/2.3.1-payment-rounding

Daily feature work

Always branch from an up-to-date develop. Keep feature branches short; anything open longer than a few days should be split or hidden behind a feature flag, because long branches are where painful merge conflicts come from.

git switch develop
git pull --ff-only
git switch -c feature/CRM-142-lead-scoring

# ...work in small commits...
git add -p
git commit -m "feat(leads): add scoring based on activity"

# stay current with develop while you work
git fetch origin
git rebase origin/develop

git push -u origin feature/CRM-142-lead-scoring
# open a pull request into develop

Rebasing your own feature branch onto develop keeps history linear and surfaces conflicts early. Only rebase branches nobody else has pulled; once a branch is shared, merge develop into it instead. After a rebase, push with --force-with-lease, which refuses to overwrite commits you have not seen.

Creating a release branch

When develop has everything planned for a version, cut a release branch. From that moment develop is open for the next version's features, while QA tests the release on staging. Fixes found during testing are committed to the release branch.

git switch develop && git pull --ff-only
git switch -c release/2.4.0
# bump version in package.json / composer.json, update CHANGELOG.md
git commit -am "chore(release): 2.4.0"
git push -u origin release/2.4.0

# when QA signs off: merge into main via PR, then tag
git switch main && git pull --ff-only
git merge --no-ff release/2.4.0
git tag -a v2.4.0 -m "Release 2.4.0"
git push origin main --tags

# bring release fixes back into develop
git switch develop && git pull --ff-only
git merge --no-ff release/2.4.0
git push origin develop
git branch -d release/2.4.0 && git push origin --delete release/2.4.0

Hotfix branch workflow

A hotfix branches from main, because main is what is running in production and develop may contain unreleased work. It is merged into both main and develop, so the fix is not lost in the next release. Forgetting the second merge is the single most common Git Flow mistake I have seen, and it means the bug quietly comes back.

git switch main && git pull --ff-only
git switch -c hotfix/2.3.1-payment-rounding
# fix, test, bump patch version
git commit -am "fix(payments): round totals to integer cents"
git push -u origin hotfix/2.3.1-payment-rounding

# PR into main, then tag
git switch main && git pull --ff-only
git tag -a v2.3.1 -m "Hotfix 2.3.1"
git push origin --tags

# merge main back into develop (or into the open release branch)
git switch develop && git pull --ff-only
git merge --no-ff main
git push origin develop

If a release branch is open when the hotfix lands, merge the hotfix into the release branch instead of develop. It will reach develop when the release finishes.

Commit messages and versioning

We use Conventional Commits (feat, fix, chore, refactor, docs) and Semantic Versioning. A fix bumps the patch number, a feature bumps the minor number and a breaking change bumps the major number. The prefixes make changelogs easy to generate and make it obvious during review what kind of change you are looking at.

Team rules that matter more than the branching model

Git Flow only describes where code lives. These habits are what actually keep main deployable.

  • Small pull requests: aim for under 400 changed lines so reviewers read them properly.
  • At least one approving review before merge, and the author never merges their own unreviewed work.
  • CI must pass before merge: tests, linting and a build.
  • Protect main and develop so nobody can push directly or force-push.
  • Descriptive commit messages and a pull request template with what changed, why and how to test it.
  • Delete branches after merge so the branch list reflects work in progress.

Setting up branch protection

On GitHub, add a ruleset for main and develop: require a pull request, require at least one approval, require status checks to pass, block force pushes and block deletion. GitLab calls these protected branches and Bitbucket calls them branch restrictions, but the options are the same. This turns the team rules from a wiki page into something the tooling enforces.

Git Flow vs GitHub Flow vs trunk-based development

Git Flow is not always the right answer. It fits teams that ship versioned releases on a schedule, support mobile apps that go through store review, or need a staging sign-off before each release.

GitHub Flow is simpler: one main branch and short feature branches deployed straight to production after review. It suits web apps with good automated tests that deploy many times a day.

Trunk-based development goes further, with everyone merging small changes into main at least daily and unfinished work hidden behind feature flags. It produces the fastest feedback, but it needs strong CI and discipline. Many teams, including mine on some projects, start with Git Flow and move towards GitHub Flow as test coverage and confidence grow.

Common Git Flow mistakes and fixes

  • Hotfix lost in the next release: the hotfix was merged into main but not develop. Make merging back part of the hotfix checklist.
  • Features merged directly into a release branch: only fixes belong there. New work waits for the next release.
  • Long-lived feature branches with huge conflicts: rebase onto develop daily and split large features.
  • Accidentally committed to develop locally: create a branch from your current position with git switch -c, then reset develop to origin/develop.
  • Force-pushing over a teammate's work: use --force-with-lease and never rewrite shared branches.

Conclusion

A Git Flow branching strategy gives a small team predictable answers to everyday questions: where does this change go, what is in production, and how do we fix it fast. Keep main equal to production, integrate on develop, freeze versions on release branches and always merge hotfixes back. Pair that with small reviewed pull requests, protected branches and CI, and broken deployments become rare. When your tests and deployment pipeline mature, revisit the model; simpler flows may serve you better.

Frequently asked questions

What is the difference between Git Flow and GitHub Flow?

Git Flow uses main and develop plus feature, release and hotfix branches, which suits scheduled versioned releases. GitHub Flow uses only main and short feature branches that deploy right after review, which suits continuous delivery.

Is Git Flow still relevant in 2026?

Yes, for teams that ship versioned releases, mobile apps or products that need a staging sign-off. Teams that deploy many times a day with strong automated tests usually prefer GitHub Flow or trunk-based development.

Should a hotfix be merged into develop or main?

Both. Create the hotfix from main, merge it into main and tag a release, then merge it into develop, or into the open release branch, so the fix is not lost.

Should I rebase or merge feature branches?

Rebase your own feature branch onto develop to keep it current and its history clean. Use merge commits when integrating into develop and main, and never rebase a branch others are working on.