Git Habits That Have Saved Me More Than Once
I have had to recover a production file from git history twice in my career. Both times the history was clean enough that I could find exactly what changed, when, and by whom. Both times I was very glad I had been disciplined about commits.
Write Commits Like Someone Six Months From Now Will Read Them
Because they will. And it is often you.
# Bad — tells you nothing
git commit -m "fix"
git commit -m "update"
git commit -m "wip"
# Good — self-documenting
git commit -m "Fix null error when user has no active subscription"
git commit -m "Add rate limiting to /api/v1/posts (60 req/min)"
git commit -m "Refactor payment processing into PaymentService class"
For non-obvious changes, add a body explaining the why:
Disable caching on user dashboard for admins
Admin users see real-time data from multiple teams. Cached
responses were showing stale team counts when members were
added mid-session.
One Logical Change Per Commit
If you have fixed a bug AND added a feature AND updated the README in the same commit, that commit is going to be painful to understand, impossible to cleanly revert, and annoying to code-review. Use git add -p to stage hunk by hunk:
git add -p # Stage changes interactively, not file by file
Branch Naming That Is Self-Documenting
feature/APP-142-user-subscription-billing
bugfix/APP-201-null-pointer-on-logout
hotfix/APP-214-critical-payment-timeout
chore/APP-198-update-php-84-dependencies
The type tells you what kind of work it is. The ticket ID links to your task tracker. The description says what it does without opening the PR.
git bisect: The Underused Lifesaver
A bug appeared in production sometime in the last 47 commits. Instead of reading every commit manually:
git bisect start
git bisect bad # Current state is broken
git bisect good v2.3.0 # This version was fine
# Git checks out the midpoint — you test it
git bisect bad # Still broken
# OR
git bisect good # Working
# Repeat until git identifies the exact breaking commit
git bisect reset # Return to HEAD when done
Interactive Rebase Before Merging
Clean up messy history before your PR lands on main. Squash WIP commits into logical units:
git rebase -i origin/main
# In the editor:
pick a1b2c3 Add user authentication
fixup d4e5f6 fix typo # squash into previous
fixup g7h8i9 oops forgot test
fixup m3n4o5 lint
Now the PR shows one clean commit. The reviewer sees intent, not iteration noise.
The Rule I Never Break
Never force-push to a branch anyone else is working on. On your own branch, fine. On a shared branch or main, absolutely not. Rebasing shared branches destroys other people's work and takes an hour to untangle.