* [FFmpeg-devel] [PR] commitcheck (PR #22260)
@ 2026-02-23 2:47 Jun Zhao via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: Jun Zhao via ffmpeg-devel @ 2026-02-23 2:47 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Jun Zhao
PR #22260 opened by Jun Zhao (mypopydev)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22260
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22260.patch
This patch series adds automated commit message validation:
1. **tools/check_commit_msg**: add commit message validation script — A POSIX shell script that checks subject format
(component: description), blank line separation, duplicate Signed-off-by, and squash-mess detection. Supports stdin,
file argument, and revision range input modes.
2. **.forgejo/workflows**: add commit message validation CI workflow — Runs the script on every pull request over
origin/master..HEAD.
3. **.forgejo/pre-commit**: add local commit-msg hook — Registers the script as a commit-msg hook so developers catch issues
locally before pushing.
Tested against the most recent 1000 commits on master: 995/1000 passed. The 5 flagged commits are all genuinely
non-conforming (e.g. **Fix overflow in STSD parser** missing a component prefix, **avutil/dovi_meta - fix ...** using a dash
instead of a colon, a duplicate Signed-off-by in 677cf95ea4be805fa326adb082eff666a2e790ea, and a missing blank line).
From fc2e44caf1f81b35f673d954e560893ec5d523ab Mon Sep 17 00:00:00 2001
From: Jun Zhao <barryjzhao@tencent.com>
Date: Mon, 23 Feb 2026 09:59:01 +0800
Subject: [PATCH 1/3] tools/check_commit_msg: add commit message validation
script
Add a shell script that validates commit messages against FFmpeg
conventions. Supports three entry points: stdin (for CI pipelines),
file argument (for pre-commit commit-msg hook), and revision range
(for manual batch checking).
Checks enforced (errors fail CI):
- Subject matches "component[/module]: description" pattern
- Blank line between subject and body
- No duplicate Signed-off-by from same person
- No multiple subject-like lines in body (squash-mess detection)
Checks that only warn:
- Subject line > 100 characters
- Trailing whitespace on subject line
Usage:
echo "avcodec/vvc: fix pred" | sh tools/check_commit_msg.sh
sh tools/check_commit_msg.sh .git/COMMIT_EDITMSG
sh tools/check_commit_msg.sh HEAD~5..HEAD
Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
---
tools/check_commit_msg.sh | 187 ++++++++++++++++++++++++++++++++++++++
1 file changed, 187 insertions(+)
create mode 100755 tools/check_commit_msg.sh
diff --git a/tools/check_commit_msg.sh b/tools/check_commit_msg.sh
new file mode 100755
index 0000000000..7594748727
--- /dev/null
+++ b/tools/check_commit_msg.sh
@@ -0,0 +1,187 @@
+#!/bin/sh
+#
+# Validate commit messages.
+# Exit code 0 = all pass, non-zero = errors found.
+#
+# Usage:
+# Single message from stdin:
+# git log -1 --format="%B" <commit> | sh tools/check_commit_msg.sh
+#
+# Single message from file (pre-commit commit-msg hook):
+# sh tools/check_commit_msg.sh .git/COMMIT_EDITMSG
+#
+# Multiple commits via revision range:
+# sh tools/check_commit_msg.sh HEAD~5..HEAD
+# sh tools/check_commit_msg.sh origin/master..my-branch
+# sh tools/check_commit_msg.sh -10 # last 10 commits
+#
+# Examples:
+# echo "avcodec/vvc: fix intra prediction" | sh tools/check_commit_msg.sh
+# # => commit message OK
+#
+# echo "fix bug" | sh tools/check_commit_msg.sh
+# # => ERROR: subject does not match 'component: description' pattern: fix bug
+#
+# Checks (errors fail CI, warnings only print):
+# - Subject matches "component[/module]: description" pattern (error)
+# - Subject line <= 100 characters (warning)
+# - Blank line between subject and body (if body exists) (error)
+# - No trailing whitespace on subject line (warning)
+# - No duplicate Signed-off-by from same person (error)
+# - No multiple subject-like lines (2+) in body (squash-mess) (error)
+
+RED='\033[1;31m'
+YEL='\033[1;33m'
+RST='\033[0m'
+
+# --- Single-message validation (operates on a temp file) ---
+
+check_message() {
+ msgfile=$1
+ err=0
+ warn_count=0
+
+ error() {
+ printf "${RED}ERROR:${RST} %s\n" "$1" >&2
+ err=1
+ }
+
+ warning() {
+ printf "${YEL}WARNING:${RST} %s\n" "$1" >&2
+ warn_count=$((warn_count + 1))
+ }
+
+ # Check for empty message
+ if ! grep -q '[^[:space:]]' "$msgfile"; then
+ error "commit message is empty"
+ return $err
+ fi
+
+ # Split into subject (first line) and the rest
+ subject=$(head -n 1 "$msgfile")
+
+ # --- Subject checks ---
+
+ # Trailing whitespace on subject (second pattern is a literal tab)
+ case "$subject" in
+ *" "|*" ")
+ warning "trailing whitespace on subject line"
+ ;;
+ esac
+
+ # Subject format: component/module: description
+ # Allow nested paths like avcodec/vvc/inter: or single component like doc:
+ # Also allow "Merge" and "Revert" subjects used by forges
+ # Patterns supported:
+ # component: desc (avcodec/vvc: fix)
+ # component modifier: desc (avcodec/vvc decode: fix)
+ # component, component: desc (avformat/a, avcodec/b: fix)
+ # .component: desc (.forgejo/CODEOWNERS: add)
+ # {component}: desc ({lib{a,b}/x86/,}Makefile: fix)
+ # COMP: a component token starting with a letter, at least 2 characters.
+ COMP='[a-zA-Z][]a-zA-Z0-9_./{},*?|()[-]+'
+ case "$subject" in
+ Merge\ *|Revert\ *)
+ # Merge and revert commits get a pass on the component: format
+ ;;
+ *)
+ if ! echo "$subject" | grep -qE "^[{.]?${COMP}(, *${COMP})*( +[a-zA-Z0-9_]+)*: "; then
+ error "subject does not match 'component: description' pattern: $subject"
+ fi
+ ;;
+ esac
+
+ # Subject length
+ subj_len=${#subject}
+ if [ "$subj_len" -gt 100 ]; then
+ warning "subject is $subj_len characters (> 100)"
+ fi
+
+ # --- Blank line between subject and body ---
+ # Use sed to check line 2 directly; works regardless of trailing newline.
+ second_line=$(sed -n '2p' "$msgfile")
+ rest=$(sed -n '2,$p' "$msgfile")
+ if [ -n "$rest" ]; then
+ if [ -n "$second_line" ]; then
+ error "missing blank line between subject and body"
+ fi
+
+ # Body is everything after the blank separator line
+ body=$(tail -n +3 "$msgfile")
+ else
+ body=""
+ fi
+
+ # --- Squash-mess detection ---
+
+ if [ -n "$body" ]; then
+ # Multiple Signed-off-by from the same person
+ sob_dups=$(echo "$body" | grep -i '^Signed-off-by:' | sort | uniq -d)
+ if [ -n "$sob_dups" ]; then
+ error "duplicate Signed-off-by: $(echo "$sob_dups" | head -n 1)"
+ fi
+
+ # Multiple subject-like lines in body: lines matching "component/module: text"
+ # that look like additional commit subjects from a squashed merge.
+ # Require a '/' in the component (e.g. avcodec/vvc:) to distinguish from
+ # prose definitions (e.g. "maximum: No restriction") or data labels.
+ # Exclude known trailer tags.
+ subj_like_count=$(echo "$body" \
+ | grep -E '^[a-zA-Z][a-zA-Z0-9_./-]*/[a-zA-Z0-9_./-]+: [A-Z]' \
+ | grep -ivE '^(Signed-off-by|Reviewed-by|Acked-by|Tested-by|CC|Reported-by|Co-authored-by|Link|Fixes|Note|Suggested-by|Bug):' \
+ | wc -l | tr -d ' ')
+ if [ "$subj_like_count" -ge 2 ]; then
+ error "body contains $subj_like_count subject-like lines (squash-mess?)"
+ fi
+ fi
+
+ if [ $err -eq 0 ] && [ $warn_count -eq 0 ]; then
+ printf "commit message OK\n"
+ fi
+
+ return $err
+}
+
+# --- Main ---
+
+if [ $# -eq 0 ]; then
+ # No argument: read a single message from stdin
+ tmpfile=$(mktemp)
+ trap 'rm -f "$tmpfile"' EXIT
+ cat > "$tmpfile"
+ check_message "$tmpfile"
+ exit $?
+elif [ $# -eq 1 ] && [ -f "$1" ]; then
+ # Single argument is an existing file: treat as commit message file
+ # (used by pre-commit commit-msg hook passing .git/COMMIT_EDITMSG)
+ check_message "$1"
+ exit $?
+else
+ # Argument(s) provided: treat as git revision range
+ revs=$(git log --format=%H "$@" 2>/dev/null) || {
+ printf "${RED}ERROR:${RST} invalid revision range: %s\n" "$*" >&2
+ exit 1
+ }
+ if [ -z "$revs" ]; then
+ printf "no commits in range: %s\n" "$*"
+ exit 0
+ fi
+
+ tmpfile=$(mktemp)
+ trap 'rm -f "$tmpfile"' EXIT
+ total=0
+ failures=0
+
+ for sha in $revs; do
+ total=$((total + 1))
+ printf '\n--- %s: %s ---\n' "$sha" "$(git log -1 --format=%s "$sha")"
+ git log -1 --format="%B" "$sha" > "$tmpfile"
+ if ! check_message "$tmpfile"; then
+ failures=$((failures + 1))
+ fi
+ done
+
+ printf '\n--- Result: %d/%d passed ---\n' "$((total - failures))" "$total"
+ [ "$failures" -eq 0 ]
+ exit $?
+fi
--
2.52.0
From ad6401213d8edf43f3943f47409505e027b29b85 Mon Sep 17 00:00:00 2001
From: Jun Zhao <barryjzhao@tencent.com>
Date: Mon, 23 Feb 2026 10:01:52 +0800
Subject: [PATCH 2/3] .forgejo/workflows: add commit message validation CI
workflow
Add a Forgejo CI workflow that runs check_commit_msg.sh on every
pull request to validate all commit messages in the PR range.
Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
---
.forgejo/workflows/commitmsg.yml | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 .forgejo/workflows/commitmsg.yml
diff --git a/.forgejo/workflows/commitmsg.yml b/.forgejo/workflows/commitmsg.yml
new file mode 100644
index 0000000000..e6e874ffd2
--- /dev/null
+++ b/.forgejo/workflows/commitmsg.yml
@@ -0,0 +1,19 @@
+name: Commit Messages
+
+on:
+ pull_request:
+
+concurrency:
+ cancel-in-progress: ${{ forge.event_name == 'pull_request' }}
+
+jobs:
+ check:
+ name: Validate
+ runs-on: utilities
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ - name: Check commit messages
+ run: sh tools/check_commit_msg.sh origin/master..HEAD
--
2.52.0
From 06567c8d906d366f15e05f27e792cb55ecb7053d Mon Sep 17 00:00:00 2001
From: Jun Zhao <barryjzhao@tencent.com>
Date: Mon, 23 Feb 2026 10:03:39 +0800
Subject: [PATCH 3/3] .forgejo/pre-commit: add local commit-msg hook
Register check_commit_msg.sh as a commit-msg stage hook in the
pre-commit configuration so developers get immediate feedback
on commit message format during `git commit`.
Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
---
.forgejo/pre-commit/config.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/.forgejo/pre-commit/config.yaml b/.forgejo/pre-commit/config.yaml
index f1ab7765ef..c8de05fadf 100644
--- a/.forgejo/pre-commit/config.yaml
+++ b/.forgejo/pre-commit/config.yaml
@@ -20,6 +20,12 @@ repos:
- id: trailing-whitespace
- repo: local
hooks:
+ - id: check-commit-message
+ name: validate commit message format
+ language: script
+ entry: ./tools/check_commit_msg.sh
+ stages: [commit-msg]
+ always_run: true
- id: aarch64-asm-indent
name: fix aarch64 assembly indentation
files: ^.*/aarch64/.*\.S$
--
2.52.0
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-02-23 15:23 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-23 2:47 [FFmpeg-devel] [PR] commitcheck (PR #22260) Jun Zhao via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git