I've tried trunk-based development, Gitflow, GitHub Flow, and several hybrids. The thing that actually works for me day-to-day is boring.
One Branch Per Feature
I branch off main, work on one thing, open a PR, merge it. That's it. No long-lived feature branches, no release branches, no develop branch sitting around getting stale.
The feature is either small enough to do in one branch, or it needs to be broken into smaller pieces. That constraint is a feature.
Commit Messages Are for Future You
`` fix: stop card hover state persisting on mobile ``
Not fix stuff, not wip, not asdfgh. One line describing what changed and why, in the imperative tense. Future me reading git log is the audience.
I use conventional commits loosely — feat:, fix:, refactor:, docs: — not because I'm generating changelogs automatically but because the prefix forces me to think about what kind of change I'm making.
Rebase Before You Merge
``bash git fetch origin git rebase origin/main ``
Clean linear history is much easier to bisect and reason about than a tangle of merge commits. I do this before every PR.
git stash is Emergency Storage Only
I used to stash constantly. Now I treat an unclean working tree as a sign I'm doing too many things at once. Finish the thing, commit it (even as a draft commit), then switch contexts.
Aliases That Save Seconds
``bash git config --global alias.st status git config --global alias.co checkout git config --global alias.lg "log --oneline --graph --decorate" ``
None of these are clever. They're just shorter to type.