Git Commit Conventions¶
How to write commit messages that are clear, searchable, and team-friendly. Commands: Git Command Set; local checks: Gitignore & Hooks.
See also: Git Command Set · Gitignore & Hooks · Collaboration
How to read this page¶
- Concepts — parts of a message and why conventions help
- Overview — common formats, type table, related commands
- Scenarios — real patterns with good/bad examples
- Symptom lookup — at the bottom
What a commit message is¶
Every git commit stores a message: a subject (usually line 1), optional body, and optional footers (trailers).
In plain terms: “Label this change so future-you and teammates know what changed and why.”
Effect: Messages live in history forever—git log, PRs, changelogs, and release tools all read them. Quality directly affects review and debugging.
Overview¶
Common format patterns¶
| Style | Best for | Example subject | Scenario |
|---|---|---|---|
| Imperative one-liner | Solo repos, tiny changes | Fix login redirect loop | Minimal bar |
| Conventional Commits | OSS, teams, automated changelog | feat(auth): add OAuth callback | Conventional |
| Subject + body | Complex changes, explain why | subject + blank line + paragraphs | Multiline |
| Breaking change | Incompatible API changes | feat!: drop Node 16 or footer | Breaking |
| Trailers | Credit, co-authors, issues | Closes #42 | Trailers |
Conventional Commits is widely used (Angular, Semantic Release, etc.). Teams may subset it (e.g. type: without scope)—clarity beats rigid compliance.
Conventional Commits: type cheat sheet¶
| type | Meaning | Example subject |
|---|---|---|
feat | New feature | feat: add export to CSV |
fix | Bug fix | fix: handle null user id |
docs | Documentation only | docs: update install steps |
style | Formatting, no logic change | style: format with prettier |
refactor | Refactor, not feat/fix | refactor: extract auth middleware |
perf | Performance | perf: cache config parse result |
test | Tests | test: add login API cases |
build | Build / dependencies | build: bump webpack to 5.94 |
ci | CI config | ci: add matrix for Node 20/22 |
chore | Maintenance | chore: update gitignore |
revert | Revert a commit | revert: feat(auth): add OAuth |
Optional scope: feat(api): ..., fix(ui): ....
Breaking changes: ! after type (feat(api)!: ...) or BREAKING CHANGE: in footer.
Git commands used in this doc¶
| Command | Job | Scenario | Command set |
|---|---|---|---|
git commit -m '...' | One-line message | Minimal | git commit |
git commit (no -m) | Editor for multiline | Multiline | git commit |
git commit --amend | Edit last message | Amend | git commit |
git commit --amend --no-edit | Add files, keep message | Amend | git commit |
git revert <hash> | Undo via new commit | After push | git revert |
git log --oneline | Scan subjects | Finding hash for revert | git log |
git commit --no-verify | Skip commit hooks | Hooks doc | git commit |
Subject-line principles (language-agnostic)¶
| Principle | Guideline |
|---|---|
| Mood | Imperative (Add feature not Added feature) |
| Length | ~50 characters on subject (soft limit; use body for detail) |
| Focus | What changed; why in the body |
| Punctuation | Usually no trailing period on subject |
| Scope | One logical change per commit; avoid WIP on main |
Scenario: minimal bar when there is no team standard¶
Typical uses: Personal repos, teams without CONTRIBUTING yet.
In plain terms: “Subject should explain the change in five seconds.”
# Good: specific verb + context
git commit -m "Fix crash when profile avatar is missing"
# Bad: too vague
git commit -m "update"
git commit -m "fix bug"
git commit -m "WIP"
# Bad: issue number alone
git commit -m "#123"
Scenario: Conventional Commits¶
Typical uses: Open source, automated changelogs, PR titles aligned with history.
git commit -m "feat(billing): support annual subscription"
git commit -m "fix: prevent double submit on checkout"
git commit -m "docs: add API rate limit section"
git commit -m "build: upgrade eslint to 9.x"
Effect: Tools can classify feat vs fix for releases; reviewers filter git log by type.
Note
Before contributing, read that repo’s CONTRIBUTING.md, PR template, or git log --oneline -20—match existing style first.
Scenario: multiline subject + body¶
Typical uses: Refactors, behavior changes—explain why, not a file list.
git commit -m "$(cat <<'EOF'
fix(auth): reject expired refresh tokens
Previously expired tokens still passed middleware when
clock skew was under 30s. Align with OAuth spec and
return 401 with clear error code.
Refs #8842
EOF
)"
Or use the editor:
Effect: git log shows subject; git show reveals body and footers.
Scenario: fix the last message (not pushed yet)¶
Typical uses: Typo in subject, forgot git add a file.
git commit --amend
git commit --amend -m "feat: add password reset email"
git add forgotten.ts
git commit --amend --no-edit
In plain terms: “Last commit still local—you may rewrite it.”
Effect: Commit hash changes—only safe if not pushed to a shared branch.
Warning
Do not amend commits already on shared remote branches unless the team explicitly allows it and coordinates force push. See After push.
Scenario: already pushed to the remote¶
Typical uses: Wrong message or need to undo, but commit is on origin/main.
| Situation | Prefer | Example |
|---|---|---|
| Message wrong, code OK | New commit or team-approved process | Avoid empty “fix message” commits unless policy allows |
| Undo the whole change | git revert (history preserved) | git revert abc1234 |
| Solo feature branch, no dependents | rebase/amend + push --force-with-lease | See Collaboration |
In plain terms: “Published history gets new commits, not erased rewrites.”
Scenario: breaking changes¶
Typical uses: Remove API, change defaults, major version bumps.
git commit -m "$(cat <<'EOF'
feat(api)!: remove legacy /v1 users endpoint
BREAKING CHANGE: clients must use /v2/users. v1 removed
after deprecation period noted in 2025-Q4 changelog.
EOF
)"
git commit -m "feat: switch default TLS to 1.3
BREAKING CHANGE: TLS 1.0/1.1 no longer supported."
Effect: Release tools may bump major version; users search logs for BREAKING CHANGE.
Scenario: trailers and issue links¶
Trailers are structured footer lines (Key: Value), common in OSS and GitHub/GitLab.
| Trailer | Purpose | Example |
|---|---|---|
Closes #123 / Fixes #123 | Close issue on merge | GitHub automation |
Refs #123 | Link only | Partial work |
Co-authored-by: Name <email> | Co-author credit | Pair programming, squash |
Signed-off-by: Name <email> | DCO sign-off | Many foundation projects |
Reviewed-by: Name <email> | Record review | Some enterprise policies |
git commit -m "$(cat <<'EOF'
fix: validate upload MIME type
Co-authored-by: Alex Chen <alex@example.com>
Closes #456
EOF
)"
Typical uses:
- Squash merge combining multiple authors’ trailers
- IDE / AI tools auto-inserting footers—review before commit and drop lines the project forbids
Note
Allowed trailers depend on the target repo’s CONTRIBUTING / DCO—this page teaches common patterns, not project policy.
Scenario: what lands after a PR merge¶
Typical uses: GitHub/GitLab merge produces merge or squash commits.
| Merge style | Typical log | Message tip |
|---|---|---|
| Merge commit | Merge pull request #99 ... | Branch commits preserved |
| Squash merge | Single feat: ... (#99) | PR title often becomes final subject—write it well upfront |
| Rebase merge | Linear commits from branch | Each commit should stay atomic |
In plain terms: “On squash merge, PR title ≈ future commit subject.”
Scenario: working with commit-msg hooks¶
Teams validate format in a commit-msg hook (required prefix, non-empty subject). Examples: Gitignore & Hooks — commit-msg.
Effect: Bad messages fail locally before push.
Warning
Do not habitually use git commit --no-verify; fix the message or follow team exceptions.
Scenario: good vs bad examples¶
| Bad | Good | Why |
|---|---|---|
update code | fix(ui): prevent modal focus trap | Specific, searchable |
fix bug and add feature | Split into two commits | One intent per commit |
Fixed the thing. | fix: handle timeout in payment webhook | Imperative, no period |
| Body is 200 lines of diff | Body explains why and trade-offs | Diff is in git show |
feat: mixed with unrelated deps bump | Separate build: commit | Easier revert / bisect |
Quick lookup: symptom → suggestion¶
| Symptom | Suggestion |
|---|---|
| Blank on what to write | Subject: what + where; body: why |
| Change spans many modules | Split commits or pick primary scope |
| Forgot a file after commit | Not pushed → git add + commit --amend --no-edit |
| Typo on pushed main | No amend on main; revert or follow-up |
| Project has CONTRIBUTING | Follow project docs; this page is general reference |
| Hook rejects commit | Read error, fix format; see hooks doc |
| Co-authors needed | Use Co-authored-by; align with platform merge docs |
More commands: Git Command Set — git commit.