Skip to content

Instantly share code, notes, and snippets.

@yudhasetiawan
Last active September 20, 2016 04:04
Show Gist options
  • Save yudhasetiawan/55318bcaa4590053ce33c5e09e09b5f9 to your computer and use it in GitHub Desktop.
Save yudhasetiawan/55318bcaa4590053ce33c5e09e09b5f9 to your computer and use it in GitHub Desktop.
Modification version of some commons best practice commit message guidelines to improve readability.

Commit Message Guidelines

The commit message plays an important role in revision control systems. They are the first thing other people will see of your commit. Git and related tools work best when following a certain guideline for commit messages. A deeper introduction on git revision log conventions is helpful to understand the scope. Commit messages should describe what changed, and reference the issue number if the commit closes or is associated with a particular issue.

This leads to more readable messages that are easy to follow when looking through the project history. But also, often commit messages will be used to generate a change log for releases of an application, plugin or code base.

Having commit messages that follow this formatting will prepare your code for distribution, and make formatting consistent.

Goals

  • Automatic generating of the changelog.
  • Provide more information when browsing the history (eg. ignoring type of changes).
  • Recognizing unimportant commits.
  • Format of the commit message.

Structural split of changes

The cardinal rule for creating good commits is to ensure there is only one logical change per commit. There are many reasons why this is an important rule:

  • The smaller the amount of code being changed, the quicker and easier it is to review and identify potential flaws.
  • If a change is found to be flawed later, it may be necessary to revert the broken commit. This is much easier to do if there are not other unrelated code changes entangled with the original commit.
  • When troubleshooting problems using Git's bisect capability, small well defined changes will aid in isolating exactly where the code problem was introduced.
  • When browsing history using Git annotate/blame, small well defined changes also aid in isolating exactly where and why a piece of code came from.

Source: Open Stack

Structure

A commit message consists of 3 parts - The header (short description), the body (long description) and the footer (issue reference). There should have an empty line between each section. The format of the commit shall be composed of the following:

  • A first line as a header (with type, optional scope, and a subject).
  • An optional multi-line message as a body (always preceded by a blank line).
  • An optional footer area (always preceded by a blank line).

Any line of the commit message cannot be longer 100 characters! This allows the message to be easier to read in various git tools. Every commit must have a header (the first line). Everything else is optional.

Quick summary:

<header>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

A detailed commit message might look like this:

Type(Scope): Capitalized, short (50 chars or less) summary

If a code change can be split into a sequence of patches/commits,
then it should be split. Less is not more. More is more.

Most importantly, describe what is changed with the commit and not
what is has not been working (*that is part of the bug report
already*).

More detailed explanatory text, if necessary. Wrap it to 72 characters.
The first line is treated as the subject of the commit message and
the rest of the text as the body. The blank line separating the subject
from the body is critical (unless you omit the body entirely); tools
like git rebase can get confused if you run the two together.

Write your commit message in the ***imperative present tense***
("Fix bug", not "Fixed bug"). This convention matches up with generated
commit messages by commands like git merge and git revert.

Help others to understand what you did (Motivation for the change?
Whats the difference to the previous version?), but keep it simple if
possible.

The commit message solely describes ***what is changed***.

- Bullet points are okay
- Typically a hyphen (-) or asterisk (*) is used for the bullet,
  followed by a single space, with blank lines in between, but
  conventions vary here
- Use a hanging indent

Closes: #123456
References: #12345
Releases: master, 1.2.3

Header

The first line of the commit message is known as the header. It describes the change briefly and helps reviewers to see at a glance what the commit is about. So it is the most critical. To make it very clear and easy to scan and filter, the header has a special format that includes a type, a scope and a subject.

  • The type tag is mandatory.
  • Optionally follow it with the changed scope that affected.
  • The subject as a summary description is mandatory.
  • Since we use type and scope in the first line (header) it is really hard to limit to 50 characters, so limit the header to 72 characters by remove optional patterns (e.g. scope tag).
  • Use triple exclamation mark (!!!) as a prefix of the header followed by the type only if this changes contain breaking changes.

Format of the commit header:

!!!Type(Scope): Capitalized, short (50 chars or less) summary
^-^^--^ ^---^ ^ ^-------------------------------------------^
|  |    |     | |
|  |    |     | +-> Summary description of the change in present tense (required).
|  |    |     |
|  |    |     +---> A single colon as separator (required).
|  |    |
|  |    +---------> The scope indicating what area of the code is affected by this commit (optional).
|  |
|  +--------------> This describes the kind of change that this commit is providing (required).
|
+-----------------> A flag that indicate this changes contain **breaking changes**

A detailed commit header might look like this:

Feature: Add option to hide search box in list view
Fix(Http): Throw HttpStatusException in Controller
!!!Task: Replace Foo API with new approach
!!!Feature(Controller/Login): Implement new login form service

