Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m1foley/fit-commit
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.3.0
Choose a base ref
...
head repository: m1foley/fit-commit
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Oct 12, 2015

  1. Document whole commit message validation

    See #24
    hmnhf committed Oct 12, 2015
    Copy the full SHA
    e847c73 View commit details
  2. Merge pull request #25 from hmnhf/improve/readme

    Document whole commit message validation
    m1foley committed Oct 12, 2015
    Copy the full SHA
    782b377 View commit details

Commits on Dec 8, 2015

  1. Fixup and squash commits update

    Skip capital letter check for fixup and squash commits title
    barthez committed Dec 8, 2015
    Copy the full SHA
    6c18108 View commit details
  2. Merge pull request #26 from barthez/skip-capital-letter-check-for-fix…

    …ups-and-squashes
    
    Skip capital letter check for autosquash commits
    m1foley committed Dec 8, 2015
    Copy the full SHA
    672672e View commit details
  3. Bump version to 3.4.0

    Mike Foley committed Dec 8, 2015
    Copy the full SHA
    049922c View commit details

Commits on Apr 5, 2016

  1. Enable validations on all branches by default

    Fixes #27
    Mike Foley committed Apr 5, 2016
    Copy the full SHA
    e5052d9 View commit details
  2. Bump version to 3.5.0

    Mike Foley committed Apr 5, 2016
    Copy the full SHA
    d63dd3a View commit details

Commits on May 15, 2016

  1. Link to fit-commit-js in README

    Fixes #29
    Mike Foley committed May 15, 2016
    Copy the full SHA
    d11c160 View commit details

Commits on Nov 19, 2016

  1. Disable Wip check by default

    I've found this to be annoying. It can be enabled via config file if
    someone wants to use it.
    Mike Foley committed Nov 19, 2016
    Copy the full SHA
    51b58c0 View commit details
  2. Bump version to 3.6.0

    Mike Foley committed Nov 19, 2016
    Copy the full SHA
    b1e2349 View commit details
  3. Update Frat House description

    Mike Foley committed Nov 19, 2016
    Copy the full SHA
    2928625 View commit details
  4. Bump version to 3.6.1

    Mike Foley committed Nov 19, 2016
    Copy the full SHA
    358bdb1 View commit details

Commits on Dec 13, 2016

  1. Document running standalone

    Fixes #32
    Mike Foley committed Dec 13, 2016
    Copy the full SHA
    d18d5c8 View commit details

Commits on Jan 7, 2017

  1. Use bundler if fit-commit was installed using it

    Bundler will install gems to a different path, which most likely won't be picked
    up by ruby. So use `bundle exec` to run ruby to ensure gem path is correctly set
    for fit-commit.
    yasn77 committed Jan 7, 2017
    Copy the full SHA
    059aa17 View commit details

Commits on Jan 8, 2017

  1. Merge pull request #33 from yasn77/use_bundler

    Use bundler if fit-commit was installed using it
    m1foley authored Jan 8, 2017
    Copy the full SHA
    f30184b View commit details
  2. Bump version to 3.7.0

    Mike Foley committed Jan 8, 2017
    Copy the full SHA
    a95d330 View commit details

Commits on Mar 6, 2017

  1. Modify CI documentation

    Mike Foley committed Mar 6, 2017
    Copy the full SHA
    ee03b7f View commit details

Commits on Oct 13, 2017

  1. Add TODO

    m1foley committed Oct 13, 2017
    Copy the full SHA
    12b98b0 View commit details

Commits on Oct 29, 2017

  1. Refactor configuration file loading

    This allows code outside the gem to more easily call Fit Commit using
    non-default config files. Using dependency injection for the file paths
    makes them less baked-in and follows the open-closed principle.
    
    `let` is being removed from the test suite because its usage leads to
    unnecessary confusion. This makes the test styles inconsistent until
    it's completely removed.
    m1foley committed Oct 29, 2017
    Copy the full SHA
    107fe0b View commit details

Commits on Mar 15, 2018

  1. Copy the full SHA
    bf73617 View commit details

Commits on Aug 4, 2018

  1. Only warn for single-word lowercase messages

    If the commit message consists of a single word, we can assume it's a
    quick WIP commit and they don't want Fit Commit to be in the way.
    m1foley committed Aug 4, 2018
    Copy the full SHA
    98e24e0 View commit details

Commits on Aug 6, 2018

  1. Copy the full SHA
    c19b431 View commit details
  2. Bump version to 3.8.0

    m1foley committed Aug 6, 2018
    Copy the full SHA
    abddce6 View commit details

