Image of Git Commit Messages: Best Practices & Guidelines

ADVERTISEMENT

Table of Contents

Introduction

So you’ve been working on your git repository for a while, and now it is in a state that you wish to record. You’ve already added your modified files to the staging area and are ready to commit.

But how does one write commit messages? What is a commit message anyway? What are the rules for a great Git commit message style?

This article will answer those questions and more, in an easy to follow and practical way. Let's take a closer look.

Background

Every time you perform a commit, you’re recording a snapshot of your project that you can revert to or compare to later.

— Pro Git Book, (1)

This article is about effective commit messages using the Git version control system. Git is one of the top source control management tools. If you are new to Git, then please consider reading these excellent introductions:

  1. Introduction to version control with Git
  2. Beginner Git Cheat Sheet

What is a Commit Message?

A commit message is descriptive text that is added to the commit object by the developer who made the commit. It has a title line, and an optional body.

Let's print out the details of an existing commit using the git show command to demonstrate the anatomy of a commit message:

1 $ git show --pretty=fuller -s HEAD
2 commit 39d008dd5239acd93b3719918c1fe2ebc2bc46al (HEAD -> ACME-1_add_logging, origin/ACME-1_add_logging)
3 Author: 	Daenerys Targaryen <daenerys.targaryen@acme.com>
4 AuthorDate: Thu Mar 24 16:26:17 2022 -0400
5 Commit: 	Daenerys Targaryen <daenerys.targaryen@acme.com>
6 CommitDate: Thu Mar 24 17:15:13 2022 -0400
7
8	[ACME-1] Add Logging
9
10	Add logging to controller so that access analysis is possible

Note that we used the --pretty=fuller option to show additional details. We can see that the commit is made up of an alphanumeric commit ID, author details, a commit date, and the commit message itself. Line 8 above shows the commit message title. Line 10 shows the optional commit message body.

The message conveys what changes the commit makes at a high-level, the motivation for making the changes, and any links to detailed external references.

Why Should you Write Good Commit Messages?

Getting in the habit of creating quality commit messages makes using and collaborating with Git a lot easier.

— Pro Git Book, (1)

You might think that you can get away with any old commit naming convention. This could work for a while, but what about after you've added hundreds of commits, or 12 months have passed, or you have to work with someone else's commits? Spend some time putting the right information in commit messages now, and it could really pay off later when you need to remember what you worked on, or share it with other developers.

Commit messages are used in many ways including:

  1. To help a future reader quickly understand what changed and why it changed
  2. To assist with easily undoing specific changes
  3. To prepare change notes or bump versions for a release

All three of these use cases require a clean and consistent commit message style.

General Commit Message Guidelines

As a general rule, your messages should start with a single line that’s no more than about 50 characters and that describes the changeset concisely, followed by a blank line, followed by a more detailed explanation.

— Pro Git Book, (1)

The same recommendations apply whether you are working on a Github commit, Gitlab commit, or your local git server. Follow these guidelines when writing good commit messages:

  1. Keep it short (less than 150 characters total)
    • Committing fewer changes at a time can help with this
  2. Use the imperative mood
    • This convention aligns with commit messages generated by commands like git merge and git revert
    • Consistency enhances speed of reading comprehension
    • Tends to be more concise than the other moods
  3. Add a title
    • Less than 50 characters
    • Use Title case (i.e. "Add Logging" instead of "add logging")
  4. Add a body (optional)
    • Less than 100 characters
    • Explain WHAT the change is, but especially WHY the change was needed
    • Leave a blank line between the title and body
    • Separate paragraphs in the body with blank lines
    • Use a hyphen (-) for bullet points if needed
    • Use hanging indents if needed

Bad commit examples:

debugging
I've added a delete route to the accounts controller

Good commit Examples:

Enable Logging Globally
Add Account Delete Route

Needed for account deletion workflow on frontend

Ways to Specify Commit Message in Git

Let's take a quick aside to talk about the two main ways to commit, plus a few options.

Git Commit Message Command-Line Method

The -m option allows the title message to be added directly from the command line.

git commit -m "Add Account Delete Route"

This can be convenient. Use the following method if you want to add a body.

Text Editor Method

If the -m option is omitted, then Git will open the default text editor.

You can view the default Git text editor using the git config command:

git config --global core.editor

Set the Git default text editor:

git config --global core.editor "atom --wait" // Atom
git config --global core.editor "code --wait" // VS Code
git config --global core.editor "subl -n -w"  // Sublime Text
git config --global core.editor "mate -w" 	// TextMate
git config --global core.editor "pico"    	// pico
git config --global core.editor "vim"     	// vim

When the editor opens it will look like this:


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch ACME-1_add_logging
# Your branch is up to date with 'origin/ACME-1_add_logging'.
#
# Changes to be committed:
#   	modified:   app/controllers/base_controller.rb
#