Note:

  • The very first commit of a project is often the lone exception to these rules, and is conventionally "Initial Commit".
  • Breaking Changes use case. The triple exclamation mark (!!!) prefix is added at the very beginning of the line, so it doesn't get overlooked.
  • Deprecations must not use the Breaking Changes prefix (!!!)
  • Deprecations may only be of type Chore or Feature.
  • The relationship between various types of commits and the expectation of a corresponding change in Semantic Versioning version number:
    • A breaking changes commit should accompany a change in the Major version number.
    • A Feature commit should accompany a change in the Minor version number.
    • A commit of any other type should accompany a change in the Patch number.

Type

This describes the kind of change that this commit is providing.

  • It should always be CamelCase.
  • Optionally it should use abbreviations.

Must be one of the following:

Type Description
Feature A new feature (also small additions). Most likely it will be an added feature, but it could also be removed.
BugFix A fix for a bug.
Doc A changes of documentation only.
Refractor A code change that neither fixes a bug nor adds a feature.
Performance A code change that improves performance.
Security Visualizes that a change fixes a security issue.
Style This tag may be used for changes that do not alter the logic of code, but try to improve code style and readability (e.g. white-space, coding guideline compliance, comment improvements, semi-colons, etc).
Test Adding missing tests or correcting existing tests.
CI Changes that affect CI configuration files and scripts (e.g. Travis, SauceLabs, etc).
Package Changes that affect the build system or external dependencies (e.g. PHP Composer, Node NPM, Ruby Rake*, etc).
Chore Other changes that not covered by the above categories. In general almost all commits should fall into one of these categories (e.g. Refactoring of a scope).

Often a change will include more than one type. Use the type highest on this list that applies. If desired, you can list multiple types separated by a vertical bar (|). This is generally only necessary when the types are orthogonal in nature. For example, a new feature (Feature type) will often also include new documentation (Doc) and tests (Test). In this case, simply state the type as Feature.

Scope

You may suffix the type with a scope indicating what area of the code is affected by this commit. The scope could be anything specifying element of the commit change that affect. For example Parser, Resolver, DI, Service, etc.

When a project is large, it is often composed of distinct subsystems or areas. The nature of this division and the names themselves are project-specific; and decided on by the author(s). The key is consistency - be sure to have a definitive list of the various scopes somewhere in the project notes so that all contributors are aware of them. Adding a new scope should be a considered decision, not just a whim when making your commit.

Case (upper/lower) is up to the authors - again, just be consistent. When scope correlates with an existing org unit, it may be useful to use the same case as the element it corresponds to. Additionally, not every commit to projects that do have scopes will necessarily specify a scope. If the commit is not limited or relevant to a single scope, leave it out.

  • Scopes are denoted by brackets (()).
  • Optionally it should be CamelCase.
  • It should not contains any whitespace between type and scope.
  • Avoid abbreviations if possible.
  • Treat acronyms as words in names (XmlHttpRequest not XMLHTTPRequest), even if the acronym is the entire name (Html not HTML).
  • Multiple scope names should reordered by priority of the code is affected by this commit and separated by vertical bar (|), but try to avoid this.

It should be one of the following:

  • Group of directory structures.
  • Module name.
  • Class name.
  • Keyword affecting multiple areas relating to the type of change (e.g. Doc, Test, Build, etc).

Subject

Bear in mind that many interfaces use the subject to identify a commit. The subject contains summary description of the change.

  • Since this is a title (not a paragraph) it should not end in a period/full stop, but should begin with a capital letter.
  • Use the imperative present tense (e.g. "Change foo to bar"; not "Changes foo", "Changing foo", nor "Changed foo").
  • Keep the whole line below 50 characters if possible, but below 72 characters including a type and a scope.
  • Separate the type and/or scope from the subject with a single colon (:) followed by a single space.

Common use cases:

  • E-mail notification, IRC notification, Search results
  • Commit subject
  • Commit history, Commit subject
  • Release notes

In all these cases the subject is rendered as plain text. Avoid using references in the subject as they have no meaning in plain text. Instead mention them in the body text and/or as key-value pairs in the footer.

Revert

If the commit reverts a previous commit, the header should prefix with Revert: , followed by the type, scope and subject of the reverted commit. In the body it should say: This reverts commit <hash>., where the hash is the SHA of the commit being reverted.

  • It should not wrap the line if the whole line above 72 characters including a Revert flag.
  • Separate the Revert flag and the previous commit with a single colon (:) followed by a single space.
  • The previous commit should be wrapped with double quotes (:).

Format of the reverted commit header:

Revert: "Type(Scope): Capitalized, short (50 chars or less) summary"
^----^^ ^^--------------------------------------------------------^^
|     | ||                                                         |
|     | |+-> Previous commit                                       |
|     | |                                                          |
|     | +--> Double quotes is mandatory <--------------------------+
|     |
|     +----> Separator is mandatory if the description provided.
|
+----------> Revert flag

Body

