Git doesn’t enforce a workflow. You can branch however you want, merge whenever you want, push whatever you want. That freedom is powerful and dangerous.

A good branching strategy reduces merge conflicts, enables parallel development, and makes releases predictable. A bad one creates confusion, broken builds, and deployment fear.

Trunk-Based Development

The simplest model: everyone commits to main, frequently.

main:f(ehaotuurrse)f(ihxours)f(ehaotuurrse)

Rules:

  • Features are small, completed in hours or a day
  • All commits pass CI before merging
  • Feature flags hide incomplete work
  • No long-lived branches
1
2
3
4
5
6
7
8
# Daily workflow
git checkout main
git pull
git checkout -b feature/add-button
# ... work for a few hours ...
git commit -m "Add submit button"
git push -u origin feature/add-button
# Create PR, get review, merge same day

Pros: Simple, minimal merge conflicts, continuous integration Cons: Requires feature flags, demands CI discipline, scary without good tests

Best for: Small teams, experienced developers, strong CI/CD.

GitHub Flow

One step up: short-lived feature branches with PR review.

main:f(edaatyusr)ef(edaatyusr)e

Rules:

  • Main is always deployable
  • Branch from main for features
  • Open PR for discussion
  • Merge after review and CI passes
  • Deploy immediately after merge
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
git checkout main
git pull
git checkout -b feature/user-dashboard

# Work over several days
git commit -m "Add dashboard layout"
git commit -m "Add user stats widget"
git push -u origin feature/user-dashboard

# Open PR, discuss, review
# CI runs automatically
# Merge when approved
# Deploy automatically

Pros: Simple, encourages review, works well with CI/CD Cons: No staging environment distinction, features must be complete to merge

Best for: SaaS products, web apps with continuous deployment.

GitFlow

Structured model with multiple long-lived branches:

mdfrhaeeeoivaltnetef:luaiorsxpee::::

Branches:

  • main: Production code, tagged releases only
  • develop: Integration branch for features
  • feature/*: Individual features, branch from develop
  • release/*: Release preparation, branch from develop
  • hotfix/*: Production fixes, branch from main
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Start feature
git checkout develop
git checkout -b feature/new-feature

# Complete feature
git checkout develop
git merge feature/new-feature

# Create release
git checkout -b release/1.2.0 develop
# ... testing, fixes ...
git checkout main
git merge release/1.2.0
git tag -a v1.2.0
git checkout develop
git merge release/1.2.0

# Hotfix
git checkout -b hotfix/1.2.1 main
# ... fix ...
git checkout main
git merge hotfix/1.2.1
git tag -a v1.2.1
git checkout develop
git merge hotfix/1.2.1

Pros: Clear release process, parallel development, hotfix path Cons: Complex, merge overhead, not suited for continuous deployment

Best for: Packaged software, mobile apps, scheduled releases.

Release Branches

Simplified GitFlow for products with multiple supported versions:

mrraeeillnee:aassee//12..xx::
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Develop on main
git checkout main
git commit -m "New feature"

# Cherry-pick fixes to release branches
git checkout release/1.x
git cherry-pick abc123

git checkout release/2.x
git cherry-pick abc123

Best for: Libraries, APIs, products with LTS versions.

Commit Message Conventions

Consistent messages enable automation:

<<<tbfyoopdoeyt>>e(r<>scope>):<subject>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • style: Formatting
  • refactor: Code restructuring
  • test: Tests
  • chore: Maintenance
1
2
3
4
5
6
feat(auth): add OAuth2 login support

Implement OAuth2 authorization code flow for Google
and GitHub providers.

Closes #123

Tools like semantic-release use these to auto-generate changelogs and version bumps.

Merge vs Rebase vs Squash

Merge commit: Preserves full history

1
2
3
git checkout main
git merge feature/xyz
# Creates merge commit, keeps all feature commits

Rebase: Linear history

1
2
3
4
5
git checkout feature/xyz
git rebase main
git checkout main
git merge feature/xyz --ff-only
# Feature commits appear after main commits

Squash: Single commit

1
2
3
4
git checkout main
git merge --squash feature/xyz
git commit -m "Add feature XYZ"
# All feature commits become one

Recommendation: Squash for features (clean history), merge for releases (preserve context).

Branch Protection

Prevent accidents with branch rules:

1
2
3
4
5
6
# GitHub branch protection
- Require PR reviews (1-2 reviewers)
- Require status checks (CI must pass)
- Require up-to-date branches
- No force pushes
- No deletions

Nobody can push directly to main, not even admins in a hurry.

Handling Long-Running Branches

Sometimes branches must live longer. Minimize pain:

1
2
3
4
5
6
7
8
# Regularly sync with main
git checkout feature/big-refactor
git fetch origin
git rebase origin/main
# Resolve conflicts incrementally

# Or merge main into feature
git merge origin/main

Sync at least weekly. Daily is better. The longer you wait, the worse conflicts become.

Monorepo Considerations

Multiple projects in one repo need path-based triggers:

1
2
3
4
5
6
# CI only runs for changed paths
on:
  push:
    paths:
      - 'services/api/**'
      - '.github/workflows/api.yml'

Consider tools like Nx or Turborepo for smarter builds.

Choosing a Strategy

SituationStrategy
Small team, continuous deployTrunk-based
Standard SaaS productGitHub Flow
Mobile app, scheduled releasesGitFlow
Library with versionsRelease branches
Enterprise, compliance needsGitFlow + approvals

Start simple. Add complexity only when you feel specific pain.


Git workflows are about communication as much as code. A good strategy makes it obvious where work happens, how it gets reviewed, and when it ships.

Pick a model, document it, enforce it with tooling. The best workflow is the one your team actually follows.