BytePane

Git Branching Strategies: GitFlow, GitHub Flow & Trunk-Based Development

Git12 min read

Why Branching Strategies Matter

Every software team uses Git, but how you organize branches determines whether your workflow is a smooth highway or a merge-conflict minefield. A branching strategy defines how developers create, name, merge, and delete branches. The right strategy depends on your team size, release cadence, and deployment pipeline. The wrong one leads to integration nightmares, stale branches, and deployment fear.

This guide compares the three most popular strategies -- GitFlow, GitHub Flow, and Trunk-Based Development -- with concrete examples, branch diagrams, and decision criteria so you can pick the one that fits your team.

Strategy Comparison at a Glance

FeatureGitFlowGitHub FlowTrunk-Based
Long-lived branchesmain + developmain onlymain only
Feature branchesYes (days-weeks)Yes (hours-days)Optional (hours)
Release branchesYesNoNo
Hotfix branchesYesNo (use feature)No (commit to trunk)
Merge strategyMerge commitsSquash merge / PRRebase / direct push
Release cadenceScheduledContinuousContinuous
Feature flags neededNoSometimesYes (critical)
Best forVersioned software, mobile appsWeb apps, small teamsLarge teams, CD pipelines

GitFlow: The Classic Enterprise Workflow

GitFlow, introduced by Vincent Driessen in 2010, uses two long-lived branches (main and develop) plus three types of supporting branches: feature, release, and hotfix. It is designed for projects with scheduled releases and multiple versions in production.

Branch Structure

main          ──●──────────────────●──────●──── (production releases)
                \                  /      /
release/1.0    \──●──●──────●    /       (stabilization)
                \       \    /   /
develop        ──●──●──●──●──●──●──●──── (integration branch)
                  \  /       \     /
feature/login   ●──●         \   /
                           ●──●
                        feature/auth

Branch naming:
  feature/user-login
  feature/JIRA-123-payment
  release/1.2.0
  hotfix/critical-auth-fix

GitFlow Workflow Commands

# Start a new feature
git checkout develop
git checkout -b feature/user-dashboard

# Work on the feature... commit changes
git add .
git commit -m "Add dashboard layout with chart widgets"

# Finish feature -- merge back into develop
git checkout develop
git merge --no-ff feature/user-dashboard
git branch -d feature/user-dashboard

# Create a release branch when develop is ready
git checkout develop
git checkout -b release/1.2.0

# Fix bugs on the release branch, update version
git commit -m "Bump version to 1.2.0"

# Finish release -- merge into main AND develop
git checkout main
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release 1.2.0"
git checkout develop
git merge --no-ff release/1.2.0
git branch -d release/1.2.0

# Emergency hotfix from main
git checkout main
git checkout -b hotfix/fix-payment-crash
# Fix the bug...
git checkout main
git merge --no-ff hotfix/fix-payment-crash
git tag -a v1.2.1 -m "Hotfix 1.2.1"
git checkout develop
git merge --no-ff hotfix/fix-payment-crash
git branch -d hotfix/fix-payment-crash

When to Use GitFlow

  • Mobile apps with app store review cycles (iOS, Android)
  • Desktop software with versioned releases (v1.0, v2.0)
  • Enterprise projects with QA/staging environments per release
  • Teams supporting multiple major versions simultaneously

When to Avoid GitFlow

  • Web applications deployed continuously (too much ceremony)
  • Small teams under 5 developers (unnecessary complexity)
  • Projects without version numbers or scheduled releases

GitHub Flow: Simple and Continuous

GitHub Flow, popularized by GitHub itself, strips branching down to the essentials: one main branch, short-lived feature branches, pull requests, and continuous deployment. There is no develop branch, no release branches, and no hotfix branches. Every merge to main is deployed to production.

The Complete Workflow

# 1. Create a feature branch from main
git checkout main
git pull origin main
git checkout -b add-search-filter

