Back to catalog

git-hygiene

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

# Git Hygiene Skill

## Purpose

Branch cleanup automation, commit quality maintenance, and repository health practices for trunk-based development.

## Scope

- Automated branch cleanup
- Stale branch detection
- Commit squashing techniques
- Git hooks setup
- Repository maintenance

## Branch Cleanup Automation

### Delete Merged Branches
```bash
# Delete local merged branches
git branch --merged main | grep -v "main" | xargs git branch -d

# Delete remote merged branches
git remote prune origin

# Comprehensive cleanup script
#!/bin/bash
echo "🧹 Cleaning up branches..."

# Local merged branches
MERGED=$(git branch --merged main | grep -v "main")
if [ -n "$MERGED" ]; then
  echo "Deleting merged local branches:"
  echo "$MERGED"
  git branch --merged main | grep -v "main" | xargs git branch -d
fi

# Remote merged branches
git remote prune origin

# Show remaining branches
echo "Remaining branches:"
git branch -a | grep -v "main"
echo "✅ Cleanup complete"
```

### Stale Branch Detection
```bash
# Find branches older than 24 hours
git for-each-ref --sort=-committerdate refs/heads/ \
  --format='%(refname:short)|%(committerdate:relative)|%(authorname)' | \
  grep -E '[0-9]+ (day|week|month)s? ago'

# Delete stale branches (with confirmation)
#!/bin/bash
echo "Detecting stale branches (>24h)..."

git for-each-ref --sort=-committerdate refs/heads/ \
  --format='%(refname:short)|%(committerdate:relative)' | \
  while IFS='|' read -r branch age; do
    if [ "$branch" != "main" ]; then
      if [[ "$age" == *"day"* ]] || [[ "$age" == *"week"* ]] || [[ "$age" == *"month"* ]]; then
        echo "⚠️ Stale: $branch ($age)"
        echo "Delete? (y/n)"
        read -r response
        if [ "$response" = "y" ]; then
          git branch -D "$branch"
          git push origin --delete "$branch" 2>/dev/null
        fi
      fi
    fi
  done
```

### Branch Count Monitoring
```bash
# Check branch count (should be ≤3)
check_branch_limit() {
  COUNT=$(git branch | grep -v "main" | wc -l | tr -d ' ')

  if [ $COUNT -gt 3 ]; then
    echo "❌ Too many branches ($COUNT/3)"
    echo "Active branches:"
    git branch | grep -v "main"
    echo ""
    echo "Action required: Merge or delete branches to continue"
    return 1
  elif [ $COUNT -eq 3 ]; then
    echo "⚠️ At branch limit ($COUNT/3). Merge before creating new branches."
    return 0
  else
    echo "✅ Branch count OK ($COUNT/3)"
    return 0
  fi
}
```

## Commit Quality

### Commit Squashing
```bash
# Interactive rebase to squash commits
git rebase -i main

# In editor, change 'pick' to 'squash' (or 's'):
pick abc1234 feat: add playlist model
squash def5678 WIP - fix typo
squash ghi9012 WIP - update styling
squash jkl3456 add tests

# Save and close editor
# Edit combined commit message
# Result: One clean commit

# Force push (be careful!)
git push --force-with-lease origin feature/add-playlist
```

### Amending Commits
```bash
# Fix last commit (before pushing)
git add forgotten-file.js
git commit --amend --no-edit

# Or update commit message
git commit --amend -m "feat: add playlist feature with tests"

# If already pushed
git push --force-with-lease origin feature/add-playlist
```

### Commit Message Validation
```bash
#!/bin/bash
# .git/hooks/commit-msg

COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

# Check format: <type>: <description>
if ! echo "$COMMIT_MSG" | grep -qE "^(feat|fix|refactor|test|docs|style|perf|chore): .{10,}"; then
  echo "❌ Invalid commit message format"
  echo ""
  echo "Format: <type>: <description>"
  echo ""
  echo "Types: feat, fix, refactor, test, docs, style, perf, chore"
  echo "Description: At least 10 characters"
  echo ""
  echo "Examples:"
  echo "  feat: add playlist creation feature"
  echo "  fix: resolve audio player crash"
  echo "  test: add unit tests for favorites service"
  exit 1
fi

# Check for forbidden words
if echo "$COMMIT_MSG" | grep -qiE "^(WIP|temp|fix stuff|updates)"; then
  echo "❌ Commit message too vague: $COMMIT_MSG"
  echo "Be more descriptive about what changed and why"
  exit 1
fi

echo "✅ Commit message format valid"
```

