Every team argues about Git workflow. Trunk-based vs. GitFlow vs. GitHub Flow vs. whatever the latest thought leader is promoting. The arguments miss the point: the best workflow is the one that fits your team, your deployment cadence, and your risk tolerance.

The Spectrum

Git workflows exist on a spectrum from “move fast” to “control everything.”

Trunk-BFCFSaaIemss/aaetCtldDulirtrePeeRrqfsaultiairgoesndGitFlowMMLFuaoolnnrtugmia-aplllliervveeerldreeslabiesroaeanssniecnhgtersacks

Neither end is wrong. They optimize for different things.

Trunk-Based Development

Everyone commits to main. Features ship behind flags. Branches live hours, not days.

main:short-livedfeaturebranches

The workflow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Start work
git checkout main
git pull
git checkout -b feature/add-checkout-button

# Work in small increments
git add -p
git commit -m "Add checkout button component"
git push -u origin feature/add-checkout-button

# Open PR immediately (even if WIP)
# Get review, merge same day
# Delete branch

Key practices:

  1. Branch lifetime < 1 day Long branches accumulate merge conflicts and integration risk.

  2. Feature flags for incomplete work

    1
    2
    3
    
    if feature_flags.is_enabled("new_checkout"):
        return new_checkout_flow()
    return old_checkout_flow()
    
  3. CI runs on every commit If tests pass, it’s deployable.

  4. Small, focused commits Each commit should be reviewable in minutes, not hours.

Who it’s for:

  • Teams with strong CI/CD
  • Web applications with continuous deployment
  • Teams comfortable with feature flags
  • Organizations prioritizing speed

GitHub Flow

Simplified branching: main is always deployable, features branch off and merge back.

mfhaeoiatntf:uirxe::

The workflow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Create branch from main
git checkout main
git pull
git checkout -b feature/user-profiles

# Work, commit, push
git commit -m "Add user profile page"
git push -u origin feature/user-profiles

# Open PR, get reviews
# Squash merge to main
# Deploy main to production

Branch naming conventions:

ffhdreiooeaxtcft/fsaudi/crexdtes/eo/cdsrdrec/eisrdspciectrpsriitciopirpntoitinpiotoninon#####NBUDCeuroowggcdeueffnmeitecaxnltepteusraarotnediusuopcntionfixes

Who it’s for:

  • Most web teams
  • Projects with single production version
  • Teams wanting simplicity
  • GitHub-centric workflows

GitFlow

Structured branching for projects with formal releases.

mdfrhaeeeoivaltnetef:luaiorsxpee::s::(releases)

The branches:

