1
0
Fork 0

Adding upstream version 0.13.1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-13 05:54:40 +01:00
parent 1805ece79d
commit d8f166e6bb
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
167 changed files with 15302 additions and 0 deletions

432
docs/configuration.md Normal file
View file

@ -0,0 +1,432 @@
# Configuration
Gitlint can be configured through different means.
# Config files #
You can modify gitlint's behavior by adding a ```.gitlint``` file to your git repository.
Generate a default ```.gitlint``` config file by running:
```bash
gitlint generate-config
```
You can also use a different config file like so:
```bash
gitlint --config myconfigfile.ini
```
The block below shows a sample ```.gitlint``` file. Details about rule config options can be found on the
[Rules](rules.md) page, details about the ```[general]``` section can be found in the
[General Configuration](configuration.md#general-configuration) section of this page.
```ini
# Edit this file as you like.
#
# All these sections are optional. Each section with the exception of [general] represents
# one rule and each key in it is an option for that specific rule.
#
# Rules and sections can be referenced by their full name or by id. For example
# section "[body-max-line-length]" could be written as "[B1]". Full section names are
# used in here for clarity.
# Rule reference documentation: http://jorisroovers.github.io/gitlint/rules/
#
# Use 'gitlint generate-config' to generate a config file with all possible options
[general]
# Ignore certain rules (comma-separated list), you can reference them by their
# id or by their full name
ignore=title-trailing-punctuation, T3
# verbosity should be a value between 1 and 3, the commandline -v flags take
# precedence over this
verbosity = 2
# By default gitlint will ignore merge, revert, fixup and squash commits.
ignore-merge-commits=true
ignore-revert-commits=true
ignore-fixup-commits=true
ignore-squash-commits=true
# Ignore any data send to gitlint via stdin
ignore-stdin=true
# Fetch additional meta-data from the local repository when manually passing a
# commit message to gitlint via stdin or --commit-msg. Disabled by default.
staged=true
# Enable debug mode (prints more output). Disabled by default.
debug=true
# Enable community contributed rules
# See http://jorisroovers.github.io/gitlint/contrib_rules for details
contrib=contrib-title-conventional-commits,CC1
# Set the extra-path where gitlint will search for user defined rules
# See http://jorisroovers.github.io/gitlint/user_defined_rules for details
extra-path=examples/
# This is an example of how to configure the "title-max-length" rule and
# set the line-length it enforces to 80
[title-max-length]
line-length=80
[title-must-not-contain-word]
# Comma-separated list of words that should not occur in the title. Matching is case
# insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING"
# will not cause a violation, but "WIP: my title" will.
words=wip
[title-match-regex]
# python like regex (https://docs.python.org/2/library/re.html) that the
# commit-msg title must be matched to.
# Note that the regex can contradict with other rules if not used correctly
# (e.g. title-must-not-contain-word).
regex=^US[0-9]*
[body-max-line-length]
line-length=120
[body-min-length]
min-length=5
[body-is-missing]
# Whether to ignore this rule on merge commits (which typically only have a title)
# default = True
ignore-merge-commits=false
[body-changed-file-mention]
# List of files that need to be explicitly mentioned in the body when they are changed
# This is useful for when developers often erroneously edit certain files or git submodules.
# By specifying this rule, developers can only change the file when they explicitly reference
# it in the commit message.
files=gitlint/rules.py,README.md
[author-valid-email]
# python like regex (https://docs.python.org/2/library/re.html) that the
# commit author email address should be matched to
# For example, use the following regex if you only want to allow email addresses from foo.com
regex=[^@]+@foo.com
[ignore-by-title]
# Ignore certain rules for commits of which the title matches a regex
# E.g. Match commit titles that start with "Release"
regex=^Release(.*)
# Ignore certain rules, you can reference them by their id or by their full name
# Use 'all' to ignore all rules
ignore=T1,body-min-length
[ignore-by-body]
# Ignore certain rules for commits of which the body has a line that matches a regex
# E.g. Match bodies that have a line that that contain "release"
# regex=(.*)release(.*)
#
# Ignore certain rules, you can reference them by their id or by their full name
# Use 'all' to ignore all rules
ignore=T1,body-min-length
# This is a contrib rule - a community contributed rule. These are disabled by default.
# You need to explicitly enable them one-by-one by adding them to the "contrib" option
# under [general] section above.
[contrib-title-conventional-commits]
# Specify allowed commit types. For details see: https://www.conventionalcommits.org/
types = bugfix,user-story,epic
```
# Commandline config #
You can also use one or more ```-c``` flags like so:
```
$ gitlint -c general.verbosity=2 -c title-max-length.line-length=80 -c B1.line-length=100
```
The generic config flag format is ```-c <rule>.<option>=<value>``` and supports all the same rules and options which
you can also use in a ```.gitlint``` config file.
# Commit specific config #
You can also configure gitlint by adding specific lines to your commit message.
For now, we only support ignoring commits by adding ```gitlint-ignore: all``` to the commit
message like so:
```
WIP: This is my commit message
I want gitlint to ignore this entire commit message.
gitlint-ignore: all
```
```gitlint-ignore: all``` can occur on any line, as long as it is at the start of the line.
You can also specify specific rules to be ignored as follows:
```
WIP: This is my commit message
I want gitlint to ignore this entire commit message.
gitlint-ignore: T1, body-hard-tab
```
# Configuration precedence #
gitlint configuration is applied in the following order of precedence:
1. Commit specific config (e.g.: ```gitlint-ignore: all``` in the commit message)
2. Configuration Rules (e.g.: [ignore-by-title](/rules/#i1-ignore-by-title))
3. Commandline convenience flags (e.g.: ```-vv```, ```--silent```, ```--ignore```)
4. Commandline configuration flags (e.g.: ```-c title-max-length=123```)
5. Configuration file (local ```.gitlint``` file, or file specified using ```-C```/```--config```)
6. Default gitlint config
# General Options
Below we outline all configuration options that modify gitlint's overall behavior. These options can be specified
using commandline flags or in ```[general]``` section in a ```.gitlint``` configuration file.
## silent
Enable silent mode (no output). Use [exit](index.md#exit-codes) code to determine result.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
false | >= 0.1.0 | ```--silent```
### Examples
```sh
# CLI
gitlint --silent
```
## verbosity
Amount of output gitlint will show when printing errors.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
3 | >= 0.1.0 | `-v`
### Examples
```sh
# CLI
gitlint -vvv # default (level 3)
gitlint -vv # less output (level 2)
gitlint -v # even less (level 1)
gitlint --silent # no output (level 0)
gitlint -c general.verbosity=1 # Set specific level
gitlint -c general.verbosity=0 # Same as --silent
```
```ini
.gitlint
[general]
verbosity=2
```
## ignore-merge-commits
Whether or not to ignore merge commits.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
true | >= 0.7.0 | Not Available
### Examples
```sh
# CLI
gitlint -c general.ignore-merge-commits=false
```
```ini
#.gitlint
[general]
ignore-merge-commits=false
```
## ignore-revert-commits
Whether or not to ignore revert commits.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
true | >= 0.13.0 | Not Available
### Examples
```sh
# CLI
gitlint -c general.ignore-revert-commits=false
```
```ini
#.gitlint
[general]
ignore-revert-commits=false
```
## ignore-fixup-commits
Whether or not to ignore [fixup](https://git-scm.com/docs/git-commit#git-commit---fixupltcommitgt) commits.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
true | >= 0.9.0 | Not Available
### Examples
```sh
# CLI
gitlint -c general.ignore-fixup-commits=false
```
```ini
#.gitlint
[general]
ignore-fixup-commits=false
```
## ignore-squash-commits
Whether or not to ignore [squash](https://git-scm.com/docs/git-commit#git-commit---squashltcommitgt) commits.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
true | >= 0.9.0 | Not Available
### Examples
```sh
# CLI
gitlint -c general.ignore-squash-commits=false
```
```ini
#.gitlint
[general]
ignore-squash-commits=false
```
## ignore
Comma separated list of rules to ignore (by name or id).
Default value | gitlint version | commandline flag
---------------------------|------------------|-------------------
[] (=empty list) | >= 0.1.0 | `--ignore`
### Examples
```sh
# CLI
gitlint --ignore=body-min-length # ignore single rule
gitlint --ignore=T1,body-min-length # ignore multiple rule
gitlint -c general.ignore=T1,body-min-length # different way of doing the same
```
```ini
#.gitlint
[general]
ignore=T1,body-min-length
```
## debug
Enable debugging output.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
false | >= 0.7.1 | `--debug`
### Examples
```sh
# CLI
gitlint --debug
# --debug is special, the following does NOT work
# gitlint -c general.debug=true
```
## target
Target git repository gitlint should be linting against.
Default value | gitlint version | commandline flag
---------------------------|------------------|-------------------
(empty) | >= 0.8.0 | `--target`
### Examples
```sh
# CLI
gitlint --target=/home/joe/myrepo/
gitlint -c general.target=/home/joe/myrepo/ # different way of doing the same
```
```ini
#.gitlint
[general]
target=/home/joe/myrepo/
```
## extra-path
Path where gitlint looks for [user-defined rules](user_defined_rules.md).
Default value | gitlint version | commandline flag
---------------------------|------------------|-------------------
(empty) | >= 0.8.0 | `--extra-path`
### Examples
```sh
# CLI
gitlint --extra-path=/home/joe/rules/
gitlint -c general.extra-path=/home/joe/rules/ # different way of doing the same
```
```ini
#.gitlint
[general]
extra-path=/home/joe/rules/
```
## contrib
[Contrib rules](contrib_rules) to enable.
Default value | gitlint version | commandline flag
---------------------------|------------------|-------------------
(empty) | >= 0.12.0 | `--contrib`
### Examples
```sh
# CLI
gitlint --contrib=contrib-title-conventional-commits,CC1
gitlint -c general.contrib=contrib-title-conventional-commits,CC1 # different way of doing the same
```
```ini
#.gitlint
[general]
contrib=contrib-title-conventional-commits,CC1
```
## ignore-stdin
Ignore any stdin data. Sometimes useful when running gitlint in a CI server.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
false | >= 0.12.0 | `--ignore-stdin`
### Examples
```sh
# CLI
gitlint --ignore-stdin
gitlint -c general.ignore-stdin=true # different way of doing the same
```
```ini
#.gitlint
[general]
ignore-stdin=true
```
## staged
Fetch additional meta-data from the local `repository when manually passing a commit message to gitlint via stdin or ```--commit-msg```.
Default value | gitlint version | commandline flag
---------------|------------------|-------------------
false | >= 0.13.0 | `--staged`
### Examples
```sh
# CLI
gitlint --staged
gitlint -c general.staged=true # different way of doing the same
```
```ini
#.gitlint
[general]
staged=true
```

67
docs/contrib_rules.md Normal file
View file

@ -0,0 +1,67 @@
# Using Contrib Rules
_Introduced in gitlint v0.12.0_
Contrib rules are community-**contrib**uted rules that are disabled by default, but can be enabled through configuration.
Contrib rules are meant to augment default gitlint behavior by providing users with rules for common use-cases without
forcing these rules on all gitlint users. This also means that users don't have to
re-implement these commonly used rules themselves as [user-defined](user_defined_rules) rules.
To enable certain contrib rules, you can use the ```--contrib``` flag.
```sh
$ cat examples/commit-message-1 | gitlint --contrib contrib-title-conventional-commits,CC1
1: CC1 Body does not contain a 'Signed-Off-By' line
1: CL1 Title does not start with one of fix, feat, chore, docs, style, refactor, perf, test: "WIP: This is the title of a commit message."
# These are the default violations
1: T3 Title has trailing punctuation (.): "WIP: This is the title of a commit message."
1: T5 Title contains the word 'WIP' (case-insensitive): "WIP: This is the title of a commit message."
2: B4 Second line is not empty: "The second line should typically be empty"
3: B1 Line exceeds max length (123>80): "Lines typically need to have a max length, meaning that they can't exceed a preset number of characters, usually 80 or 120."
```
Same thing using a ```.gitlint``` file:
```ini
[general]
# You HAVE to add the rule here to enable it, only configuring (such as below)
# does NOT enable it.
contrib=contrib-title-conventional-commits,CC1
[contrib-title-conventional-commits]
# Specify allowed commit types. For details see: https://www.conventionalcommits.org/
types = bugfix,user-story,epic
```
You can also configure contrib rules using [any of the other ways to configure gitlint](configuration.md).
# Available Contrib Rules
ID | Name | gitlint version | Description
------|-------------------------------------|------------------ |-------------------------------------------
CT1 | contrib-title-conventional-commits | >= 0.12.0 | Enforces [Conventional Commits](https://www.conventionalcommits.org/) commit message style on the title.
CC1 | contrib-requires-signed-off-by | >= 0.12.0 | Commit body must contain a `Signed-Off-By` line.
## CT1: contrib-title-conventional-commits ##
ID | Name | gitlint version | Description
------|---------------------------------------|--------------------|-------------------------------------------
CT1 | contrib-title-conventional-commits | >= 0.12.0 | Enforces [Conventional Commits](https://www.conventionalcommits.org/) commit message style on the title.
### Options ###
Name | gitlint version | Default | Description
---------------|--------------------|--------------|----------------------------------
types | >= 0.12.0 | `fix,feat,chore,docs,style,refactor,perf,test,revert` | Comma separated list of allowed commit types.
## CC1: contrib-requires-signed-off-by ##
ID | Name | gitlint version | Description
------|---------------------------------------|--------------------|-------------------------------------------
CC1 | contrib-requires-signed-off-by | >= 0.12.0 | Commit body must contain a `Signed-Off-By` line. This means, a line that starts with the `Signed-Off-By` keyword.
# Contributing Contrib rules
We'd love for you to contribute new Contrib rules to gitlint or improve existing ones! Please visit the [Contributing](contributing) page on how to get started.

132
docs/contributing.md Normal file
View file

@ -0,0 +1,132 @@
# Contributing
We'd love for you to contribute to gitlint. Thanks for your interest!
The [source-code and issue tracker](https://github.com/jorisroovers/gitlint) are hosted on Github.
Often it takes a while for us (well, actually just [me](https://github.com/jorisroovers)) to get back to you
(sometimes up to a few months, this is a hobby project), but rest assured that we read your message and appreciate
your interest!
We maintain a [loose roadmap on our wiki](https://github.com/jorisroovers/gitlint/wiki/Roadmap), but
that's open to a lot of change and input.
# Guidelines
When contributing code, please consider all the parts that are typically required:
- [Unit tests](https://github.com/jorisroovers/gitlint/tree/master/gitlint/tests) (automatically
[enforced by CI](https://github.com/jorisroovers/gitlint/actions)). Please consider writing
new ones for your functionality, not only updating existing ones to make the build pass.
- [Integration tests](https://github.com/jorisroovers/gitlint/tree/master/qa) (also automatically
[enforced by CI](https://github.com/jorisroovers/gitlint/actions)). Again, please consider writing new ones
for your functionality, not only updating existing ones to make the build pass.
- [Documentation](https://github.com/jorisroovers/gitlint/tree/master/docs)
Since we want to maintain a high standard of quality, all of these things will have to be done regardless before code
can make it as part of a release. If you can already include them as part of your PR, it's a huge timesaver for us
and it's likely that your PR will be merged and released a lot sooner. Thanks!
# Development #
There is a Vagrantfile in this repository that can be used for development.
```bash
vagrant up
vagrant ssh
```
Or you can choose to use your local environment:
```bash
virtualenv .venv
pip install -r requirements.txt -r test-requirements.txt -r doc-requirements.txt
python setup.py develop
```
To run tests:
```bash
./run_tests.sh # run unit tests and print test coverage
./run_test.sh gitlint/tests/test_body_rules.py::BodyRuleTests::test_body_missing # run a single test
./run_tests.sh --no-coverage # run unit tests without test coverage
./run_tests.sh --collect-only --no-coverage # Only collect, don't run unit tests
./run_tests.sh --integration # Run integration tests (requires that you have gitlint installed)
./run_tests.sh --build # Run build tests (=build python package)
./run_tests.sh --pep8 # pep8 checks
./run_tests.sh --stats # print some code stats
./run_tests.sh --git # inception: run gitlint against itself
./run_tests.sh --lint # run pylint checks
./run_tests.sh --all # Run unit, integration, pep8 and gitlint checks
```
The ```Vagrantfile``` comes with ```virtualenv```s for python 2.7, 3.5, 3.6, 3.7 and pypy2.
You can easily run tests against specific python environments by using the following commands *inside* of the Vagrant VM:
```
./run_tests.sh --envs 27 # Run the unit tests against Python 2.7
./run_tests.sh --envs 27,35,pypy2 # Run the unit tests against Python 2.7, Python 3.5 and Pypy2
./run_tests.sh --envs 27,35 --pep8 # Run pep8 checks against Python 2.7 and Python 3.5 (also works for ```--git```, ```--integration```, ```--pep8```, ```--stats``` and ```--lint```).
./run_tests.sh --envs all --all # Run all tests against all environments
./run_tests.sh --all-env --all # Idem: Run all tests against all environments
```
!!! important
Gitlint commits and pull requests are gated on all of our tests and checks.
# Packaging #
To see the package description in HTML format
```
pip install docutils
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
python setup.py --long-description | rst2html.py > output.html
```
# Documentation #
We use [mkdocs](https://www.mkdocs.org/) for generating our documentation from markdown.
To use it, do the following outside of the vagrant box (on your host machine):
```bash
pip install -r doc-requirements.txt # install doc requirements
mkdocs serve
```
Then access the documentation website on your host machine on [http://localhost:8000]().
# Tools #
We keep a small set of scripts in the ```tools/``` directory:
```sh
tools/create-test-repo.sh # Create a test git repo in your /tmp directory
tools/windows/create-test-repo.bat # Windows: create git test repo
tools/windows/run_tests.bat # Windows run unit tests
```
# Contrib rules
Since gitlint 0.12.0, we support [Contrib rules](../contrib_rules): community contributed rules that are part of gitlint
itself. Thanks for considering to add a new one to gitlint!
Before starting, please read all the other documentation on this page about contributing first.
Then, we suggest taking the following approach to add a Contrib rule:
1. **Write your rule as a [user-defined rule](../user_defined_rules)**. In terms of code, Contrib rules are identical to
user-defined rules, they just happen to have their code sit within the gitlint codebase itself.
2. **Add your user-defined rule to gitlint**. You should put your file(s) in the [gitlint/contrib/rules](https://github.com/jorisroovers/gitlint/tree/master/gitlint/contrib/rules) directory.
3. **Write unit tests**. The gitlint codebase contains [Contrib rule test files you can copy and modify](https://github.com/jorisroovers/gitlint/tree/master/gitlint/tests/contrib).
4. **Write documentation**. In particular, you should update the [gitlint/docs/contrib_rules.md](https://github.com/jorisroovers/gitlint/blob/master/docs/contrib_rules.md) file with details on your Contrib rule.
5. **Create a Pull Request**: code review typically requires a bit of back and forth. Thanks for your contribution!
## Contrib rule requirements
If you follow the steps above and follow the existing gitlint conventions wrt naming things, you should already be fairly close to done.
In case you're looking for a slightly more formal spec, here's what gitlint requires of Contrib rules.
- Since Contrib rules are really just user-defined rules that live within the gitlint code-base, all the [user-rule requirements](../user_defined_rules/#rule-requirements) also apply to Contrib rules.
- All contrib rules **must** have associated unit tests. We *sort of* enforce this by a unit test that verifies that there's a
test file for each contrib file.
- All contrib rules **must** have names that start with `contrib-`. This is to easily distinguish them from default gitlint rules.
- All contrib rule ids **must** start with `CT` (for LineRules targeting the title), `CB` (for LineRules targeting the body) or `CC` (for CommitRules). Again, this is to easily distinguish them from default gitlint rules.
- All contrib rules **must** have unique names and ids.
- You **can** add multiple rule classes to the same file, but classes **should** be logically grouped together in a single file that implements related rules.
- Contrib rules **should** be meaningfully different from one another. If a behavior change or tweak can be added to an existing rule by adding options, that should be considered first. However, large [god classes](https://en.wikipedia.org/wiki/God_object) that implement multiple rules in a single class should obviously also be avoided.
- Contrib rules **should** use [options](../user_defined_rules/#options) to make rules configurable.

3798
docs/demos/asciicinema.json Normal file

File diff suppressed because it is too large Load diff

75
docs/demos/scenario.txt Normal file
View file

@ -0,0 +1,75 @@
sudo pip uninstall gitlint
virtualenv ~/gitlint-demo
source ~/gitlint-demo
mkdir ~/my-git-repo
git init
echo "test" > myfile.txt
git add .
git commit
WIP: This is a commit message title.
Second line not empty
This body line exceeds the defacto standard length of 80 characters per line in a commit m
essage.
cd ..
asciicinema rec demo.json
------------------------------------
pip install gitlint
# Go to your git repo
cd my-git-repo
# Run gitlint to check for violations in the last commit message
gitlint
# For reference, here you can see that last commit message
git log -1
# You can also install gitlint as a git commit-msg hook
gitlint install-hook
# Let's try it out
echo "This is a test" > foo.txt
git add .
git commit
WIP: Still working on this awesome patchset that will change the world forever!
[Keep commit -> yes]
# You can modify gitlint's behavior by adding a .gitlint file
gitlint generate-config
vim .gitlint
gitlint
# Or specify additional config via the commandline
gitlint --ignore title-trailing-punctuation
# For more info, visit: http://jorisroovers.github.io/gitlint
exit
------------------------------

4
docs/extra.css Normal file
View file

@ -0,0 +1,4 @@
a.toctree-l3 {
margin-left: 10px;
/* display: none; */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

351
docs/index.md Normal file
View file

@ -0,0 +1,351 @@
# Intro
Gitlint is a git commit message linter written in python: it checks your commit messages for style.
Great for use as a [commit-msg git hook](#using-gitlint-as-a-commit-msg-hook) or as part of your gating script in a
[CI pipeline (e.g. Jenkins)](index.md#using-gitlint-in-a-ci-environment).
<script type="text/javascript" src="https://asciinema.org/a/30477.js" id="asciicast-30477" async></script>
!!! note
**Gitlint support for Windows is experimental**, and [there are some known issues](https://github.com/jorisroovers/gitlint/issues?q=is%3Aissue+is%3Aopen+label%3Awindows).
Also, gitlint is not the only git commit message linter out there, if you are looking for an alternative written in a different language,
have a look at [fit-commit](https://github.com/m1foley/fit-commit) (Ruby),
[node-commit-msg](https://github.com/clns/node-commit-msg) (Node.js) or [commitlint](http://marionebl.github.io/commitlint) (Node.js).
## Features ##
- **Commit message hook**: [Auto-trigger validations against new commit message right when you're committing](#using-gitlint-as-a-commit-msg-hook). Also [works with pre-commit](#using-gitlint-through-pre-commit).
- **Easily integrated**: Gitlint is designed to work [with your own scripts or CI system](#using-gitlint-in-a-ci-environment).
- **Sane defaults:** Many of gitlint's validations are based on
[well-known](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
[community](http://addamhardy.com/2013/06/05/good-commit-messages-and-enforcing-them-with-git-hooks.html),
[standards](http://chris.beams.io/posts/git-commit/), others are based on checks that we've found
useful throughout the years.
- **Easily configurable:** Gitlint has sane defaults, but [you can also easily customize it to your own liking](configuration.md).
- **Community contributed rules**: Conventions that are common but not universal [can be selectively enabled](contrib_rules).
- **User-defined rules:** Want to do more then what gitlint offers out of the box? Write your own [user defined rules](user_defined_rules.md).
- **Broad python version support:** Gitlint supports python versions 2.7, 3.5+, PyPy2 and PyPy3.5.
- **Full unicode support:** Lint your Russian, Chinese or Emoji commit messages with ease!
- **Production-ready:** Gitlint checks a lot of the boxes you're looking for: actively maintained, high unit test coverage, integration tests,
python code standards (pep8, pylint), good documentation, widely used, proven track record.
# Getting Started
## Installation
```bash
# Pip is recommended to install the latest version
pip install gitlint
# macOS
brew tap rockyluke/devops
brew install gitlint
# Ubuntu
apt-get install gitlint
# Docker: https://hub.docker.com/r/jorisroovers/gitlint
docker run -v $(pwd):/repo jorisroovers/gitlint
```
## Usage
```sh
# Check the last commit message
gitlint
# Alternatively, pipe a commit message to gitlint:
cat examples/commit-message-1 | gitlint
# or
git log -1 --pretty=%B | gitlint
# Or read the commit-msg from a file, like so:
gitlint --msg-filename examples/commit-message-2
# Lint all commits in your repo
gitlint --commits HEAD
# To install a gitlint as a commit-msg git hook:
gitlint install-hook
```
Output example:
```bash
$ cat examples/commit-message-2 | gitlint
1: T1 Title exceeds max length (134>80): "This is the title of a commit message that is over 80 characters and contains hard tabs and trailing whitespace and the word wiping "
1: T2 Title has trailing whitespace: "This is the title of a commit message that is over 80 characters and contains hard tabs and trailing whitespace and the word wiping "
1: T4 Title contains hard tab characters (\t): "This is the title of a commit message that is over 80 characters and contains hard tabs and trailing whitespace and the word wiping "
2: B4 Second line is not empty: "This line should not contain text"
3: B1 Line exceeds max length (125>80): "Lines typically need to have a max length, meaning that they can't exceed a preset number of characters, usually 80 or 120. "
3: B2 Line has trailing whitespace: "Lines typically need to have a max length, meaning that they can't exceed a preset number of characters, usually 80 or 120. "
3: B3 Line contains hard tab characters (\t): "Lines typically need to have a max length, meaning that they can't exceed a preset number of characters, usually 80 or 120. "
```
!!! note
The returned exit code equals the number of errors found. [Some exit codes are special](index.md#exit-codes).
# Configuration
For in-depth documentation of general and rule-specific configuration options, have a look at the [Configuration](configuration.md) and [Rules](rules.md) pages.
Short example ```.gitlint``` file ([full reference](configuration.md)):
```ini
[general]
# Ignore certain rules (comma-separated list), you can reference them by
# their id or by their full name
ignore=body-is-missing,T3
# Ignore any data send to gitlint via stdin
ignore-stdin=true
# Configure title-max-length rule, set title length to 80 (72 = default)
[title-max-length]
line-length=80
# You can also reference rules by their id (B1 = body-max-line-length)
[B1]
line-length=123
```
Example use of flags:
```bash
# Change gitlint's verbosity.
$ gitlint -v
# Ignore certain rules
$ gitlint --ignore body-is-missing,T3
# Enable debug mode
$ gitlint --debug
# Load user-defined rules (see http://jorisroovers.github.io/gitlint/user_defined_rules)
$ gitlint --extra-path /home/joe/mygitlint_rules
```
Other commands and variations:
```no-highlight
$ gitlint --help
Usage: gitlint [OPTIONS] COMMAND [ARGS]...
Git lint tool, checks your git commit messages for styling issues
Documentation: http://jorisroovers.github.io/gitlint
Options:
--target DIRECTORY Path of the target git repository. [default:
current working directory]
-C, --config FILE Config file location [default: .gitlint]
-c TEXT Config flags in format <rule>.<option>=<value>
(e.g.: -c T1.line-length=80). Flag can be used
multiple times to set multiple config values.
--commits TEXT The range of commits to lint. [default: HEAD]
-e, --extra-path PATH Path to a directory or python module with extra
user-defined rules
--ignore TEXT Ignore rules (comma-separated by id or name).
--contrib TEXT Contrib rules to enable (comma-separated by id or
name).
--msg-filename FILENAME Path to a file containing a commit-msg.
--ignore-stdin Ignore any stdin data. Useful for running in CI
server.
--staged Read staged commit meta-info from the local
repository.
-v, --verbose Verbosity, more v's for more verbose output (e.g.:
-v, -vv, -vvv). [default: -vvv]
-s, --silent Silent mode (no output). Takes precedence over -v,
-vv, -vvv.
-d, --debug Enable debugging output.
--version Show the version and exit.
--help Show this message and exit.
Commands:
generate-config Generates a sample gitlint config file.
install-hook Install gitlint as a git commit-msg hook.
lint Lints a git repository [default command]
uninstall-hook Uninstall gitlint commit-msg hook.
When no COMMAND is specified, gitlint defaults to 'gitlint lint'.
```
# Using gitlint as a commit-msg hook ##
_Introduced in gitlint v0.4.0_
You can also install gitlint as a git ```commit-msg``` hook so that gitlint checks your commit messages automatically
after each commit.
```bash
gitlint install-hook
# To remove the hook
gitlint uninstall-hook
```
!!! important
Gitlint cannot work together with an existing hook. If you already have a ```.git/hooks/commit-msg```
file in your local repository, gitlint will refuse to install the ```commit-msg``` hook. Gitlint will also only
uninstall unmodified commit-msg hooks that were installed by gitlint.
If you're looking to use gitlint in conjunction with other hooks, you should consider
[using gitlint with pre-commit](#using-gitlint-through-pre-commit).
# Using gitlint through [pre-commit](https://pre-commit.com)
`gitlint` can be configured as a plugin for the `pre-commit` git hooks
framework. Simply add the configuration to your `.pre-commit-config.yaml`:
```yaml
- repo: https://github.com/jorisroovers/gitlint
rev: # Fill in a tag / sha here
hooks:
- id: gitlint
```
You then need to install the pre-commit hook like so:
```sh
pre-commit install --hook-type commit-msg
```
!!! important
It's important that you run ```pre-commit install --hook-type commit-msg```, even if you've already used
```pre-commit install``` before. ```pre-commit install``` does **not** install commit-msg hooks by default!
To manually trigger gitlint using ```pre-commit``` for your last commit message, use the following command:
```sh
pre-commit run gitlint --hook-stage commit-msg --commit-msg-filename .git/COMMIT_EDITMSG
```
In case you want to change gitlint's behavior, you should either use a `.gitlint` file
(see [Configuration](configuration.md)) or modify the gitlint invocation in
your `.pre-commit-config.yaml` file like so:
```yaml
- repo: https://github.com/jorisroovers/gitlint
rev: # Fill in a tag / sha here
hooks:
- id: gitlint
stages: [commit-msg]
entry: gitlint
args: [--contrib=CT1, --msg-filename]
```
# Using gitlint in a CI environment ##
By default, when just running ```gitlint``` without additional parameters, gitlint lints the last commit in the current
working directory.
This makes it easy to use gitlint in a CI environment (Jenkins, TravisCI, Github Actions, pre-commit, CircleCI, Gitlab, etc).
In fact, this is exactly what we do ourselves: on every commit,
[we run gitlint as part of our CI checks](https://github.com/jorisroovers/gitlint/blob/v0.12.0/run_tests.sh#L133-L134).
This will cause the build to fail when we submit a bad commit message.
Alternatively, gitlint will also lint any commit message that you feed it via stdin like so:
```bash
# lint the last commit message
git log -1 --pretty=%B | gitlint
# lint a specific commit: 62c0519
git log -1 --pretty=%B 62c0519 | gitlint
```
Note that gitlint requires that you specify ```--pretty=%B``` (=only print the log message, not the metadata),
future versions of gitlint might fix this and not require the ```--pretty``` argument.
## Linting a range of commits ##
_Introduced in gitlint v0.9.0 (experimental in v0.8.0)_
Gitlint allows users to commit a number of commits at once like so:
```bash
# Lint a specific commit range:
gitlint --commits "019cf40...d6bc75a"
# You can also use git's special references:
gitlint --commits "origin..HEAD"
# Or specify a single specific commit in refspec format, like so:
gitlint --commits "019cf40^...019cf40"
```
The ```--commits``` flag takes a **single** refspec argument or commit range. Basically, any range that is understood
by [git rev-list](https://git-scm.com/docs/git-rev-list) as a single argument will work.
Prior to v0.8.1 gitlint didn't support this feature. However, older versions of gitlint can still lint a range or set
of commits at once by creating a simple bash script that pipes the commit messages one by one into gitlint. This
approach can still be used with newer versions of gitlint in case ```--commits``` doesn't provide the flexibility you
are looking for.
```bash
#!/bin/bash
for commit in $(git rev-list master); do
commit_msg=$(git log -1 --pretty=%B $commit)
echo "$commit"
echo "$commit_msg" | gitlint
echo "--------"
done
```
!!! note
One downside to this approach is that you invoke gitlint once per commit vs. once per set of commits.
This means you'll incur the gitlint startup time once per commit, making this approach rather slow if you want to
lint a large set of commits. Always use ```--commits``` if you can to avoid this performance penalty.
# Merge, fixup and squash commits ##
_Introduced in gitlint v0.7.0 (merge), v0.9.0 (fixup, squash) and v0.13.0 (revert)_
**Gitlint ignores merge, revert, fixup and squash commits by default.**
For merge and revert commits, the rationale for ignoring them is
that most users keep git's default messages for these commits (i.e *Merge/Revert "[original commit message]"*).
Often times these commit messages are also auto-generated through tools like github.
These default/auto-generated commit messages tend to cause gitlint violations.
For example, a common case is that *"Merge:"* being auto-prepended triggers a
[title-max-length](rules.md#t1-title-max-length) violation. Most users don't want this, so we disable linting
on Merge and Revert commits by default.
For [squash](https://git-scm.com/docs/git-commit#git-commit---squashltcommitgt) and [fixup](https://git-scm.com/docs/git-commit#git-commit---fixupltcommitgt) commits, the rationale is that these are temporary
commits that will be squashed into a different commit, and hence the commit messages for these commits are very
short-lived and not intended to make it into the final commit history. In addition, by prepending *"fixup!"* or
*"squash!"* to your commit message, certain gitlint rules might be violated
(e.g. [title-max-length](rules.md#t1-title-max-length)) which is often undesirable.
In case you *do* want to lint these commit messages, you can disable this behavior by setting the
general ```ignore-merge-commits```, ```ignore-revert-commits```, ```ignore-fixup-commits``` or
```ignore-squash-commits``` option to ```false```
[using one of the various ways to configure gitlint](configuration.md).
# Ignoring commits ##
_Introduced in gitlint v0.10.0_
You can configure gitlint to ignore specific commits.
One way to do this, is to by [adding a gitline-ignore line to your commit message](configuration.md#commit-specific-config).
If you have a case where you want to ignore a certain type of commits all-together, you can
use gitlint's *ignore* rules.
Here's an example gitlint file that configures gitlint to ignore rules ```title-max-length``` and ```body-min-length```
for all commits with a title starting with *"Release"*.
```ini
[ignore-by-title]
# Match commit titles starting with Release
regex=^Release(.*)
ignore=title-max-length,body-min-length
# ignore all rules by setting ignore to 'all'
# ignore=all
[ignore-by-body]
# Match commits message bodies that have a line that contains 'release'
regex=(.*)release(.*)
ignore=all
```
!!! note
Right now it's not possible to write user-defined ignore rules to handle more complex use-cases.
This is however something that we'd like to implement in a future version. If this is something you're interested in
please let us know by [opening an issue](https://github.com/jorisroovers/gitlint/issues).
# Exit codes ##
Gitlint uses the exit code as a simple way to indicate the number of violations found.
Some exit codes are used to indicate special errors as indicated in the table below.
Because of these special error codes and the fact that
[bash only supports exit codes between 0 and 255](http://tldp.org/LDP/abs/html/exitcodes.html), the maximum number
of violations counted by the exit code is 252. Note that gitlint does not have a limit on the number of violations
it can detect, it will just always return with exit code 252 when the number of violations is greater than or equal
to 252.
Exit Code | Description
-----------|------------------------------------------------------------
253 | Wrong invocation of the ```gitlint``` command.
254 | Something went wrong when invoking git.
255 | Invalid gitlint configuration

243
docs/rules.md Normal file
View file

@ -0,0 +1,243 @@
# Overview #
The table below shows an overview of all gitlint's built-in rules.
Note that you can also [write your own user-defined rule](user_defined_rules.md) in case you don't find
what you're looking for.
The rest of this page contains details on the available configuration options for each built-in rule.
ID | Name | gitlint version | Description
------|-----------------------------|-------------------|-------------------------------------------
T1 | title-max-length | >= 0.1.0 | Title length must be &lt; 72 chars.
T2 | title-trailing-whitespace | >= 0.1.0 | Title cannot have trailing whitespace (space or tab)
T3 | title-trailing-punctuation | >= 0.1.0 | Title cannot have trailing punctuation (?:!.,;)
T4 | title-hard-tab | >= 0.1.0 | Title cannot contain hard tab characters (\t)
T5 | title-must-not-contain-word | >= 0.1.0 | Title cannot contain certain words (default: "WIP")
T6 | title-leading-whitespace | >= 0.4.0 | Title cannot have leading whitespace (space or tab)
T7 | title-match-regex | >= 0.5.0 | Title must match a given regex (default: .*)
B1 | body-max-line-length | >= 0.1.0 | Lines in the body must be &lt; 80 chars
B2 | body-trailing-whitespace | >= 0.1.0 | Body cannot have trailing whitespace (space or tab)
B3 | body-hard-tab | >= 0.1.0 | Body cannot contain hard tab characters (\t)
B4 | body-first-line-empty | >= 0.1.0 | First line of the body (second line of commit message) must be empty
B5 | body-min-length | >= 0.4.0 | Body length must be at least 20 characters
B6 | body-is-missing | >= 0.4.0 | Body message must be specified
B7 | body-changed-file-mention | >= 0.4.0 | Body must contain references to certain files if those files are changed in the last commit
M1 | author-valid-email | >= 0.9.0 | Author email address must be a valid email address
I1 | ignore-by-title | >= 0.10.0 | Ignore a commit based on matching its title
I2 | ignore-by-body | >= 0.10.0 | Ignore a commit based on matching its body
## T1: title-max-length ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
T1 | title-max-length | >= 0.1 | Title length must be &lt; 72 chars.
### Options ###
Name | gitlint version | Default | Description
---------------|-----------------|---------|----------------------------------
line-length | >= 0.2 | 72 | Maximum allowed title length
## T2: title-trailing-whitespace ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
T2 | title-trailing-whitespace | >= 0.1 | Title cannot have trailing whitespace (space or tab)
## T3: title-trailing-punctuation ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
T3 | title-trailing-punctuation | >= 0.1 | Title cannot have trailing punctuation (?:!.,;)
## T4: title-hard-tab ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
T4 | title-hard-tab | >= 0.1 | Title cannot contain hard tab characters (\t)
## T5: title-must-not-contain-word ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
T5 | title-must-not-contain-word | >= 0.1 | Title cannot contain certain words (default: "WIP")
### Options ###
Name | gitlint version | Default | Description
---------------|-----------------|---------|----------------------------------
words | >= 0.3 | WIP | Comma-separated list of words that should not be used in the title. Matching is case insensitive
## T6: title-leading-whitespace ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
T6 | title-leading-whitespace | >= 0.4 | Title cannot have leading whitespace (space or tab)
## T7: title-match-regex ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
T7 | title-match-regex | >= 0.5 | Title must match a given regex (default: .*)
### Options ###
Name | gitlint version | Default | Description
---------------|-----------------|---------|----------------------------------
regex | >= 0.5 | .* | [Python-style regular expression](https://docs.python.org/3.5/library/re.html) that the title should match.
## B1: body-max-line-length ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
B1 | body-max-line-length | >= 0.1 | Lines in the body must be &lt; 80 chars
### Options ###
Name | gitlint version | Default | Description
---------------|-----------------|---------|----------------------------------
line-length | >= 0.2 | 80 | Maximum allowed line length in the commit message body
## B2: body-trailing-whitespace ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
B2 | body-trailing-whitespace | >= 0.1 | Body cannot have trailing whitespace (space or tab)
## B3: body-hard-tab ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
B3 | body-hard-tab | >= 0.1 | Body cannot contain hard tab characters (\t)
## B4: body-first-line-empty ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
B4 | body-first-line-empty | >= 0.1 | First line of the body (second line of commit message) must be empty
## B5: body-min-length ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
B5 | body-min-length | >= 0.4 | Body length must be at least 20 characters. In versions >= 0.8.0, gitlint will not count newline characters.
### Options ###
Name | gitlint version | Default | Description
---------------|-----------------|---------|----------------------------------
min-length | >= 0.4 | 20 | Minimum number of required characters in body
## B6: body-is-missing ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
B6 | body-is-missing | >= 0.4 | Body message must be specified
### Options ###
Name | gitlint version | Default | Description
----------------------|-----------------|-----------|----------------------------------
ignore-merge-commits | >= 0.4 | true | Whether this rule should be ignored during merge commits. Allowed values: true,false.
## B7: body-changed-file-mention ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
B7 | body-changed-file-mention | >= 0.4 | Body must contain references to certain files if those files are changed in the last commit
### Options ###
Name | gitlint version | Default | Description
----------------------|-----------------|--------------|----------------------------------
files | >= 0.4 | (empty) | Comma-separated list of files that need to an explicit mention in the commit message in case they are changed.
## M1: author-valid-email ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
M1 | author-valid-email | >= 0.8.3 | Author email address must be a valid email address
!!! note
Email addresses are [notoriously hard to validate and the official email valid spec is often too loose for any real world application](http://stackoverflow.com/a/201378/381010).
Gitlint by default takes a pragmatic approach and requires users to enter email addresses that contain a name, domain and tld and has no spaces.
### Options ###
Name | gitlint version | Default | Description
----------------------|-------------------|------------------------------|----------------------------------
regex | >= 0.9.0 | ```[^@ ]+@[^@ ]+\.[^@ ]+``` | Regex the commit author email address is matched against
!!! note
An often recurring use-case is to only allow email addresses from a certain domain. The following regular expression achieves this: ```[^@]+@foo.com```
## I1: ignore-by-title ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
I1 | ignore-by-title | >= 0.10.0 | Ignore a commit based on matching its title.
### Options ###
Name | gitlint version | Default | Description
----------------------|-------------------|------------------------------|----------------------------------
regex | >= 0.10.0 | None | Regex to match against commit title. On match, the commit will be ignored.
ignore | >= 0.10.0 | all | Comma-seperated list of rule names or ids to ignore when this rule is matched.
### Examples
#### .gitlint
```ini
# Match commit titles starting with Release
# For those commits, ignore title-max-length and body-min-length rules
[ignore-by-title]
regex=^Release(.*)
ignore=title-max-length,body-min-length
# ignore all rules by setting ignore to 'all'
# ignore=all
```
## I2: ignore-by-body ##
ID | Name | gitlint version | Description
------|-----------------------------|-----------------|-------------------------------------------
I2 | ignore-by-body | >= 0.10.0 | Ignore a commit based on matching its body.
### Options ###
Name | gitlint version | Default | Description
----------------------|-------------------|------------------------------|----------------------------------
regex | >= 0.10.0 | None | Regex to match against each line of the body. On match, the commit will be ignored.
ignore | >= 0.10.0 | all | Comma-seperated list of rule names or ids to ignore when this rule is matched.
### Examples
#### .gitlint
```ini
# Ignore all commits with a commit message body with a line that contains 'release'
[ignore-by-body]
regex=(.*)release(.*)
ignore=all
# For matching commits, only ignore rules T1, body-min-length, B6.
# You can use both names as well as ids to refer to other rules.
[ignore-by-body]
regex=(.*)release(.*)
ignore=T1,body-min-length,B6
```

312
docs/user_defined_rules.md Normal file
View file

@ -0,0 +1,312 @@
# User Defined Rules
_Introduced in gitlint v0.8.0_
Gitlint supports the concept of **user-defined** rules: the ability for users to write their own custom rules in python.
In a nutshell, use ```--extra-path /home/joe/myextensions``` to point gitlint to a ```myextensions``` directory where it will search
for python files containing gitlint rule classes. You can also specify a single python module, ie
```--extra-path /home/joe/my_rules.py```.
```bash
cat examples/commit-message-1 | gitlint --extra-path examples/
# Example output of a user-defined Signed-Off-By rule
1: UC2 Body does not contain a 'Signed-Off-By Line'
# other violations were removed for brevity
```
The `SignedOffBy` user-defined ```CommitRule``` was discovered by gitlint when it scanned
[examples/gitlint/my_commit_rules.py](https://github.com/jorisroovers/gitlint/blob/master/examples/my_commit_rules.py),
which is part of the examples directory that was passed via ```--extra-path```:
```python
from gitlint.rules import CommitRule, RuleViolation
class SignedOffBy(CommitRule):
""" This rule will enforce that each commit contains a "Signed-Off-By" line.
We keep things simple here and just check whether the commit body contains a
line that starts with "Signed-Off-By".
"""
# A rule MUST have a human friendly name
name = "body-requires-signed-off-by"
# A rule MUST have a *unique* id, we recommend starting with UC
# (for User-defined Commit-rule).
id = "UC2"
def validate(self, commit):
for line in commit.message.body:
if line.startswith("Signed-Off-By"):
return
msg = "Body does not contain a 'Signed-Off-By' line"
return [RuleViolation(self.id, msg, line_nr=1)]
```
As always, ```--extra-path``` can also be set by adding it under the ```[general]``` section in your ```.gitlint``` file or using
[one of the other ways to configure gitlint](configuration.md).
If you want to check whether your rules are properly discovered by gitlint, you can use the ```--debug``` flag:
```bash
$ gitlint --debug --extra-path examples/
# [output cut for brevity]
UC1: body-max-line-count
body-max-line-count=3
UC2: body-requires-signed-off-by
UL1: title-no-special-chars
special-chars=['$', '^', '%', '@', '!', '*', '(', ')']
```
!!! Note
In most cases it's really the easiest to just copy an example from the
[examples](https://github.com/jorisroovers/gitlint/tree/master/examples) directory and modify it to your needs.
The remainder of this page contains the technical details, mostly for reference.
# Line and Commit Rules ##
The ```SignedOffBy``` class above was an example of a user-defined ```CommitRule```. Commit rules are gitlint rules that
act on the entire commit at once. Once the rules are discovered, gitlint will automatically take care of applying them
to the entire commit. This happens exactly once per commit.
A ```CommitRule``` contrasts with a ```LineRule```
(see e.g.: [examples/my_line_rules.py](https://github.com/jorisroovers/gitlint/blob/master/examples/my_line_rules.py))
in that a ```CommitRule``` is only applied once on an entire commit while a ```LineRule``` is applied for every line in the commit
(you can also apply it once to the title using a ```target``` - see the examples section below).
The benefit of a commit rule is that it allows commit rules to implement more complex checks that span multiple lines and/or checks
that should only be done once per commit.
While every ```LineRule``` can be implemented as a ```CommitRule```, it's usually easier and more concise to go with a ```LineRule``` if
that fits your needs.
## Examples ##
In terms of code, writing your own ```CommitRule``` or ```LineRule``` is very similar.
The only 2 differences between a ```CommitRule``` and a ```LineRule``` are the parameters of the ```validate(...)``` method and the extra
```target``` attribute that ```LineRule``` requires.
Consider the following ```CommitRule``` that can be found in [examples/my_commit_rules.py](https://github.com/jorisroovers/gitlint/blob/master/examples/my_commit_rules.py):
```python
from gitlint.rules import CommitRule, RuleViolation
class SignedOffBy(CommitRule):
""" This rule will enforce that each commit contains a "Signed-Off-By" line.
We keep things simple here and just check whether the commit body contains a
line that starts with "Signed-Off-By".
"""
# A rule MUST have a human friendly name
name = "body-requires-signed-off-by"
# A rule MUST have a *unique* id, we recommend starting with UC
# (for User-defined Commit-rule).
id = "UC2"
def validate(self, commit):
for line in commit.message.body:
if line.startswith("Signed-Off-By"):
return []
msg = "Body does not contain a 'Signed-Off-By Line'"
return [RuleViolation(self.id, msg, line_nr=1)]
```
Note the use of the ```name``` and ```id``` class attributes and the ```validate(...)``` method taking a single ```commit``` parameter.
Contrast this with the following ```LineRule``` that can be found in [examples/my_line_rules.py](https://github.com/jorisroovers/gitlint/blob/master/examples/my_line_rules.py):
```python
from gitlint.rules import LineRule, RuleViolation, CommitMessageTitle
from gitlint.options import ListOption
class SpecialChars(LineRule):
""" This rule will enforce that the commit message title does not contai
any of the following characters:
$^%@!*() """
# A rule MUST have a human friendly name
name = "title-no-special-chars"
# A rule MUST have a *unique* id, we recommend starting with UL
# for User-defined Line-rule), but this can really be anything.
id = "UL1"
# A line-rule MUST have a target (not required for CommitRules).
target = CommitMessageTitle
# A rule MAY have an option_spec if its behavior should be configurable.
options_spec = [ListOption('special-chars', ['$', '^', '%', '@', '!', '*', '(', ')'],
"Comma separated list of characters that should not occur in the title")]
def validate(self, line, commit):
violations = []
# option values can be accessed via self.options
for char in self.options['special-chars'].value:
if char in line:
violation = RuleViolation(self.id, "Title contains the special character '{}'".format(char), line)
violations.append(violation)
return violations
```
Note the following 2 differences:
- **extra ```target``` class attribute**: in this example set to ```CommitMessageTitle``` indicating that this ```LineRule```
should only be applied once to the commit message title. The alternative value for ```target``` is ```CommitMessageBody```,
in which case gitlint will apply
your rule to **every** line in the commit message body.
- **```validate(...)``` takes 2 parameters**: Line rules get the ```line``` against which they are applied as the first parameter and
the ```commit``` object of which the line is part of as second.
In addition, you probably also noticed the extra ```options_spec``` class attribute which allows you to make your rules configurable.
Options are not unique to ```LineRule```s, they can also be used by ```CommitRule```s and are further explained in the
[Options](user_defined_rules.md#options) section below.
# The commit object ##
Both ```CommitRule```s and ```LineRule```s take a ```commit``` object in their ```validate(...)``` methods.
The table below outlines the various attributes of that commit object that can be used during validation.
Property | Type | Description
-------------------------------| ---------------|-------------------
commit.message | object | Python object representing the commit message
commit.message.original | string | Original commit message as returned by git
commit.message.full | string | Full commit message, with comments (lines starting with #) removed.
commit.message.title | string | Title/subject of the commit message: the first line
commit.message.body | string[] | List of lines in the body of the commit message (i.e. starting from the second line)
commit.author_name | string | Name of the author, result of ```git log --pretty=%aN```
commit.author_email | string | Email of the author, result of ```git log --pretty=%aE```
commit.date | datetime | Python ```datetime``` object representing the time of commit
commit.is_merge_commit | boolean | Boolean indicating whether the commit is a merge commit or not.
commit.is_revert_commit | boolean | Boolean indicating whether the commit is a revert commit or not.
commit.is_fixup_commit | boolean | Boolean indicating whether the commit is a fixup commit or not.
commit.is_squash_commit | boolean | Boolean indicating whether the commit is a squash commit or not.
commit.parents | string[] | List of parent commit ```sha```s (only for merge commits).
commit.changed_files | string[] | List of files changed in the commit (relative paths).
commit.branches | string[] | List of branch names the commit is part of
commit.context | object | Object pointing to the bigger git context that the commit is part of
commit.context.current_branch | string | Name of the currently active branch (of local repo)
commit.context.repository_path | string | Absolute path pointing to the git repository being linted
commit.context.commits | object[] | List of commits gitlint is acting on, NOT all commits in the repo.
# Violations ##
In order to let gitlint know that there is a violation in the commit being linted, users should have the ```validate(...)```
method in their rules return a list of ```RuleViolation```s.
!!! important
The ```validate(...)``` method doesn't always need to return a list, you can just skip the return statement in case there are no violations.
However, in case of a single violation, validate should return a **list** with a single item.
The ```RuleViolation``` class has the following generic signature:
```
RuleViolation(rule_id, message, content=None, line_nr=None):
```
With the parameters meaning the following:
Parameter | Type | Description
--------------|---------|--------------------------------
rule_id | string | Rule's unique string id
message | string | Short description of the violation
content | string | (optional) the violating part of commit or line
line_nr | int | (optional) line number in the commit message where the violation occurs. **Automatically set to the correct line number for ```LineRule```s if not set explicitly.**
A typical ```validate(...)``` implementation for a ```CommitRule``` would then be as follows:
```python
def validate(self, commit)
for line_nr, line in commit.message.body:
if "Jon Snow" in line:
# we add 1 to the line_nr because we offset the title which is on the first line
return [RuleViolation(self.id, "Commit message has the words 'Jon Snow' in it", line, line_nr + 1)]
return []
```
The parameters of this ```RuleViolation``` can be directly mapped onto gitlint's output as follows:
![How Rule violations map to gitlint output](images/RuleViolation.png)
# Options ##
In order to make your own rules configurable, you can add an optional ```options_spec``` attribute to your rule class
(supported for both ```LineRule``` and ```CommitRule```).
```python
from gitlint.rules import CommitRule, RuleViolation
from gitlint.options import IntOption
class BodyMaxLineCount(CommitRule):
# A rule MUST have a human friendly name
name = "body-max-line-count"
# A rule MUST have a *unique* id, we recommend starting with UC (for
# User-defined Commit-rule).
id = "UC1"
# A rule MAY have an option_spec if its behavior should be configurable.
options_spec = [IntOption('max-line-count', 3, "Maximum body line count")]
def validate(self, commit):
line_count = len(commit.message.body)
max_line_count = self.options['max-line-count'].value
if line_count > max_line_count:
message = "Body contains too many lines ({0} > {1})".format(line_count,
max_line_count)
return [RuleViolation(self.id, message, line_nr=1)]
```
By using ```options_spec```, you make your option available to be configured through a ```.gitlint``` file
or one of the [other ways to configure gitlint](configuration.md). Gitlint automatically takes care of the parsing and input validation.
For example, to change the value of the ```max-line-count``` option, add the following to your ```.gitlint``` file:
```ini
[body-max-line-count]
body-max-line-count=1
```
As ```options_spec``` is a list, you can obviously have multiple options per rule. The general signature of an option is:
```Option(name, default_value, description)```.
Gitlint supports a variety of different option types, all can be imported from ```gitlint.options```:
Option Class | Use for
----------------|--------------
StrOption | Strings
IntOption | Integers. ```IntOption``` takes an optional ```allow_negative``` parameter if you want to allow negative integers.
BoolOption | Booleans. Valid values: `true`, `false`. Case-insensitive.
ListOption | List of strings. Comma separated.
PathOption | Directory or file path. Takes an optional ```type``` parameter for specifying path type (```file```, ```dir``` (=default) or ```both```).
!!! note
Gitlint currently does not support options for all possible types (e.g. float, list of int, etc).
[We could use a hand getting those implemented](contributing.md)!
# Rule requirements ##
As long as you stick with simple rules that are similar to the sample user-defined rules (see the
[examples](https://github.com/jorisroovers/gitlint/blob/master/examples/my_commit_rules.py) directory), gitlint
should be able to discover and execute them. While clearly you can run any python code you want in your rules,
you might run into some issues if you don't follow the conventions that gitlint requires.
While the [rule finding source-code](https://github.com/jorisroovers/gitlint/blob/master/gitlint/rule_finder.py) is the
ultimate source of truth, here are some of the requirements that gitlint enforces.
## Rule class requirements ###
- Rules **must** extend from ```LineRule``` or ```CommitRule```
- Rule classes **must** have ```id``` and ```name``` string attributes. The ```options_spec``` is optional,
but if set, it **must** be a list of gitlint Options.
- Rule classes **must** have a ```validate``` method. In case of a ```CommitRule```, ```validate``` **must** take a single ```commit``` parameter.
In case of ```LineRule```, ```validate``` **must** take ```line``` and ```commit``` as first and second parameters.
- LineRule classes **must** have a ```target``` class attributes that is set to either ```CommitMessageTitle``` or ```CommitMessageBody```.
- User Rule id's **cannot** start with ```R```, ```T```, ```B``` or ```M``` as these rule ids are reserved for gitlint itself.
- Rules **should** have a case-insensitive unique id as only one rule can exist with a given id. While gitlint does not enforce this, having multiple rules with
the same id might lead to unexpected or undeterministic behavior.
## extra-path requirements ###
- If ```extra-path``` is a directory, it does **not** need to be a proper python package, i.e. it doesn't require an ```__init__.py``` file.
- Python files containing user-defined rules must have a ```.py``` extension. Files with a different extension will be ignored.
- The ```extra-path``` will be searched non-recursively, i.e. all rule classes must be present at the top level ```extra-path``` directory.
- User rule classes must be defined in the modules that are part of ```extra-path```, rules that are imported from outside the ```extra-path``` will be ignored.