Writing good commit messages

A good commit message explains a change quickly, accurately, and in a way that stays useful after the pull request has been closed. It should help a future reader understand what…

A good commit message explains a change quickly, accurately, and in a way that stays useful after the pull request has been closed. It should help a future reader understand what changed and why.

Write for the future reader

The future reader may be investigating a regression, reviewing a release, auditing a security fix, or trying to understand why a line of code exists. They need context, not a diary of the work session.

A weak message describes the activity:

git commit -m "Update files"

A stronger message describes the result:

git commit -m "Validate refresh tokens before issuing sessions"

The second message gives the reader a useful index entry in the project history.

Keep each commit focused

A clear message starts with a clear commit. Do not mix unrelated changes in the same commit. A bug fix, a rename, and a formatting sweep should usually be separate commits unless they are inseparable.

Before committing, inspect the diff:

git diff --check
git diff --staged

The first command warns about whitespace errors and conflict markers. By default it flags trailing whitespace and a space followed by a tab inside the initial indent. The second command shows exactly what is staged. Use both to catch accidental edits before they become part of history.

Use a direct subject line

Use a short subject line that states the change. Start with a verb in the imperative mood when that reads naturally:

git commit -m "Reject expired password reset links"

That style matches the way Git itself describes commits when it generates messages, such as "Merge branch ..." or "Revert ...". The official guidance in Git's SubmittingPatches document recommends the imperative mood, as if you are giving orders to the codebase to change its behaviour. It also reads well in a list: "This commit will reject expired password reset links".

Avoid vague subjects such as:

  • "Fix bug"
  • "More changes"
  • "WIP"
  • "Address review comments"
  • "Final update"

Those messages force the reader to open the diff before they can begin to understand the purpose.

Add a body when the subject is not enough

A one line message is fine for a small, obvious change. Use a body when the change has context, trade offs, migration steps, or behaviour that is not obvious from the diff.

You can supply a body with a second -m option. Git concatenates multiple -m values as separate paragraphs, so the subject and body are split by a blank line automatically:

git commit -m "Limit login attempts per account" -m "Track failed attempts against the account identifier rather than the source IP address. This avoids locking out shared networks while still slowing repeated credential attacks."

The body should explain why the change exists and any important constraints. It should not repeat every line of the diff.

Mention user visible behaviour

When a change affects behaviour, say what changes for the user, operator, or caller. That helps release notes, support investigation, and incident review.

For example:

git commit -m "Return 404 for deleted invoice exports" -m "Deleted exports no longer return an empty CSV. The API now reports that the export is unavailable, which matches the documented lifecycle."

This message gives the reader the practical effect and the reason.

Use structured messages only when they add value

Some teams use a structured format such as Conventional Commits. That format can be useful when tooling generates changelogs or infers semantic version changes. Under that convention a fix commit maps to a patch release and a feat commit maps to a minor release, while a breaking change marker maps to a major release.

A structured subject may look like this:

git commit -m "fix(auth): reject expired password reset links"

Use structure consistently or do not use it. A half adopted convention creates noise because readers and tools cannot rely on it.

Do not let the type replace the explanation. The message still needs to say what changed, and for non-trivial commits the body still needs to explain why.

Avoid leaking private context

A commit message is part of the repository history. It may be copied into mirrors, release notes, package metadata, or audit exports.

Do not include:

  • personal names unless the project explicitly requires attribution there
  • client names
  • private URLs
  • incident details that do not belong in source control
  • secrets, tokens, keys, or credentials
  • internal system names that are not already public in the repository

Keep the message about the technical change.

Fix the message before sharing it

If the commit has not been pushed or shared, amend the message when it is wrong:

git commit --amend

This replaces the tip of the current branch with a new commit and reuses the original message as the starting point unless you supply a new one. If several local commits need cleanup, use interactive rebase:

git rebase -i HEAD~3

Only rewrite commits that are still private, or where the team has explicitly agreed that rewriting the branch is safe. Once others may have based work on a commit, prefer a new corrective commit.

Use pull request text for review context

A commit message and a pull request description do different jobs. The commit message must stand alone in Git history. The pull request description can carry the review plan, screenshots, test evidence, rollout notes, and links to the issue tracker.

Do not rely on the pull request alone for information that is essential to understanding the committed change. Pull request systems can change, but Git history remains the durable record.

Conclusion

Good commit messages are specific, verifiable, and useful after the immediate review is over. Keep commits focused, write a direct subject, add a body when the reason matters, and remove private context. A clear history makes debugging, review, and maintenance faster.