A commit is the unit of history. Six months from now, it is the commits that you (or a colleague) will use to figure out what changed and why. The difference between a history that helps and a history that is a dumping ground is craft: what to put into a commit, how to label it, and what to keep out of the repository entirely.
Atomic commit: one change — one snapshot
A good commit is atomic: one logical change in its entirety. Fixed a bug — commit; reformatted code — a separate commit; added a feature — a third one. The bad extremes: a "dumpster commit" (bug + refactoring + new feature in one) and a "saved just in case" commit with half of an unfinished thought.
Why it matters: atomic commits can be reverted one by one, moved between branches, and understood during review. A dumpster — only as a whole.
Precision of the set is what the index provides:
git add src/OrderService.java # only the files you need
git add -p # interactively: piece by piece within a file!
git commit -m "fix: recalculate total when removing the last item"
git add -p is an underrated command: it shows changes in chunks and asks which ones to include. That is how one file with two edits becomes two clean commits.
The commit message: answering "why"
The first line is a short summary (up to ~70 characters), in the imperative mood, no trailing period. What exactly changed is visible from the code; the message should answer what the code does not contain: why.
- Bad:
fix,edits,wip,changed OrderService. - Good:
fix: cart total was not reset when removing the last item.
Many teams use conventional commits prefixes (feat:, fix:, refactor:, docs:) — they make it easy to filter history and generate a changelog automatically. If more context is needed — a blank line after the summary and a paragraph of details.
Reading history: log, diff, blame
git log --oneline -15 # the last 15 commits, briefly
git log -p file.txt # history of a specific file with changes
git diff # what changed but is not yet in the index
git diff --staged # what is already in the index (will go into the commit)
git show abc123 # what a specific commit did
git blame OrderService.java # who changed each line and in which commit
git blame sounds accusatory, but it is a tool of archaeology, not of finding culprits: you found a strange line → blame → the commit → its message explains "why" → and if it doesn't, you know whom to ask. The "diff before commit" habit cuts off accidental changes: a second of review saves you from committed debug output.
.gitignore: what does not belong in history
Git offers to commit everything it sees — but not everything should end up in the repository:
build/ # build artifacts — reproducible from the code
node_modules/ # dependencies — restored from the lock file
*.log # logs
.idea/ # IDE settings
.env # SECRETS — never in git!
The .gitignore file in the project root lists such paths — Git stops noticing them. Two rules. First: generated and personal things are ignored, source and shared things are committed. Second, and most important: secrets (passwords, API keys) must never get into history — history is distributed and eternal, truly deleting from it is hard; a key that made it into a commit is considered compromised and must be rotated. On automatic protection against this — Gitleaks in the security series.
Fixing the last commit
Forgot a file or made a typo in the message — do not create a "fix the previous one" commit:
git add forgotten-file.txt
git commit --amend # replaces the last commit with a new one
One caution: --amend rewrites the commit — it is safe as long as the commit has not been sent to the server. The "don't rewrite what's been pushed" rule is covered in detail in the rebase article.
In short
- An atomic commit: one logical change.
git add -pbuilds a commit piece by piece. - The message answers "why": a short summary, with a paragraph of details when needed. feat/fix prefixes help with filtering.
- History is a tool: log for the timeline, diff before committing, blame for archaeology.
- .gitignore: build output, dependencies, IDE files — out; secrets — never in git, and if they got in — rotate them.
--amendfixes the last commit, as long as it has not been pushed.
What to read next
- Branches and merging — the next topic in the series.
- Undo and recovery — reset, revert, and reflog when something goes wrong.
- What is Git — if you missed the beginning.