# 2. Make commits with clear messages
git add src/components/SearchFilter.tsx
git commit -m "Add search filter component with debounce"

git add src/hooks/useSearch.ts
git commit -m "Add useSearch hook with fuzzy matching"

# 3. Push and open a Pull Request
git push -u origin add-search-filter
# Open PR on GitHub -- triggers CI/CD checks

# 4. Code review + automated tests pass

# 5. Merge PR (squash or merge commit)
# GitHub merges and optionally deploys to production

# 6. Delete the branch
git checkout main
git pull origin main
git branch -d add-search-filter

Best Practices for GitHub Flow

  1. Keep branches short-lived -- merge within 1-3 days. Longer branches accumulate merge conflicts and integration risk.
  2. Write descriptive PR titles -- your PR title becomes the merge commit message. Make it meaningful for git log.
  3. Require CI checks before merge -- set up branch protection rules to prevent merging broken code.
  4. Use squash merge for clean history -- squash merge combines all PR commits into one clean commit on main.
  5. Deploy after every merge -- if you are not deploying after every merge, GitHub Flow is not the right fit.

Trunk-Based Development: The Speed-First Approach

Trunk-Based Development (TBD) takes simplicity to its logical extreme: all developers commit directly to the main branch (the "trunk") or use very short-lived feature branches that last hours, not days. Google, Facebook, Netflix, and most high-performing engineering teams use TBD.

How It Works

# Option A: Commit directly to main
git checkout main
git pull --rebase origin main
# Make small, focused changes
git add src/utils/format.ts
git commit -m "Add currency formatter with locale support"
git push origin main

# Option B: Very short-lived branch (merged same day)
git checkout main
git pull --rebase origin main
git checkout -b fix-date-parser
# Fix in 2-3 hours max
git add .
git commit -m "Fix date parser timezone offset bug"
git checkout main
git merge fix-date-parser
git push origin main
git branch -d fix-date-parser

# Feature flags for incomplete work
// config/features.ts
export const FEATURES = {
  NEW_DASHBOARD: process.env.FEATURE_DASHBOARD === 'true',
  BETA_SEARCH: process.env.FEATURE_SEARCH === 'true',
}

// In your component
import { FEATURES } from '@/config/features'

function App() {
  return FEATURES.NEW_DASHBOARD
    ? <NewDashboard />
    : <LegacyDashboard />
}

Prerequisites for Trunk-Based Development

  • Comprehensive automated tests -- every commit goes to production, so your test suite must catch regressions
  • Feature flags -- you need a way to deploy incomplete features without exposing them to users
  • Fast CI/CD pipeline -- builds must complete in minutes, not hours, to keep the trunk green
  • Small, incremental commits -- each commit should be independently deployable and safe
  • Team discipline -- no one commits code that breaks the build

Branch Naming Conventions

Consistent branch naming makes your repository navigable and your CI/CD rules predictable. Here are the most common conventions across all three strategies.

TypePatternExamples
Featurefeature/descriptionfeature/user-auth, feature/JIRA-456-search
Bug fixfix/descriptionfix/login-redirect, fix/null-pointer
Releaserelease/versionrelease/1.2.0, release/2026-q1
Hotfixhotfix/descriptionhotfix/crash-on-login, hotfix/security-patch
Chorechore/descriptionchore/update-deps, chore/ci-config

Use lowercase, hyphens as separators, and include a ticket number if your team uses issue tracking. Validate branch names with a regex pattern like (feature|fix|hotfix|release|chore)/[a-z0-9-]+ in a pre-push hook.

Merge vs Rebase: Which to Use

The merge-vs-rebase debate is one of the most common Git discussions. The answer depends on your branching strategy and what you optimize for: history clarity or traceability.

