Skip to main content

Git Workflows: Git Flow vs GitHub Flow vs Trunk-Based Development

Master Git workflows by comparing Git Flow, GitHub Flow, and Trunk-Based Development. Learn when to use each workflow with practical examples, branching strategies, and team collaboration patterns.

Table of Contents

Introduction

Choosing the right Git workflow is one of the most critical decisions for any development team. The workflow you adopt affects how quickly you can ship features, how easily you can collaborate, how you handle releases, and how you manage code quality. With so many branching strategies available, it’s easy to feel overwhelmed or stick with what you know, even if it’s not the best fit for your team.

The wrong workflow can lead to merge conflicts, deployment bottlenecks, code quality issues, and frustrated developers. On the other hand, the right workflow can streamline your development process, reduce friction, enable faster releases, and improve code quality through better collaboration patterns.

This comprehensive guide compares three of the most popular Git workflows: Git Flow, GitHub Flow, and Trunk-Based Development. You’ll learn how each workflow works, when to use it, and how to implement it effectively. We’ll cover practical examples, branching strategies, release management, and team collaboration patterns that help you make informed decisions for your projects.

By the end of this guide, you’ll understand the trade-offs between different workflows and be able to choose and implement the right one for your team’s needs, whether you’re working on a small startup project or a large enterprise application.


Understanding Git Workflows

Before diving into specific workflows, it’s essential to understand what a Git workflow is and why it matters for your development process.

What is a Git Workflow?

A Git workflow is a branching strategy that defines how your team uses Git to collaborate on code. It establishes rules for:

  • How branches are created and named
  • When branches are merged
  • How releases are managed
  • How conflicts are resolved
  • How code review processes work

A well-defined workflow provides structure and consistency, making it easier for team members to understand the codebase’s state and contribute effectively.

Why Workflows Matter

Different workflows serve different purposes:

  • Release Management: Some workflows are designed for projects with scheduled releases (like Git Flow), while others support continuous deployment (like GitHub Flow)
  • Team Size: Small teams might prefer simpler workflows, while large teams need more structure
  • Deployment Frequency: Teams deploying multiple times per day need different workflows than teams releasing monthly
  • Code Stability: Projects requiring high stability benefit from workflows with more testing gates

Key Concepts

Before comparing workflows, let’s establish some key concepts:

Main Branch: The primary branch containing production-ready code (often called main or master)

Feature Branches: Branches created for developing new features

Release Branches: Branches used to prepare releases (not all workflows use these)

Hotfix Branches: Branches for urgent production fixes

Pull Requests/Merge Requests: Code review mechanisms before merging branches

Continuous Integration (CI): Automated testing and validation on every commit

Continuous Deployment (CD): Automated deployment to production when code passes tests


Git Flow: Feature Branch Workflow

Git Flow is a branching model introduced by Vincent Driessen in 2010. It’s designed for projects with scheduled releases and provides a robust structure for managing features, releases, and hotfixes.

Branch Structure

Git Flow uses five types of branches:

main (master)
├── develop
│ ├── feature/user-authentication
│ ├── feature/payment-integration
│ └── release/1.2.0
│ └── hotfix/critical-security-fix

Main Branches:

  • main (or master): Contains production-ready code
  • develop: Integration branch for features

