Git & GitHub Workflow Mastery: Branching, CI/CD & Team Collaboration
Rating
Verdict
Trunk-based development with feature flags is the most productive strategy for fast-moving teams. Combine with GitHub Actions for CI/CD, Conventional Commits for automated changelogs, and semantic-release for versioning.
Pros
- Git branching strategies prevent merge chaos
- GitHub Actions is incredibly powerful and free for open source
- Interactive rebase keeps history clean
- Protected branches enforce quality gates
- Semantic versioning + conventional commits enables automation
Cons
- Git rebase rewrites history — dangerous on shared branches
- Complex merge conflicts can be time-consuming
- CI/CD pipelines require upfront investment
Git & GitHub Workflow Mastery: Branching, CI/CD & Team Collaboration
Git mastery separates senior developers from junior ones. While everyone knows git commit and git push, truly effective teams use Git strategically — clean history, automated quality gates, and workflows that scale with team size. This guide covers everything from branching strategies to full CI/CD pipeline automation with GitHub Actions.
Branching Strategies
Gitflow
Gitflow uses long-lived branches: main, develop, feature branches, release branches, and hotfix branches. Best for software with scheduled releases:
# Feature development
git checkout develop
git checkout -b feature/user-authentication
# Work, commit, push
git add -A
git commit -m "feat(auth): add JWT authentication middleware"
# Merge back to develop
git checkout develop
git merge --no-ff feature/user-authentication
git branch -d feature/user-authentication
# Release
git checkout -b release/1.2.0
# Bump version, update changelog...
git checkout main
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release 1.2.0"
Trunk-Based Development (Recommended)
Trunk-based development keeps all work on main, with short-lived feature branches (max 1-2 days). Feature flags hide incomplete work:
# Short-lived feature branch
git checkout -b feat/search-filters
# Implement + test
git push origin feat/search-filters
# Open PR → merge same day
// Feature flag to hide incomplete work
import { useFeatureFlag } from '@/lib/flags';
export function SearchBar() {
const advancedFiltersEnabled = useFeatureFlag('advanced-search-filters');
return (
);
}
Conventional Commits
Conventional Commits provide a machine-readable commit format that enables automated changelogs and semantic versioning:
# Format:
Use commitlint to enforce this format:
npm install -D @commitlint/cli @commitlint/config-conventional husky
# commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
# Add husky hook
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
Interactive Rebase
Interactive rebase is your tool for cleaning up messy commit history before merging:
# Squash the last 4 commits into one
git rebase -i HEAD~4
# In the editor:
# pick a1b2c3d feat: start user auth
# squash e4f5g6h wip: add token validation
# squash h7i8j9k fix: typo in error message
# squash k1l2m3n feat: complete user auth
# Result: one clean commit instead of 4 messy ones
# Reorder commits
git rebase -i HEAD~3
# Move the second commit above the first in the editor
# Rebase feature branch onto latest main (avoid merge commits)
git fetch origin
git rebase origin/main
# Resolve conflicts, then:
git add .
git rebase --continue
GitHub Actions CI/CD
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
jobs:
quality:
name: Quality Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Type Check
run: npx tsc --noEmit
- name: Lint
run: npm run lint
- name: Unit Tests
run: npm test -- --coverage
env:
CI: true
- name: Upload Coverage
uses: codecov/codecov-action@v3
e2e:
name: E2E Tests
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: test_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
- run: npm run test:e2e
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t app:${{ github.sha }} .
- name: Run smoke tests
run: |
docker run -d -p 3000:3000 --name test-app app:${{ github.sha }}
sleep 5
curl -f http://localhost:3000/api/health || exit 1
docker stop test-app
- name: Deploy
run: |
echo "Deploy to production..."
# Your deployment command here
Protected Branches & Code Review
Configure branch protection rules in GitHub Settings → Branches:
- Require pull request reviews before merging (minimum 1 reviewer)
- Require status checks to pass (CI must be green)
- Require conversation resolution before merging
- Require signed commits (optional but recommended)
- Do not allow force pushes to
main
# .github/CODEOWNERS
# Global owners
* @org/core-team
# Frontend
/src/components/ @org/frontend-team
/src/app/ @org/frontend-team
# Infrastructure
/docker/ @org/devops-team
/.github/ @org/devops-team
/prisma/ @org/backend-team
Automated Releases with semantic-release
// .releaserc.json
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
["@semantic-release/github", {
"assets": [{"path": "dist/*.zip", "label": "Distribution"}]
}],
["@semantic-release/git", {
"assets": ["CHANGELOG.md", "package.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]"
}]
]
}
Productivity Git Aliases
# ~/.gitconfig
[alias]
lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
st = status -sb
undo = reset HEAD~1 --mixed
amend = commit --amend --no-edit
wip = !git add -A && git commit -m "wip: checkpoint"
undo-wip = reset HEAD~1 --soft
cleanup = "!git branch --merged main | grep -v main | xargs git branch -d"
pushf = push --force-with-lease # Safer force push
Conclusion
A well-designed Git workflow multiplies team productivity. Trunk-based development keeps everyone moving fast. Conventional commits enable automation. GitHub Actions provides a reliable quality gate. Protected branches prevent accidents. Together, these practices create a development velocity that's sustainable at scale.