Git Cop

Enforces Git rebase workflow with consistent Git commits for a clean and easy to read/debug project history.

Table of Contents

Features

Screencasts

Requirements

  1. Ruby 2.4.0 (or higher)

Setup

Install

For a secure install, type the following (recommended):

gem cert --add <(curl --location --silent https://www.alchemists.io/gem-public.pem)
gem install git-cop --trust-policy MediumSecurity

NOTE: A HighSecurity trust policy would be best but MediumSecurity enables signed gem verification while allowing the installation of unsigned dependencies since they are beyond the scope of this gem.

For an insecure install, type the following (not recommended):

gem install git-cop

Configuration

This gem can be configured via a global configuration:

~/.config/git-cop/configuration.yml

It can also be configured via XDG environment variables as provided by the Runcom gem.

The default configuration is:

:commit_author_email:
  :enabled: true
  :severity: :error
:commit_author_name_capitalization:
  :enabled: true
  :severity: :error
:commit_author_name_parts:
  :enabled: true
  :severity: :error
  :minimum: 2
:commit_body_bullet:
  :enabled: true
  :severity: :error
  :blacklist:
    - "\\*"
    - "•"
:commit_body_bullet_capitalization:
  :enabled: true
  :severity: :error
  :whitelist: "\\-"
:commit_body_issue_tracker_link:
  :enabled: true,
  :severity: :error
  :blacklist:
    - "(f|F)ix(es|ed)?\\s\\#\\d+"
    - "(c|C)lose(s|d)?\\s\\#\\d+"
    - "(r|R)esolve(s|d)?\\s\\#\\d+"
    - "github\\.com\\/.+\\/issues\\/\\d+"
:commit_body_leading_line:
  :enabled: false
  :severity: :warn
:commit_body_leading_space:
  :enabled: true
  :severity: :error
:commit_body_line_length:
  :enabled: true
  :severity: :error
  :length: 72
:commit_body_paragraph_capitalization:
  :enabled: true
  :severity: :error
:commit_body_phrase:
  :enabled: true
  :severity: :error
  :blacklist:
  basically
    - basically
    - "\\beasy\\b"
    - everyone knows
    - however
    - "\\bjust\\b"
    - obviously
    - of course
    - simply
:commit_body_presence:
  :enabled: false
  :severity: :warn
  :minimum: 1
:commit_body_single_bullet:
  :enabled: true
  :severity: :error
  :whitelist: "\\-"
:commit_subject_length:
  :enabled: true
  :severity: :error
  :length: 72
:commit_subject_prefix:
  :enabled: true
  :severity: :error
  :whitelist:
    - Fixed
    - Added
    - Updated
    - Removed
    - Refactored
:commit_subject_suffix:
  :enabled: true
  :severity: :error
  :whitelist:
    - "\\."

Feel free to take this default configuration, modify, and save as your own custom configuration.yml.

Enablement

By default, most cops are enabled. Accepted values are true or false. If you wish to disable a cop, set it to false.

Severity Levels

By default, most cops are set to error severity. If you wish to reduce the severity level of a cop, you can set it to warn instead. Here are the accepted values and what each means:

Regular Expressions

Some cops support whitelist or blacklist options. These lists can consist of strings, regular expressions, or a combination thereof. Regardless of your choice, all lists are automatically converted to regular expression for use by the cops. This means a string like "example" becomes /example/ and a regular expression of "\AExample.+" becomes /\AExample.+/.

If you need help constructing complex regular expressions for these lists, try launching an IRB session and using Regexp.new or Regexp.escape to experiment with the types of words/phrases you want to turn into regular expressions. For purposes of the YAML configuration, these need to be expressed as strings with special characters escaped properly for internal conversion to a regular expression.

Rake

This gem provides optional Rake tasks. They can be added to your project by adding the following requirement to the top of your Rakefile:

require "git/cop/rake/setup"

Now, when running bundle exec rake -T, you'll see git_cop included in the list.

If you need a concrete example, check out the Rakefile of this project for details.

Usage

Command Line Interface (CLI)

From the command line, type: git-cop --help

git-cop --hook                # Add Git Hook support.
git-cop -c, [--config]        # Manage gem configuration.
git-cop -h, [--help=COMMAND]  # Show this message or get help for a command.
git-cop -p, [--police]        # Check feature branch for issues.
git-cop -v, [--version]       # Show gem version.

To check if your Git commit history is clean, run: git-cop --police. It will exit with a failure if at least one issue, with error severity, is detected.

This gem does not check commits on master. This is intentional as you would, generally, not want to rewrite or fix commits on master. This gem is best used on feature branches as it automatically detects all commits made since master on the feature branch.

Here is an example workflow, using gem defaults with issues detected:

cd example
git checkout -b test
touch text.txt
git add --all .
git commit --message "This is a bogus commit message that is also terribly long and will word wrap"
git-cop --police

# Output:
Running Git Cop...

83dbad531d84a184e55cbb38c5b2a4e5fa5bcaee (Brooke Kuhlmann, 0 seconds ago): This is a bogus commit message that is also terribly long and will word wrap
  Commit Body Presence Warning. Use minimum of 1 line (non-empty).
  Commit Subject Length Error. Use 72 characters or less.
  Commit Subject Prefix Error. Use: /Fixed/, /Added/, /Updated/, /Removed/, /Refactored/.
  Commit Subject Suffix Error. Use: /\./.

1 commit inspected. 4 issues detected (1 warning, 3 errors).

Git Hooks

This gem supports Git Hooks.

It is highly recommended you manage Git Hooks as global scripts as it'll reduce project maintenance costs for you. To configure global Git Hooks, add the following to your ~/.gitconfig:

[core]
  hooksPath = ~/.git_template/hooks

Then you can customize Git Hooks for all of your projects. Check out these examples.

If using a global configuration is not desired, you can add Git Hooks at a per project level by editing any of the scripts within the .git/hooks directory of the repository.

Commit Message

The commit-msg hook, which is the best way to use this gem as a Git Hook, is provided as a --hook option. Run git-cop --help --hook for usage:

Usage:
  git-cop --hook

Options:
  [--commit-message=PATH]  # Check commit message.

Add Git Hook support.

As shown above, the --commit-message option accepts a file path (i.e. .git/COMMIT_EDITMSG) which is provided to you by Git within the .git/hooks/commit-msg script. Here is a working example of what that script might look like:

#! /usr/bin/env bash

set -o nounset
set -o errexit
set -o pipefail
IFS=$'\n\t'

if ! command -v git-cop > /dev/null; then
   printf "%s\n" "[git]: Git Cop not found. To install, run: gem install git-cop --trust-policy MediumSecurity."
   exit 1
fi

git-cop --hook --commit-message "${BASH_ARGV[0]}"

Whenever you attempt to add a commit, Git Cop will check your commit for issues prior to saving it.

Post Commit

The post-commit hook is possible via the --police --commits option. Usage:

Usage:
  git-cop -p, [--police]

Options:
  -c, [--commits=one two three]  # Check specific commit SHA(s).

Check feature branch for issues.

The post-commit hook can be used multiple ways but, if you want it to check each commit after it has been made, here is a working example which can be used as a .git/hooks/post-commit script:

#! /usr/bin/env bash

set -o nounset
set -o errexit
set -o pipefail
IFS=$'\n\t'

if ! command -v git-cop > /dev/null; then
   printf "%s\n" "[git]: Git Cop not found. To install, run: gem install git-cop --trust-policy MediumSecurity."
   exit 1
fi

git-cop --police --commits $(git log --pretty=format:%H -1)

Whenever a commit has been saved, this script will run Git Cop to check for issues.

Continuous Integration (CI)

This gem automatically configures itself for known CI build servers.

Calculation of commits is done by reviewing all commits made on the feature branch since branching from master. Below are the build servers which are supported and tested. If you have a build server that is not listed, please open a pull request with support.

Circle CI

This gem automatically detects and configures itself for Circle CI builds by checking the CIRCLECI environment variable. No additional setup required!

Travis CI

This gem automatically detects and configures itself for Travis CI builds by checking the TRAVIS environment variable. No additional setup required!

Cops

The following details the various cops provided by this gem to ensure a high standard of commits for your project.

Commit Author Email

| Enabled | Severity | Defaults | |———|———-|———-| | true | error | none |

Ensures author email address exists. Git requires an author email when you use it for the first time too. This takes it a step further to ensure the email address loosely resembles an email address.

# Disallowed
mudder_man

# Allowed
jayne@serenity.com

Commit Author Name Capitalization

| Enabled | Severity | Defaults | |———|———-|———-| | true | error | none |

Ensures auther name is properly capitalized. Example:

# Disallowed
jayne cobb
dr. simon tam

# Allowed
Jayne Cobb
Dr. Simon Tam

Commit Author Name Parts

| Enabled | Severity | Defaults | |———|———-|————| | true | error | minimum: 2 |

Ensures author name consists of, at least, a first and last name. Example:

# Disallowed
Kaylee

# Allowed
Kaywinnet Lee Frye

Commit Body Bullet

| Enabled | Severity | Defaults | |———|———-|—————————| | true | error | blacklist: ["\*", "•"] |

Ensures commit message bodies use a standard Markdown syntax for bullet points. Markdown supports the following syntax for bullets:

*
-

It's best to use - for bullet point syntax as * are easier to read when used for emphasis. This makes parsing the Markdown syntax easier when reviewing a Git commit as the syntax used for bullet points and emphasis are now, distinctly, unique.

Commit Body Bullet Capitalization

| Enabled | Severity | Defaults | |———|———-|———————-| | true | error | whitelist: ["\-"] |

Ensures commit body bullet lines are capitalized. Example:

# Disallowed

- an example bullet.

# Allowed

- An example bullet.

| Enabled | Severity | Defaults | |———|———-|——————————————————| | true | error | blacklist: (see configuration list, mentioned above) |

Ensures commit body doesn't contain a link to an issue tracker. The blacklist defaults to GitHub Issue links but can be customized for any issue tracker.

There are several reasons for exluding issue tracker links from commit bodies:

  1. Not all issue trackers preserve issues (meaning they can be deleted). This makes make reading historic commits much harder to understand why the change was made when the link no longer works.

  2. When not connected to the internet or working on a laggy connection, it's hard to understand why a commit was made when all you have is a link to an issue with no other supporting context.

  3. During the course of a repository's life, issue trackers can be replaced (rare but it does happen). If the old issue tracker service is no longer paid for, none of the links within the commit will be of any relevance.

  4. An issue might span several commits in order to resolve it. Including a link in each commit is tedious and can create noise within the issue's history which is distracting.

Instead of linking to issues, take the time to write a short summary as to why the commit was made. Doing this will make it easier to understand why the commit was made, keeps the commit self- contained, and makes learning about/debugging the commit faster.

Issue tracker links are best used at the pull request level due to an issue usually spanning multiple commits in order to complete the work. When reading a pull request, this is a great opportunity to link to an issue in order to provide a high level overview and reason why the pull request exists.

Commit Body Leading Line

| Enabled | Severity | Defaults | |———|———-|———-| | true | error | none |

Ensures there is a leading, empty line, between the commit subject and body. Generally, this isn't an issue but sometimes the Git CLI can be misued or a misconfigured Git editor will smash the subject line and start of the body as one run-on paragraph. Example:

# Disallowed

Curabitur eleifend wisi iaculis ipsum.
Pellentque morbi-trist sentus et netus et malesuada fames ac turpis egestas. Vestibulum tortor
quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu_libero sit amet quam
egestas semper. Aenean ultricies mi vitae est. Mauris placerat's eleifend leo. Quisque et sapien
ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, orn si amt wit.

# Allowed

Curabitur eleifend wisi iaculis ipsum.

Pellentque morbi-trist sentus et netus et malesuada fames ac turpis egestas. Vestibulum tortor
quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu_libero sit amet quam
egestas semper. Aenean ultricies mi vitae est. Mauris placerat's eleifend leo. Quisque et sapien
ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, orn si amt wit.

Commit Body Leading Space

| Enabled | Severity | Defaults | |———|———-|———-| | false | warn | none |

This cop has been deprecated and is a duplicate of the Commit Body Leading Line cop mentioned above. If enabled, this cop will print deprecation warnings and recommend using the Commit Body Leading Line cop instead.

This cop will be permantently removed in the 2.0.0 version release.

Commit Body Line Length

| Enabled | Severity | Defaults | |———|———-|————| | true | error | length: 72 |

Ensures each line of the commit body is no longer than 72 characters in length for consistent readabilty and word-wrap prevention on smaller screen sizes. For further details, read Tim Pope's original article on the subject.

Commit Body Paragraph Capitalization

| Enabled | Severity | Defaults | |———|———-|———-| | true | error | none |

Ensures each paragraph of the commit body is capitalized. Example:

# Disallowed

curabitur eleifend wisi iaculis ipsum.

# Allowed

Curabitur eleifend wisi iaculis ipsum.

Commit Body Phrase

| Enabled | Severity | Defaults | |———|———-|——————————————————| | true | error | blacklist: (see configuration list, mentioned above) |

Ensures non-descriptive words/phrases are avoided in order to keep commit message bodies informative and specific. The blacklist is case insensitive. Detection of blacklisted words/phrases is case insensitve as well. Example:

# Disallowed

Obviously, the existing implementation was too simple for my tastes. Of course, this couldn't be
allowed. Everyone knows the correct way to implement this code is to do just what I've added in
this commit. Easy!

# Allowed

Necessary to fix due to a bug detected in production. The included implentation fixes the bug
and provides the missing spec to ensure this doesn't happen again.

Commit Body Presence

| Enabled | Severity | Defaults | |———|———-|————| | false | warn | minimum: 1 |

Ensures a minimum number of lines are present within the commit body. Lines with empty characters (i.e. whitespace, carriage returns, etc.) are considered to be empty.

Automatically ignores fixup! commits as they are not meant to have bodies.

Commit Body Single Bullet

| Enabled | Severity | Defaults | |———|———-|——————–| | true | error | whitelist: "\-" |

Ensures a single bullet is never used when a paragraph could be used instead. Example:

# Disallowed

- Pellentque morbi-trist sentus et netus et malesuada fames ac turpis egestas. Vestibulum tortor
  quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu_libero sit amet quam.

# Allowed

Pellentque morbi-trist sentus et netus et malesuada fames ac turpis egestas. Vestibulum tortor
quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu_libero sit amet quam.

Commit Subject Length

| Enabled | Severity | Defaults | |———|———-|————| | true | error | length: 72 |

Ensures the commit subject length is no more than 72 characters in length. This default is more lenient than the 50/72 rule as it gives one the ability to formulate a more descriptive subject line without being too wordy or suffer being word wrapped.

Automatically ignores fixup! or squash! commit prefixes when calculating subject length.

Commit Subject Prefix

| Enabled | Severity | Defaults | |———|———-|————————| | true | error | whitelist: (see below) |

Ensures the commit subject uses consistent prefixes that help explain what is being commited. The whitelist is case sensitive. The default whitelist consists of the following prefixes:

In practice, using a prefix other than what has been detailed above to explain what is being committed is never needed. This whitelist is not only short and easy to remember but also has the added benefit of categorizing the commits for building release notes, change logs, etc. This becomes handy when coupled with another tool, Milestoner, for producing consistent project milestones and Git tag histories.

Automatically ignores fixup! or squash! commit prefixes when used as a Git Hook in order to not disturb interactive rebase workflows.

Commit Subject Suffix

| Enabled | Severity | Defaults | |———|———-|———————-| | true | error | whitelist: ["\."] |

Ensures commit subjects are suffixed consistently. The whitelist is case sensitive and only allows for periods (.) to ensure each commit is sentance-like when generating release notes, Git tags, change logs, etc. This is handy when coupled with a tool, like Milestoner, which automate project milestone releases.

Style Guide

In addition to what is described above and automated for you, the following style guide is also worth considering:

General

Commits

Branches

Tags

Rebases

Pull Requests

GitHub

When using GitHub, make sure to enforce a rebase workflow for all of your GitHub projects (highly recommended). You can do this via your project options (i.e. https://github.com///settings) and editing your merge options for pull requests as follows:

Doing this will help maintain a clean Git history.

Tests

To test, run:

bundle exec rake

Versioning

Read Semantic Versioning for details. Briefly, it means:

Code of Conduct

Please note that this project is released with a CODE OF CONDUCT. By participating in this project you agree to abide by its terms.

Contributions

Read CONTRIBUTING for details.

License

Copyright © 2017 Alchemists. Read LICENSE for details.

History

Read CHANGES for details. Built with Gemsmith.

Credits

Developed by Brooke Kuhlmann at Alchemists.