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
7 changes: 6 additions & 1 deletion commitizen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,15 @@
"action": "store_true",
"help": "show output to stdout, no commit, no modified files",
},
{
"name": "--write-message-to-file",
"metavar": "FILE_PATH",
"help": "write message to file before commiting (can be combined with --dry-run)",
},
{
"name": ["-s", "--signoff"],
"action": "store_true",
"help": "Sign off the commit",
"help": "sign off the commit",
},
],
},
Expand Down
13 changes: 13 additions & 0 deletions commitizen/commands/commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
NoAnswersError,
NoCommitBackupError,
NotAGitProjectError,
NotAllowed,
NothingToCommitError,
)
from commitizen.git import smart_open
Expand Down Expand Up @@ -63,10 +64,18 @@ def prompt_commit_questions(self) -> str:

def __call__(self):
dry_run: bool = self.arguments.get("dry_run")
write_message_to_file = self.arguments.get("write_message_to_file")

if git.is_staging_clean() and not dry_run:
raise NothingToCommitError("No files added to staging!")

if write_message_to_file is not None:
if not isinstance(write_message_to_file, str):
raise NotAllowed(
"Commit message file name is broken.\n"
"Check the flag `--write-message-to-file` in the terminal"
)

retry: bool = self.arguments.get("retry")

if retry:
Expand All @@ -76,6 +85,10 @@ def __call__(self):

out.info(f"\n{m}\n")

if write_message_to_file:
with smart_open(write_message_to_file, "w") as file:
file.write(m)

if dry_run:
raise DryRunExit()

Expand Down
4 changes: 4 additions & 0 deletions docs/commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ In your terminal run `cz commit` or the shortcut `cz c` to generate a guided git

A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`.

You can run `cz commit --write-message-to-file COMMIT_MSG_FILE` to additionally save the
generated message to a file. This can be combined with the `--dry-run` flag to only
write the message to a file and not modify files and create a commit.

!!! note
To maintain platform compatibility, the `commit` command disable ANSI escaping in its output.
In particular pre-commit hooks coloring will be deactivated as discussed in [commitizen-tools/commitizen#417](https://github.com/commitizen-tools/commitizen/issues/417).
27 changes: 27 additions & 0 deletions docs/tutorials/auto_prepare_commit_message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Automatically prepare message before commit

## About

To automatically prepare a commit message prior to committing, you can use a [Git hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks).

## How to

- Step 1: Create a new [`prepare-commit-msg`][prepare-commit-msg-docs] Git hook by running the following commands from the root of the Git repository:

```sh
cd .git/hooks
touch prepare-commit-msg
chmod +x prepare-commit-msg
```

- Step 2: Edit the newly created file and add the following content:

```sh
#!/bin/sh
COMMIT_MSG_FILE=$1
exec < /dev/tty && cz commit --dry-run --write-message-to-file $COMMIT_MSG_FILE || true
```

See the Git hooks documentation on [`prepare-commit-msg` hooks][prepare-commit-msg-docs] for details on how this works.

[prepare-commit-msg-docs]: https://git-scm.com/docs/githooks#_prepare_commit_msg
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ nav:
- Tutorials:
- Writing commits: "tutorials/writing_commits.md"
- Auto check commits: "tutorials/auto_check.md"
- Auto prepare commit message: "tutorials/auto_prepare_commit_message.md"
- GitLab CI: "tutorials/gitlab_ci.md"
- Github Actions: "tutorials/github_actions.md"
- Jenkins pipeline: "tutorials/jenkins_pipeline.md"
Expand Down
48 changes: 48 additions & 0 deletions tests/commands/test_commit_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
NoAnswersError,
NoCommitBackupError,
NotAGitProjectError,
NotAllowed,
NothingToCommitError,
)

Expand Down Expand Up @@ -109,6 +110,53 @@ def test_commit_command_with_dry_run_option(config, mocker: MockFixture):
commit_cmd()


@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_write_message_to_file_option(
config, tmp_path, mocker: MockFixture
):
tmp_file = tmp_path / "message"

prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"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, {"write_message_to_file": str(tmp_file)})()
success_mock.assert_called_once()
assert tmp_file.exists()
assert tmp_file.read_text() == "feat: user created"


@pytest.mark.usefixtures("staging_is_clean")
@pytest.mark.parametrize("message_file", [True, False, 0, 1])
def test_commit_command_with_invalid_write_message_to_file_option(
config, message_file, mocker: MockFixture
):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}

with pytest.raises(NotAllowed):
print(isinstance(message_file, str))
commit_cmd = commands.Commit(config, {"write_message_to_file": message_file})
commit_cmd()


@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_signoff_option(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
Expand Down