Note: Lines beginning with # are treated as comments and are ignored.

The hint comments are provided to remind you of what you are committing.

Type your message and then save to exit the editor and return to the command line. This option allows you to include a message body in your commit message.

Other Git Commit Message Options

Run git commit --help for additional options. Shown here are those options related to commit messages:

-C <commit>, --reuse-message=<commit>
  Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit.

-c <commit>, --reedit-message=<commit>
  Like -C, but with -c the editor is invoked, so that the user can further edit the commit message.

--fixup=<commit>
  Construct a commit message for use with rebase --autosquash. The commit message will be the subject line from the specified commit with a prefix of "fixup! ". See git-rebase(1) for details.

--squash=<commit>
  Construct a commit message for use with rebase --autosquash. The commit message subject line is taken from the specified commit with a prefix of "squash! ". Can be used with additional commit message options (-m/-c/-C/-F). See git-rebase(1) for details.

-F <file>, --file=<file>
  Take the commit message from the given file. Use - to read the message from the standard input.

-t <file>, --template=<file>
       	When editing the commit message, start the editor with the contents in the given file. The commit.template configuration variable is often used to give this option implicitly to the command. This mechanism can be used by projects that want to guide participants with some hints on what to write in the message in what order. If the user exits the editor without editing the message, the commit is aborted. This has no effect when a message is given by other means, e.g. with the -m or -F options.

--allow-empty-message
  Like --allow-empty this command is primarily for use by foreign SCM interface scripts. It allows you to create a commit with an empty commit message without using plumbing commands like git-commit-tree(1).

--cleanup=<mode>
       	This option determines how the supplied commit message should be cleaned up before committing. The <mode> can be strip,
       	whitespace, verbatim, scissors or default.

       	strip
           	Strip leading and trailing empty lines, trailing whitespace, commentary and collapse consecutive empty lines.

       	whitespace
           	Same as strip except #commentary is not removed.

       	verbatim
           	Do not change the message at all.

       	scissors
           	Same as whitespace except that everything from (and including) the line found below is truncated, if the message is to
           	be edited. "#" can be customized with core.commentChar.

               	# ------------------------ >8 ------------------------

       	default
           	Same as strip if the message is to be edited. Otherwise whitespace.

       	The default can be changed by the commit.cleanup configuration variable (see git-config(1)).

-e, --edit
  The message taken from file with -F, command line with -m, and from commit object with -C are usually used as the commit log message unmodified. This option lets you further edit the message taken from these sources.

--no-edit
  Use the selected commit message without launching an editor. For example, git commit --amend --no-edit amends a commit without changing its commit message.

Viewing your Commit Message

You can use the git show command to view the commit message for a specific commit.

Show short description for last commit:

git show -s

Show short description for particular commit:

git show -s c27dce0210092a828de53b11bc676865c5ce17a2

You can view several commit messages at once using the git log command.

Common options:

-p            	Show patch
-<n>          	Limit output to n commits. Example -2
--stat        	Show file change statistics
--pretty=oneline  Display each commit as one line of output

Guidelines for "Initial Commit" Messages

An initial commit is the first commit that a developer makes in a Git repository. Every Git repository has one and only one initial commit. By definition, an Initial Commit has no parent commit(s).

According to our research on Initial Commit messages, 98% of initial commit messages on Github are some variant of the text "initial commit"

.

However, all initial commits include some file content, even if it is just a README.md file. Therefore, the best practices mentioned above still apply. Most developers don't think about it too much and use the conventional text "initial commit" or "first commit".

Here are some alternatives you may want to consider:

Add README.md
Initialize Project

- Add README.md
- Generate Rails scaffolding
- Add .gitignore

Semantic Commit Messages

You may have heard the term semantic commit message from a colleague or article and are wondering what it means.

A semantic commit message is one that follows a structured pattern, and includes enumerated values for several fields. The intention is to convey additional meaning in a concise way. This is especially useful for machine-readable commit messages and task automation.

There are several implementations. Here is Josh Buchea's implementation.

Another implementation is Conventional Commits, which we'll briefly touch on next.

Conventional Commits Specification

Conventional Commits is a well supported implementation of semantic commit messages. Tools in the ecosystem include Bumped and Commitizen.

Learn more at conventionalcommits.org.

Git Commit Message Rules

There are very few hard and fast rules when it comes to commit messages, but some things should never be violated:

  1. Do NOT include any secrets such as passwords or TLS keys in the message.
  2. Do NOT include anything that you wouldn't want published in a newspaper. Usually messages can be read by others.
  3. Avoid massive sizes. There is no practical limit to how much text may be stuffed into the message, however, a 100MB message would absolutely destroy performance.

