# Git Hooks: Automate Workflow Like Pro

# Git Hooks: Automate Workflow Like a Pro

## The Magic Behind Pre-Commit Hooks

Git hooks are like invisible ninjas in your repository—they silently watch your every move and spring into action at critical moments. Whether you're committing code, pushing to a remote, or merging branches, hooks can automate tedious tasks, enforce standards, and save your team from countless headaches.

Think of them as automated gatekeepers that ensure quality before code even reaches your repository.

## The Story: Why We Need Git Hooks

**The Scenario:**
Sarah's team was drowning in code review comments. Every pull request had the same issues: inconsistent formatting, missing tests, hardcoded credentials, and linting errors. The team was spending 40% of their time on trivial fixes instead of actual development.

Then Sarah discovered git hooks.

Within a week, her team's workflow transformed. Developers couldn't commit broken code even if they tried. Linters ran automatically. Tests executed before pushing. Credentials were caught before they leaked. Code reviews became about logic and architecture, not formatting.

**The Result:**
- 60% reduction in review cycle time
- Zero accidental credential leaks
- Consistent code style across the entire codebase
- Developers shipping features 30% faster

## Pre-Commit Hooks: The Foundation

Pre-commit hooks fire before your commit is finalized. They're your first line of defense.

### Setting Up Your First Hook

```bash
# Navigate to your git hooks directory
cd .git/hooks

# Create a pre-commit hook
touch pre-commit
chmod +x pre-commit
```

### Basic Pre-Commit Hook Example

```bash
#!/bin/bash
# .git/hooks/pre-commit

echo "🔍 Running pre-commit checks..."

# Check for staged changes
if git diff --cached --name-only | grep -q "\.js$"; then
    echo "📝 Linting JavaScript files..."
    npm run lint -- --fix
    
    if [ $? -ne 0 ]; then
        echo "❌ Linting failed! Fix errors before committing."
        exit 1
    fi
fi

# Check for console.log statements
if git diff --cached | grep -q "console\.log"; then
    echo "⚠️  Found console.log statements. Remove them!"
    exit 1
fi

echo "✅ Pre-commit checks passed!"
exit 0
```

## The Hook Arsenal: Essential Hooks Explained

### 1. **Pre-Commit Hook** (Most Important)
Runs before the commit is created. Perfect for:
- Linting and formatting
- Running tests
- Checking for secrets
- Validating file sizes

### 2. **Commit-Msg Hook**
Validates commit messages. Enforce conventional commits:

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

COMMIT_MSG=$(cat $1)

# Check if commit follows conventional commits format
if ! echo "$COMMIT_MSG" | grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?!?: .+"; then
    echo "❌ Commit message must follow conventional commits:"
    echo "   feat(scope): description"
    echo "   fix(scope): description"
    exit 1
fi

echo "✅ Commit message is valid!"
exit 0
```

### 3. **Pre-Push Hook**
Runs before pushing to remote. Catch issues before they go public:

```bash
#!/bin/bash
# .git/hooks/pre-push

echo "🚀 Running pre-push checks..."

# Run full test suite
npm test
if [ $? -ne 0 ]; then
    echo "❌ Tests failed! Fix before pushing."
    exit 1
fi

# Check for unresolved merge conflicts
if git diff --name-only --diff-filter=U | grep -q .; then
    echo "❌ Unresolved merge conflicts detected!"
    exit 1
fi

echo "✅ Ready to push!"
exit 0
```

### 4. **Post-Merge Hook**
Runs after merging branches. Useful for:
- Reinstalling dependencies
- Updating database migrations
- Refreshing build artifacts

```bash
#!/bin/bash
# .git/hooks/post-merge

# Check if package.json changed
if git diff HEAD@{1} HEAD --name-only | grep -q "package.json"; then
    echo "📦 Dependencies changed. Running npm install..."
    npm install
fi

