← Back to blog

2025-09-09 · 11 min read

GitFlow Branching Strategy: The Complete Guide (Commands, Diagram & Best Practices)

A complete guide to the GitFlow branching strategy, main, develop, feature, release, and hotfix branches, the git-flow commands, naming conventions, and when to use it.

#git#git-flow#branching#version-control#devops#github
GitFlow Branching Strategy: The Complete Guide (Commands, Diagram & Best Practices)

GitFlow is a Git branching model that organizes work into long-lived main and develop branches plus short-lived feature, release, and hotfix branches. It gives teams a predictable structure for developing features, cutting releases, and shipping urgent production fixes without stepping on each other. Created by Vincent Driessen in 2010, it's still one of the most widely used branching strategies, and one of the most misunderstood.

This guide covers the full model: what each branch is for, the exact Git commands, the git-flow CLI helper, naming conventions, how it compares to GitHub Flow and trunk-based development, and, importantly, when not to use it.

What is GitFlow?

GitFlow is a branching strategy, not a tool. It defines a set of rules for how branches are created, named, and merged so that development, release preparation, and production maintenance each have a dedicated place to live.

The problem it solves: on a single shared branch, half-finished features, release stabilization, and emergency hotfixes all collide. GitFlow separates those concerns:

  • Day-to-day development happens off develop.
  • Releases are stabilized in isolation before going to production.
  • Production emergencies are fixed against the live code without pulling in unreleased work.

That separation is why GitFlow fits scheduled, versioned releases (mobile apps, desktop software, on-prem products, anything with explicit version numbers) better than continuously deployed web apps.

