The output of git log --oneline reads like a set of short logbook entries. A good history reads like a well-structured article: each section does one thing, the headings stand on their own, the order makes sense, and the reader never has to guess why.
This is not about pretty commits. It is about whether someone can safely enter the context you left behind.
One commit, one decision
If a commit fixes a bug, upgrades a dependency, renames files, and adjusts indentation, the reviewer cannot tell which line caused what. Split changes into separate decisions and the history becomes readable on its own.
The unit is not lines of code. It is the question being answered:
- if the title contains two “and”s, it may need to be split;
- if reverting one change forces you to revert another, they are coupled;
- if a formatting change makes the real change hard to review, commit the format first.
The goal is not tiny commits. The goal is commits that can each be understood in isolation.
A title should stand on its own
Treat the subject as the first line of a changelog. It should say what changed, not “update some stuff”.
feat(archive): add year grouping to writing index
fix(search): keep focus inside the dialog
docs: explain how bilingual routes work
The feat, fix, and docs prefixes are not rituals. They help readers judge the blast radius quickly. If a project has no convention, choose one and use it consistently.
The body can explain why, but it should not read like meeting minutes. Put background, trade-offs, and verification where they matter.
Tidy with rebase, do not hide with it
My feature branches often contain draft commits:
wip
fix typo
actually fix it
tests
Merging them is not wrong, but the history becomes noisy. When the project allows it, I run an interactive rebase before opening a PR:
git rebase -i origin/main
Squash WIP commits into meaningful ones, reorder them, and rewrite the messages. Only do this to commits that have not been shared yet. Never rewrite history that other people have already pulled.
Describe the boundaries, not just the result
A useful commit explains its edge cases. “Fixed search” is weaker than “restore focus to the trigger after closing the dialog and prevent arrow keys from scrolling the page”.
If a scenario is known but not covered, write it in the PR or commit body rather than leaving it for the next person to discover. Honest notes about limits are part of engineering judgment.
The compound interest of history
Commit history rarely pays off immediately. Its value appears months later: when you need to trace a regression, when a new teammate needs to understand a strange branch, or when you want to reuse the same design in another project.
Readable history is not extra work. It is future communication compressed into small records. Git stores time. Good commit messages make that time readable again.