Skip to content

Commit 363508d

Browse files
authored
feat(commit): add a tag(--body-length-limit) and a function for command commit (#1849)
1 parent 88c25e3 commit 363508d

14 files changed

Lines changed: 145 additions & 5 deletions

commitizen/cli.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ def __call__(
165165
"type": int,
166166
"help": "Set the length limit of the commit message; 0 for no limit.",
167167
},
168+
{
169+
"name": ["--body-length-limit"],
170+
"type": int,
171+
"help": "Set the length limit of the commit body. Commit message in body will be rewrapped to this length; 0 for no limit.",
172+
},
168173
{
169174
"name": ["--"],
170175
"action": "store_true",

commitizen/commands/commit.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import shutil
66
import subprocess
77
import tempfile
8+
import textwrap
9+
from itertools import chain
810
from pathlib import Path
911
from typing import TYPE_CHECKING, TypedDict
1012

@@ -36,6 +38,7 @@ class CommitArgs(TypedDict, total=False):
3638
edit: bool
3739
extra_cli_args: list[str]
3840
message_length_limit: int
41+
body_length_limit: int
3942
no_retry: bool
4043
signoff: bool
4144
write_message_to_file: Path | None
@@ -82,6 +85,7 @@ def _get_message_by_prompt_commit_questions(self) -> str:
8285

8386
message = self.cz.message(answers)
8487
self._validate_subject_length(message)
88+
message = self._wrap_body(message)
8589
return message
8690

8791
def _validate_subject_length(self, message: str) -> None:
@@ -100,6 +104,28 @@ def _validate_subject_length(self, message: str) -> None:
100104
f"Length of commit message exceeds limit ({len(subject)}/{message_length_limit}), subject: '{subject}'"
101105
)
102106

107+
def _wrap_body(self, message: str) -> str:
108+
"""
109+
Wrap the body of the commit message to the --body-length-limit length.
110+
"""
111+
112+
body_length_limit = self.arguments.get(
113+
"body_length_limit", self.config.settings["body_length_limit"]
114+
)
115+
# By the contract, body_length_limit is set to 0 for no limit
116+
if not body_length_limit or body_length_limit <= 0:
117+
return message
118+
119+
lines = message.split("\n")
120+
if len(lines) < 3:
121+
return message
122+
123+
# First line is subject, second is blank line, rest are body lines
124+
wrapped_body_lines = chain.from_iterable(
125+
textwrap.wrap(line, width=body_length_limit) for line in lines[2:]
126+
)
127+
return "\n".join(chain(lines[:2], wrapped_body_lines))
128+
103129
def manual_edit(self, message: str) -> str:
104130
editor = git.get_core_editor()
105131
if editor is None:

commitizen/defaults.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class Settings(TypedDict, total=False):
4949
legacy_tag_formats: Sequence[str]
5050
major_version_zero: bool
5151
message_length_limit: int
52+
body_length_limit: int
5253
name: str
5354
post_bump_hooks: list[str] | None
5455
pre_bump_hooks: list[str] | None
@@ -115,6 +116,7 @@ class Settings(TypedDict, total=False):
115116
"extras": {},
116117
"breaking_change_exclamation_in_title": False,
117118
"message_length_limit": 0, # 0 for no limit
119+
"body_length_limit": 0, # 0 for no limit
118120
}
119121

120122
MAJOR = "MAJOR"