Commits on Mar 14, 2019

  1. Copy the full SHA
    846cf28 View commit details
  2. Merge pull request #36 from msdundar/exit-code-of-help

    Do not exit with error when help running
    m1foley authored Mar 14, 2019
    Copy the full SHA
    a3be7f7 View commit details
  3. Copy the full SHA
    ca03c1c View commit details
  4. Bump gem requirements

    No big changes. Just making sure these stay up to date.
    m1foley committed Mar 14, 2019
    Copy the full SHA
    5a1ff03 View commit details
  5. Bump version to 3.8.1

    Only significant change is returning success code on help output.
    m1foley committed Mar 14, 2019
    Copy the full SHA
    0b2031f View commit details

Commits on Mar 9, 2021

  1. Shorten post-install message

    m1foley committed Mar 9, 2021
    Copy the full SHA
    f1f0c3d View commit details
  2. Update "master" references to "main"

    This is becoming the standard branch name now, and the integration tests
    no longer work on my machine without this change.
    m1foley committed Mar 9, 2021
    Copy the full SHA
    e4e0ca1 View commit details
  3. Update development dependency versions

    Unimportant version bumps; no important changes.
    m1foley committed Mar 9, 2021
    Copy the full SHA
    b08e5dc View commit details
  4. Bump version to 3.8.2

    m1foley committed Mar 9, 2021
    Copy the full SHA
    4ceb6e1 View commit details

Commits on Aug 26, 2021

  1. Copy the full SHA
    32fd3bf View commit details

Commits on Jun 8, 2023

  1. Change "Force commit?" to "Commit anyway?"

    The old wording might be confused with Git forced pushes.
    m1foley committed Jun 8, 2023
    Copy the full SHA
    382cbd3 View commit details
  2. Bump version to 3.8.4

    m1foley committed Jun 8, 2023
    Copy the full SHA
    b73466d View commit details
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

### v3.8.0 (2018-08-06)
- Add CapitalizeSubject config option to warn on WIPlikes

### v3.7.0 (2017-01-07)
- Run via Bundler if available

### v3.6.0 (2016-11-18)
- Disable WIP check by default

### v3.5.0 (2016-04-05)
- All validations are enabled on all branches by default

### v3.4.0 (2015-12-08)
- Skip capitalization check for autosquash commits

### v3.3.0 (2015-10-10)
- Add support for custom validators

51 changes: 40 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar
2: Error: Second line must be blank.
3: Error: Lines should be <= 72 chars. (76)
Force commit? [y/n/e] ▊
Commit anyway? [y/n/e] ▊
```

## Prerequisites
@@ -39,8 +39,8 @@ This creates a `.git/hooks/commit-msg` script which will automatically check you
* **Tense**: Message must use imperative present tense: "Fix bug" and not "Fixed bug" or "Fixes bug."
* **Subject Period**: Do not end your subject line with a period.
* **Capitalize Subject**: Begin all subject lines with a capital letter.
* **WIP**: Do not commit WIPs to shared branches.
* **Frat House**: No frat house commit messages in shared branches.
* **Frat House**: No offensive content.
* **WIP**: Do not commit WIPs to shared branches (disabled by default)

## Configuration

@@ -61,12 +61,11 @@ Validators/SubjectPeriod:
Enabled: true
Validators/CapitalizeSubject:
Enabled: true
Validators/Wip:
Enabled:
- master
WarnOnWiplikes: true
Validators/Frathouse:
Enabled:
- master
Enabled: true
Validators/Wip:
Enabled: false
```
The `Enabled` property accepts multiple formats:
@@ -75,10 +74,10 @@ The `Enabled` property accepts multiple formats:
# true/false to enable/disable the validation (branch agnostic)
Validators/Foo:
Enabled: false
# Array of String/Regex matching each branch for which it's enabled
# Array of String or Regex matching each branch it's enabled on
Validators/Bar:
Enabled:
- master
- main
- !ruby/regexp /\Afoo.+bar/
```

@@ -98,6 +97,19 @@ module FitCommit
end
end
end
# A validator can also validate the commit message as a whole:
module FitCommit
module Validators
class MyCustomValidator < Base
def validate(lines)
if lines.none? { |line| line.text =~ /#\d+/ }
add_warning(lines.last.lineno, "Related issue not referenced.")
end
end
end
end
end
```

