Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions commitizen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ def __call__(
"action": "store_true",
"help": "Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected.",
},
{
"name": ["-l", "--message-length-limit"],
"type": int,
"default": 0,
"help": "length limit of the commit message; 0 for no limit",
},
],
},
{
Expand Down
12 changes: 11 additions & 1 deletion commitizen/commands/commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from commitizen.cz.utils import get_backup_file_path
from commitizen.exceptions import (
CommitError,
CommitMessageLengthExceededError,
CustomError,
DryRunExit,
NoAnswersError,
Expand Down Expand Up @@ -61,7 +62,16 @@ def prompt_commit_questions(self) -> str:

if not answers:
raise NoAnswersError()
return cz.message(answers)

message = cz.message(answers)
message_len = len(message.partition("\n")[0].strip())
message_length_limit: int = self.arguments.get("message_length_limit", 0)
if message_length_limit > 0 and message_len > message_length_limit:
raise CommitMessageLengthExceededError(
f"Length of commit message exceeds limit ({message_len}/{message_length_limit})"
)

return message

def __call__(self):
dry_run: bool = self.arguments.get("dry_run")
Expand Down
2 changes: 1 addition & 1 deletion commitizen/cz/jira/jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def questions(self) -> Questions:
]
return questions

def message(self, answers) -> str:
def message(self, answers: dict) -> str:
return " ".join(
filter(
bool,
Expand Down
6 changes: 6 additions & 0 deletions commitizen/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class ExitCode(enum.IntEnum):
CHANGELOG_FORMAT_UNKNOWN = 29
CONFIG_FILE_NOT_FOUND = 30
CONFIG_FILE_IS_EMPTY = 31
COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED = 32


class CommitizenException(Exception):
Expand Down Expand Up @@ -201,3 +202,8 @@ class ConfigFileNotFound(CommitizenException):
class ConfigFileIsEmpty(CommitizenException):
exit_code = ExitCode.CONFIG_FILE_IS_EMPTY
message = "Config file is empty, please check your file path again."


class CommitMessageLengthExceededError(CommitizenException):
exit_code = ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED
message = "Length of commit message exceeds the given limit."
11 changes: 11 additions & 0 deletions docs/commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ You can use `cz commit --retry` to reuse the last commit message when the previo
To automatically retry when running `cz commit`, you can set the `retry_after_failure`
configuration option to `true`. Running `cz commit --no-retry` makes commitizen ignore `retry_after_failure`, forcing
a new commit message to be prompted.

### Commit message length limit

The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages.
An exception would be raised when the message length exceeds the limit.
For example, `cz commit -l 72` will limit the length of commit messages to 72 characters.
By default the limit is set to 0, which means no limit on the length.

**Note that the limit applies only to the first line of the message.**
Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject,
while the body and the footer are not counted.
27 changes: 27 additions & 0 deletions tests/commands/test_commit_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from commitizen.cz.utils import get_backup_file_path
from commitizen.exceptions import (
CommitError,
CommitMessageLengthExceededError,
CustomError,
DryRunExit,
NoAnswersError,
Expand Down Expand Up @@ -379,3 +380,29 @@ def test_commit_command_with_extra_args(config, mocker: MockFixture):
commands.Commit(config, {"extra_cli_args": "-- -extra-args1 -extra-arg2"})()
commit_mock.assert_called_once_with(ANY, args="-- -extra-args1 -extra-arg2")
success_mock.assert_called_once()


@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_message_length_limit(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prefix = "feat"
subject = "random subject"
message_length = len(prefix) + len(": ") + len(subject)
prompt_mock.return_value = {
"prefix": prefix,
"subject": subject,
"scope": "",
"is_breaking_change": False,
"body": "random body",
"footer": "random footer",
}

commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")

commands.Commit(config, {"message_length_limit": message_length})()
success_mock.assert_called_once()

with pytest.raises(CommitMessageLengthExceededError):
commands.Commit(config, {"message_length_limit": message_length - 1})()
2 changes: 1 addition & 1 deletion tests/test_cz_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class DummyCz(BaseCommitizen):
def questions(self):
return [{"type": "input", "name": "commit", "message": "Initial commit:\n"}]

def message(self, answers):
def message(self, answers: dict):
return answers["commit"]


Expand Down