From 51ada0260ffe75fbc7f7b5d0810b745d34e1eeea Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Thu, 15 Aug 2024 22:18:57 +0200 Subject: [PATCH 1/3] fix(pre-commit-hooks)!: move 'check' to 'args' for global options Details: Global options like '--no-raise' must come before 'check'. With 'entry: cz check', anything in 'args' comes after it, so global options cannot be used. Use 'entry: cz' instead. --- BREAKING CHANGE: Overridden 'args' for the 'commitizen' and 'commitizen-branch' hooks must now start with 'check' Signed-off-by: Adrian DC --- .pre-commit-hooks.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index d57006f153..d2c6c29d2f 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -4,8 +4,8 @@ Check whether the current commit message follows committing rules. Allow empty commit messages by default, because they typically indicate to Git that the commit should be aborted. - entry: cz check - args: [--allow-abort, --commit-msg-file] + entry: cz + args: [check, --allow-abort, --commit-msg-file] stages: [commit-msg] language: python language_version: python3 @@ -18,8 +18,8 @@ default branch on the origin repository. Useful for checking messages after the fact (e.g., pre-push or in CI) without an expensive check of the entire repository history. - entry: cz check - args: [--rev-range, "$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF"] + entry: cz + args: [check, --rev-range, "$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF"] always_run: true pass_filenames: false language: python From 16a1b9e71cd255b4c7f3b519c26ade9d98bad8e8 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Thu, 15 Aug 2024 22:21:25 +0200 Subject: [PATCH 2/3] fix(pre-commit-hooks): allow empty ranges in 'commitizen-branch' Details: Force-pushing a branch back to an older commit pushes no new commits, so the range is empty and 'cz check' blocks the push: > No commit found with range: '...' Ignore this error (exit code 3) with '--no-raise 3'. Invalid commit messages are still rejected. --- Signed-off-by: Adrian DC --- .pre-commit-hooks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index d2c6c29d2f..21e44c1566 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -19,7 +19,7 @@ the fact (e.g., pre-push or in CI) without an expensive check of the entire repository history. entry: cz - args: [check, --rev-range, "$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF"] + args: [--no-raise, "3", check, --rev-range, "$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF"] always_run: true pass_filenames: false language: python From 836b32a1e41d261486d4e9a252c07bf531ef927c Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sun, 8 Nov 2026 13:56:22 +0100 Subject: [PATCH 3/3] fix(check): support first pushes in the 'commitizen-branch' hook Details: When a push includes the root commit (first push to an empty remote, or a new unrelated history), pre-commit does not set 'PRE_COMMIT_FROM_REF' and 'PRE_COMMIT_TO_REF'. The 'commitizen-branch' hook then fails with: > fatal: ambiguous argument '$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF' In this case, check all commits of 'PRE_COMMIT_LOCAL_BRANCH', since they are all new to the remote. --- Signed-off-by: Adrian DC --- commitizen/commands/check.py | 7 +++++++ tests/commands/test_check_command.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index fbf707341f..86ff0c2829 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -42,6 +42,13 @@ def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> N self.commit_msg_file = arguments.get("commit_msg_file") self.commit_msg = arguments.get("message") rev_range = arguments.get("rev_range") + if ( + rev_range == "$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF" + and "PRE_COMMIT_FROM_REF" not in os.environ + and "PRE_COMMIT_LOCAL_BRANCH" in os.environ + ): + # pre-commit omits the refs when the push includes the root commit + rev_range = "$PRE_COMMIT_LOCAL_BRANCH" # Expand env vars so the packaged ``commitizen-branch`` pre-push hook # (which passes the literal ``$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF``) # keeps working after the ``shell=False`` switch in #1941. See #2003. diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index 227fa5fa58..4de961a1d9 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -204,6 +204,7 @@ def test_check_rev_range_leaves_unset_env_vars_literal( rewritten to an empty range.""" monkeypatch.delenv("PRE_COMMIT_FROM_REF", raising=False) monkeypatch.delenv("PRE_COMMIT_TO_REF", raising=False) + monkeypatch.delenv("PRE_COMMIT_LOCAL_BRANCH", raising=False) get_commits = mocker.patch( "commitizen.git.get_commits", return_value=_build_fake_git_commits(COMMIT_LOG), @@ -219,6 +220,26 @@ def test_check_rev_range_leaves_unset_env_vars_literal( ) +def test_check_rev_range_falls_back_to_pushed_branch_on_root_push( + config, mocker: MockFixture, monkeypatch: pytest.MonkeyPatch +): + """pre-commit omits the from/to refs when a push includes the root commit.""" + monkeypatch.delenv("PRE_COMMIT_FROM_REF", raising=False) + monkeypatch.delenv("PRE_COMMIT_TO_REF", raising=False) + monkeypatch.setenv("PRE_COMMIT_LOCAL_BRANCH", "refs/heads/main") + get_commits = mocker.patch( + "commitizen.git.get_commits", + return_value=_build_fake_git_commits(COMMIT_LOG), + ) + + commands.Check( + config=config, + arguments={"rev_range": "$PRE_COMMIT_FROM_REF..$PRE_COMMIT_TO_REF"}, + )() + + get_commits.assert_called_once_with(None, "refs/heads/main") + + def test_check_command_with_invalid_argument(config): with pytest.raises( InvalidCommandArgumentError,