`Require` the file and enable the validator in your config:
@@ -145,6 +157,21 @@ To copy your default hooks into existing repos:
$ git init
```

### How can I run this standalone, like part of a CI process?

Fit Commit can be run outside of a Git hook context with a simple shell script:

```sh
$ export GIT_BRANCH_NAME=branch_name
$ export COMMIT_MESSAGE_PATH=path/to/message
# Using Bundler
$ bundle exec ruby -e 'require "fit_commit"; FitCommit.run'
# Not using Bundler
$ rbenv exec ruby -rrubygems -e 'require "fit_commit"; FitCommit.run'
```

It exits with an error code if any errors are present, which will fail a build if it's part of a CI run.

### Who decided these rules?
Fit Commit aims to enforce *community standards*. The two influential guides are:

@@ -163,4 +190,6 @@ Author: [Mike Foley](https://github.com/m1foley)

Inspiration taken from: [Tim Pope](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), [Jason Fox](https://gist.github.com/jasonrobertfox/8057124), [Addam Hardy](http://addamhardy.com/blog/2013/06/05/good-commit-messages-and-enforcing-them-with-git-hooks/), [pre-commit](https://github.com/jish/pre-commit)

Similar projects: [gitlint](https://github.com/jorisroovers/gitlint) (written in Python)
Similar projects:
- [gitlint](https://github.com/jorisroovers/gitlint) (written in Python)
- [fit-commit-js](https://www.npmjs.com/package/fit-commit-js) (Node.js package)
3 changes: 3 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# TODO

- Ideas from: https://github.com/bkuhlmann/git-cop
10 changes: 4 additions & 6 deletions fit-commit.gemspec
Original file line number Diff line number Diff line change
@@ -12,22 +12,20 @@ Gem::Specification.new do |gem|
gem.description = "A Git hook to validate your commit messages based on community standards."
gem.files = `git ls-files`.split("\n")
gem.executables = ["fit-commit"]
gem.default_executable = "fit-commit"
gem.test_files = `git ls-files -- test/*`.split("\n")
gem.require_paths = ["lib"]
gem.extra_rdoc_files = ["README.md"]
gem.rdoc_options = ["--main", "README.md"]

gem.post_install_message = <<-EOF
Thank you for installing Fit Commit!
Install the hook in each git repo you want to scan using:
Install Fit Commit hooks in each git repo you want to scan using:
> fit-commit install
Read more: https://github.com/m1foley/fit-commit#readme
EOF

gem.add_dependency("swearjar", "~> 1.0")
gem.add_development_dependency("minitest", "~> 5.8")
gem.add_development_dependency("rake", "~> 10.4")
gem.add_dependency("swearjar", "~> 1.4")
gem.add_development_dependency("minitest", "~> 5.14")
gem.add_development_dependency("rake", "~> 13.0")
end
2 changes: 1 addition & 1 deletion lib/fit_commit/cli.rb
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ def help
$stderr.puts "fit-commit v#{FitCommit::VERSION}"
$stderr.puts "Usage: fit-commit install"
$stderr.puts "Usage: fit-commit uninstall"
EXIT_CODE_FAILURE
EXIT_CODE_SUCCESS
end

def install
49 changes: 30 additions & 19 deletions lib/fit_commit/configuration_loader.rb
Original file line number Diff line number Diff line change
@@ -2,47 +2,58 @@

module FitCommit
class ConfigurationLoader
def global_configuration
all_filepaths.each_with_object({}) do |filepath, config|
SYSTEM_FILEPATH = "/etc/fit_commit.yml"
LOCAL_FILEPATH = ".fit_commit.yml"

def initialize(filepaths)
self.filepaths = filepaths
end

def self.default_configuration
new(default_filepaths).configuration
end

def configuration
filepaths.each_with_object({}) do |filepath, config|
config.merge!(read_config(filepath)) do |_key, oldval, newval|
oldval.merge(newval)
end
end
end

private

def all_filepaths
# sorted by increasing precedence
[default_filepath, system_filepath, user_filepath, config_filepath, local_filepath]
def self.default_filepaths
[
gem_default_filepath,
SYSTEM_FILEPATH,
user_filepath,
config_filepath,
LOCAL_FILEPATH
]
end

def default_filepath
def self.gem_default_filepath
File.expand_path("../../../templates/config/fit_commit.default.yml", __FILE__)
end

def system_filepath
"/etc/fit_commit.yml"
end

def user_filepath
def self.user_filepath
File.join(ENV["HOME"], ".fit_commit.yml")
end

def config_filepath
def self.config_filepath
File.join(git_top_level, "config", "fit_commit.yml")
end

def local_filepath
".fit_commit.yml"
end

def git_top_level
def self.git_top_level
top_level = `git rev-parse --show-toplevel`.chomp.strip
fail "Git repo not found! Please submit a bug report." if top_level == ""
top_level
end

private

# sorted by increasing precedence
attr_accessor :filepaths

def read_config(path)
load_yaml(path).each_with_object({}) do |(key, value), config|
translated_key = translate_config_key(key)
6 changes: 3 additions & 3 deletions lib/fit_commit/message_parser.rb
Original file line number Diff line number Diff line change
@@ -2,6 +2,9 @@

module FitCommit
class MessageParser
GIT_VERBOSE_MARKER = "# ------------------------ >8 ------------------------"
COMMENT_REGEX = /\A#/

attr_accessor :message_path
def initialize(message_path)
self.message_path = message_path
@@ -13,9 +16,6 @@ def lines

private

GIT_VERBOSE_MARKER = "# ------------------------ >8 ------------------------"
COMMENT_REGEX = /\A#/

def relevant_lines
message_text.lines.each_with_object([]) do |line, relevant_lines|
line.chomp!
5 changes: 2 additions & 3 deletions lib/fit_commit/runner.rb
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ class Runner

EXIT_CODE_ALLOW_COMMIT = 0
EXIT_CODE_REJECT_COMMIT = 1
DEFAULT_EDITOR = "vim"

attr_accessor :message_path, :branch_name, :stderr, :stdin
def initialize(message_path, branch_name, stderr = $stderr, stdin = $stdin)
@@ -51,7 +52,7 @@ def retry_on_user_edit

def ask_force_commit
return unless interactive?
stderr.print "\nForce commit? [y/n/e] "
stderr.print "\nCommit anyway? [y/n/e] "
input = stdin.gets
fail StartOverOnEditException if input =~ /e/i
input =~ /y/i
@@ -105,8 +106,6 @@ def edit_message
system(editor, message_path)
end

DEFAULT_EDITOR = "vim"

def editor
editor = ENV["EDITOR"]
editor = DEFAULT_EDITOR unless editor && editor != "none"
5 changes: 3 additions & 2 deletions lib/fit_commit/validator_loader.rb
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@

module FitCommit
class ValidatorLoader
attr_accessor :branch_name, :configuration
def initialize(branch_name, configuration = load_configuration)
self.branch_name = branch_name
self.configuration = configuration
@@ -14,8 +13,10 @@ def validators

private

attr_accessor :branch_name, :configuration

def load_configuration
FitCommit::ConfigurationLoader.new.global_configuration
FitCommit::ConfigurationLoader.default_configuration
end

def all_validators
24 changes: 21 additions & 3 deletions lib/fit_commit/validators/capitalize_subject.rb
Original file line number Diff line number Diff line change
@@ -3,11 +3,29 @@
module FitCommit
module Validators
class CapitalizeSubject < Base
def validate_line(lineno, text)
if lineno == 1 && text[0] =~ /[[:lower:]]/
add_error(lineno, "Begin all subject lines with a capital letter.")
MESSAGE = "Begin all subject lines with a capital letter."
SINGLE_WORD = /\A\w+\z/
AUTOSQUASH = /\A(fixup|squash)! /

def validate(lines)
if lines[0].text =~ /\A[[:lower:]]/ && lines[0].text !~ AUTOSQUASH
if ignore_on_wiplikes? && wiplike?(lines)
add_warning(1, MESSAGE)
else
add_error(1, MESSAGE)
end
end
end

private

def wiplike?(lines)
lines[0].text =~ SINGLE_WORD && lines[1..-1].all?(&:empty?)
end

def ignore_on_wiplikes?
config.fetch("WarnOnWiplikes")
end
end
end
end
2 changes: 1 addition & 1 deletion lib/fit_commit/validators/frathouse.rb
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ module Validators
class Frathouse < Base
def validate_line(lineno, text)
if Swearjar.default.profane?(text)
add_error(lineno, "No frat house commit messages in shared branches.")
add_error(lineno, "No frat house commit messages.")
end
end
end
36 changes: 20 additions & 16 deletions lib/fit_commit/validators/line_length.rb
Original file line number Diff line number Diff line change
@@ -3,28 +3,32 @@
module FitCommit
module Validators
class LineLength < Base
MERGE_COMMIT = /\AMerge branch '[^']+' into ./
URL = %r{[a-z]+://}

def validate_line(lineno, text)
if lineno == 1 && text.empty?
add_error(lineno, "Subject line cannot be blank.")
elsif lineno == 2 && !text.empty?
add_error(lineno, "Second line must be blank.")
elsif line_too_long?(text)
if lineno == 1
if text.empty?
add_error(lineno, "Subject line cannot be blank.")
elsif text !~ MERGE_COMMIT
if text.length > max_line_length
add_error(lineno, format("Lines should be <= %i chars. (%i)",
max_line_length, text.length))
elsif text.length > subject_warn_length
add_warning(lineno, format("Subject line should be <= %i chars. (%i)",
subject_warn_length, text.length))
end
end
elsif lineno == 2
unless text.empty?
add_error(lineno, "Second line must be blank.")
end
elsif text.length > max_line_length && !(allow_long_urls? && text =~ URL)
add_error(lineno, format("Lines should be <= %i chars. (%i)",
max_line_length, text.length))
elsif lineno == 1 && text.length > subject_warn_length
add_warning(lineno, format("Subject line should be <= %i chars. (%i)",
subject_warn_length, text.length))
end
end

def line_too_long?(text)
text.length > max_line_length && !(allow_long_urls? && contains_url?(text))
end

def contains_url?(text)
text =~ %r{[a-z]+://}
end

def max_line_length
config.fetch("MaxLineLength")
end
Loading