git add stages; git commit records. Everything else in git — branches, merges, rebases — moves commits around, but this pair is where every change enters history, and the craft is in what you stage and what you write.
Staging with intent #
$ git add src/blog.js # one file
$ git add -p # choose hunks interactively
$ git add -A # everything, including new files
-p is the honesty feature: it walks each hunk, and y/n/s (split) let you stage a logical change out of a messy working tree. Two unrelated fixes in one file? Stage them as two commits — status shows the split the whole way.
Committing #
$ git commit -m "fix: correct cache headers on media responses"
$ git commit # opens the editor for the long form
The long form is worth the habit for anything non-trivial:
fix: correct cache headers on media responses
The media route set max-age=0 after the immutable change,
so browsers revalidated every asset. Restore the one-year
header and keep nosniff.
Subject in the imperative ("fix", not "fixed"), under 50 characters, blank line, then the why — the what is visible in the diff.
Amending and empty commits #
$ git add forgotten.js
$ git commit --amend --no-edit
$ git commit --amend -m "fix: correct cache headers on media" # new message
Amend rewrites the last commit — perfect before it is pushed, forbidden after. The empty commit (--allow-empty) marks events in history: a deploy, a release gate.
The craft rules #
| Rule | Why |
|---|---|
| One logical change per commit | Reverts and reviews become precise |
| Commit early, perfect with amend | Small diffs are readable diffs |
| Tests pass before committing | Every commit is a working state |
| Never commit secrets | History keeps them forever |
The granularity rule pays off downstream: cherry-pick moves exactly one logical change, revert undoes exactly one, and reviewers read one idea at a time.
History rewriting for clean lines is rebase; combining commits is its interactive form. The Git series continues.