BranchPurposeMerges to
mainProduction releases-
developIntegrationmain (via release)
feature/*New workdevelop
release/*Release prepmain and develop
hotfix/*Production fixesmain and develop

Creating a release:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Start release from develop
git checkout develop
git checkout -b release/1.2.0

# Bump version, final fixes
npm version 1.2.0
git commit -am "Bump version to 1.2.0"

# Merge to main and tag
git checkout main
git merge release/1.2.0
git tag -a v1.2.0 -m "Release 1.2.0"

# Merge back to develop
git checkout develop
git merge release/1.2.0

# Cleanup
git branch -d release/1.2.0

Who it’s for:

  • Packaged software with versions
  • Mobile apps with app store releases
  • Projects needing to support multiple versions
  • Teams with dedicated release management

Ship/Show/Ask

A social workflow that categorizes changes by risk.

Ship (no review needed):

  • Typo fixes
  • Dependency updates
  • Small refactors you’re confident about
1
2
3
git checkout main
git commit -m "Fix typo in README"
git push

Show (merge, then review):

  • Low-risk changes you want feedback on
  • Learning opportunities for the team
1
2
3
4
5
git checkout -b show/refactor-auth
# Make changes
git push
# Open PR, merge immediately
# Team reviews async, comments are for learning

Ask (review before merge):

  • High-risk changes
  • Unfamiliar territory
  • Security-sensitive code
1
2
3
4
5
git checkout -b ask/payment-integration
# Make changes
git push
# Open PR, wait for approval
# Address feedback, then merge

Who it’s for:

  • High-trust teams
  • Teams wanting to reduce review bottlenecks
  • Organizations with strong testing culture

Practical Patterns

The Perfect Commit Message

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

Example:

fIUCemslapeotlss(eecmSshetenr#cti1kp2oAe3up'4tps)l:ePaaPydamdyenAatpspRlaeeqpuPaeaysymtesnAutPpIpmoefrtothrodbrionwscehrecckoomuptatfilboiwl.ity.

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • refactor: Code restructuring
  • test: Test changes
  • chore: Maintenance

Squash vs. Merge vs. Rebase

Squash merge (recommended for most):

1
2
3
# Combines all branch commits into one
git merge --squash feature/thing
git commit -m "feat: add thing"
  • Clean main history
  • Loses individual commit detail
  • Good for feature branches

Merge commit:

1
git merge feature/thing
  • Preserves complete history
  • Creates merge commit
  • Good for long-running branches

Rebase:

1
2
3
4
git checkout feature/thing
git rebase main
git checkout main
git merge --ff-only feature/thing
  • Linear history
  • Rewrites commits
  • Requires force push

Handling Merge Conflicts

Prevention is better than resolution:

  1. Pull often

    1
    2
    3
    4
    5
    6
    
    # Before starting work
    git checkout main && git pull
    
    # While working
    git fetch origin
    git rebase origin/main  # or merge
    
  2. Keep branches short Shorter branches = fewer conflicts

  3. Communicate “I’m touching the auth module today” prevents duplicate work

When conflicts happen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# See what's conflicted
git status

# For each file, edit to resolve
# Look for <<<<<<<, =======, >>>>>>>

# Mark resolved
git add <file>

# Continue
git rebase --continue  # or commit if merging

Protecting Main

Branch protection rules (GitHub/GitLab):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Example GitHub branch protection
protection_rules:
  main:
    required_reviews: 1
    dismiss_stale_reviews: true
    require_status_checks:
      - ci/tests
      - ci/lint
    require_linear_history: false
    allow_force_pushes: false
    allow_deletions: false

Minimum settings:

  • Require PR (no direct pushes)
  • Require CI pass
  • Require at least one approval

The Monorepo Question

Monorepos change workflow considerations:

repo/apipanpcfskr/wmaausaeopgihsbbie/at/i/srrl/euedc//ture/

Challenges:

  • CI needs to know what changed
  • Not every change affects every app
  • Large diffs become unmanageable

Solutions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Only run tests for affected packages
npx turbo run test --filter=...[origin/main]

# Scope PRs clearly
feat(web): add dark mode
fix(api): handle null user

# Use CODEOWNERS for automatic reviewers
# .github/CODEOWNERS
/apps/web/    @frontend-team
/apps/api/    @backend-team
/packages/ui/ @design-system-team

Choosing Your Workflow

Use Trunk-Based when:

  • You deploy continuously
  • You have strong CI/CD
  • You’re comfortable with feature flags
  • Team is experienced and high-trust

Use GitHub Flow when:

  • You want simplicity
  • You deploy frequently but not continuously
  • You have a single production version
  • Team is growing or mixed experience

Use GitFlow when:

  • You have formal release cycles
  • You support multiple versions
  • You have dedicated release management
  • Regulatory requirements demand traceability

Use Ship/Show/Ask when:

  • You trust your team completely
  • Review bottlenecks are slowing you down
  • You have excellent test coverage
  • You want to foster autonomy

The Meta-Rule

Whatever workflow you choose:

  1. Write it down - Document your workflow in CONTRIBUTING.md
  2. Automate enforcement - Use CI checks and branch protection
  3. Review periodically - Workflows should evolve with your team
  4. Stay consistent - Inconsistent workflow is worse than imperfect workflow

The best Git workflow is one your whole team understands and follows. Everything else is details.


Git is a tool. Workflows are how you use tools together. Pick one and stick with it.