From 712a69cc6cc5e29f8f0f013c7d7c5c46080cf1c5 Mon Sep 17 00:00:00 2001 From: sirius Date: Sun, 30 Aug 2020 16:41:35 +0800 Subject: [PATCH 1/9] feat(commands/): add undo command fix #251 --- commitizen/cli.py | 17 ++++++++++ commitizen/commands/__init__.py | 2 ++ commitizen/commands/undo.py | 51 +++++++++++++++++++++++++++++ docs/README.md | 7 ++-- tests/commands/test_undo_command.py | 6 ++++ 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 commitizen/commands/undo.py create mode 100644 tests/commands/test_undo_command.py diff --git a/commitizen/cli.py b/commitizen/cli.py index e25c3e1a77..3955b31b80 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -133,6 +133,23 @@ }, ], }, + { + "name": "undo", + "help": "revert bump or commit", + "func": commands.Undo, + "arguments": [ + { + "name": ["--bump"], + "action": "store_true", + "help": "revert bump", + }, + { + "name": "--commit", + "action": "store_true", + "help": "revert latest commit, equal to git reset HEAD~", + }, + ], + }, { "name": ["changelog", "ch"], "help": ( diff --git a/commitizen/commands/__init__.py b/commitizen/commands/__init__.py index 806e384522..b71bc82064 100644 --- a/commitizen/commands/__init__.py +++ b/commitizen/commands/__init__.py @@ -6,6 +6,7 @@ from .info import Info from .init import Init from .list_cz import ListCz +from .undo import Undo from .schema import Schema from .version import Version @@ -17,6 +18,7 @@ "Example", "Info", "ListCz", + "Undo", "Schema", "Version", "Init", diff --git a/commitizen/commands/undo.py b/commitizen/commands/undo.py new file mode 100644 index 0000000000..a41cab9829 --- /dev/null +++ b/commitizen/commands/undo.py @@ -0,0 +1,51 @@ +from commitizen import factory, out, cmd, git +from commitizen.config import BaseConfig +from commitizen.exceptions import ( + InvalidCommandArgumentError +) + + +class Undo: + """ """ + + def __init__(self, config: BaseConfig, arguments: dict): + self.config: BaseConfig = config + self.cz = factory.commiter_factory(self.config) + self.arguments = arguments + + def _get_bump_command(self): + latest_tag = git.get_tags()[0] + latest_commit = git.get_commits()[0] + + if latest_tag.rev != latest_commit.rev: + raise InvalidCommandArgumentError( + "The revision of the latest tag is not equal to the latest commit, use git undo --commit instead\n\n" + f"Latest Tag: {latest_tag.name}, {latest_tag.rev}, {latest_tag.date}\n" + f"Latest Commit: {latest_commit.title}, {latest_commit.rev}" + ) + + created_tag = git.get_latest_tag_name() + command = f"git tag --delete {created_tag} && git reset HEAD~ && git reset --hard HEAD" + + return command + + def __call__(self): + bump: bool = self.arguments.get("bump") + commit: bool = self.arguments.get("commit") + + if bump: + command = self._get_bump_command() + elif commit: + command = "git reset HEAD~" + else: + raise InvalidCommandArgumentError( + ( + "One and only one argument is required for check command! " + "See 'cz undo -h' for more information" + ) + ) + + c = cmd.run(command) + if c.err: + return None + return c.out.strip() diff --git a/docs/README.md b/docs/README.md index 189504fb3b..7daf53e555 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,7 +11,7 @@ versions](https://img.shields.io/pypi/pyversions/commitizen.svg?style=flat-squar --- -**Documentation**: https://commitizen-tools.github.io/ +**Documentation**: https://commitizen-tools.github.io/commitizen/ --- @@ -105,7 +105,7 @@ Read more about the `check` command [here](https://commitizen-tools.github.io/co ```bash $ cz --help usage: cz [-h] [--debug] [-n NAME] [--version] - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} + {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version,undo} ... Commitizen is a cli tool to generate conventional commits. @@ -119,7 +119,7 @@ optional arguments: --version get the version of the installed commitizen commands: - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} + {init,commit,c,ls,example,info,schema,bump,undo,changelog,ch,check,version} init init commitizen configuration commit (c) create new commit ls show available commitizens @@ -127,6 +127,7 @@ commands: info show information about the cz schema show commit schema bump bump semantic version based on the git log + undo revert the latest bump or commit changelog (ch) generate changelog (note that it will overwrite existing file) check validates that a commit message matches the commitizen diff --git a/tests/commands/test_undo_command.py b/tests/commands/test_undo_command.py new file mode 100644 index 0000000000..552cbc127e --- /dev/null +++ b/tests/commands/test_undo_command.py @@ -0,0 +1,6 @@ +from commitizen import factory, out, cmd, git +from commitizen.config import BaseConfig +from commitizen.exceptions import ( + InvalidCommandArgumentError +) + From 38b15c371c9f5f84c6640572e1e5e66c46025cd1 Mon Sep 17 00:00:00 2001 From: sirius Date: Mon, 31 Aug 2020 16:38:34 +0800 Subject: [PATCH 2/9] style(commands/undo): rename variables and update package import order --- commitizen/commands/__init__.py | 4 ++-- commitizen/commands/undo.py | 17 +++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/commitizen/commands/__init__.py b/commitizen/commands/__init__.py index b71bc82064..040edf7faa 100644 --- a/commitizen/commands/__init__.py +++ b/commitizen/commands/__init__.py @@ -6,8 +6,8 @@ from .info import Info from .init import Init from .list_cz import ListCz -from .undo import Undo from .schema import Schema +from .undo import Undo from .version import Version __all__ = ( @@ -18,8 +18,8 @@ "Example", "Info", "ListCz", - "Undo", "Schema", + "Undo", "Version", "Init", ) diff --git a/commitizen/commands/undo.py b/commitizen/commands/undo.py index a41cab9829..3eaf8827d7 100644 --- a/commitizen/commands/undo.py +++ b/commitizen/commands/undo.py @@ -1,8 +1,6 @@ -from commitizen import factory, out, cmd, git +from commitizen import cmd, factory, git, out from commitizen.config import BaseConfig -from commitizen.exceptions import ( - InvalidCommandArgumentError -) +from commitizen.exceptions import InvalidCommandArgumentError class Undo: @@ -14,17 +12,16 @@ def __init__(self, config: BaseConfig, arguments: dict): self.arguments = arguments def _get_bump_command(self): - latest_tag = git.get_tags()[0] - latest_commit = git.get_commits()[0] + created_tag = git.get_tags()[0] + created_commit = git.get_commits()[0] - if latest_tag.rev != latest_commit.rev: + if created_tag.rev != created_commit.rev: raise InvalidCommandArgumentError( "The revision of the latest tag is not equal to the latest commit, use git undo --commit instead\n\n" - f"Latest Tag: {latest_tag.name}, {latest_tag.rev}, {latest_tag.date}\n" - f"Latest Commit: {latest_commit.title}, {latest_commit.rev}" + f"Latest Tag: {created_tag.name}, {created_tag.rev}, {created_tag.date}\n" + f"Latest Commit: {created_commit.title}, {created_commit.rev}" ) - created_tag = git.get_latest_tag_name() command = f"git tag --delete {created_tag} && git reset HEAD~ && git reset --hard HEAD" return command From aa19ed70e1a0a1aef641dfb8da39705af6c2f762 Mon Sep 17 00:00:00 2001 From: sirius Date: Mon, 28 Sep 2020 23:33:56 +0800 Subject: [PATCH 3/9] fix: prevent undo bump error The last version could not handle tags order by [0.3.0, 0.3.0a0, ...], it would try to delete 0.3.0a0 --- commitizen/commands/undo.py | 10 ++++++---- commitizen/git.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/commitizen/commands/undo.py b/commitizen/commands/undo.py index 3eaf8827d7..5937630394 100644 --- a/commitizen/commands/undo.py +++ b/commitizen/commands/undo.py @@ -12,7 +12,7 @@ def __init__(self, config: BaseConfig, arguments: dict): self.arguments = arguments def _get_bump_command(self): - created_tag = git.get_tags()[0] + created_tag = git.get_latest_tag() created_commit = git.get_commits()[0] if created_tag.rev != created_commit.rev: @@ -22,7 +22,7 @@ def _get_bump_command(self): f"Latest Commit: {created_commit.title}, {created_commit.rev}" ) - command = f"git tag --delete {created_tag} && git reset HEAD~ && git reset --hard HEAD" + command = f"git tag --delete {created_tag.name} && git reset HEAD~ && git reset --hard HEAD" return command @@ -44,5 +44,7 @@ def __call__(self): c = cmd.run(command) if c.err: - return None - return c.out.strip() + out.error(c.err) + + out.write(c.out) + out.success("Undo successful!") diff --git a/commitizen/git.py b/commitizen/git.py index 81aa9166cf..14d1a350f2 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -123,6 +123,18 @@ def get_latest_tag_name() -> Optional[str]: return c.out.strip() +def get_latest_tag() -> GitTag: + latest_tag_name = get_latest_tag_name() + tags = get_tags() + + if tags[0].name == latest_tag_name: + return tags[0] + + tag_names = [tag.name for tag in tags] + latest_tag_index = tag_names.index(latest_tag_name) + return tags[latest_tag_index] + + def get_tag_names() -> Optional[List[str]]: c = cmd.run("git tag --list") if c.err: From 139020c45b58d3be1d18dd6ecb28acbde2f23e75 Mon Sep 17 00:00:00 2001 From: sirius Date: Mon, 28 Sep 2020 23:35:51 +0800 Subject: [PATCH 4/9] test: add tests of undo command --- tests/commands/test_undo_command.py | 78 +++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/tests/commands/test_undo_command.py b/tests/commands/test_undo_command.py index 552cbc127e..1132d17774 100644 --- a/tests/commands/test_undo_command.py +++ b/tests/commands/test_undo_command.py @@ -1,6 +1,74 @@ -from commitizen import factory, out, cmd, git -from commitizen.config import BaseConfig -from commitizen.exceptions import ( - InvalidCommandArgumentError -) +import sys +import pytest + +from commitizen import cli, cmd, commands, git +from commitizen.exceptions import InvalidCommandArgumentError +from tests.utils import create_file_and_commit + + +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_undo_commit(config, mocker): + create_file_and_commit("feat: new file") + # We can not revert the first commit, thus we commit twice. + create_file_and_commit("feat: extra file") + + testargs = ["cz", "undo", "--commit"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + commits = git.get_commits() + + assert len(commits) == 1 + + +def _execute_command(mocker, testargs): + mocker.patch.object(sys, "argv", testargs) + cli.main() + + +def _undo_bump(mocker, tag_num: int = 0): + testargs = ["cz", "undo", "--bump"] + tags = git.get_tags() + print(tags) + _execute_command(mocker, testargs) + + tags = git.get_tags() + print(tags) + print(git.get_commits()) + + assert len(tags) == tag_num + + +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_undo_bump(config, mocker): + # MINOR + create_file_and_commit("feat: new file") + _execute_command(mocker, ["cz", "bump", "--yes"]) + _undo_bump(mocker) + + # PATCH + create_file_and_commit("feat: new file") + _execute_command(mocker, ["cz", "bump", "--yes"]) + + create_file_and_commit("fix: username exception") + _execute_command(mocker, ["cz", "bump"]) + _undo_bump(mocker, 1) + + # PRERELEASE + create_file_and_commit("feat: location") + _execute_command(mocker, ["cz", "bump", "--prerelease", "alpha"]) + _undo_bump(mocker, 1) + + # # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE + create_file_and_commit("feat: location") + _execute_command(mocker, ["cz", "bump", "--prerelease", "alpha"]) + _execute_command(mocker, ["cz", "bump"]) + _undo_bump(mocker, 2) + + # MAJOR + create_file_and_commit( + "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported" + ) + _execute_command(mocker, ["cz", "bump"]) + _undo_bump(mocker, 2) From be85bcc96176226329b546429521e621be01a872 Mon Sep 17 00:00:00 2001 From: sirius Date: Tue, 6 Oct 2020 21:59:24 +0800 Subject: [PATCH 5/9] fix: (undo) handle no tag or commit case --- commitizen/commands/undo.py | 7 +++++-- commitizen/git.py | 7 +++++-- tests/commands/test_undo_command.py | 8 +------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/commitizen/commands/undo.py b/commitizen/commands/undo.py index 5937630394..3b4d8e1b6e 100644 --- a/commitizen/commands/undo.py +++ b/commitizen/commands/undo.py @@ -4,7 +4,7 @@ class Undo: - """ """ + """Reset the latest git commit or git tag.""" def __init__(self, config: BaseConfig, arguments: dict): self.config: BaseConfig = config @@ -13,7 +13,10 @@ def __init__(self, config: BaseConfig, arguments: dict): def _get_bump_command(self): created_tag = git.get_latest_tag() - created_commit = git.get_commits()[0] + commits = git.get_commits() + + if created_tag and commits: + created_commit = commits[0] if created_tag.rev != created_commit.rev: raise InvalidCommandArgumentError( diff --git a/commitizen/git.py b/commitizen/git.py index 14d1a350f2..495484fd4e 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -123,9 +123,12 @@ def get_latest_tag_name() -> Optional[str]: return c.out.strip() -def get_latest_tag() -> GitTag: - latest_tag_name = get_latest_tag_name() +def get_latest_tag() -> Optional[GitTag]: tags = get_tags() + latest_tag_name = get_latest_tag_name() + + if not tags or not latest_tag_name: + return None if tags[0].name == latest_tag_name: return tags[0] diff --git a/tests/commands/test_undo_command.py b/tests/commands/test_undo_command.py index 1132d17774..2e88941f90 100644 --- a/tests/commands/test_undo_command.py +++ b/tests/commands/test_undo_command.py @@ -2,8 +2,7 @@ import pytest -from commitizen import cli, cmd, commands, git -from commitizen.exceptions import InvalidCommandArgumentError +from commitizen import cli, git from tests.utils import create_file_and_commit @@ -29,14 +28,9 @@ def _execute_command(mocker, testargs): def _undo_bump(mocker, tag_num: int = 0): testargs = ["cz", "undo", "--bump"] - tags = git.get_tags() - print(tags) _execute_command(mocker, testargs) tags = git.get_tags() - print(tags) - print(git.get_commits()) - assert len(tags) == tag_num From ec235d394919097e50a439a0e6368bfe1bfecd46 Mon Sep 17 00:00:00 2001 From: sirius Date: Wed, 7 Oct 2020 23:24:35 +0800 Subject: [PATCH 6/9] feat: (undo) add exception for no tag or commit case --- commitizen/commands/undo.py | 2 ++ tests/commands/test_undo_command.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/commitizen/commands/undo.py b/commitizen/commands/undo.py index 3b4d8e1b6e..72f06846ef 100644 --- a/commitizen/commands/undo.py +++ b/commitizen/commands/undo.py @@ -17,6 +17,8 @@ def _get_bump_command(self): if created_tag and commits: created_commit = commits[0] + else: + raise InvalidCommandArgumentError("There is no tag or commit to undo") if created_tag.rev != created_commit.rev: raise InvalidCommandArgumentError( diff --git a/tests/commands/test_undo_command.py b/tests/commands/test_undo_command.py index 2e88941f90..8af8d3433f 100644 --- a/tests/commands/test_undo_command.py +++ b/tests/commands/test_undo_command.py @@ -54,7 +54,7 @@ def test_undo_bump(config, mocker): _execute_command(mocker, ["cz", "bump", "--prerelease", "alpha"]) _undo_bump(mocker, 1) - # # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE + # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE create_file_and_commit("feat: location") _execute_command(mocker, ["cz", "bump", "--prerelease", "alpha"]) _execute_command(mocker, ["cz", "bump"]) From 7985e9a823359402d36c6636191c1ec9f36c1efa Mon Sep 17 00:00:00 2001 From: sirius Date: Mon, 12 Oct 2020 20:05:29 +0800 Subject: [PATCH 7/9] fix: fix mypy incompatible type error --- commitizen/commands/init.py | 4 ++-- commitizen/git.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 1c459d5ed4..1bfb643f0e 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -47,7 +47,7 @@ def __call__(self): def _ask_config_path(self) -> str: name = questionary.select( "Please choose a supported config file: (default: pyproject.toml)", - choices=config_files, + choices=config_files, # type: ignore default="pyproject.toml", style=self.cz.style, ).ask() @@ -79,7 +79,7 @@ def _ask_tag(self) -> str: latest_tag = questionary.select( "Please choose the latest tag: ", - choices=get_tag_names(), + choices=get_tag_names(), # type: ignore style=self.cz.style, ).ask() diff --git a/commitizen/git.py b/commitizen/git.py index 495484fd4e..6fcd21a46e 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -138,7 +138,7 @@ def get_latest_tag() -> Optional[GitTag]: return tags[latest_tag_index] -def get_tag_names() -> Optional[List[str]]: +def get_tag_names() -> List[Optional[str]]: c = cmd.run("git tag --list") if c.err: return [] From b80d9deb380e13e060e9a77fb6b0ed8b2308da5b Mon Sep 17 00:00:00 2001 From: sirius Date: Mon, 12 Oct 2020 20:18:54 +0800 Subject: [PATCH 8/9] feat: add description of undo --bump --- commitizen/commands/undo.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/commitizen/commands/undo.py b/commitizen/commands/undo.py index 72f06846ef..5fe268fcad 100644 --- a/commitizen/commands/undo.py +++ b/commitizen/commands/undo.py @@ -29,6 +29,10 @@ def _get_bump_command(self): command = f"git tag --delete {created_tag.name} && git reset HEAD~ && git reset --hard HEAD" + out.info("Reverting version bump, running:") + out.info(f"{command}") + out.info(f"The tag can be removed from a remote by running `git push origin :{created_tag.name}`") + return command def __call__(self): From b3bf009339434ea0c1572c5caae88b3ffef6e5bf Mon Sep 17 00:00:00 2001 From: sirius Date: Tue, 13 Oct 2020 11:32:06 +0800 Subject: [PATCH 9/9] feat: add shortcut of undo commands --- commitizen/cli.py | 4 ++-- commitizen/commands/undo.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 3955b31b80..519ae1ec43 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -139,12 +139,12 @@ "func": commands.Undo, "arguments": [ { - "name": ["--bump"], + "name": ["--bump", "-b"], "action": "store_true", "help": "revert bump", }, { - "name": "--commit", + "name": ["--commit", "-c"], "action": "store_true", "help": "revert latest commit, equal to git reset HEAD~", }, diff --git a/commitizen/commands/undo.py b/commitizen/commands/undo.py index 5fe268fcad..12ba1b2bf0 100644 --- a/commitizen/commands/undo.py +++ b/commitizen/commands/undo.py @@ -31,7 +31,9 @@ def _get_bump_command(self): out.info("Reverting version bump, running:") out.info(f"{command}") - out.info(f"The tag can be removed from a remote by running `git push origin :{created_tag.name}`") + out.info( + f"The tag can be removed from a remote by running `git push origin :{created_tag.name}`" + ) return command