- devops
- l2
- topic-pack
- git-workflows --- Portal | Level: L2: Operations | Topics: Git Workflows & Branching Strategies | Domain: DevOps & Tooling
Git Workflows & Branching Strategies — Primer¶
Why This Matters¶
Every team that uses Git eventually answers the same question: "How do we organize branches, releases, and collaboration?" The answer is a workflow — a set of conventions about when you branch, how you merge, and what triggers a deploy. Choose the wrong workflow and your team drowns in merge conflicts, broken builds, and release delays. Choose the right one and your CI/CD pipeline hums along while developers stay productive.
The existing git topic covers commands and fundamentals. The git-advanced topic covers internals, rebase mechanics, and recovery. This topic sits on top: it is the strategy layer — which workflow fits your team, why, and how to operate it day-to-day.
Etymology note: The concept of a "branching model" barely existed before distributed VCS. In SVN/CVS, branching was expensive and merging was painful, so teams avoided branches. Git made branching cheap (a branch is just a 41-byte pointer file), which unlocked the explosion of workflow strategies starting around 2008-2010.
Core Concepts¶
1. Trunk-Based Development (TBD)¶
Origin: Google and Facebook have practiced trunk-based development since the mid-2000s. Google's monorepo (with 86 TB of history as of 2015) runs on a single trunk. Facebook's mobile team moved to trunk-based in 2013 after GitFlow-style branching caused week-long merge freezes.
Mental model: Everyone commits to main (the trunk). Feature branches, if they exist, live less than one day. Long-lived branches are forbidden. Features that are not ready for users are hidden behind feature flags rather than kept on separate branches.
Key properties: - Branches live hours, not days - Feature flags decouple deploy from release - CI runs on every push to main - Deployments are continuous or near-continuous - Merge conflicts are rare because branches are short
When to use: - High CI/CD maturity (automated tests, fast builds) - Teams deploying multiple times per day - SaaS/web services where you control the runtime - Teams with strong test culture and feature flag infrastructure
When NOT to use: - You ship versioned software (desktop apps, SDKs, firmware) - Your CI pipeline takes >30 minutes (branches pile up) - You have compliance requirements that mandate release branches - Your team lacks automated testing discipline (broken trunk = everyone blocked)
Mnemonic: "TBD = Tiny Branch Duration." If your branch survives overnight, it is not trunk-based development.
2. GitHub Flow¶
Origin: Scott Chacon (GitHub co-founder) described GitHub Flow in 2011 as a deliberate simplification. GitHub's own team found GitFlow too heavy — they deployed 5-10 times per day and needed something lighter.
Mental model: There is one eternal branch: main. Every change starts as a feature branch off main, gets a pull request, passes CI, gets reviewed, and merges back to main. Deployments happen from main (or directly from the PR branch for preview environments).
Key properties:
- main is always deployable
- Feature branches are short-lived (hours to a few days)
- Pull requests are the integration gate
- CI runs on the PR branch before merge
- No release branches, no develop branch, no hotfix branches
When to use: - Teams of 2-20 developers - Continuous delivery (you deploy what is on main) - Web applications and SaaS products - Teams that want simplicity without the extreme discipline of TBD
When NOT to use: - You need to support multiple released versions simultaneously - You have a formal release/QA cycle that takes days - Your team lets feature branches live for weeks (this is GitHub Flow in name only)
Mnemonic: "One branch to rule them all." If you have more than
main+ short feature branches, you have outgrown GitHub Flow.
3. GitFlow¶
Origin: Vincent Driessen published "A Successful Git Branching Model" on January 5, 2010. It became the most-linked Git workflow article in history. In 2020, Driessen added a "note of reflection" to the post, acknowledging that GitFlow is not suitable for teams practicing continuous delivery — it was designed for software that ships named versions (1.0, 2.0, etc.).
Mental model: Two long-lived branches: main (production releases) and develop (integration). Feature branches come off develop. When you are ready to release, you cut a release/X.Y branch from develop, stabilize it, then merge it to both main (tagged) and back to develop. Hotfixes branch from main and merge to both main and develop.
main: ──v1.0─────────────v1.1──────────v1.2──
\ / /
release: \ ──R1.1── /
\ / \ /
develop: ──────A──B──C──D──E──F──G──H──I──J──
\ / \ /
feature: └──┘ └────┘
Key properties:
- main only receives merges from release and hotfix branches (every commit on main is a release)
- develop is the integration branch
- Feature branches come from and merge to develop
- Release branches stabilize the code (bugfixes only, no new features)
- Hotfix branches enable emergency patches to production
When to use: - Versioned software products (desktop apps, mobile apps, SDKs, libraries) - Teams that ship named releases on a schedule (quarterly, monthly) - Multiple versions supported simultaneously in production - Compliance environments requiring release artifacts and audit trails
When NOT to use: - SaaS teams deploying multiple times per day (way too much ceremony) - Small teams (<5 developers) — the overhead is not justified - Teams with high CI/CD maturity — GitFlow adds friction for little benefit
Mnemonic: "DFRHM" — Develop, Feature, Release, Hotfix, Main. Five branch types. If you cannot name all five, you are doing GitFlow wrong.
Memory aid: Think of GitFlow as a train schedule — releases depart on a schedule, features board during development, and hotfixes are emergency stops.
4. GitLab Flow¶
Origin: GitLab proposed this in 2014 as a middle ground between GitHub Flow (too simple for some) and GitFlow (too complex for most). It introduces environment branches that model your deployment pipeline.
Mental model: main is the development trunk. Downstream branches represent environments: staging, production, or version branches like 1.0-stable. Code flows one direction: main -> staging -> production. You never commit directly to environment branches — you merge from upstream.
Key properties: - Environment branches model the promotion pipeline - Merges flow downstream only (main -> staging -> production) - Each environment branch is always deployable to its environment - Cherry-picks are discouraged; full merges are preferred - Issue tracking integration (branches link to issues)
When to use: - Teams with distinct staging/production environments - Organizations that need a gated promotion model - Teams that want GitHub Flow simplicity but with environment awareness - Projects using GitLab CI/CD with environment-specific deploy jobs
When NOT to use: - You have a single deploy target (just use GitHub Flow) - You need to support multiple released versions (use release branching) - Your promotion model is more complex than linear (e.g., regional deploys)
Mnemonic: "Water flows downhill." Code always merges downstream through environments — never upstream.
5. Release Branching¶
Origin: This is the oldest pattern, predating Git itself. Linux kernel development used release branches (2.6.x stable series). It is still the standard for projects that must support multiple released versions: databases, operating systems, frameworks, SDKs.
Mental model: Development happens on main. When a release is ready, you cut a release/X.Y branch. That branch receives only bugfixes and security patches (cherry-picked or targeted). main continues forward development. Multiple release branches can coexist.
main: ──A──B──C──D──E──F──G──H──I──
\ \
release/1.0: └──B'──P1──P2──P3 (maintenance)
\
release/2.0: └──E'──P4 (maintenance)
Key properties: - Multiple release branches coexist, each receiving patches - Cherry-picks carry fixes from main to release branches (or vice versa) - Each release branch has its own CI pipeline and artifact build - Version tags mark specific releases (v1.0.0, v1.0.1, v1.0.2) - EOL (end-of-life) dates govern when branches stop receiving patches
When to use: - Multiple versions in the field simultaneously (PostgreSQL 14/15/16/17) - Enterprise software with LTS commitments - Open-source libraries with semver and backport policies - Any project where users cannot be forced to upgrade
When NOT to use: - SaaS products (you only run one version) - Small projects with a single deploy target - Teams without the discipline to cherry-pick consistently
Mnemonic: "Branch per promise." Every release branch is a promise to your users: "we will patch this version until date X."
Decision Framework¶
Choosing a workflow is not a technical decision — it is an organizational one. The right workflow depends on how your team works, not which workflow has the coolest diagram.
The Four Questions¶
| Question | If the answer is... | Consider... |
|---|---|---|
| How often do you deploy? | Multiple times/day | TBD or GitHub Flow |
| Weekly/biweekly | GitHub Flow or GitLab Flow | |
| Monthly/quarterly | GitFlow or Release Branching | |
| How many versions do you support? | One (SaaS) | TBD, GitHub Flow, or GitLab Flow |
| 2-3 concurrent | Release Branching | |
| Many (LTS model) | Release Branching | |
| What is your CI/CD maturity? | Full automation, fast builds | TBD |
| Automated tests, moderate builds | GitHub Flow | |
| Manual QA stages | GitFlow or GitLab Flow | |
| What is your team size? | 1-5 | TBD or GitHub Flow |
| 5-20 | GitHub Flow or GitLab Flow | |
| 20+ with sub-teams | GitLab Flow or GitFlow |
Quick Decision Tree¶
Do you ship versioned software with LTS support?
├── YES → Release Branching (possibly with GitFlow for the development phase)
└── NO → Do you deploy multiple times per day?
├── YES → Do you have feature flags and fast CI?
│ ├── YES → Trunk-Based Development
│ └── NO → GitHub Flow
└── NO → Do you have staging/production promotion gates?
├── YES → GitLab Flow
└── NO → GitHub Flow
Mnemonic: "FAST" — Frequency, Active versions, Stage gates, Team size. Walk through these four factors and the workflow picks itself.
Merge vs Rebase vs Squash¶
Every workflow requires merging code. The how matters as much as the when.
Merge Commit (git merge --no-ff)¶
Creates a merge commit that preserves the complete branch history.
Pros:
- Full history preserved — every individual commit is visible
- git bisect works perfectly across the branch
- Merge commits serve as documentation ("Feature X was integrated here")
- Easy to revert an entire feature: git revert -m 1 <merge-SHA>
Cons:
- History can become noisy with many small branches
- git log --oneline on main shows merge commits between feature commits
- GitFlow's develop branch becomes particularly hard to read
Best for: GitFlow, Release Branching, teams that value audit trails.
Rebase (git rebase main then fast-forward merge)¶
Replays feature commits on top of main, creating a linear history.
Pros:
- Clean, linear history
- git log --oneline reads like a story
- No merge commits cluttering the log
- git bisect works on a single line
Cons: - Rewrites commit SHAs (C becomes C') — dangerous on shared branches - Requires force-push if the branch was already pushed - Conflict resolution happens per-commit during rebase (can be tedious) - Loses the "this was a branch" information
Best for: Trunk-based development, small teams, personal branches before merging.
Squash Merge (git merge --squash)¶
Combines all feature branch commits into a single commit on main.
Pros:
- Extremely clean history — one commit per feature/PR
- Easy to revert a feature: git revert <squash-SHA>
- Hides messy "WIP" and "fix typo" commits from main
Cons:
- Loses individual commit granularity — git bisect cannot find which part of a feature caused a bug
- Large features become one enormous diff
- Original commit messages and authorship are lost (only the squash message remains)
- The feature branch cannot be deleted safely until you verify the squash
Best for: GitHub Flow with strict PR hygiene, teams that want one-commit-per-PR on main.
Comparison Table¶
| Factor | Merge | Rebase | Squash |
|---|---|---|---|
| History readability | Noisy but complete | Clean and linear | Minimal and clean |
git bisect granularity |
Full | Full | Per-PR only |
| Feature revert ease | revert -m 1 |
Revert each commit | revert <SHA> |
| Shared branch safety | Safe | Dangerous | Safe |
| Commit authorship | Preserved | Preserved | Lost (squash author only) |
git blame detail |
Per-commit | Per-commit | Per-PR |
Mnemonic: "MRS" — Merge (complete history), Rebase (linear beauty), Squash (one commit per story). Each sacrifices something the others preserve.
CI/CD Mapping¶
Each workflow implies a different CI/CD pipeline design.
Trunk-Based Development CI/CD¶
- CI runs on every push to main (the only branch that matters)
- Deployments are automatic — if tests pass, code ships
- Feature flags are the release mechanism, not branches
- Rollback = turn off the flag (or deploy previous commit)
GitHub Flow CI/CD¶
Push to feature branch → Run tests on PR → Review + Approve
→ Merge to main → Run tests → Deploy to production
- CI runs on PR branches (catch failures before merge)
- Main is always deployable — deploy on merge or on schedule
- Preview environments per PR are a common enhancement
- Rollback = revert the merge commit
GitFlow CI/CD¶
Push to develop → Run tests → Nightly build
Cut release/X.Y → Run full QA suite → Fix bugs on release branch
Merge to main → Tag vX.Y.Z → Build release artifacts → Deploy
- CI runs on develop (integration tests), release branches (QA), and main (release builds)
- Deploy artifacts come from tagged commits on main
- Hotfix branches trigger emergency CI + deploy pipeline
- Multiple CI pipeline definitions for different branch patterns
GitLab Flow CI/CD¶
Push to main → Run tests → Merge to staging → Deploy to staging
→ Verify in staging → Merge to production → Deploy to production
- CI runs on main (unit tests) and environment branches (integration/deployment tests)
- Each environment branch triggers deployment to its target
- Promotion is a merge, not a deploy command
- Rollback = revert the merge on the environment branch
Release Branching CI/CD¶
Push to main → Run tests → Continue development
Cut release/X.Y → Run release QA → Tag vX.Y.0 → Build artifacts
Cherry-pick fix → Tag vX.Y.1 → Build patch artifacts
- Each release branch has its own CI configuration (may target different OS versions, SDKs)
- Artifact builds are triggered by version tags
- Backport CI validates cherry-picks on older branches
- Matrix builds for multiple supported versions
Etymology & History¶
Understanding where these workflows came from helps you understand their design assumptions.
| Workflow | Year | Creator | Context |
|---|---|---|---|
| Release Branching | ~2000 | Linux kernel community | Supporting multiple kernel series (2.4.x, 2.6.x) |
| GitFlow | 2010 | Vincent Driessen | Blog post aimed at teams shipping versioned software |
| GitHub Flow | 2011 | Scott Chacon (GitHub) | Reaction to GitFlow's complexity for web apps |
| GitLab Flow | 2014 | GitLab team | Middle ground for teams with environment promotion |
| Trunk-Based Dev | ~2005 (formalized ~2017) | Google, Facebook, Paul Hammant | Scaled engineering at Google; Hammant's trunkbaseddevelopment.com |
Driessen's 2010 blog post ("A Successful Git Branching Model") was the watershed moment. Before it, most teams either committed to trunk or invented ad-hoc branching. Driessen gave teams a vocabulary: develop, feature, release, hotfix. The model spread virally — and was adopted by many teams for whom it was a poor fit (SaaS teams shipping daily do not need release branches).
Google's monorepo and trunk-based development showed that even at massive scale (25,000+ engineers), a single trunk works — if you have the tooling (Piper, CitC, TAP) and cultural discipline. Most teams do not have Google-scale tooling, which is why GitHub Flow is the most commonly adopted workflow in practice.
The 2020 reckoning: Driessen updated his original blog post with a note:
"If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team."
This was significant — the creator of GitFlow explicitly said it is not for everyone.
Quick Reference¶
Workflow Cheatsheet¶
| Workflow | Long-lived branches | Feature branches | Release mechanism | Best for |
|---|---|---|---|---|
| Trunk-Based | main only |
Hours (<1 day) | Feature flags + continuous deploy | SaaS, high CI/CD maturity |
| GitHub Flow | main only |
Days (1-3) | Deploy from main | Most web teams |
| GitFlow | main + develop |
Days to weeks | Release branches + tags | Versioned software |
| GitLab Flow | main + env branches |
Days (1-3) | Merge to env branch | Teams with staging gates |
| Release Branching | main + release/* |
Varies | Cherry-pick to release branch | Multi-version support |
Merge Strategy Quick Pick¶
| Your situation | Use |
|---|---|
| You want full history and easy feature reverts | Merge commits |
| You want clean linear history on main | Rebase |
| You want one commit per PR on main | Squash merge |
| You are on a shared branch others have pulled | Never rebase |
You need git bisect granularity inside a feature |
Do NOT squash |
Memory Aids Summary¶
- TBD = Tiny Branch Duration — branches die in hours
- GitHub Flow = "One branch to rule them all" — just main + feature branches
- GitFlow = DFRHM — Develop, Feature, Release, Hotfix, Main
- GitLab Flow = "Water flows downhill" — always merge downstream
- Release Branching = "Branch per promise" — each branch is a support commitment
- FAST decision framework — Frequency, Active versions, Stage gates, Team size
- MRS merge strategy — Merge (complete), Rebase (linear), Squash (minimal)
Wiki Navigation¶
Prerequisites¶
- Git for DevOps (Topic Pack, L0)
Related Content¶
- Git Workflows Flashcards (CLI) (flashcard_deck, L1) — Git Workflows & Branching Strategies