trunk-based-git
Skill from retex73/music-app
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- Updated: 2025-11-22
Download the latest version (v1.0.0) from this page.
SKILL.md
# Trunk-Based Git Skill
## Purpose
Master trunk-based git workflow patterns, branch management strategies, and merge practices for continuous integration and deployment.
## Scope
- Trunk-based development workflow
- Branch management (≤3 active branches)
- Daily merge requirements
- Small, frequent commits
- Main branch protection
- Merge strategies
- Git commands for trunk-based workflow
## Core Principles
### Principle 1: Main Branch is Sacred
- Always deployment-ready ("green")
- All tests pass at all times
- No direct commits allowed
- Protected by automation
### Principle 2: Short-Lived Branches
- Maximum lifespan: 24 hours
- Maximum active branches: 3
- Delete immediately after merge
- Named clearly and consistently
### Principle 3: Small, Frequent Integration
- Merge to main at least daily
- Small commits (< 200 lines)
- Continuous integration mindset
- Reduce merge conflicts
## Branch Management
### Creating Branches
```bash
# Check current branch count first
BRANCH_COUNT=$(git branch | grep -v "main" | wc -l)
if [ $BRANCH_COUNT -ge 3 ]; then
echo "❌ Max branches reached. Merge or delete first."
exit 1
fi
# Create properly named branch
git checkout main
git pull origin main
git checkout -b feature/add-playlist
# Naming conventions:
# feature/ - New features
# fix/ - Bug fixes
# hotfix/ - Critical production fixes
# refactor/ - Code refactoring
```
### Checking Branch Status
```bash
# List all branches with ages
git for-each-ref --sort=-committerdate refs/heads/ \
--format='%(refname:short) | %(committerdate:relative) | %(authorname)'
# Count active branches
git branch | grep -v "main" | wc -l
# Find stale branches (>24h)
git for-each-ref --sort=-committerdate refs/heads/ \
--format='%(refname:short) %(committerdate:relative)' | \
grep -E '[0-9]+ (day|week|month)s? ago'
# Check if branch is behind main
git fetch origin main
git log HEAD..origin/main --oneline
```
### Daily Merge Workflow
```bash
# 1. Update main branch
git checkout main
git pull origin main
# 2. Update your feature branch
git checkout feature/add-playlist
git rebase main # Or merge main into feature branch
# 3. Resolve any conflicts
# Edit conflicted files
git add .
git rebase --continue # If rebasing
# or
git commit # If merging
# 4. Run tests
npm test
# 5. Push changes
git push origin feature/add-playlist
# 6. Create/update PR
gh pr create --title "Add playlist feature" --body "..."
# 7. After approval, merge
git checkout main
git pull origin main
git merge --no-ff feature/add-playlist # Or use GitHub merge button
git push origin main
# 8. Delete branch immediately
git branch -d feature/add-playlist
git push origin --delete feature/add-playlist
```
### Branch Cleanup
```bash
# Delete merged local branches
git branch --merged main | grep -v "main" | xargs git branch -d
# Delete merged remote branches
git remote prune origin
# Force delete unmerged branch (use with caution!)
git branch -D feature/abandoned
git push origin --delete feature/abandoned
```
## Commit Practices
### Small, Frequent Commits
```bash
# Good: Multiple small commits
git add src/services/playlistService.js
git commit -m "feat: add playlist data model"
git add src/components/PlaylistCard.jsx
git commit -m "feat: add playlist card component"
git add src/pages/PlaylistsPage.jsx
git commit -m "feat: add playlists page"
# Bad: One massive commit
git add .
git commit -m "add playlist feature" # 500+ lines changed
```
### Commit Message Standards
```bash
# Format: <type>: <description>
#
# Types:
# feat: New feature
# fix: Bug fix
# refactor: Code refactoring
# test: Adding tests
# docs: Documentation
# style: Formatting
# perf: Performance
# chore: Build/dependencies
# Good examples:
git commit -m "feat: add playlist creation modal"
git commit -m "fix: resolve audio player crash on iOS Safari"
git commit -m "refactor: extract tune search to custom hook"
git commit -m "test: add unit tests for favorites service"
git commit -m "perf: memoize expensive filter operation"
# Bad examples:
git commit -m "WIP" # Not descriptive
git commit -m "fix stuff" # Vague
git commit -m "updates" # No context
git commit -m "final version" # Not helpful
```
### Checking Commit Size
```bash
# Check size of last commit
git diff --stat HEAD~1
# Check size of all commits in branch
git diff --stat main..HEAD
# Lines changed in last commit
git show --stat HEAD
# If commit is too large, split it:
git reset HEAD~1 # Undo last commit, keep changes
# Then make multiple smaller commits
```
### Squashing Commits
```bash
# Interactive rebase to squash WIP commits
git rebase -i main
# In editor, mark commits to squash:
pick abc1234 feat: add playlist model
squash def5678 WIP
squash ghi9012 WIP
squash jkl3456 fix typo
# Result: One clean commit
# "feat: add playlist model"
# Force push (your branch only!)
git push --force-with-lease origin feature/add-playlist
```
## Merge Strategies
### Fast-Forward Merge (Simple Cases)
```bash
# When no conflicts and linear history
git checkout main
git merge feature/simple-fix
# Results in clean, linear history
```
### No-Fast-Forward Merge (Preserves Context)
```bash
# Preserves branch context with merge commit
git checkout main
git merge --no-ff feature/add-playlist
# Creates merge commit even if fast-forward possible
# Good for:
- Important features
- Multiple related commits
- Preserving PR context
```
### Squash Merge (Clean History)
```bash
# Squashes all commits into one
git checkout main
git merge --squash feature/add-playlist
git commit -m "feat: add playlist feature"
# Good for:
- Many WIP commits
- Clean main history
- Simple feature additions
# Bad for:
- Losing commit context
- Complex features needing history
```
### Rebase Before Merge
```bash
# Update feature branch with main changes
git checkout feature/add-playlist
git fetch origin main
git rebase origin/main
# Resolve conflicts
# Edit files
git add .
git rebase --continue
# Force push
git push --force-with-lease origin feature/add-playlist
# Then merge (will be fast-forward)
git checkout main
git merge feature/add-playlist
```
## Main Branch Protection
### Pre-Merge Checks
```bash
#!/bin/bash
# pre-merge-check.sh
echo "🔍 Running pre-merge checks..."
# 1. Ensure on feature branch
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" == "main" ]; then
echo "❌ Cannot run from main branch"
exit 1
fi
# 2. Update main
git fetch origin main
# 3. Check if behind main
BEHIND=$(git rev-list --count HEAD..origin/main)
if [ $BEHIND -gt 0 ]; then
echo "❌ Branch is $BEHIND commits behind main. Rebase first."
exit 1
fi
# 4. Run tests
echo "Running tests..."
npm test || {
echo "❌ Tests failed"
exit 1
}
# 5. Run build
echo "Building..."
npm run build || {
echo "❌ Build failed"
exit 1
}
# 6. Run linter
echo "Linting..."
npm run lint || {
echo "⚠️ Lint errors found"
exit 1
}
# 7. Check PR size
LINES=$(git diff --stat origin/main..HEAD | tail -1 | awk '{print $4}')
if [ "$LINES" -gt 200 ]; then
echo "⚠️ PR is large ($LINES lines). Consider splitting."
fi
echo "✅ All pre-merge checks passed!"
echo "Ready to create/update PR"
```
### Post-Merge Verification
```bash
#!/bin/bash
# post-merge-verify.sh
echo "🔍 Verifying main branch health..."
# 1. Switch to main
git checkout main
git pull origin main
# 2. Run tests
npm test || {
echo "🚨 MAIN BRANCH BROKEN! Tests failing!"
echo "Immediate action required:"
echo "1. Identify breaking commit: git log -1"
echo "2. Revert: git revert HEAD"
echo "3. Push: git push origin main"
exit 1
}
# 3. Run build
npm run build || {
echo "🚨 MAIN BRANCH BROKEN! Build failing!"
exit 1
}
echo "✅ Main branch healthy"
```
## GitHub/GitLab Integration
### Creating Pull Requests
```bash
# Using GitHub CLI
gh pr create \
--title "feat: add playlist feature" \
--body "Implements playlist creation and management
- Adds playlist data model
- Creates playlist UI components
- Adds tests
Closes #123" \
--base main \
--head feature/add-playlist
# Check PR status
gh pr status
# List PRs
gh pr list
# Merge PR
gh pr merge --squash --delete-branch
```
### Branch Protection Rules
```yaml
# GitHub branch protection for 'main':
- Require pull request reviews (1 minimum)
- Require status checks to pass:
- tests
- build
- lint
- Require branches to be up to date before merge
- No force pushes
- No deletions
- Require linear history (optional)
```
## Common Scenarios
### Scenario 1: Working on Multiple Small Features
```bash
# DON'T create multiple branches
# DO use feature flags
# Create one branch
git checkout -b feature/playlists
# Add feature A with flag
# Commit and merge daily
# Add feature B with flag
# Commit and merge daily
# Enable flags when features complete
```
### Scenario 2: Urgent Hotfix Needed
```bash
# 1. Create hotfix branch from main
git checkout main
git pull origin main
git checkout -b hotfix/critical-auth-bug
# 2. Make minimal fix
# Edit files
git add .
git commit -m "hotfix: fix authentication bypass vulnerability"
# 3. Fast-track merge
npm test # Must pass
git checkout main
git merge hotfix/critical-auth-bug
git push origin main
# 4. Delete branch
git branch -d hotfix/critical-auth-bug
# 5. Verify fix in production
```
### Scenario 3: Resolving Merge Conflicts
```bash
# 1. Update main
git checkout main
git pull origin main
# 2. Rebase feature branch
git checkout feature/add-playlist
git rebase main
# 3. Conflicts appear
# Edit conflicted files, resolve markers
# 4. Stage resolved files
git add .
# 5. Continue rebase
git rebase --continue
# 6. If more conflicts, repeat steps 3-5
# 7. Force push
git push --force-with-lease origin feature/add-playlist
```
### Scenario 4: Feature Not Complete at EOD
```bash
# Option 1: Use feature flag (preferred)
# Wrap incomplete code in flag
# Merge to main with flag disabled
# Option 2: Keep branch if near completion
# But merge first thing next morning
# Don't let branch age >24h
# Option 3: Stash and delete if very incomplete
git stash
git checkout main
git branch -D feature/incomplete
# Resume later with fresh branch
```
## Best Practices
### ✅ Do
1. **Merge daily** - At least once per day
2. **Small commits** - < 200 lines each
3. **Update often** - Pull main frequently
4. **Test before push** - Always run tests
5. **Delete branches** - Immediately after merge
6. **Use feature flags** - For incomplete work
7. **Descriptive messages** - Clear commit messages
8. **Rebase before PR** - Clean, linear history
### ❌ Don't
1. **Long-lived branches** - No branches >24h
2. **Many branches** - Keep ≤ 3 active
3. **Large commits** - Avoid massive changesets
4. **Direct to main** - Always use PRs
5. **Skip tests** - Never push failing tests
6. **Leave branches** - Clean up after merge
7. **Vague messages** - No "WIP" or "fix stuff"
8. **Batch work** - Don't hold changes for days
## Troubleshooting
### Issue: "Too many branches active"
```bash
# List branches
git branch
# Check which are merged
git branch --merged main
# Delete merged branches
git branch --merged main | grep -v "main" | xargs git branch -d
# Delete unneeded branches
git branch -D feature/abandoned
```
### Issue: "Branch behind main"
```bash
# Update main
git checkout main
git pull origin main
# Rebase feature branch
git checkout feature/your-branch
git rebase main
# Or merge main into feature
git merge main
```
### Issue: "Commit too large"
```bash
# Undo last commit, keep changes
git reset HEAD~1
# Stage changes selectively
git add file1.js file2.js
git commit -m "feat: part 1 - data model"
git add file3.jsx file4.jsx
git commit -m "feat: part 2 - UI components"
```
### Issue: "Main branch broken after merge"
```bash
# Immediate revert
git checkout main
git revert HEAD --no-edit
git push origin main
# Or reset if very recent (dangerous!)
git reset --hard HEAD~1
git push --force origin main # Only if nobody pulled yet!
```
## Integration with CI/CD
### GitHub Actions Example
```yaml
# .github/workflows/trunk-based-checks.yml
name: Trunk-Based Development Checks
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
validate-workflow:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Full history for branch checks
- name: Check branch count
run: |
COUNT=$(git branch -r | grep -v "main" | wc -l)
if [ $COUNT -gt 3 ]; then
echo "❌ Too many branches ($COUNT). Max is 3."
exit 1
fi
- name: Check PR size
if: github.event_name == 'pull_request'
run: |
LINES=$(git diff --stat origin/main..HEAD | tail -1 | awk '{print $4}')
if [ "$LINES" -gt 200 ]; then
echo "⚠️ PR is large ($LINES lines). Consider splitting."
fi
- name: Run tests
run: npm test
- name: Build check
run: npm run build
- name: Lint check
run: npm run lint
```
## Resources
- **Atlassian Guide**: https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development
- **Git Documentation**: https://git-scm.com/doc
- **GitHub CLI**: https://cli.github.com/
- **Conventional Commits**: https://www.conventionalcommits.org/