I touch Git hundreds of times a day. Here's the workflow I've settled on.
Branching Strategy
For personal projects: I mostly work on main. Small changes go directly. Larger features get a branch.
For OSS contributions: Always branch. Never touch main in my fork. Branch names describe the change: fix/oauth-scope-override, feat/tool-annotations.
For client work: Follow their conventions. Usually feature branches with PR review before merge.
Commit Messages
I use conventional commits loosely:
fix: preserve scope when explicitly set
feat: add tool annotations to memory server
docs: update README with installation steps
test: add coverage for edge cases
The prefix helps when scanning history. But I don't stress about perfect categorization. fix vs chore vs refactor—close enough is fine.
The message body matters more than the prefix. What changed? Why? What should reviewers know?
Commit Size
Small commits, frequently. I commit after:
- A test passes
- A function works
- A logical chunk completes
This creates useful checkpoints. If something breaks, I can bisect quickly. If I need to revert, I'm not losing hours of work.
The anti-pattern: one giant commit at end of day with message "WIP" or "done". That's not version control, that's a backup.
The Commands I Actually Use
90% of my Git usage:
git status # What's changed?
git add -p # Stage interactively
git commit -m "..." # Commit with message
git push # Push to remote
git pull --rebase # Pull with rebase
git log --oneline -10 # Recent historyThe other 10%:
git stash # Temporarily shelve changes
git rebase -i HEAD~3 # Clean up recent commits
git cherry-pick abc # Grab a specific commit
git reflog # Find lost commitsI rarely need anything fancier. If I do, I look it up.
Pull Request Habits
For OSS PRs:
- One fix per PR. Don't bundle unrelated changes.
- Descriptive title.
fix: Xnotfix stuff. - Thorough description. Problem, solution, testing, related issues.
- Small diff. Under 100 lines if possible.
For team PRs:
- Draft first if you want early feedback.
- Self-review before requesting review.
- Respond to comments same day.
- Squash on merge to keep history clean.
Keeping History Clean
I rebase, not merge, when updating branches:
git fetch upstream
git rebase upstream/mainThis keeps my branch commits on top, no merge commits cluttering history.
Before pushing a branch, I sometimes squash:
git rebase -i HEAD~5 # Squash last 5 into logical chunksBut I don't obsess over this. Clean enough is fine.
The .gitconfig
A few aliases I use:
[alias]
co = checkout
br = branch
st = status
lg = log --oneline --graph --decorate -10And some config:
[pull]
rebase = true
[push]
autoSetupRemote = true
[init]
defaultBranch = mainWhat I've Learned
Commit early, commit often. You can always squash later. You can't recover work you didn't commit.
Write for future you. In six months, you won't remember why you made a change. The commit message is the only context.
Don't fear the reflog. Git almost never loses data. If you mess up, git reflog usually has your back.
Keep it simple. Fancy Git workflows create friction. The best workflow is one you actually follow.