Expert in the specific Git workflows, conventions, and patterns that make development teams — and solo developers working with AI coding agents — productive. Not "how to use git" but "how to use git well."
feat(auth)!: replace API key with OAuth2
BREAKING CHANGE: The `apiKey` parameter in `createClient()` has been
removed. Use `oauth: { clientId, clientSecret }` instead.
Migration: npx @your-tool/codemod v2-to-v3
The ! after the type/scope and BREAKING CHANGE: footer both signal a MAJOR version bump.
Good vs Bad Commit Messages
# BAD - what, not why
fix: update code
feat: add stuff
chore: changes
# GOOD - why, with context
fix(auth): prevent token refresh race condition on slow networks
feat(search): add fuzzy matching for skill names with Levenshtein distance
chore(deps): bump vitest to 3.1 for Node 22 compatibility
# Install
npm install -D husky lint-staged
# Initialize husky
npx husky init
# The init command creates .husky/ and adds a prepare script# Edit .husky/pre-commit:echo"npx lint-staged" > .husky/pre-commit
lint-staged Configuration
// package.json{"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"],"*.{json,md,yml}":["prettier --write"],"*.test.{ts,tsx}":["vitest related --run"]}}
Monorepo Hook Configuration
Install Husky at root, scope lint-staged by package path: "packages/core/**/*.ts": "pnpm --filter @your-org/core lint". Each package gets its own lint command, preventing cross-package leak.
Hook Performance Guidelines
Hook
Max Time
What to Run
pre-commit
<5 seconds
Lint + format staged files only
commit-msg
<1 second
commitlint validation
pre-push
<30 seconds
Type check + fast test suite
If hooks are slow, developers skip them with --no-verify. Keep them fast.
Essential Hook Set
# .husky/pre-commit — fast, staged files only
npx lint-staged
# .husky/commit-msg — validate message format
npx --no -- commitlint --edit $1# .husky/pre-push — more thorough checks
pnpm typecheck && pnpm test --run
Git Worktrees for AI Agent Isolation
Worktrees let you check out multiple branches simultaneously in separate directories, sharing the same .git history. This is the primary isolation primitive for parallel AI agents.
Basic Worktree Workflow
# Create a worktree for a feature
git worktree add ../myproject-feature-auth feature/auth
# List active worktrees
git worktree list
# Work in the worktree (it's a normal directory)cd ../myproject-feature-auth
# ... make changes, commit, push ...# Remove when done
git worktree remove ../myproject-feature-auth
AI Agent Worktree Pattern
# Agent 1: Working on auth
git worktree add ../project-agent-auth -b agent/auth-refactor
# Agent 2: Working on API (parallel, isolated)
git worktree add ../project-agent-api -b agent/api-endpoints
# Agent 3: Working on tests (parallel, isolated)
git worktree add ../project-agent-tests -b agent/test-coverage
# Each agent has full filesystem isolation# Commits in any worktree are visible to all (shared .git)# When agents finish, merge results
git merge agent/auth-refactor
git merge agent/api-endpoints
git merge agent/test-coverage
# Clean up
git worktree remove ../project-agent-auth
git worktree remove ../project-agent-api
git worktree remove ../project-agent-tests
Worktree Gotchas
Problem
Solution
Port conflicts
Each worktree needs unique dev server ports (use env vars or port-daddy)
Shared node_modules
Run npm install in each worktree (separate node_modules per worktree)
Database conflicts
Use per-worktree database names or Docker containers
Same branch twice
Git prevents this — you cannot check out a branch that's checked out elsewhere
Every PR template should include: What (1-2 sentences, what and why) -> How (technical approach) -> Testing (steps + checkboxes) -> Screenshots (before/after for UI) -> Breaking Changes (none or migration path). Save as .github/pull_request_template.md.
Reviewer Checklist
Understand what/why, tests cover happy + edge path, no TODO without issue link, error handling present, no secrets in diff, breaking changes documented.
Merge Strategy
Strategy
When
Command
Squash merge (default)
Feature branches, messy commits
git merge --squash feature/auth then single conventional commit
Rebase + FF merge
Linear history, each commit meaningful
git rebase main then git merge feature/auth
Merge commit
Long-running integration branches (rare)
git merge feature/auth
Decision: Are all commits on the branch meaningful and tested? Yes -> rebase. No -> squash.
Monorepo Git Patterns
Sparse Checkout (Work on One Package)
# Clone with sparse checkout
git clone --sparse --filter=blob:none https://github.com/org/monorepo.git
cd monorepo
# Check out only what you need
git sparse-checkout set packages/core packages/shared
Path-Scoped Git Operations
# Log for one package
git log --oneline -- packages/core/
# Diff for one package
git diff main -- packages/ui/
# Blame within scope
git log --all --oneline -- packages/cli/src/index.ts
[pull]rebase = true# Rebase on pull instead of merge[push]autoSetupRemote = true# Auto set upstream on first push[merge]conflictstyle = zdiff3 # Better conflict markers with common ancestor[diff]algorithm = histogram # Better diff output[rerere]enabled = true# Remember conflict resolutions[fetch]prune = true# Auto-delete stale remote branches
.gitignore essentials: node_modules/, .env*, dist/, .turbo/, .DS_Store, coverage/. Always commit lock files (pnpm-lock.yaml). Use gitignore.io for stack-specific templates. Set up ~/.gitignore_global for per-developer ignores.
Anti-Patterns
1. Long-Lived Feature Branches
Symptom: Branch diverges from main for weeks. Merge becomes a nightmare.
Fix: Trunk-based development. Feature flags for incomplete work. Branch lives max 2 days.
2. "WIP" Commit Messages
Symptom: git log shows "wip", "stuff", "fix", "asdf".
Fix: Squash merge into a single conventional commit. Or use git rebase -i to clean up before PR.
3. Skipping Hooks with --no-verify
Symptom: Developers routinely bypass pre-commit hooks.
Fix: Hooks are too slow. Make them faster (lint-staged, not full project). Or move slow checks to pre-push.
4. Force Pushing to Shared Branches
Symptom: git push --force to main or shared feature branches.
Fix: Protect main in GitHub/GitLab settings. Use --force-with-lease if you must force push to your own branch.
5. Committing Generated Files
Symptom: dist/, node_modules/, *.lock diffs pollute PRs.
Fix: Add to .gitignore. Only commit lock files (package-lock.json, pnpm-lock.yaml) — those belong in version control.
6. Secrets in Git History
Symptom: API keys, passwords committed and "removed" in a later commit. They're still in history.
Fix: Use git-filter-repo to rewrite history. Rotate the exposed credentials immediately. Add .env to .gitignore before first commit.
7. No Branch Protection
Symptom: Anyone can push directly to main. No required reviews. No CI gate.
Fix: Enable branch protection: require PR, require 1+ review, require CI green, require up-to-date branch.
8. Rebasing Public/Shared Branches
Symptom: Rebasing a branch that others have pulled causes history divergence.
Fix: Only rebase your own local branches. Once pushed and shared, merge only.
Quality Checklist
[ ] Branching strategy documented and agreed upon by team
[ ] Conventional commit format enforced via commitlint + husky
[ ] Pre-commit hooks run lint + format on staged files only (< 5 seconds)
[ ] Pre-push hooks run typecheck + tests
[ ] Branch protection enabled on main (PR required, CI green, review required)
[ ] .gitignore covers dependencies, build output, env files, OS files
[ ] PR template exists with what/how/testing/breaking sections
[ ] Merge strategy chosen and configured (squash recommended)
[ ] CODEOWNERS file maps directories to responsible teams
[ ] git config includes rebase-on-pull, prune-on-fetch, zdiff3 conflicts
[ ] Worktree conventions documented for AI agent workflows
[ ] No secrets in git history (scanned with trufflehog or gitleaks)
[ ] Lock files (pnpm-lock.yaml) committed, generated output is not