What you'll learn
Quick Answer
A good git commit message has a short imperative subject line (about 50 characters), a blank line, then an optional body wrapped near 72 columns that explains why the change was made, not just what changed. Keep each commit to one focused idea, and consider a conventional prefix like feat: or fix: so the history reads at a glance. Done well, your git log becomes documentation your future self and teammates can actually use.
Why good commit messages matter
Open any git history and you can tell within seconds whether the team cared about their commits. A log full of fix, update, and asdf tells you nothing. A log of clear entries reads like a diary of the project. Learning to write good git commit messages is one of the cheapest habits you can build, and it pays off every time someone (often future-you) tries to understand why a line of code exists.
You do not need a big team to benefit. Even on a solo project, a readable history helps you find the commit that broke something, write release notes, or remember what you were thinking three months ago. This guide walks through the anatomy of a clear commit, with good and bad examples side by side.
The anatomy of a commit message
A complete commit message has up to three parts:
- Subject line — a short summary of the change, ideally under 50 characters.
- Blank line — one empty line separating the subject from the body. Git treats this blank line as meaningful.
- Body — an optional longer explanation, wrapped at about 72 characters, describing why the change was made.
Here is the shape of it:
Add password strength meter to signup form
The old form accepted "123456" without warning, which led to
several weak accounts. This adds a live strength check so users
get feedback before they submit.
For a small change, the subject line alone is fine. The body matters most when the reason behind a change is not obvious from the diff itself.
Write the subject in the imperative mood
Write the subject line as a command, as if you are telling the codebase what to do. This is called the imperative mood.
A quick trick: your subject should complete the sentence "If applied, this commit will..."
- "If applied, this commit will Add a logout button" reads correctly.
- "If applied, this commit will Added a logout button" does not.
So write Add logout button, not Added logout button or Adds logout button. This is not only a style choice — git's own generated messages (like Merge branch and Revert "...") use the imperative, so your commits stay consistent with them. Capitalise the first word and skip the full stop at the end; the subject is a title, not a sentence.
Follow the 50/72 rule
The 50/72 rule is a simple formatting convention:
- Keep the subject line at or under 50 characters.
- Wrap the body text at about 72 characters per line.
Why these numbers? The 50-character subject stays fully visible in git log --oneline, on GitHub, and in most tools without being cut off. The 72-character body leaves room for git to indent the text when it displays a commit, so nothing wraps awkwardly in a standard 80-column terminal.
These are guidelines, not hard errors. If your subject is 53 characters, the world will not end. But if you find yourself needing 90 characters for the subject, that is usually a sign the commit is doing too much and should be split into smaller commits.
Describe the why, not just the what
The single biggest upgrade to your commits is explaining why a change was made, not just what changed. The diff already shows the what — anyone can read that lines were added or removed. What the diff cannot show is your reasoning.
Compare two bodies for the same change:
// What only
Change timeout from 30s to 60s
// What + why
Increase API timeout from 30s to 60s
Users on slower mobile networks report failed uploads of
large files. Doubling the timeout fixes the reported failures
without affecting normal requests.
Six months from now, nobody remembers why the timeout was 60. The second message answers the question before it is even asked. Focus the body on context: the problem, the constraint, the trade-off, or the bug report that prompted the work.
Use conventional commit prefixes
Many teams add a short prefix to the subject line so the history is easy to scan and even parse by tools. This convention is called Conventional Commits. The format is:
<type>: <short summary>
The common types are:
feat:— a new featurefix:— a bug fixdocs:— documentation onlyrefactor:— a code change that neither fixes a bug nor adds a featuretest:— adding or fixing testschore:— build process, dependencies, or toolingstyle:— formatting or whitespace, no logic change
So you get subjects like feat: add dark mode toggle or fix: prevent crash on empty search. Beyond readability, these prefixes let tools auto-generate changelogs and decide version numbers. You do not have to adopt this on a personal project, but it is worth knowing because you will see it everywhere in open source and professional teams.
Good and bad examples side by side
Here are commit subjects with a verdict on each. The pattern is consistent: vague and past-tense is weak, while specific and imperative is clear.
| Commit subject | Good? | Reason |
|---|---|---|
| stuff | No | Says nothing about the change |
| Fixed bug | No | Which bug? Past tense, no detail |
| update code and also fix login and css | Partial | Two unrelated changes in one commit |
| fix: reject expired tokens on login | Yes | Imperative, specific, one idea |
| feat: add CSV export to reports page | Yes | Clear type, clear scope |
| docs: explain env setup in README | Yes | Reader knows exactly what changed |
Notice the "Partial" row: bundling unrelated changes is a common beginner mistake. If your subject needs the word "and", that is usually a hint to make two commits instead.
A quick commit checklist
Before you run git commit, a quick mental checklist:
- Is the subject under about 50 characters and in the imperative mood?
- Does it describe one focused change?
- If the reason is not obvious, did you add a body explaining why?
- Did you leave a blank line between the subject and body?
- Would this line make sense to a teammate who has never seen the code?
A practical tip: run git commit without the -m flag. It opens your editor so you can write a proper subject and body, instead of squeezing everything into a rushed one-liner. And avoid git commit -m "wip" on shared branches — if you must save messy work in progress, squash those commits before you push.
Commit messages are a small slice of a much bigger version-control workflow — branching, merging, pull requests, and resolving conflicts. If you want to go deeper, our free Git course walks through the whole thing, from your first commit to collaborating on real projects.
Frequently Asked Questions
What tense should a git commit message use?
Use the imperative present tense — "Add feature", not "Added feature" or "Adds feature". A simple trick is that the subject should complete the sentence "If applied, this commit will...". This also keeps your commits consistent with git's own auto-generated merge and revert messages.
Should I use git commit -m or write a longer message?
For a tiny, self-explanatory change, the -m flag is fine. For anything where the reasoning matters, run git commit with no -m so your editor opens and you can write a proper subject line plus a body that explains why.
Do I really need conventional commit prefixes like feat: and fix:?
Not on personal projects. But prefixes make history much easier to scan and let tools build changelogs and version numbers automatically, so they are worth adopting on team and open-source projects where you will see them everywhere.
What if my commit does two different things?
Split it into two commits. You can stage changes selectively with git add -p and commit each one separately. A good signal: if your subject line needs the word "and", it should probably be more than one commit.
Can I fix a bad commit message after committing?
Yes. For the most recent commit, use git commit --amend. For older commits you can use an interactive rebase. Just avoid rewriting history that you have already pushed to a shared branch, since it forces everyone else to reconcile the change.