## Git Hooks Setup

### Installing Hooks
```bash
# Create hooks directory if needed
mkdir -p .git/hooks

# Pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
echo "🔍 Running pre-commit checks..."

# Run tests
npm test || {
  echo "❌ Tests failed"
  exit 1
}

# Run linter
npm run lint || {
  echo "❌ Lint errors found"
  exit 1
}

echo "✅ Pre-commit checks passed"
EOF

chmod +x .git/hooks/pre-commit

# Pre-push hook
cat > .git/hooks/pre-push << 'EOF'
#!/bin/bash
echo "🔍 Running pre-push checks..."

# Check branch count
COUNT=$(git branch | grep -v "main" | wc -l | tr -d ' ')
if [ $COUNT -gt 3 ]; then
  echo "❌ Too many branches ($COUNT). Max is 3."
  exit 1
fi

# Check tests
npm test || {
  echo "❌ Tests failed"
  exit 1
}

echo "✅ Pre-push checks passed"
EOF

chmod +x .git/hooks/pre-push
```

### Using Husky (Modern Approach)
```bash
# Install Husky
npm install --save-dev husky

# Initialize
npx husky init

# Create pre-commit hook
npx husky add .husky/pre-commit "npm test"
npx husky add .husky/pre-commit "npm run lint"

# Create pre-push hook
npx husky add .husky/pre-push "npm test"

# Hooks now version-controlled and shared with team
```

## Repository Maintenance

### Regular Health Checks
```bash
#!/bin/bash
# repo-health-check.sh

echo "🏥 Repository Health Check"
echo "=========================="

# Branch count
COUNT=$(git branch | grep -v "main" | wc -l)
echo "Active branches: $COUNT/3"
if [ $COUNT -gt 3 ]; then
  echo "❌ OVER LIMIT"
else
  echo "✅ OK"
fi

# Stale branches
echo ""
echo "Branch ages:"
git for-each-ref --sort=-committerdate refs/heads/ \
  --format='%(refname:short) | %(committerdate:relative)'

# Main branch status
echo ""
echo "Main branch status:"
git checkout main -q
git pull origin main -q
npm test > /dev/null 2>&1
if [ $? -eq 0 ]; then
  echo "✅ Main is green (tests pass)"
else
  echo "❌ Main is broken (tests fail)"
fi

# Last merge time
echo ""
echo "Last merge to main:"
git log --merges -1 --format="%cr - %s"

# Unmerged work
echo ""
git checkout - -q
AHEAD=$(git rev-list --count HEAD..main)
BEHIND=$(git rev-list --count main..HEAD)
echo "Current branch vs main: $BEHIND ahead, $AHEAD behind"
```

## Automation Scripts

### Daily Cleanup Cron Job
```bash
# crontab -e
# Add this line to run cleanup daily at 6 PM:
0 18 * * * cd /path/to/music-app && ./scripts/daily-cleanup.sh

# scripts/daily-cleanup.sh
#!/bin/bash
git fetch origin
git checkout main
git pull origin main

# Delete merged branches
git branch --merged main | grep -v "main" | xargs git branch -d
git remote prune origin

# Alert about stale branches
git for-each-ref --sort=-committerdate refs/heads/ \
  --format='%(refname:short)|%(committerdate:relative)' | \
  grep -E '[0-9]+ (day|week)s? ago' | \
  mail -s "Stale branches detected" team@example.com
```

## Best Practices

### ✅ Do

1. **Clean up daily** - Delete merged branches
2. **Automate checks** - Use git hooks
3. **Monitor health** - Run regular health checks
4. **Squash WIP commits** - Clean history before merge
5. **Validate messages** - Use commit-msg hook
6. **Prune remotes** - Remove stale remote tracking branches

### ❌ Don't

1. **Leave merged branches** - Delete immediately
2. **Skip hooks** - Hooks ensure quality
3. **Force push to main** - Never!
4. **Ignore stale branches** - Clean up or merge
5. **Commit without message** - Always explain changes

## Common Issues

**Issue**: Git hooks not running
**Solution**: Ensure hooks are executable: `chmod +x .git/hooks/*`

**Issue**: Branch deletion fails (has unmerged commits)
**Solution**: Use `git branch -D` to force delete (be careful!)

**Issue**: Commit message hook too strict
**Solution**: Review and adjust regex pattern in commit-msg hook

## Resources

- **Git Hooks Documentation**: https://git-scm.com/docs/githooks
- **Husky**: https://typicode.github.io/husky/
- **Conventional Commits**: https://www.conventionalcommits.org/