Skip to content

feat: add an argument to limit the length of commit message #1076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
Loading