Contributing to Existing Projects (Project Specific Guidelines)

Many projects have existing guidelines that must be followed, often described in a CONTRIBUTING.md file. Project specific guidelines ALWAYS take priority, unless you want your pull request to be rejected! :)

Teams with Git Ticketing Systems

Many teams use ticketing systems, such as Jira, that assign a ticket number to a particular piece of work. How should we include references in commit messages? It is best practice to include the ticket number in the commit message as a reference.

Example for Jira ticket "ACME-1":

[ACME-1] Enable Logging Globally

Changing Existing Commit Messages

Oops! You've made a mistake in a commit message and now you need to fix it! Luckily, git provides several commands to rewrite history, such as git amend, Git rebase, and filter-branch.

Just be sure to heed these words of wisdom:

Be careful, because you can’t always undo some of these undos. This is one of the few areas in Git where you may lose some work if you do it wrong.

— Pro Git Book, (1)

Automatically Enforce Commit Message Format

Git commit message formats, and many other things, may be enforced using Server Side Hooks. Specifically, the update hook, which requires the following 3 parameters and executes for each pushed branch:

  1. The ref you are pushing to
  2. The old revision specifying that branch
  3. The new revision you are pushing

Here is a minimal example that checks for a ticket reference in the message (i.e. starts with [ACME-100] or similar):

#!/bin/bash

set -o errexit # fail script if command fails. add "|| true" to commands that you allow to fail
set -o nounset # exit if undeclared variables are used
#set -o xtrace  # trace that which has been executed. uncomment for debugging

refname=$1
oldrev=$2
newrev=$3

git rev-list ${oldrev}..${newrev} | while IFS=$'\n' read rev; do
  message=$(git show -s --format=%B ${rev})
  echo $message
  [[ "$message" =~ ^\[[A-Z]+\-[0-9]+\] ]] ||  exit 1
done

This would be applied by uploading it to the hooks directory on your Git server. Directories vary by server.

Git Commit Message Tips

  1. Use the hint comments in editor mode (stripped out but are shown for reference).
  2. Use git commit -v to show the diff at the bottom of the commit message template to help you describe the commit by reminding what changes the commit has.
  3. You can skip the staging area with git commit -a. Note that this ignores untracked files.

5 Steps to Write Better Commit Messages

  1. Limit the Git commit subject
    • Commit more often to keep the size down
    • Use options such as git add --patch <filename> to stage only what you need
  2. Convey WHAT changed and WHY it changed in a concise way
  3. Use a Git Commit Message Template
    • Reach consensus with other contributors
    • Consider some sort of Semantic Commit message. The extra overhead could easily pay off in automation gains.
  4. Begin automatically enforcing the template (see "Automatically Enforce Commit Message Format" section above)
  5. Regularly review your commit messages with other contributors to discuss what is working well, and what needs to change

FAQs

Can I commit without adding?

It is recommended to always add files prior to committing.

This is what happens if you don't:

$ git commit -m "Add Code"
On branch my_branch
Your branch is up to date with 'origin/my_branch'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
  modified:   app/models/base_controller.rb

no changes added to commit (use "git add" and/or "git commit -a")

Technically, you can commit without adding files, by passing the --allow-empty flag, but ask yourself why would you want to do that? Your changes will not be recorded in Git. Virtually all use cases require adding valid file changes with git add first.

Can I commit without staging?

See "Can I commit without adding?" above. Staging and adding are synonymous.

Does git commit need a message?

It is recommended to always provide a commit message following the best practices in this guide.

This is what happens if you don't:

$ git commit -m ""
Aborting commit due to empty commit message.

Technically, you can commit without a message by using git commit --allow-empty-message -m "". Ask yourself why you would want to do that though? Most use cases indicate that a message is appropriate.

What is a good git commit message?

A good commit message is one that clearly and concisely tells WHAT changed, and WHY it changed.

Summary

Git is a powerful tool, but with that power comes complexity. Who would have thought that something as simple as a commit message could be used in so many incredible ways!

You now know how to write good commit messages, and why they matter so much. You've also learned about Semantic Commit messages and how powerful they can be for enabling automation, and enforcing message formats.

Next Steps

If you're interested in learning more about how Git works under the hood, check out our Baby Git Guidebook for Developers, which dives into Git's code in an accessible way. We wrote it for curious developers to learn how Git works at the code level. To do this we documented the first version of Git's code and discuss it in detail.

We hope you enjoyed this post! Feel free to shoot me an email at jacob@initialcommit.io with any questions or comments.

References

  1. Pro Git Book https://git-scm.com/book/en/v2
  2. Josh Buchea's Semantic Commits https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716
  3. Conventional Commits https://www.conventionalcommits.org

Final Notes