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

Reply via email to