AspectMergeRebase
HistoryPreserves full branch historyCreates linear history
Merge commitsCreates merge commitNo merge commit
Conflict resolutionOnce per mergePer rebased commit
Safe for shared branchesYesNo (rewrites history)
Best used forIntegrating feature into mainUpdating feature from main
# Golden rule: rebase to update, merge to integrate

# Update your feature branch with latest main
git checkout feature/search
git rebase main
# Resolve conflicts per commit if any

# Integrate feature into main (via PR or manual merge)
git checkout main
git merge --no-ff feature/search
# Creates a merge commit -- clear integration point

# Squash merge for cleaner history (GitHub PR option)
git checkout main
git merge --squash feature/search
git commit -m "Add search with fuzzy matching and filters"

Choosing the Right Strategy for Your Team

Use this decision framework to pick the strategy that matches your team's reality, not its aspirations.

If your team...UseReason
Ships versioned releases (v1, v2)GitFlowRelease branches for stabilization
Deploys web apps continuouslyGitHub FlowSimple, PR-based, CI/CD friendly
Has 2-5 developersGitHub FlowMinimal overhead, easy to learn
Has 50+ developers on one repoTrunk-BasedAvoids long-lived branch conflicts
Deploys 10+ times per dayTrunk-BasedBranches add unnecessary latency
Supports multiple prod versionsGitFlowParallel release/hotfix branches

Many teams start with GitHub Flow and migrate to Trunk-Based Development as their CI/CD pipeline matures. GitFlow is less common in modern web development but remains the standard for mobile and desktop software with app store release cycles.

Automating Branch Policies

Branch protection rules enforce your strategy automatically. Here is an example GitHub Actions workflow that validates branch names and requires checks before merging.

# .github/workflows/branch-policy.yml
name: Branch Policy
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  validate-branch:
    runs-on: ubuntu-latest
    steps:
      - name: Check branch name
        run: |
          BRANCH=${{ github.head_ref }}
          if [[ ! "$BRANCH" =~ ^(feature|fix|hotfix|chore)/ ]]; then
            echo "Invalid branch name: $BRANCH"
            echo "Must start with feature/, fix/, hotfix/, or chore/"
            exit 1
          fi

      - name: Check PR size
        uses: actions/github-script@v7
        with:
          script: |
            const { data: files } = await github.rest.pulls.listFiles({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number,
            });
            const changes = files.reduce((sum, f) =>
              sum + f.additions + f.deletions, 0);
            if (changes > 500) {
              core.warning(
                `Large PR: ${changes} lines changed. Consider splitting.`
              );
            }

You can validate branch names, commit messages, and other patterns with a Regex Tester before hardcoding them into CI/CD configs. For scheduled automation like stale branch cleanup, cron expressions handle the timing.

Frequently Asked Questions

What is the best Git branching strategy for small teams?
GitHub Flow is ideal for small teams (2-10 developers). It uses a single main branch with short-lived feature branches that are merged via pull requests. There are no develop, release, or hotfix branches to manage. This simplicity reduces overhead while maintaining code quality through mandatory code reviews and CI/CD checks before merging.
Should I use git merge or git rebase for feature branches?
Use rebase for updating your feature branch with the latest main branch changes (git rebase main) to keep a linear history. Use merge (via pull request) when integrating the feature branch back into main, as this creates a merge commit that clearly marks when the feature was integrated. Never rebase branches that other developers have pulled -- this rewrites shared history and causes conflicts.
What is Trunk-Based Development and when should I use it?
Trunk-Based Development (TBD) is a strategy where all developers commit directly to the main branch (trunk) or use very short-lived feature branches lasting hours, not days. It requires feature flags to hide incomplete work, comprehensive automated testing, and a strong CI/CD pipeline. TBD is best for teams practicing continuous deployment with 10+ releases per day, such as Google, Facebook, and Netflix.

Validate Your Git Patterns

Test branch naming conventions, commit message formats, and CI/CD patterns with our free Regex Tester. Build and validate patterns directly in your browser.

Open Regex Tester

Related Articles