Git Workflow
Switch over checkout
Use git switch for branch operations. Never git checkout. checkout -- silently destroys uncommitted work, and checkout <branch> is ambiguous when a file shares the name. switch only switches branches. Use git restore for file-level operations.
Trunk-based, small PRs
Target ≤ 200 lines of changed code per PR (excluding generated files). Larger changes get split into stacked diffs. Stack with worktrees, not branch-switching:
git fetch origin && git switch dev && git pull --ff-only
mkdir -p ../wt
git worktree add -b feat/a ../wt/a dev
git worktree add -b feat/b ../wt/b feat/a # stacked on feat/a
After feat/a merges, rebase downstream branches:
git -C ../wt/b fetch origin && git -C ../wt/b rebase origin/dev
Conventional Commits
type(scope): description. Type is feat, fix, refactor, docs, test, chore. Imperative mood, lowercase, no period, ≤72 chars.
feat(auth): add org-level permissions
fix(billing): handle zero-balance edge case
refactor(api): extract validation to middleware
Never use "and" in a commit message. If you need "and", you're describing two changes that should be two commits. Split them.
Force pushes
After a rebase, use --force-with-lease, never --force. Never force-push to main/master.
Heredoc for multi-line commit messages
git commit -m "$(cat <<'EOF'
type(scope): one-liner subject
Optional body explaining why, not what.
Refs #123
EOF
)"
Pre-commit checklist
git status -sb-- review staged + unstaged changes.git diff-- eyeball every hunk.git add -p-- stage precisely; split unrelated hunks across commits.pnpm typecheck && pnpm lint && pnpm test(or the project's equivalent).git commit -m "..."-- Conventional, ≤72 chars.
Never
--no-verify,--no-gpg-sign(unless explicitly requested).git rebase -ifrom a tool call (interactive, will hang).--amendonce pushed (unless explicitly requested + force-with-lease).git configupdates from a tool call.