tests/commands/test_commit_command.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,70 @@ def test_commit_command_with_config_message_length_limit(
374374
success_mock.reset_mock()
375375
commands.Commit(config, {"message_length_limit": 0})()
376376
success_mock.assert_called_once()
377+
378+
379+
@pytest.mark.usefixtures("staging_is_clean")
380+
@pytest.mark.parametrize(
381+
("body", "body_length_limit"),
382+
[
383+
pytest.param(
384+
"This is a very long line that exceeds 72 characters and should be automatically wrapped by the system to fit within the limit",
385+
72,
386+
id="wrapping",
387+
),
388+
pytest.param(
389+
"Line1 is shorter than the limit but has newline\nLine2 is shorter than the limit but has newline\nLine3 is shorter than the limit but has newline",
390+
100,
391+
id="preserves_line_breaks",
392+
),
393+
pytest.param(
394+
"This is a very long line that exceeds 72 characters and should NOT be wrapped when body_length_limit is set to 0",
395+
0,
396+
id="disabled",
397+
),
398+
pytest.param(
399+
"",
400+
72,
401+
id="no_body",
402+
),
403+
],
404+
)
405+
def test_commit_command_body_length_limit(
406+
body,
407+
body_length_limit,
408+
config,
409+
success_mock: MockType,
410+
commit_mock,
411+
mocker: MockFixture,
412+
file_regression,
413+
):
414+
"""Parameterized test for body_length_limit feature with file regression."""
415+
mocker.patch(
416+
"questionary.prompt",
417+
return_value={
418+
"prefix": "feat",
419+
"subject": "add feature",
420+
"scope": "",
421+
"is_breaking_change": False,
422+
"body": body,
423+
"footer": "",
424+
},
425+
)
426+
427+
commands.Commit(config, {"body_length_limit": body_length_limit})()
428+
success_mock.assert_called_once()
429+
committed_message = commit_mock.call_args[0][0]
430+
file_regression.check(committed_message, extension=".txt")
431+
432+
lines = committed_message.split("\n")
433+
body_lines = lines[2:] # Skip subject and blank line
434+
435+
if body_length_limit > 0:
436+
for line in body_lines:
437+
assert len(line) <= body_length_limit, (
438+
f"Line exceeds {body_length_limit} chars: '{line}' ({len(line)} chars)"
439+
)
440+
elif body_length_limit == 0:
441+
assert len(body_lines) == 1, (
442+
"Body should not be wrapped when body_length_limit is set to 0"
443+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
feat: add feature
2+
3+
This is a very long line that exceeds 72 characters and should NOT be wrapped when body_length_limit is set to 0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
feat: add feature
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
feat: add feature
2+
3+
Line1 is shorter than the limit but has newline
4+
Line2 is shorter than the limit but has newline
5+
Line3 is shorter than the limit but has newline
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
feat: add feature
2+
3+
This is a very long line that exceeds 72 characters and should be
4+
automatically wrapped by the system to fit within the limit

tests/commands/test_common_command/test_command_shows_description_when_use_help_option_py_3_10_commit_.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
usage: cz commit [-h] [--retry] [--no-retry] [--dry-run]
22
[--write-message-to-file FILE_PATH] [-s] [-a] [-e]
3-
[-l MESSAGE_LENGTH_LIMIT] [--]
3+
[-l MESSAGE_LENGTH_LIMIT]
4+
[--body-length-limit BODY_LENGTH_LIMIT] [--]
45

56
Create new commit
67

@@ -22,4 +23,8 @@ options:
2223
-l MESSAGE_LENGTH_LIMIT, --message-length-limit MESSAGE_LENGTH_LIMIT
2324
Set the length limit of the commit message; 0 for no
2425
limit.
26+
--body-length-limit BODY_LENGTH_LIMIT
27+
Set the length limit of the commit body. Commit
28+
message in body will be rewrapped to this length; 0
29+
for no limit.
2530
-- Positional arguments separator (recommended).

tests/commands/test_common_command/test_command_shows_description_when_use_help_option_py_3_11_commit_.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
usage: cz commit [-h] [--retry] [--no-retry] [--dry-run]
22
[--write-message-to-file FILE_PATH] [-s] [-a] [-e]
3-
[-l MESSAGE_LENGTH_LIMIT] [--]
3+
[-l MESSAGE_LENGTH_LIMIT]
4+
[--body-length-limit BODY_LENGTH_LIMIT] [--]
45

56
Create new commit
67

@@ -22,4 +23,8 @@ options:
2223
-l MESSAGE_LENGTH_LIMIT, --message-length-limit MESSAGE_LENGTH_LIMIT
2324
Set the length limit of the commit message; 0 for no
2425
limit.
26+
--body-length-limit BODY_LENGTH_LIMIT
27+
Set the length limit of the commit body. Commit
28+
message in body will be rewrapped to this length; 0
29+
for no limit.
2530
-- Positional arguments separator (recommended).

0 commit comments

Comments
 (0)