DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

Git Branching Strategies: Trunk-Based, GitHub Flow, Git Flow — and Which to Choose

Git branching strategy is one of those things that seems obvious until you're dealing with a broken production deploy, three merge conflicts, and a hotfix that can't go out because the release branch is locked. Getting the strategy right early prevents a lot of pain.

Here are the main approaches, what they optimize for, and when to use each.

Trunk-Based Development

Everyone commits to main (or trunk) directly, or uses very short-lived feature branches (1-2 days max). CI runs on every commit. Deployments happen frequently — sometimes multiple times a day.

main: A - B - C - D - E - F
                ↑   ↑   ↑
             deploys
Enter fullscreen mode Exit fullscreen mode

When to use: Teams with strong CI/CD, good test coverage, and feature flags. Works well for SaaS products where you control the deployment. Google and Meta use trunk-based development at scale.

Advantages:

  • No long-lived merge hell
  • Forces small, reviewable changes
  • Simulates what users will get quickly
  • Integrates well with feature flags (ship dark)

Disadvantages:

  • Requires discipline to keep main releasable at all times
  • Feature flags add complexity
  • Less intuitive for teams used to longer PR cycles

GitHub Flow

One long-lived branch (main) plus short-lived feature branches. Feature branches are merged via PR, then deployed immediately.

main: A -------- B -------- C
       \        /  \       /
     feature-1     feature-2
Enter fullscreen mode Exit fullscreen mode

Workflow:

git checkout -b feature/user-auth
# work
git push -u origin feature/user-auth
# open PR → review → merge
# deploy main
Enter fullscreen mode Exit fullscreen mode

When to use: Teams deploying continuously, open-source projects, apps where main is always deployable.

Advantages:

  • Simple to understand
  • Works well with GitHub/GitLab PR workflow
  • Short feedback loops

Disadvantages:

  • No clear separation between development and release
  • Doesn't handle scheduled releases well

Git Flow

Multiple long-lived branches: main, develop, release/*, hotfix/*. Feature branches come off develop.

main:    A -------- B -------- C  (only releases + hotfixes)
           \        ↑    ↑
develop: A - D - E - F - G - H  (ongoing work)
              \     /    \   /
          feature/x    feature/y
Enter fullscreen mode Exit fullscreen mode

Workflow:

# Start feature
git checkout -b feature/payment develop

# Finish feature
git checkout develop
git merge --no-ff feature/payment

# Start release
git checkout -b release/1.2 develop
# bug fixes only on release branch
git checkout main
git merge --no-ff release/1.2
git tag -a v1.2

# Hotfix
git checkout -b hotfix/fix-payment main
git checkout main && git merge --no-ff hotfix/fix-payment
git checkout develop && git merge --no-ff hotfix/fix-payment
Enter fullscreen mode Exit fullscreen mode

When to use: Mobile apps, versioned software, teams with scheduled releases, libraries with multiple supported versions.

Disadvantages:

  • Complex — many branches to track
  • Long-lived branches create merge conflicts
  • Overkill for most web apps that deploy continuously

A Simplified Two-Branch Strategy

For most web teams, a simplified version works well:

  • main: always deployable, auto-deploys to staging
  • production: tracks what's live, auto-deploys to production
  • Feature branches: off main, merged via PR
# Feature work
git checkout -b feature/dark-mode main
# develop, commit, push
# Open PR → review → merge to main → deploys to staging

# Release
git checkout production
git merge main
git push  # deploys to production
git tag v1.4.0
Enter fullscreen mode Exit fullscreen mode

Hotfixes go directly to production and are immediately back-merged to main.

Practical Git Habits

Always branch from the right place

# Make sure your base is up to date
git checkout main
git pull origin main
git checkout -b feature/my-thing
Enter fullscreen mode Exit fullscreen mode

Keep commits small and single-purpose

# Bad: one commit that does everything
git commit -m "add auth, update UI, fix bug, refactor data layer"

# Good: atomic commits
git commit -m "add JWT validation middleware"
git commit -m "add login/signup endpoint"
git commit -m "add auth state to Redux store"
Enter fullscreen mode Exit fullscreen mode

Use --no-ff for merges you want to track

git merge --no-ff feature/my-thing
Enter fullscreen mode Exit fullscreen mode

This preserves the feature branch in history. Without --no-ff, fast-forward merges lose the branch topology.

Rebase to clean up before PR

# Squash work-in-progress commits before review
git rebase -i origin/main

# Rebase onto updated main instead of merging
git rebase origin/main
Enter fullscreen mode Exit fullscreen mode

Interactive rebase lets you squash, reword, and reorder commits before they're reviewed. This keeps main history readable.

Protect your main branch

On GitHub:

  • Require PRs to merge to main
  • Require status checks (CI must pass)
  • Require at least 1 review
  • Prevent force pushes
# .github/branch-protection.yml (via API or UI)
required_status_checks:
  strict: true
  contexts:
    - "ci/tests"
    - "ci/lint"
required_pull_request_reviews:
  required_approving_review_count: 1
enforce_admins: true
Enter fullscreen mode Exit fullscreen mode

Versioning and Tags

Tag releases so you can track what's deployed:

# Semantic version tag
git tag -a v1.4.2 -m "v1.4.2: fix checkout bug"
git push origin v1.4.2

# List tags
git tag -l "v1.*"

# Find which tag a commit is in
git describe --tags HEAD
Enter fullscreen mode Exit fullscreen mode

Semantic versioning: MAJOR.MINOR.PATCH

  • MAJOR: breaking changes
  • MINOR: new features, backwards-compatible
  • PATCH: bug fixes

Merge vs Rebase: The Practical Answer

  • Rebase your feature branch onto main before opening a PR: keeps a linear, readable history
  • Merge (or squash) when merging the PR: preserves the fact that a set of commits came in together
  • Never rebase commits that have been pushed to a shared branch (rewrites history others may have based work on)

The rule: rebase privately (your local feature branch), merge publicly (into main via PR).

Commit Message Format

A conventional commits format makes changelogs and searches easier:

feat: add dark mode toggle
fix: resolve session expiry on Firefox mobile
docs: update API auth section
chore: upgrade dependencies
refactor: split user service into auth and profile
Enter fullscreen mode Exit fullscreen mode

Tools like semantic-release can auto-generate changelogs and version bumps from these.

The right branching strategy is the simplest one that handles your deployment cadence and team size without creating merge nightmares. For most web apps: GitHub Flow (or a simple two-branch variant). For versioned software or scheduled releases: Git Flow or a lighter variant.

Top comments (0)