MUST, SHOULD and other key words are used as defined in RFC 2119 and RFC 8174.
- Goals
- Applicability and precedence
- Commit structure
- Commit contents
- Special commits
- AI-assisted commit messages
- Complete examples
- Reasoning
- References
- Author information
A commit message is part of the project's technical history. It should help a
contributor scanning the log, reviewing a change, investigating a regression or
using git blame understand the change without first reconstructing its purpose
from the diff.
A good commit message answers:
- Which part of the project changed?
- What behavior, capability or constraint changed?
- Why was the change needed, when that is not obvious?
The diff records how the change was implemented. The commit message records what changed and why.
This guide defines the baseline for foundata repositories. Rules from a specific project, upstream community or contribution ecosystem MAY extend this guide or override it where they conflict. The narrowest applicable rule takes precedence.
You MUST:
- Follow a repository's contribution rules when they differ from this guide, including when contributing to a repository outside foundata.
You SHOULD:
- Identify adopted or externally imposed rules in the repository's
CONTRIBUTING.md,DEVELOPMENT.mdor equivalent contributor documentation.
A policy does not automatically apply merely because a project uses the technology associated with it.
- Ansible Community projects and resources: The
Ansible Community Policy for AI-Assisted Contributions
applies to the public projects and communication channels listed by that
policy. Among other requirements, it recommends disclosing contributions that
substantially retain AI-generated output and permits an
Assisted-bycommit trailer. Individual Ansible projects MAY impose additional or stricter policies. A foundata project that uses Ansible but is outside the policy's stated scope is not covered automatically and MUST document the policy if it adopts it.
Normal commits use the Scoped Commits structure:
<scope>: <description>
[optional body]
[optional references and trailers]
You MUST:
- Write a subject using the
<scope>: <description>format.
You SHOULD:
- Add a body when the motivation, prior behavior, constraints or consequences are not clear from the subject.
- Put ticket references and trailers at the end.
You MAY:
- Omit the body and trailers when the subject contains all necessary information.
- Use the format generated by Git or the repository hosting software for merges, reverts and other special commits.
The subject appears without the body in places such as git log --oneline,
release comparisons and repository hosting interfaces.
You MUST:
- Summarize one logical change.
- Make the subject understandable without the body.
- Use the format
<scope>: <description>for normal commits.
You SHOULD:
- Keep the subject at 72 characters or fewer. A longer subject is acceptable when shortening it would remove essential meaning or make an identifier ambiguous.
You MUST NOT:
- End the subject with a period.
- Use a Conventional Commits type such as
feat,fix,refactor,choreordocsin place of the scope. - Use a ticket number as the scope.
The scope identifies the subsystem, component, package, service, command, documentation set or other stable area affected by the commit. It puts the information most useful when scanning history first.
You MUST:
- Choose a scope specific enough to distinguish the affected area from unrelated parts of the project.
You SHOULD:
- Use terminology contributors already use for the project.
- Write the scope in lowercase unless it contains an identifier whose spelling is case-sensitive.
- Choose a scope that remains stable when files are renamed or implementation details change.
- Choose the narrowest scope that remains useful to somebody reading the history.
- Use a cross-cutting scope such as
build,dependencies,releaseorrepositorywhen that is the actual subject of the change. - Use the shared subsystem or the component whose behavior is primarily changed when one logical change necessarily touches several components.
- Document recurring or potentially ambiguous scopes, for example in
DEVELOPMENT.md.
You MAY:
- Use
/for a meaningful hierarchy, such asapi/authordocs/python. - Use a name commonly treated as a Conventional Commits type when it identifies
a real project area. For example,
docs/pythonidentifies a documentation set.
You MUST NOT:
- Use a generic change type such as
feature,bugfix,cleanup,refactor,choreormiscas the scope. - Use a type-like scope merely to classify the change. For example, a bare
docsprefix is not a scope when it says only that documentation changed.
You SHOULD NOT:
- Require an exhaustive scope registry when ordinary project terminology is sufficient.
Good examples:
auth
scanner
pdf
cli
api/auth
docs/python
dependencies # Dependency version or lock file updates
release # Release preparation, e.g. "release: prepare 2.4.0"
repository # Repository-wide, non-code concerns: README, CONTRIBUTING.md, .gitignore, repo config
licensing # License files, headers, metadata such as REUSE/SPDX
build # Build tooling, packaging metadata, distribution artifacts
tests # Test suite or fixtures not attributable to one subsystem
Bad examples:
feat # Conventional Commits type, not a project area
fix # Conventional Commits type, not a project area
chore # Conventional Commits type, not a project area
docs # Classifies the change as documentation without saying which part of the project it documents
misc # Too vague to scan, filter or blame against
PROJ-123 # A ticket number is not a scope; put it in a trailer instead
The description states the behavioral or operational result of the commit. Prefer what the affected code can now do, what it prevents or what it guarantees over the edits used to produce that result.
You MUST:
- Make the description concrete enough to distinguish the change from other work in the same scope.
You SHOULD:
- Use the imperative mood, as in
reject,support,preserveorremove. - Begin with a lowercase letter unless the description starts with a proper name or case-sensitive identifier.
- Include the reason when it is both short and essential to understanding the result.
You MUST NOT:
- Merely say
update,improve,change,cleanup,various fixesor a similarly vague phrase without saying what changed. - Narrate implementation steps that are already apparent from the diff.
Good examples:
auth: reject expired refresh tokens
scanner: retry devices that are temporarily busy
pdf: preserve the OCR language in document metadata
foo: support Bar for legacy exports
dependencies: update pypdf to 6.0
Bad examples:
fix(auth): fix token bug
PROJ-123: update authentication
pdf: improve code
foo: add three switch statements and call the Bar helper
misc: various changes
You MUST:
- Separate the body from the subject with one blank line.
You SHOULD:
- Explain information that future contributors cannot reliably recover from the final diff.
- Write the body as prose or a short list when a list communicates distinct consequences more clearly.
- Mention implementation details only when they explain a design decision, compatibility constraint, surprising behavior or important tradeoff.
- Describe validation only when it provides useful information beyond the normal project checks, such as a manual hardware test or a reproduced production failure.
Useful context includes:
- The problem or prior behavior that motivated the change.
- Why this approach was chosen over a plausible alternative.
- Compatibility requirements, invariants or operational constraints.
- User-visible consequences, migration requirements or known limitations.
- Non-obvious risks and how they were controlled.
You MAY:
- Omit the body when the subject is complete or when there is no motivation or non-obvious decision to record. Empty ceremony is less useful than no body.
You MUST NOT:
- Repeat the subject in more words.
- List every changed file, function, conditional or test merely to prove that work was performed.
- Use the body as a table of contents for the diff. For documentation and configuration changes, do not enumerate the rules, sections or settings added.
- Claim motivation, test results or issue relationships that you have not verified.
You MUST:
- Use the
Token: valueform documented bygit interpret-trailersfor trailers intended for Git's trailer tooling.
You SHOULD:
- Put issue, ticket and review references at the end of the body rather than replacing the scope or description.
- Separate references and trailers from the preceding body with one blank line.
- Use the project-defined trailer name for external systems.
Jira-Ticket: PROJ-123
GitHub-Issue: #123
You MAY:
- Use an issue-closing directive as the final reference line when the hosting service supports it.
- Use standard trailers such as
Signed-off-by,Co-authored-byandReviewed-bywhen required by the project's contribution or review process.
For example:
Closes #123
Closes #123 is hosting-service syntax rather than a Git trailer because it has
no colon.
You MUST NOT:
- Use an issue-closing directive unless closing the issue is intended. Use a neutral issue trailer when the commit merely relates to the issue.
- Invent a person, review or sign-off.
A sign-off is a Signed-off-by: Name <email> trailer, not a cryptographic
signature. Its exact meaning is defined by the project; it often records that
the contributor created the work or has the right to submit it under the
applicable license and agrees to a
Developer Certificate of Origin. The
Linux kernel contribution process
is a prominent example.
Projects MAY:
- Require contributors to sign off commits or patches.
- Separately require cryptographic commit signatures.
You MUST:
- Follow the project's sign-off policy and understand the certification made by
its
Signed-off-bytrailer before adding one.
You SHOULD:
- Use
git commit --signoff, or its short formgit commit -s, when a sign-off is required.
You MUST NOT:
- Add a sign-off for another person or add one when you cannot make the project's required certification.
- Treat a
Signed-off-bytrailer as proof that Git cryptographically verified the commit or the contributor's identity.
Git intentionally provides no commit.signoff configuration setting because a
sign-off should remain a conscious act. The format.signOff setting affects
git format-patch only and is not a default for git commit. The
commit.gpgSign = true setting can cryptographically sign every commit, but it
does not add a DCO sign-off. See the
git commit documentation
and
Git FAQ
for the distinction.
You SHOULD:
- Make each commit atomic: represent one coherent change that can be reviewed, reverted and understood independently.
- Include directly required tests, documentation and changelog entries in the same commit as the behavior they describe unless the project deliberately uses a separate review workflow for them.
- Include generated output with the source change that produces it.
- Separate broad mechanical formatting or generated-file churn from a behavioral change when doing so makes both commits easier to review.
- Keep a refactoring with the behavior change when separating them would create artificial or broken intermediate states; split it when it is independently meaningful and makes the behavioral change clearer.
- Make each commit pass the checks relevant to its contents unless the repository explicitly preserves a series whose intermediate commits are not expected to stand alone.
- Fold local
fixup!andsquash!commits into the intended commit before merging unless the project deliberately preserves the review history.
You MAY:
- Use local
fixup!andsquash!commits while developing.
You MUST NOT:
- Combine unrelated behavior changes merely because they were developed at the same time.
You SHOULD:
- Retain Git's
Revert "..."subject for revert commits and identify the reverted commit. Add the reason for the revert when it is not obvious. - Use a scope such as
releasefor release preparation commits, for examplerelease: prepare 2.4.0. - Use the affected dependency or
dependenciesas the scope for dependency-only commits, for exampledependencies: update pypdf to 6.0. - Describe what an initial commit establishes, for example
project: establish the ScanMole application, rather than merely sayingInitial commit.
You MAY:
- Retain the subject generated by Git or the repository hosting software for merge commits.
You MUST:
- Take responsibility for the accuracy, relevance and wording of an AI-assisted commit message.
- Review generated text against both the diff and the reason the change was requested.
- Apply the same rules to AI-assisted messages as to manually written messages.
State these expectations explicitly in repository-level agent instructions
such as
AGENTS.md, or include them in the prompt. Suitable wording includes:- Describe the behavioral or operational result and, when useful, why it was needed.
- BE CONCISE. Omit the body when the subject is sufficient. Otherwise, use at most one tight paragraph for context and one for the resolution. Do not narrate or repeat the diff. For code comments, document only non-obvious intent or constraints. Treat user rewrites as templates for similar future messages and comments.
You MAY:
- Use AI to assist with drafting a commit message.
You MUST NOT:
- Narrate the diff. The diff shows how the change was implemented; the commit message should preserve what changed and why.
- Include inventories of files, functions, sections, rules, settings, branches, conditions, imports or helper calls.
- State that tests or documentation were added when that is routine and not the purpose of the commit.
- Make generic claims such as "improves maintainability", "enhances robustness" or "provides a seamless experience" without a concrete, verified meaning.
- Restate the same change in the subject, an introductory sentence and a bullet list.
- Infer motivation, ticket relationships, compatibility claims or test results without evidence.
Good example:
foo: support Bar for legacy exports
Legacy exports require Bar values to retain their original identifiers.
Bad example:
foo: enhance Bar handling
Extended foo() by adding three new switch branches, calling the Bar helper inline and updating the related imports. Added comprehensive tests for the new functionality.
An implementation detail belongs in the message when it is itself significant. For example, naming a database migration strategy, wire-format change or compatibility workaround can be essential because it explains constraints that the diff alone does not preserve.
Bug fix with context and references:
auth: reject expired sessions during login
Expired sessions were accepted until the first authenticated request, which made a failed login appear successful. Reject them during login so clients receive the authentication error immediately.
Jira-Ticket: PROJ-123
Closes #123
Feature whose reason is not obvious:
scanner: retain pages after an OCR failure
Keep the scanned image so an operator can retry OCR without rescanning the original document. The failed page remains excluded from the generated PDF until OCR succeeds.
Small change that needs no body:
cli: show the SANE device name in scan errors
Breaking change:
api/auth: require an explicit token audience
Reject tokens without an audience instead of accepting the server hostname implicitly. Deployments must configure the expected audience before upgrading.
Jira-Ticket: SEC-418
Scope comes before description because contributors, reviewers and incident responders usually scan history for changes to a particular subsystem. Whether a commit was labeled a feature, fix or refactor is less useful and is normally already apparent from a concrete description.
Conventional Commits prioritize a change-type classification that is frequently ambiguous and redundant. A single coherent change can simultaneously add a capability, fix behavior and refactor its implementation. Requiring one type loses information while moving the stable subject of the change out of the most prominent position.
Commit messages should not duplicate diffs. File names and control flow can be inspected directly and change as commits are rebased; motivation, rejected alternatives and intended behavior often cannot be recovered later. Recording what and why makes the history useful without filling it with mechanical narration.
Commit logs are developer-facing history, while changelogs are user-facing release documentation, so they remain separate: several commits may form one release note, internal or reverted commits may need none, and a commit category cannot determine compatibility or semantic versioning. Most foundata projects use Keep a Changelog, while Ansible projects use antsibull-changelog where appropriate; user-visible changes update the selected workflow with text written for users. Build, test and release automation uses changed files and explicit release configuration instead of commit-message labels.
This guide was written by foundata to make Git history
concise, scoped and useful to maintainers. It is informed by
Scoped Commits,
Stop Using Conventional Commits
and Git's
git interpret-trailers
documentation.