Skip to content

Commit

Permalink
feat(commands/commit): add force-edit functionality after answering q…
Browse files Browse the repository at this point in the history
…uestions

refactor: remove redundant return None

Co-authored-by: Wei Lee <[email protected]>

Update test_commit_command.py

Co-authored-by: Wei Lee <[email protected]>
  • Loading branch information
josix and Lee-W committed Oct 23, 2024
1 parent 2f6b7cc commit 08a259d
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 56 deletions.
6 changes: 6 additions & 0 deletions commitizen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,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": ["-e", "--edit"],
"action": "store_true",
"default": False,
"help": "edit the commit message before committing",
},
{
"name": ["-l", "--message-length-limit"],
"type": int,
Expand Down
24 changes: 24 additions & 0 deletions commitizen/commands/commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import contextlib
import os
import shutil
import subprocess
import tempfile

import questionary

Expand Down Expand Up @@ -72,9 +75,27 @@ def prompt_commit_questions(self) -> str:

return message

def manual_edit(self, message: str) -> str:
editor = git.get_core_editor()
if editor is None:
raise RuntimeError("No 'editor' value given and no default available.")
exec_path = shutil.which(editor)
if exec_path is None:
raise RuntimeError(f"Editor '{editor}' not found.")
with tempfile.NamedTemporaryFile(mode="w", delete=False) as file:
file.write(message)
file_path = file.name
argv = [exec_path, file_path]
subprocess.call(argv)
with open(file_path) as temp_file:
message = temp_file.read().strip()
file.unlink()
return message

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

is_all: bool = self.arguments.get("all")
if is_all:
Expand All @@ -101,6 +122,9 @@ def __call__(self):
else:
m = self.prompt_commit_questions()

if manual_edit:
m = self.manual_edit(m)

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

if write_message_to_file:
Expand Down
7 changes: 7 additions & 0 deletions commitizen/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,13 @@ def get_eol_style() -> EOLTypes:
return map["native"]


def get_core_editor() -> str | None:
c = cmd.run("git var GIT_EDITOR")
if c.out:
return c.out.strip()
return None


def smart_open(*args, **kargs):
"""Open a file with the EOL style determined from Git."""
return open(*args, newline=get_eol_style().get_eol_for_open(), **kargs)
Expand Down
2 changes: 1 addition & 1 deletion docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ If you're a first-time contributor, you can check the issues with [good first is
(We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.)
7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/).
8. Run `./scripts/format` and `./scripts/test` to ensure you follow the coding style and the tests pass.
9. Optionally, update the `./docs/README.md`.
9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `scripts/gen_cli_help_screenshots.py`).
9. **Do not** update the `CHANGELOG.md`, it will be automatically created after merging to `master`.
10. **Do not** update the versions in the project, they will be automatically updated.
10. If your changes are about documentation. Run `poetry run mkdocs serve` to serve documentation locally and check whether there is any warning or error.
Expand Down
112 changes: 58 additions & 54 deletions docs/images/cli_help/cz_commit___help.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions tests/commands/test_commit_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,36 @@ def test_commit_command_with_message_length_limit(config, mocker: MockFixture):
commands.Commit(config, {"message_length_limit": message_length - 1})()


@pytest.mark.usefixtures("staging_is_clean")
@pytest.mark.parametrize("editor", ["vim", None])
def test_manual_edit(editor, config, mocker: MockFixture, tmp_path):
mocker.patch("commitizen.git.get_core_editor", return_value=editor)
subprocess_mock = mocker.patch("subprocess.call")

mocker.patch("shutil.which", return_value=editor)

test_message = "Initial commit message"
temp_file = tmp_path / "temp_commit_message"
temp_file.write_text(test_message)

mock_temp_file = mocker.patch("tempfile.NamedTemporaryFile")
mock_temp_file.return_value.__enter__.return_value.name = str(temp_file)

commit_cmd = commands.Commit(config, {"edit": True})

if editor is None:
with pytest.raises(RuntimeError):
commit_cmd.manual_edit(test_message)
else:
edited_message = commit_cmd.manual_edit(test_message)

subprocess_mock.assert_called_once_with(["vim", str(temp_file)])

assert edited_message == test_message.strip()

temp_file.unlink()


@skip_below_py_3_13
def test_commit_command_shows_description_when_use_help_option(
mocker: MockFixture, capsys, file_regression
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
usage: cz commit [-h] [--retry] [--no-retry] [--dry-run]
[--write-message-to-file FILE_PATH] [-s] [-a]
[--write-message-to-file FILE_PATH] [-s] [-a] [-e]
[-l MESSAGE_LENGTH_LIMIT]

create new commit
Expand All @@ -16,5 +16,6 @@ options:
-a, --all 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.
-e, --edit edit the commit message before committing
-l, --message-length-limit MESSAGE_LENGTH_LIMIT
length limit of the commit message; 0 for no limit
20 changes: 20 additions & 0 deletions tests/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,26 @@ def test_eoltypes_get_eol_for_open():
assert git.EOLTypes.get_eol_for_open(git.EOLTypes.CRLF) == "\r\n"


def test_get_core_editor(mocker):
mocker.patch.dict(os.environ, {"GIT_EDITOR": "nano"})
assert git.get_core_editor() == "nano"

mocker.patch.dict(os.environ, clear=True)
mocker.patch(
"commitizen.cmd.run",
return_value=cmd.Command(
out="vim", err="", stdout=b"", stderr=b"", return_code=0
),
)
assert git.get_core_editor() == "vim"

mocker.patch(
"commitizen.cmd.run",
return_value=cmd.Command(out="", err="", stdout=b"", stderr=b"", return_code=1),
)
assert git.get_core_editor() is None


def test_create_tag_with_message(tmp_commitizen_project):
with tmp_commitizen_project.as_cwd():
create_file_and_commit("feat(test): test")
Expand Down

0 comments on commit 08a259d

Please sign in to comment.