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 <[email protected]> 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 <[email protected]> --- 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 <[email protected]> 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 <[email protected]> --- .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 <[email protected]> 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 <[email protected]> --- .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 -- [email protected] To unsubscribe send an email to [email protected]
