From 59ae33e64c0a857d2399e0bf82b0bc7e3ed86877 Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Wed, 12 Aug 2026 09:09:37 +0000 Subject: [PATCH 1/2] docs: document --rev and the skip notice, and date v2.14.0 The recipes page gains a revision tab for message checks, the author-check section explains what --rev changes about whose identity is judged, the range recipe drops the stdin pipe for --rev, and a new section documents the skip status: which rules decline merge subjects, the stderr notice, and the JSON status. Both examples are pasted from real runs. The changelog gains the v2.15.0 entry for the engine changes shipping in it and stamps v2.14.0 with its release date. --- docs/changelog.md | 45 ++++++++++++++++++++++++++++- docs/example.md | 72 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5801395..c9fdcea 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -11,6 +11,7 @@ below and to the page that documents the feature properly. | Version | What changed | Documented in | |---|---|---| +| [2.15.0](#v2150) | `--rev` names the commit under test; skipped checks are named on stderr | [Command-line recipes](example.md#checking-a-range-of-commits) | | [2.14.0](#v2140) | CC003 judges imperative mood by a word's form, not by a list of verbs | [CC003](rules.md#cc003) | | [2.13.1](#v2131) | JSON output reports the checked value for passing checks | [Output for scripts and CI](example.md#output-for-scripts-and-ci) | | [2.13.0](#v2130) | Stable rule IDs in terminal output and JSON | [Rules reference](rules.md) | @@ -24,7 +25,49 @@ below and to the page that documents the feature properly. | [2.5.0](#v250) | Organization-wide config with `inherit_from` | [Integrations](guides/integrations.md#across-an-organization) | | [2.0.0](#v200) | Configuration moved from YAML to TOML — breaking | [Migrating from v1](migration.md) | -## v2.14.0 (unreleased) { #v2140 } +## v2.15.0 (unreleased) { #v2150 } + +### Added + +* **`--rev REVISION` names the commit under test** — anything `git rev-parse` + understands: a SHA, `HEAD~2`, `HEAD^2`. Message checks read that commit's + message, and the author checks read **that commit's recorded author, never + the local git config** — an existing commit's identity is a fact about the + commit, not about whoever runs the check. Before `--rev`, CI could not + iterate a pull request's commits without checking each one out, and a + malformed author on any commit passed as long as the operator's own config + was valid. A revision that does not resolve is a one-line error before any + check runs; combining `--rev` with a message file or stdin is rejected, + since each would name a second subject for the same checks. See + [Checking a range of commits](example.md#checking-a-range-of-commits). + +### Fixed + +* **The CLI no longer hangs on an open, idle stdin** — stdin was read + whenever it was not a terminal, for every check type. Under CI runners and + process managers that hand the process a pipe nothing ever writes to or + closes, `commit-check --author-name` blocked forever: a stuck step, not a + failed one. The read is now gated on data actually being available, and + genuinely piped input still works unchanged. + +### Changed + +* **Skipped checks are named on stderr instead of passing in silence** — a + check that had nothing to judge (a merge subject under the subject rules, + an absent message) reports a skip, and text mode prints one line naming + every skipped check: `⊘ skipped (not validated): subject-max-length, + subject-min-length`. Exit codes are unchanged and stdout is untouched. The + case that motivated it: on a `pull_request` checkout `HEAD` is the + synthetic merge commit, so a bare `commit-check -m` used to exit `0` + having validated nothing it was asked about. See + [When a check is skipped](example.md#when-a-check-is-skipped). + +* **Only git's literal merge and fixup prefixes bypass the subject rules** — + the bypass matched any subject starting with the word "merge" in any case, + so an author's own `merge the parser tables` escaped judgement. Now only + the machine-written forms qualify: `Merge ` (and `fixup! ` for CC003). + +## v2.14.0 (2026-08-12) { #v2140 } ### Changed diff --git a/docs/example.md b/docs/example.md index c58d8b0..a3ab3c3 100644 --- a/docs/example.md +++ b/docs/example.md @@ -9,7 +9,8 @@ in [Configuration](configuration.md). ## Checking a commit message -The message can come from the repository, a file, or standard input. +The message can come from the repository, a file, standard input, or a named +revision. === "From the repository" @@ -33,6 +34,21 @@ The message can come from the repository, a file, or standard input. $ echo "feat(auth): add OAuth2 login" | commit-check -m ``` +=== "From a revision" + + `--rev` names the commit under test — anything `git rev-parse` + understands. A revision that does not resolve is a one-line error before + any check runs. + + ```console + $ commit-check -m --rev HEAD~1 + $ commit-check -m --rev 1a2b3c4 + ``` + + A revision and a message file would name two different subjects for the + same checks, so passing both is rejected; stdin is likewise not consulted + while `--rev` is set. + ### Trying a message before you write it ```console @@ -91,6 +107,17 @@ Either flag works alone. [CC101](rules.md#cc101) and [CC102](rules.md#cc102) describe what the built-in patterns accept and how to tighten them. +Without `--rev`, these validate the *local git config* — whoever is about to +commit — falling back to `HEAD`'s author only when no identity is configured. +That is the right subject for a hook and the wrong one for CI: an existing +commit's identity is a fact about the commit, not about the operator running +the check. Add `--rev` and both checks read that commit's recorded author, and +the config is never consulted: + +```console +$ commit-check --author-name --author-email --rev HEAD +``` + ## Blocking force pushes ```console @@ -165,11 +192,13 @@ and how CLI, environment and file settings override each other. ### Checking a range of commits -Nothing built in, but the exit code makes it a one-liner: +`--rev` makes each commit addressable without checking it out or piping its +message, and it is the only way the author checks apply to the commit rather +than to the local config: ```bash title="check-recent.sh" #!/usr/bin/env bash -# Check the last N commit messages; exits non-zero if any fail. +# Check the last N commits; exits non-zero if any fail. # Resolved before the loop rather than inside it: an unreadable range or a # directory that is not a repository would otherwise expand to nothing, and @@ -178,7 +207,7 @@ shas=$(git rev-list -n "${1:-10}" HEAD) || exit 1 status=0 for sha in $shas; do - if ! git log -1 --format=%B "$sha" | commit-check -m --compact; then + if ! commit-check -m --author-name --author-email --rev "$sha" --compact; then echo " ↑ $sha" status=1 fi @@ -186,6 +215,41 @@ done exit $status ``` +On a `pull_request` checkout the same loop covers exactly the commits the PR +adds — `HEAD` is GitHub's synthetic merge commit, whose first parent is the +base branch and second the PR branch: + +```console +$ git rev-list HEAD^1..HEAD^2 +``` + +### When a check is skipped + +A check that had nothing to judge reports a **skip**, not a pass. The common +case is a merge subject: `Merge branch 'x'` is git's writing, so +[CC002](rules.md#cc002), [CC003](rules.md#cc003), [CC004](rules.md#cc004) and +[CC005](rules.md#cc005) decline it rather than grade prose the author never +wrote. Only git's literal `Merge ` prefix qualifies (plus `fixup! ` for +CC003); a subject that merely starts with the lowercase word is judged like +any other. + +Text mode names every skipped check in one line on stderr, leaving stdout and +the exit code untouched — a skip is still not a failure: + +```console +$ echo "Merge branch 'main' into topic" | commit-check -m --no-banner +⊘ skipped (not validated): subject-max-length, subject-min-length +``` + +In JSON each skipped check carries `"status": "skip"`, distinct from `"pass"`. + +!!! warning "A green run can still have validated nothing" + + On a `pull_request` checkout, `HEAD` is the synthetic merge commit — so a + bare `commit-check -m` exits `0` with every subject rule skipped. The + notice makes that visible; the fix is to check what you actually mean: + the PR title piped on stdin, or each branch commit via `--rev` as above. + ### Reading the JSON ```console From 0c9310ed51707e6920d1da4a8889ab49b0439dd4 Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Wed, 12 Aug 2026 09:11:16 +0000 Subject: [PATCH 2/2] docs: bump the install pins to v2.14.0 The docs-sync test pins every install snippet to the released version, which became 2.14.0 today. --- docs/configuration.md | 2 +- docs/example.md | 2 +- docs/guides/integrations.md | 4 ++-- docs/index.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 6d5fe20..6b03041 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -167,7 +167,7 @@ Used from a hook definition, with no config file anywhere in the repository: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.4 + rev: v2.14.0 hooks: - id: check-message args: diff --git a/docs/example.md b/docs/example.md index a3ab3c3..fc443cb 100644 --- a/docs/example.md +++ b/docs/example.md @@ -131,7 +131,7 @@ pushed: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.4 + rev: v2.14.0 hooks: - id: check-no-force-push stages: [pre-push] diff --git a/docs/guides/integrations.md b/docs/guides/integrations.md index 308c19d..e706f57 100644 --- a/docs/guides/integrations.md +++ b/docs/guides/integrations.md @@ -23,7 +23,7 @@ Add Commit Check to `.pre-commit-config.yaml`: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.4 + rev: v2.14.0 hooks: - id: check-message - id: check-branch @@ -70,7 +70,7 @@ Options can be passed as hook arguments, which keeps everything in one file: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.4 + rev: v2.14.0 hooks: - id: check-message args: diff --git a/docs/index.md b/docs/index.md index a721bae..eb5e267 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,7 +36,7 @@ whatever your AI agent is committing on your behalf. ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.4 + rev: v2.14.0 hooks: - id: check-message - id: check-branch