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
- Understanding Git Workflows
- Git Flow: Feature Branch Workflow
- GitHub Flow: Simplified Continuous Deployment
- Trunk-Based Development: Continuous Integration
- Comparing the Three Workflows
- Choosing the Right Workflow
- Implementing Each Workflow
- Best Practices for Git Workflows
- Common Pitfalls and How to Avoid Them
- Migration Strategies
- Conclusion
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-fixMain Branches:
main(ormaster): Contains production-ready codedevelop: Integration branch for features
Supporting Branches:
feature/*: New featuresrelease/*: Preparing releaseshotfix/*: Urgent production fixes
How Git Flow Works
1. Feature Development
Features are developed in branches off develop:
# Start a new featuregit checkout developgit pull origin developgit checkout -b feature/user-authentication
# Work on the featuregit add .git commit -m "feat: add user login form"git commit -m "feat: implement authentication logic"
# Finish the featuregit checkout developgit pull origin developgit merge --no-ff feature/user-authenticationgit branch -d feature/user-authenticationgit 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:
# Create release branchgit checkout developgit checkout -b release/1.2.0
# Update version numbers, changelog, etc.# Fix bugs found during release testinggit commit -m "chore: bump version to 1.2.0"git commit -m "fix: resolve release blocker bug"
# Finish releasegit checkout maingit merge --no-ff release/1.2.0git tag -a v1.2.0 -m "Release version 1.2.0"git checkout developgit merge --no-ff release/1.2.0git branch -d release/1.2.0git push origin main --tagsgit push origin develop3. Hotfixes
Hotfixes branch directly from main for urgent production fixes:
# Create hotfix branchgit checkout maingit checkout -b hotfix/critical-security-fix
# Fix the issuegit commit -m "fix: patch critical security vulnerability"
# Finish hotfixgit checkout maingit merge --no-ff hotfix/critical-security-fixgit tag -a v1.1.1 -m "Hotfix version 1.1.1"git checkout developgit merge --no-ff hotfix/critical-security-fixgit branch -d hotfix/critical-security-fixgit push origin main --tagsgit push origin developAdvantages 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-bugMain Branch:
main: Always deployable, contains production-ready code
Feature Branches:
- Created from
mainfor any change - Merged back to
mainvia pull request - Deployed immediately after merge
How GitHub Flow Works
1. Feature Development
All changes start as feature branches:
# Create feature branchgit checkout maingit pull origin maingit checkout -b feature/user-authentication
# Make changesgit add .git commit -m "feat: add user login form"git commit -m "feat: implement authentication logic"
# Push and create pull requestgit push origin feature/user-authentication2. Pull Request Process
Create a pull request on GitHub (or your Git hosting platform):
# 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:
# After PR is mergedgit checkout maingit pull origin main# Code is ready to deploy!3. Deployment
Deployments happen directly from main:
# Deploy from main (automated via CI/CD)# No release branches neededgit checkout maingit pull origin main# Deploy to productionAdvantages 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
maincan 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(ortrunk): 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:
# Small, incremental changesgit checkout maingit pull origin main
# Make small changegit add .git commit -m "feat: add input validation"git push origin main
# CI/CD runs tests automatically# If tests pass, code is deployed2. Short-Lived Feature Branches
For larger features, use very short-lived branches:
# Create feature branchgit checkout maingit pull origin maingit 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 days3. Feature Flags
Use feature flags to hide incomplete features:
// Feature flag exampleconst 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:
| Aspect | Git Flow | GitHub Flow | Trunk-Based Development |
|---|---|---|---|
| Complexity | High | Low | Very Low |
| Release Frequency | Scheduled (weeks/months) | Continuous (daily) | Continuous (multiple/day) |
| Branch Lifetime | Long (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 Size | Any size | Small to medium | Any size (requires maturity) |
| Learning Curve | Steep | Gentle | Moderate |
| Merge Conflicts | Common | Moderate | Rare |
| Deployment Speed | Slow | Fast | Very Fast |
| Code Stability | High | Medium | Requires 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:
-
How often do you release?
- Multiple times/day → Trunk-Based Development or GitHub Flow
- Daily → GitHub Flow
- Weekly/Monthly → Git Flow
-
Do you need formal release management?
- Yes → Git Flow
- No → GitHub Flow or Trunk-Based Development
-
How stable does production need to be?
- Very stable → Git Flow
- Stable with good testing → GitHub Flow
- Rapid iteration → Trunk-Based Development
-
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
# Install git-flow (optional helper tool)# macOSbrew install git-flow
# Linuxapt-get install git-flow
# Initialize git-flow in your repositorygit 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
# Using git-flow helpergit flow feature start user-authentication
# Or manuallygit checkout developgit pull origin developgit checkout -b feature/user-authentication3. Finish Feature
# Using git-flow helpergit flow feature finish user-authentication
# Or manuallygit checkout developgit merge --no-ff feature/user-authenticationgit branch -d feature/user-authenticationgit push origin develop4. Create Release
# Using git-flow helpergit flow release start 1.2.0
# Make release preparations# Update version, changelog, etc.
git flow release finish 1.2.0Setting Up GitHub Flow
1. Configure Main Branch Protection
In GitHub (or your Git hosting platform):
- Go to repository settings
- Navigate to “Branches”
- 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
# Create branch from maingit checkout maingit pull origin maingit checkout -b feature/user-authentication
# Make changes and commitgit add .git commit -m "feat: add user authentication"git push origin feature/user-authentication3. Create Pull Request
# 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 approved4. Deploy from Main
# After PR merge, deploy from maingit checkout maingit 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 examplename: CIon: 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 build2. Enable Feature Flags
Set up feature flag system:
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
# Small changes: commit directlygit checkout maingit pull origin maingit add .git commit -m "feat: add input validation"git push origin main
# Larger changes: short-lived branchgit checkout -b feature/user-auth# Work and commitgit push origin feature/user-auth# Create PR and merge same dayBest Practices for Git Workflows
Regardless of which workflow you choose, follow these best practices:
Commit Messages
✅ Use Conventional Commits:
# 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:
# Git Flowfeature/user-authenticationrelease/1.2.0hotfix/critical-security-fix
# GitHub Flowfeature/user-authenticationbugfix/login-errorhotfix/payment-bug
# Trunk-Based Developmentfeature/user-auth # Short-livedbugfix/login # Short-livedCode 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:
# Run tests before pushingnpm testnpm run lintnpm run type-check
# CI/CD should run tests automaticallyKeep Branches Up to Date
✅ Regularly Sync with Main:
# For feature branchesgit checkout maingit pull origin maingit checkout feature/my-featuregit merge main# Or use rebasegit rebase mainUse Tags for Releases
✅ Tag Important Versions:
# Semantic versioninggit tag -a v1.2.0 -m "Release version 1.2.0"git tag -a v1.2.1 -m "Hotfix: security patch"git push origin --tagsClean Up Branches
✅ Delete Merged Branches:
# Delete local branchesgit branch -d feature/merged-feature
# Delete remote branchesgit push origin --delete feature/merged-feature
# Prune remote tracking branchesgit fetch --pruneCommon Pitfalls and How to Avoid Them
Pitfall 1: Long-Lived Feature Branches
❌ Problem: Feature branches that exist for weeks or months
# Bad: Branch exists for 2 monthsgit checkout -b feature/major-refactor# ... work for 2 months ...git merge main # Many conflicts!✅ Solution: Keep branches short-lived
# Good: Break into smaller featuresgit 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 mainPitfall 2: Merging Broken Code to Main
❌ Problem: Merging code that breaks the build
# Bad: Merge without testinggit checkout maingit merge feature/broken-featuregit push origin main# Build breaks!✅ Solution: Use CI/CD and branch protection
# Good: CI/CD prevents broken merges# 1. Create PR# 2. CI/CD runs tests# 3. Tests must pass before merge# 4. Branch protection enforces thisPitfall 3: Inconsistent Branch Naming
❌ Problem: Inconsistent branch names make it hard to understand the codebase
# Bad: Inconsistent namingfeature/user-authbugfix/login-bugnew-feature-paymenthotfix-security✅ Solution: Establish and follow naming conventions
# Good: Consistent namingfeature/user-authenticationbugfix/login-errorfeature/payment-integrationhotfix/security-patchPitfall 4: Not Syncing with Main
❌ Problem: Feature branches become outdated
# Bad: Branch diverges from maingit checkout feature/my-feature# ... work for a week without syncing ...git merge main # Many conflicts!✅ Solution: Regularly sync with main
# Good: Sync regularlygit checkout feature/my-featuregit fetch origingit merge origin/main# Or use rebasegit rebase origin/mainPitfall 5: Force Pushing to Shared Branches
❌ Problem: Force pushing rewrites history
# Bad: Force push to shared branchgit push --force origin main# Other developers' branches break!✅ Solution: Never force push to shared branches
# Good: Only force push to your own feature branchesgit push --force origin feature/my-feature
# For shared branches, use regular pushgit push origin mainMigration 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
git checkout maingit merge developgit push origin mainStep 2: Update CI/CD to deploy from main
Step 3: Create feature branches from main instead of develop
# Old waygit checkout developgit checkout -b feature/new-feature
# New waygit checkout maingit checkout -b feature/new-featureStep 4: Archive develop branch (optional)
git branch -m develop develop-archivedgit push origin develop-archivedMigrating 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
# Start with small, safe changesgit checkout maingit pull origin main# Make small changegit commit -m "feat: add input validation"git push origin mainStep 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
git checkout maingit checkout -b developgit push origin developStep 2: Update CI/CD to build from develop
Step 3: Start creating feature branches from develop
git checkout developgit checkout -b feature/new-featureStep 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.