The GitFlow branching model at a glance

  hotfix/*    ──┐ (branch from main, urgent prod fix)
                │
  main      ●───┴──●──────────●        production / tagged releases
            ▲       ▲           ▲
            │       │ (merge release)  │ (merge hotfix back)
  release/* │   ┌───●──────┐    │
            │   │ (stabilize)│    │
  develop   ●───┴●───●───────┴────●     integration of all work
                 ▲    ▲
                 │    │ (merge finished feature)
  feature/* ─────┴────┘  (branch from develop)

Two permanent branches (main and develop) and three kinds of temporary branches (feature/*, release/*, hotfix/*). Everything flows through develop except hotfixes, which start from main.

Note: older GitFlow docs call the production branch master. Most teams now use main. They mean the same thing, pick one and be consistent.

The branches explained

main: production

main always reflects what's currently in production. Every commit on main is a release, and each one is tagged with a version (v1.4.0). You never commit directly to main; code only arrives via a merged release or hotfix branch.

develop: integration

develop is the integration branch where finished features accumulate for the next release. It holds the latest delivered development work and is always slightly ahead of main.

Feature branches

Feature branches are where new work happens. They:

  • branch from develop
  • merge back into develop
  • never interact with main directly
  • are named feature/short-description

Release branches

A release branch is cut from develop when you have enough features for a release. It exists to stabilize: final QA, version bumps, docs, last-minute bug fixes, while develop stays open for the next cycle's work. When ready, it merges into both main (to ship) and back into develop (so the fixes aren't lost).

Hotfix branches

Hotfix branches handle urgent production bugs. They branch from main (the live code), get the fix, then merge into both main and develop. This is the one flow that bypasses develop on the way out, because you can't wait for the next release to fix a production incident.

Support branches (optional)

The git-flow tooling also supports long-lived support/* branches for maintaining older major versions (e.g. keeping 1.x alive after 2.0 ships). Most teams never need these.

Branch naming conventions

Consistent prefixes make branches self-documenting and let the git-flow CLI find them automatically.

Branch typeBranches fromMerges intoNaming conventionExample
Featuredevelopdevelopfeature/*feature/oauth-login
Releasedevelopmain + developrelease/*release/1.4.0
Hotfixmainmain + develophotfix/*hotfix/payment-timeout
Supportmain (tag),support/*support/1.x

Keep names lowercase, hyphenated, and descriptive. For release and hotfix branches, use the version number directly so the tag is obvious.

The GitFlow workflow, step by step

Starting a feature

Branch off develop, do the work, then merge back:

# create the feature branch
git checkout develop
git pull
git checkout -b feature/add-login-page

# ... commit your work ...
git add .
git commit -m "Add login page"

# merge it back into develop
git checkout develop
git merge --no-ff feature/add-login-page
git branch -d feature/add-login-page
git push origin develop

Use --no-ff (no fast-forward) so the merge creates a commit that records the feature as a unit, it keeps history readable and makes reverts easy.

In practice, you'd push the feature branch and open a pull request instead of merging locally, so the work gets code review and CI before it lands in develop.

Cutting a release

When develop has enough for a release, branch a release/* from it. develop stays open for new work; the release branch only gets stabilization commits.

git checkout -b release/1.4.0 develop

# bump version, update changelog, fix release-only bugs
git commit -am "Bump version to 1.4.0"

# ship it: merge into main and tag
git checkout main
git merge --no-ff release/1.4.0
git tag -a v1.4.0 -m "Release 1.4.0"

# merge the stabilization fixes back into develop
git checkout develop
git merge --no-ff release/1.4.0

# clean up
git branch -d release/1.4.0
git push origin main develop --tags

The double merge is the part people forget: a release branch goes into both main and develop.

Shipping a hotfix for a critical production bug

This is the scenario most GitFlow questions are really about: production is broken, and you need to fix it now without dragging in unreleased work from develop.

The correct procedure: branch the hotfix from main, then merge it into both main and develop.

# branch from production, not develop
git checkout -b hotfix/payment-timeout main

# fix the bug
git commit -am "Fix payment gateway timeout"

# deploy the fix: merge into main and tag a patch release
git checkout main
git merge --no-ff hotfix/payment-timeout
git tag -a v1.4.1 -m "Hotfix 1.4.1"

# make sure the fix survives into the next release
git checkout develop
git merge --no-ff hotfix/payment-timeout

git branch -d hotfix/payment-timeout
git push origin main develop --tags

Branching from develop would be wrong, develop contains unreleased features that aren't ready for production. Branching from main guarantees the fix is built on exactly what's live. And merging back into develop ensures the bug doesn't reappear in the next release.

Using the git-flow CLI

The manual commands above are the model. The git-flow extension wraps them in higher-level commands so you don't have to remember every merge and tag step.

# one-time setup in a repo (prompts for branch names: accept defaults)
git flow init

# features
git flow feature start oauth-login     # branch from develop
git flow feature finish oauth-login    # merge into develop, delete branch

# releases
git flow release start 1.4.0           # branch from develop
git flow release finish 1.4.0          # merge into main + develop, tag

# hotfixes
git flow hotfix start payment-timeout  # branch from main
git flow hotfix finish payment-timeout # merge into main + develop, tag

Each finish command performs the correct merges, tagging, and cleanup automatically. The CLI is convenient, but understand the underlying Git operations first, when something goes sideways, you'll be resolving it with plain Git.

A complete end-to-end example

Feature → release → hotfix, start to finish:

# 1. build a feature
git flow feature start new-dashboard
git commit -am "Add analytics dashboard"
git flow feature finish new-dashboard

# 2. cut and ship a release
git flow release start 2.0.0
git commit -am "Bump to 2.0.0, update changelog"
git flow release finish 2.0.0          # -> main (tag v2.0.0) + develop

# 3. a bug shows up in production
git flow hotfix start dashboard-crash
git commit -am "Fix null pointer in dashboard widget"
git flow hotfix finish dashboard-crash # -> main (tag v2.0.1) + develop

git push origin main develop --tags

GitFlow vs other branching strategies

GitFlow isn't the only model, and it's not always the right one. Here's how it compares to the common alternatives.

StrategyLong-lived branchesRelease styleBest for
GitFlowmain + developScheduled, versionedVersioned products, multiple releases in flight, on-prem/mobile
GitHub Flowmain onlyContinuousWeb apps with frequent deploys, small teams
Trunk-basedmain (trunk)Continuous, behind flagsHigh-velocity CI/CD, large teams, deploy many times a day
Release Flowmain + release branchesPer-release branch off trunkTrunk-based teams that still ship versioned releases

A few honest takes from running these in production:

  • GitHub Flow is simpler: branch from main, open a PR, merge, deploy. If you deploy continuously, the develop branch in GitFlow is overhead you don't need.
  • Trunk-based development pairs short-lived branches with feature flags so incomplete work ships disabled rather than living on a long-running branch. It's the model most high-throughput teams converge on.
  • Feature flags vs GitFlow isn't really either/or: flags decouple deploy from release, which reduces how much you lean on release branches regardless of strategy.

GitFlow's extra structure earns its keep when you genuinely have multiple versions to support or a real QA/staging gate before release. If you deploy main to production several times a day, GitFlow will feel like friction.

When to use GitFlow (and when not to)

Use GitFlow when:

  • You ship discrete, versioned releases (v1.0, v1.1, v2.0).
  • You support more than one version in the wild at a time.
  • You have a formal QA/staging phase before production.
  • Releases are scheduled rather than continuous.

Skip GitFlow when:

  • You deploy continuously (multiple times a day). Reach for GitHub Flow or trunk-based.
  • Your team is small and the develop + release-branch overhead slows you down.
  • You already use feature flags heavily and deploy main directly.

Best practices

  • Always merge with --no-ff so feature and release merges show up as distinct units in history.
  • Use pull requests, not local merges, so everything gets code review and CI.
  • Keep feature branches short-lived. Long-running branches drift from develop and turn merges into rebasing nightmares. Days, not weeks.
  • Tag every release on main with an annotated, semantic version tag: it's your rollback map.
  • Never commit directly to main or develop. Code only arrives via a merged branch.
  • Automate the merges in CI/CD so the "merge into both main and develop" steps for releases and hotfixes can't be skipped by hand.
  • Pull before you branch so features and hotfixes start from current code.

FAQ

Is GitFlow still relevant? Yes, for versioned and release-scheduled software. For continuously deployed web apps, GitHub Flow or trunk-based development is usually the better fit. The right answer depends on how you ship, not on fashion.

What's the difference between GitFlow and GitHub Flow? GitHub Flow has one long-lived branch (main) and short feature branches that merge straight back. GitFlow adds a permanent develop branch plus dedicated release and hotfix branches, more structure, better for scheduled versioned releases, more overhead for continuous deployment.

Where does a hotfix branch come from in GitFlow? From main (the production branch), so the fix is built on exactly what's live. It then merges into both main and develop.

Should release branches be deleted after merging? Yes. Once a release branch is merged into main and develop and the release is tagged, delete the branch. The tag (v1.4.0) is the permanent record, the branch has done its job.

Can I use GitFlow without the git-flow CLI? Absolutely. The CLI is just a convenience wrapper around standard Git commands. Plenty of teams run GitFlow with plain git checkout, git merge --no-ff, and git tag.

Summary

GitFlow gives you a clear home for every kind of change: features off develop, releases stabilized in isolation and merged into both main and develop, and hotfixes branched from main and merged back into both. That structure shines for versioned, scheduled releases and gets in the way of continuous deployment. Pick the model that matches how you actually ship, and whichever you choose, lean on pull requests, CI/CD, and tags to keep it honest.

Share:LinkedInXWhatsApp

This article was originally published on dev.to.

Related articles

Reactions & comments