# Check if migrations changed
if git diff HEAD@{1} HEAD --name-only | grep -q "migrations/"; then
    echo "🗄️  Database migrations detected. Review before proceeding!"
fi
```

## Pro Tips & Best Practices

### Tip 1: Use a Hook Framework
Don't reinvent the wheel. Use **Husky** for Node.js projects:

```bash
npm install husky --save-dev
npx husky install

# Add a hook
npx husky add .husky/pre-commit "npm run lint"
npx husky add .husky/commit-msg 'echo "Validating commit message..."'
```

### Tip 2: Combine with Lint-Staged
Only lint files that are actually staged:

```bash
npm install lint-staged --save-dev
```

```json
{
  "lint-staged": {
    "*.js": "eslint --fix",
    "*.css": "stylelint --fix",
    "*.md": "prettier --write"
  }
}
```

### Tip 3: Make Hooks Bypassable (When Necessary)
Sometimes you need to skip hooks:

```bash
# Skip pre-commit hook
git commit --no-verify -m "Emergency fix"

# Skip pre-push hook
git push --no-verify
```

### Tip 4: Share Hooks Across Team
Store hooks in version control:

```bash
# Create hooks directory in repo
mkdir -p .githooks

# Move hooks there
mv .git/hooks/pre-commit .githooks/

# Configure git to use this directory
git config core.hooksPath .githooks

# Team members run this once
git config core.hooksPath .githooks
```

### Tip 5: Add Helpful Output
Make hooks user-friendly:

```bash
#!/bin/bash
# Use colors and emojis
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo -e "${YELLOW}🔍 Checking code quality...${NC}"

if npm run lint; then
    echo -e "${GREEN}✅ All checks passed!${NC}"
else
    echo -e "${RED}❌ Linting failed. Fix errors and try again.${NC}"
    exit 1
fi
```

### Tip 6: Performance Matters
Keep hooks fast. Slow hooks frustrate developers:

```bash
# ❌ Bad: Runs full test suite
npm test

# ✅ Good: Runs only affected tests
npm run test:affected
```

## Advanced Hook Patterns

### Pattern 1: Prevent Secrets from Leaking
```bash
#!/bin/bash
# Detect AWS keys, API tokens, etc.

PATTERNS=(
    "AKIA[0-9A-Z]{16}"  # AWS Access Key
    "aws_secret_access_key"
    "private_key"
    "password.*="
)

for pattern in "${PATTERNS[@]}"; do
    if git diff --cached | grep -iE "$pattern"; then
        echo "❌ Potential secret detected!"
        exit 1
    fi
done
```

### Pattern 2: Enforce Branch Naming
```bash
#!/bin/bash
# .git/hooks/pre-commit

BRANCH=$(git rev-parse --abbrev-ref HEAD)

if ! echo "$BRANCH" | grep -qE "^(main|develop|feature|bugfix|hotfix)\/"; then
    echo "❌ Branch name must follow pattern: feature/*, bugfix/*, etc."
    exit 1
fi
```

### Pattern 3: Auto-Format on Commit
```bash
#!/bin/bash
# Auto-format and re-stage files

npm run format
git add .
```

## Troubleshooting Common Issues

| Issue | Solution |
|-------|----------|
| Hook not executing | Check file permissions: `chmod +x .git/hooks/pre-commit` |
| Hook runs but changes aren't staged | Use `git add` to re-stage modified files |
| Team members skip hooks | Use Husky to enforce hooks across team |
| Hooks too slow | Optimize with lint-staged or parallel execution |
| Hook works locally but not in CI | Ensure CI environment has same dependencies |

## Conclusion

Git hooks transform your workflow from reactive (catching issues in review) to proactive (preventing issues before they happen). Start with pre-commit hooks for linting and formatting, add commit-msg validation, then expand to pre-push and post-merge hooks.

The magic isn't in the hooks themselves—it's in the consistency and automation they bring to your team's development process. Your future self (and your code reviewers) will thank you.

**Start small. Automate wisely. Ship better code.**