The body should include the description of the change and contrast this with previous behavior.

  • Not mandatory but helps explain what you're doing.
  • Separate the body from the header with an empty line.
  • Give an overview of why you're committing this changes.
    • What the commit changes.
    • Any new design choices made.
    • Areas to focus on for recommendations or to verify correct implementation.
    • Any research you might have done about this changes.
  • Bytes are cheap, so just write! If possible keep it simple and don't repeat information that is already part of the issue tracker. Especially avoid "How to reproduce" part. At most, try to explain the change itself, if it is not already clear by reading the difference. Do not repeat the code change itself in the body text.
  • Try to make your commit message understandable without shortcut external resources (e.g., instead of just giving a URL to a mailing list archive, summarize the relevant points of the discussion and then provide your reference URL).
  • Wrap the lines after 72 characters manually if possible, but below 100 characters in any case to allows the message to be easier to read in various git tools.
  • Write your commit message in the imperative present tense ("Fix bug" and not "Fixed bug"), since a commit is a set of instructions for how to go from a previous state to the new state and the commit message should describe this process. This convention matches up with generated commit messages by commands like git merge and git revert.
  • Don't break the URL. If a URL is too long, give it its own line, or try to use URL shortener tools. Optionally it should be named.

Breaking Changes

All breaking changes have to be mentioned as a breaking change block in the body, which should start with the word BREAKING CHANGE with a space and the tag !!! is mandatory as a prefix of the header. The rest of the commit message is then the description of the change, justification and migration notes. The breaking change has a special tag that includes Before and After.

  • Optionally it should provide tag Before and After for the code that affect this changes.
  • If those tags (Before and After) is used, it must wrapped by triple hyphens (-).
  • If tag Before is used, then tag After is mandatory.

Example usage:

BREAKING CHANGE: The foo option was replaced by a bar option.
---
Before:
scope: {
    attribute: 'attribute',
    foo: 'foo'
}

After:
scope: {
    attribute: 'attribute',
    bar: {
        baz: "quux"
    }
}
---

Footer

The footer should contain any information of the changes and is also the place to reference issues that this commit resolves. Whenever you refer to another commit, use the SHA-1 of the merged commit, or (if the change is still pending review) use the Change-Id hash (avoid using url), because the SHA1 relates to an individual patch set (a reference that will be a dead-end once merged).

  • The space after the colon (:) is mandatory.
  • Multiple values should be separated by comma (,) followed by a single space.

External References

External references to issues or pull requests go after the long description, each one on their own line.

Change-id

The Change-id line is a unique hash describing the change, which is generated by a Git commit hook. This should not be changed when rebasing a commit following review feedback, since it is used by Gerrit, to track versions of a patch.

Closes

Use Closes when the commit closes an open pull request.

Closes: #123, #234, #345

Fixes

Use Fixes when the commit fixes an open issue.

Fixes: #123, #234, #345

Partial

Use Partial if the commit is only a partial fix and more work is needed.

Partial: #123, #234, #345

References

Use References when referencing an issue or pull request that is already closed or should remain open (e.g. include partial fixes and commits that add a test but not a fix).

References: #123, #234, #345

Releases

Use Releases when referencing to the branches for which this change will apply.

Releases: master, 1.2.3

DocImpact

The DocImpact line contains the string DocImpact, and a comment about why the change impacts documentation. Put DocImpact on a line by itself. Use this flag to indicate that documentation is either contained in the patch or the change causes documentation to be needed to understand the change. Include as much information as possible.

SecurityImpact

The SecurityImpact line simply contains the string SecurityImpact. It is used to indicate that a change has security implications and should be reviewed by the project owner.

Sign your work

The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (Developer Certificate):

Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.

then you just need to add a line to every git commit message:

Signed-off-by: Joe Smith <joe@gmail.com>

using your real name (sorry, no pseudonyms or anonymous contributions.)

You can add the sign off when creating the git commit via git commit -s.

Why Do We Need Good Commits?

  • An atomic commit is way easier to review. The reviewer thus will be able to review faster and find more bugs due to the lower complexity of the change.
  • Atomic commits are like good objects in object oriented programming - you can split up a bigger thing into many small objects. Reducing complexity is the key to developing good software and finding its bug before they occur.
  • Good commit messages make it easy to check at a glance what happened in a time range.
  • It is way easier to revert single changes without side effects. Reverting multiple commits at a time is easy, reverting a part of a commit is not.
  • git blame will be much more effective. It is the best documentation you can get. The older your code is, the more documentation it has. The better the commit messages are, the better is your hidden documentation. Your commit messages document the reason for every single change you did to any line.
  • git bisect will be much more effective. If you bisect through atomic commits to find the commit which caused a bug, you should be able to identify the real cause of the bug fastly. Good commit messages and atomicity of commits are key to that ability. Next Previous

License

This work is licensed under the Creative Commons Attribution 4.0 International License.

To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment