Commit Check¶
Table of Contents
Overview¶
Commit Check (aka cchk) is the most comprehensive open-source tool for enforcing Git commit standards — including commit messages, branch naming, author identity, commit signoff, and more — helping teams maintain consistency and compliance across every repository.
As a lightweight, free alternative to GitHub Enterprise Metadata restrictions and Bitbucket’s paid Yet Another Commit Checker plugin, Commit Check integrates DevOps principles and Infrastructure as Code (IaC) practices for a modern workflow.
Quick Start¶
1. Install and run with zero configuration:
pip install commit-check
commit-check --message --branch
2. Add to your pre-commit hooks (.pre-commit-config.yaml):
repos:
- repo: https://github.com/commit-check/commit-check
rev: v2.6.0
hooks:
- id: check-message
- id: check-branch
3. Add a badge to your repository:
[](https://github.com/commit-check/commit-check)
Installation¶
To install Commit Check, you can use pip:
pip install commit-check
Or install directly from the GitHub repository:
pip install git+https://github.com/commit-check/commit-check.git@main
Then, run commit-check --help or cchk --help (alias for commit-check) from the command line.
For more information, see the docs.
Configuration¶
Commit Check can be configured in three ways (in order of priority):
Command-line arguments — Override settings for specific runs
Environment variables — Configure via
CCHK_*environment variablesConfiguration files — Use
cchk.tomlorcommit-check.toml
Use Default Configuration¶
Commit Check uses a default configuration if you do not provide a
cchk.tomlorcommit-check.tomlfile.The default configuration is lenient — it only checks whether commit messages follow the Conventional Commits specification and branch names follow the Conventional Branch convention.
Use Custom Configuration File¶
To customize the behavior, create a configuration file named cchk.toml or commit-check.toml in your repository’s root directory or in the .github folder, e.g., cchk.toml or .github/cchk.toml.
[commit]
# https://www.conventionalcommits.org
conventional_commits = true
subject_imperative = true
subject_max_length = 80
allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore", "ci"]
allow_merge_commits = true
allow_wip_commits = false
require_signed_off_by = false
# Bypass checks for bot/automation authors and co-authors:
ignore_authors = ["dependabot[bot]", "renovate[bot]", "copilot[bot]"]
[branch]
# https://conventional-branch.github.io/
conventional_branch = true
allow_branch_types = ["feature", "bugfix", "hotfix", "release", "chore", "feat", "fix"]
Organization-Level Configuration (inherit_from)¶
Share a base configuration across all repositories in your organization using inherit_from:
# .github/cchk.toml — inherits from org-level config, then overrides locally
inherit_from = "github:my-org/.github:cchk.toml"
[commit]
subject_max_length = 72 # Local override
The inherit_from field accepts:
A GitHub shorthand (recommended):
inherit_from = "github:owner/repo:path/to/cchk.toml"A GitHub shorthand with ref:
inherit_from = "github:owner/repo@main:path/to/cchk.toml"A local file path (relative or absolute):
inherit_from = "../shared/cchk.toml"An HTTPS URL:
inherit_from = "https://example.com/cchk.toml"
The github: shorthand fetches from raw.githubusercontent.com. HTTP (non-TLS) URLs are rejected for security.
Local settings always override the inherited base configuration.
Use CLI Arguments or Environment Variables¶
For one-off checks or CI/CD pipelines, you can configure via CLI arguments or environment variables:
# Using CLI arguments
commit-check --message --subject-imperative=true --subject-max-length=72
# Using environment variables
export CCHK_SUBJECT_IMPERATIVE=true
export CCHK_SUBJECT_MAX_LENGTH=72
commit-check --message
# In pre-commit hooks (.pre-commit-config.yaml)
repos:
- repo: https://github.com/commit-check/commit-check
rev: v2.6.0
hooks:
- id: check-message
args:
- --subject-imperative=false
- --subject-max-length=100
See the Configuration documentation for all available options.
AI-Native Usage¶
Commit Check is designed to be consumed by AI agents, LLM toolchains, and automation scripts — not just by humans reading terminal output.
Machine-Readable JSON Output (--format json)¶
Pass --format json to any CLI invocation to receive structured JSON instead
of human-readable ASCII art. The exit code is unchanged (0 = pass, 1 = fail),
so existing CI scripts continue to work:
echo "feat: add streaming support" | commit-check -m --format json
{
"status": "pass",
"checks": [
{ "check": "message", "status": "pass", "value": "", "error": "", "suggest": "" },
{ "check": "subject_imperative", "status": "pass", "value": "", "error": "", "suggest": "" }
]
}
On failure the failing checks carry the full error and suggest fields
an agent needs to self-correct:
echo "wip bad commit" | commit-check -m --format json
{
"status": "fail",
"checks": [
{
"check": "message",
"status": "fail",
"value": "wip bad commit",
"error": "The commit message should follow Conventional Commits. See https://www.conventionalcommits.org",
"suggest": "Use <type>(<scope>): <description>, where <type> is one of: feat, fix, docs, ..."
}
]
}
Python API (no subprocess required)¶
The commit_check.api module exposes a lightweight, import-friendly interface
so AI agents, tools, and scripts can validate commits without spawning a
subprocess. All functions return plain dicts that are easy to serialise,
forward to an LLM, or chain into larger workflows:
from commit_check.api import validate_message, validate_branch, validate_all
# --- validate a single commit message ---
result = validate_message("feat: add streaming support")
print(result["status"]) # "pass"
# --- validate a branch name ---
result = validate_branch("feature/add-streaming")
print(result["status"]) # "pass"
# --- run multiple checks at once ---
result = validate_all(
message="feat: implement new feature",
branch="feature/new-feature",
author_name="Ada Lovelace",
author_email="ada@example.com",
)
if result["status"] == "fail":
for check in result["checks"]:
if check["status"] == "fail":
print(f"[{check['check']}] {check['error']}")
print(f" suggestion: {check['suggest']}")
# --- supply a custom config to restrict allowed types ---
result = validate_message(
"docs: update readme",
config={"commit": {"allow_commit_types": ["feat", "fix"]}},
)
print(result["status"]) # "fail" — 'docs' not in allowed types
Return-value schema (all API functions):
{
"status": "pass" | "fail",
"checks": [
{
"check": "<rule name>",
"status": "pass" | "fail",
"value": "<actual value that was checked>",
"error": "<human-readable error description>",
"suggest": "<how to fix>",
},
# ... one entry per active rule
]
}
Available API functions:
validate_message(message, *, config=None)— validate a commit message stringvalidate_branch(branch=None, *, config=None)— validate a branch name (defaults to current git branch)validate_author(name=None, email=None, *, config=None)— validate author name/emailvalidate_all(message, branch, author_name, author_email, *, config=None)— run all checks at once
For detailed usage instructions including pre-commit hooks, CLI commands, and STDIN examples, see the Usage Examples documentation.
Examples¶
Check Commit Message Failed
Commit rejected by Commit-Check.
(c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
/ ._. \ / ._. \ / ._. \ / ._. \ / ._. \
__\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
|| E || || R || || R || || O || || R ||
_.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
`-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
Commit rejected.
Type message check failed ==> test commit message check
The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
Suggest: Use <type>(<scope>): <description>, where <type> is one of: feat, fix, docs, style, refactor, test, chore, ci
Check Branch Naming Failed
Commit rejected by Commit-Check.
(c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
/ ._. \ / ._. \ / ._. \ / ._. \ / ._. \
__\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
|| E || || R || || R || || O || || R ||
_.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
`-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
Commit rejected.
Type branch check failed ==> test-branch
The branch should follow Conventional Branch. See https://conventional-branch.github.io/
Suggest: Use <type>/<description> with allowed types or add branch name to allow_branch_names in config, or use ignore_authors in config branch section to bypass
More examples see example documentation.
Badging your repository¶
You can add a badge to your repository to show that you use commit-check!
Markdown
[](https://github.com/commit-check/commit-check)
reStructuredText
.. image:: https://img.shields.io/badge/commit--check-enabled-brightgreen?logo=Git&logoColor=white&color=%232c9ccd
:target: https://github.com/commit-check/commit-check
:alt: commit-check
Why Commit Check?¶
The table below compares common approaches to commit policy enforcement.
commitlint is a specialized commit-message linter. Custom Git hooks and
the pre-commit framework are integration mechanisms, so the last column
reflects a DIY approach rather than built-in product features.
Feature |
Commit Check ✅ |
commitlint |
Custom hooks |
|---|---|---|---|
Conventional Commits enforcement |
✅ |
✅ |
DIY |
Branch naming validation |
✅ |
❌ |
DIY |
Author name / email validation |
✅ |
❌ |
DIY |
Signed-off-by trailer enforcement |
✅ |
✅ |
DIY |
Co-author ignore list |
✅ |
❌ |
DIY |
Organization-level shared config |
✅ |
✅ |
DIY |
Zero-config defaults |
✅ |
❌ |
❌ |
Works without Node.js |
✅ |
❌ |
Depends |
Native TOML configuration |
✅ |
❌ |
Depends |
Git hook / pre-commit integration |
✅ |
Partial |
✅ |
CI/CD-friendly configuration |
✅ |
Partial |
DIY |
For commitlint, organization-level shared config is typically delivered via
shareable config packages or local files. DIY means you can implement a
capability with custom Git hooks or pre-commit scripts, but it is not
provided as a turnkey policy layer.
Versioning¶
Versioning follows Semantic Versioning.
Have question or feedback?¶
Please post to issues for feedback, feature requests, or bug reports.
License¶
This project is released under the MIT License