Supporting Branches:

  • feature/*: New features
  • release/*: Preparing releases
  • hotfix/*: Urgent production fixes

How Git Flow Works

1. Feature Development

Features are developed in branches off develop:

Terminal window
# Start a new feature
git checkout develop
git pull origin develop
git checkout -b feature/user-authentication
# Work on the feature
git add .
git commit -m "feat: add user login form"
git commit -m "feat: implement authentication logic"
# Finish the feature
git checkout develop
git pull origin develop
git merge --no-ff feature/user-authentication
git branch -d feature/user-authentication
git push origin develop

Best Practice: Use --no-ff flag to preserve branch history and make it clear when features were merged.

2. Release Preparation

When develop is ready for release, create a release branch:

Terminal window
# Create release branch
git checkout develop
git checkout -b release/1.2.0
# Update version numbers, changelog, etc.
# Fix bugs found during release testing
git commit -m "chore: bump version to 1.2.0"
git commit -m "fix: resolve release blocker bug"
# Finish release
git checkout main
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release version 1.2.0"
git checkout develop
git merge --no-ff release/1.2.0
git branch -d release/1.2.0
git push origin main --tags
git push origin develop

3. Hotfixes

Hotfixes branch directly from main for urgent production fixes:

Terminal window
# Create hotfix branch
git checkout main
git checkout -b hotfix/critical-security-fix
# Fix the issue
git commit -m "fix: patch critical security vulnerability"
# Finish hotfix
git checkout main
git merge --no-ff hotfix/critical-security-fix
git tag -a v1.1.1 -m "Hotfix version 1.1.1"
git checkout develop
git merge --no-ff hotfix/critical-security-fix
git branch -d hotfix/critical-security-fix
git push origin main --tags
git push origin develop

Advantages of Git Flow

Structured Release Process: Clear separation between development and production code

Parallel Development: Multiple features can be developed simultaneously without conflicts

Release Management: Release branches allow for final testing and bug fixes before production

Hotfix Support: Quick fixes can be applied to production without disrupting development

Version History: Clear tagging and versioning for releases

Disadvantages of Git Flow

Complexity: More branches to manage, especially for small teams

Slower Releases: Multiple merge steps can delay releases

Merge Conflicts: Long-lived branches increase risk of conflicts

Overhead: Too much structure for simple projects or small teams

When to Use Git Flow

Git Flow works best for:

  • Projects with scheduled releases (monthly, quarterly)
  • Teams requiring formal release processes
  • Projects with multiple versions in production
  • Teams that need to support multiple release versions simultaneously

GitHub Flow: Simplified Continuous Deployment

GitHub Flow is a lightweight workflow designed for continuous deployment. It was popularized by GitHub and emphasizes simplicity and speed over structure.

Branch Structure

GitHub Flow uses a simple two-branch model:

main
├── feature/user-authentication
├── feature/payment-integration
└── hotfix/critical-bug

Main Branch:

  • main: Always deployable, contains production-ready code

Feature Branches:

  • Created from main for any change
  • Merged back to main via pull request
  • Deployed immediately after merge

How GitHub Flow Works

1. Feature Development

All changes start as feature branches:

Terminal window
# Create feature branch
git checkout main
git pull origin main
git checkout -b feature/user-authentication
# Make changes
git add .
git commit -m "feat: add user login form"
git commit -m "feat: implement authentication logic"
# Push and create pull request
git push origin feature/user-authentication

2. Pull Request Process

Create a pull request on GitHub (or your Git hosting platform):

Terminal window
# Pull request workflow:
# 1. Code review happens in the PR
# 2. CI/CD runs automated tests
# 3. Once approved, merge to main
# 4. Deploy automatically (if CI/CD is configured)

After merge, the feature is immediately deployable:

Terminal window
# After PR is merged
git checkout main
git pull origin main
# Code is ready to deploy!

3. Deployment

Deployments happen directly from main:

Terminal window
# Deploy from main (automated via CI/CD)
# No release branches needed
git checkout main
git pull origin main
# Deploy to production

Advantages of GitHub Flow

Simplicity: Easy to understand and implement

Fast Releases: No release branches means faster time to production

Continuous Deployment: Supports deploying multiple times per day

Clear History: Linear history with feature branches

Low Overhead: Minimal branching complexity

Disadvantages of GitHub Flow

No Release Management: Difficult to prepare releases with multiple features

Production Stability: main must always be deployable (requires discipline)

No Versioning: No built-in version tagging or release management

Hotfix Complexity: Hotfixes require same PR process as features

When to Use GitHub Flow

GitHub Flow works best for:

  • Web applications with continuous deployment
  • Small to medium-sized teams
  • Projects where main can always be deployed
  • Teams comfortable with frequent deployments
  • SaaS applications with single version in production

Trunk-Based Development: Continuous Integration

Trunk-Based Development (TBD) is a workflow where developers work directly on a single branch (main or trunk) with very short-lived feature branches or no branches at all.

Branch Structure

Trunk-Based Development minimizes branching:

main (trunk)
├── (short-lived feature branches, if any)
└── (all commits go directly to main)

Main Branch:

  • main (or trunk): Single source of truth
  • All developers commit directly or via very short-lived branches

Feature Branches (Optional):

  • Exist for less than 1-2 days
  • Merged immediately after review

How Trunk-Based Development Works

1. Direct Commits to Main

Developers commit directly to main for small changes:

Terminal window
# Small, incremental changes
git checkout main
git pull origin main
# Make small change
git add .
git commit -m "feat: add input validation"
git push origin main
# CI/CD runs tests automatically
# If tests pass, code is deployed

2. Short-Lived Feature Branches

For larger features, use very short-lived branches:

Terminal window
# Create feature branch
git checkout main
git pull origin main
git checkout -b feature/user-authentication
# Work on feature (same day or next day)
git add .
git commit -m "feat: implement user authentication"
git push origin feature/user-authentication
# Create PR and merge immediately
# Merge within hours, not days

3. Feature Flags

Use feature flags to hide incomplete features:

// Feature flag example
const FEATURE_FLAGS = {
NEW_AUTHENTICATION: process.env.ENABLE_NEW_AUTH === 'true'
};
function LoginForm() {
if (FEATURE_FLAGS.NEW_AUTHENTICATION) {
return <NewAuthenticationForm />;
}
return <LegacyAuthenticationForm />;
}

This allows incomplete features to be merged to main without being visible to users.

Advantages of Trunk-Based Development

Minimal Merge Conflicts: Short-lived branches reduce conflicts

Fast Integration: Code is integrated quickly, catching issues early

Simple Mental Model: Single branch to think about

Continuous Integration: True CI with frequent commits

Rapid Feedback: Issues are caught immediately

Disadvantages of Trunk-Based Development

Requires Discipline: main must always be stable

Feature Flags Complexity: Need to manage feature flags

Code Review Challenges: Harder to review large features

Deployment Risk: More frequent deployments require robust testing

Team Coordination: Requires good communication and coordination

When to Use Trunk-Based Development

Trunk-Based Development works best for:

  • Teams with strong CI/CD practices
  • Projects requiring rapid iteration
  • Teams comfortable with feature flags
  • Mature codebases with good test coverage
  • Organizations prioritizing speed over structure

Comparing the Three Workflows

Let’s compare the three workflows across key dimensions:

AspectGit FlowGitHub FlowTrunk-Based Development
ComplexityHighLowVery Low
Release FrequencyScheduled (weeks/months)Continuous (daily)Continuous (multiple/day)
Branch LifetimeLong (weeks)Medium (days)Short (hours)
Release Management✅ Formal process❌ No formal process❌ No formal process
Hotfix Support✅ Dedicated branches⚠️ Same as features⚠️ Direct commits
Team SizeAny sizeSmall to mediumAny size (requires maturity)
Learning CurveSteepGentleModerate
Merge ConflictsCommonModerateRare
Deployment SpeedSlowFastVery Fast
Code StabilityHighMediumRequires discipline
Versioning✅ Tagged releases⚠️ Manual tagging⚠️ Manual tagging

Workflow Comparison Matrix

Use Git Flow when:

  • You have scheduled releases
  • You need to support multiple versions
  • You require formal release processes
  • You have a large team with complex features

Use GitHub Flow when:

  • You deploy continuously
  • You have a single production version
  • You want simplicity
  • You’re building web applications

Use Trunk-Based Development when:

  • You deploy multiple times per day
  • You have excellent CI/CD
  • You use feature flags
  • You prioritize speed and integration

Choosing the Right Workflow

Selecting the right workflow depends on several factors:

Team Size

Small Teams (1-5 developers):

  • ✅ GitHub Flow or Trunk-Based Development
  • ❌ Git Flow (too much overhead)

Medium Teams (6-15 developers):

  • ✅ GitHub Flow or Git Flow
  • ⚠️ Trunk-Based Development (requires maturity)

Large Teams (15+ developers):

  • ✅ Git Flow or Trunk-Based Development
  • ⚠️ GitHub Flow (may need more structure)

Release Frequency

Multiple times per day:

  • ✅ Trunk-Based Development
  • ✅ GitHub Flow
  • ❌ Git Flow (too slow)

Daily releases:

  • ✅ GitHub Flow
  • ✅ Trunk-Based Development
  • ⚠️ Git Flow (possible but not ideal)

Weekly/Monthly releases:

  • ✅ Git Flow
  • ⚠️ GitHub Flow (works but no release management)
  • ❌ Trunk-Based Development (not designed for this)

Project Type

Web Applications (SaaS):

  • ✅ GitHub Flow
  • ✅ Trunk-Based Development
  • ⚠️ Git Flow (can work but may be overkill)

Mobile Applications:

  • ✅ Git Flow (app store releases)
  • ⚠️ GitHub Flow (possible but challenging)
  • ❌ Trunk-Based Development (not ideal for app stores)

Enterprise Software:

  • ✅ Git Flow
  • ⚠️ GitHub Flow (may need more structure)
  • ⚠️ Trunk-Based Development (requires maturity)

Decision Framework

Ask yourself these questions:

  1. How often do you release?

    • Multiple times/day → Trunk-Based Development or GitHub Flow
    • Daily → GitHub Flow
    • Weekly/Monthly → Git Flow
  2. Do you need formal release management?

    • Yes → Git Flow
    • No → GitHub Flow or Trunk-Based Development
  3. How stable does production need to be?

    • Very stable → Git Flow
    • Stable with good testing → GitHub Flow
    • Rapid iteration → Trunk-Based Development
  4. What’s your team’s experience level?

    • Beginners → GitHub Flow
    • Intermediate → Git Flow or GitHub Flow
    • Advanced → Any workflow

Implementing Each Workflow

Let’s see how to set up each workflow in practice.

Setting Up Git Flow

1. Initialize Git Flow

Terminal window
# Install git-flow (optional helper tool)
# macOS
brew install git-flow
# Linux
apt-get install git-flow
# Initialize git-flow in your repository
git flow init
# Follow prompts to set branch names:
# - Production branch: main
# - Development branch: develop
# - Feature branches: feature/
# - Release branches: release/
# - Hotfix branches: hotfix/

2. Create Feature Branch

Terminal window
# Using git-flow helper
git flow feature start user-authentication
# Or manually
git checkout develop
git pull origin develop
git checkout -b feature/user-authentication

3. Finish Feature

Terminal window
# Using git-flow helper
git flow feature finish user-authentication
# Or manually
git checkout develop
git merge --no-ff feature/user-authentication
git branch -d feature/user-authentication
git push origin develop

4. Create Release

Terminal window
# Using git-flow helper
git flow release start 1.2.0
# Make release preparations
# Update version, changelog, etc.
git flow release finish 1.2.0

Setting Up GitHub Flow

1. Configure Main Branch Protection

In GitHub (or your Git hosting platform):

  1. Go to repository settings
  2. Navigate to “Branches”
  3. Add branch protection rule for main:
    • ✅ Require pull request reviews
    • ✅ Require status checks to pass
    • ✅ Require branches to be up to date
    • ✅ Include administrators

2. Create Feature Branch

Terminal window
# Create branch from main
git checkout main
git pull origin main
git checkout -b feature/user-authentication
# Make changes and commit
git add .
git commit -m "feat: add user authentication"
git push origin feature/user-authentication

3. Create Pull Request

Terminal window
# On GitHub:
# 1. Click "New Pull Request"
# 2. Select feature branch
# 3. Add description and reviewers
# 4. Wait for CI/CD and reviews
# 5. Merge when approved

4. Deploy from Main

Terminal window
# After PR merge, deploy from main
git checkout main
git pull origin main
# Trigger deployment (via CI/CD or manually)

Setting Up Trunk-Based Development

1. Configure CI/CD

Ensure robust CI/CD pipeline:

# .github/workflows/ci.yml example
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: npm test
- name: Run linting
run: npm run lint
- name: Build
run: npm run build

2. Enable Feature Flags

Set up feature flag system:

lib/feature-flags.ts
type FeatureFlag = {
name: string;
enabled: boolean;
rolloutPercentage?: number;
};
const featureFlags: FeatureFlag[] = [
{ name: "NEW_AUTHENTICATION", enabled: false },
{ name: "PAYMENT_V2", enabled: true, rolloutPercentage: 50 },
];
export function isFeatureEnabled(flagName: string): boolean {
const flag = featureFlags.find((f) => f.name === flagName);
if (!flag) return false;
if (!flag.enabled) return false;
if (!flag.rolloutPercentage) return true;
// Percentage-based rollout
return Math.random() * 100 < flag.rolloutPercentage;
}

3. Commit Directly or Use Short Branches

Terminal window
# Small changes: commit directly
git checkout main
git pull origin main
git add .
git commit -m "feat: add input validation"
git push origin main
# Larger changes: short-lived branch
git checkout -b feature/user-auth
# Work and commit
git push origin feature/user-auth
# Create PR and merge same day

Best Practices for Git Workflows

Regardless of which workflow you choose, follow these best practices:

Commit Messages

Use Conventional Commits:

Terminal window
# Format: <type>(<scope>): <subject>
git commit -m "feat(auth): add user login form"
git commit -m "fix(payment): resolve credit card validation bug"
git commit -m "docs(readme): update installation instructions"
git commit -m "refactor(api): simplify error handling"

Common types: feat, fix, docs, style, refactor, test, chore

Branch Naming

Use Consistent Naming Conventions:

Terminal window
# Git Flow
feature/user-authentication
release/1.2.0
hotfix/critical-security-fix
# GitHub Flow
feature/user-authentication
bugfix/login-error
hotfix/payment-bug
# Trunk-Based Development
feature/user-auth # Short-lived
bugfix/login # Short-lived

Code Review

Require Code Reviews:

  • At least one approval before merge
  • Use pull requests for all changes (except direct commits in TBD)
  • Review for correctness, style, and best practices
  • Keep PRs small and focused

Testing

Automate Testing:

Terminal window
# Run tests before pushing
npm test
npm run lint
npm run type-check
# CI/CD should run tests automatically

Keep Branches Up to Date

Regularly Sync with Main:

Terminal window
# For feature branches
git checkout main
git pull origin main
git checkout feature/my-feature
git merge main
# Or use rebase
git rebase main

Use Tags for Releases

Tag Important Versions:

Terminal window
# Semantic versioning
git tag -a v1.2.0 -m "Release version 1.2.0"
git tag -a v1.2.1 -m "Hotfix: security patch"
git push origin --tags

Clean Up Branches

Delete Merged Branches:

Terminal window
# Delete local branches
git branch -d feature/merged-feature
# Delete remote branches
git push origin --delete feature/merged-feature
# Prune remote tracking branches
git fetch --prune

Common Pitfalls and How to Avoid Them

Pitfall 1: Long-Lived Feature Branches

Problem: Feature branches that exist for weeks or months

Terminal window
# Bad: Branch exists for 2 months
git checkout -b feature/major-refactor
# ... work for 2 months ...
git merge main # Many conflicts!

Solution: Keep branches short-lived

Terminal window
# Good: Break into smaller features
git checkout -b feature/auth-login
# ... work for 2-3 days ...
git merge main
git checkout -b feature/auth-logout
# ... work for 2-3 days ...
git merge main

Pitfall 2: Merging Broken Code to Main

Problem: Merging code that breaks the build

Terminal window
# Bad: Merge without testing
git checkout main
git merge feature/broken-feature
git push origin main
# Build breaks!

Solution: Use CI/CD and branch protection

Terminal window
# Good: CI/CD prevents broken merges
# 1. Create PR
# 2. CI/CD runs tests
# 3. Tests must pass before merge
# 4. Branch protection enforces this

Pitfall 3: Inconsistent Branch Naming

Problem: Inconsistent branch names make it hard to understand the codebase

Terminal window
# Bad: Inconsistent naming
feature/user-auth
bugfix/login-bug
new-feature-payment
hotfix-security

Solution: Establish and follow naming conventions

Terminal window
# Good: Consistent naming
feature/user-authentication
bugfix/login-error
feature/payment-integration
hotfix/security-patch

Pitfall 4: Not Syncing with Main

Problem: Feature branches become outdated

Terminal window
# Bad: Branch diverges from main
git checkout feature/my-feature
# ... work for a week without syncing ...
git merge main # Many conflicts!

Solution: Regularly sync with main

Terminal window
# Good: Sync regularly
git checkout feature/my-feature
git fetch origin
git merge origin/main
# Or use rebase
git rebase origin/main

Pitfall 5: Force Pushing to Shared Branches

Problem: Force pushing rewrites history

Terminal window
# Bad: Force push to shared branch
git push --force origin main
# Other developers' branches break!

Solution: Never force push to shared branches

Terminal window
# Good: Only force push to your own feature branches
git push --force origin feature/my-feature
# For shared branches, use regular push
git push origin main

Migration Strategies

If you’re currently using one workflow and want to migrate to another, here’s how to do it:

Migrating from Git Flow to GitHub Flow

Step 1: Merge develop into main

Terminal window
git checkout main
git merge develop
git push origin main

Step 2: Update CI/CD to deploy from main

Step 3: Create feature branches from main instead of develop

Terminal window
# Old way
git checkout develop
git checkout -b feature/new-feature
# New way
git checkout main
git checkout -b feature/new-feature

Step 4: Archive develop branch (optional)

Terminal window
git branch -m develop develop-archived
git push origin develop-archived

Migrating from GitHub Flow to Trunk-Based Development

Step 1: Set up feature flags system

Step 2: Enable branch protection on main with strict CI/CD requirements

Step 3: Start committing small changes directly to main

Terminal window
# Start with small, safe changes
git checkout main
git pull origin main
# Make small change
git commit -m "feat: add input validation"
git push origin main

Step 4: Gradually reduce feature branch lifetime

Step 5: Train team on feature flags and incremental development

Migrating from Any Workflow to Git Flow

Step 1: Create develop branch

Terminal window
git checkout main
git checkout -b develop
git push origin develop

Step 2: Update CI/CD to build from develop

Step 3: Start creating feature branches from develop

Terminal window
git checkout develop
git checkout -b feature/new-feature

Step 4: Establish release process

Step 5: Document workflow for team


Conclusion

Choosing the right Git workflow is crucial for your team’s productivity and code quality. Git Flow provides structure for scheduled releases, GitHub Flow offers simplicity for continuous deployment, and Trunk-Based Development enables rapid iteration for mature teams.

Each workflow has its place:

  • Git Flow excels when you need formal release management and support multiple versions
  • GitHub Flow shines for web applications with continuous deployment
  • Trunk-Based Development works best for teams with strong CI/CD and feature flag practices

The key is matching your workflow to your team’s needs, release frequency, and project requirements. Start with a simpler workflow and evolve as your needs change. Remember, no workflow is perfect—what matters is consistency, clear communication, and adapting to your team’s unique situation.

For more Git commands and advanced techniques, check out our Git cheatsheet. If you’re implementing CI/CD as part of your workflow, our guide on testing strategies can help ensure your automated tests catch issues before they reach production.

The best workflow is the one your team understands, follows consistently, and enables you to ship quality code quickly. Start with what works for your team today, and don’t be afraid to evolve as your needs change.