Long commit message lines are difficult to read after git and email add indentation or quotation prefixes. Enforce the documented 75-character limit while allowing Git-style trailers to remain unwrapped.
Add coverage for boundary values, trailer exemptions, and raw email input. Assisted-by: GPT-5.6-Sol, OpenCode Signed-off-by: Ales Musil <[email protected]> --- tests/checkpatch.at | 103 ++++++++++++++++++++++++++++++++++++++++ utilities/checkpatch.py | 16 ++++++- 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/tests/checkpatch.at b/tests/checkpatch.at index 352a5f4d2..af95579bf 100755 --- a/tests/checkpatch.at +++ b/tests/checkpatch.at @@ -39,6 +39,23 @@ try_checkpatch__() { AT_CHECK([$1 $top_srcdir/utilities/checkpatch.py -q test.patch]) fi } + +try_checkpatch_stdin() { + echo "$1" | sed 's/^ //' > test.patch + if test -n "$2"; then + echo "$2" | sed 's/^ //' > expout + else + : > expout + fi + + if test -s expout; then + AT_CHECK([$PYTHON3 $top_srcdir/utilities/checkpatch.py -q < test.patch], + [1], [stdout]) + AT_CHECK([sed '/^Lines checked:/,$d' stdout], [0], [expout]) + else + AT_CHECK([$PYTHON3 $top_srcdir/utilities/checkpatch.py -q < test.patch]) + fi +} OVS_END_SHELL_HELPERS AT_SETUP([checkpatch - sign-offs]) @@ -433,6 +450,92 @@ try_checkpatch \ AT_CLEANUP +AT_SETUP([checkpatch - commit message body length]) + +body_75=$(printf '%75s' '' | tr ' ' x) +body_76=${body_75}x +long_tag_value=$(printf '%80s' '' | tr ' ' x) + +try_checkpatch \ + "Author: A + Commit: A + + $body_75 + Signed-off-by: A" + +try_checkpatch \ + "Author: A + Commit: A + + $body_76 + Signed-off-by: A" \ + "WARNING: Commit message body line is 76 characters long (recommended limit is 75) + 1: $body_76 +" + +try_checkpatch_stdin \ + "Author: A + Commit: A + Subject: checkpatch: Test body length. + $long_tag_value + + $body_76 + Signed-off-by: A + ---" \ + "WARNING: Commit message body line is 76 characters long (recommended limit is 75) + 6: $body_76 +" + +try_checkpatch \ + "Author: A + Commit: A + + Acked-by: B + Tested-at: https://example.com/test/1 + Link: https://example.com/reviews/$long_tag_value + Arbitrary-Tag: $long_tag_value + Reported-at: https://example.com/bugs/$long_tag_value + Assisted-by: $long_tag_value + Fixes: 123456789abc (\"$long_tag_value\") + Signed-off-by: A" + +try_checkpatch \ + "Author: A + Commit: A + + This is ordinary prose with a colon: $long_tag_value + Signed-off-by: A" \ + "WARNING: Commit message body line is 117 characters long (recommended limit is 75) + 1: This is ordinary prose with a colon: $long_tag_value +" + +try_checkpatch \ + "Author: A + Commit: A + + Not/A/Trailer: $long_tag_value + Signed-off-by: A" \ + "WARNING: Commit message body line is 95 characters long (recommended limit is 75) + 1: Not/A/Trailer: $long_tag_value +" + +raw_patch="From 123456789abc Mon Sep 17 00:00:00 2001 +From: A +Date: Tue, 29 Sep 2026 08:00:00 +0000 +X-Long-Header: $long_tag_value + $long_tag_value +Subject: [PATCH] checkpatch: Test body length. + +$body_75 +Link: https://example.com/reviews/$long_tag_value +Signed-off-by: A +---" + +try_checkpatch "$raw_patch" +try_checkpatch_stdin "$raw_patch" + +AT_CLEANUP + AT_SETUP([checkpatch - malformed tags]) try_checkpatch \ " Author: A diff --git a/utilities/checkpatch.py b/utilities/checkpatch.py index d644db9e1..49de50b19 100755 --- a/utilities/checkpatch.py +++ b/utilities/checkpatch.py @@ -886,7 +886,8 @@ def run_subject_checks(subject, spellcheck=False): return warnings -def ovs_checkpatch_parse(text, filename, author=None, committer=None): +def ovs_checkpatch_parse(text, filename, author=None, committer=None, + body_only=False): global print_file_name, total_line, checking_file, \ empty_return_check_state @@ -915,6 +916,7 @@ def ovs_checkpatch_parse(text, filename, author=None, committer=None): re.I | re.M | re.S) is_fixes = re.compile(r'(\s*(Fixes:)(.*))$', re.I | re.M | re.S) is_fixes_exact = re.compile(r'^Fixes: [0-9a-f]{12} \(".*"\)$') + is_trailer = re.compile(r'^[A-Za-z0-9][A-Za-z0-9-]*:[ \t]+\S') tags_typos = { r'^Acked by:': 'Acked-by:', @@ -929,6 +931,8 @@ def ovs_checkpatch_parse(text, filename, author=None, committer=None): reset_counters() current_line = "" + subject_seen = False + in_commit_body = body_only for line in text.split("\n"): if current_file != previous_file: previous_file = current_file @@ -953,6 +957,8 @@ def ovs_checkpatch_parse(text, filename, author=None, committer=None): # Form feed continue if len(line) <= 0: + if subject_seen: + in_commit_body = True continue if checking_file: @@ -1022,6 +1028,7 @@ def ovs_checkpatch_parse(text, filename, author=None, committer=None): elif is_author.match(line): author = is_author.match(line).group(2) elif is_subject.match(line): + subject_seen = True run_subject_checks(line, spellcheck) elif is_signature.match(line): m = is_signature.match(line) @@ -1041,6 +1048,11 @@ def ovs_checkpatch_parse(text, filename, author=None, committer=None): '--pretty=format:"Fixes: %h (\\\"%s\\\")" ' '--abbrev=12 COMMIT_REF\n') print("%d: %s\n" % (lineno, line)) + elif (in_commit_body and len(line) > 75 + and not is_trailer.match(line)): + print_warning("Commit message body line is %d characters " + "long (recommended limit is 75)" % len(line)) + print("%d: %s\n" % (lineno, line)) elif spellcheck: check_spelling(line, False) for typo, correct in tags_typos.items(): @@ -1138,7 +1150,7 @@ def ovs_checkpatch_file(filename): continue result = ovs_checkpatch_parse(part.get_payload(decode=False), filename, mail.get('Author', mail['From']), - mail['Commit']) + mail['Commit'], body_only=True) if not mail['Subject'] or not mail['Subject'].strip(): if mail['Subject']: -- 2.55.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
