GitHub Flow
GitHub Flow is the trunk-based workflow most teams use today: short-lived feature branches off main, PRs with automated checks, squash-merge to main, deploy frequently. It is simpler than GitFlow, fits CI/CD, and pairs naturally with conventional commits + release-please.
A working GitHub Flow with branch protection
EXAMPLE
# ===== 1) The flow =====
# 1. main is ALWAYS deployable
# 2. Create a short-lived branch off main for each unit of work
# 3. Open a PR early; let CI run
# 4. Review + tests pass
# 5. Squash-merge to main
# 6. Deploy (continuous or after main is tagged)
# 7. Delete the branch
#
# No 'develop' branch. No long-lived 'release/*'. Hotfixes are normal PRs.
# ===== 2) Create the branch =====
git switch main
git pull --rebase
git switch -c feat/cart-quantity-stepper
# Branch naming convention
# feat/<short-name>
# fix/<short-name>
# chore/<short-name>
# refactor/<short-name>
# docs/<short-name>
# ===== 3) Make small commits =====
git add -p # stage hunks deliberately
git commit -m 'feat: add Stepper component'
# Repeat. Keep commits intentional; squash-merge will collapse them into one
# atomic message in main, but they're useful while you work.
# ===== 4) Push + open PR =====
git push -u origin feat/cart-quantity-stepper
gh pr create --fill --base main
# OR open in the GitHub UI
# ===== 5) Keep up with main while the PR is open =====
git fetch
git rebase origin/main # replay your branch on top of latest main
# If rebase has conflicts:
# resolve, git add path, git rebase --continue
# git push --force-with-lease (your branch only, never main)
# ===== 6) Squash merge =====
# Click 'Squash and merge' in GitHub.
# Edit the resulting commit subject to use Conventional Commits:
# feat: cart quantity stepper
# Use the PR description as the body.
#
# This keeps main linear and lets release-please / changesets generate the
# changelog automatically.
# ===== 7) Delete the branch + sync =====
git switch main
git pull --rebase
git branch -d feat/cart-quantity-stepper # local
git push origin --delete feat/cart-quantity-stepper # remote (GitHub auto-deletes if enabled)
# ===== 8) Branch protection rules — set in repo settings =====
# - Require pull request before merging
# - Require approvals (>= 1)
# - Require status checks: tests, lint, typecheck, security scan
# - Require branches to be up to date before merging
# - Require linear history (forces squash or rebase merge)
# - Restrict pushes to main (no direct commits)
# - Block force-pushes
# - Auto-delete head branches on merge
# ===== 9) Pre-commit hooks (Husky example) =====
# package.json
# {
# "scripts": { "prepare": "husky" }
# }
# .husky/pre-commit:
# npx lint-staged
# .husky/commit-msg:
# npx --no -- commitlint --edit $1
#
# Catches lint/format issues + bad commit messages BEFORE pushing.
# ===== 10) When NOT to use GitHub Flow =====
# - You ship rare, big releases (e.g. on-premise software with quarterly releases)
# -> GitFlow's release branches help with parallel maintenance
# - Mobile apps with store review gating
# -> A 'release' branch may be needed for the duration of the review
# - Open source with many in-flight long-running forks
# -> GitHub Flow still works; you just gain rebase practice
# ===== 11) Decision tree =====
# - Many small changes / day, CD to prod -> GitHub Flow + squash
# - Multiple parallel maintained versions -> GitFlow OR trunk + release tags
# - Tight regulatory gates -> trunk + release branches + signoff PRs
# - Two-week sprints with a release at the end -> GitHub Flow + a soft 'release readiness' tag
# ===== 12) Pitfalls =====
# - Long-running branches that diverge from main -> merge hell
# Fix: rebase daily; ship small.
# - Letting hotfixes become a 'master/hotfix' branch family -> just use main
# - Force-pushing to main (NEVER)
# - PRs with 20 commits and unclear scope -> reviewers cannot focus
# - Skipping branch protection 'just this once' -> the rule becomes optional
# ===== 13) Quick aliases that pay off =====
git config --global alias.sw 'switch'
git config --global alias.co 'checkout'
git config --global alias.last 'log -1 HEAD --stat'
git config --global alias.unstage 'reset HEAD --'
git config --global alias.lola "log --graph --decorate --oneline --all"
git config --global pull.rebase true
git config --global push.autoSetupRemote true
git config --global init.defaultBranch main
git config --global rerere.enabled true
Why it matters
Short branches + squash-merge + branch protection + auto-delete + rebase-on-pull is the combination that makes GitHub Flow work in practice. Each is a small config flip; together they keep main clean, prevent accidents, and turn "we ship" into the boring outcome of every PR — exactly what you want.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
# main is always deployable. # feature branch -> PR -> review -> merge -> deploy.Try it Yourself »
Discussion
Loading…