From a202e661bacf9643373255965f34bbdb382cb299 Mon Sep 17 00:00:00 2001
From: "Axel H." <noirbizarre@gmail.com>
Date: Sun, 2 Mar 2025 17:35:30 +0100
Subject: [PATCH 001/169] ci(pre-commit): update `pre-commit` hooks to latest
 version and fix detected errors

---
 .pre-commit-config.yaml |  6 +++---
 commitizen/tags.py      | 20 +++++++++++---------
 docs/config.md          | 19 +++++++++----------
 3 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 86997bc3ad..bb1beaefcb 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,7 +13,7 @@ repos:
       - id: check-useless-excludes
 
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.4.0
+    rev: v5.0.0
     hooks:
       - id: check-vcs-permalinks
       - id: end-of-file-fixer
@@ -30,13 +30,13 @@ repos:
       - id: detect-private-key
 
   - repo: https://github.com/asottile/blacken-docs
-    rev: 1.13.0
+    rev: 1.19.1
     hooks:
       - id: blacken-docs
         additional_dependencies: [ black~=23.11 ]
 
   - repo: https://github.com/codespell-project/codespell
-    rev: v2.2.4
+    rev: v2.4.1
     hooks:
       - id: codespell
         name: Run codespell to check for common misspellings in files
diff --git a/commitizen/tags.py b/commitizen/tags.py
index 915dd3120c..aa11cb7a9c 100644
--- a/commitizen/tags.py
+++ b/commitizen/tags.py
@@ -55,28 +55,30 @@ class TagRules:
 
     Example:
 
-    ```py
+    ```python
     settings = DEFAULT_SETTINGS.clone()
-    settings.update({
-        "tag_format": "v{version}"
-        "legacy_tag_formats": ["version{version}", "ver{version}"],
-        "ignored_tag_formats": ["ignored{version}"],
-    })
+    settings.update(
+        {
+            "tag_format": "v{version}",
+            "legacy_tag_formats": ["version{version}", "ver{version}"],
+            "ignored_tag_formats": ["ignored{version}"],
+        }
+    )
 
     rules = TagRules.from_settings(settings)
 
     assert rules.is_version_tag("v1.0.0")
     assert rules.is_version_tag("version1.0.0")
     assert rules.is_version_tag("ver1.0.0")
-    assert not rules.is_version_tag("ignored1.0.0", warn=True) # Does not warn
-    assert not rules.is_version_tag("warn1.0.0", warn=True) # Does warn
+    assert not rules.is_version_tag("ignored1.0.0", warn=True)  # Does not warn
+    assert not rules.is_version_tag("warn1.0.0", warn=True)  # Does warn
 
     assert rules.search_version("# My v1.0.0 version").version == "1.0.0"
     assert rules.extract_version("v1.0.0") == Version("1.0.0")
     try:
         assert rules.extract_version("not-a-v1.0.0")
     except InvalidVersion:
-        print "Does not match a tag format"
+        print("Does not match a tag format")
     ```
     """
 
diff --git a/docs/config.md b/docs/config.md
index 3d53249585..d1ae90b29a 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -352,7 +352,7 @@ Commitizen provides some version providers for some well known formats:
 | `scm`        | Fetch the version from git and does not need to set it back                                                                                                                                                             |
 | `pep621`     | Get and set version from `pyproject.toml` `project.version` field                                                                                                                                                       |
 | `poetry`     | Get and set version from `pyproject.toml` `tool.poetry.version` field                                                                                                                                                   |
-| `uv`         | Get and set version from `pyproject.toml` `project.version` field and `uv.lock` `pacakge.version` field whose `package.name` field is the same as `pyproject.toml` `project.name` field                                 |
+| `uv`         | Get and set version from `pyproject.toml` `project.version` field and `uv.lock` `package.version` field whose `package.name` field is the same as `pyproject.toml` `project.name` field                                 |
 | `cargo`      | Get and set version from `Cargo.toml` `project.version` field                                                                                                                                                           |
 | `npm`        | Get and set version from `package.json` `version` field, `package-lock.json` `version,packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `version,packages.''.version` fields if the file exists |
 | `composer`   | Get and set version from `composer.json` `project.version` field                                                                                                                                                        |
@@ -386,22 +386,21 @@ class MyProvider(VersionProvider):
 
     def set_version(self, version: str):
         self.file.write_text(version)
-
 ```
 
 ```python title="setup.py"
 from setuptools import setup
 
 setup(
-    name='my-commitizen-provider',
-    version='0.1.0',
-    py_modules=['my_provider'],
-    install_requires=['commitizen'],
-    entry_points = {
-        'commitizen.provider': [
-            'my-provider = my_provider:MyProvider',
+    name="my-commitizen-provider",
+    version="0.1.0",
+    py_modules=["my_provider"],
+    install_requires=["commitizen"],
+    entry_points={
+        "commitizen.provider": [
+            "my-provider = my_provider:MyProvider",
         ]
-    }
+    },
 )
 ```
 

From 58eecdccbaf02879dc5672f431485b582c66ffad Mon Sep 17 00:00:00 2001
From: Capi Etheriel <barraponto@gmail.com>
Date: Sun, 16 Mar 2025 19:18:22 -0300
Subject: [PATCH 002/169] update precommit example

---
 docs/getting_started.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/getting_started.md b/docs/getting_started.md
index 378b819192..3c6257c363 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -94,7 +94,7 @@ repos:
     hooks:
       - id: commitizen
       - id: commitizen-branch
-        stages: [push]
+        stages: [pre-push]
 ```
 
 After the configuration is added, you'll need to run:

From 13bc11a9c560bad69c24a08b153b7ad0ca769fff Mon Sep 17 00:00:00 2001
From: Wei Lee <weilee.rx@gmail.com>
Date: Thu, 20 Mar 2025 22:11:40 +0800
Subject: [PATCH 003/169] build(pyproject.toml): fix license metadata

---
 pyproject.toml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index 314ddd925c..cedccabb0e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,7 @@ maintainers = [
     { name = "Wei Lee", email = "weilee.rx@gmail.com" },
     { name = "Axel H.", email = "noirbizarre@gmail.com" },
 ]
-license = { text = "LICENSE" }
+license = { file = "LICENSE" }
 readme = "docs/README.md"
 requires-python = ">=3.9,<4.0"
 dependencies = [
@@ -42,6 +42,7 @@ classifiers = [
     "Programming Language :: Python :: 3.12",
     "Programming Language :: Python :: 3.13",
     "Programming Language :: Python :: Implementation :: CPython",
+    "License :: OSI Approved :: MIT License",
 ]
 
 [project.urls]

From 17021fa05ef37e754fd37053c822071c3368b3ca Mon Sep 17 00:00:00 2001
From: Wei Lee <weilee.rx@gmail.com>
Date: Sun, 30 Mar 2025 17:50:23 +0800
Subject: [PATCH 004/169] fix(commands/init): add missing uv provider to "cz
 init"

---
 commitizen/commands/init.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index e39dfbe291..14ec8067e9 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -228,6 +228,7 @@ def _ask_version_provider(self) -> str:
             "npm": "npm: Get and set version from package.json:project.version field",
             "pep621": "pep621: Get and set version from pyproject.toml:project.version field",
             "poetry": "poetry: Get and set version from pyproject.toml:tool.poetry.version field",
+            "uv": "uv: Get and Get and set version from pyproject.toml and uv.lock",
             "scm": "scm: Fetch the version from git and does not need to set it back",
         }
 

From 904173e3193714d2db6a67615bd8ee1132065526 Mon Sep 17 00:00:00 2001
From: Wei Lee <weilee.rx@gmail.com>
Date: Fri, 4 Apr 2025 23:48:55 +0800
Subject: [PATCH 005/169] feat(init): set uv to default value if both
 pyproject.toml and uv.lock present

---
 commitizen/commands/init.py | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index 14ec8067e9..df872ec7ee 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -24,6 +24,10 @@ class ProjectInfo:
     def has_pyproject(self) -> bool:
         return os.path.isfile("pyproject.toml")
 
+    @property
+    def has_uv_lock(self) -> bool:
+        return os.path.isfile("uv.lock")
+
     @property
     def has_setup(self) -> bool:
         return os.path.isfile("setup.py")
@@ -32,6 +36,10 @@ def has_setup(self) -> bool:
     def has_pre_commit_config(self) -> bool:
         return os.path.isfile(".pre-commit-config.yaml")
 
+    @property
+    def is_python_uv(self) -> bool:
+        return self.has_pyproject and self.has_uv_lock
+
     @property
     def is_python_poetry(self) -> bool:
         if not self.has_pyproject:
@@ -236,6 +244,8 @@ def _ask_version_provider(self) -> str:
         if self.project_info.is_python:
             if self.project_info.is_python_poetry:
                 default_val = "poetry"
+            elif self.project_info.is_python_uv:
+                default_val = "uv"
             else:
                 default_val = "pep621"
         elif self.project_info.is_rust_cargo:

From e0b1c7743d5ea0bcba82e4ff515ce2caf5e87865 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 4 Apr 2025 22:07:31 +0000
Subject: [PATCH 006/169] =?UTF-8?q?bump:=20version=204.4.1=20=E2=86=92=204?=
 =?UTF-8?q?.5.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   |  2 +-
 CHANGELOG.md              | 10 ++++++++++
 commitizen/__version__.py |  2 +-
 pyproject.toml            |  4 ++--
 4 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index bb1beaefcb..6c1cbd7670 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.4.1 # automatically updated by Commitizen
+    rev: v4.5.0 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8fc88ad695..6250af20d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## v4.5.0 (2025-04-04)
+
+### Feat
+
+- **init**: set uv to default value if both pyproject.toml and uv.lock present
+
+### Fix
+
+- **commands/init**: add missing uv provider to "cz init"
+
 ## v4.4.1 (2025-03-02)
 
 ### Fix
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 9905939ff6..9faa2c2dd5 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.4.1"
+__version__ = "4.5.0"
diff --git a/pyproject.toml b/pyproject.toml
index cedccabb0e..009bd13ca4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.4.1"
+version = "4.5.0"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.4.1"
+version = "4.5.0"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From d666aa0bae8aec53b23b0495f8b007eeac4fdba8 Mon Sep 17 00:00:00 2001
From: Carlos Sanchez <carlos@apache.org>
Date: Fri, 21 Mar 2025 17:32:39 +0100
Subject: [PATCH 007/169] fix: print which tag is invalid

and the regex used for validation
---
 commitizen/tags.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/commitizen/tags.py b/commitizen/tags.py
index aa11cb7a9c..5724bb2574 100644
--- a/commitizen/tags.py
+++ b/commitizen/tags.py
@@ -146,7 +146,9 @@ def extract_version(self, tag: GitTag) -> Version:
             m for regex in self.version_regexes if (m := regex.fullmatch(tag.name))
         )
         if not (m := next(candidates, None)):
-            raise InvalidVersion()
+            raise InvalidVersion(
+                f"Invalid version tag: '{tag.name}' does not match any configured tag format"
+            )
         if "version" in m.groupdict():
             return self.scheme(m.group("version"))
 

From 1451bc53f274b3794776e345212b518736272eca Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Wed, 9 Apr 2025 01:24:52 +0000
Subject: [PATCH 008/169] =?UTF-8?q?bump:=20version=204.5.0=20=E2=86=92=204?=
 =?UTF-8?q?.5.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 6 ++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6c1cbd7670..19b4394cf6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.5.0 # automatically updated by Commitizen
+    rev: v4.5.1 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6250af20d7..39eee25df0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## v4.5.1 (2025-04-09)
+
+### Fix
+
+- print which tag is invalid
+
 ## v4.5.0 (2025-04-04)
 
 ### Feat
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 9faa2c2dd5..f9f7166e62 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.5.0"
+__version__ = "4.5.1"
diff --git a/pyproject.toml b/pyproject.toml
index 009bd13ca4..c9ea806cfc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.5.0"
+version = "4.5.1"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.5.0"
+version = "4.5.1"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From d7b1a89061f44bda94ded6f0d54f193373d5b6d6 Mon Sep 17 00:00:00 2001
From: Alejandro Martinez Ruiz <alex@flawedcode.org>
Date: Thu, 20 Mar 2025 14:25:47 +0100
Subject: [PATCH 009/169] feat(git): add parents' digests in commit information

---
 commitizen/git.py | 16 +++++++++++---
 tests/test_git.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/commitizen/git.py b/commitizen/git.py
index 7de8e1f1c8..19ca46b6c3 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -44,13 +44,20 @@ def __eq__(self, other) -> bool:
 
 class GitCommit(GitObject):
     def __init__(
-        self, rev, title, body: str = "", author: str = "", author_email: str = ""
+        self,
+        rev,
+        title,
+        body: str = "",
+        author: str = "",
+        author_email: str = "",
+        parents: list[str] | None = None,
     ):
         self.rev = rev.strip()
         self.title = title.strip()
         self.body = body.strip()
         self.author = author.strip()
         self.author_email = author_email.strip()
+        self.parents = parents or []
 
     @property
     def message(self):
@@ -137,7 +144,9 @@ def get_commits(
     for rev_and_commit in git_log_entries:
         if not rev_and_commit:
             continue
-        rev, title, author, author_email, *body_list = rev_and_commit.split("\n")
+        rev, parents, title, author, author_email, *body_list = rev_and_commit.split(
+            "\n"
+        )
         if rev_and_commit:
             git_commit = GitCommit(
                 rev=rev.strip(),
@@ -145,6 +154,7 @@ def get_commits(
                 body="\n".join(body_list).strip(),
                 author=author,
                 author_email=author_email,
+                parents=[p for p in parents.strip().split(" ") if p],
             )
             git_commits.append(git_commit)
     return git_commits
@@ -286,7 +296,7 @@ def smart_open(*args, **kargs):
 def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]:
     """Get string representation of each log entry"""
     delimiter = "----------commit-delimiter----------"
-    log_format: str = "%H%n%s%n%an%n%ae%n%b"
+    log_format: str = "%H%n%P%n%s%n%an%n%ae%n%b"
     git_log_cmd = (
         f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args}"
     )
diff --git a/tests/test_git.py b/tests/test_git.py
index f929ba6a44..8b2fc2b86e 100644
--- a/tests/test_git.py
+++ b/tests/test_git.py
@@ -132,11 +132,13 @@ def test_get_commits_author_and_email():
 def test_get_commits_without_email(mocker: MockFixture):
     raw_commit = (
         "a515bb8f71c403f6f7d1c17b9d8ebf2ce3959395\n"
+        "95bbfc703eb99cb49ba0d6ffd8469911303dbe63 12d3b4bdaa996ea7067a07660bb5df4772297bdd\n"
         "\n"
         "user name\n"
         "\n"
         "----------commit-delimiter----------\n"
         "12d3b4bdaa996ea7067a07660bb5df4772297bdd\n"
+        "de33bc5070de19600f2f00262b3c15efea762408\n"
         "feat(users): add username\n"
         "user name\n"
         "\n"
@@ -159,16 +161,19 @@ def test_get_commits_without_email(mocker: MockFixture):
 def test_get_commits_without_breakline_in_each_commit(mocker: MockFixture):
     raw_commit = (
         "ae9ba6fc5526cf478f52ef901418d85505109744\n"
+        "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
         "bump: version 2.13.0 → 2.14.0\n"
         "GitHub Action\n"
         "action@github.com\n"
         "----------commit-delimiter----------\n"
         "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
+        "b4dc83284dc8c9729032a774a037df1d1f2397d5 20a54bf1b82cd7b573351db4d1e8814dd0be205d\n"
         "Merge pull request #332 from cliles/feature/271-redux\n"
         "User\n"
         "user@email.com\n"
         "Feature/271 redux----------commit-delimiter----------\n"
         "20a54bf1b82cd7b573351db4d1e8814dd0be205d\n"
+        "658f38c3fe832cdab63ed4fb1f7b3a0969a583be\n"
         "feat(#271): enable creation of annotated tags when bumping\n"
         "User 2\n"
         "user@email.edu\n"
@@ -193,6 +198,55 @@ def test_get_commits_without_breakline_in_each_commit(mocker: MockFixture):
     )
 
 
+def test_get_commits_with_and_without_parents(mocker: MockFixture):
+    raw_commit = (
+        "4206e661bacf9643373255965f34bbdb382cb2b9\n"
+        "ae9ba6fc5526cf478f52ef901418d85505109744 bf8479e7aa1a5b9d2f491b79e3a4d4015519903e\n"
+        "Merge pull request from someone\n"
+        "Maintainer\n"
+        "maintainer@email.com\n"
+        "This is a much needed feature----------commit-delimiter----------\n"
+        "ae9ba6fc5526cf478f52ef901418d85505109744\n"
+        "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
+        "Release 0.1.0\n"
+        "GitHub Action\n"
+        "action@github.com\n"
+        "----------commit-delimiter----------\n"
+        "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
+        "\n"
+        "Initial commit\n"
+        "User\n"
+        "user@email.com\n"
+        "----------commit-delimiter----------\n"
+    )
+    mocker.patch("commitizen.cmd.run", return_value=FakeCommand(out=raw_commit))
+
+    commits = git.get_commits()
+
+    assert commits[0].author == "Maintainer"
+    assert commits[1].author == "GitHub Action"
+    assert commits[2].author == "User"
+
+    assert commits[0].author_email == "maintainer@email.com"
+    assert commits[1].author_email == "action@github.com"
+    assert commits[2].author_email == "user@email.com"
+
+    assert commits[0].title == "Merge pull request from someone"
+    assert commits[1].title == "Release 0.1.0"
+    assert commits[2].title == "Initial commit"
+
+    assert commits[0].body == "This is a much needed feature"
+    assert commits[1].body == ""
+    assert commits[2].body == ""
+
+    assert commits[0].parents == [
+        "ae9ba6fc5526cf478f52ef901418d85505109744",
+        "bf8479e7aa1a5b9d2f491b79e3a4d4015519903e",
+    ]
+    assert commits[1].parents == ["ff2f56ca844de72a9d59590831087bf5a97bac84"]
+    assert commits[2].parents == []
+
+
 def test_get_commits_with_signature():
     config_file = ".git/config"
     config_backup = ".git/config.bak"

From 57ea95f652439e78804e16dca822f36f9f5debcc Mon Sep 17 00:00:00 2001
From: Alejandro Martinez Ruiz <alex@flawedcode.org>
Date: Thu, 20 Mar 2025 14:25:54 +0100
Subject: [PATCH 010/169] feat(changelog): expose commit parents' digests when
 processing commits

COMMIT_DATA in test_changelog.py had to be type annotated to avoid an
issue with mypy linting when creating a GitCommit where the tool would
expect the wrong type for the arguments.
---
 commitizen/changelog.py |  1 +
 tests/test_changelog.py | 75 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/commitizen/changelog.py b/commitizen/changelog.py
index 95bf21d6f9..704efe6071 100644
--- a/commitizen/changelog.py
+++ b/commitizen/changelog.py
@@ -172,6 +172,7 @@ def process_commit_message(
 ):
     message: dict = {
         "sha1": commit.rev,
+        "parents": commit.parents,
         "author": commit.author,
         "author_email": commit.author_email,
         **parsed.groupdict(),
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index accbf5d33c..df42b82264 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1,7 +1,7 @@
 import re
 from dataclasses import dataclass
 from pathlib import Path
-from typing import Optional
+from typing import Any, Optional
 
 import pytest
 from jinja2 import FileSystemLoader
@@ -14,9 +14,10 @@
 from commitizen.exceptions import InvalidConfigurationError
 from commitizen.version_schemes import Pep440
 
-COMMITS_DATA = [
+COMMITS_DATA: list[dict[str, Any]] = [
     {
         "rev": "141ee441c9c9da0809c554103a558eb17c30ed17",
+        "parents": ["6c4948501031b7d6405b54b21d3d635827f9421b"],
         "title": "bump: version 1.1.1 → 1.2.0",
         "body": "",
         "author": "Commitizen",
@@ -24,6 +25,7 @@
     },
     {
         "rev": "6c4948501031b7d6405b54b21d3d635827f9421b",
+        "parents": ["ddd220ad515502200fe2dde443614c1075d26238"],
         "title": "docs: how to create custom bumps",
         "body": "",
         "author": "Commitizen",
@@ -31,6 +33,7 @@
     },
     {
         "rev": "ddd220ad515502200fe2dde443614c1075d26238",
+        "parents": ["ad17acff2e3a2e141cbc3c6efd7705e4e6de9bfc"],
         "title": "feat: custom cz plugins now support bumping version",
         "body": "",
         "author": "Commitizen",
@@ -38,6 +41,7 @@
     },
     {
         "rev": "ad17acff2e3a2e141cbc3c6efd7705e4e6de9bfc",
+        "parents": ["56c8a8da84e42b526bcbe130bd194306f7c7e813"],
         "title": "docs: added bump gif",
         "body": "",
         "author": "Commitizen",
@@ -45,6 +49,7 @@
     },
     {
         "rev": "56c8a8da84e42b526bcbe130bd194306f7c7e813",
+        "parents": ["74c6134b1b2e6bb8b07ed53410faabe99b204f36"],
         "title": "bump: version 1.1.0 → 1.1.1",
         "body": "",
         "author": "Commitizen",
@@ -52,6 +57,7 @@
     },
     {
         "rev": "74c6134b1b2e6bb8b07ed53410faabe99b204f36",
+        "parents": ["cbc7b5f22c4e74deff4bc92d14e19bd93524711e"],
         "title": "refactor: changed stdout statements",
         "body": "",
         "author": "Commitizen",
@@ -59,6 +65,7 @@
     },
     {
         "rev": "cbc7b5f22c4e74deff4bc92d14e19bd93524711e",
+        "parents": ["1ba46f2a63cb9d6e7472eaece21528c8cd28b118"],
         "title": "fix(bump): commit message now fits better with semver",
         "body": "",
         "author": "Commitizen",
@@ -66,6 +73,7 @@
     },
     {
         "rev": "1ba46f2a63cb9d6e7472eaece21528c8cd28b118",
+        "parents": ["c35dbffd1bb98bb0b3d1593797e79d1c3366af8f"],
         "title": "fix: conventional commit 'breaking change' in body instead of title",
         "body": "closes #16",
         "author": "Commitizen",
@@ -73,6 +81,7 @@
     },
     {
         "rev": "c35dbffd1bb98bb0b3d1593797e79d1c3366af8f",
+        "parents": ["25313397a4ac3dc5b5c986017bee2a614399509d"],
         "title": "refactor(schema): command logic removed from commitizen base",
         "body": "",
         "author": "Commitizen",
@@ -80,6 +89,7 @@
     },
     {
         "rev": "25313397a4ac3dc5b5c986017bee2a614399509d",
+        "parents": ["d2f13ac41b4e48995b3b619d931c82451886e6ff"],
         "title": "refactor(info): command logic removed from commitizen base",
         "body": "",
         "author": "Commitizen",
@@ -87,6 +97,7 @@
     },
     {
         "rev": "d2f13ac41b4e48995b3b619d931c82451886e6ff",
+        "parents": ["d839e317e5b26671b010584ad8cc6bf362400fa1"],
         "title": "refactor(example): command logic removed from commitizen base",
         "body": "",
         "author": "Commitizen",
@@ -94,6 +105,7 @@
     },
     {
         "rev": "d839e317e5b26671b010584ad8cc6bf362400fa1",
+        "parents": ["12d0e65beda969f7983c444ceedc2a01584f4e08"],
         "title": "refactor(commit): moved most of the commit logic to the commit command",
         "body": "",
         "author": "Commitizen",
@@ -101,6 +113,7 @@
     },
     {
         "rev": "12d0e65beda969f7983c444ceedc2a01584f4e08",
+        "parents": ["fb4c85abe51c228e50773e424cbd885a8b6c610d"],
         "title": "docs(README): updated documentation url)",
         "body": "",
         "author": "Commitizen",
@@ -108,6 +121,7 @@
     },
     {
         "rev": "fb4c85abe51c228e50773e424cbd885a8b6c610d",
+        "parents": ["17efb44d2cd16f6621413691a543e467c7d2dda6"],
         "title": "docs: mkdocs documentation",
         "body": "",
         "author": "Commitizen",
@@ -115,6 +129,7 @@
     },
     {
         "rev": "17efb44d2cd16f6621413691a543e467c7d2dda6",
+        "parents": ["6012d9eecfce8163d75c8fff179788e9ad5347da"],
         "title": "Bump version 1.0.0 → 1.1.0",
         "body": "",
         "author": "Commitizen",
@@ -122,6 +137,7 @@
     },
     {
         "rev": "6012d9eecfce8163d75c8fff179788e9ad5347da",
+        "parents": ["0c7fb0ca0168864dfc55d83c210da57771a18319"],
         "title": "test: fixed issues with conf",
         "body": "",
         "author": "Commitizen",
@@ -129,6 +145,7 @@
     },
     {
         "rev": "0c7fb0ca0168864dfc55d83c210da57771a18319",
+        "parents": ["cb1dd2019d522644da5bdc2594dd6dee17122d7f"],
         "title": "docs(README): some new information about bump",
         "body": "",
         "author": "Commitizen",
@@ -136,6 +153,7 @@
     },
     {
         "rev": "cb1dd2019d522644da5bdc2594dd6dee17122d7f",
+        "parents": ["9c7450f85df6bf6be508e79abf00855a30c3c73c"],
         "title": "feat: new working bump command",
         "body": "",
         "author": "Commitizen",
@@ -143,6 +161,7 @@
     },
     {
         "rev": "9c7450f85df6bf6be508e79abf00855a30c3c73c",
+        "parents": ["9f3af3772baab167e3fd8775d37f041440184251"],
         "title": "feat: create version tag",
         "body": "",
         "author": "Commitizen",
@@ -150,6 +169,7 @@
     },
     {
         "rev": "9f3af3772baab167e3fd8775d37f041440184251",
+        "parents": ["b0d6a3defbfde14e676e7eb34946409297d0221b"],
         "title": "docs: added new changelog",
         "body": "",
         "author": "Commitizen",
@@ -157,6 +177,7 @@
     },
     {
         "rev": "b0d6a3defbfde14e676e7eb34946409297d0221b",
+        "parents": ["d630d07d912e420f0880551f3ac94e933f9d3beb"],
         "title": "feat: update given files with new version",
         "body": "",
         "author": "Commitizen",
@@ -164,6 +185,7 @@
     },
     {
         "rev": "d630d07d912e420f0880551f3ac94e933f9d3beb",
+        "parents": ["1792b8980c58787906dbe6836f93f31971b1ec2d"],
         "title": "fix: removed all from commit",
         "body": "",
         "author": "Commitizen",
@@ -171,6 +193,7 @@
     },
     {
         "rev": "1792b8980c58787906dbe6836f93f31971b1ec2d",
+        "parents": ["52def1ea3555185ba4b936b463311949907e31ec"],
         "title": "feat(config): new set key, used to set version to cfg",
         "body": "",
         "author": "Commitizen",
@@ -178,6 +201,7 @@
     },
     {
         "rev": "52def1ea3555185ba4b936b463311949907e31ec",
+        "parents": ["3127e05077288a5e2b62893345590bf1096141b7"],
         "title": "feat: support for pyproject.toml",
         "body": "",
         "author": "Commitizen",
@@ -185,6 +209,7 @@
     },
     {
         "rev": "3127e05077288a5e2b62893345590bf1096141b7",
+        "parents": ["fd480ed90a80a6ffa540549408403d5b60d0e90c"],
         "title": "feat: first semantic version bump implementation",
         "body": "",
         "author": "Commitizen",
@@ -192,6 +217,7 @@
     },
     {
         "rev": "fd480ed90a80a6ffa540549408403d5b60d0e90c",
+        "parents": ["e4840a059731c0bf488381ffc77e989e85dd81ad"],
         "title": "fix: fix config file not working",
         "body": "",
         "author": "Commitizen",
@@ -199,6 +225,7 @@
     },
     {
         "rev": "e4840a059731c0bf488381ffc77e989e85dd81ad",
+        "parents": ["aa44a92d68014d0da98965c0c2cb8c07957d4362"],
         "title": "refactor: added commands folder, better integration with decli",
         "body": "",
         "author": "Commitizen",
@@ -206,6 +233,7 @@
     },
     {
         "rev": "aa44a92d68014d0da98965c0c2cb8c07957d4362",
+        "parents": ["58bb709765380dbd46b74ce6e8978515764eb955"],
         "title": "Bump version: 1.0.0b2 → 1.0.0",
         "body": "",
         "author": "Commitizen",
@@ -213,6 +241,7 @@
     },
     {
         "rev": "58bb709765380dbd46b74ce6e8978515764eb955",
+        "parents": ["97afb0bb48e72b6feca793091a8a23c706693257"],
         "title": "docs(README): new badges",
         "body": "",
         "author": "Commitizen",
@@ -220,6 +249,10 @@
     },
     {
         "rev": "97afb0bb48e72b6feca793091a8a23c706693257",
+        "parents": [
+            "9cecb9224aa7fa68d4afeac37eba2a25770ef251",
+            "e004a90b81ea5b374f118759bce5951202d03d69",
+        ],
         "title": "Merge pull request #10 from Woile/feat/decli",
         "body": "Feat/decli",
         "author": "Commitizen",
@@ -227,6 +260,7 @@
     },
     {
         "rev": "9cecb9224aa7fa68d4afeac37eba2a25770ef251",
+        "parents": ["f5781d1a2954d71c14ade2a6a1a95b91310b2577"],
         "title": "style: black to files",
         "body": "",
         "author": "Commitizen",
@@ -234,6 +268,7 @@
     },
     {
         "rev": "f5781d1a2954d71c14ade2a6a1a95b91310b2577",
+        "parents": ["80105fb3c6d45369bc0cbf787bd329fba603864c"],
         "title": "ci: added travis",
         "body": "",
         "author": "Commitizen",
@@ -241,6 +276,7 @@
     },
     {
         "rev": "80105fb3c6d45369bc0cbf787bd329fba603864c",
+        "parents": ["a96008496ffefb6b1dd9b251cb479eac6a0487f7"],
         "title": "refactor: removed delegator, added decli and many tests",
         "body": "BREAKING CHANGE: API is stable",
         "author": "Commitizen",
@@ -248,6 +284,7 @@
     },
     {
         "rev": "a96008496ffefb6b1dd9b251cb479eac6a0487f7",
+        "parents": ["aab33d13110f26604fb786878856ec0b9e5fc32b"],
         "title": "docs: updated test command",
         "body": "",
         "author": "Commitizen",
@@ -255,6 +292,7 @@
     },
     {
         "rev": "aab33d13110f26604fb786878856ec0b9e5fc32b",
+        "parents": ["b73791563d2f218806786090fb49ef70faa51a3a"],
         "title": "Bump version: 1.0.0b1 → 1.0.0b2",
         "body": "",
         "author": "Commitizen",
@@ -262,6 +300,7 @@
     },
     {
         "rev": "b73791563d2f218806786090fb49ef70faa51a3a",
+        "parents": ["7aa06a454fb717408b3657faa590731fb4ab3719"],
         "title": "docs(README): updated to reflect current state",
         "body": "",
         "author": "Commitizen",
@@ -269,6 +308,10 @@
     },
     {
         "rev": "7aa06a454fb717408b3657faa590731fb4ab3719",
+        "parents": [
+            "7c7e96b723c2aaa1aec3a52561f680adf0b60e97",
+            "9589a65880016996cff156b920472b9d28d771ca",
+        ],
         "title": "Merge pull request #9 from Woile/dev",
         "body": "feat: py3 only, tests and conventional commits 1.0",
         "author": "Commitizen",
@@ -276,6 +319,7 @@
     },
     {
         "rev": "7c7e96b723c2aaa1aec3a52561f680adf0b60e97",
+        "parents": ["ed830019581c83ba633bfd734720e6758eca6061"],
         "title": "Bump version: 0.9.11 → 1.0.0b1",
         "body": "",
         "author": "Commitizen",
@@ -283,6 +327,7 @@
     },
     {
         "rev": "ed830019581c83ba633bfd734720e6758eca6061",
+        "parents": ["c52eca6f74f844ab3ffbde61d98ef96071e132b7"],
         "title": "feat: py3 only, tests and conventional commits 1.0",
         "body": "more tests\npyproject instead of Pipfile\nquestionary instead of whaaaaat (promptkit 2.0.0 support)",
         "author": "Commitizen",
@@ -290,6 +335,7 @@
     },
     {
         "rev": "c52eca6f74f844ab3ffbde61d98ef96071e132b7",
+        "parents": ["0326652b2657083929507ee66d4d1a0899e861ba"],
         "title": "Bump version: 0.9.10 → 0.9.11",
         "body": "",
         "author": "Commitizen",
@@ -297,6 +343,7 @@
     },
     {
         "rev": "0326652b2657083929507ee66d4d1a0899e861ba",
+        "parents": ["b3f89892222340150e32631ae6b7aab65230036f"],
         "title": "fix(config): load config reads in order without failing if there is no commitizen section",
         "body": "Closes #8",
         "author": "Commitizen",
@@ -304,6 +351,7 @@
     },
     {
         "rev": "b3f89892222340150e32631ae6b7aab65230036f",
+        "parents": ["5e837bf8ef0735193597372cd2d85e31a8f715b9"],
         "title": "Bump version: 0.9.9 → 0.9.10",
         "body": "",
         "author": "Commitizen",
@@ -311,6 +359,7 @@
     },
     {
         "rev": "5e837bf8ef0735193597372cd2d85e31a8f715b9",
+        "parents": ["684e0259cc95c7c5e94854608cd3dcebbd53219e"],
         "title": "fix: parse scope (this is my punishment for not having tests)",
         "body": "",
         "author": "Commitizen",
@@ -318,6 +367,7 @@
     },
     {
         "rev": "684e0259cc95c7c5e94854608cd3dcebbd53219e",
+        "parents": ["ca38eac6ff09870851b5c76a6ff0a2a8e5ecda15"],
         "title": "Bump version: 0.9.8 → 0.9.9",
         "body": "",
         "author": "Commitizen",
@@ -325,6 +375,7 @@
     },
     {
         "rev": "ca38eac6ff09870851b5c76a6ff0a2a8e5ecda15",
+        "parents": ["64168f18d4628718c49689ee16430549e96c5d4b"],
         "title": "fix: parse scope empty",
         "body": "",
         "author": "Commitizen",
@@ -332,6 +383,7 @@
     },
     {
         "rev": "64168f18d4628718c49689ee16430549e96c5d4b",
+        "parents": ["9d4def716ef235a1fa5ae61614366423fbc8256f"],
         "title": "Bump version: 0.9.7 → 0.9.8",
         "body": "",
         "author": "Commitizen",
@@ -339,6 +391,7 @@
     },
     {
         "rev": "9d4def716ef235a1fa5ae61614366423fbc8256f",
+        "parents": ["33b0bf1a0a4dc60aac45ed47476d2e5473add09e"],
         "title": "fix(scope): parse correctly again",
         "body": "",
         "author": "Commitizen",
@@ -346,6 +399,7 @@
     },
     {
         "rev": "33b0bf1a0a4dc60aac45ed47476d2e5473add09e",
+        "parents": ["696885e891ec35775daeb5fec3ba2ab92c2629e1"],
         "title": "Bump version: 0.9.6 → 0.9.7",
         "body": "",
         "author": "Commitizen",
@@ -353,6 +407,7 @@
     },
     {
         "rev": "696885e891ec35775daeb5fec3ba2ab92c2629e1",
+        "parents": ["bef4a86761a3bda309c962bae5d22ce9b57119e4"],
         "title": "fix(scope): parse correctly",
         "body": "",
         "author": "Commitizen",
@@ -360,6 +415,7 @@
     },
     {
         "rev": "bef4a86761a3bda309c962bae5d22ce9b57119e4",
+        "parents": ["72472efb80f08ee3fd844660afa012c8cb256e4b"],
         "title": "Bump version: 0.9.5 → 0.9.6",
         "body": "",
         "author": "Commitizen",
@@ -367,6 +423,7 @@
     },
     {
         "rev": "72472efb80f08ee3fd844660afa012c8cb256e4b",
+        "parents": ["b5561ce0ab3b56bb87712c8f90bcf37cf2474f1b"],
         "title": "refactor(conventionalCommit): moved filters to questions instead of message",
         "body": "",
         "author": "Commitizen",
@@ -374,6 +431,7 @@
     },
     {
         "rev": "b5561ce0ab3b56bb87712c8f90bcf37cf2474f1b",
+        "parents": ["3e31714dc737029d96898f412e4ecd2be1bcd0ce"],
         "title": "fix(manifest): included missing files",
         "body": "",
         "author": "Commitizen",
@@ -381,6 +439,7 @@
     },
     {
         "rev": "3e31714dc737029d96898f412e4ecd2be1bcd0ce",
+        "parents": ["9df721e06595fdd216884c36a28770438b4f4a39"],
         "title": "Bump version: 0.9.4 → 0.9.5",
         "body": "",
         "author": "Commitizen",
@@ -388,6 +447,7 @@
     },
     {
         "rev": "9df721e06595fdd216884c36a28770438b4f4a39",
+        "parents": ["0cf6ada372470c8d09e6c9e68ebf94bbd5a1656f"],
         "title": "fix(config): home path for python versions between 3.0 and 3.5",
         "body": "",
         "author": "Commitizen",
@@ -395,6 +455,7 @@
     },
     {
         "rev": "0cf6ada372470c8d09e6c9e68ebf94bbd5a1656f",
+        "parents": ["973c6b3e100f6f69a3fe48bd8ee55c135b96c318"],
         "title": "Bump version: 0.9.3 → 0.9.4",
         "body": "",
         "author": "Commitizen",
@@ -402,6 +463,7 @@
     },
     {
         "rev": "973c6b3e100f6f69a3fe48bd8ee55c135b96c318",
+        "parents": ["dacc86159b260ee98eb5f57941c99ba731a01399"],
         "title": "feat(cli): added version",
         "body": "",
         "author": "Commitizen",
@@ -409,6 +471,7 @@
     },
     {
         "rev": "dacc86159b260ee98eb5f57941c99ba731a01399",
+        "parents": ["4368f3c3cbfd4a1ced339212230d854bc5bab496"],
         "title": "Bump version: 0.9.2 → 0.9.3",
         "body": "",
         "author": "Commitizen",
@@ -416,6 +479,7 @@
     },
     {
         "rev": "4368f3c3cbfd4a1ced339212230d854bc5bab496",
+        "parents": ["da94133288727d35dae9b91866a25045038f2d38"],
         "title": "feat(committer): conventional commit is a bit more intelligent now",
         "body": "",
         "author": "Commitizen",
@@ -423,6 +487,7 @@
     },
     {
         "rev": "da94133288727d35dae9b91866a25045038f2d38",
+        "parents": ["1541f54503d2e1cf39bd777c0ca5ab5eb78772ba"],
         "title": "docs(README): motivation",
         "body": "",
         "author": "Commitizen",
@@ -430,6 +495,7 @@
     },
     {
         "rev": "1541f54503d2e1cf39bd777c0ca5ab5eb78772ba",
+        "parents": ["ddc855a637b7879108308b8dbd85a0fd27c7e0e7"],
         "title": "Bump version: 0.9.1 → 0.9.2",
         "body": "",
         "author": "Commitizen",
@@ -437,6 +503,7 @@
     },
     {
         "rev": "ddc855a637b7879108308b8dbd85a0fd27c7e0e7",
+        "parents": ["46e9032e18a819e466618c7a014bcb0e9981af9e"],
         "title": "refactor: renamed conventional_changelog to conventional_commits, not backward compatible",
         "body": "",
         "author": "Commitizen",
@@ -444,6 +511,7 @@
     },
     {
         "rev": "46e9032e18a819e466618c7a014bcb0e9981af9e",
+        "parents": ["0fef73cd7dc77a25b82e197e7c1d3144a58c1350"],
         "title": "Bump version: 0.9.0 → 0.9.1",
         "body": "",
         "author": "Commitizen",
@@ -451,6 +519,7 @@
     },
     {
         "rev": "0fef73cd7dc77a25b82e197e7c1d3144a58c1350",
+        "parents": [],
         "title": "fix(setup.py): future is now required for every python version",
         "body": "",
         "author": "Commitizen",
@@ -489,6 +558,7 @@ def gitcommits() -> list:
             commit["body"],
             commit["author"],
             commit["author_email"],
+            commit["parents"],
         )
         for commit in COMMITS_DATA
     ]
@@ -1108,6 +1178,7 @@ def test_generate_tree_from_commits(gitcommits, tags, merge_prereleases):
                 assert change["author"] == "Commitizen"
                 assert change["author_email"] in "author@cz.dev"
                 assert "sha1" in change
+                assert "parents" in change
 
 
 def test_generate_tree_from_commits_with_no_commits(tags):

From 3f767e154f1459207523b688b8794f0343191f69 Mon Sep 17 00:00:00 2001
From: Alejandro Martinez Ruiz <alex@flawedcode.org>
Date: Fri, 21 Mar 2025 12:53:45 +0100
Subject: [PATCH 011/169] docs(customization): add "parents" to the list of
 fields in Changes

---
 docs/customization.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/docs/customization.md b/docs/customization.md
index 132f4f0490..50113301db 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -507,12 +507,16 @@ Each `Change` has the following fields:
 | scope | `str | None` | An optional scope |
 | message | `str` | The commit message body |
 | sha1 | `str` | The commit `sha1` |
+| parents | `list[str]` | The parent commit(s) `sha1`(s) |
 | author | `str` | The commit author name |
 | author_email | `str` | The commit author email |
 
 !!! Note
     The field values depend on the customization class and/or the settings you provide
 
+The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case
+is listing commits that belong to the same pull request.
+
 When using another template (either provided by a plugin or by yourself), you can also pass extra template variables
 by:
 

From 35f5c23b6dad6bd6be24f783857751bca71ae36d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Sun, 13 Apr 2025 07:26:06 +0000
Subject: [PATCH 012/169] =?UTF-8?q?bump:=20version=204.5.1=20=E2=86=92=204?=
 =?UTF-8?q?.6.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 7 +++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 19b4394cf6..1373277b2d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.5.1 # automatically updated by Commitizen
+    rev: v4.6.0 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39eee25df0..1d2ab1e48f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## v4.6.0 (2025-04-13)
+
+### Feat
+
+- **changelog**: expose commit parents' digests when processing commits
+- **git**: add parents' digests in commit information
+
 ## v4.5.1 (2025-04-09)
 
 ### Fix
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index f9f7166e62..db01fb213a 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.5.1"
+__version__ = "4.6.0"
diff --git a/pyproject.toml b/pyproject.toml
index c9ea806cfc..6475ec01a2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.5.1"
+version = "4.6.0"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.5.1"
+version = "4.6.0"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 58ca48db42fe994bda1617ea998b9089a2222b03 Mon Sep 17 00:00:00 2001
From: Jakob Keller <57402305+jakob-keller@users.noreply.github.com>
Date: Tue, 22 Apr 2025 13:43:12 +0200
Subject: [PATCH 013/169] ci(github-actions): bump deprecated ubuntu-20.04 to
 ubuntu-22.04

Signed-off-by: Jakob Keller <57402305+jakob-keller@users.noreply.github.com>
---
 .github/workflows/pythonpackage.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index f2363745cb..b50b02a681 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -7,7 +7,7 @@ jobs:
     strategy:
       matrix:
         python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
-        platform: [ubuntu-20.04, macos-latest, windows-latest]
+        platform: [ubuntu-22.04, macos-latest, windows-latest]
     runs-on: ${{ matrix.platform }}
     steps:
       - uses: actions/checkout@v4

From af285656734fd74c13a8d373a2a0ff303e0971f1 Mon Sep 17 00:00:00 2001
From: Jakob Keller <57402305+jakob-keller@users.noreply.github.com>
Date: Tue, 22 Apr 2025 13:30:57 +0200
Subject: [PATCH 014/169] build(deps): bump argcomplete from 3.5.3 to 3.6.2

Signed-off-by: Jakob Keller <57402305+jakob-keller@users.noreply.github.com>
---
 poetry.lock    | 8 ++++----
 pyproject.toml | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index 073e1bc247..cccd7a532a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2,14 +2,14 @@
 
 [[package]]
 name = "argcomplete"
-version = "3.5.3"
+version = "3.6.2"
 description = "Bash tab completion for argparse"
 optional = false
 python-versions = ">=3.8"
 groups = ["main"]
 files = [
-    {file = "argcomplete-3.5.3-py3-none-any.whl", hash = "sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61"},
-    {file = "argcomplete-3.5.3.tar.gz", hash = "sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392"},
+    {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"},
+    {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"},
 ]
 
 [package.extras]
@@ -1965,4 +1965,4 @@ type = ["pytest-mypy"]
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "b0f8544806163bc0dddc039eb313f9d82119b845b3a19dedc381e9c88e8f4466"
+content-hash = "e15b424a0569f939e297c8abfcf09753f1fbcc5b4ad891163cc0982accd3b372"
diff --git a/pyproject.toml b/pyproject.toml
index 6475ec01a2..5be231aaa7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,7 @@ dependencies = [
     "tomlkit (>=0.5.3,<1.0.0)",
     "jinja2>=2.10.3",
     "pyyaml>=3.08",
-    "argcomplete >=1.12.1,<3.6",
+    "argcomplete >=1.12.1,<3.7",
     "typing-extensions (>=4.0.1,<5.0.0) ; python_version < '3.11'",
     "charset-normalizer (>=2.1.0,<4)",
     # Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility

From 2a8a8ea93d519e5964ef92a9a673b5b014898064 Mon Sep 17 00:00:00 2001
From: Alexander Gubin <alexander.gubin@oediv.de>
Date: Tue, 29 Apr 2025 14:32:43 +0200
Subject: [PATCH 015/169] fix(commit): use os.unlink to remove temp file

NamedTemporaryFile doesn't have a unlink function when delete=False is used

Fix https://github.com/commitizen-tools/commitizen/issues/1352
---
 commitizen/commands/commit.py         | 2 +-
 tests/commands/test_commit_command.py | 2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index abecb3b3ca..93f048082b 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -89,7 +89,7 @@ def manual_edit(self, message: str) -> str:
         subprocess.call(argv)
         with open(file_path) as temp_file:
             message = temp_file.read().strip()
-        file.unlink()
+        os.unlink(file.name)
         return message
 
     def __call__(self):
diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py
index 55751f6902..3a92f5af48 100644
--- a/tests/commands/test_commit_command.py
+++ b/tests/commands/test_commit_command.py
@@ -511,8 +511,6 @@ def test_manual_edit(editor, config, mocker: MockFixture, tmp_path):
 
         assert edited_message == test_message.strip()
 
-        temp_file.unlink()
-
 
 @skip_below_py_3_13
 def test_commit_command_shows_description_when_use_help_option(

From 53d43ad0920b23e8867952274783f14848cb887e Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 5 May 2025 01:09:40 +0000
Subject: [PATCH 016/169] =?UTF-8?q?bump:=20version=204.6.0=20=E2=86=92=204?=
 =?UTF-8?q?.6.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 6 ++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1373277b2d..ee153c3500 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.6.0 # automatically updated by Commitizen
+    rev: v4.6.1 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d2ab1e48f..ecd6b68143 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## v4.6.1 (2025-05-05)
+
+### Fix
+
+- **commit**: use os.unlink to remove temp file
+
 ## v4.6.0 (2025-04-13)
 
 ### Feat
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index db01fb213a..b56d675263 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.6.0"
+__version__ = "4.6.1"
diff --git a/pyproject.toml b/pyproject.toml
index 5be231aaa7..6744c14858 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.6.0"
+version = "4.6.1"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.6.0"
+version = "4.6.1"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 943b51fb7890b2a14e220ce6d30c7f0d40ba185c Mon Sep 17 00:00:00 2001
From: Raffaello Baluyot <baluyotraf@outlook.com>
Date: Mon, 5 May 2025 22:19:55 +0200
Subject: [PATCH 017/169] fix(docs): fix url link and table formatting in the
 customization docs (#1399)

---
 docs/customization.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/customization.md b/docs/customization.md
index 50113301db..da41071b98 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -164,7 +164,7 @@ commitizen:
 | `change_type_map`   | `dict` | `None`  | (OPTIONAL) Dictionary mapping the type of the commit to a changelog entry                                                                                                                                                        |
 
 [jinja2]: https://jinja.palletsprojects.com/en/2.10.x/
-[changelog-spec]: https://commitizen-tools.github.io/commitizen/changelog/
+[changelog-spec]: https://commitizen-tools.github.io/commitizen/commands/changelog/
 
 #### Detailed `questions` content
 
@@ -177,6 +177,7 @@ commitizen:
 | `default`   | `Any`  | `None`  | (OPTIONAL) The default value for this question.                                                                                                                                                 |
 | `filter`    | `str`  | `None`  | (OPTIONAL) Validator for user's answer. **(Work in Progress)**                                                                                                                                  |
 | `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`.                                                                                                                                            |
+
 [different-question-types]: https://github.com/tmbo/questionary#different-question-types
 
 #### Shortcut keys

From c44e2fe8ccd7760147d90426c3782d525558d589 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 5 May 2025 20:20:18 +0000
Subject: [PATCH 018/169] =?UTF-8?q?bump:=20version=204.6.1=20=E2=86=92=204?=
 =?UTF-8?q?.6.2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 6 ++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ee153c3500..ac8b996267 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.6.1 # automatically updated by Commitizen
+    rev: v4.6.2 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ecd6b68143..def8dab180 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## v4.6.2 (2025-05-05)
+
+### Fix
+
+- **docs**: fix url link and table formatting in the customization docs (#1399)
+
 ## v4.6.1 (2025-05-05)
 
 ### Fix
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index b56d675263..456bc7c315 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.6.1"
+__version__ = "4.6.2"
diff --git a/pyproject.toml b/pyproject.toml
index 6744c14858..43bed32b5c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.6.1"
+version = "4.6.2"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.6.1"
+version = "4.6.2"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From a3682d5afc156272f7b3135232c3619c4d25b142 Mon Sep 17 00:00:00 2001
From: baluyotraf <baluyotraf@outlook.com>
Date: Mon, 5 May 2025 18:06:48 +0200
Subject: [PATCH 019/169] docs: Fix the changelog_pattern regex in the monorepo
 tutorial

---
 docs/tutorials/monorepo_guidance.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md
index 817f92321d..e530c596c7 100644
--- a/docs/tutorials/monorepo_guidance.md
+++ b/docs/tutorials/monorepo_guidance.md
@@ -71,11 +71,11 @@ Example config and commit for `library-b`:
 
 ```toml
 [tool.commitizen.customize]
-changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" #the pattern on types can be a wild card or any types you wish to include
+changelog_pattern = "^(feat|fix)\(library-b\)(!)?:" #the pattern on types can be a wild card or any types you wish to include
 ```
 
 A commit message looking like this, would be included:
 
 ```
-fix:(library-b) Some awesome message
+fix(library-b): Some awesome message
 ```

From 5f607f3d090bbc9291cce4ca3fec6bd1618ac2d5 Mon Sep 17 00:00:00 2001
From: baluyotraf <baluyotraf@outlook.com>
Date: Tue, 6 May 2025 15:42:35 +0200
Subject: [PATCH 020/169] Revert removal of escape in toml

---
 docs/tutorials/monorepo_guidance.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md
index e530c596c7..792c8c224f 100644
--- a/docs/tutorials/monorepo_guidance.md
+++ b/docs/tutorials/monorepo_guidance.md
@@ -71,7 +71,7 @@ Example config and commit for `library-b`:
 
 ```toml
 [tool.commitizen.customize]
-changelog_pattern = "^(feat|fix)\(library-b\)(!)?:" #the pattern on types can be a wild card or any types you wish to include
+changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" #the pattern on types can be a wild card or any types you wish to include
 ```
 
 A commit message looking like this, would be included:

From c991feaf07370e635e38cd713d59da06eb19aba1 Mon Sep 17 00:00:00 2001
From: name <you@example.com>
Date: Tue, 8 Apr 2025 02:19:34 +0800
Subject: [PATCH 021/169] docs(customization.md): add select type and search
 filter documentation

---
 docs/customization.md | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/docs/customization.md b/docs/customization.md
index da41071b98..31749d1c83 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -170,13 +170,15 @@ commitizen:
 
 | Parameter   | Type   | Default | Description                                                                                                                                                                                     |
 | ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `type`      | `str`  | `None`  | The type of questions. Valid type: `list`, `input` and etc. [See More][different-question-types]                                                                                                |
+| `type`      | `str`  | `None`  | The type of questions. Valid types: `list`, `select`, `input` and etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types]                  |
 | `name`      | `str`  | `None`  | The key for the value answered by user. It's used in `message_template`                                                                                                                         |
 | `message`   | `str`  | `None`  | Detail description for the question.                                                                                                                                                            |
-| `choices`   | `list` | `None`  | (OPTIONAL) The choices when `type = list`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. |
+| `choices`   | `list` | `None`  | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. |
 | `default`   | `Any`  | `None`  | (OPTIONAL) The default value for this question.                                                                                                                                                 |
 | `filter`    | `str`  | `None`  | (OPTIONAL) Validator for user's answer. **(Work in Progress)**                                                                                                                                  |
-| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`.                                                                                                                                            |
+| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`.                                                                                                                                        |
+| `use_search_filter` | `bool` | `False` | (OPTIONAL) Enable search/filter functionality for list/select type questions. This allows users to type and filter through the choices.                                                  |
+| `use_jk_keys` | `bool` | `True` | (OPTIONAL) Enable/disable j/k keys for navigation in list/select type questions. Set to false if you prefer arrow keys only.                                                                    |
 
 [different-question-types]: https://github.com/tmbo/questionary#different-question-types
 
@@ -445,8 +447,8 @@ Commitizen gives you the possibility to provide your own changelog template, by:
 
 - providing one with your customization class
 - providing one from the current working directory and setting it:
-    - as [configuration][template-config]
-    - as `--template` parameter to both `bump` and `changelog` commands
+  - as [configuration][template-config]
+  - as `--template` parameter to both `bump` and `changelog` commands
 - either by providing a template with the same name as the default template
 
 By default, the template used is the `CHANGELOG.md.j2` file from the commitizen repository.

From 5be28470ecf803c5092794201810ef89f54b9f87 Mon Sep 17 00:00:00 2001
From: yusin huang <os-yusin.huang@twtp1pc1753.deltaos.corp>
Date: Mon, 14 Apr 2025 10:11:42 +0800
Subject: [PATCH 022/169] test(test_cz_search_filter.py): add test cases for
 search filter configuration

---
 tests/test_cz_search_filter.py | 76 ++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 tests/test_cz_search_filter.py

diff --git a/tests/test_cz_search_filter.py b/tests/test_cz_search_filter.py
new file mode 100644
index 0000000000..0e70e3104a
--- /dev/null
+++ b/tests/test_cz_search_filter.py
@@ -0,0 +1,76 @@
+import pytest
+
+from commitizen.config import TomlConfig
+from commitizen.cz.customize import CustomizeCommitsCz
+
+TOML_WITH_SEARCH_FILTER = r"""
+[tool.commitizen]
+name = "cz_customize"
+
+[tool.commitizen.customize]
+message_template = "{{change_type}}:{% if scope %} ({{scope}}){% endif %}{% if breaking %}!{% endif %} {{message}}"
+
+[[tool.commitizen.customize.questions]]
+type = "select"
+name = "change_type"
+message = "Select the type of change you are committing"
+use_search_filter = true
+use_jk_keys = false
+choices = [
+    {value = "fix", name = "fix: A bug fix. Correlates with PATCH in SemVer"},
+    {value = "feat", name = "feat: A new feature. Correlates with MINOR in SemVer"},
+    {value = "docs", name = "docs: Documentation only changes"},
+    {value = "style", name = "style: Changes that do not affect the meaning of the code"},
+    {value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature"},
+    {value = "perf", name = "perf: A code change that improves performance"},
+    {value = "test", name = "test: Adding missing or correcting existing tests"},
+    {value = "build", name = "build: Changes that affect the build system or external dependencies"},
+    {value = "ci", name = "ci: Changes to CI configuration files and scripts"}
+]
+
+[[tool.commitizen.customize.questions]]
+type = "input"
+name = "scope"
+message = "What is the scope of this change? (class or file name): (press [enter] to skip)"
+
+[[tool.commitizen.customize.questions]]
+type = "input"
+name = "message"
+message = "Write a short and imperative summary of the code changes: (lower case and no period)"
+"""
+
+
+@pytest.fixture
+def config():
+    return TomlConfig(data=TOML_WITH_SEARCH_FILTER, path="not_exist.toml")
+
+
+def test_questions_with_search_filter(config):
+    """Test that questions are properly configured with search filter"""
+    cz = CustomizeCommitsCz(config)
+    questions = cz.questions()
+
+    # Test that the first question (change_type) has search filter enabled
+    assert questions[0]["type"] == "select"
+    assert questions[0]["name"] == "change_type"
+    assert questions[0]["use_search_filter"] is True
+    assert questions[0]["use_jk_keys"] is False
+
+    # Test that the choices are properly configured
+    choices = questions[0]["choices"]
+    assert len(choices) == 9  # We have 9 commit types
+    assert choices[0]["value"] == "fix"
+    assert choices[1]["value"] == "feat"
+
+
+def test_message_template(config):
+    """Test that the message template is properly configured"""
+    cz = CustomizeCommitsCz(config)
+    template = cz.message(
+        {
+            "change_type": "feat",
+            "scope": "search",
+            "message": "add search filter support",
+        }
+    )
+    assert template == "feat: (search) add search filter support"

From 776d70f700752789a42b31864807c389337e314f Mon Sep 17 00:00:00 2001
From: Yusin Huang <abcd51018@gmail.com>
Date: Tue, 15 Apr 2025 23:00:50 +0800
Subject: [PATCH 023/169] fix(changelog.py): modify the CHANGELOG.md generated
 by cz bump --changelog to the right place

---
 commitizen/commands/changelog.py | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 80a72651e4..71bc93265b 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -32,21 +32,28 @@ def __init__(self, config: BaseConfig, args):
             raise NotAGitProjectError()
 
         self.config: BaseConfig = config
-        self.encoding = self.config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
-
-        self.start_rev = args.get("start_rev") or self.config.settings.get(
-            "changelog_start_rev"
-        )
-        self.file_name = args.get("file_name") or cast(
+        changelog_file_name = args.get("file_name") or cast(
             str, self.config.settings.get("changelog_file")
         )
-        if not isinstance(self.file_name, str):
+        if not isinstance(changelog_file_name, str):
             raise NotAllowed(
                 "Changelog file name is broken.\n"
                 "Check the flag `--file-name` in the terminal "
                 f"or the setting `changelog_file` in {self.config.path}"
             )
+        self.file_name = (
+            str(Path(self.config.path.parent) / changelog_file_name)
+            if self.config.path is not None
+            else changelog_file_name
+        )
+
+        self.encoding = self.config.settings["encoding"]
+        self.cz = factory.commiter_factory(self.config)
+
+        self.start_rev = args.get("start_rev") or self.config.settings.get(
+            "changelog_start_rev"
+        )
+
         self.changelog_format = get_changelog_format(self.config, self.file_name)
 
         self.incremental = args["incremental"] or self.config.settings.get(

From 15c185709ea2dba6a850f1b19c9a3790d4e92f50 Mon Sep 17 00:00:00 2001
From: Yusin Huang <abcd51018@gmail.com>
Date: Tue, 15 Apr 2025 23:01:40 +0800
Subject: [PATCH 024/169] test(test_changelog_command.py): add test for
 changelog file_name construction from args and config

---
 tests/test_changelog.py | 38 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index df42b82264..6ffb6bc294 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1,13 +1,18 @@
+from __future__ import annotations
+
 import re
 from dataclasses import dataclass
 from pathlib import Path
-from typing import Any, Optional
+from typing import Any
+from unittest.mock import Mock
 
 import pytest
 from jinja2 import FileSystemLoader
 
 from commitizen import changelog, git
 from commitizen.changelog_formats import ChangelogFormat
+from commitizen.commands.changelog import Changelog
+from commitizen.config import BaseConfig
 from commitizen.cz.conventional_commits.conventional_commits import (
     ConventionalCommitsCz,
 )
@@ -1499,7 +1504,7 @@ def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
 def test_render_changelog_with_changelog_release_hook(
     gitcommits, tags, any_changelog_format: ChangelogFormat
 ):
-    def changelog_release_hook(release: dict, tag: Optional[git.GitTag]) -> dict:
+    def changelog_release_hook(release: dict, tag: git.GitTag | None) -> dict:
         release["extra"] = "whatever"
         return release
 
@@ -1631,3 +1636,32 @@ def test_tags_rules_get_version_tags(capsys: pytest.CaptureFixture):
     captured = capsys.readouterr()
     assert captured.err.count("InvalidVersion") == 2
     assert captured.err.count("not-a-version") == 2
+
+
+def test_changelog_file_name_from_args_and_config():
+    mock_config = Mock(spec=BaseConfig)
+    mock_config.path.parent = "/my/project/"
+    mock_config.settings = {
+        "name": "cz_conventional_commits",
+        "changelog_file": "CHANGELOG.md",
+        "encoding": "utf-8",
+        "changelog_start_rev": "v1.0.0",
+        "tag_format": "$version",
+        "legacy_tag_formats": [],
+        "ignored_tag_formats": [],
+        "incremental": True,
+        "changelog_merge_prerelease": True,
+    }
+
+    args = {
+        "file_name": "CUSTOM.md",
+        "incremental": None,
+        "dry_run": False,
+        "unreleased_version": "1.0.1",
+    }
+    changelog = Changelog(mock_config, args)
+    assert changelog.file_name == "/my/project/CUSTOM.md"
+
+    args = {"incremental": None, "dry_run": False, "unreleased_version": "1.0.1"}
+    changelog = Changelog(mock_config, args)
+    assert changelog.file_name == "/my/project/CHANGELOG.md"

From 7bea5a5082fe0fafc1e62515a9868095bb96fa4c Mon Sep 17 00:00:00 2001
From: Yusin Huang <abcd51018@gmail.com>
Date: Thu, 17 Apr 2025 01:02:27 +0800
Subject: [PATCH 025/169] fix(changelog.py): cross-platform path handling using
 os.path.join and modify the path linter and test parameter

---
 commitizen/commands/changelog.py |  3 ++-
 tests/test_changelog.py          | 11 ++++++++---
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 71bc93265b..3b8f43e372 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import os
 import os.path
 from difflib import SequenceMatcher
 from operator import itemgetter
@@ -42,7 +43,7 @@ def __init__(self, config: BaseConfig, args):
                 f"or the setting `changelog_file` in {self.config.path}"
             )
         self.file_name = (
-            str(Path(self.config.path.parent) / changelog_file_name)
+            os.path.join(str(self.config.path.parent), changelog_file_name)
             if self.config.path is not None
             else changelog_file_name
         )
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index 6ffb6bc294..67ba273b5c 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import os
 import re
 from dataclasses import dataclass
 from pathlib import Path
@@ -1640,7 +1641,7 @@ def test_tags_rules_get_version_tags(capsys: pytest.CaptureFixture):
 
 def test_changelog_file_name_from_args_and_config():
     mock_config = Mock(spec=BaseConfig)
-    mock_config.path.parent = "/my/project/"
+    mock_config.path.parent = "/my/project"
     mock_config.settings = {
         "name": "cz_conventional_commits",
         "changelog_file": "CHANGELOG.md",
@@ -1660,8 +1661,12 @@ def test_changelog_file_name_from_args_and_config():
         "unreleased_version": "1.0.1",
     }
     changelog = Changelog(mock_config, args)
-    assert changelog.file_name == "/my/project/CUSTOM.md"
+    assert os.path.normpath(changelog.file_name) == os.path.normpath(
+        os.path.join("/my/project", "CUSTOM.md")
+    )
 
     args = {"incremental": None, "dry_run": False, "unreleased_version": "1.0.1"}
     changelog = Changelog(mock_config, args)
-    assert changelog.file_name == "/my/project/CHANGELOG.md"
+    assert os.path.normpath(changelog.file_name) == os.path.normpath(
+        os.path.join("/my/project", "CHANGELOG.md")
+    )

From e177141ec11f6bf72a6661c6a9fabebe3b670251 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Wed, 7 May 2025 00:43:53 +0000
Subject: [PATCH 026/169] =?UTF-8?q?bump:=20version=204.6.2=20=E2=86=92=204?=
 =?UTF-8?q?.6.3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 7 +++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ac8b996267..08c31ba0a0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.6.2 # automatically updated by Commitizen
+    rev: v4.6.3 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index def8dab180..dbad463955 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## v4.6.3 (2025-05-07)
+
+### Fix
+
+- **changelog.py**: cross-platform path handling using os.path.join and modify the path linter and test parameter
+- **changelog.py**: modify the CHANGELOG.md generated by cz bump --changelog to the right place
+
 ## v4.6.2 (2025-05-05)
 
 ### Fix
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 456bc7c315..de3842dc77 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.6.2"
+__version__ = "4.6.3"
diff --git a/pyproject.toml b/pyproject.toml
index 43bed32b5c..92b28fd091 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.6.2"
+version = "4.6.3"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.6.2"
+version = "4.6.3"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 72c274309fbbb87f8bccd0ba7aee12ea71fa1fae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tuna=20Alika=C5=9Fifo=C4=9Flu?= <tunakasif@gmail.com>
Date: Thu, 8 May 2025 22:55:24 +0300
Subject: [PATCH 027/169] feat(providers): add support for `Cargo.lock`

If `Cargo.lock` file is present in the same path as the `Cargo.toml`,
the version of the package will also be updated in the `Cargo.lock`
file.

The implementation is similar to the one used for `UvProvider`, which
extends the `CargoProvider` to handle lock files. The change does not
break any existing functionality, if the `Cargo.lock` file is not
present, since it checks for the existence of the file before
attempting a lock file update.
---
 commitizen/providers/cargo_provider.py | 27 ++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py
index cee687c15b..2e73ff35a1 100644
--- a/commitizen/providers/cargo_provider.py
+++ b/commitizen/providers/cargo_provider.py
@@ -1,5 +1,7 @@
 from __future__ import annotations
 
+from pathlib import Path
+
 import tomlkit
 
 from commitizen.providers.base_provider import TomlProvider
@@ -13,6 +15,11 @@ class CargoProvider(TomlProvider):
     """
 
     filename = "Cargo.toml"
+    lock_filename = "Cargo.lock"
+
+    @property
+    def lock_file(self) -> Path:
+        return Path() / self.lock_filename
 
     def get(self, document: tomlkit.TOMLDocument) -> str:
         try:
@@ -28,3 +35,23 @@ def set(self, document: tomlkit.TOMLDocument, version: str):
         except tomlkit.exceptions.NonExistentKey:
             ...
         document["package"]["version"] = version  # type: ignore
+
+    def set_version(self, version: str) -> None:
+        super().set_version(version)
+        if self.lock_file.exists():
+            self.set_lock_version(version)
+
+    def set_lock_version(self, version: str) -> None:
+        cargo_toml_content = tomlkit.parse(self.file.read_text())
+        try:
+            package_name = cargo_toml_content["package"]["name"]  # type: ignore
+        except tomlkit.exceptions.NonExistentKey:
+            package_name = cargo_toml_content["workspace"]["package"]["name"]  # type: ignore
+
+        cargo_lock_content = tomlkit.parse(self.lock_file.read_text())
+        packages: tomlkit.items.AoT = cargo_lock_content["package"]  # type: ignore[assignment]
+        for i, package in enumerate(packages):
+            if package["name"] == package_name:
+                cargo_lock_content["package"][i]["version"] = version  # type: ignore[index]
+                break
+        self.lock_file.write_text(tomlkit.dumps(cargo_lock_content))

From aabeae3f64c91b2735259a849c6832bfc0b997f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tuna=20Alika=C5=9Fifo=C4=9Flu?= <tunakasif@gmail.com>
Date: Thu, 8 May 2025 23:07:24 +0300
Subject: [PATCH 028/169] refactor(tests): increase verbosity of variables

To add more test variables, increase the verbosity of current ones
and make them more clear.
---
 tests/providers/test_cargo_provider.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py
index 646ef3a53d..2144a145a7 100644
--- a/tests/providers/test_cargo_provider.py
+++ b/tests/providers/test_cargo_provider.py
@@ -15,7 +15,7 @@
 version = "0.1.0"
 """
 
-CARGO_EXPECTED = """\
+CARGO_TOML_EXPECTED = """\
 [package]
 name = "whatever"
 version = "42.1"
@@ -27,7 +27,7 @@
 version = "0.1.0"
 """
 
-CARGO_WORKSPACE_EXPECTED = """\
+CARGO_WORKSPACE_TOML_EXPECTED = """\
 [workspace.package]
 name = "whatever"
 version = "42.1"
@@ -37,8 +37,8 @@
 @pytest.mark.parametrize(
     "content, expected",
     (
-        (CARGO_TOML, CARGO_EXPECTED),
-        (CARGO_WORKSPACE_TOML, CARGO_WORKSPACE_EXPECTED),
+        (CARGO_TOML, CARGO_TOML_EXPECTED),
+        (CARGO_WORKSPACE_TOML, CARGO_WORKSPACE_TOML_EXPECTED),
     ),
 )
 def test_cargo_provider(

From d14218b3ddf0e95855f6147ae472559c3a9d5055 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tuna=20Alika=C5=9Fifo=C4=9Flu?= <tunakasif@gmail.com>
Date: Thu, 8 May 2025 23:08:12 +0300
Subject: [PATCH 029/169] test: add test for cargo provider with lock files

Add a test to check the behavior of the cargo provider when a
`Cargo.lock` file is present. This test ensures that the
`CargoProvider` behaves correctly when the lock file is present, and
updates both the `Cargo.lock` and `Cargo.toml` files accordingly.
---
 tests/providers/test_cargo_provider.py | 69 ++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py
index 2144a145a7..4b20c6ea53 100644
--- a/tests/providers/test_cargo_provider.py
+++ b/tests/providers/test_cargo_provider.py
@@ -33,6 +33,32 @@
 version = "42.1"
 """
 
+CARGO_LOCK = """\
+[[package]]
+name = "whatever"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "123abc"
+dependencies = [
+ "packageA",
+ "packageB",
+ "packageC",
+]
+"""
+
+CARGO_LOCK_EXPECTED = """\
+[[package]]
+name = "whatever"
+version = "42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "123abc"
+dependencies = [
+ "packageA",
+ "packageB",
+ "packageC",
+]
+"""
+
 
 @pytest.mark.parametrize(
     "content, expected",
@@ -58,3 +84,46 @@ def test_cargo_provider(
 
     provider.set_version("42.1")
     assert file.read_text() == dedent(expected)
+
+
+@pytest.mark.parametrize(
+    "toml_content, lock_content, toml_expected, lock_expected",
+    (
+        (
+            CARGO_TOML,
+            CARGO_LOCK,
+            CARGO_TOML_EXPECTED,
+            CARGO_LOCK_EXPECTED,
+        ),
+        (
+            CARGO_WORKSPACE_TOML,
+            CARGO_LOCK,
+            CARGO_WORKSPACE_TOML_EXPECTED,
+            CARGO_LOCK_EXPECTED,
+        ),
+    ),
+)
+def test_cargo_provider_with_lock(
+    config: BaseConfig,
+    chdir: Path,
+    toml_content: str,
+    lock_content: str,
+    toml_expected: str,
+    lock_expected: str,
+):
+    filename = CargoProvider.filename
+    file = chdir / filename
+    file.write_text(dedent(toml_content))
+
+    lock_filename = CargoProvider.lock_filename
+    lock_file = chdir / lock_filename
+    lock_file.write_text(dedent(lock_content))
+    config.settings["version_provider"] = "cargo"
+
+    provider = get_provider(config)
+    assert isinstance(provider, CargoProvider)
+    assert provider.get_version() == "0.1.0"
+
+    provider.set_version("42.1")
+    assert file.read_text() == dedent(toml_expected)
+    assert lock_file.read_text() == dedent(lock_expected)

From 7173c47d8561ec86d060ec3c8783604b7744cea9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tuna=20Alika=C5=9Fifo=C4=9Flu?= <tunakasif@gmail.com>
Date: Thu, 8 May 2025 23:22:17 +0300
Subject: [PATCH 030/169] docs(providers): update `cargo` explanation

Add `Cargo.lock` update information to the documentation.
---
 docs/config.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/config.md b/docs/config.md
index d1ae90b29a..a522312743 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -353,7 +353,7 @@ Commitizen provides some version providers for some well known formats:
 | `pep621`     | Get and set version from `pyproject.toml` `project.version` field                                                                                                                                                       |
 | `poetry`     | Get and set version from `pyproject.toml` `tool.poetry.version` field                                                                                                                                                   |
 | `uv`         | Get and set version from `pyproject.toml` `project.version` field and `uv.lock` `package.version` field whose `package.name` field is the same as `pyproject.toml` `project.name` field                                 |
-| `cargo`      | Get and set version from `Cargo.toml` `project.version` field                                                                                                                                                           |
+| `cargo`      | Get and set version from `Cargo.toml` `package.version` field and `Cargo.lock` `package.version` field whose `package.name` field is the same as `Cargo.toml` `package.name` field                                      |
 | `npm`        | Get and set version from `package.json` `version` field, `package-lock.json` `version,packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `version,packages.''.version` fields if the file exists |
 | `composer`   | Get and set version from `composer.json` `project.version` field                                                                                                                                                        |
 

From be02801f92cb948ba86d8c1a9a306be4172e4efe Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Sat, 10 May 2025 13:47:12 +0000
Subject: [PATCH 031/169] =?UTF-8?q?bump:=20version=204.6.3=20=E2=86=92=204?=
 =?UTF-8?q?.7.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   |  2 +-
 CHANGELOG.md              | 10 ++++++++++
 commitizen/__version__.py |  2 +-
 pyproject.toml            |  4 ++--
 4 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 08c31ba0a0..ad78003cf3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.6.3 # automatically updated by Commitizen
+    rev: v4.7.0 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbad463955..7ca4f2aa70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## v4.7.0 (2025-05-10)
+
+### Feat
+
+- **providers**: add support for `Cargo.lock`
+
+### Refactor
+
+- **tests**: increase verbosity of variables
+
 ## v4.6.3 (2025-05-07)
 
 ### Fix
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index de3842dc77..8355eb42a9 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.6.3"
+__version__ = "4.7.0"
diff --git a/pyproject.toml b/pyproject.toml
index 92b28fd091..02c6d12762 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.6.3"
+version = "4.7.0"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.6.3"
+version = "4.7.0"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 454494788f0b9efc90b90c496d144b0edfc76348 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 16:09:40 +0800
Subject: [PATCH 032/169] docs(bug_report.yml): update bug_report.yml

Closes #1134
---
 .github/ISSUE_TEMPLATE/bug_report.yml | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 10d782d30b..3b15906cfa 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -48,14 +48,14 @@ body:
     attributes:
       label: Environment
       description: |
-        For older commitizen versions, please include the output of the following commands manually
-      placeholder: |
-        - commitizen version: `cz version`
-        - python version: `python --version`
-        - operating system: `python3 -c "import platform; print(platform.system())"`
+        For older commitizen versions, please include the output of the following command manually:
 
         ```bash
         cz version --report
         ```
+      placeholder: |
+        Commitizen Version: 4.0.0
+        Python Version: 3.13.3 (main, Apr  8 2025, 13:54:08) [Clang 16.0.0 (clang-1600.0.26.6)]
+        Operating System: Darwin
     validations:
       required: true

From 617b610e2f6e1ea9c373a09e7355d134ff0e7ac8 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 21:04:16 +0800
Subject: [PATCH 033/169] docs(bug_report.yml): resolve comments

---
 .github/ISSUE_TEMPLATE/bug_report.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 3b15906cfa..9ed5badae4 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -48,7 +48,7 @@ body:
     attributes:
       label: Environment
       description: |
-        For older commitizen versions, please include the output of the following command manually:
+        Please use the following command to retrieve environment information
 
         ```bash
         cz version --report

From d86ec4c32b619020f86724fdf7804cee48b77adc Mon Sep 17 00:00:00 2001
From: Tim Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 19:11:52 +0800
Subject: [PATCH 034/169] style: fix some typos (#1407)

---
 commitizen/commands/bump.py                    |  2 +-
 commitizen/commands/changelog.py               |  2 +-
 commitizen/commands/check.py                   |  6 +++---
 commitizen/commands/commit.py                  |  2 +-
 commitizen/commands/example.py                 |  2 +-
 commitizen/commands/info.py                    |  2 +-
 commitizen/commands/init.py                    |  2 +-
 commitizen/commands/schema.py                  |  2 +-
 commitizen/factory.py                          |  2 +-
 commitizen/version_schemes.py                  | 10 +++++-----
 tests/commands/test_bump_command.py            | 18 +++++++++---------
 tests/commands/test_changelog_command.py       |  4 ++--
 tests/commands/test_check_command.py           |  2 +-
 tests/commands/test_init_command.py            |  2 +-
 tests/conftest.py                              |  2 +-
 tests/test_bump_update_version_in_files.py     |  4 ++--
 tests/test_changelog_format_asciidoc.py        |  2 +-
 tests/test_changelog_format_markdown.py        |  2 +-
 .../test_changelog_format_restructuredtext.py  |  2 +-
 tests/test_changelog_format_textile.py         |  2 +-
 tests/test_cz_customize.py                     |  6 +++---
 tests/test_factory.py                          |  4 ++--
 tests/test_version_scheme_pep440.py            |  4 ++--
 tests/test_version_scheme_semver.py            |  4 ++--
 24 files changed, 45 insertions(+), 45 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 60853094f9..0a2bbe37fc 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -68,7 +68,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
                 if arguments[key] is not None
             },
         }
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
         self.changelog_flag = arguments["changelog"]
         self.changelog_config = self.config.settings.get("update_changelog_on_bump")
         self.changelog_to_stdout = arguments["changelog_to_stdout"]
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 3b8f43e372..fb6f76a6b7 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -49,7 +49,7 @@ def __init__(self, config: BaseConfig, args):
         )
 
         self.encoding = self.config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
         self.start_rev = args.get("start_rev") or self.config.settings.get(
             "changelog_start_rev"
diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index e22155cf78..087db03ea5 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -46,7 +46,7 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd(
 
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
     def _valid_command_argument(self):
         num_exclusive_args_provided = sum(
@@ -72,7 +72,7 @@ def __call__(self):
             raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'")
 
         pattern = self.cz.schema_pattern()
-        ill_formated_commits = [
+        ill_formatted_commits = [
             commit
             for commit in commits
             if not self.validate_commit_message(commit.message, pattern)
@@ -80,7 +80,7 @@ def __call__(self):
         displayed_msgs_content = "\n".join(
             [
                 f'commit "{commit.rev}": "{commit.message}"'
-                for commit in ill_formated_commits
+                for commit in ill_formatted_commits
             ]
         )
         if displayed_msgs_content:
diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index 93f048082b..cb34c41a50 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -35,7 +35,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
 
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
         self.arguments = arguments
         self.temp_file: str = get_backup_file_path()
 
diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py
index e7abe7b318..a28ad85f16 100644
--- a/commitizen/commands/example.py
+++ b/commitizen/commands/example.py
@@ -7,7 +7,7 @@ class Example:
 
     def __init__(self, config: BaseConfig, *args):
         self.config: BaseConfig = config
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
     def __call__(self):
         out.write(self.cz.example())
diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py
index afac9797e4..abd4197e7f 100644
--- a/commitizen/commands/info.py
+++ b/commitizen/commands/info.py
@@ -7,7 +7,7 @@ class Info:
 
     def __init__(self, config: BaseConfig, *args):
         self.config: BaseConfig = config
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
     def __call__(self):
         out.write(self.cz.info())
diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index df872ec7ee..20277399d8 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -82,7 +82,7 @@ class Init:
     def __init__(self, config: BaseConfig, *args):
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
         self.project_info = ProjectInfo()
 
     def __call__(self):
diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py
index 0940648cde..4af5679cf5 100644
--- a/commitizen/commands/schema.py
+++ b/commitizen/commands/schema.py
@@ -7,7 +7,7 @@ class Schema:
 
     def __init__(self, config: BaseConfig, *args):
         self.config: BaseConfig = config
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
     def __call__(self):
         out.write(self.cz.schema())
diff --git a/commitizen/factory.py b/commitizen/factory.py
index 09af5fd0f7..b5d665b65e 100644
--- a/commitizen/factory.py
+++ b/commitizen/factory.py
@@ -4,7 +4,7 @@
 from commitizen.exceptions import NoCommitizenFoundException
 
 
-def commiter_factory(config: BaseConfig) -> BaseCommitizen:
+def committer_factory(config: BaseConfig) -> BaseCommitizen:
     """Return the correct commitizen existing in the registry."""
     name: str = config.settings["name"]
     try:
diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py
index 2486be58c8..84ded9316e 100644
--- a/commitizen/version_schemes.py
+++ b/commitizen/version_schemes.py
@@ -19,7 +19,7 @@
 else:
     import importlib_metadata as metadata
 
-from packaging.version import InvalidVersion  # noqa: F401: Rexpose the common exception
+from packaging.version import InvalidVersion  # noqa: F401: expose the common exception
 from packaging.version import Version as _BaseVersion
 
 from commitizen.defaults import MAJOR, MINOR, PATCH, Settings
@@ -78,7 +78,7 @@ def is_prerelease(self) -> bool:
 
     @property
     def prerelease(self) -> str | None:
-        """The prelease potion of the version is this is a prerelease."""
+        """The prerelease potion of the version is this is a prerelease."""
         raise NotImplementedError("must be implemented")
 
     @property
@@ -142,7 +142,7 @@ def bump(
             prerelease: The type of prerelease, if Any
             is_local_version: Whether to increment the local version instead
             exact_increment: Treat the increment and prerelease arguments explicitly.  Disables logic
-                that attempts to deduce the correct increment when a prelease suffix is present.
+                that attempts to deduce the correct increment when a prerelease suffix is present.
         """
 
 
@@ -351,7 +351,7 @@ class SemVer2(SemVer):
     See: https://semver.org/spec/v2.0.0.html
     """
 
-    _STD_PRELEASES = {
+    _STD_PRERELEASES = {
         "a": "alpha",
         "b": "beta",
     }
@@ -359,7 +359,7 @@ class SemVer2(SemVer):
     @property
     def prerelease(self) -> str | None:
         if self.is_prerelease and self.pre:
-            prerelease_type = self._STD_PRELEASES.get(self.pre[0], self.pre[0])
+            prerelease_type = self._STD_PRERELEASES.get(self.pre[0], self.pre[0])
             return f"{prerelease_type}.{self.pre[1]}"
         return None
 
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index b5ff7e6edb..f3c1ba923b 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -209,7 +209,7 @@ def test_bump_command_increment_option(
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
-def test_bump_command_prelease(mocker: MockFixture):
+def test_bump_command_prerelease(mocker: MockFixture):
     create_file_and_commit("feat: location")
 
     # Create an alpha pre-release.
@@ -281,7 +281,7 @@ def test_bump_command_prelease(mocker: MockFixture):
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
-def test_bump_command_prelease_increment(mocker: MockFixture):
+def test_bump_command_prerelease_increment(mocker: MockFixture):
     # FINAL RELEASE
     create_file_and_commit("fix: location")
 
@@ -317,7 +317,7 @@ def test_bump_command_prelease_increment(mocker: MockFixture):
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
-def test_bump_command_prelease_exact_mode(mocker: MockFixture):
+def test_bump_command_prerelease_exact_mode(mocker: MockFixture):
     # PRERELEASE
     create_file_and_commit("feat: location")
 
@@ -437,7 +437,7 @@ def test_bump_on_git_with_hooks_no_verify_enabled(mocker: MockFixture):
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
-def test_bump_when_bumpping_is_not_support(mocker: MockFixture):
+def test_bump_when_bumping_is_not_support(mocker: MockFixture):
     create_file_and_commit(
         "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported"
     )
@@ -1062,7 +1062,7 @@ def test_bump_use_version_provider(mocker: MockFixture):
     mock.set_version.assert_called_once_with("0.0.1")
 
 
-def test_bump_command_prelease_scheme_via_cli(
+def test_bump_command_prerelease_scheme_via_cli(
     tmp_commitizen_project_initial, mocker: MockFixture
 ):
     tmp_commitizen_project = tmp_commitizen_project_initial()
@@ -1101,7 +1101,7 @@ def test_bump_command_prelease_scheme_via_cli(
             assert "0.2.0" in f.read()
 
 
-def test_bump_command_prelease_scheme_via_config(
+def test_bump_command_prerelease_scheme_via_config(
     tmp_commitizen_project_initial, mocker: MockFixture
 ):
     tmp_commitizen_project = tmp_commitizen_project_initial(
@@ -1145,7 +1145,7 @@ def test_bump_command_prelease_scheme_via_config(
             assert "0.2.0" in f.read()
 
 
-def test_bump_command_prelease_scheme_check_old_tags(
+def test_bump_command_prerelease_scheme_check_old_tags(
     tmp_commitizen_project_initial, mocker: MockFixture
 ):
     tmp_commitizen_project = tmp_commitizen_project_initial(
@@ -1285,7 +1285,7 @@ def test_bump_command_version_scheme_priority_over_version_type(mocker: MockFixt
         ),
     ),
 )
-def test_bump_template_option_precedance(
+def test_bump_template_option_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     any_changelog_format: ChangelogFormat,
@@ -1327,7 +1327,7 @@ def test_bump_template_option_precedance(
     assert out == expected
 
 
-def test_bump_template_extras_precedance(
+def test_bump_template_extras_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     any_changelog_format: ChangelogFormat,
diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py
index f794b8d9f3..12a2e04b42 100644
--- a/tests/commands/test_changelog_command.py
+++ b/tests/commands/test_changelog_command.py
@@ -1481,7 +1481,7 @@ def test_changelog_from_current_version_tag_with_nonversion_tag(
         ),
     ),
 )
-def test_changelog_template_option_precedance(
+def test_changelog_template_option_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     any_changelog_format: ChangelogFormat,
@@ -1523,7 +1523,7 @@ def test_changelog_template_option_precedance(
     assert out == expected
 
 
-def test_changelog_template_extras_precedance(
+def test_changelog_template_extras_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     mock_plugin: BaseCommitizen,
diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py
index f1db446190..d95a173d8a 100644
--- a/tests/commands/test_check_command.py
+++ b/tests/commands/test_check_command.py
@@ -365,7 +365,7 @@ def test_check_command_with_pipe_message_and_failed(mocker: MockFixture):
     assert "commit validation: failed!" in str(excinfo.value)
 
 
-def test_check_command_with_comment_in_messege_file(mocker: MockFixture, capsys):
+def test_check_command_with_comment_in_message_file(mocker: MockFixture, capsys):
     testargs = ["cz", "check", "--commit-msg-file", "some_file"]
     mocker.patch.object(sys, "argv", testargs)
     mocker.patch(
diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py
index ea18e89a2b..f617c51d8f 100644
--- a/tests/commands/test_init_command.py
+++ b/tests/commands/test_init_command.py
@@ -183,7 +183,7 @@ def check_pre_commit_config(expected: list[dict[str, Any]]):
 
 @pytest.mark.usefixtures("pre_commit_installed")
 class TestPreCommitCases:
-    def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config):
+    def test_no_existing_pre_commit_config(_, default_choice, tmpdir, config):
         with tmpdir.as_cwd():
             commands.Init(config)()
             check_cz_config(default_choice)
diff --git a/tests/conftest.py b/tests/conftest.py
index 3d88f19b12..60c586f2e6 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -232,7 +232,7 @@ def message(self, answers: dict) -> str:
 @pytest.fixture
 def mock_plugin(mocker: MockerFixture, config: BaseConfig) -> BaseCommitizen:
     mock = MockPlugin(config)
-    mocker.patch("commitizen.factory.commiter_factory", return_value=mock)
+    mocker.patch("commitizen.factory.committer_factory", return_value=mock)
     return mock
 
 
diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py
index 850b59c166..c14e4ad1cb 100644
--- a/tests/test_bump_update_version_in_files.py
+++ b/tests/test_bump_update_version_in_files.py
@@ -197,7 +197,7 @@ def test_file_version_inconsistent_error(
     assert expected_msg in str(excinfo.value)
 
 
-def test_multiplt_versions_to_bump(
+def test_multiple_versions_to_bump(
     multiple_versions_to_update_poetry_lock, file_regression
 ):
     old_version = "1.2.9"
@@ -216,7 +216,7 @@ def test_update_version_in_globbed_files(commitizen_config_file, file_regression
     print(commitizen_config_file, other)
     copyfile(commitizen_config_file, other)
 
-    # Prepend full ppath as test assume absolute paths or cwd-relative
+    # Prepend full path as test assume absolute paths or cwd-relative
     version_files = [commitizen_config_file.dirpath("*.toml")]
 
     bump.update_version_in_files(
diff --git a/tests/test_changelog_format_asciidoc.py b/tests/test_changelog_format_asciidoc.py
index 59ca56191e..cc81a24f2a 100644
--- a/tests/test_changelog_format_asciidoc.py
+++ b/tests/test_changelog_format_asciidoc.py
@@ -160,7 +160,7 @@ def test_parse_title_type_of_line(
         pytest.param(CHANGELOG_D, EXPECTED_D, id="D"),
     ),
 )
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: AsciiDoc, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
diff --git a/tests/test_changelog_format_markdown.py b/tests/test_changelog_format_markdown.py
index e1f0d67311..1abc63f29f 100644
--- a/tests/test_changelog_format_markdown.py
+++ b/tests/test_changelog_format_markdown.py
@@ -160,7 +160,7 @@ def test_parse_title_type_of_line(
         pytest.param(CHANGELOG_D, EXPECTED_D, id="D"),
     ),
 )
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: Markdown, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
diff --git a/tests/test_changelog_format_restructuredtext.py b/tests/test_changelog_format_restructuredtext.py
index 74b6b736f9..14bc15ec09 100644
--- a/tests/test_changelog_format_restructuredtext.py
+++ b/tests/test_changelog_format_restructuredtext.py
@@ -311,7 +311,7 @@ def format_with_tags(config: BaseConfig, request) -> RestructuredText:
 
 
 @pytest.mark.parametrize("content, expected", CASES)
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: RestructuredText, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
diff --git a/tests/test_changelog_format_textile.py b/tests/test_changelog_format_textile.py
index eb03484ad5..812fa6bf60 100644
--- a/tests/test_changelog_format_textile.py
+++ b/tests/test_changelog_format_textile.py
@@ -153,7 +153,7 @@ def test_parse_title_type_of_line(
         pytest.param(CHANGELOG_D, EXPECTED_D, id="D"),
     ),
 )
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: Textile, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py
index 210c8b6774..933b1aa065 100644
--- a/tests/test_cz_customize.py
+++ b/tests/test_cz_customize.py
@@ -473,16 +473,16 @@ def test_answer(config):
     cz = CustomizeCommitsCz(config)
     answers = {
         "change_type": "feature",
-        "message": "this feature enaable customize through config file",
+        "message": "this feature enable customize through config file",
         "show_message": True,
     }
     message = cz.message(answers)
-    assert message == "feature: this feature enaable customize through config file"
+    assert message == "feature: this feature enable customize through config file"
 
     cz = CustomizeCommitsCz(config)
     answers = {
         "change_type": "feature",
-        "message": "this feature enaable customize through config file",
+        "message": "this feature enable customize through config file",
         "show_message": False,
     }
     message = cz.message(answers)
diff --git a/tests/test_factory.py b/tests/test_factory.py
index 390742f467..d81a84b3d5 100644
--- a/tests/test_factory.py
+++ b/tests/test_factory.py
@@ -28,7 +28,7 @@ class OtherPlugin:
 def test_factory():
     config = BaseConfig()
     config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]})
-    r = factory.commiter_factory(config)
+    r = factory.committer_factory(config)
     assert isinstance(r, BaseCommitizen)
 
 
@@ -36,7 +36,7 @@ def test_factory_fails():
     config = BaseConfig()
     config.settings.update({"name": "Nothing"})
     with pytest.raises(NoCommitizenFoundException) as excinfo:
-        factory.commiter_factory(config)
+        factory.committer_factory(config)
 
     assert "The committer has not been found in the system." in str(excinfo)
 
diff --git a/tests/test_version_scheme_pep440.py b/tests/test_version_scheme_pep440.py
index 6b1f621cb8..a983dad14a 100644
--- a/tests/test_version_scheme_pep440.py
+++ b/tests/test_version_scheme_pep440.py
@@ -144,7 +144,7 @@
     (("3.1.4a0", "MAJOR", "alpha", 0, None), "4.0.0a0"),
 ]
 
-excact_cases = [
+exact_cases = [
     (("1.0.0", "PATCH", None, 0, None), "1.0.1"),
     (("1.0.0", "MINOR", None, 0, None), "1.1.0"),
     # with exact_increment=False: "1.0.0b0"
@@ -213,7 +213,7 @@ def test_bump_pep440_version(test_input, expected):
     )
 
 
-@pytest.mark.parametrize("test_input, expected", excact_cases)
+@pytest.mark.parametrize("test_input, expected", exact_cases)
 def test_bump_pep440_version_force(test_input, expected):
     current_version = test_input[0]
     increment = test_input[1]
diff --git a/tests/test_version_scheme_semver.py b/tests/test_version_scheme_semver.py
index 71d5e5876c..8785717a34 100644
--- a/tests/test_version_scheme_semver.py
+++ b/tests/test_version_scheme_semver.py
@@ -83,7 +83,7 @@
     (("1.0.0-alpha1", None, "alpha", 0, None), "1.0.0-a2"),
 ]
 
-excact_cases = [
+exact_cases = [
     (("1.0.0", "PATCH", None, 0, None), "1.0.1"),
     (("1.0.0", "MINOR", None, 0, None), "1.1.0"),
     # with exact_increment=False: "1.0.0-b0"
@@ -144,7 +144,7 @@ def test_bump_semver_version(test_input, expected):
     )
 
 
-@pytest.mark.parametrize("test_input, expected", excact_cases)
+@pytest.mark.parametrize("test_input, expected", exact_cases)
 def test_bump_semver_version_force(test_input, expected):
     current_version = test_input[0]
     increment = test_input[1]

From 2cb2daf39b78d0796ff39cc2912047ea6ea32e5d Mon Sep 17 00:00:00 2001
From: Tim Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 19:13:20 +0800
Subject: [PATCH 035/169] docs(pull_request_template): make the checklist
 holistic (#1408)

---
 .github/pull_request_template.md | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 0064604fba..e28480e5b9 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -9,12 +9,24 @@ Please fill in the following content to let us know better about this change.
 
 ## Checklist
 
+- [ ] I have read the [contributing guidelines](https://commitizen-tools.github.io/commitizen/contributing/)
+
+### Code Changes
+
 - [ ] Add test cases to all the changes you introduce
-- [ ] Run `poetry all` locally to ensure this change passes linter check and test
-- [ ] Test the changes on the local machine manually
+- [ ] Run `poetry all` locally to ensure this change passes linter check and tests
+- [ ] Manually test the changes:
+  - [ ] Verify the feature/bug fix works as expected in real-world scenarios
+  - [ ] Test edge cases and error conditions
+  - [ ] Ensure backward compatibility is maintained
+  - [ ] Document any manual testing steps performed
 - [ ] Update the documentation for the changes
 
-## Expected behavior
+### Documentation Changes
+
+- [ ] Run `poetry doc` locally to ensure the documentation pages renders correctly
+
+## Expected Behavior
 <!-- A clear and concise description of what you expected to happen -->
 
 
@@ -25,5 +37,5 @@ Please fill in the following content to let us know better about this change.
 3. ... -->
 
 
-## Additional context
+## Additional Context
 <!-- Add any other RELATED ISSUE, context or screenshots about the pull request here. -->

From 46c5ecc79ec7b3e1006a33cd69e1f576a5ba6379 Mon Sep 17 00:00:00 2001
From: "Axel H." <noirbizarre@users.noreply.github.com>
Date: Fri, 16 May 2025 04:50:15 +0200
Subject: [PATCH 036/169] fix(bump): don't fail if an invalid version tag is
 present (fix #1410) (#1418)

---
 commitizen/tags.py                       | 12 +++++----
 tests/commands/test_bump_command.py      | 32 ++++++++++++++++++++++++
 tests/commands/test_changelog_command.py |  8 +++---
 tests/test_changelog.py                  |  2 +-
 4 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/commitizen/tags.py b/commitizen/tags.py
index 5724bb2574..2b9a4b091a 100644
--- a/commitizen/tags.py
+++ b/commitizen/tags.py
@@ -113,6 +113,10 @@ def _format_regex(self, tag_pattern: str, star: bool = False) -> str:
             format_regex = format_regex.replace(pattern, regex)
         return format_regex
 
+    def _version_tag_error(self, tag: str) -> str:
+        """Format the error message for an invalid version tag"""
+        return f"Invalid version tag: '{tag}' does not match any configured tag format"
+
     def is_version_tag(self, tag: str | GitTag, warn: bool = False) -> bool:
         """
         True if a given tag is a legit version tag.
@@ -120,9 +124,9 @@ def is_version_tag(self, tag: str | GitTag, warn: bool = False) -> bool:
         if `warn` is `True`, it will print a warning message if the tag is not a version tag.
         """
         tag = tag.name if isinstance(tag, GitTag) else tag
-        is_legit = any(regex.match(tag) for regex in self.version_regexes)
+        is_legit = any(regex.fullmatch(tag) for regex in self.version_regexes)
         if warn and not is_legit and not self.is_ignored_tag(tag):
-            out.warn(f"InvalidVersion {tag} doesn't match any configured tag format")
+            out.warn(self._version_tag_error(tag))
         return is_legit
 
     def is_ignored_tag(self, tag: str | GitTag) -> bool:
@@ -146,9 +150,7 @@ def extract_version(self, tag: GitTag) -> Version:
             m for regex in self.version_regexes if (m := regex.fullmatch(tag.name))
         )
         if not (m := next(candidates, None)):
-            raise InvalidVersion(
-                f"Invalid version tag: '{tag.name}' does not match any configured tag format"
-            )
+            raise InvalidVersion(self._version_tag_error(tag.name))
         if "version" in m.groupdict():
             return self.scheme(m.group("version"))
 
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index f3c1ba923b..e15539d8a7 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -1656,3 +1656,35 @@ def test_bump_detect_legacy_tags_from_scm(
     cli.main()
 
     assert git.tag_exist("v0.4.3")
+
+
+def test_bump_warn_but_dont_fail_on_invalid_tags(
+    tmp_commitizen_project: py.path.local,
+    mocker: MockFixture,
+    capsys: pytest.CaptureFixture,
+):
+    project_root = Path(tmp_commitizen_project)
+    tmp_commitizen_cfg_file = project_root / "pyproject.toml"
+    tmp_commitizen_cfg_file.write_text(
+        "\n".join(
+            [
+                "[tool.commitizen]",
+                'version_provider = "scm"',
+                'version_scheme = "pep440"',
+            ]
+        ),
+    )
+    create_file_and_commit("feat: new file")
+    create_tag("0.4.2")
+    create_file_and_commit("feat: new file")
+    create_tag("0.4.3.deadbeaf")
+    create_file_and_commit("feat: new file")
+
+    testargs = ["cz", "bump", "--increment", "patch", "--changelog"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    _, err = capsys.readouterr()
+
+    assert err.count("Invalid version tag: '0.4.3.deadbeaf'") == 1
+    assert git.tag_exist("0.4.3")
diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py
index 12a2e04b42..0eb29cdb04 100644
--- a/tests/commands/test_changelog_command.py
+++ b/tests/commands/test_changelog_command.py
@@ -1788,13 +1788,13 @@ def test_changelog_ignored_tags(
     out = open(changelog_path).read()
     _, err = capsys.readouterr()
     assert "## ignore-0.1.0" not in out
-    assert "InvalidVersion ignore-0.1.0" not in err
+    assert "Invalid version tag: 'ignore-0.1.0'" not in err
     assert "## ignored" not in out
-    assert "InvalidVersion ignored" not in err
+    assert "Invalid version tag: 'ignored'" not in err
     assert "## not-ignored" not in out
-    assert "InvalidVersion not-ignored" in err
+    assert "Invalid version tag: 'not-ignored'" in err
     assert "## v0.3.0" in out
-    assert "InvalidVersion v0.3.0" not in err
+    assert "Invalid version tag: 'v0.3.0'" not in err
 
 
 def test_changelog_template_extra_quotes(
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index 67ba273b5c..067adb4a91 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1635,7 +1635,7 @@ def test_tags_rules_get_version_tags(capsys: pytest.CaptureFixture):
     }
 
     captured = capsys.readouterr()
-    assert captured.err.count("InvalidVersion") == 2
+    assert captured.err.count("Invalid version tag:") == 2
     assert captured.err.count("not-a-version") == 2
 
 

From a0cc4901b0faaced74c713a9e355555fc4de0880 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 16 May 2025 02:50:37 +0000
Subject: [PATCH 037/169] =?UTF-8?q?bump:=20version=204.7.0=20=E2=86=92=204?=
 =?UTF-8?q?.7.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 6 ++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ad78003cf3..df32532598 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.7.0 # automatically updated by Commitizen
+    rev: v4.7.1 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7ca4f2aa70..486ee8dcb2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## v4.7.1 (2025-05-16)
+
+### Fix
+
+- **bump**: don't fail if an invalid version tag is present (fix #1410) (#1418)
+
 ## v4.7.0 (2025-05-10)
 
 ### Feat
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 8355eb42a9..cc72154c1b 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.7.0"
+__version__ = "4.7.1"
diff --git a/pyproject.toml b/pyproject.toml
index 02c6d12762..734d5127be 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.7.0"
+version = "4.7.1"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.7.0"
+version = "4.7.1"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 94c02b3faf89cacf7186ad8a84e9200ede851574 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 02:28:08 +0800
Subject: [PATCH 038/169] refactor(default): capitalize all constants and
 remove unnecessary variable

---
 commitizen/bump.py                            |  8 ++++----
 commitizen/commands/changelog.py              |  2 +-
 commitizen/commands/init.py                   |  4 ++--
 commitizen/config/__init__.py                 |  2 +-
 .../conventional_commits.py                   |  8 ++++----
 commitizen/cz/customize/customize.py          |  8 ++++----
 commitizen/defaults.py                        | 19 +++++++++----------
 tests/test_conf.py                            |  4 ++--
 8 files changed, 27 insertions(+), 28 deletions(-)

diff --git a/commitizen/bump.py b/commitizen/bump.py
index adfab64cb0..76a8e15893 100644
--- a/commitizen/bump.py
+++ b/commitizen/bump.py
@@ -8,7 +8,7 @@
 from string import Template
 from typing import cast
 
-from commitizen.defaults import MAJOR, MINOR, PATCH, bump_message, encoding
+from commitizen.defaults import BUMP_MESSAGE, ENCODING, MAJOR, MINOR, PATCH
 from commitizen.exceptions import CurrentVersionNotFoundError
 from commitizen.git import GitCommit, smart_open
 from commitizen.version_schemes import Increment, Version
@@ -64,7 +64,7 @@ def update_version_in_files(
     files: list[str],
     *,
     check_consistency: bool = False,
-    encoding: str = encoding,
+    encoding: str = ENCODING,
 ) -> list[str]:
     """Change old version to the new one in every file given.
 
@@ -121,7 +121,7 @@ def _bump_with_regex(
     current_version: str,
     new_version: str,
     regex: str,
-    encoding: str = encoding,
+    encoding: str = ENCODING,
 ) -> tuple[bool, str]:
     current_version_found = False
     lines = []
@@ -148,6 +148,6 @@ def create_commit_message(
     message_template: str | None = None,
 ) -> str:
     if message_template is None:
-        message_template = bump_message
+        message_template = BUMP_MESSAGE
     t = Template(message_template)
     return t.safe_substitute(current_version=current_version, new_version=new_version)
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index fb6f76a6b7..0e4efabfa1 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -78,7 +78,7 @@ def __init__(self, config: BaseConfig, args):
         self.change_type_order = (
             self.config.settings.get("change_type_order")
             or self.cz.change_type_order
-            or defaults.change_type_order
+            or defaults.CHANGE_TYPE_ORDER
         )
         self.rev_range = args.get("rev_range")
         self.tag_format: str = (
diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index 20277399d8..0eb3d99d17 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -11,7 +11,7 @@
 from commitizen.__version__ import __version__
 from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig
 from commitizen.cz import registry
-from commitizen.defaults import DEFAULT_SETTINGS, config_files
+from commitizen.defaults import CONFIG_FILES, DEFAULT_SETTINGS
 from commitizen.exceptions import InitFailedError, NoAnswersError
 from commitizen.git import get_latest_tag_name, get_tag_names, smart_open
 from commitizen.version_schemes import KNOWN_SCHEMES, Version, get_version_scheme
@@ -165,7 +165,7 @@ def _ask_config_path(self) -> str:
 
         name: str = questionary.select(
             "Please choose a supported config file: ",
-            choices=config_files,
+            choices=CONFIG_FILES,
             default=default_path,
             style=self.cz.style,
         ).unsafe_ask()
diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py
index f3720bb1b3..9dfd591c40 100644
--- a/commitizen/config/__init__.py
+++ b/commitizen/config/__init__.py
@@ -28,7 +28,7 @@ def read_cfg(filepath: str | None = None) -> BaseConfig:
         cfg_paths = (
             path / Path(filename)
             for path in cfg_search_paths
-            for filename in defaults.config_files
+            for filename in defaults.CONFIG_FILES
         )
 
     for filename in cfg_paths:
diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index c7b88258cb..af29a209fc 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -28,9 +28,9 @@ def parse_subject(text):
 
 
 class ConventionalCommitsCz(BaseCommitizen):
-    bump_pattern = defaults.bump_pattern
-    bump_map = defaults.bump_map
-    bump_map_major_version_zero = defaults.bump_map_major_version_zero
+    bump_pattern = defaults.BUMP_PATTERN
+    bump_map = defaults.BUMP_MAP
+    bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO
     commit_parser = r"^((?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?|\w+!):\s(?P<message>.*)?"  # noqa
     change_type_map = {
         "feat": "Feat",
@@ -38,7 +38,7 @@ class ConventionalCommitsCz(BaseCommitizen):
         "refactor": "Refactor",
         "perf": "Perf",
     }
-    changelog_pattern = defaults.bump_pattern
+    changelog_pattern = defaults.BUMP_PATTERN
 
     def questions(self) -> Questions:
         questions: Questions = [
diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py
index d53ae29f1b..afa35e92de 100644
--- a/commitizen/cz/customize/customize.py
+++ b/commitizen/cz/customize/customize.py
@@ -21,10 +21,10 @@
 
 
 class CustomizeCommitsCz(BaseCommitizen):
-    bump_pattern = defaults.bump_pattern
-    bump_map = defaults.bump_map
-    bump_map_major_version_zero = defaults.bump_map_major_version_zero
-    change_type_order = defaults.change_type_order
+    bump_pattern = defaults.BUMP_PATTERN
+    bump_map = defaults.BUMP_MAP
+    bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO
+    change_type_order = defaults.CHANGE_TYPE_ORDER
 
     def __init__(self, config: BaseConfig):
         super().__init__(config)
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 0b78e1b0bb..0b6c28e6a9 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -60,8 +60,7 @@ class Settings(TypedDict, total=False):
     extras: dict[str, Any]
 
 
-name: str = "cz_conventional_commits"
-config_files: list[str] = [
+CONFIG_FILES: list[str] = [
     "pyproject.toml",
     ".cz.toml",
     ".cz.json",
@@ -70,10 +69,10 @@ class Settings(TypedDict, total=False):
     "cz.yaml",
     "cz.toml",
 ]
-encoding: str = "utf-8"
+ENCODING = "utf-8"
 
 DEFAULT_SETTINGS: Settings = {
-    "name": name,
+    "name": "cz_conventional_commits",
     "version": None,
     "version_files": [],
     "version_provider": "commitizen",
@@ -102,7 +101,7 @@ class Settings(TypedDict, total=False):
     "pre_bump_hooks": [],
     "post_bump_hooks": [],
     "prerelease_offset": 0,
-    "encoding": encoding,
+    "encoding": ENCODING,
     "always_signoff": False,
     "template": None,  # default provided by plugin
     "extras": {},
@@ -114,8 +113,8 @@ class Settings(TypedDict, total=False):
 
 CHANGELOG_FORMAT = "markdown"
 
-bump_pattern = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):"
-bump_map = OrderedDict(
+BUMP_PATTERN = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):"
+BUMP_MAP = OrderedDict(
     (
         (r"^.+!$", MAJOR),
         (r"^BREAKING[\-\ ]CHANGE", MAJOR),
@@ -125,7 +124,7 @@ class Settings(TypedDict, total=False):
         (r"^perf", PATCH),
     )
 )
-bump_map_major_version_zero = OrderedDict(
+BUMP_MAP_MAJOR_VERSION_ZERO = OrderedDict(
     (
         (r"^.+!$", MINOR),
         (r"^BREAKING[\-\ ]CHANGE", MINOR),
@@ -135,8 +134,8 @@ class Settings(TypedDict, total=False):
         (r"^perf", PATCH),
     )
 )
-change_type_order = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]
-bump_message = "bump: version $current_version → $new_version"
+CHANGE_TYPE_ORDER = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]
+BUMP_MESSAGE = "bump: version $current_version → $new_version"
 
 
 def get_tag_regexes(
diff --git a/tests/test_conf.py b/tests/test_conf.py
index 80d58983e7..f89a0049f4 100644
--- a/tests/test_conf.py
+++ b/tests/test_conf.py
@@ -151,7 +151,7 @@ def test_find_git_project_root(tmpdir):
 
 
 @pytest.mark.parametrize(
-    "config_files_manager", defaults.config_files.copy(), indirect=True
+    "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True
 )
 def test_set_key(config_files_manager):
     _conf = config.read_cfg()
@@ -162,7 +162,7 @@ def test_set_key(config_files_manager):
 
 class TestReadCfg:
     @pytest.mark.parametrize(
-        "config_files_manager", defaults.config_files.copy(), indirect=True
+        "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True
     )
     def test_load_conf(_, config_files_manager):
         cfg = config.read_cfg()

From 8d573069ff591f909c9b9725da769fbab05d5bb0 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Sun, 18 May 2025 15:17:25 +0000
Subject: [PATCH 039/169] =?UTF-8?q?bump:=20version=204.7.1=20=E2=86=92=204?=
 =?UTF-8?q?.7.2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 6 ++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index df32532598..7497946571 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.7.1 # automatically updated by Commitizen
+    rev: v4.7.2 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 486ee8dcb2..de772c6a75 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## v4.7.2 (2025-05-18)
+
+### Refactor
+
+- **default**: capitalize all constants and remove unnecessary variable
+
 ## v4.7.1 (2025-05-16)
 
 ### Fix
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index cc72154c1b..98c926d162 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.7.1"
+__version__ = "4.7.2"
diff --git a/pyproject.toml b/pyproject.toml
index 734d5127be..8d9214615a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.7.1"
+version = "4.7.2"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.7.1"
+version = "4.7.2"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 767c5fede43c8f8858778b3e7f372c532495c48e Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 19 May 2025 20:17:08 +0800
Subject: [PATCH 040/169] docs(ISSUE_TEMPLATE): correct labels

---
 .github/ISSUE_TEMPLATE/bug_report.yml      | 2 +-
 .github/ISSUE_TEMPLATE/documentation.yml   | 2 +-
 .github/ISSUE_TEMPLATE/feature_request.yml | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 9ed5badae4..9d597c7b36 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,7 +1,7 @@
 name: 🛠 Bug report
 description: Create a report to help us improve
 title: Good bug title tells us about precise symptom, not about the root cause.
-labels: [bug]
+labels: ['type: bug']
 body:
   - type: textarea
     id: description
diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml
index 51d378b747..8fe40136c3 100644
--- a/.github/ISSUE_TEMPLATE/documentation.yml
+++ b/.github/ISSUE_TEMPLATE/documentation.yml
@@ -1,7 +1,7 @@
 name: 📖 Documentation
 description: Suggest an improvement for the documentation of this project
 title: Content to be added or fixed
-labels: [documentation]
+labels: ['type: documentation']
 body:
   - type: checkboxes
     id: type
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 7d67eb18af..4bd19b9b3a 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,7 +1,7 @@
 name: 🚀 Feature request
 description: Suggest an idea for this project
 title: "<One feature request per issue>"
-labels: [feature]
+labels: ['type: feature']
 body:
   - type: textarea
     id: description

From 2982c6e5e31c9fd171e364c2a2866dc109303c20 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 19 May 2025 20:32:54 +0800
Subject: [PATCH 041/169] docs(label_issues): add logics for adding os related
 labels

---
 .github/workflows/label_issues.yml | 28 +++++++++++++++++++++++++---
 1 file changed, 25 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml
index 45ca450f89..ec18d98be2 100644
--- a/.github/workflows/label_issues.yml
+++ b/.github/workflows/label_issues.yml
@@ -15,9 +15,31 @@ jobs:
       - uses: actions/github-script@v7
         with:
           script: |
-            github.rest.issues.addLabels({
+            const issue = await github.rest.issues.get({
               issue_number: context.issue.number,
               owner: context.repo.owner,
               repo: context.repo.repo,
-              labels: ['issue-status: needs-triage']
-            })
+            });
+
+            const body = issue.data.body || '';
+
+            const osLabels = new Set(); // Use a Set to avoid duplicates
+
+            if (body.includes('Operating System: Darwin')) {
+              osLabels.add('os: macOS');
+            }
+
+            if (body.includes('Operating System: Linux')) {
+              osLabels.add('os: Linux');
+            }
+
+            if (body.includes('Operating System: Windows')) {
+              osLabels.add('os: Windows');
+            }
+
+            await github.rest.issues.addLabels({
+              issue_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              labels: ['issue-status: needs-triage', ...osLabels],
+            });

From 49f4d2b1a68de7e9b78cd39497483787de892db2 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 19 May 2025 21:13:19 +0800
Subject: [PATCH 042/169] docs(label_issues): add comment

---
 .github/workflows/label_issues.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml
index ec18d98be2..e74a36c4bd 100644
--- a/.github/workflows/label_issues.yml
+++ b/.github/workflows/label_issues.yml
@@ -25,6 +25,7 @@ jobs:
 
             const osLabels = new Set(); // Use a Set to avoid duplicates
 
+            // Parse the "Environment" section, output of `cz version --report`
             if (body.includes('Operating System: Darwin')) {
               osLabels.add('os: macOS');
             }

From e3b4465261cfe9c541cdd1174adae53bc7e309cb Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 19 May 2025 22:05:33 +0800
Subject: [PATCH 043/169] docs(bug_report): add fallback command if cz version
 --report is not available

---
 .github/ISSUE_TEMPLATE/bug_report.yml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 9d597c7b36..5eac443f29 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -53,6 +53,14 @@ body:
         ```bash
         cz version --report
         ```
+
+        If `cz version --report` is not available, please run the following commands to retrieve your environment information:
+
+        ```bash
+        echo "Commitizen Version: $(cz version)"
+        echo "Python Version: $(python3 -c 'import sys; print(sys.version)')"
+        echo "Operating System: $(python3 -c 'import platform; print(platform.system())')"
+        ```
       placeholder: |
         Commitizen Version: 4.0.0
         Python Version: 3.13.3 (main, Apr  8 2025, 13:54:08) [Clang 16.0.0 (clang-1600.0.26.6)]

From 87873f99d89b3da65388d1a93827a1536e29348e Mon Sep 17 00:00:00 2001
From: Jani Jappinen <2580412+jappja@users.noreply.github.com>
Date: Thu, 15 May 2025 09:19:20 +0000
Subject: [PATCH 044/169] feat(cli): add --tag-format argument to changelog
 command

Changelog command already checks "tag_format" argument but cli support was missing.
---
 commitizen/cli.py                                             | 4 ++++
 ...angelog_command_shows_description_when_use_help_option.txt | 4 +++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index 72d824380d..d08afc6706 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -450,6 +450,10 @@ def __call__(
                         "help": "Export the changelog template into this file instead of rendering it",
                     },
                     *deepcopy(tpl_arguments),
+                    {
+                        "name": "--tag-format",
+                        "help": "The format of the tag, wrap around simple quotes",
+                    },
                 ],
             },
             {
diff --git a/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt
index 461eb2edd6..91b7f389b5 100644
--- a/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt
+++ b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt
@@ -3,7 +3,7 @@ usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME]
                     [--start-rev START_REV] [--merge-prerelease]
                     [--version-scheme {pep440,semver,semver2}]
                     [--export-template EXPORT_TEMPLATE] [--template TEMPLATE]
-                    [--extra EXTRA]
+                    [--extra EXTRA] [--tag-format TAG_FORMAT]
                     [rev_range]
 
 generate changelog (note that it will overwrite existing file)
@@ -37,3 +37,5 @@ options:
                         changelog template file name (relative to the current
                         working directory)
   --extra, -e EXTRA     a changelog extra variable (in the form 'key=value')
+  --tag-format TAG_FORMAT
+                        The format of the tag, wrap around simple quotes

From dbdfa60e1f95eccd9eb5d22e20f854043b2a8d37 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 20 May 2025 15:03:32 +0000
Subject: [PATCH 045/169] docs(cli/screenshots): update CLI screenshots

[skip ci]
---
 docs/images/cli_help/cz_changelog___help.svg | 194 ++++++++++---------
 1 file changed, 101 insertions(+), 93 deletions(-)

diff --git a/docs/images/cli_help/cz_changelog___help.svg b/docs/images/cli_help/cz_changelog___help.svg
index 1160ccf6cf..69304f40cf 100644
--- a/docs/images/cli_help/cz_changelog___help.svg
+++ b/docs/images/cli_help/cz_changelog___help.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 994 1050.4" xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 994 1099.2" xmlns="http://www.w3.org/2000/svg">
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -19,202 +19,210 @@
         font-weight: 700;
     }
 
-    .terminal-1106739011-matrix {
+    .terminal-2926696453-matrix {
         font-family: Fira Code, monospace;
         font-size: 20px;
         line-height: 24.4px;
         font-variant-east-asian: full-width;
     }
 
-    .terminal-1106739011-title {
+    .terminal-2926696453-title {
         font-size: 18px;
         font-weight: bold;
         font-family: arial;
     }
 
-    .terminal-1106739011-r1 { fill: #c5c8c6 }
-.terminal-1106739011-r2 { fill: #c5c8c6;font-weight: bold }
-.terminal-1106739011-r3 { fill: #68a0b3;font-weight: bold }
-.terminal-1106739011-r4 { fill: #98a84b }
+    .terminal-2926696453-r1 { fill: #c5c8c6 }
+.terminal-2926696453-r2 { fill: #c5c8c6;font-weight: bold }
+.terminal-2926696453-r3 { fill: #68a0b3;font-weight: bold }
+.terminal-2926696453-r4 { fill: #98a84b }
     </style>
 
     <defs>
-    <clipPath id="terminal-1106739011-clip-terminal">
-      <rect x="0" y="0" width="975.0" height="999.4" />
+    <clipPath id="terminal-2926696453-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="1048.2" />
     </clipPath>
-    <clipPath id="terminal-1106739011-line-0">
+    <clipPath id="terminal-2926696453-line-0">
     <rect x="0" y="1.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-1">
+<clipPath id="terminal-2926696453-line-1">
     <rect x="0" y="25.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-2">
+<clipPath id="terminal-2926696453-line-2">
     <rect x="0" y="50.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-3">
+<clipPath id="terminal-2926696453-line-3">
     <rect x="0" y="74.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-4">
+<clipPath id="terminal-2926696453-line-4">
     <rect x="0" y="99.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-5">
+<clipPath id="terminal-2926696453-line-5">
     <rect x="0" y="123.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-6">
+<clipPath id="terminal-2926696453-line-6">
     <rect x="0" y="147.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-7">
+<clipPath id="terminal-2926696453-line-7">
     <rect x="0" y="172.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-8">
+<clipPath id="terminal-2926696453-line-8">
     <rect x="0" y="196.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-9">
+<clipPath id="terminal-2926696453-line-9">
     <rect x="0" y="221.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-10">
+<clipPath id="terminal-2926696453-line-10">
     <rect x="0" y="245.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-11">
+<clipPath id="terminal-2926696453-line-11">
     <rect x="0" y="269.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-12">
+<clipPath id="terminal-2926696453-line-12">
     <rect x="0" y="294.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-13">
+<clipPath id="terminal-2926696453-line-13">
     <rect x="0" y="318.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-14">
+<clipPath id="terminal-2926696453-line-14">
     <rect x="0" y="343.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-15">
+<clipPath id="terminal-2926696453-line-15">
     <rect x="0" y="367.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-16">
+<clipPath id="terminal-2926696453-line-16">
     <rect x="0" y="391.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-17">
+<clipPath id="terminal-2926696453-line-17">
     <rect x="0" y="416.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-18">
+<clipPath id="terminal-2926696453-line-18">
     <rect x="0" y="440.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-19">
+<clipPath id="terminal-2926696453-line-19">
     <rect x="0" y="465.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-20">
+<clipPath id="terminal-2926696453-line-20">
     <rect x="0" y="489.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-21">
+<clipPath id="terminal-2926696453-line-21">
     <rect x="0" y="513.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-22">
+<clipPath id="terminal-2926696453-line-22">
     <rect x="0" y="538.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-23">
+<clipPath id="terminal-2926696453-line-23">
     <rect x="0" y="562.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-24">
+<clipPath id="terminal-2926696453-line-24">
     <rect x="0" y="587.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-25">
+<clipPath id="terminal-2926696453-line-25">
     <rect x="0" y="611.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-26">
+<clipPath id="terminal-2926696453-line-26">
     <rect x="0" y="635.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-27">
+<clipPath id="terminal-2926696453-line-27">
     <rect x="0" y="660.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-28">
+<clipPath id="terminal-2926696453-line-28">
     <rect x="0" y="684.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-29">
+<clipPath id="terminal-2926696453-line-29">
     <rect x="0" y="709.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-30">
+<clipPath id="terminal-2926696453-line-30">
     <rect x="0" y="733.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-31">
+<clipPath id="terminal-2926696453-line-31">
     <rect x="0" y="757.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-32">
+<clipPath id="terminal-2926696453-line-32">
     <rect x="0" y="782.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-33">
+<clipPath id="terminal-2926696453-line-33">
     <rect x="0" y="806.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-34">
+<clipPath id="terminal-2926696453-line-34">
     <rect x="0" y="831.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-35">
+<clipPath id="terminal-2926696453-line-35">
     <rect x="0" y="855.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-36">
+<clipPath id="terminal-2926696453-line-36">
     <rect x="0" y="879.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-37">
+<clipPath id="terminal-2926696453-line-37">
     <rect x="0" y="904.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-38">
+<clipPath id="terminal-2926696453-line-38">
     <rect x="0" y="928.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-1106739011-line-39">
+<clipPath id="terminal-2926696453-line-39">
     <rect x="0" y="953.1" width="976" height="24.65"/>
             </clipPath>
+<clipPath id="terminal-2926696453-line-40">
+    <rect x="0" y="977.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-41">
+    <rect x="0" y="1001.9" width="976" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="1048.4" rx="8"/>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="1097.2" rx="8"/>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
             <circle cx="44" cy="0" r="7" fill="#28c840"/>
             </g>
         
-    <g transform="translate(9, 41)" clip-path="url(#terminal-1106739011-clip-terminal)">
+    <g transform="translate(9, 41)" clip-path="url(#terminal-2926696453-clip-terminal)">
     
-    <g class="terminal-1106739011-matrix">
-    <text class="terminal-1106739011-r1" x="0" y="20" textLength="256.2" clip-path="url(#terminal-1106739011-line-0)">$&#160;cz&#160;changelog&#160;--help</text><text class="terminal-1106739011-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1106739011-line-0)">
-</text><text class="terminal-1106739011-r1" x="0" y="44.4" textLength="244" clip-path="url(#terminal-1106739011-line-1)">usage:&#160;cz&#160;changelog&#160;</text><text class="terminal-1106739011-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">[</text><text class="terminal-1106739011-r1" x="256.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1106739011-line-1)">-h</text><text class="terminal-1106739011-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">]</text><text class="terminal-1106739011-r2" x="305" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">[</text><text class="terminal-1106739011-r1" x="317.2" y="44.4" textLength="109.8" clip-path="url(#terminal-1106739011-line-1)">--dry-run</text><text class="terminal-1106739011-r2" x="427" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">]</text><text class="terminal-1106739011-r2" x="451.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">[</text><text class="terminal-1106739011-r1" x="463.6" y="44.4" textLength="256.2" clip-path="url(#terminal-1106739011-line-1)">--file-name&#160;FILE_NAME</text><text class="terminal-1106739011-r2" x="719.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">]</text><text class="terminal-1106739011-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">
-</text><text class="terminal-1106739011-r2" x="244" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">[</text><text class="terminal-1106739011-r1" x="256.2" y="68.8" textLength="475.8" clip-path="url(#terminal-1106739011-line-2)">--unreleased-version&#160;UNRELEASED_VERSION</text><text class="terminal-1106739011-r2" x="732" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">]</text><text class="terminal-1106739011-r2" x="756.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">[</text><text class="terminal-1106739011-r1" x="768.6" y="68.8" textLength="158.6" clip-path="url(#terminal-1106739011-line-2)">--incremental</text><text class="terminal-1106739011-r2" x="927.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">]</text><text class="terminal-1106739011-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">
-</text><text class="terminal-1106739011-r2" x="244" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">[</text><text class="terminal-1106739011-r1" x="256.2" y="93.2" textLength="256.2" clip-path="url(#terminal-1106739011-line-3)">--start-rev&#160;START_REV</text><text class="terminal-1106739011-r2" x="512.4" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">]</text><text class="terminal-1106739011-r2" x="536.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">[</text><text class="terminal-1106739011-r1" x="549" y="93.2" textLength="219.6" clip-path="url(#terminal-1106739011-line-3)">--merge-prerelease</text><text class="terminal-1106739011-r2" x="768.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">]</text><text class="terminal-1106739011-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">
-</text><text class="terminal-1106739011-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">[</text><text class="terminal-1106739011-r1" x="256.2" y="117.6" textLength="207.4" clip-path="url(#terminal-1106739011-line-4)">--version-scheme&#160;</text><text class="terminal-1106739011-r2" x="463.6" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">{</text><text class="terminal-1106739011-r1" x="475.8" y="117.6" textLength="256.2" clip-path="url(#terminal-1106739011-line-4)">pep440,semver,semver2</text><text class="terminal-1106739011-r2" x="732" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">}</text><text class="terminal-1106739011-r2" x="744.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">]</text><text class="terminal-1106739011-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">
-</text><text class="terminal-1106739011-r2" x="244" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">[</text><text class="terminal-1106739011-r1" x="256.2" y="142" textLength="402.6" clip-path="url(#terminal-1106739011-line-5)">--export-template&#160;EXPORT_TEMPLATE</text><text class="terminal-1106739011-r2" x="658.8" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">]</text><text class="terminal-1106739011-r2" x="683.2" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">[</text><text class="terminal-1106739011-r1" x="695.4" y="142" textLength="231.8" clip-path="url(#terminal-1106739011-line-5)">--template&#160;TEMPLATE</text><text class="terminal-1106739011-r2" x="927.2" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">]</text><text class="terminal-1106739011-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">
-</text><text class="terminal-1106739011-r2" x="244" y="166.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-6)">[</text><text class="terminal-1106739011-r1" x="256.2" y="166.4" textLength="158.6" clip-path="url(#terminal-1106739011-line-6)">--extra&#160;EXTRA</text><text class="terminal-1106739011-r2" x="414.8" y="166.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-6)">]</text><text class="terminal-1106739011-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-6)">
-</text><text class="terminal-1106739011-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-7)">
-</text><text class="terminal-1106739011-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-8)">
-</text><text class="terminal-1106739011-r1" x="0" y="239.6" textLength="231.8" clip-path="url(#terminal-1106739011-line-9)">generate&#160;changelog&#160;</text><text class="terminal-1106739011-r2" x="231.8" y="239.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-9)">(</text><text class="terminal-1106739011-r1" x="244" y="239.6" textLength="500.2" clip-path="url(#terminal-1106739011-line-9)">note&#160;that&#160;it&#160;will&#160;overwrite&#160;existing&#160;file</text><text class="terminal-1106739011-r2" x="744.2" y="239.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-9)">)</text><text class="terminal-1106739011-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-9)">
-</text><text class="terminal-1106739011-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1106739011-line-10)">
-</text><text class="terminal-1106739011-r1" x="0" y="288.4" textLength="256.2" clip-path="url(#terminal-1106739011-line-11)">positional&#160;arguments:</text><text class="terminal-1106739011-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-11)">
-</text><text class="terminal-1106739011-r1" x="0" y="312.8" textLength="805.2" clip-path="url(#terminal-1106739011-line-12)">&#160;&#160;rev_range&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generates&#160;changelog&#160;for&#160;the&#160;given&#160;version&#160;</text><text class="terminal-1106739011-r2" x="805.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">(</text><text class="terminal-1106739011-r1" x="817.4" y="312.8" textLength="61" clip-path="url(#terminal-1106739011-line-12)">e.g:&#160;</text><text class="terminal-1106739011-r3" x="878.4" y="312.8" textLength="36.6" clip-path="url(#terminal-1106739011-line-12)">1.5</text><text class="terminal-1106739011-r1" x="915" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">.</text><text class="terminal-1106739011-r3" x="927.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">3</text><text class="terminal-1106739011-r2" x="939.4" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">)</text><text class="terminal-1106739011-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">
-</text><text class="terminal-1106739011-r1" x="0" y="337.2" textLength="500.2" clip-path="url(#terminal-1106739011-line-13)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;or&#160;version&#160;range&#160;</text><text class="terminal-1106739011-r2" x="500.2" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">(</text><text class="terminal-1106739011-r1" x="512.4" y="337.2" textLength="61" clip-path="url(#terminal-1106739011-line-13)">e.g:&#160;</text><text class="terminal-1106739011-r3" x="573.4" y="337.2" textLength="36.6" clip-path="url(#terminal-1106739011-line-13)">1.5</text><text class="terminal-1106739011-r1" x="610" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">.</text><text class="terminal-1106739011-r3" x="622.2" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">3</text><text class="terminal-1106739011-r1" x="634.4" y="337.2" textLength="24.4" clip-path="url(#terminal-1106739011-line-13)">..</text><text class="terminal-1106739011-r3" x="658.8" y="337.2" textLength="36.6" clip-path="url(#terminal-1106739011-line-13)">1.7</text><text class="terminal-1106739011-r1" x="695.4" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">.</text><text class="terminal-1106739011-r3" x="707.6" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">9</text><text class="terminal-1106739011-r2" x="719.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">)</text><text class="terminal-1106739011-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">
-</text><text class="terminal-1106739011-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-14)">
-</text><text class="terminal-1106739011-r1" x="0" y="386" textLength="97.6" clip-path="url(#terminal-1106739011-line-15)">options:</text><text class="terminal-1106739011-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1106739011-line-15)">
-</text><text class="terminal-1106739011-r1" x="0" y="410.4" textLength="671" clip-path="url(#terminal-1106739011-line-16)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1106739011-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-16)">
-</text><text class="terminal-1106739011-r1" x="0" y="434.8" textLength="585.6" clip-path="url(#terminal-1106739011-line-17)">&#160;&#160;--dry-run&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;changelog&#160;to&#160;stdout</text><text class="terminal-1106739011-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-17)">
-</text><text class="terminal-1106739011-r1" x="0" y="459.2" textLength="280.6" clip-path="url(#terminal-1106739011-line-18)">&#160;&#160;--file-name&#160;FILE_NAME</text><text class="terminal-1106739011-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-18)">
-</text><text class="terminal-1106739011-r1" x="0" y="483.6" textLength="573.4" clip-path="url(#terminal-1106739011-line-19)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;file&#160;name&#160;of&#160;changelog&#160;</text><text class="terminal-1106739011-r2" x="573.4" y="483.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-19)">(</text><text class="terminal-1106739011-r1" x="585.6" y="483.6" textLength="109.8" clip-path="url(#terminal-1106739011-line-19)">default:&#160;</text><text class="terminal-1106739011-r4" x="695.4" y="483.6" textLength="170.8" clip-path="url(#terminal-1106739011-line-19)">&#x27;CHANGELOG.md&#x27;</text><text class="terminal-1106739011-r2" x="866.2" y="483.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-19)">)</text><text class="terminal-1106739011-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-19)">
-</text><text class="terminal-1106739011-r1" x="0" y="508" textLength="500.2" clip-path="url(#terminal-1106739011-line-20)">&#160;&#160;--unreleased-version&#160;UNRELEASED_VERSION</text><text class="terminal-1106739011-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1106739011-line-20)">
-</text><text class="terminal-1106739011-r1" x="0" y="532.4" textLength="707.6" clip-path="url(#terminal-1106739011-line-21)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;set&#160;the&#160;value&#160;for&#160;the&#160;new&#160;version&#160;</text><text class="terminal-1106739011-r2" x="707.6" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)">(</text><text class="terminal-1106739011-r1" x="719.8" y="532.4" textLength="207.4" clip-path="url(#terminal-1106739011-line-21)">use&#160;the&#160;tag&#160;value</text><text class="terminal-1106739011-r2" x="927.2" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)">)</text><text class="terminal-1106739011-r1" x="939.4" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)">,</text><text class="terminal-1106739011-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)">
-</text><text class="terminal-1106739011-r1" x="0" y="556.8" textLength="622.2" clip-path="url(#terminal-1106739011-line-22)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;instead&#160;of&#160;using&#160;unreleased</text><text class="terminal-1106739011-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-22)">
-</text><text class="terminal-1106739011-r1" x="0" y="581.2" textLength="939.4" clip-path="url(#terminal-1106739011-line-23)">&#160;&#160;--incremental&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generates&#160;changelog&#160;from&#160;last&#160;created&#160;version,&#160;useful</text><text class="terminal-1106739011-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-23)">
-</text><text class="terminal-1106739011-r1" x="0" y="605.6" textLength="817.4" clip-path="url(#terminal-1106739011-line-24)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if&#160;the&#160;changelog&#160;has&#160;been&#160;manually&#160;modified</text><text class="terminal-1106739011-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-24)">
-</text><text class="terminal-1106739011-r1" x="0" y="630" textLength="280.6" clip-path="url(#terminal-1106739011-line-25)">&#160;&#160;--start-rev&#160;START_REV</text><text class="terminal-1106739011-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1106739011-line-25)">
-</text><text class="terminal-1106739011-r1" x="0" y="654.4" textLength="866.2" clip-path="url(#terminal-1106739011-line-26)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;start&#160;rev&#160;of&#160;the&#160;changelog.&#160;If&#160;not&#160;set,&#160;it&#160;will</text><text class="terminal-1106739011-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-26)">
-</text><text class="terminal-1106739011-r1" x="0" y="678.8" textLength="695.4" clip-path="url(#terminal-1106739011-line-27)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;changelog&#160;from&#160;the&#160;start</text><text class="terminal-1106739011-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-27)">
-</text><text class="terminal-1106739011-r1" x="0" y="703.2" textLength="915" clip-path="url(#terminal-1106739011-line-28)">&#160;&#160;--merge-prerelease&#160;&#160;&#160;&#160;collect&#160;all&#160;changes&#160;from&#160;prereleases&#160;into&#160;next&#160;non-</text><text class="terminal-1106739011-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-28)">
-</text><text class="terminal-1106739011-r1" x="0" y="727.6" textLength="951.6" clip-path="url(#terminal-1106739011-line-29)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;prerelease.&#160;If&#160;not&#160;set,&#160;it&#160;will&#160;include&#160;prereleases&#160;in</text><text class="terminal-1106739011-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-29)">
-</text><text class="terminal-1106739011-r1" x="0" y="752" textLength="451.4" clip-path="url(#terminal-1106739011-line-30)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;changelog</text><text class="terminal-1106739011-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-1106739011-line-30)">
-</text><text class="terminal-1106739011-r1" x="0" y="776.4" textLength="231.8" clip-path="url(#terminal-1106739011-line-31)">&#160;&#160;--version-scheme&#160;</text><text class="terminal-1106739011-r2" x="231.8" y="776.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-31)">{</text><text class="terminal-1106739011-r1" x="244" y="776.4" textLength="256.2" clip-path="url(#terminal-1106739011-line-31)">pep440,semver,semver2</text><text class="terminal-1106739011-r2" x="500.2" y="776.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-31)">}</text><text class="terminal-1106739011-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-31)">
-</text><text class="terminal-1106739011-r1" x="0" y="800.8" textLength="549" clip-path="url(#terminal-1106739011-line-32)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;choose&#160;version&#160;scheme</text><text class="terminal-1106739011-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-32)">
-</text><text class="terminal-1106739011-r1" x="0" y="825.2" textLength="427" clip-path="url(#terminal-1106739011-line-33)">&#160;&#160;--export-template&#160;EXPORT_TEMPLATE</text><text class="terminal-1106739011-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-33)">
-</text><text class="terminal-1106739011-r1" x="0" y="849.6" textLength="927.2" clip-path="url(#terminal-1106739011-line-34)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Export&#160;the&#160;changelog&#160;template&#160;into&#160;this&#160;file&#160;instead</text><text class="terminal-1106739011-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-34)">
-</text><text class="terminal-1106739011-r1" x="0" y="874" textLength="475.8" clip-path="url(#terminal-1106739011-line-35)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;of&#160;rendering&#160;it</text><text class="terminal-1106739011-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-1106739011-line-35)">
-</text><text class="terminal-1106739011-r1" x="0" y="898.4" textLength="305" clip-path="url(#terminal-1106739011-line-36)">&#160;&#160;--template,&#160;-t&#160;TEMPLATE</text><text class="terminal-1106739011-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-36)">
-</text><text class="terminal-1106739011-r1" x="0" y="922.8" textLength="646.6" clip-path="url(#terminal-1106739011-line-37)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;changelog&#160;template&#160;file&#160;name&#160;</text><text class="terminal-1106739011-r2" x="646.6" y="922.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-37)">(</text><text class="terminal-1106739011-r1" x="658.8" y="922.8" textLength="280.6" clip-path="url(#terminal-1106739011-line-37)">relative&#160;to&#160;the&#160;current</text><text class="terminal-1106739011-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-37)">
-</text><text class="terminal-1106739011-r1" x="0" y="947.2" textLength="500.2" clip-path="url(#terminal-1106739011-line-38)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;working&#160;directory</text><text class="terminal-1106739011-r2" x="500.2" y="947.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-38)">)</text><text class="terminal-1106739011-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-38)">
-</text><text class="terminal-1106739011-r1" x="0" y="971.6" textLength="622.2" clip-path="url(#terminal-1106739011-line-39)">&#160;&#160;--extra,&#160;-e&#160;EXTRA&#160;&#160;&#160;&#160;&#160;a&#160;changelog&#160;extra&#160;variable&#160;</text><text class="terminal-1106739011-r2" x="622.2" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">(</text><text class="terminal-1106739011-r1" x="634.4" y="971.6" textLength="146.4" clip-path="url(#terminal-1106739011-line-39)">in&#160;the&#160;form&#160;</text><text class="terminal-1106739011-r4" x="780.8" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">&#x27;</text><text class="terminal-1106739011-r4" x="793" y="971.6" textLength="36.6" clip-path="url(#terminal-1106739011-line-39)">key</text><text class="terminal-1106739011-r4" x="829.6" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">=</text><text class="terminal-1106739011-r4" x="841.8" y="971.6" textLength="61" clip-path="url(#terminal-1106739011-line-39)">value</text><text class="terminal-1106739011-r4" x="902.8" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">&#x27;</text><text class="terminal-1106739011-r2" x="915" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">)</text><text class="terminal-1106739011-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">
-</text><text class="terminal-1106739011-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-1106739011-line-40)">
+    <g class="terminal-2926696453-matrix">
+    <text class="terminal-2926696453-r1" x="0" y="20" textLength="256.2" clip-path="url(#terminal-2926696453-line-0)">$&#160;cz&#160;changelog&#160;--help</text><text class="terminal-2926696453-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2926696453-line-0)">
+</text><text class="terminal-2926696453-r1" x="0" y="44.4" textLength="244" clip-path="url(#terminal-2926696453-line-1)">usage:&#160;cz&#160;changelog&#160;</text><text class="terminal-2926696453-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="256.2" y="44.4" textLength="24.4" clip-path="url(#terminal-2926696453-line-1)">-h</text><text class="terminal-2926696453-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r2" x="305" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="317.2" y="44.4" textLength="109.8" clip-path="url(#terminal-2926696453-line-1)">--dry-run</text><text class="terminal-2926696453-r2" x="427" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r2" x="451.4" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="463.6" y="44.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-1)">--file-name&#160;FILE_NAME</text><text class="terminal-2926696453-r2" x="719.8" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">
+</text><text class="terminal-2926696453-r2" x="244" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">[</text><text class="terminal-2926696453-r1" x="256.2" y="68.8" textLength="475.8" clip-path="url(#terminal-2926696453-line-2)">--unreleased-version&#160;UNRELEASED_VERSION</text><text class="terminal-2926696453-r2" x="732" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">]</text><text class="terminal-2926696453-r2" x="756.4" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">[</text><text class="terminal-2926696453-r1" x="768.6" y="68.8" textLength="158.6" clip-path="url(#terminal-2926696453-line-2)">--incremental</text><text class="terminal-2926696453-r2" x="927.2" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">]</text><text class="terminal-2926696453-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">
+</text><text class="terminal-2926696453-r2" x="244" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">[</text><text class="terminal-2926696453-r1" x="256.2" y="93.2" textLength="256.2" clip-path="url(#terminal-2926696453-line-3)">--start-rev&#160;START_REV</text><text class="terminal-2926696453-r2" x="512.4" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">]</text><text class="terminal-2926696453-r2" x="536.8" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">[</text><text class="terminal-2926696453-r1" x="549" y="93.2" textLength="219.6" clip-path="url(#terminal-2926696453-line-3)">--merge-prerelease</text><text class="terminal-2926696453-r2" x="768.6" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">]</text><text class="terminal-2926696453-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">
+</text><text class="terminal-2926696453-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">[</text><text class="terminal-2926696453-r1" x="256.2" y="117.6" textLength="207.4" clip-path="url(#terminal-2926696453-line-4)">--version-scheme&#160;</text><text class="terminal-2926696453-r2" x="463.6" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">{</text><text class="terminal-2926696453-r1" x="475.8" y="117.6" textLength="256.2" clip-path="url(#terminal-2926696453-line-4)">pep440,semver,semver2</text><text class="terminal-2926696453-r2" x="732" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">}</text><text class="terminal-2926696453-r2" x="744.2" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">]</text><text class="terminal-2926696453-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">
+</text><text class="terminal-2926696453-r2" x="244" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">[</text><text class="terminal-2926696453-r1" x="256.2" y="142" textLength="402.6" clip-path="url(#terminal-2926696453-line-5)">--export-template&#160;EXPORT_TEMPLATE</text><text class="terminal-2926696453-r2" x="658.8" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">]</text><text class="terminal-2926696453-r2" x="683.2" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">[</text><text class="terminal-2926696453-r1" x="695.4" y="142" textLength="231.8" clip-path="url(#terminal-2926696453-line-5)">--template&#160;TEMPLATE</text><text class="terminal-2926696453-r2" x="927.2" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">]</text><text class="terminal-2926696453-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">
+</text><text class="terminal-2926696453-r2" x="244" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">[</text><text class="terminal-2926696453-r1" x="256.2" y="166.4" textLength="158.6" clip-path="url(#terminal-2926696453-line-6)">--extra&#160;EXTRA</text><text class="terminal-2926696453-r2" x="414.8" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">]</text><text class="terminal-2926696453-r2" x="439.2" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">[</text><text class="terminal-2926696453-r1" x="451.4" y="166.4" textLength="280.6" clip-path="url(#terminal-2926696453-line-6)">--tag-format&#160;TAG_FORMAT</text><text class="terminal-2926696453-r2" x="732" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">]</text><text class="terminal-2926696453-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">
+</text><text class="terminal-2926696453-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-7)">
+</text><text class="terminal-2926696453-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-8)">
+</text><text class="terminal-2926696453-r1" x="0" y="239.6" textLength="231.8" clip-path="url(#terminal-2926696453-line-9)">generate&#160;changelog&#160;</text><text class="terminal-2926696453-r2" x="231.8" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">(</text><text class="terminal-2926696453-r1" x="244" y="239.6" textLength="500.2" clip-path="url(#terminal-2926696453-line-9)">note&#160;that&#160;it&#160;will&#160;overwrite&#160;existing&#160;file</text><text class="terminal-2926696453-r2" x="744.2" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">)</text><text class="terminal-2926696453-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">
+</text><text class="terminal-2926696453-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2926696453-line-10)">
+</text><text class="terminal-2926696453-r1" x="0" y="288.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-11)">positional&#160;arguments:</text><text class="terminal-2926696453-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-11)">
+</text><text class="terminal-2926696453-r1" x="0" y="312.8" textLength="805.2" clip-path="url(#terminal-2926696453-line-12)">&#160;&#160;rev_range&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generates&#160;changelog&#160;for&#160;the&#160;given&#160;version&#160;</text><text class="terminal-2926696453-r2" x="805.2" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">(</text><text class="terminal-2926696453-r1" x="817.4" y="312.8" textLength="61" clip-path="url(#terminal-2926696453-line-12)">e.g:&#160;</text><text class="terminal-2926696453-r3" x="878.4" y="312.8" textLength="36.6" clip-path="url(#terminal-2926696453-line-12)">1.5</text><text class="terminal-2926696453-r1" x="915" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">.</text><text class="terminal-2926696453-r3" x="927.2" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">3</text><text class="terminal-2926696453-r2" x="939.4" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">)</text><text class="terminal-2926696453-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">
+</text><text class="terminal-2926696453-r1" x="0" y="337.2" textLength="500.2" clip-path="url(#terminal-2926696453-line-13)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;or&#160;version&#160;range&#160;</text><text class="terminal-2926696453-r2" x="500.2" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">(</text><text class="terminal-2926696453-r1" x="512.4" y="337.2" textLength="61" clip-path="url(#terminal-2926696453-line-13)">e.g:&#160;</text><text class="terminal-2926696453-r3" x="573.4" y="337.2" textLength="36.6" clip-path="url(#terminal-2926696453-line-13)">1.5</text><text class="terminal-2926696453-r1" x="610" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">.</text><text class="terminal-2926696453-r3" x="622.2" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">3</text><text class="terminal-2926696453-r1" x="634.4" y="337.2" textLength="24.4" clip-path="url(#terminal-2926696453-line-13)">..</text><text class="terminal-2926696453-r3" x="658.8" y="337.2" textLength="36.6" clip-path="url(#terminal-2926696453-line-13)">1.7</text><text class="terminal-2926696453-r1" x="695.4" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">.</text><text class="terminal-2926696453-r3" x="707.6" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">9</text><text class="terminal-2926696453-r2" x="719.8" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">)</text><text class="terminal-2926696453-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">
+</text><text class="terminal-2926696453-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-14)">
+</text><text class="terminal-2926696453-r1" x="0" y="386" textLength="97.6" clip-path="url(#terminal-2926696453-line-15)">options:</text><text class="terminal-2926696453-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2926696453-line-15)">
+</text><text class="terminal-2926696453-r1" x="0" y="410.4" textLength="671" clip-path="url(#terminal-2926696453-line-16)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-2926696453-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-16)">
+</text><text class="terminal-2926696453-r1" x="0" y="434.8" textLength="585.6" clip-path="url(#terminal-2926696453-line-17)">&#160;&#160;--dry-run&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;changelog&#160;to&#160;stdout</text><text class="terminal-2926696453-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-17)">
+</text><text class="terminal-2926696453-r1" x="0" y="459.2" textLength="280.6" clip-path="url(#terminal-2926696453-line-18)">&#160;&#160;--file-name&#160;FILE_NAME</text><text class="terminal-2926696453-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-18)">
+</text><text class="terminal-2926696453-r1" x="0" y="483.6" textLength="573.4" clip-path="url(#terminal-2926696453-line-19)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;file&#160;name&#160;of&#160;changelog&#160;</text><text class="terminal-2926696453-r2" x="573.4" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">(</text><text class="terminal-2926696453-r1" x="585.6" y="483.6" textLength="109.8" clip-path="url(#terminal-2926696453-line-19)">default:&#160;</text><text class="terminal-2926696453-r4" x="695.4" y="483.6" textLength="170.8" clip-path="url(#terminal-2926696453-line-19)">&#x27;CHANGELOG.md&#x27;</text><text class="terminal-2926696453-r2" x="866.2" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">)</text><text class="terminal-2926696453-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">
+</text><text class="terminal-2926696453-r1" x="0" y="508" textLength="500.2" clip-path="url(#terminal-2926696453-line-20)">&#160;&#160;--unreleased-version&#160;UNRELEASED_VERSION</text><text class="terminal-2926696453-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2926696453-line-20)">
+</text><text class="terminal-2926696453-r1" x="0" y="532.4" textLength="707.6" clip-path="url(#terminal-2926696453-line-21)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;set&#160;the&#160;value&#160;for&#160;the&#160;new&#160;version&#160;</text><text class="terminal-2926696453-r2" x="707.6" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">(</text><text class="terminal-2926696453-r1" x="719.8" y="532.4" textLength="207.4" clip-path="url(#terminal-2926696453-line-21)">use&#160;the&#160;tag&#160;value</text><text class="terminal-2926696453-r2" x="927.2" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">)</text><text class="terminal-2926696453-r1" x="939.4" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">,</text><text class="terminal-2926696453-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">
+</text><text class="terminal-2926696453-r1" x="0" y="556.8" textLength="622.2" clip-path="url(#terminal-2926696453-line-22)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;instead&#160;of&#160;using&#160;unreleased</text><text class="terminal-2926696453-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-22)">
+</text><text class="terminal-2926696453-r1" x="0" y="581.2" textLength="939.4" clip-path="url(#terminal-2926696453-line-23)">&#160;&#160;--incremental&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generates&#160;changelog&#160;from&#160;last&#160;created&#160;version,&#160;useful</text><text class="terminal-2926696453-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-23)">
+</text><text class="terminal-2926696453-r1" x="0" y="605.6" textLength="817.4" clip-path="url(#terminal-2926696453-line-24)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if&#160;the&#160;changelog&#160;has&#160;been&#160;manually&#160;modified</text><text class="terminal-2926696453-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-24)">
+</text><text class="terminal-2926696453-r1" x="0" y="630" textLength="280.6" clip-path="url(#terminal-2926696453-line-25)">&#160;&#160;--start-rev&#160;START_REV</text><text class="terminal-2926696453-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-2926696453-line-25)">
+</text><text class="terminal-2926696453-r1" x="0" y="654.4" textLength="866.2" clip-path="url(#terminal-2926696453-line-26)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;start&#160;rev&#160;of&#160;the&#160;changelog.&#160;If&#160;not&#160;set,&#160;it&#160;will</text><text class="terminal-2926696453-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-26)">
+</text><text class="terminal-2926696453-r1" x="0" y="678.8" textLength="695.4" clip-path="url(#terminal-2926696453-line-27)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;changelog&#160;from&#160;the&#160;start</text><text class="terminal-2926696453-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-27)">
+</text><text class="terminal-2926696453-r1" x="0" y="703.2" textLength="915" clip-path="url(#terminal-2926696453-line-28)">&#160;&#160;--merge-prerelease&#160;&#160;&#160;&#160;collect&#160;all&#160;changes&#160;from&#160;prereleases&#160;into&#160;next&#160;non-</text><text class="terminal-2926696453-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-28)">
+</text><text class="terminal-2926696453-r1" x="0" y="727.6" textLength="951.6" clip-path="url(#terminal-2926696453-line-29)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;prerelease.&#160;If&#160;not&#160;set,&#160;it&#160;will&#160;include&#160;prereleases&#160;in</text><text class="terminal-2926696453-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-29)">
+</text><text class="terminal-2926696453-r1" x="0" y="752" textLength="451.4" clip-path="url(#terminal-2926696453-line-30)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;changelog</text><text class="terminal-2926696453-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-2926696453-line-30)">
+</text><text class="terminal-2926696453-r1" x="0" y="776.4" textLength="231.8" clip-path="url(#terminal-2926696453-line-31)">&#160;&#160;--version-scheme&#160;</text><text class="terminal-2926696453-r2" x="231.8" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">{</text><text class="terminal-2926696453-r1" x="244" y="776.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-31)">pep440,semver,semver2</text><text class="terminal-2926696453-r2" x="500.2" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">}</text><text class="terminal-2926696453-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">
+</text><text class="terminal-2926696453-r1" x="0" y="800.8" textLength="549" clip-path="url(#terminal-2926696453-line-32)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;choose&#160;version&#160;scheme</text><text class="terminal-2926696453-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-32)">
+</text><text class="terminal-2926696453-r1" x="0" y="825.2" textLength="427" clip-path="url(#terminal-2926696453-line-33)">&#160;&#160;--export-template&#160;EXPORT_TEMPLATE</text><text class="terminal-2926696453-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-33)">
+</text><text class="terminal-2926696453-r1" x="0" y="849.6" textLength="927.2" clip-path="url(#terminal-2926696453-line-34)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Export&#160;the&#160;changelog&#160;template&#160;into&#160;this&#160;file&#160;instead</text><text class="terminal-2926696453-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-34)">
+</text><text class="terminal-2926696453-r1" x="0" y="874" textLength="475.8" clip-path="url(#terminal-2926696453-line-35)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;of&#160;rendering&#160;it</text><text class="terminal-2926696453-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-2926696453-line-35)">
+</text><text class="terminal-2926696453-r1" x="0" y="898.4" textLength="305" clip-path="url(#terminal-2926696453-line-36)">&#160;&#160;--template,&#160;-t&#160;TEMPLATE</text><text class="terminal-2926696453-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-36)">
+</text><text class="terminal-2926696453-r1" x="0" y="922.8" textLength="646.6" clip-path="url(#terminal-2926696453-line-37)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;changelog&#160;template&#160;file&#160;name&#160;</text><text class="terminal-2926696453-r2" x="646.6" y="922.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-37)">(</text><text class="terminal-2926696453-r1" x="658.8" y="922.8" textLength="280.6" clip-path="url(#terminal-2926696453-line-37)">relative&#160;to&#160;the&#160;current</text><text class="terminal-2926696453-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-37)">
+</text><text class="terminal-2926696453-r1" x="0" y="947.2" textLength="500.2" clip-path="url(#terminal-2926696453-line-38)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;working&#160;directory</text><text class="terminal-2926696453-r2" x="500.2" y="947.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-38)">)</text><text class="terminal-2926696453-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-38)">
+</text><text class="terminal-2926696453-r1" x="0" y="971.6" textLength="622.2" clip-path="url(#terminal-2926696453-line-39)">&#160;&#160;--extra,&#160;-e&#160;EXTRA&#160;&#160;&#160;&#160;&#160;a&#160;changelog&#160;extra&#160;variable&#160;</text><text class="terminal-2926696453-r2" x="622.2" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">(</text><text class="terminal-2926696453-r1" x="634.4" y="971.6" textLength="146.4" clip-path="url(#terminal-2926696453-line-39)">in&#160;the&#160;form&#160;</text><text class="terminal-2926696453-r4" x="780.8" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">&#x27;</text><text class="terminal-2926696453-r4" x="793" y="971.6" textLength="36.6" clip-path="url(#terminal-2926696453-line-39)">key</text><text class="terminal-2926696453-r4" x="829.6" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">=</text><text class="terminal-2926696453-r4" x="841.8" y="971.6" textLength="61" clip-path="url(#terminal-2926696453-line-39)">value</text><text class="terminal-2926696453-r4" x="902.8" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">&#x27;</text><text class="terminal-2926696453-r2" x="915" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">)</text><text class="terminal-2926696453-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">
+</text><text class="terminal-2926696453-r1" x="0" y="996" textLength="305" clip-path="url(#terminal-2926696453-line-40)">&#160;&#160;--tag-format&#160;TAG_FORMAT</text><text class="terminal-2926696453-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-2926696453-line-40)">
+</text><text class="terminal-2926696453-r1" x="0" y="1020.4" textLength="878.4" clip-path="url(#terminal-2926696453-line-41)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;The&#160;format&#160;of&#160;the&#160;tag,&#160;wrap&#160;around&#160;simple&#160;quotes</text><text class="terminal-2926696453-r1" x="976" y="1020.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-41)">
+</text><text class="terminal-2926696453-r1" x="976" y="1044.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-42)">
 </text>
     </g>
     </g>

From 225fd76ecbd80fd81471886047d3b60b9710bbf9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 20 May 2025 15:03:39 +0000
Subject: [PATCH 046/169] =?UTF-8?q?bump:=20version=204.7.2=20=E2=86=92=204?=
 =?UTF-8?q?.8.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 6 ++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7497946571..ff222cac1a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.7.2 # automatically updated by Commitizen
+    rev: v4.8.0 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de772c6a75..d11930cce2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## v4.8.0 (2025-05-20)
+
+### Feat
+
+- **cli**: add --tag-format argument to changelog command
+
 ## v4.7.2 (2025-05-18)
 
 ### Refactor
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 98c926d162..ea674c5c54 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.7.2"
+__version__ = "4.8.0"
diff --git a/pyproject.toml b/pyproject.toml
index 8d9214615a..4e41a885fa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.7.2"
+version = "4.8.0"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.7.2"
+version = "4.8.0"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 10581b3b4399abb2972d932d22d9b4b55b433a3b Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 16 May 2025 02:18:37 +0800
Subject: [PATCH 047/169] test(changelog): code cleanup and better type

---
 tests/test_changelog.py | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index 067adb4a91..b1c7c802e1 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -556,8 +556,8 @@
 
 
 @pytest.fixture
-def gitcommits() -> list:
-    commits = [
+def gitcommits() -> list[git.GitCommit]:
+    return [
         git.GitCommit(
             commit["rev"],
             commit["title"],
@@ -568,13 +568,11 @@ def gitcommits() -> list:
         )
         for commit in COMMITS_DATA
     ]
-    return commits
 
 
 @pytest.fixture
-def tags() -> list:
-    tags = [git.GitTag(*tag) for tag in TAGS]
-    return tags
+def tags() -> list[git.GitTag]:
+    return [git.GitTag(*tag) for tag in TAGS]
 
 
 @pytest.fixture

From d0c26742ef05f3976bf3e42486857612b17e83d4 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 14 May 2025 18:05:55 +0800
Subject: [PATCH 048/169] refactor(customize): improve code readability

---
 commitizen/cz/customize/customize.py | 54 ++++++++--------------------
 1 file changed, 15 insertions(+), 39 deletions(-)

diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py
index afa35e92de..53ada4b2c0 100644
--- a/commitizen/cz/customize/customize.py
+++ b/commitizen/cz/customize/customize.py
@@ -33,35 +33,17 @@ def __init__(self, config: BaseConfig):
             raise MissingCzCustomizeConfigError()
         self.custom_settings = self.config.settings["customize"]
 
-        custom_bump_pattern = self.custom_settings.get("bump_pattern")
-        if custom_bump_pattern:
-            self.bump_pattern = custom_bump_pattern
-
-        custom_bump_map = self.custom_settings.get("bump_map")
-        if custom_bump_map:
-            self.bump_map = custom_bump_map
-
-        custom_bump_map_major_version_zero = self.custom_settings.get(
-            "bump_map_major_version_zero"
-        )
-        if custom_bump_map_major_version_zero:
-            self.bump_map_major_version_zero = custom_bump_map_major_version_zero
-
-        custom_change_type_order = self.custom_settings.get("change_type_order")
-        if custom_change_type_order:
-            self.change_type_order = custom_change_type_order
-
-        commit_parser = self.custom_settings.get("commit_parser")
-        if commit_parser:
-            self.commit_parser = commit_parser
-
-        changelog_pattern = self.custom_settings.get("changelog_pattern")
-        if changelog_pattern:
-            self.changelog_pattern = changelog_pattern
-
-        change_type_map = self.custom_settings.get("change_type_map")
-        if change_type_map:
-            self.change_type_map = change_type_map
+        for attr_name in [
+            "bump_pattern",
+            "bump_map",
+            "bump_map_major_version_zero",
+            "change_type_order",
+            "commit_parser",
+            "changelog_pattern",
+            "change_type_map",
+        ]:
+            if value := self.custom_settings.get(attr_name):
+                setattr(self, attr_name, value)
 
     def questions(self) -> Questions:
         return self.custom_settings.get("questions", [{}])
@@ -70,8 +52,7 @@ def message(self, answers: dict) -> str:
         message_template = Template(self.custom_settings.get("message_template", ""))
         if getattr(Template, "substitute", None):
             return message_template.substitute(**answers)  # type: ignore
-        else:
-            return message_template.render(**answers)
+        return message_template.render(**answers)
 
     def example(self) -> str:
         return self.custom_settings.get("example") or ""
@@ -83,12 +64,7 @@ def schema(self) -> str:
         return self.custom_settings.get("schema") or ""
 
     def info(self) -> str:
-        info_path = self.custom_settings.get("info_path")
-        info = self.custom_settings.get("info")
-        if info_path:
+        if info_path := self.custom_settings.get("info_path"):
             with open(info_path, encoding=self.config.settings["encoding"]) as f:
-                content = f.read()
-            return content
-        elif info:
-            return info
-        return ""
+                return f.read()
+        return self.custom_settings.get("info") or ""

From ba571827e4bba1b5ac5fbdaf38302e53cd8efe75 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Thu, 22 May 2025 03:12:05 +0000
Subject: [PATCH 049/169] =?UTF-8?q?bump:=20version=204.8.0=20=E2=86=92=204?=
 =?UTF-8?q?.8.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 6 ++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ff222cac1a..f1aef91193 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.8.0 # automatically updated by Commitizen
+    rev: v4.8.1 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d11930cce2..9904ae3bbf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## v4.8.1 (2025-05-22)
+
+### Refactor
+
+- **customize**: improve code readability
+
 ## v4.8.0 (2025-05-20)
 
 ### Feat
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index ea674c5c54..1969a66ecd 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.8.0"
+__version__ = "4.8.1"
diff --git a/pyproject.toml b/pyproject.toml
index 4e41a885fa..853d9ccaf8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.8.0"
+version = "4.8.1"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.8.0"
+version = "4.8.1"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From fe72043eeec8ffdd34d721ada65c45c622acdac2 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 19:59:18 +0800
Subject: [PATCH 050/169] refactor(check): remove unnecessary variable

---
 commitizen/commands/check.py | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index 087db03ea5..a0d06cbc47 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -72,15 +72,11 @@ def __call__(self):
             raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'")
 
         pattern = self.cz.schema_pattern()
-        ill_formatted_commits = [
-            commit
-            for commit in commits
-            if not self.validate_commit_message(commit.message, pattern)
-        ]
         displayed_msgs_content = "\n".join(
             [
                 f'commit "{commit.rev}": "{commit.message}"'
-                for commit in ill_formatted_commits
+                for commit in commits
+                if not self.validate_commit_message(commit.message, pattern)
             ]
         )
         if displayed_msgs_content:

From 7fd6ce33d59f90f0a85826b54224aa063ddfdc70 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 20:05:39 +0800
Subject: [PATCH 051/169] refactor(check): simplify code

---
 commitizen/commands/check.py | 27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index a0d06cbc47..1e3b8464e1 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -88,24 +88,21 @@ def __call__(self):
             )
         out.success("Commit validation: successful!")
 
+    def _get_commit_message(self) -> str | None:
+        if self.commit_msg_file is None:
+            # Get commit message from command line (--message)
+            return self.commit_msg
+
+        with open(self.commit_msg_file, encoding=self.encoding) as commit_file:
+            # Get commit message from file (--commit-msg-file)
+            return commit_file.read()
+
     def _get_commits(self):
-        msg = None
-        # Get commit message from file (--commit-msg-file)
-        if self.commit_msg_file is not None:
-            # Enter this branch if commit_msg_file is "".
-            with open(self.commit_msg_file, encoding=self.encoding) as commit_file:
-                msg = commit_file.read()
-        # Get commit message from command line (--message)
-        elif self.commit_msg is not None:
-            msg = self.commit_msg
-        if msg is not None:
-            msg = self._filter_comments(msg)
-            return [git.GitCommit(rev="", title="", body=msg)]
+        if (msg := self._get_commit_message()) is not None:
+            return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))]
 
         # Get commit messages from git log (--rev-range)
-        if self.rev_range:
-            return git.get_commits(end=self.rev_range)
-        return git.get_commits()
+        return git.get_commits(end=self.rev_range or "HEAD")
 
     @staticmethod
     def _filter_comments(msg: str) -> str:

From a8094aebad266040ef07f118a96c88a93f4aecf8 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Thu, 22 May 2025 03:16:18 +0000
Subject: [PATCH 052/169] =?UTF-8?q?bump:=20version=204.8.1=20=E2=86=92=204?=
 =?UTF-8?q?.8.2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   | 2 +-
 CHANGELOG.md              | 7 +++++++
 commitizen/__version__.py | 2 +-
 pyproject.toml            | 4 ++--
 4 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f1aef91193..4e108eb9b9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.8.1 # automatically updated by Commitizen
+    rev: v4.8.2 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9904ae3bbf..5b737a851a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## v4.8.2 (2025-05-22)
+
+### Refactor
+
+- **check**: simplify code
+- **check**: remove unnecessary variable
+
 ## v4.8.1 (2025-05-22)
 
 ### Refactor
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 1969a66ecd..8e288a597c 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.8.1"
+__version__ = "4.8.2"
diff --git a/pyproject.toml b/pyproject.toml
index 853d9ccaf8..b306e5e78c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.8.1"
+version = "4.8.2"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.8.1"
+version = "4.8.2"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",

From 9420b44c86a9271d0d9f107360501a058533f9e2 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 23 May 2025 16:55:07 +0800
Subject: [PATCH 053/169] docs(label_pr.yml): add labels based on new PR title

---
 .github/workflows/label_pr.yml | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml
index b409c8b757..176d7cc794 100644
--- a/.github/workflows/label_pr.yml
+++ b/.github/workflows/label_pr.yml
@@ -17,3 +17,31 @@ jobs:
     - uses: actions/labeler@v5
       with:
         configuration-path: .github/labeler.yml
+    - name: Label based on PR title
+      uses: actions/github-script@v7
+      with:
+        script: |
+          const title = context.payload.pull_request.title.toLowerCase();
+          const labels = [];
+
+          if (title.includes("fix") || title.includes("bug")) {
+            labels.push("type: bug");
+          }
+          if (title.includes("feat")) {
+            labels.push("type: feature");
+          }
+          if (title.includes("doc")) {
+            labels.push("type: documentation");
+          }
+          if (title.includes("refactor")) {
+            labels.push("type: refactor");
+          }
+
+          if (labels.length > 0) {
+            await github.rest.issues.addLabels({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: context.payload.pull_request.number,
+              labels
+            });
+          }

From 5aa58c6638f7e2237f596ec373e9001e21ba1a8c Mon Sep 17 00:00:00 2001
From: Adrian Carolli <adrian.caarolli@gmail.com>
Date: Mon, 26 May 2025 19:45:10 -0400
Subject: [PATCH 054/169] docs: add installation and usage guide for cz-ai
 plugin leveraging GPT-4o

---
 docs/third-party-commitizen.md | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md
index 1c8ef8a183..b6d598985f 100644
--- a/docs/third-party-commitizen.md
+++ b/docs/third-party-commitizen.md
@@ -3,6 +3,22 @@
 In addition to the native templates, some alternative commit format templates
 are available as PyPI packages (installable with `pip`).
 
+### [cz-ai](https://github.com/watadarkstar/cz_ai)
+
+A Commitizen plugin that leverages OpenAI's GPT-4o to automatically generate clear, concise, and conventional commit messages based on your staged git changes.
+
+#### Installation
+
+```sh
+pip install cz-ai
+```
+
+#### Usage
+
+```sh
+cz --name cz_ai commit
+```
+
 ### [Conventional JIRA](https://pypi.org/project/conventional-JIRA/)
 
 Just like _conventional commit_ format, but the scope has been restricted to a

From e69e0a1babe6b9dd64f7c3cabb2246bd299379ab Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 17:04:41 +0800
Subject: [PATCH 055/169] docs: fix typos and grammar mistakes

---
 docs/commands/changelog.md        | 10 +++++-----
 docs/commands/check.md            | 22 ++++++++++-----------
 docs/commands/commit.md           |  2 +-
 docs/config.md                    |  6 +++---
 docs/contributing.md              | 32 +++++++++++++++----------------
 docs/customization.md             | 26 ++++++++++++-------------
 docs/faq.md                       |  8 ++++----
 docs/third-party-commitizen.md    |  2 +-
 docs/tutorials/auto_check.md      | 18 ++++++++---------
 docs/tutorials/dev_releases.md    | 11 +++--------
 docs/tutorials/github_actions.md  |  4 ++--
 docs/tutorials/gitlab_ci.md       |  8 ++++----
 docs/tutorials/tag_format.md      | 14 +++++++-------
 docs/tutorials/writing_commits.md | 16 ++++++++--------
 14 files changed, 87 insertions(+), 92 deletions(-)

diff --git a/docs/commands/changelog.md b/docs/commands/changelog.md
index cbf22b15a7..aee1a0e075 100644
--- a/docs/commands/changelog.md
+++ b/docs/commands/changelog.md
@@ -56,7 +56,7 @@ These are the variables used by the changelog generator.
 It will create a full block like above per version found in the tags.
 And it will create a list of the commits found.
 The `change_type` and the `scope` are optional, they don't need to be provided,
-but if your regex does they will be rendered.
+but if your regex does, they will be rendered.
 
 The format followed by the changelog is the one from [keep a changelog][keepachangelog]
 and the following variables are expected:
@@ -108,7 +108,7 @@ cz bump --changelog
 
 This value can be updated in the `toml` file with the key `changelog_file` under `tools.commitizen`
 
-Specify the name of the output file, remember that changelog only works with markdown.
+Specify the name of the output file, remember that changelog only works with Markdown.
 
 ```bash
 cz changelog --file-name="CHANGES.md"
@@ -120,7 +120,7 @@ This flag can be set in the `toml` file with the key `changelog_incremental` und
 
 Benefits:
 
-- Build from latest version found in changelog, this is useful if you have a different changelog and want to use commitizen
+- Build from the latest version found in changelog, this is useful if you have a different changelog and want to use commitizen
 - Update unreleased area
 - Allows users to manually touch the changelog without being rewritten.
 
@@ -185,8 +185,8 @@ See [the template customization section](../customization.md#customizing-the-cha
 
 Supported hook methods:
 
-- per parsed message: useful to add links
-- end of changelog generation: useful to send slack or chat message, or notify another department
+- Per parsed message: Useful to add links
+- End of changelog generation: Useful to send Slack or chat messages, or notify another department
 
 Read more about hooks in the [customization page][customization]
 
diff --git a/docs/commands/check.md b/docs/commands/check.md
index e45ecd86c8..33e41e04f8 100644
--- a/docs/commands/check.md
+++ b/docs/commands/check.md
@@ -2,9 +2,9 @@
 
 ## About
 
-This feature checks whether the commit message follows the given committing rules. And comment in git message will be ignored.
+This feature checks whether the commit message follows the given committing rules. Comments in git messages will be ignored.
 
-If you want to setup an automatic check before every git commit, please refer to
+If you want to set up an automatic check before every git commit, please refer to
 [Automatically check message before commit](../tutorials/auto_check.md).
 
 ## Usage
@@ -27,7 +27,7 @@ $ cz check --rev-range REV_RANGE
 
 For example, if you'd like to check all commits on a branch, you can use `--rev-range master..HEAD`. Or, if you'd like to check all commits starting from when you first implemented commit message linting, you can use `--rev-range <first_commit_sha>..HEAD`.
 
-For more info on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges).
+For more information on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges).
 
 ### Commit Message
 
@@ -47,7 +47,7 @@ In this option, MESSAGE is the commit message to be checked.
 $ echo MESSAGE | cz check
 ```
 
-In this option, MESSAGE is piped to cz check and would be checked.
+In this option, MESSAGE is piped to cz check and will be checked.
 
 ### Commit Message File
 
@@ -55,8 +55,8 @@ In this option, MESSAGE is piped to cz check and would be checked.
 $ cz check --commit-msg-file COMMIT_MSG_FILE
 ```
 
-In this option, COMMIT_MSG_FILE is the path of the temporal file that contains the commit message.
-This argument can be useful when cooperating with git hook, please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hook.
+In this option, COMMIT_MSG_FILE is the path of the temporary file that contains the commit message.
+This argument can be useful when cooperating with git hooks. Please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hooks.
 
 ### Allow Abort
 
@@ -69,8 +69,8 @@ permit them. Since `git commit` accepts an `--allow-empty-message` flag (primari
 
 ### Allowed Prefixes
 
-If the commit message starts by some specific prefixes, `cz check` returns `True` without checkign the regex.
-By default, the the following prefixes are allowed: `Merge`, `Revert`, `Pull request`, `fixup!` and `squash!`.
+If the commit message starts with some specific prefixes, `cz check` returns `True` without checking the regex.
+By default, the following prefixes are allowed: `Merge`, `Revert`, `Pull request`, `fixup!` and `squash!`.
 
 ```bash
 cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix'
@@ -78,10 +78,10 @@ cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix'
 
 ### Commit message length limit
 
-The argument `-l` (or `--message-length-limmit`) followed by a positive number, can limit the length of commit messages.
+The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages.
 For example, `cz check --message MESSAGE -l 3` would fail the check, since `MESSAGE` is more than 3 characters long.
 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.***
+**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.
+while the body and the footer are not counted.
diff --git a/docs/commands/commit.md b/docs/commands/commit.md
index 7760a2b88e..ea033cc411 100644
--- a/docs/commands/commit.md
+++ b/docs/commands/commit.md
@@ -45,7 +45,7 @@ a new commit message to be prompted.
 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.
+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,
diff --git a/docs/config.md b/docs/config.md
index a522312743..b6dd794fb3 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -341,7 +341,7 @@ commitizen:
 ## Version providers
 
 Commitizen can read and write version from different sources.
-By default, it use the `commitizen` one which is using the `version` field from the commitizen settings.
+By default, it uses the `commitizen` one which is using the `version` field from the commitizen settings.
 But you can use any `commitizen.provider` entrypoint as value for `version_provider`.
 
 Commitizen provides some version providers for some well known formats:
@@ -369,9 +369,9 @@ version_provider = "pep621"
 
 ### Custom version provider
 
-You can add you own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint.
+You can add your own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint.
 
-Here a quick example of a `my-provider` provider reading and writing version in a `VERSION` file.
+Here is a quick example of a `my-provider` provider reading and writing version in a `VERSION` file.
 
 ```python title="my_provider.py"
 from pathlib import Path
diff --git a/docs/contributing.md b/docs/contributing.md
index 0da1707da6..1192e57c0a 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -8,25 +8,25 @@ If you're a first-time contributor, you can check the issues with [good first is
 
 ## Install before contributing
 
-1. Install [poetry](https://python-poetry.org/) `>=2.0.0`, installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer)
-2. Install [gpg](https://gnupg.org), installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you could try [homebrew](https://brew.sh/).
+1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer).
+2. Install [gpg](https://gnupg.org). See installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/).
 
 ## Before making a pull request
 
 1. Fork [the repository](https://github.com/commitizen-tools/commitizen).
-2. Clone the repository from your GitHub.
-3. Setup development environment through [poetry](https://python-poetry.org/) (`poetry install`).
-4. Setup [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`)
-5. Check out a new branch and add your modification.
-6. Add test cases for all your changes.
+1. Clone the repository from your GitHub.
+1. Set up development environment through [poetry](https://python-poetry.org/) (`poetry install`).
+1. Set up [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`).
+1. Checkout a new branch and add your modifications.
+1. Add test cases for all your changes.
    (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 `poetry all` to ensure you follow the coding style and the tests pass.
-9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `poetry doc:screenshots`).
-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 doc` to serve documentation locally and check whether there is any warning or error.
-11. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏
+1. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/).
+1. Run `poetry all` to ensure you follow the coding style and the tests pass.
+1. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `poetry doc:screenshots`).
+1. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`.
+1. **Do not** update the versions in the project; they will be automatically updated.
+1. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors.
+1. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏
 
 ## Use of GitHub Labels
 
@@ -45,8 +45,8 @@ If you're a first-time contributor, you can check the issues with [good first is
 * pr-status: wait-for-modification
 * pr-status: wait-for-response
 * pr-status: ready-to-merge
-* needs: test-case *(pr only)*
-* needs: documentation *(pr only)*
+* needs: test-case *(PR only)*
+* needs: documentation *(PR only)*
 * type: feature
 * type: bug
 * type: documentation
diff --git a/docs/customization.md b/docs/customization.md
index 31749d1c83..cef03469e0 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -170,7 +170,7 @@ commitizen:
 
 | Parameter   | Type   | Default | Description                                                                                                                                                                                     |
 | ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `type`      | `str`  | `None`  | The type of questions. Valid types: `list`, `select`, `input` and etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types]                  |
+| `type`      | `str`  | `None`  | The type of questions. Valid types: `list`, `select`, `input`, etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types]                  |
 | `name`      | `str`  | `None`  | The key for the value answered by user. It's used in `message_template`                                                                                                                         |
 | `message`   | `str`  | `None`  | Detail description for the question.                                                                                                                                                            |
 | `choices`   | `list` | `None`  | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. |
@@ -192,10 +192,10 @@ To specify keyboard shortcuts for your custom choices, provide the shortcut usin
 
 The basic steps are:
 
-1. Inheriting from `BaseCommitizen`
+1. Inheriting from `BaseCommitizen`.
 2. Give a name to your rules.
-3. Create a python package using `setup.py`, `poetry`, etc
-4. Expose the class as a `commitizen.plugin` entrypoint
+3. Create a python package using `setup.py`, `poetry`, etc.
+4. Expose the class as a `commitizen.plugin` entrypoint.
 
 Check an [example][convcomms] on how to configure `BaseCommitizen`.
 
@@ -322,9 +322,9 @@ You can customize it of course, and this are the variables you need to add to yo
 | `commit_parser`                  | `str`                                                                    | NO       | Regex which should provide the variables explained in the [changelog description][changelog-des]                                                                                                                    |
 | `changelog_pattern`              | `str`                                                                    | NO       | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern                                                                  |
 | `change_type_map`                | `dict`                                                                   | NO       | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided                                                                                      |
-| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None`                                  | NO       | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. |
+| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None`                  | NO       | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. |
 | `changelog_hook`                 | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO       | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog                                                     |
-| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich a releases before they are rendered. Must return the update release
+| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich releases before they are rendered. Must return the update release
 
 ```python
 from commitizen.cz.base import BaseCommitizen
@@ -395,7 +395,7 @@ class NoSubjectProvidedException(CzException):
 Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`.
 Migration should be straight-forward for legacy plugins:
 
-- Remove the `discover_this` line from you plugin module
+- Remove the `discover_this` line from your plugin module
 - Expose the plugin class under as a `commitizen.plugin` entrypoint.
 
 The name of the plugin is now determined by the name of the entrypoint.
@@ -455,12 +455,12 @@ By default, the template used is the `CHANGELOG.md.j2` file from the commitizen
 
 ### Providing a template with your customization class
 
-There is 3 parameters available to change the template rendering from your custom `BaseCommitizen`.
+There are 3 parameters available to change the template rendering from your custom `BaseCommitizen`.
 
 | Parameter         | Type   | Default | Description                                                                                           |
 | ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- |
-| `template`        | `str`  | `None`  | Provide your own template name (default to `CHANGELOG.md.j2`)                            |
-| `template_loader` | `str`  | `None`  | Override the default template loader (so you can provide template from you customization class)       |
+| `template`        | `str`  | `None`  | Provide your own template name (default to `CHANGELOG.md.j2`)                                         |
+| `template_loader` | `str`  | `None`  | Override the default template loader (so you can provide template from your customization class)      |
 | `template_extras` | `dict` | `None`  | Provide some extra template parameters                                                                |
 
 Let's see an example.
@@ -484,14 +484,14 @@ This snippet will:
 
 ### Providing a template from the current working directory
 
-Users can provides their own template from their current working directory (your project root) by:
+Users can provide their own template from their current working directory (your project root) by:
 
 - providing a template with the same name (`CHANGELOG.md.j2` unless overridden by your custom class)
 - setting your template path as `template` configuration
 - giving your template path as `--template` parameter to `bump` and `changelog` commands
 
 !!! Note
-    The path is relative to the current working directory, aka. your project root most of the time.
+    The path is relative to the current working directory, aka your project root most of the time.
 
 ### Template variables
 
@@ -524,7 +524,7 @@ When using another template (either provided by a plugin or by yourself), you ca
 by:
 
 - defining them in your configuration with the [`extras` settings][extras-config]
-- providing them on the commandline with the `--extra/-e` parameter to `bump` and `changelog` commands
+- providing them on the command line with the `--extra/-e` parameter to `bump` and `changelog` commands
 
 [template-config]: config.md#template
 [extras-config]: config.md#extras
diff --git a/docs/faq.md b/docs/faq.md
index 29d9f40512..0302efd267 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -52,7 +52,7 @@ It is not affiliated.
 Both are used for similar purposes, parsing commits, generating changelog and version we presume.
 This one is written in python to make integration easier for python projects and the other serves the JS packages.
 
-They differ a bit in design, not sure if cz-js does any of this, but these are some of the stuff you can do with this repo (python's commitizen):
+They differ a bit in design, not sure if cz-js does any of this, but these are some things you can do with this repo (python's commitizen):
 
 - create custom rules, version bumps and changelog generation, by default we use the popular conventional commits (I think cz-js allows this).
 - single package, install one thing and it will work (cz-js is a monorepo, but you have to install different dependencies AFAIK)
@@ -83,14 +83,14 @@ More discussion can be found in issue [#318](https://github.com/commitizen-tools
 ## Why does commitizen not support CalVer?
 
 `commitizen` could support CalVer alongside SemVer, but in practice implementing CalVer
-creates numerous edge cases that are difficult to maintain ([#385]) and more generally
+creates numerous edge cases that are difficult to maintain ([#385]) and more generally,
 mixing the two version schemes may not be a good idea. If CalVer or other custom
 versioning scheme is needed, `commitizen` could still be used to standardize commits
 and create changelogs, but a separate package should be used for version increments.
 
 Mixing CalVer and SemVer is generally not recommended because each versioning scheme
-serves a different purposes. Diverging from either specification can be confusing to
-users and cause errors with third party tools that don't expect the non-standard format.
+serves a different purpose. Diverging from either specification can be confusing to
+users and cause errors with third-party tools that don't expect the non-standard format.
 
 In the future, `commitizen` may support some implementation of CalVer, but at the time
 of writing, there are no plans to implement the feature ([#173]).
diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md
index b6d598985f..e9eb822ed3 100644
--- a/docs/third-party-commitizen.md
+++ b/docs/third-party-commitizen.md
@@ -117,7 +117,7 @@ See the [README][1] for instructions on configuration
 
 ## Third-Party Commitizen Providers
 
-Commitizen can read and write version from different sources.  In addition to the native providers, some alternative version sources are available as PyPI packages (installable with `pip`).
+Commitizen can read and write version from different sources. In addition to the native providers, some alternative version sources are available as PyPI packages (installable with `pip`).
 
 ### [commitizen-deno-provider](https://pypi.org/project/commitizen-deno-provider/)
 
diff --git a/docs/tutorials/auto_check.md b/docs/tutorials/auto_check.md
index 2fce57f9bd..d143528767 100644
--- a/docs/tutorials/auto_check.md
+++ b/docs/tutorials/auto_check.md
@@ -6,9 +6,9 @@ To automatically check a commit message prior to committing, you can use a [git
 
 ## How to
 
-There are two common methods for installing the hook:
+There are two common methods for installing the hooks:
 
-### Method 1: Add git hook through [pre-commit](https://pre-commit.com/)
+### Method 1: Add a git hook through [pre-commit](https://pre-commit.com/)
 
 - Step 1: Install [pre-commit](https://pre-commit.com/)
 
@@ -16,7 +16,7 @@ There are two common methods for installing the hook:
 python -m pip install pre-commit
 ```
 
-- Step 2: Create `.pre-commit-config.yaml` at your root directory with the following content
+- Step 2: Create `.pre-commit-config.yaml` in your root directory with the following content
 
 ```yaml
 ---
@@ -28,19 +28,19 @@ repos:
         stages: [commit-msg]
 ```
 
-- Step 3: Install the configuration into git hook through `pre-commit`
+- Step 3: Install the configuration into the git hook through `pre-commit`
 
 ```bash
 pre-commit install --hook-type commit-msg
 ```
 
-### Method 2: Manually add git hook
+### Method 2: Manually add a git hook
 
-The command might be included inside of a Git hook (inside of `.git/hooks/` at the root of the project).
+The command might be included inside a Git hook (inside `.git/hooks/` at the root of the project).
 
 The selected hook might be the file called commit-msg.
 
-This example shows how to use the check command inside of commit-msg.
+This example shows how to use the check command inside commit-msg.
 
 At the root of the project:
 
@@ -62,7 +62,7 @@ Where `$1` is the name of the temporary file that contains the current commit me
 
 The `--commit-msg-file` flag is required, not optional.
 
-Each time you create a commit, automatically, this hook will analyze it.
-If the commit message is invalid, it'll be rejected.
+Each time you create a commit, this hook will automatically analyze it.
+If the commit message is invalid, it will be rejected.
 
 The commit should follow the given committing rules; otherwise, it won't be accepted.
diff --git a/docs/tutorials/dev_releases.md b/docs/tutorials/dev_releases.md
index 8142334754..e2b29fb191 100644
--- a/docs/tutorials/dev_releases.md
+++ b/docs/tutorials/dev_releases.md
@@ -5,9 +5,7 @@
 To make use of a `.dev` suffix, as per
 [PEP440](https://peps.python.org/pep-0440/#developmental-releases).
 
-If more than one active branch attempts to create a tag, relative to the main
-branch, there is the possibility that each will attempt to create the _same_
-tag, resulting in a collision.
+If multiple active branches attempt to create a tag relative to the main branch, there is a possibility that they will attempt to create the _same_ tag, resulting in a collision.
 
 Developmental releases aim to avoid this by including a `.dev` segment which
 includes a non-negative integer unique to that workflow:
@@ -19,9 +17,7 @@ X.Y.devN
 !!! note
     As noted in
     [PEP440](https://peps.python.org/pep-0440/#developmental-releases),
-    although developmental releases are useful in avoiding the situation
-    described above, depending on the value passed as the developmental
-    release, they can be _"difficult to parse for human readers"_.
+    while developmental releases help avoid the situation described above, they can be _"difficult to parse for human readers"_ depending on the value passed as the developmental release.
 
 ## How to
 
@@ -64,8 +60,7 @@ Equally, as the developmental release needs only a non-negative integer, it is
 possible to use the Unix time (i.e. the number of seconds since 1st January
 1970 UTC).
 
-This would create the possibility of a collision if two builds occur at
-precisely the same second but this may be sufficient for many cases:
+This approach could potentially create a collision if two builds occur at precisely the same second, but it may be sufficient for many use cases:
 
 ```sh
 --devrelease $(date +%s)
diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md
index 7a98abe2be..bcb3fda22c 100644
--- a/docs/tutorials/github_actions.md
+++ b/docs/tutorials/github_actions.md
@@ -51,7 +51,7 @@ where to output the content of the changelog for the newly created version.
 And then add a step using a github action to create the release: `softprops/action-gh-release`
 
 The commitizen action creates an env variable called `REVISION`, containing the
-newely created version.
+newly created version.
 
 ```yaml
 - name: Create bump and changelog
@@ -119,7 +119,7 @@ jobs:
           ./scripts/publish
 ```
 
-Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and the publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish)
+Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish)
 You can also use [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) to publish your package.
 
 Push the changes and that's it.
diff --git a/docs/tutorials/gitlab_ci.md b/docs/tutorials/gitlab_ci.md
index 85abb3fe6d..85b6a615c3 100644
--- a/docs/tutorials/gitlab_ci.md
+++ b/docs/tutorials/gitlab_ci.md
@@ -38,7 +38,7 @@ The latest step is to create a `deploy key.` To do this, we should create it und
 
 If you have more projects under the same organization, you can reuse the deploy key created before, but you will have to repeat the step where we have created the environment variables (ssh key, email, and username).
 
-tip: If the CI raise some errors, try to unprotected the private key.
+Tip: If the CI raise some errors, try to unprotect the private key.
 
 ### Defining GitLab CI Pipeline
 
@@ -105,9 +105,9 @@ auto-bump:
       - variables
 ```
 
-So, every time that a developer push to any branch, the `test` job is executed. If the branch is `master` and the test jobs success, the `auto-bump` takes place.
-To be able to push using the Gitlab runner, we have to set the ssh key, configure git, and finally execute the auto bump.
+So, every time that a developer pushes to any branch, the `test` job is executed. If the branch is `master` and the test jobs succeed, the `auto-bump` takes place.
+To be able to push using the GitLab runner, we have to set the SSH key, configure git, and finally execute the auto bump.
 
-After merging the new changed into master, we have the final result:
+After merging the new changes into master, we have the final result:
 
 ![gitlab final ci result](../images/gitlab_ci/gitlab_final_ci_result.png)
diff --git a/docs/tutorials/tag_format.md b/docs/tutorials/tag_format.md
index 59c42bea13..8408b4c801 100644
--- a/docs/tutorials/tag_format.md
+++ b/docs/tutorials/tag_format.md
@@ -10,7 +10,7 @@ tag_format: $version
 version_scheme: pep440
 ```
 
-As this is the default value so you don't have to specify it.
+As this is the default value, you don't have to specify it.
 
 This setting means that:
 
@@ -52,7 +52,7 @@ You will obviously want to keep all those features working as expected.
 
 Commitizen can deal with it as long as you provide the legacy tag format in the configuration.
 
-Using the previous example, let say you want to move from `v${version}` to `component-${version}`.
+Using the previous example, let's say you want to move from `v${version}` to `component-${version}`.
 Then `component-${version}` will be the new tag format and `v${version}` the legacy one.
 
 ```yaml
@@ -62,14 +62,14 @@ legacy_tag_formats:
  - v${version}
 ```
 
-This way, you won't loose your version history, you'll still be able to generate you changelog properly
-and on the next version bump, your last version in the form `v${version}` will be properly recognizef if you use the `scm` version provider.
+This way, you won't lose your version history, you'll still be able to generate your changelog properly,
+and on the next version bump, your last version in the form `v${version}` will be properly recognized if you use the `scm` version provider.
 Your new tag will be in the form `component-${version}`.
 
 ## Known tags to ignore
 
-Now let's say you have some known tags you want to ignore, either because they are not versions, either because they are not versions of the component you are dealing with.
-As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format:
+Now let's say you have some known tags you want to ignore, either because they are not versions, or because they are not versions of the component you are dealing with.
+As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format.
 
 Then you can tell Commitizen about it using the [`ignored_tag_formats`](../config.md#ignored_tag_formats) setting:
 
@@ -93,7 +93,7 @@ This will ignore:
     It will match any string from any length. This allows to exclude by prefix, whether it is followed by a version or not.
 
 !!! tip
-    If you don't want to be warned when Commitizen detect an unknown tag, you can by setting:
+    If you don't want to be warned when Commitizen detects an unknown tag, you can do so by setting:
     ```
     [tool.commitizen]
     ignored_tag_formats = ["*"]
diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md
index 9ba151cc37..d1b2c6645d 100644
--- a/docs/tutorials/writing_commits.md
+++ b/docs/tutorials/writing_commits.md
@@ -1,21 +1,21 @@
 For this project to work well in your pipeline, a commit convention must be followed.
 
-By default commitizen uses the known [conventional commits][conventional_commits], but
-you can create your own following the docs information over at
+By default, commitizen uses the known [conventional commits][conventional_commits], but
+you can create your own following the documentation information over at
 [customization][customization].
 
 ## Conventional commits
 
 If you are using [conventional commits][conventional_commits], the most important
 thing to know is that you must begin your commits with at least one of these tags:
-`fix`, `feat`. And if you introduce a breaking change, then, you must
+`fix`, `feat`. And if you introduce a breaking change, then you must
 add to your commit body the following `BREAKING CHANGE`.
-Using these 3 keywords will allow the proper identification of the semantic version.
+Using these three keywords will allow the proper identification of the semantic version.
 Of course, there are other keywords, but I'll leave it to the reader to explore them.
 
 ## Writing commits
 
-Now to the important part, when writing commits, it's important to think about:
+Now to the important part: when writing commits, it's important to think about:
 
 - Your future self
 - Your colleagues
@@ -23,16 +23,16 @@ Now to the important part, when writing commits, it's important to think about:
 You may think this is trivial, but it's not. It's important for the reader to
 understand what happened.
 
-Emojis may be added as well (e.g. see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration].
+Emojis may be added as well (e.g., see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration].
 
 ### Recommendations
 
 - **Keep the message short**: Makes the list of commits more readable (~50 chars).
 - **Talk imperative**: Follow this rule: `If applied, this commit will <commit message>`
-- **Think about the CHANGELOG**: Your commits will probably end up in the changelog
+- **Think about the CHANGELOG**: Your commits will probably end up in the changelog,
   so try writing for it, but also keep in mind that you can skip sending commits to the
   CHANGELOG by using different keywords (like `build`).
-- **Use a commit per new feature**: if you introduce multiple things related to the same
+- **Use a commit per new feature**: If you introduce multiple things related to the same
   commit, squash them. This is useful for auto-generating CHANGELOG.
 
 | Do's | Don'ts |

From c3d4152fb736969b83be7b555ea44b025ffc6c7e Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 17:19:32 +0800
Subject: [PATCH 056/169] docs: fix typo issues in cli.py, README.md, and
 contributing.md

---
 commitizen/cli.py    |  2 +-
 docs/README.md       | 18 +++++++++---------
 docs/contributing.md | 30 +++++++++++++++---------------
 3 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index d08afc6706..f0df2f4242 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -105,7 +105,7 @@ def __call__(
             "name": ["-nr", "--no-raise"],
             "type": str,
             "required": False,
-            "help": "comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/",
+            "help": "comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/",
         },
     ],
     "subcommands": {
diff --git a/docs/README.md b/docs/README.md
index 128602dfb3..ee50ad4582 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -18,16 +18,16 @@
 
 ## About
 
-Commitizen is release management tool designed for teams.
+Commitizen is a release management tool designed for teams.
 
 Commitizen assumes your team uses a standard way of committing rules
 and from that foundation, it can bump your project's version, create
 the changelog, and update files.
 
 By default, commitizen uses [conventional commits][conventional_commits], but you
-can build your own set of rules, and publish them.
+can build your own set of rules and publish them.
 
-Using a standardized set of rules to write commits, makes commits easier to read, and enforces writing
+Using a standardized set of rules to write commits makes commits easier to read and enforces writing
 descriptive commits.
 
 ### Features
@@ -55,7 +55,7 @@ pipx install commitizen
 pipx upgrade commitizen
 ```
 
-Install commitizen using `pip` with `--user` flag:
+Install commitizen using `pip` with the `--user` flag:
 
 ```bash
 pip install --user -U commitizen
@@ -63,7 +63,7 @@ pip install --user -U commitizen
 
 ### Python project
 
-You can add it to your local project using one of the following.
+You can add it to your local project using one of the following methods.
 
 With `pip`:
 
@@ -99,7 +99,7 @@ brew install commitizen
 
 ## Usage
 
-Most of the time this is the only command you'll run:
+Most of the time, this is the only command you'll run:
 
 ```sh
 cz bump
@@ -120,7 +120,7 @@ $ cz --help
 usage: cz [-h] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ...
 
 Commitizen is a cli tool to generate conventional commits.
-For more information about the topic go to https://conventionalcommits.org/
+For more information about the topic, go to https://conventionalcommits.org/
 
 optional arguments:
   -h, --help            show this help message and exit
@@ -128,7 +128,7 @@ optional arguments:
   --debug               use debug mode
   -n NAME, --name NAME  use the given commitizen (default: cz_conventional_commits)
   -nr NO_RAISE, --no-raise NO_RAISE
-                        comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-
+                        comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-
                         tools.github.io/commitizen/exit_codes/
 
 commands:
@@ -147,7 +147,7 @@ commands:
 
 ## Setting up bash completion
 
-When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this argcomplete needs to be enabled.
+When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this, argcomplete needs to be enabled.
 
 argcomplete is installed when you install Commitizen since it's a dependency.
 
diff --git a/docs/contributing.md b/docs/contributing.md
index 1192e57c0a..0987771066 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -4,29 +4,29 @@ First of all, thank you for taking the time to contribute! 🎉
 
 When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change.
 
-If you're a first-time contributor, you can check the issues with [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag.
+If you're a first-time contributor, you can check the issues with the [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag.
 
 ## Install before contributing
 
-1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer).
-2. Install [gpg](https://gnupg.org). See installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/).
+1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See the installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer).
+2. Install [gpg](https://gnupg.org). See the installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/).
 
 ## Before making a pull request
 
 1. Fork [the repository](https://github.com/commitizen-tools/commitizen).
-1. Clone the repository from your GitHub.
-1. Set up development environment through [poetry](https://python-poetry.org/) (`poetry install`).
-1. Set up [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`).
-1. Checkout a new branch and add your modifications.
-1. Add test cases for all your changes.
+2. Clone the repository from your GitHub.
+3. Set up the development environment through [poetry](https://python-poetry.org/) (`poetry install`).
+4. Set up the [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`).
+5. Checkout a new branch and add your modifications.
+6. Add test cases for all your changes.
    (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.)
-1. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/).
-1. Run `poetry all` to ensure you follow the coding style and the tests pass.
-1. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `poetry doc:screenshots`).
-1. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`.
-1. **Do not** update the versions in the project; they will be automatically updated.
-1. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors.
-1. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏
+7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to make git commits. We follow [conventional commits](https://www.conventionalcommits.org/).
+8. Run `poetry all` to ensure you follow the coding style and the tests pass.
+9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (by running `poetry doc:screenshots`).
+10. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`.
+11. **Do not** update the versions in the project; they will be automatically updated.
+12. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors.
+13. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏
 
 ## Use of GitHub Labels
 

From 23bd8936a448c15e36f775496221731fb40df0ad Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 22:53:40 +0800
Subject: [PATCH 057/169] docs(README.md): sync help section with the latest
 `cz --help` command output

---
 docs/README.md | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index ee50ad4582..bcc206642d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -115,21 +115,22 @@ Read more in the section [Getting Started](./getting_started.md).
 
 ### Help
 
+<!-- Please manually update the following section after changing `cz --help` command output. -->
+
 ```sh
 $ cz --help
-usage: cz [-h] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ...
+usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ...
 
 Commitizen is a cli tool to generate conventional commits.
-For more information about the topic, go to https://conventionalcommits.org/
+For more information about the topic go to https://conventionalcommits.org/
 
-optional arguments:
+options:
   -h, --help            show this help message and exit
-  --config              the path of configuration file
+  --config CONFIG       the path of configuration file
   --debug               use debug mode
-  -n NAME, --name NAME  use the given commitizen (default: cz_conventional_commits)
-  -nr NO_RAISE, --no-raise NO_RAISE
-                        comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-
-                        tools.github.io/commitizen/exit_codes/
+  -n, --name NAME       use the given commitizen (default: cz_conventional_commits)
+  -nr, --no-raise NO_RAISE
+                        comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/
 
 commands:
   {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}

From 3fcefbd591aa1c14c40730d9deae82f7918f8b2a Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 23:08:34 +0800
Subject: [PATCH 058/169] docs(README.md): make it easier for readers to
 understand what Commitizen does

---
 docs/README.md | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index bcc206642d..616e7610cc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -18,17 +18,26 @@
 
 ## About
 
-Commitizen is a release management tool designed for teams.
+Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.
 
-Commitizen assumes your team uses a standard way of committing rules
-and from that foundation, it can bump your project's version, create
-the changelog, and update files.
+### What Commitizen Does
 
-By default, commitizen uses [conventional commits][conventional_commits], but you
-can build your own set of rules and publish them.
+By enforcing standardized commit conventions (defaulting to [Conventional Commits][conventional_commits]), Commitizen helps teams:
 
-Using a standardized set of rules to write commits makes commits easier to read and enforces writing
-descriptive commits.
+- Write clear, structured commit messages
+- Automatically manage version numbers using semantic versioning
+- Generate and maintain changelogs
+- Streamline the release process
+
+### Key Benefits
+
+With just a simple `cz bump` command, Commitizen handles:
+
+1. **Version Management**: Automatically bumps version numbers and updates version files based on your commit history
+2. **Changelog Generation**: Creates and updates changelogs following the [Keep a changelog][keepchangelog] format
+3. **Commit Standardization**: Enforces consistent commit message formats across your team
+
+This standardization makes your commit history more readable and meaningful, while the automation reduces manual work and potential errors in the release process.
 
 ### Features
 

From 889198d5688e78793e4099e7493b030a652bb670 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 23:38:41 +0800
Subject: [PATCH 059/169] docs(README.md): move Getting Started to README and
 improve clarity of the section

Closes #1405
---
 docs/README.md          | 137 ++++++++++++++++++++++++++++++++--------
 docs/getting_started.md | 119 ----------------------------------
 mkdocs.yml              |   1 -
 3 files changed, 112 insertions(+), 145 deletions(-)
 delete mode 100644 docs/getting_started.md

diff --git a/docs/README.md b/docs/README.md
index 616e7610cc..2a9e79bbfb 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -48,81 +48,168 @@ This standardization makes your commit history more readable and meaningful, whi
 - Display information about your commit rules (commands: schema, example, info)
 - Create your own set of rules and publish them to pip. Read more on [Customization](./customization.md)
 
-## Requirements
+## Getting Started
 
-[Python](https://www.python.org/downloads/) `3.9+`
+### Requirements
 
-[Git][gitscm] `1.8.5.2+`
+Before installing Commitizen, ensure you have:
 
-## Installation
+- [Python](https://www.python.org/downloads/) `3.9+`
+- [Git][gitscm] `1.8.5.2+`
 
-Install commitizen in your system using `pipx` (Recommended, <https://pypa.github.io/pipx/installation/>):
+### Installation
+
+#### Global Installation (Recommended)
+
+The recommended way to install Commitizen is using `pipx`, which ensures a clean, isolated installation:
 
 ```bash
+# Install pipx if you haven't already
 pipx ensurepath
+
+# Install Commitizen
 pipx install commitizen
+
+# Keep it updated
 pipx upgrade commitizen
 ```
 
-Install commitizen using `pip` with the `--user` flag:
+If you're on macOS, you can also install Commitizen using Homebrew:
 
 ```bash
-pip install --user -U commitizen
+brew install commitizen
 ```
 
-### Python project
+#### Project-Specific Installation
 
-You can add it to your local project using one of the following methods.
+You can add Commitizen to your Python project using any of these package managers:
 
-With `pip`:
+**Using pip:**
 
 ```bash
 pip install -U commitizen
 ```
 
-With `conda`:
+**Using conda:**
 
 ```bash
 conda install -c conda-forge commitizen
 ```
 
-With Poetry >= 1.2.0:
+**Using Poetry:**
 
 ```bash
+# For Poetry >= 1.2.0
 poetry add commitizen --group dev
+
+# For Poetry < 1.2.0
+poetry add commitizen --dev
 ```
 
-With Poetry < 1.2.0:
+### Basic Commands
 
-```bash
-poetry add commitizen --dev
+#### Initialize Commitizen
+
+To get started, you'll need to set up your configuration. You have two options:
+
+1. Use the interactive setup:
+```sh
+cz init
 ```
 
-### macOS
+2. Manually create a configuration file (`.cz.toml` or `cz.toml`):
+```toml
+[tool.commitizen]
+version = "0.1.0"
+update_changelog_on_bump = true
+```
 
-via [homebrew](https://formulae.brew.sh/formula/commitizen):
+#### Create Commits
 
-```bash
-brew install commitizen
+Create standardized commits using:
+```sh
+cz commit
+# or use the shortcut
+cz c
 ```
 
-## Usage
+To sign off your commits:
+```sh
+cz commit -- --signoff
+# or use the shortcut
+cz commit -- -s
+```
 
-Most of the time, this is the only command you'll run:
+For more commit options, run `cz commit --help`.
 
+#### Version Management
+
+The most common command you'll use is:
 ```sh
 cz bump
 ```
 
-On top of that, you can use commitizen to assist you with the creation of commits:
+This command:
+- Bumps your project's version
+- Creates a git tag
+- Updates the changelog (if `update_changelog_on_bump` is enabled)
+- Updates version files
+
+You can customize:
+- [Version files](./commands/bump.md#version_files)
+- [Version scheme](./commands/bump.md#version_scheme)
+- [Version provider](./config.md#version-providers)
+
+For all available options, see the [bump command documentation](./commands/bump.md).
+
+### Advanced Usage
+
+#### Get Project Version
 
+To get your project's version (instead of Commitizen's version):
 ```sh
-cz commit
+cz version -p
+```
+
+This is particularly useful for automation. For example, to preview changelog changes for Slack:
+```sh
+cz changelog --dry-run "$(cz version -p)"
+```
+
+#### Pre-commit Integration
+
+Commitizen can automatically validate your commit messages using pre-commit hooks.
+
+1. Add to your `.pre-commit-config.yaml`:
+```yaml
+---
+repos:
+  - repo: https://github.com/commitizen-tools/commitizen
+    rev: master  # Replace with latest tag
+    hooks:
+      - id: commitizen
+      - id: commitizen-branch
+        stages: [pre-push]
+```
+
+2. Install the hooks:
+```sh
+pre-commit install --hook-type commit-msg --hook-type pre-push
 ```
 
-Read more in the section [Getting Started](./getting_started.md).
+| Hook              | Recommended Stage |
+| ----------------- | ----------------- |
+| commitizen        | commit-msg        |
+| commitizen-branch | pre-push          |
+
+> **Note**: Replace `master` with the [latest tag](https://github.com/commitizen-tools/commitizen/tags) to avoid warnings. You can automatically update this with:
+> ```sh
+> pre-commit autoupdate
+> ```
 
-### Help
+For more details about commit validation, see the [check command documentation](commands/check.md).
+
+## Usage
 
 <!-- Please manually update the following section after changing `cz --help` command output. -->
 
diff --git a/docs/getting_started.md b/docs/getting_started.md
deleted file mode 100644
index 3c6257c363..0000000000
--- a/docs/getting_started.md
+++ /dev/null
@@ -1,119 +0,0 @@
-## Initialize commitizen
-
-If it's your first time, you'll need to create a commitizen configuration file.
-
-The assistant utility will help you set up everything
-
-```sh
-cz init
-```
-
-Alternatively, create a file `.cz.toml` or `cz.toml` in your project's directory.
-
-```toml
-[tool.commitizen]
-version = "0.1.0"
-update_changelog_on_bump = true
-```
-
-## Usage
-
-### Bump version
-
-```sh
-cz bump
-```
-
-This command will bump your project's version, and it will create a tag.
-
-Because of the setting `update_changelog_on_bump`, bump will also create the **changelog**.
-You can also [update files](./commands/bump.md#version_files).
-You can configure the [version scheme](./commands/bump.md#version_scheme) and [version provider](./config.md#version-providers).
-
-There are many more options available, please read the docs for the [bump command](./commands/bump.md).
-
-### Committing
-
-Run in your terminal
-
-```bash
-cz commit
-```
-
-or the shortcut
-
-```bash
-cz c
-```
-
-#### Sign off the commit
-
-Run in the terminal
-
-```bash
-cz commit -- --signoff
-```
-
-or the shortcut
-
-```bash
-cz commit -- -s
-```
-
-### Get project version
-
-Running `cz version` will return the version of commitizen, but if you want
-your project's version you can run:
-
-```sh
-cz version -p
-```
-
-This can be useful in many situations, where otherwise, you would require a way
-to parse the version of your project. Maybe it's simple if you use a `VERSION` file,
-but once you start working with many different projects, it becomes tricky.
-
-A common example is, when you need to send to slack, the changes for the version that you
-just created:
-
-```sh
-cz changelog --dry-run "$(cz version -p)"
-```
-
-### Integration with Pre-commit
-
-Commitizen can lint your commit message for you with `cz check`.
-
-You can integrate this in your [pre-commit](https://pre-commit.com/) config with:
-
-```yaml
----
-repos:
-  - repo: https://github.com/commitizen-tools/commitizen
-    rev: master
-    hooks:
-      - id: commitizen
-      - id: commitizen-branch
-        stages: [pre-push]
-```
-
-After the configuration is added, you'll need to run:
-
-```sh
-pre-commit install --hook-type commit-msg --hook-type pre-push
-```
-
-If you aren't using both hooks, you needn't install both stages.
-
-| Hook              | Recommended Stage |
-| ----------------- | ----------------- |
-| commitizen        | commit-msg        |
-| commitizen-branch | pre-push          |
-
-Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with:
-
-```sh
-pre-commit autoupdate
-```
-
-Read more about the `check` command [here](commands/check.md).
diff --git a/mkdocs.yml b/mkdocs.yml
index 6a642161d2..a8a2fe2d44 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -31,7 +31,6 @@ edit_uri: ""
 
 nav:
   - Introduction: "README.md"
-  - Getting Started: "getting_started.md"
   - Commands:
       - init: "commands/init.md"
       - commit: "commands/commit.md"

From 36c7ae576fa4ffae3abcd16f95d5caf066fd7911 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 11 May 2025 23:55:40 +0800
Subject: [PATCH 060/169] docs(README.md): improve clarity of README reference
 section

---
 docs/README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 65 insertions(+), 7 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index 2a9e79bbfb..22d22f42c9 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -209,7 +209,11 @@ pre-commit install --hook-type commit-msg --hook-type pre-push
 
 For more details about commit validation, see the [check command documentation](commands/check.md).
 
-## Usage
+## Help & Reference
+
+### Command Line Interface
+
+Commitizen provides a comprehensive CLI with various commands. Here's the complete reference:
 
 <!-- Please manually update the following section after changing `cz --help` command output. -->
 
@@ -242,31 +246,85 @@ commands:
     version             get the version of the installed commitizen or the current project (default: installed commitizen)
 ```
 
+### Quick Reference
+
+| Command | Description | Alias |
+|---------|-------------|-------|
+| `cz init` | Initialize Commitizen configuration | - |
+| `cz commit` | Create a new commit | `cz c` |
+| `cz bump` | Bump version and update changelog | - |
+| `cz changelog` | Generate changelog | `cz ch` |
+| `cz check` | Validate commit messages | - |
+| `cz version` | Show version information | - |
+
+### Additional Resources
+
+- [Conventional Commits Specification][conventional_commits]
+- [Exit Codes Reference](./exit_codes.md)
+- [Configuration Guide](./config.md)
+- [Command Documentation](./commands/init.md)
+
+### Getting Help
+
+For each command, you can get detailed help by adding `--help`:
+
+```sh
+cz commit --help
+cz bump --help
+cz changelog --help
+```
+
+For more detailed documentation, visit our [documentation site](https://commitizen-tools.github.io/commitizen/).
+
 ## Setting up bash completion
 
-When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this, argcomplete needs to be enabled.
+Commitizen supports command-line completion through [argcomplete](https://kislyuk.github.io/argcomplete/), which is automatically installed as a dependency. This feature provides intelligent auto-completion for all Commitizen commands and options.
+
+### Supported Shells
+
+- **Bash**: Full support
+- **Zsh**: Limited support
+- **Fish**: Limited support
+- **Tcsh**: Limited support
 
-argcomplete is installed when you install Commitizen since it's a dependency.
+### Installation Methods
+
+#### Global Installation (Recommended)
 
-If Commitizen is installed globally, global activation can be executed:
+If you installed Commitizen globally (e.g., using `pipx` or `brew`), you can enable global completion:
 
 ```bash
+# Enable global completion for all Python applications
 sudo activate-global-python-argcomplete
 ```
 
-For permanent (but not global) Commitizen activation, use:
+#### User-Specific Installation
+
+For a user-specific installation that persists across sessions:
 
 ```bash
+# Add to your shell's startup file (e.g., ~/.bashrc, ~/.zshrc)
 register-python-argcomplete cz >> ~/.bashrc
 ```
 
-For one-time activation of argcomplete for Commitizen only, use:
+#### Temporary Installation
+
+For one-time activation in your current shell session:
 
 ```bash
+# Activate completion for current session only
 eval "$(register-python-argcomplete cz)"
 ```
 
-For further information on activation, please visit the [argcomplete website](https://kislyuk.github.io/argcomplete/).
+### Verification
+
+After installation, you can verify the completion is working by:
+
+1. Opening a new terminal session
+2. Typing `cz` followed by a space and pressing `TAB` twice
+3. You should see a list of available commands
+
+For more detailed information about argcomplete configuration and troubleshooting, visit the [argcomplete documentation](https://kislyuk.github.io/argcomplete/).
 
 ## Sponsors
 

From 37a4a3ae4b0f9347f3cb4445931f027fee0ea9c2 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 12 May 2025 00:58:47 +0800
Subject: [PATCH 061/169] docs(cli): align cli output with docs

---
 commitizen/cli.py | 5 ++---
 docs/README.md    | 4 ++--
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index f0df2f4242..cb834c5d6f 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -86,9 +86,8 @@ def __call__(
 data = {
     "prog": "cz",
     "description": (
-        "Commitizen is a cli tool to generate conventional commits.\n"
-        "For more information about the topic go to "
-        "https://conventionalcommits.org/"
+        "Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.\n"
+        "For more information, please visit https://commitizen-tools.github.io/commitizen"
     ),
     "formatter_class": argparse.RawDescriptionHelpFormatter,
     "arguments": [
diff --git a/docs/README.md b/docs/README.md
index 22d22f42c9..144c253a81 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -221,8 +221,8 @@ Commitizen provides a comprehensive CLI with various commands. Here's the comple
 $ cz --help
 usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ...
 
-Commitizen is a cli tool to generate conventional commits.
-For more information about the topic go to https://conventionalcommits.org/
+Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.
+For more information, please visit https://commitizen-tools.github.io/commitizen
 
 options:
   -h, --help            show this help message and exit

From 4cd6d78a6bfc603497f9cb2c1b14c38616218b9f Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 12 May 2025 01:56:29 +0800
Subject: [PATCH 062/169] docs(contributing.md): improve readability of
 contributing docs

---
 docs/contributing.md | 97 ++++++++++++++++++++++++++++++++------------
 1 file changed, 70 insertions(+), 27 deletions(-)

diff --git a/docs/contributing.md b/docs/contributing.md
index 0987771066..4ca12765aa 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -1,32 +1,78 @@
 ## Contributing to commitizen
 
-First of all, thank you for taking the time to contribute! 🎉
+First, thank you for taking the time to contribute! 🎉
 
 When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change.
 
 If you're a first-time contributor, you can check the issues with the [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag.
 
-## Install before contributing
-
-1. Install [poetry](https://python-poetry.org/) `>=2.0.0`. See the installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer).
-2. Install [gpg](https://gnupg.org). See the installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you can use [homebrew](https://brew.sh/).
-
-## Before making a pull request
-
-1. Fork [the repository](https://github.com/commitizen-tools/commitizen).
-2. Clone the repository from your GitHub.
-3. Set up the development environment through [poetry](https://python-poetry.org/) (`poetry install`).
-4. Set up the [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`).
-5. Checkout a new branch and add your modifications.
-6. Add test cases for all your changes.
-   (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.)
-7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to make git commits. We follow [conventional commits](https://www.conventionalcommits.org/).
-8. Run `poetry all` to ensure you follow the coding style and the tests pass.
-9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (by running `poetry doc:screenshots`).
-10. **Do not** update the `CHANGELOG.md`; it will be automatically created after merging to `master`.
-11. **Do not** update the versions in the project; they will be automatically updated.
-12. If your changes are about documentation, run `poetry doc` to serve documentation locally and check whether there are any warnings or errors.
-13. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏
+## Prerequisites & Setup
+
+### Required Tools
+
+1. **Python Environment**
+    - Python `>=3.9`
+    - [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0`
+2. **Version Control & Security**
+    - Git
+    - Commitizen
+    - [GPG](https://gnupg.org) for commit signing
+        - [Installation page](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation)
+        - For Mac users: `brew install gnupg`
+        - For Windows users: Download from [Gpg4win](https://www.gpg4win.org/)
+        - For Linux users: Use your distribution's package manager (e.g., `apt install gnupg` for Ubuntu)
+
+### Getting Started
+
+1. Fork [Commitizen](https://github.com/commitizen-tools/commitizen)
+2. Clone your fork:
+    ```bash
+    git clone https://github.com/YOUR_USERNAME/commitizen.git
+    cd commitizen
+    ```
+3. Add the upstream repository:
+    ```bash
+    git remote add upstream https://github.com/commitizen-tools/commitizen.git
+    ```
+4. Set up the development environment:
+    ```bash
+    poetry install
+    ```
+5. Set up pre-commit hooks:
+    ```bash
+    poetry setup-pre-commit
+    ```
+
+## Development Workflow
+
+1. **Create a New Branch**
+    ```bash
+    git switch -c feature/your-feature-name
+    # or
+    git switch -c fix/your-bug-fix
+    ```
+2. **Make Your Changes**
+    - Write your code
+    - Add tests for new functionalities or fixes
+    - Update documentation if needed
+    - Follow the existing code style
+3. **Testing**
+    - Run the full test suite: `poetry all`
+    - Ensure test coverage doesn't drop (we use [CodeCov](https://codecov.io/))
+    - For documentation changes, run `poetry doc` to check for warnings/errors
+4. **Committing Changes**
+    - Use commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/))
+    - Example: `cz commit`
+5. **Documentation**
+    - Update `docs/README.md` if needed
+    - For CLI help screenshots: `poetry doc:screenshots`
+    - **DO NOT** update `CHANGELOG.md` (automatically generated)
+    - **DO NOT** update version numbers (automatically handled)
+6. **Pull Request**
+    - Push your changes: `git push origin your-branch-name`
+    - Create a pull request on GitHub
+    - Ensure CI checks pass
+    - Wait for review and address any feedback
 
 ## Use of GitHub Labels
 
@@ -57,7 +103,7 @@ If you're a first-time contributor, you can check the issues with the [good firs
 * os: macOS
 
 
-### Issue life cycle
+## Issue life cycle
 
 ```mermaid
 graph TD
@@ -75,7 +121,7 @@ graph TD
     close --> output[/close/]
 ```
 
-### Pull request life cycle
+## Pull request life cycle
 
 ```mermaid
 flowchart TD
@@ -103,6 +149,3 @@ flowchart TD
     --modification-received-->
     review
 ```
-
-
-[conventional-commits]: https://www.conventionalcommits.org/

From 5e14843ef8ac0c85724588e0f49317132c3cab0f Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 12 May 2025 03:05:22 +0800
Subject: [PATCH 063/169] docs(pyproject.toml): fix typo

---
 pyproject.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index b306e5e78c..a653ecdb5d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -257,7 +257,7 @@ all.sequence = [
     "check-commit",
 ]
 
-"doc:screenshots".help = "Render documentation screeenshots"
+"doc:screenshots".help = "Render documentation screenshots"
 "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots"
 
 "doc:build".help = "Build the documentation"

From 614eaa3995cf45f55e8d1e7efdcf52e94946f2f8 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Tue, 13 May 2025 00:29:29 +0800
Subject: [PATCH 064/169] docs(README): update get project version section

---
 docs/README.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index 144c253a81..721ab99791 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -166,16 +166,16 @@ For all available options, see the [bump command documentation](./commands/bump.
 
 #### Get Project Version
 
-To get your project's version (instead of Commitizen's version):
 ```sh
+# Get your project's version (instead of Commitizen's version)
 cz version -p
-```
-
-This is particularly useful for automation. For example, to preview changelog changes for Slack:
-```sh
+# Preview changelog changes
 cz changelog --dry-run "$(cz version -p)"
 ```
 
+This command is particularly useful for automation scripts and CI/CD pipelines.
+For example, you can use the output of the command `cz changelog --dry-run "$(cz version -p)"` to notify your team about a new release in Slack.
+
 #### Pre-commit Integration
 
 Commitizen can automatically validate your commit messages using pre-commit hooks.

From f893d082030527dc965348ec6dfb163130b75359 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 14 May 2025 23:00:31 +0800
Subject: [PATCH 065/169] docs(README): update installation methods

---
 docs/README.md | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index 721ab99791..e35cf5e358 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -61,12 +61,9 @@ Before installing Commitizen, ensure you have:
 
 #### Global Installation (Recommended)
 
-The recommended way to install Commitizen is using `pipx`, which ensures a clean, isolated installation:
-
+The recommended way to install Commitizen is using [`pipx`](https://pipx.pypa.io/) or [`uv`](https://docs.astral.sh/uv/), which ensures a clean, isolated installation:
+**Using pipx:**
 ```bash
-# Install pipx if you haven't already
-pipx ensurepath
-
 # Install Commitizen
 pipx install commitizen
 
@@ -74,8 +71,16 @@ pipx install commitizen
 pipx upgrade commitizen
 ```
 
-If you're on macOS, you can also install Commitizen using Homebrew:
+**Using uv:**
+```bash
+# Install commitizen
+uv tool install commitizen
 
+# Keep it updated
+uv tool upgrade commitizen
+```
+
+**(For macOS users) Using Homebrew:**
 ```bash
 brew install commitizen
 ```
@@ -85,19 +90,16 @@ brew install commitizen
 You can add Commitizen to your Python project using any of these package managers:
 
 **Using pip:**
-
 ```bash
 pip install -U commitizen
 ```
 
 **Using conda:**
-
 ```bash
 conda install -c conda-forge commitizen
 ```
 
 **Using Poetry:**
-
 ```bash
 # For Poetry >= 1.2.0
 poetry add commitizen --group dev

From 6930aaaea170fba7fa1aca13cbff40b11d79564e Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 20:54:23 +0800
Subject: [PATCH 066/169] docs(README): use cli screenshots

Closes #1413
---
 docs/README.md | 31 +------------------------------
 1 file changed, 1 insertion(+), 30 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index e35cf5e358..ed1aaef2a1 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -217,36 +217,7 @@ For more details about commit validation, see the [check command documentation](
 
 Commitizen provides a comprehensive CLI with various commands. Here's the complete reference:
 
-<!-- Please manually update the following section after changing `cz --help` command output. -->
-
-```sh
-$ cz --help
-usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ...
-
-Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.
-For more information, please visit https://commitizen-tools.github.io/commitizen
-
-options:
-  -h, --help            show this help message and exit
-  --config CONFIG       the path of configuration file
-  --debug               use debug mode
-  -n, --name NAME       use the given commitizen (default: cz_conventional_commits)
-  -nr, --no-raise NO_RAISE
-                        comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/
-
-commands:
-  {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}
-    init                init commitizen configuration
-    commit (c)          create new commit
-    ls                  show available commitizens
-    example             show commit example
-    info                show information about the cz
-    schema              show commit schema
-    bump                bump semantic version based on the git log
-    changelog (ch)      generate changelog (note that it will overwrite existing file)
-    check               validates that a commit message matches the commitizen schema
-    version             get the version of the installed commitizen or the current project (default: installed commitizen)
-```
+![cz --help](images/cli_help/cz___help.svg)
 
 ### Quick Reference
 

From ec57f7de0da977f43157b7235e7f817399bc10ed Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 21:13:54 +0800
Subject: [PATCH 067/169] docs: fix link warnings and minor grammar issues

---
 docs/commands/bump.md               | 10 +++++-----
 docs/config.md                      | 24 ++++++++++++------------
 docs/customization.md               |  4 ++--
 docs/tutorials/github_actions.md    |  4 ++--
 docs/tutorials/gitlab_ci.md         |  4 ++--
 docs/tutorials/jenkins_pipeline.md  |  2 +-
 docs/tutorials/monorepo_guidance.md |  4 ++--
 7 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/docs/commands/bump.md b/docs/commands/bump.md
index efdba76257..6eb5b55c54 100644
--- a/docs/commands/bump.md
+++ b/docs/commands/bump.md
@@ -72,7 +72,7 @@ cz bump --changelog
 
 The bump is a pre-release bump, meaning that in addition to a possible version bump the new version receives a
 pre-release segment compatible with the bump’s version scheme, where the segment consist of a _phase_ and a
-non-negative number. Supported options for `--prerelease` are the following phase names  `alpha`, `beta`, or
+non-negative number. Supported options for `--prerelease` are the following phase names `alpha`, `beta`, or
 `rc` (release candidate). For more details, refer to the
 [Python Packaging User Guide](https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases).
 
@@ -387,7 +387,7 @@ to skip and why.
 Remember to document somewhere this, because you'll forget.
 
 For example if the system raises a `NoneIncrementExit` error, you look it up
-on the list and then you can use the exit code:
+on the list, and then you can use the exit code:
 
 ```sh
 cz -nr 21 bump
@@ -403,7 +403,7 @@ These are used in:
 
 - `cz bump`: Find previous release tag (exact match) and generate new tag.
 - Find previous release tags in `cz changelog`.
-  - If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match.
+  - If `--incremental`: Using the latest version found in the changelog, scan existing Git tags with 89\% similarity match.
   - `--rev-range` is converted to Git tag names with `tag_format` before searching Git history.
 - If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags:
   - If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix)
@@ -477,7 +477,7 @@ in a line containing the `version` substring.
 
 Template used to specify the commit message generated when bumping.
 
-defaults to: `bump: version $current_version → $new_version`
+Defaults to: `bump: version $current_version → $new_version`
 
 | Variable           | Description                         |
 | ------------------ | ----------------------------------- |
@@ -499,7 +499,7 @@ bump_message = "release $current_version → $new_version [skip-ci]"
 
 When set to `true` the changelog is always updated incrementally when running `cz bump`, so the user does not have to provide the `--changelog` flag every time.
 
-defaults to: `false`
+Defaults to: `false`
 
 ```toml
 [tool.commitizen]
diff --git a/docs/config.md b/docs/config.md
index b6dd794fb3..1ea02d5f67 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -59,7 +59,7 @@ Default: `[ ]`
 
 Legacy git tag formats, useful for old projects that changed tag format.
 Tags matching those formats will be recognized as version tags and be included in the changelog.
-Each entry use the the syntax as [`tag_format`](#tag_format). [Read more][tag_format]
+Each entry uses the syntax as [`tag_format`](#tag_format). [Read more][tag_format]
 
 ### `ignored_tag_formats`
 
@@ -68,7 +68,7 @@ Type: `list`
 Default: `[ ]`
 
 Tags matching those formats will be totally ignored and won't raise a warning.
-Each entry use the the syntax as [`tag_format`](#tag_format) with the addition of `*`
+Each entry uses the syntax as [`tag_format`](#tag_format) with the addition of `*`
 that will match everything (non-greedy). [Read more][tag_format]
 
 ### `update_changelog_on_bump`
@@ -101,7 +101,7 @@ Type: `str`
 
 Default: `None`
 
-Create custom commit message, useful to skip ci. [Read more][bump_message]
+Create custom commit message, useful to skip CI. [Read more][bump_message]
 
 ### `retry_after_failure`
 
@@ -117,7 +117,7 @@ Type: `bool`
 
 Default: `false`
 
-Disallow empty commit messages, useful in ci. [Read more][allow_abort]
+Disallow empty commit messages, useful in CI. [Read more][allow_abort]
 
 ### `allowed_prefixes`
 
@@ -195,7 +195,7 @@ Type: `bool`
 
 Default: `false`
 
-When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero]
+When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [Read more][major-version-zero]
 
 ### `prerelease_offset`
 
@@ -203,7 +203,7 @@ Type: `int`
 
 Default: `0`
 
-In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [prerelease-offset]
+In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [Read more][prerelease-offset]
 
 ### `pre_bump_hooks`
 
@@ -247,7 +247,7 @@ Provide extra variables to the changelog template. [Read more][template-customiz
 
 ## Configuration file
 
-### pyproject.toml, .cz.toml or cz.toml
+### `pyproject.toml`, `.cz.toml` or `cz.toml`
 
 Default and recommended configuration format for a project.
 For a **python** project, we recommend adding an entry to your `pyproject.toml`.
@@ -278,7 +278,7 @@ style = [
 ]
 ```
 
-### .cz.json or cz.json
+### `.cz.json` or `cz.json`
 
 Commitizen has support for JSON configuration. Recommended for `NodeJS` projects.
 
@@ -304,7 +304,7 @@ Commitizen has support for JSON configuration. Recommended for `NodeJS` projects
 }
 ```
 
-### .cz.yaml or cz.yaml
+### `.cz.yaml` or `cz.yaml`
 
 YAML configuration is supported by Commitizen. Recommended for `Go`, `ansible`, or even `helm` charts projects.
 
@@ -358,7 +358,7 @@ Commitizen provides some version providers for some well known formats:
 | `composer`   | Get and set version from `composer.json` `project.version` field                                                                                                                                                        |
 
 !!! note
-The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin.
+    The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin.
 
 An example in your `.cz.toml` or `cz.toml` would look like this:
 
@@ -408,10 +408,10 @@ setup(
 [tag_format]: commands/bump.md#tag_format
 [bump_message]: commands/bump.md#bump_message
 [major-version-zero]: commands/bump.md#-major-version-zero
-[prerelease-offset]: commands/bump.md#-prerelease_offset
+[prerelease-offset]: commands/bump.md#prerelease_offset
 [retry_after_failure]: commands/commit.md#retry
 [allow_abort]: commands/check.md#allow-abort
-[version-scheme]: commands/bump.md#version-scheme
+[version-scheme]: commands/bump.md#-version-scheme
 [pre_bump_hooks]: commands/bump.md#pre_bump_hooks
 [post_bump_hooks]: commands/bump.md#post_bump_hooks
 [allowed_prefixes]: commands/check.md#allowed-prefixes
diff --git a/docs/customization.md b/docs/customization.md
index cef03469e0..805173e900 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -490,7 +490,7 @@ Users can provide their own template from their current working directory (your
 - setting your template path as `template` configuration
 - giving your template path as `--template` parameter to `bump` and `changelog` commands
 
-!!! Note
+!!! note
     The path is relative to the current working directory, aka your project root most of the time.
 
 ### Template variables
@@ -514,7 +514,7 @@ Each `Change` has the following fields:
 | author | `str` | The commit author name |
 | author_email | `str` | The commit author email |
 
-!!! Note
+!!! note
     The field values depend on the customization class and/or the settings you provide
 
 The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case
diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md
index bcb3fda22c..4f50aaf4e7 100644
--- a/docs/tutorials/github_actions.md
+++ b/docs/tutorials/github_actions.md
@@ -41,14 +41,14 @@ jobs:
 
 Push to master and that's it.
 
-### Creating a github release
+### Creating a GitHub release
 
 You can modify the previous action.
 
 Add the variable `changelog_increment_filename` in the `commitizen-action`, specifying
 where to output the content of the changelog for the newly created version.
 
-And then add a step using a github action to create the release: `softprops/action-gh-release`
+And then add a step using a GitHub action to create the release: `softprops/action-gh-release`
 
 The commitizen action creates an env variable called `REVISION`, containing the
 newly created version.
diff --git a/docs/tutorials/gitlab_ci.md b/docs/tutorials/gitlab_ci.md
index 85b6a615c3..6f6d53a57e 100644
--- a/docs/tutorials/gitlab_ci.md
+++ b/docs/tutorials/gitlab_ci.md
@@ -12,9 +12,9 @@ _Goal_: Bump a new version every time that a change occurs on the `master` branc
 4. For simplification, we store the software version in a file called `VERSION`. You can use any file that you want as `commitizen` supports it.
 5. The commit message executed automatically by the `CI` must include `[skip-ci]` in the message; otherwise, the process will generate a loop. You can define the message structure in [commitizen](../commands/bump.md) as well.
 
-### Gitlab Configuration
+### GitLab Configuration
 
-To be able to change files and push new changes with `Gitlab CI` runners, we need to have a `ssh` key and configure a git user.
+To be able to change files and push new changes with `GitLab CI` runners, we need to have a `ssh` key and configure a git user.
 
 First, let's create a `ssh key`. The only requirement is to create it without a passphrase:
 
diff --git a/docs/tutorials/jenkins_pipeline.md b/docs/tutorials/jenkins_pipeline.md
index fb87820c4c..2b9ad173d3 100644
--- a/docs/tutorials/jenkins_pipeline.md
+++ b/docs/tutorials/jenkins_pipeline.md
@@ -47,7 +47,7 @@ def useCz(String authorName = 'Jenkins CI Server', String authorEmail = 'your-je
 ```
 
 !!! warning
-    Using jenkins pipeline with any git plugin may require many different configurations,
+    Using jenkins pipeline with any git plugin may require many configurations,
     you'll have to tinker with it until your pipelines properly detects git events. Check your
     webhook in your git repository and check the "behaviors" and "build strategies" in
     your pipeline settings.
diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md
index 792c8c224f..434899f86f 100644
--- a/docs/tutorials/monorepo_guidance.md
+++ b/docs/tutorials/monorepo_guidance.md
@@ -56,9 +56,9 @@ In order to filter the correct commits for each component, you'll have to come u
 For example:
 
 - Trigger the pipeline based on the changed path, which can have some downsides, as you'll rely on the developer not including files from other files
-    - [github actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path`
+    - [GitHub actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path`
     - [Jenkins](https://www.jenkins.io/doc/book/pipeline/syntax/#built-in-conditions) uses `changeset`
-    - [Gitlab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes`
+    - [GitLab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes`
 - Filter certain pattern of the commit message (recommended)
 
 

From 71c2e914d477ac7e45a05eddb9ad511f4fe0c4ca Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 21:22:04 +0800
Subject: [PATCH 068/169] docs(pull_request_template): add broken link check to
 the checklist

---
 .github/pull_request_template.md | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index e28480e5b9..5686474709 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -25,6 +25,13 @@ Please fill in the following content to let us know better about this change.
 ### Documentation Changes
 
 - [ ] Run `poetry doc` locally to ensure the documentation pages renders correctly
+  - [ ] Check if there are any broken links in the documentation
+
+> When running `poetry doc`, any broken internal documentation links will be reported in the console output like this:
+>
+> ```text
+> INFO    -  Doc file 'config.md' contains a link 'commands/bump.md#-post_bump_hooks', but the doc 'commands/bump.md' does not contain an anchor '#-post_bump_hooks'.
+> ```
 
 ## Expected Behavior
 <!-- A clear and concise description of what you expected to happen -->

From 2a68e89aed582ef4d89c3d0799406f5cb6584c5a Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 21:27:41 +0800
Subject: [PATCH 069/169] docs(contributing): update codecov link

---
 docs/contributing.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/contributing.md b/docs/contributing.md
index 4ca12765aa..336f83352a 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -58,7 +58,7 @@ If you're a first-time contributor, you can check the issues with the [good firs
     - Follow the existing code style
 3. **Testing**
     - Run the full test suite: `poetry all`
-    - Ensure test coverage doesn't drop (we use [CodeCov](https://codecov.io/))
+    - Ensure test coverage doesn't drop (we use [CodeCov](https://app.codecov.io/gh/commitizen-tools/commitizen))
     - For documentation changes, run `poetry doc` to check for warnings/errors
 4. **Committing Changes**
     - Use commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/))

From 1c58b1c272c4ab9ee2ce93907280d5ac3bde10a6 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 21:44:23 +0800
Subject: [PATCH 070/169] docs(README): update project specific installation
 methods

---
 docs/README.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/docs/README.md b/docs/README.md
index ed1aaef2a1..e0c3fed747 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -108,6 +108,16 @@ poetry add commitizen --group dev
 poetry add commitizen --dev
 ```
 
+**Using uv:**
+```bash
+uv add commitizen
+```
+
+**Using pdm:**
+```bash
+pdm add -d commitizen
+```
+
 ### Basic Commands
 
 #### Initialize Commitizen

From 70af1a6dc947cb78d10de546d6441b89ccd5bcd8 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 15 May 2025 22:03:19 +0800
Subject: [PATCH 071/169] docs: capitalize commitizen for consistency

---
 docs/README.md                                |  2 +-
 docs/commands/bump.md                         | 26 +++++++++----------
 docs/commands/commit.md                       | 10 +++----
 docs/commands/init.md                         |  2 +-
 docs/commands/version.md                      |  2 +-
 docs/config.md                                |  6 ++---
 docs/contributing.md                          |  2 +-
 docs/customization.md                         | 12 ++++-----
 docs/exit_codes.md                            |  2 +-
 docs/external_links.md                        |  2 +-
 docs/faq.md                                   |  6 ++---
 docs/third-party-commitizen.md                |  4 +--
 docs/tutorials/auto_prepare_commit_message.md |  8 +++---
 docs/tutorials/github_actions.md              |  4 +--
 docs/tutorials/monorepo_guidance.md           |  2 +-
 docs/tutorials/writing_commits.md             |  2 +-
 16 files changed, 46 insertions(+), 46 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index e0c3fed747..9bb1555721 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -8,7 +8,7 @@
 [![Codecov](https://img.shields.io/codecov/c/github/commitizen-tools/commitizen.svg?style=flat-square)](https://codecov.io/gh/commitizen-tools/commitizen)
 [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?style=flat-square&logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
 
-![Using commitizen cli](images/demo.gif)
+![Using Commitizen cli](images/demo.gif)
 
 ---
 
diff --git a/docs/commands/bump.md b/docs/commands/bump.md
index 6eb5b55c54..ee3d77430a 100644
--- a/docs/commands/bump.md
+++ b/docs/commands/bump.md
@@ -116,7 +116,7 @@ Below are some examples that illustrate the difference in behavior:
 
 ### `--check-consistency`
 
-Check whether the versions defined in `version_files` and the version in commitizen
+Check whether the versions defined in `version_files` and the version in Commitizen
 configuration are consistent before bumping version.
 
 ```bash
@@ -148,7 +148,7 @@ from setuptools import setup
 setup(..., version="1.0.5", ...)
 ```
 
-If `--check-consistency` is used, commitizen will check whether the current version in `pyproject.toml`
+If `--check-consistency` is used, Commitizen will check whether the current version in `pyproject.toml`
 exists in all version_files and find out it does not exist in `setup.py` and fails.
 However, it will still update `pyproject.toml` and `src/__version__.py`.
 
@@ -174,11 +174,11 @@ If `--local-version` is used, it will bump only the local version `0.1.0` and ke
 
 ### `--annotated-tag`
 
-If `--annotated-tag` is used, commitizen will create annotated tags. Also available via configuration, in `pyproject.toml` or `.cz.toml`.
+If `--annotated-tag` is used, Commitizen will create annotated tags. It is also available via configuration, in `pyproject.toml` or `.cz.toml`.
 
 ### `--annotated-tag-message`
 
-If `--annotated-tag-message` is used, commitizen will create annotated tags with the given message.
+If `--annotated-tag-message` is used, Commitizen will create annotated tags with the given message.
 
 ### `--changelog-to-stdout`
 
@@ -332,11 +332,11 @@ cz bump --allow-no-commit 2.0.0
 
 ## Avoid raising errors
 
-Some situations from commitizen raise an exit code different than 0.
-If the error code is different than 0, any CI or script running commitizen might be interrupted.
+Some situations from Commitizen raise an exit code different from 0.
+If the error code is different from 0, any CI or script running Commitizen might be interrupted.
 
 If you have a special use case, where you don't want to raise one of this error codes, you can
-tell commitizen to not raise them.
+tell Commitizen to not raise them.
 
 ### Recommended use case
 
@@ -355,7 +355,7 @@ cz -nr 21 bump
 
 ### Easy way
 
-Check which error code was raised by commitizen by running in the terminal
+Check which error code was raised by Commitizen by running in the terminal
 
 ```sh
 echo $?
@@ -367,13 +367,13 @@ The output should be an integer like this
 3
 ```
 
-And then you can tell commitizen to ignore it:
+And then you can tell Commitizen to ignore it:
 
 ```sh
 cz --no-raise 3
 ```
 
-You can tell commitizen to skip more than one if needed:
+You can tell Commitizen to skip more than one if needed:
 
 ```sh
 cz --no-raise 3,4,5
@@ -510,7 +510,7 @@ update_changelog_on_bump = true
 
 ### `annotated_tag`
 
-When set to `true` commitizen will create annotated tags.
+When set to `true`, Commitizen will create annotated tags.
 
 ```toml
 [tool.commitizen]
@@ -521,7 +521,7 @@ annotated_tag = true
 
 ### `gpg_sign`
 
-When set to `true` commitizen will create gpg signed tags.
+When set to `true`, Commitizen will create gpg signed tags.
 
 ```toml
 [tool.commitizen]
@@ -532,7 +532,7 @@ gpg_sign = true
 
 ### `major_version_zero`
 
-When set to `true` commitizen will keep the major version at zero.
+When set to `true`, Commitizen will keep the major version at zero.
 Useful during the initial development stage of your project.
 
 Defaults to: `false`
diff --git a/docs/commands/commit.md b/docs/commands/commit.md
index ea033cc411..febaee3cf2 100644
--- a/docs/commands/commit.md
+++ b/docs/commands/commit.md
@@ -1,4 +1,4 @@
-![Using commitizen cli](../images/demo.gif)
+![Using Commitizen cli](../images/demo.gif)
 
 ## About
 
@@ -20,14 +20,14 @@ case for this is to [automatically prepare a commit message](../tutorials/auto_p
 
 ### git options
 
-`git` command options that are not implemented by commitizen can be use via the `--` syntax for the `commit` command.
-The syntax separates commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax:
+`git` command options that are not implemented by Commitizen can be use via the `--` syntax for the `commit` command.
+The syntax separates Commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax:
 ```sh
 cz commit <commitizen-args> -- <git-cli-args>
 
 # e.g., cz commit --dry-run -- -a -S
 ```
-For example, using the `-S` option on `git commit` to sign a commit is now commitizen compatible: `cz c -- -S`
+For example, using the `-S` option on `git commit` to sign a commit is now Commitizen compatible: `cz c -- -S`
 
 !!! note
     Deprecation warning: A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`.
@@ -37,7 +37,7 @@ For example, using the `-S` option on `git commit` to sign a commit is now commi
 
 You can use `cz commit --retry` to reuse the last commit message when the previous commit attempt failed.
 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
+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
diff --git a/docs/commands/init.md b/docs/commands/init.md
index 01e1db6be8..a799c44810 100644
--- a/docs/commands/init.md
+++ b/docs/commands/init.md
@@ -4,7 +4,7 @@
 
 ## Example
 
-To start using commitizen, the recommended approach is to run
+To start using Commitizen, the recommended approach is to run
 
 ```sh
 cz init
diff --git a/docs/commands/version.md b/docs/commands/version.md
index 9a8176b45f..4d2e6a0323 100644
--- a/docs/commands/version.md
+++ b/docs/commands/version.md
@@ -1,4 +1,4 @@
-Get the version of the installed commitizen or the current project (default: installed commitizen)
+Get the version of the installed Commitizen or the current project (default: installed commitizen)
 
 ## Usage
 
diff --git a/docs/config.md b/docs/config.md
index 1ea02d5f67..5ca2c5d788 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -187,7 +187,7 @@ Type: `bool`
 
 Default: `false`
 
-If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts]
+If enabled, Commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts]
 
 ### `major_version_zero`
 
@@ -341,14 +341,14 @@ commitizen:
 ## Version providers
 
 Commitizen can read and write version from different sources.
-By default, it uses the `commitizen` one which is using the `version` field from the commitizen settings.
+By default, it uses the `commitizen` one which is using the `version` field from the Commitizen settings.
 But you can use any `commitizen.provider` entrypoint as value for `version_provider`.
 
 Commitizen provides some version providers for some well known formats:
 
 | name         | description                                                                                                                                                                                                             |
 | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `commitizen` | Default version provider: Fetch and set version in commitizen config.                                                                                                                                                   |
+| `commitizen` | Default version provider: Fetch and set version in Commitizen config.                                                                                                                                                   |
 | `scm`        | Fetch the version from git and does not need to set it back                                                                                                                                                             |
 | `pep621`     | Get and set version from `pyproject.toml` `project.version` field                                                                                                                                                       |
 | `poetry`     | Get and set version from `pyproject.toml` `tool.poetry.version` field                                                                                                                                                   |
diff --git a/docs/contributing.md b/docs/contributing.md
index 336f83352a..e9e162d2df 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -61,7 +61,7 @@ If you're a first-time contributor, you can check the issues with the [good firs
     - Ensure test coverage doesn't drop (we use [CodeCov](https://app.codecov.io/gh/commitizen-tools/commitizen))
     - For documentation changes, run `poetry doc` to check for warnings/errors
 4. **Committing Changes**
-    - Use commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/))
+    - Use Commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/))
     - Example: `cz commit`
 5. **Documentation**
     - Update `docs/README.md` if needed
diff --git a/docs/customization.md b/docs/customization.md
index 805173e900..e97558a308 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -1,4 +1,4 @@
-Customizing commitizen is not hard at all.
+Customizing Commitizen is not hard at all.
 We have two different ways to do so.
 
 ## 1. Customize in configuration file
@@ -6,7 +6,7 @@ We have two different ways to do so.
 The basic steps are:
 
 1. Define your custom committing or bumping rules in the configuration file.
-2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running commitizen.
+2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running Commitizen.
 
 Example:
 
@@ -184,8 +184,8 @@ commitizen:
 
 #### Shortcut keys
 
-When the [`use_shortcuts`](config.md#settings) config option is enabled, commitizen can show and use keyboard shortcuts to select items from lists directly.
-For example, when using the `cz_conventional_commits` commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically.
+When the [`use_shortcuts`](config.md#settings) config option is enabled, Commitizen can show and use keyboard shortcuts to select items from lists directly.
+For example, when using the `cz_conventional_commits` Commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically.
 To specify keyboard shortcuts for your custom choices, provide the shortcut using the `key` parameter in dictionary form for each choice you would like to customize.
 
 ## 2. Customize through customizing a class
@@ -304,7 +304,7 @@ class StrangeCommitizen(BaseCommitizen):
     bump_map = {"break": "MAJOR", "new": "MINOR", "fix": "PATCH", "hotfix": "PATCH"}
 ```
 
-That's it, your commitizen now supports custom rules, and you can run.
+That's it, your Commitizen now supports custom rules, and you can run.
 
 ```bash
 cz -n cz_strange bump
@@ -451,7 +451,7 @@ Commitizen gives you the possibility to provide your own changelog template, by:
   - as `--template` parameter to both `bump` and `changelog` commands
 - either by providing a template with the same name as the default template
 
-By default, the template used is the `CHANGELOG.md.j2` file from the commitizen repository.
+By default, the template used is the `CHANGELOG.md.j2` file from the Commitizen repository.
 
 ### Providing a template with your customization class
 
diff --git a/docs/exit_codes.md b/docs/exit_codes.md
index af9cb83627..fd92961d38 100644
--- a/docs/exit_codes.md
+++ b/docs/exit_codes.md
@@ -20,7 +20,7 @@ These exit codes can be found in `commitizen/exceptions.py::ExitCode`.
 | NoCommitBackupError         | 10        | Commit back up file cannot be found                                                                         |
 | NothingToCommitError        | 11        | Nothing in staging to be committed                                                                          |
 | CustomError                 | 12        | `CzException` raised                                                                                        |
-| NoCommandFoundError         | 13        | No command found when running commitizen cli (e.g., `cz --debug`)                                           |
+| NoCommandFoundError         | 13        | No command found when running Commitizen cli (e.g., `cz --debug`)                                           |
 | InvalidCommitMessageError   | 14        | The commit message does not pass `cz check`                                                                 |
 | MissingConfigError          | 15        | Configuration missed for `cz_customize`                                                                     |
 | NoRevisionError             | 16        | No revision found                                                                                           |
diff --git a/docs/external_links.md b/docs/external_links.md
index 388bcc8dea..24e4127d86 100644
--- a/docs/external_links.md
+++ b/docs/external_links.md
@@ -1,4 +1,4 @@
-> If you have written over commitizen, make a PR and add the link here 💪
+> If you have written over Commitizen, make a PR and add the link here 💪
 
 ## Talks
 
diff --git a/docs/faq.md b/docs/faq.md
index 0302efd267..ceabac2e10 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -63,7 +63,7 @@ Where do they cross paths?
 
 If you are using conventional commits in your git history, then you could swap one with the other in theory.
 
-Regarding the name, [cz-js][cz-js] came first, they used the word commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen".
+Regarding the name, [cz-js][cz-js] came first, they used the word Commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen".
 
 [cz-js]: https://github.com/commitizen/cz-cli
 
@@ -80,7 +80,7 @@ This error was caused by a Python bug on Windows. It's been fixed by [this PR](h
 
 More discussion can be found in issue [#318](https://github.com/commitizen-tools/commitizen/issues/318).
 
-## Why does commitizen not support CalVer?
+## Why does Commitizen not support CalVer?
 
 `commitizen` could support CalVer alongside SemVer, but in practice implementing CalVer
 creates numerous edge cases that are difficult to maintain ([#385]) and more generally,
@@ -117,7 +117,7 @@ New bumped tags will be in the new format but old ones will still work for:
 
 
 So given if you change from `myproject-$version` to `${version}` and then `v${version}`,
-your commitizen configuration will look like this:
+your Commitizen configuration will look like this:
 
 ```toml
 tag_format = "v${version}"
diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md
index e9eb822ed3..dc9b539c85 100644
--- a/docs/third-party-commitizen.md
+++ b/docs/third-party-commitizen.md
@@ -33,9 +33,9 @@ pip install conventional-JIRA
 
 ### [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/)
 
-This plugin extends the commitizen tools by:
+This plugin extends the Commitizen tools by:
 
-- requiring a JIRA issue id in the commit message
+- requiring a JIRA issue ID in the commit message
 - creating links to GitHub commits in the CHANGELOG.md
 - creating links to JIRA issues in the CHANGELOG.md
 
diff --git a/docs/tutorials/auto_prepare_commit_message.md b/docs/tutorials/auto_prepare_commit_message.md
index 7e8295b7c8..84ac62b689 100644
--- a/docs/tutorials/auto_prepare_commit_message.md
+++ b/docs/tutorials/auto_prepare_commit_message.md
@@ -2,7 +2,7 @@
 
 ## About
 
-It can be desirable to use commitizen for all types of commits (i.e. regular, merge,
+It can be desirable to use Commitizen for all types of commits (i.e. regular, merge,
 squash) so that the complete git history adheres to the commit message convention
 without ever having to call `cz commit`.
 
@@ -18,10 +18,10 @@ To automatically perform arbitrary cleanup steps after a successful commit you c
 > This hook is invoked by git-commit. It takes no parameters, and is invoked after a
 > commit is made.
 
-A combination of these two hooks allows for enforcing the usage of commitizen so that
-whenever a commit is about to be created, commitizen is used for creating the commit
+A combination of these two hooks allows for enforcing the usage of Commitizen so that
+whenever a commit is about to be created, Commitizen is used for creating the commit
 message. Running `git commit` or `git commit -m "..."` for example, would trigger
-commitizen and use the generated commit message for the commit.
+Commitizen and use the generated commit message for the commit.
 
 ## Installation
 
diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md
index 4f50aaf4e7..bf08eb3bc5 100644
--- a/docs/tutorials/github_actions.md
+++ b/docs/tutorials/github_actions.md
@@ -50,7 +50,7 @@ where to output the content of the changelog for the newly created version.
 
 And then add a step using a GitHub action to create the release: `softprops/action-gh-release`
 
-The commitizen action creates an env variable called `REVISION`, containing the
+Commitizen action creates an env variable called `REVISION`, containing the
 newly created version.
 
 ```yaml
@@ -119,7 +119,7 @@ jobs:
           ./scripts/publish
 ```
 
-Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish)
+Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and publish with your tools (twine, poetry, etc.). Check [Commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish)
 You can also use [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) to publish your package.
 
 Push the changes and that's it.
diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md
index 434899f86f..6f15a87247 100644
--- a/docs/tutorials/monorepo_guidance.md
+++ b/docs/tutorials/monorepo_guidance.md
@@ -1,4 +1,4 @@
-# Configuring commitizen in a monorepo
+# Configuring Commitizen in a monorepo
 
 This tutorial assumes the monorepo layout is designed with multiple components that can be released independently of each
 other, it also assumes that conventional commits with scopes are in use. Some suggested layouts:
diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md
index d1b2c6645d..7d9139929c 100644
--- a/docs/tutorials/writing_commits.md
+++ b/docs/tutorials/writing_commits.md
@@ -1,6 +1,6 @@
 For this project to work well in your pipeline, a commit convention must be followed.
 
-By default, commitizen uses the known [conventional commits][conventional_commits], but
+By default, Commitizen uses the known [conventional commits][conventional_commits], but
 you can create your own following the documentation information over at
 [customization][customization].
 

From f6280c88a14aabf30324a4d9901f4fee577debfc Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 18 May 2025 21:41:24 +0800
Subject: [PATCH 072/169] docs(README): update documentation site link text

---
 docs/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/README.md b/docs/README.md
index 9bb1555721..516b2a4863 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -12,7 +12,7 @@
 
 ---
 
-**Documentation:** [https://commitizen-tools.github.io/commitizen/](https://commitizen-tools.github.io/commitizen/)
+[**Commitizen Documentation Site**](https://commitizen-tools.github.io/commitizen/)
 
 ---
 

From ba1c9c0ce4cba699e61a2ab26d08d3159d0f3bdb Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 18 May 2025 21:46:08 +0800
Subject: [PATCH 073/169] docs(README): replace internal links with
 documentation site link

---
 docs/README.md | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index 516b2a4863..078d6da06e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -42,11 +42,11 @@ This standardization makes your commit history more readable and meaningful, whi
 ### Features
 
 - Command-line utility to create commits with your rules. Defaults: [Conventional commits][conventional_commits]
-- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](./commands/bump.md)
+- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](https://commitizen-tools.github.io/commitizen/commands/bump/)
 - Generate a changelog using [Keep a changelog][keepchangelog]
 - Update your project's version files automatically
 - Display information about your commit rules (commands: schema, example, info)
-- Create your own set of rules and publish them to pip. Read more on [Customization](./customization.md)
+- Create your own set of rules and publish them to pip. Read more on [Customization](https://commitizen-tools.github.io/commitizen/customization/)
 
 ## Getting Started
 
@@ -168,11 +168,11 @@ This command:
 - Updates version files
 
 You can customize:
-- [Version files](./commands/bump.md#version_files)
-- [Version scheme](./commands/bump.md#version_scheme)
-- [Version provider](./config.md#version-providers)
+- [Version files](https://commitizen-tools.github.io/commitizen/commands/bump/#version_files)
+- [Version scheme](https://commitizen-tools.github.io/commitizen/commands/bump/#version_scheme)
+- [Version provider](https://commitizen-tools.github.io/commitizen/config/#version-providers)
 
-For all available options, see the [bump command documentation](./commands/bump.md).
+For all available options, see the [bump command documentation](https://commitizen-tools.github.io/commitizen/commands/bump/).
 
 ### Advanced Usage
 
@@ -219,7 +219,7 @@ pre-commit install --hook-type commit-msg --hook-type pre-push
 > pre-commit autoupdate
 > ```
 
-For more details about commit validation, see the [check command documentation](commands/check.md).
+For more details about commit validation, see the [check command documentation](https://commitizen-tools.github.io/commitizen/commands/check/).
 
 ## Help & Reference
 
@@ -243,9 +243,9 @@ Commitizen provides a comprehensive CLI with various commands. Here's the comple
 ### Additional Resources
 
 - [Conventional Commits Specification][conventional_commits]
-- [Exit Codes Reference](./exit_codes.md)
-- [Configuration Guide](./config.md)
-- [Command Documentation](./commands/init.md)
+- [Exit Codes Reference](https://commitizen-tools.github.io/commitizen/exit_codes/)
+- [Configuration Guide](https://commitizen-tools.github.io/commitizen/config/)
+- [Command Documentation](https://commitizen-tools.github.io/commitizen/commands/init/)
 
 ### Getting Help
 
@@ -257,7 +257,7 @@ cz bump --help
 cz changelog --help
 ```
 
-For more detailed documentation, visit our [documentation site](https://commitizen-tools.github.io/commitizen/).
+For more details, visit our [documentation site](https://commitizen-tools.github.io/commitizen/).
 
 ## Setting up bash completion
 

From bb1539deb206d58f7b94c3b8ece268f8f34f287d Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 18 May 2025 21:57:02 +0800
Subject: [PATCH 074/169] docs(README): paraphase features

---
 docs/README.md | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/docs/README.md b/docs/README.md
index 078d6da06e..f730c17837 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -41,12 +41,13 @@ This standardization makes your commit history more readable and meaningful, whi
 
 ### Features
 
-- Command-line utility to create commits with your rules. Defaults: [Conventional commits][conventional_commits]
-- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](https://commitizen-tools.github.io/commitizen/commands/bump/)
-- Generate a changelog using [Keep a changelog][keepchangelog]
-- Update your project's version files automatically
-- Display information about your commit rules (commands: schema, example, info)
-- Create your own set of rules and publish them to pip. Read more on [Customization](https://commitizen-tools.github.io/commitizen/customization/)
+- Interactive CLI for standardized commits with default [Conventional Commits][conventional_commits] support
+- Intelligent [version bumping](https://commitizen-tools.github.io/commitizen/commands/bump/) using [Semantic Versioning][semver]
+- Automatic [keep a changelog][keepchangelog] generation
+- Built-in commit validation with pre-commit hooks
+- [Customizable](https://commitizen-tools.github.io/commitizen/customization/) commit rules and templates
+- Multi-format version file support
+- Custom rules and plugins via pip
 
 ## Getting Started
 

From 8e65b88288328dd794e9f8046f5b1164c9afb569 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Wed, 28 May 2025 01:27:30 +0000
Subject: [PATCH 075/169] docs(cli/screenshots): update CLI screenshots

[skip ci]
---
 docs/images/cli_help/cz___help.svg | 172 +++++++++++++++--------------
 1 file changed, 88 insertions(+), 84 deletions(-)

diff --git a/docs/images/cli_help/cz___help.svg b/docs/images/cli_help/cz___help.svg
index 098e7df70d..d580cfe619 100644
--- a/docs/images/cli_help/cz___help.svg
+++ b/docs/images/cli_help/cz___help.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 994 928.4" xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 994 952.8" xmlns="http://www.w3.org/2000/svg">
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -19,183 +19,187 @@
         font-weight: 700;
     }
 
-    .terminal-2205183093-matrix {
+    .terminal-1389600277-matrix {
         font-family: Fira Code, monospace;
         font-size: 20px;
         line-height: 24.4px;
         font-variant-east-asian: full-width;
     }
 
-    .terminal-2205183093-title {
+    .terminal-1389600277-title {
         font-size: 18px;
         font-weight: bold;
         font-family: arial;
     }
 
-    .terminal-2205183093-r1 { fill: #c5c8c6 }
-.terminal-2205183093-r2 { fill: #c5c8c6;font-weight: bold }
-.terminal-2205183093-r3 { fill: #d0b344 }
-.terminal-2205183093-r4 { fill: #1984e9;text-decoration: underline; }
-.terminal-2205183093-r5 { fill: #68a0b3;font-weight: bold }
+    .terminal-1389600277-r1 { fill: #c5c8c6 }
+.terminal-1389600277-r2 { fill: #c5c8c6;font-weight: bold }
+.terminal-1389600277-r3 { fill: #d0b344 }
+.terminal-1389600277-r4 { fill: #1984e9;text-decoration: underline; }
+.terminal-1389600277-r5 { fill: #68a0b3;font-weight: bold }
     </style>
 
     <defs>
-    <clipPath id="terminal-2205183093-clip-terminal">
-      <rect x="0" y="0" width="975.0" height="877.4" />
+    <clipPath id="terminal-1389600277-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="901.8" />
     </clipPath>
-    <clipPath id="terminal-2205183093-line-0">
+    <clipPath id="terminal-1389600277-line-0">
     <rect x="0" y="1.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-1">
+<clipPath id="terminal-1389600277-line-1">
     <rect x="0" y="25.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-2">
+<clipPath id="terminal-1389600277-line-2">
     <rect x="0" y="50.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-3">
+<clipPath id="terminal-1389600277-line-3">
     <rect x="0" y="74.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-4">
+<clipPath id="terminal-1389600277-line-4">
     <rect x="0" y="99.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-5">
+<clipPath id="terminal-1389600277-line-5">
     <rect x="0" y="123.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-6">
+<clipPath id="terminal-1389600277-line-6">
     <rect x="0" y="147.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-7">
+<clipPath id="terminal-1389600277-line-7">
     <rect x="0" y="172.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-8">
+<clipPath id="terminal-1389600277-line-8">
     <rect x="0" y="196.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-9">
+<clipPath id="terminal-1389600277-line-9">
     <rect x="0" y="221.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-10">
+<clipPath id="terminal-1389600277-line-10">
     <rect x="0" y="245.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-11">
+<clipPath id="terminal-1389600277-line-11">
     <rect x="0" y="269.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-12">
+<clipPath id="terminal-1389600277-line-12">
     <rect x="0" y="294.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-13">
+<clipPath id="terminal-1389600277-line-13">
     <rect x="0" y="318.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-14">
+<clipPath id="terminal-1389600277-line-14">
     <rect x="0" y="343.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-15">
+<clipPath id="terminal-1389600277-line-15">
     <rect x="0" y="367.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-16">
+<clipPath id="terminal-1389600277-line-16">
     <rect x="0" y="391.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-17">
+<clipPath id="terminal-1389600277-line-17">
     <rect x="0" y="416.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-18">
+<clipPath id="terminal-1389600277-line-18">
     <rect x="0" y="440.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-19">
+<clipPath id="terminal-1389600277-line-19">
     <rect x="0" y="465.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-20">
+<clipPath id="terminal-1389600277-line-20">
     <rect x="0" y="489.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-21">
+<clipPath id="terminal-1389600277-line-21">
     <rect x="0" y="513.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-22">
+<clipPath id="terminal-1389600277-line-22">
     <rect x="0" y="538.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-23">
+<clipPath id="terminal-1389600277-line-23">
     <rect x="0" y="562.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-24">
+<clipPath id="terminal-1389600277-line-24">
     <rect x="0" y="587.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-25">
+<clipPath id="terminal-1389600277-line-25">
     <rect x="0" y="611.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-26">
+<clipPath id="terminal-1389600277-line-26">
     <rect x="0" y="635.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-27">
+<clipPath id="terminal-1389600277-line-27">
     <rect x="0" y="660.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-28">
+<clipPath id="terminal-1389600277-line-28">
     <rect x="0" y="684.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-29">
+<clipPath id="terminal-1389600277-line-29">
     <rect x="0" y="709.1" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-30">
+<clipPath id="terminal-1389600277-line-30">
     <rect x="0" y="733.5" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-31">
+<clipPath id="terminal-1389600277-line-31">
     <rect x="0" y="757.9" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-32">
+<clipPath id="terminal-1389600277-line-32">
     <rect x="0" y="782.3" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-33">
+<clipPath id="terminal-1389600277-line-33">
     <rect x="0" y="806.7" width="976" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-2205183093-line-34">
+<clipPath id="terminal-1389600277-line-34">
     <rect x="0" y="831.1" width="976" height="24.65"/>
             </clipPath>
+<clipPath id="terminal-1389600277-line-35">
+    <rect x="0" y="855.5" width="976" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="926.4" rx="8"/>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="950.8" rx="8"/>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
             <circle cx="44" cy="0" r="7" fill="#28c840"/>
             </g>
         
-    <g transform="translate(9, 41)" clip-path="url(#terminal-2205183093-clip-terminal)">
+    <g transform="translate(9, 41)" clip-path="url(#terminal-1389600277-clip-terminal)">
     
-    <g class="terminal-2205183093-matrix">
-    <text class="terminal-2205183093-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-2205183093-line-0)">$&#160;cz&#160;--help</text><text class="terminal-2205183093-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2205183093-line-0)">
-</text><text class="terminal-2205183093-r1" x="0" y="44.4" textLength="122" clip-path="url(#terminal-2205183093-line-1)">usage:&#160;cz&#160;</text><text class="terminal-2205183093-r2" x="122" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="134.2" y="44.4" textLength="24.4" clip-path="url(#terminal-2205183093-line-1)">-h</text><text class="terminal-2205183093-r2" x="158.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="195.2" y="44.4" textLength="183" clip-path="url(#terminal-2205183093-line-1)">--config&#160;CONFIG</text><text class="terminal-2205183093-r2" x="378.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="402.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="414.8" y="44.4" textLength="85.4" clip-path="url(#terminal-2205183093-line-1)">--debug</text><text class="terminal-2205183093-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="536.8" y="44.4" textLength="85.4" clip-path="url(#terminal-2205183093-line-1)">-n&#160;NAME</text><text class="terminal-2205183093-r2" x="622.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="646.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="658.8" y="44.4" textLength="146.4" clip-path="url(#terminal-2205183093-line-1)">-nr&#160;NO_RAISE</text><text class="terminal-2205183093-r2" x="805.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">
-</text><text class="terminal-2205183093-r2" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-2)">{</text><text class="terminal-2205183093-r1" x="134.2" y="68.8" textLength="829.6" clip-path="url(#terminal-2205183093-line-2)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-2205183093-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-2)">}</text><text class="terminal-2205183093-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-2)">
-</text><text class="terminal-2205183093-r3" x="0" y="93.2" textLength="36.6" clip-path="url(#terminal-2205183093-line-3)">...</text><text class="terminal-2205183093-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-3)">
-</text><text class="terminal-2205183093-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-4)">
-</text><text class="terminal-2205183093-r1" x="0" y="142" textLength="707.6" clip-path="url(#terminal-2205183093-line-5)">Commitizen&#160;is&#160;a&#160;cli&#160;tool&#160;to&#160;generate&#160;conventional&#160;commits.</text><text class="terminal-2205183093-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2205183093-line-5)">
-</text><text class="terminal-2205183093-r1" x="0" y="166.4" textLength="524.6" clip-path="url(#terminal-2205183093-line-6)">For&#160;more&#160;information&#160;about&#160;the&#160;topic&#160;go&#160;to&#160;</text><text class="terminal-2205183093-r4" x="524.6" y="166.4" textLength="390.4" clip-path="url(#terminal-2205183093-line-6)">https://conventionalcommits.org/</text><text class="terminal-2205183093-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-6)">
-</text><text class="terminal-2205183093-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-7)">
-</text><text class="terminal-2205183093-r1" x="0" y="215.2" textLength="97.6" clip-path="url(#terminal-2205183093-line-8)">options:</text><text class="terminal-2205183093-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-8)">
-</text><text class="terminal-2205183093-r1" x="0" y="239.6" textLength="671" clip-path="url(#terminal-2205183093-line-9)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-2205183093-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-9)">
-</text><text class="terminal-2205183093-r1" x="0" y="264" textLength="658.8" clip-path="url(#terminal-2205183093-line-10)">&#160;&#160;--config&#160;CONFIG&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;path&#160;of&#160;configuration&#160;file</text><text class="terminal-2205183093-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2205183093-line-10)">
-</text><text class="terminal-2205183093-r1" x="0" y="288.4" textLength="463.6" clip-path="url(#terminal-2205183093-line-11)">&#160;&#160;--debug&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;use&#160;debug&#160;mode</text><text class="terminal-2205183093-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-11)">
-</text><text class="terminal-2205183093-r1" x="0" y="312.8" textLength="597.8" clip-path="url(#terminal-2205183093-line-12)">&#160;&#160;-n,&#160;--name&#160;NAME&#160;&#160;&#160;&#160;&#160;&#160;&#160;use&#160;the&#160;given&#160;commitizen&#160;</text><text class="terminal-2205183093-r2" x="597.8" y="312.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-12)">(</text><text class="terminal-2205183093-r1" x="610" y="312.8" textLength="97.6" clip-path="url(#terminal-2205183093-line-12)">default:</text><text class="terminal-2205183093-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-12)">
-</text><text class="terminal-2205183093-r1" x="0" y="337.2" textLength="573.4" clip-path="url(#terminal-2205183093-line-13)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cz_conventional_commits</text><text class="terminal-2205183093-r2" x="573.4" y="337.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-13)">)</text><text class="terminal-2205183093-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-13)">
-</text><text class="terminal-2205183093-r1" x="0" y="361.6" textLength="317.2" clip-path="url(#terminal-2205183093-line-14)">&#160;&#160;-nr,&#160;--no-raise&#160;NO_RAISE</text><text class="terminal-2205183093-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-14)">
-</text><text class="terminal-2205183093-r1" x="0" y="386" textLength="902.8" clip-path="url(#terminal-2205183093-line-15)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;comma&#160;separated&#160;error&#160;codes&#160;that&#160;won&#x27;t&#160;rise&#160;error,</text><text class="terminal-2205183093-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2205183093-line-15)">
-</text><text class="terminal-2205183093-r1" x="0" y="410.4" textLength="439.2" clip-path="url(#terminal-2205183093-line-16)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;e.g:&#160;cz&#160;-nr&#160;</text><text class="terminal-2205183093-r5" x="439.2" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">1</text><text class="terminal-2205183093-r1" x="451.4" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">,</text><text class="terminal-2205183093-r5" x="463.6" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">2</text><text class="terminal-2205183093-r1" x="475.8" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">,</text><text class="terminal-2205183093-r5" x="488" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">3</text><text class="terminal-2205183093-r1" x="500.2" y="410.4" textLength="231.8" clip-path="url(#terminal-2205183093-line-16)">&#160;bump.&#160;See&#160;codes&#160;at</text><text class="terminal-2205183093-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">
-</text><text class="terminal-2205183093-r4" x="292.8" y="434.8" textLength="231.8" clip-path="url(#terminal-2205183093-line-17)">https://commitizen-</text><text class="terminal-2205183093-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-17)">
-</text><text class="terminal-2205183093-r1" x="0" y="459.2" textLength="756.4" clip-path="url(#terminal-2205183093-line-18)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;tools.github.io/commitizen/exit_codes/</text><text class="terminal-2205183093-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-18)">
-</text><text class="terminal-2205183093-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-19)">
-</text><text class="terminal-2205183093-r1" x="0" y="508" textLength="109.8" clip-path="url(#terminal-2205183093-line-20)">commands:</text><text class="terminal-2205183093-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2205183093-line-20)">
-</text><text class="terminal-2205183093-r2" x="24.4" y="532.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-21)">{</text><text class="terminal-2205183093-r1" x="36.6" y="532.4" textLength="829.6" clip-path="url(#terminal-2205183093-line-21)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-2205183093-r2" x="866.2" y="532.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-21)">}</text><text class="terminal-2205183093-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-21)">
-</text><text class="terminal-2205183093-r1" x="0" y="556.8" textLength="646.6" clip-path="url(#terminal-2205183093-line-22)">&#160;&#160;&#160;&#160;init&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;init&#160;commitizen&#160;configuration</text><text class="terminal-2205183093-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-22)">
-</text><text class="terminal-2205183093-r1" x="0" y="581.2" textLength="134.2" clip-path="url(#terminal-2205183093-line-23)">&#160;&#160;&#160;&#160;commit&#160;</text><text class="terminal-2205183093-r2" x="134.2" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)">(</text><text class="terminal-2205183093-r1" x="146.4" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)">c</text><text class="terminal-2205183093-r2" x="158.6" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)">)</text><text class="terminal-2205183093-r1" x="170.8" y="581.2" textLength="329.4" clip-path="url(#terminal-2205183093-line-23)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;create&#160;new&#160;commit</text><text class="terminal-2205183093-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)">
-</text><text class="terminal-2205183093-r1" x="0" y="605.6" textLength="610" clip-path="url(#terminal-2205183093-line-24)">&#160;&#160;&#160;&#160;ls&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;available&#160;commitizens</text><text class="terminal-2205183093-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-24)">
-</text><text class="terminal-2205183093-r1" x="0" y="630" textLength="524.6" clip-path="url(#terminal-2205183093-line-25)">&#160;&#160;&#160;&#160;example&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;commit&#160;example</text><text class="terminal-2205183093-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-2205183093-line-25)">
-</text><text class="terminal-2205183093-r1" x="0" y="654.4" textLength="646.6" clip-path="url(#terminal-2205183093-line-26)">&#160;&#160;&#160;&#160;info&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;information&#160;about&#160;the&#160;cz</text><text class="terminal-2205183093-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-26)">
-</text><text class="terminal-2205183093-r1" x="0" y="678.8" textLength="512.4" clip-path="url(#terminal-2205183093-line-27)">&#160;&#160;&#160;&#160;schema&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;commit&#160;schema</text><text class="terminal-2205183093-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-27)">
-</text><text class="terminal-2205183093-r1" x="0" y="703.2" textLength="805.2" clip-path="url(#terminal-2205183093-line-28)">&#160;&#160;&#160;&#160;bump&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;bump&#160;semantic&#160;version&#160;based&#160;on&#160;the&#160;git&#160;log</text><text class="terminal-2205183093-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-28)">
-</text><text class="terminal-2205183093-r1" x="0" y="727.6" textLength="170.8" clip-path="url(#terminal-2205183093-line-29)">&#160;&#160;&#160;&#160;changelog&#160;</text><text class="terminal-2205183093-r2" x="170.8" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)">(</text><text class="terminal-2205183093-r1" x="183" y="727.6" textLength="24.4" clip-path="url(#terminal-2205183093-line-29)">ch</text><text class="terminal-2205183093-r2" x="207.4" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)">)</text><text class="terminal-2205183093-r1" x="219.6" y="727.6" textLength="305" clip-path="url(#terminal-2205183093-line-29)">&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;changelog&#160;</text><text class="terminal-2205183093-r2" x="524.6" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)">(</text><text class="terminal-2205183093-r1" x="536.8" y="727.6" textLength="329.4" clip-path="url(#terminal-2205183093-line-29)">note&#160;that&#160;it&#160;will&#160;overwrite</text><text class="terminal-2205183093-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)">
-</text><text class="terminal-2205183093-r1" x="0" y="752" textLength="451.4" clip-path="url(#terminal-2205183093-line-30)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;existing&#160;file</text><text class="terminal-2205183093-r2" x="451.4" y="752" textLength="12.2" clip-path="url(#terminal-2205183093-line-30)">)</text><text class="terminal-2205183093-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-2205183093-line-30)">
-</text><text class="terminal-2205183093-r1" x="0" y="776.4" textLength="951.6" clip-path="url(#terminal-2205183093-line-31)">&#160;&#160;&#160;&#160;check&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;validates&#160;that&#160;a&#160;commit&#160;message&#160;matches&#160;the&#160;commitizen</text><text class="terminal-2205183093-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-31)">
-</text><text class="terminal-2205183093-r1" x="0" y="800.8" textLength="366" clip-path="url(#terminal-2205183093-line-32)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;schema</text><text class="terminal-2205183093-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-32)">
-</text><text class="terminal-2205183093-r1" x="0" y="825.2" textLength="902.8" clip-path="url(#terminal-2205183093-line-33)">&#160;&#160;&#160;&#160;version&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;get&#160;the&#160;version&#160;of&#160;the&#160;installed&#160;commitizen&#160;or&#160;the</text><text class="terminal-2205183093-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-33)">
-</text><text class="terminal-2205183093-r1" x="0" y="849.6" textLength="488" clip-path="url(#terminal-2205183093-line-34)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;current&#160;project&#160;</text><text class="terminal-2205183093-r2" x="488" y="849.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-34)">(</text><text class="terminal-2205183093-r1" x="500.2" y="849.6" textLength="353.8" clip-path="url(#terminal-2205183093-line-34)">default:&#160;installed&#160;commitizen</text><text class="terminal-2205183093-r2" x="854" y="849.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-34)">)</text><text class="terminal-2205183093-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-34)">
-</text><text class="terminal-2205183093-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-2205183093-line-35)">
+    <g class="terminal-1389600277-matrix">
+    <text class="terminal-1389600277-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-1389600277-line-0)">$&#160;cz&#160;--help</text><text class="terminal-1389600277-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1389600277-line-0)">
+</text><text class="terminal-1389600277-r1" x="0" y="44.4" textLength="122" clip-path="url(#terminal-1389600277-line-1)">usage:&#160;cz&#160;</text><text class="terminal-1389600277-r2" x="122" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="134.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1389600277-line-1)">-h</text><text class="terminal-1389600277-r2" x="158.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="195.2" y="44.4" textLength="183" clip-path="url(#terminal-1389600277-line-1)">--config&#160;CONFIG</text><text class="terminal-1389600277-r2" x="378.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="402.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="414.8" y="44.4" textLength="85.4" clip-path="url(#terminal-1389600277-line-1)">--debug</text><text class="terminal-1389600277-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="536.8" y="44.4" textLength="85.4" clip-path="url(#terminal-1389600277-line-1)">-n&#160;NAME</text><text class="terminal-1389600277-r2" x="622.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="646.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="658.8" y="44.4" textLength="146.4" clip-path="url(#terminal-1389600277-line-1)">-nr&#160;NO_RAISE</text><text class="terminal-1389600277-r2" x="805.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">
+</text><text class="terminal-1389600277-r2" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">{</text><text class="terminal-1389600277-r1" x="134.2" y="68.8" textLength="829.6" clip-path="url(#terminal-1389600277-line-2)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-1389600277-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">}</text><text class="terminal-1389600277-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">
+</text><text class="terminal-1389600277-r3" x="0" y="93.2" textLength="36.6" clip-path="url(#terminal-1389600277-line-3)">...</text><text class="terminal-1389600277-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-3)">
+</text><text class="terminal-1389600277-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-4)">
+</text><text class="terminal-1389600277-r1" x="0" y="142" textLength="915" clip-path="url(#terminal-1389600277-line-5)">Commitizen&#160;is&#160;a&#160;powerful&#160;release&#160;management&#160;tool&#160;that&#160;helps&#160;teams&#160;maintain&#160;</text><text class="terminal-1389600277-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1389600277-line-5)">
+</text><text class="terminal-1389600277-r1" x="0" y="166.4" textLength="951.6" clip-path="url(#terminal-1389600277-line-6)">consistent&#160;and&#160;meaningful&#160;commit&#160;messages&#160;while&#160;automating&#160;version&#160;management.</text><text class="terminal-1389600277-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-6)">
+</text><text class="terminal-1389600277-r1" x="0" y="190.8" textLength="427" clip-path="url(#terminal-1389600277-line-7)">For&#160;more&#160;information,&#160;please&#160;visit&#160;</text><text class="terminal-1389600277-r4" x="427" y="190.8" textLength="549" clip-path="url(#terminal-1389600277-line-7)">https://commitizen-tools.github.io/commitizen</text><text class="terminal-1389600277-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-7)">
+</text><text class="terminal-1389600277-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-8)">
+</text><text class="terminal-1389600277-r1" x="0" y="239.6" textLength="97.6" clip-path="url(#terminal-1389600277-line-9)">options:</text><text class="terminal-1389600277-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-9)">
+</text><text class="terminal-1389600277-r1" x="0" y="264" textLength="671" clip-path="url(#terminal-1389600277-line-10)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1389600277-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1389600277-line-10)">
+</text><text class="terminal-1389600277-r1" x="0" y="288.4" textLength="658.8" clip-path="url(#terminal-1389600277-line-11)">&#160;&#160;--config&#160;CONFIG&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;path&#160;of&#160;configuration&#160;file</text><text class="terminal-1389600277-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-11)">
+</text><text class="terminal-1389600277-r1" x="0" y="312.8" textLength="463.6" clip-path="url(#terminal-1389600277-line-12)">&#160;&#160;--debug&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;use&#160;debug&#160;mode</text><text class="terminal-1389600277-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-12)">
+</text><text class="terminal-1389600277-r1" x="0" y="337.2" textLength="597.8" clip-path="url(#terminal-1389600277-line-13)">&#160;&#160;-n,&#160;--name&#160;NAME&#160;&#160;&#160;&#160;&#160;&#160;&#160;use&#160;the&#160;given&#160;commitizen&#160;</text><text class="terminal-1389600277-r2" x="597.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-13)">(</text><text class="terminal-1389600277-r1" x="610" y="337.2" textLength="97.6" clip-path="url(#terminal-1389600277-line-13)">default:</text><text class="terminal-1389600277-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-13)">
+</text><text class="terminal-1389600277-r1" x="0" y="361.6" textLength="573.4" clip-path="url(#terminal-1389600277-line-14)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cz_conventional_commits</text><text class="terminal-1389600277-r2" x="573.4" y="361.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-14)">)</text><text class="terminal-1389600277-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-14)">
+</text><text class="terminal-1389600277-r1" x="0" y="386" textLength="317.2" clip-path="url(#terminal-1389600277-line-15)">&#160;&#160;-nr,&#160;--no-raise&#160;NO_RAISE</text><text class="terminal-1389600277-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1389600277-line-15)">
+</text><text class="terminal-1389600277-r1" x="0" y="410.4" textLength="915" clip-path="url(#terminal-1389600277-line-16)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;comma&#160;separated&#160;error&#160;codes&#160;that&#160;won&#x27;t&#160;raise&#160;error,</text><text class="terminal-1389600277-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-16)">
+</text><text class="terminal-1389600277-r1" x="0" y="434.8" textLength="439.2" clip-path="url(#terminal-1389600277-line-17)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;e.g:&#160;cz&#160;-nr&#160;</text><text class="terminal-1389600277-r5" x="439.2" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">1</text><text class="terminal-1389600277-r1" x="451.4" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">,</text><text class="terminal-1389600277-r5" x="463.6" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">2</text><text class="terminal-1389600277-r1" x="475.8" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">,</text><text class="terminal-1389600277-r5" x="488" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">3</text><text class="terminal-1389600277-r1" x="500.2" y="434.8" textLength="231.8" clip-path="url(#terminal-1389600277-line-17)">&#160;bump.&#160;See&#160;codes&#160;at</text><text class="terminal-1389600277-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">
+</text><text class="terminal-1389600277-r4" x="292.8" y="459.2" textLength="231.8" clip-path="url(#terminal-1389600277-line-18)">https://commitizen-</text><text class="terminal-1389600277-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-18)">
+</text><text class="terminal-1389600277-r1" x="0" y="483.6" textLength="756.4" clip-path="url(#terminal-1389600277-line-19)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;tools.github.io/commitizen/exit_codes/</text><text class="terminal-1389600277-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-19)">
+</text><text class="terminal-1389600277-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1389600277-line-20)">
+</text><text class="terminal-1389600277-r1" x="0" y="532.4" textLength="109.8" clip-path="url(#terminal-1389600277-line-21)">commands:</text><text class="terminal-1389600277-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-21)">
+</text><text class="terminal-1389600277-r2" x="24.4" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">{</text><text class="terminal-1389600277-r1" x="36.6" y="556.8" textLength="829.6" clip-path="url(#terminal-1389600277-line-22)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-1389600277-r2" x="866.2" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">}</text><text class="terminal-1389600277-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">
+</text><text class="terminal-1389600277-r1" x="0" y="581.2" textLength="646.6" clip-path="url(#terminal-1389600277-line-23)">&#160;&#160;&#160;&#160;init&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;init&#160;commitizen&#160;configuration</text><text class="terminal-1389600277-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-23)">
+</text><text class="terminal-1389600277-r1" x="0" y="605.6" textLength="134.2" clip-path="url(#terminal-1389600277-line-24)">&#160;&#160;&#160;&#160;commit&#160;</text><text class="terminal-1389600277-r2" x="134.2" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">(</text><text class="terminal-1389600277-r1" x="146.4" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">c</text><text class="terminal-1389600277-r2" x="158.6" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">)</text><text class="terminal-1389600277-r1" x="170.8" y="605.6" textLength="329.4" clip-path="url(#terminal-1389600277-line-24)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;create&#160;new&#160;commit</text><text class="terminal-1389600277-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">
+</text><text class="terminal-1389600277-r1" x="0" y="630" textLength="610" clip-path="url(#terminal-1389600277-line-25)">&#160;&#160;&#160;&#160;ls&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;available&#160;commitizens</text><text class="terminal-1389600277-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1389600277-line-25)">
+</text><text class="terminal-1389600277-r1" x="0" y="654.4" textLength="524.6" clip-path="url(#terminal-1389600277-line-26)">&#160;&#160;&#160;&#160;example&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;commit&#160;example</text><text class="terminal-1389600277-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-26)">
+</text><text class="terminal-1389600277-r1" x="0" y="678.8" textLength="646.6" clip-path="url(#terminal-1389600277-line-27)">&#160;&#160;&#160;&#160;info&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;information&#160;about&#160;the&#160;cz</text><text class="terminal-1389600277-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-27)">
+</text><text class="terminal-1389600277-r1" x="0" y="703.2" textLength="512.4" clip-path="url(#terminal-1389600277-line-28)">&#160;&#160;&#160;&#160;schema&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;commit&#160;schema</text><text class="terminal-1389600277-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-28)">
+</text><text class="terminal-1389600277-r1" x="0" y="727.6" textLength="805.2" clip-path="url(#terminal-1389600277-line-29)">&#160;&#160;&#160;&#160;bump&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;bump&#160;semantic&#160;version&#160;based&#160;on&#160;the&#160;git&#160;log</text><text class="terminal-1389600277-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-29)">
+</text><text class="terminal-1389600277-r1" x="0" y="752" textLength="170.8" clip-path="url(#terminal-1389600277-line-30)">&#160;&#160;&#160;&#160;changelog&#160;</text><text class="terminal-1389600277-r2" x="170.8" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">(</text><text class="terminal-1389600277-r1" x="183" y="752" textLength="24.4" clip-path="url(#terminal-1389600277-line-30)">ch</text><text class="terminal-1389600277-r2" x="207.4" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">)</text><text class="terminal-1389600277-r1" x="219.6" y="752" textLength="305" clip-path="url(#terminal-1389600277-line-30)">&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;changelog&#160;</text><text class="terminal-1389600277-r2" x="524.6" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">(</text><text class="terminal-1389600277-r1" x="536.8" y="752" textLength="329.4" clip-path="url(#terminal-1389600277-line-30)">note&#160;that&#160;it&#160;will&#160;overwrite</text><text class="terminal-1389600277-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">
+</text><text class="terminal-1389600277-r1" x="0" y="776.4" textLength="451.4" clip-path="url(#terminal-1389600277-line-31)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;existing&#160;file</text><text class="terminal-1389600277-r2" x="451.4" y="776.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-31)">)</text><text class="terminal-1389600277-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-31)">
+</text><text class="terminal-1389600277-r1" x="0" y="800.8" textLength="951.6" clip-path="url(#terminal-1389600277-line-32)">&#160;&#160;&#160;&#160;check&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;validates&#160;that&#160;a&#160;commit&#160;message&#160;matches&#160;the&#160;commitizen</text><text class="terminal-1389600277-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-32)">
+</text><text class="terminal-1389600277-r1" x="0" y="825.2" textLength="366" clip-path="url(#terminal-1389600277-line-33)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;schema</text><text class="terminal-1389600277-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-33)">
+</text><text class="terminal-1389600277-r1" x="0" y="849.6" textLength="902.8" clip-path="url(#terminal-1389600277-line-34)">&#160;&#160;&#160;&#160;version&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;get&#160;the&#160;version&#160;of&#160;the&#160;installed&#160;commitizen&#160;or&#160;the</text><text class="terminal-1389600277-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-34)">
+</text><text class="terminal-1389600277-r1" x="0" y="874" textLength="488" clip-path="url(#terminal-1389600277-line-35)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;current&#160;project&#160;</text><text class="terminal-1389600277-r2" x="488" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">(</text><text class="terminal-1389600277-r1" x="500.2" y="874" textLength="353.8" clip-path="url(#terminal-1389600277-line-35)">default:&#160;installed&#160;commitizen</text><text class="terminal-1389600277-r2" x="854" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">)</text><text class="terminal-1389600277-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">
+</text><text class="terminal-1389600277-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-36)">
 </text>
     </g>
     </g>

From aaa64aca07db9acd52ee069fc4abb572524e0cf4 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 24 May 2025 00:56:51 +0800
Subject: [PATCH 076/169] docs(faq): add features we wont add

Closes #1129
---
 docs/faq.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/docs/faq.md b/docs/faq.md
index ceabac2e10..920f533d73 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -1,3 +1,11 @@
+## Features we won't add
+
+For a comprehensive list of features that have been considered but won't be implemented, please refer to our [issue tracker](https://github.com/commitizen-tools/commitizen/issues?q=is:issue%20state:closed%20label:%22issue-status:%20wont-fix%22%20OR%20label:%22issue-status:%20wont-implement%22).
+
+- Enable multiple locations of config file `.cz.*` [#955](https://github.com/commitizen-tools/commitizen/issues/955)
+- Create a flag to build the changelog from commits in multiple git repositories [#790](https://github.com/commitizen-tools/commitizen/issues/790)
+- Global Configuration [#597](https://github.com/commitizen-tools/commitizen/issues/597)
+
 ## Support for PEP621
 
 PEP621 establishes a `[project]` definition inside `pyproject.toml`

From 3560156e518416846baec3cd7d3b772a7478ffb8 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 24 May 2025 01:17:32 +0800
Subject: [PATCH 077/169] docs(bump): rewrite bump about section, fix incorrect
 version increment rules

Closes #781
---
 docs/commands/bump.md | 92 ++++++++++++++++++++++++++++---------------
 1 file changed, 60 insertions(+), 32 deletions(-)

diff --git a/docs/commands/bump.md b/docs/commands/bump.md
index ee3d77430a..d399ec9eb8 100644
--- a/docs/commands/bump.md
+++ b/docs/commands/bump.md
@@ -2,51 +2,79 @@
 
 ## About
 
-`cz bump` **automatically** increases the version, based on the commits.
+`cz bump` is a powerful command that **automatically** determines and increases your project's version number based on your commit history. It analyzes your commits to determine the appropriate version increment according to semantic versioning principles.
 
-The commits should follow the rules established by the committer in order to be parsed correctly.
+### Key Features
 
-**prerelease** versions are supported (alpha, beta, release candidate).
+- **Automatic Version Detection**: Analyzes commit history to determine the appropriate version bump
+- **Manual Version Control**: Supports manual version specification when needed
+- **Pre-release Support**: Handles alpha, beta, and release candidate versions
+- **Multiple Version Schemes**: Supports both [PEP 0440][pep440] and [semantic versioning][semver] formats
 
-The version can also be **manually** bumped.
+### Version Increment Rules
 
-The version format follows [PEP 0440][pep440] and [semantic versioning][semver].
-
-This means `MAJOR.MINOR.PATCH`
+The version follows the `MAJOR.MINOR.PATCH` format, with increments determined by your commit types:
 
 | Increment | Description                 | Conventional commit map |
 | --------- | --------------------------- | ----------------------- |
-| `MAJOR`   | Breaking changes introduced | `BREAKING CHANGE`       |
+| `MAJOR`   | Breaking changes introduced | `BREAKING CHANGE`, bang (e.g. `feat!`)|
 | `MINOR`   | New features                | `feat`                  |
-| `PATCH`   | Fixes                       | `fix` + everything else |
+| `PATCH`   | Fixes and improvements      | `fix`, `perf`, `refactor`|
+
+### Version Schemes
 
-[PEP 0440][pep440] is the default, you can switch by using the setting `version_scheme` or the cli:
+By default, Commitizen uses [PEP 0440][pep440] for version formatting. You can switch to semantic versioning using either:
 
+1. Command line:
 ```sh
 cz bump --version-scheme semver
 ```
 
-Some examples of pep440:
+2. Configuration file:
+```toml
+[tool.commitizen]
+version_scheme = "semver"
+```
 
-```bash
-0.9.0
-0.9.1
-0.9.2
-0.9.10
-0.9.11
-1.0.0a0  # alpha
-1.0.0a1
-1.0.0b0  # beta
-1.0.0rc0 # release candidate
-1.0.0rc1
-1.0.0
-1.0.1
-1.1.0
-2.0.0
-2.0.1a
-```
-
-`post` releases are not supported yet.
+### PEP440 Version Examples
+
+Commitizen supports the [PEP 440][pep440] version format, which includes several version types. Here are examples of each:
+
+#### Standard Releases
+```text
+0.9.0    # Initial development release
+0.9.1    # Patch release
+0.9.2    # Another patch release
+0.9.10   # Tenth patch release
+0.9.11   # Eleventh patch release
+1.0.0    # First stable release
+1.0.1    # Patch release after stable
+1.1.0    # Minor feature release
+2.0.0    # Major version release
+```
+
+#### Pre-releases
+```text
+1.0.0a0  # Alpha release 0
+1.0.0a1  # Alpha release 1
+1.0.0b0  # Beta release 0
+1.0.0rc0 # Release candidate 0
+1.0.0rc1 # Release candidate 1
+```
+
+#### Development Releases
+```text
+1.0.0.dev0  # Development release 0
+1.0.0.dev1  # Development release 1
+```
+
+#### Combined Pre-release and Development
+```text
+1.0.0a1.dev0  # Development release 0 of alpha 1
+1.0.0b2.dev1  # Development release 1 of beta 2
+```
+
+> **Note**: `post` releases (e.g., `1.0.0.post1`) are not currently supported.
 
 ## Usage
 
@@ -97,7 +125,7 @@ by their precedence and showcase how a release might flow through a development
 By default, `--increment-mode` is set to `linear`, which ensures that bumping pre-releases _maintains linearity_:
 bumping of a pre-release with lower precedence than the current pre-release phase maintains the current phase of
 higher precedence. For example, if the current version is `1.0.0b1` then bumping with `--prerelease alpha` will
-continue to bump the “beta” phase.
+continue to bump the "beta" phase.
 
 Setting `--increment-mode` to `exact` instructs `cz bump` to instead apply the
 exact changes that have been specified with `--increment` or determined from the commit log. For example,
@@ -223,7 +251,7 @@ If used together with a manual version the command also fails.
 
 We recommend setting `major_version_zero = true` in your configuration file while a project
 is in its initial development. Remove that configuration using a breaking-change commit to bump
-your project’s major version to `v1.0.0` once your project has reached maturity.
+your project's major version to `v1.0.0` once your project has reached maturity.
 
 ### `--version-scheme`
 

From 08ced5dbfc6a3283be7cfac87afb86a652069622 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 28 May 2025 18:04:31 +0800
Subject: [PATCH 078/169] docs(README): fix broken bullet points

---
 docs/README.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/docs/README.md b/docs/README.md
index f730c17837..31f2a77d15 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -163,12 +163,14 @@ cz bump
 ```
 
 This command:
+
 - Bumps your project's version
 - Creates a git tag
 - Updates the changelog (if `update_changelog_on_bump` is enabled)
 - Updates version files
 
 You can customize:
+
 - [Version files](https://commitizen-tools.github.io/commitizen/commands/bump/#version_files)
 - [Version scheme](https://commitizen-tools.github.io/commitizen/commands/bump/#version_scheme)
 - [Version provider](https://commitizen-tools.github.io/commitizen/config/#version-providers)
@@ -187,6 +189,7 @@ cz changelog --dry-run "$(cz version -p)"
 ```
 
 This command is particularly useful for automation scripts and CI/CD pipelines.
+
 For example, you can use the output of the command `cz changelog --dry-run "$(cz version -p)"` to notify your team about a new release in Slack.
 
 #### Pre-commit Integration

From 59e7532cccabf08a0a4b633a13ee174853f5c6f0 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 28 May 2025 18:31:53 +0800
Subject: [PATCH 079/169] docs(external_links): typo

---
 docs/external_links.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/external_links.md b/docs/external_links.md
index 24e4127d86..a90bbd085b 100644
--- a/docs/external_links.md
+++ b/docs/external_links.md
@@ -4,7 +4,7 @@
 
 | Name                                                                      | Speaker         | Occasion                                             | Language | Extra                                                                                                                            |
 | ------------------------------------------------------------------------- | --------------- | ---------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
-| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee         | Taipey.py 2020 June Meetup, Remote Python Pizza 2020 | English  | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) |
+| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee         | Taipei.py 2020 June Meetup, Remote Python Pizza 2020 | English  | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) |
 | Automating release cycles                                                 | Santiago Fraire | PyAmsterdam June 24, 2020, Online                    | English  | [slides](https://woile.github.io/commitizen-presentation/)                                                                       |
 | [Automatizando Releases con Commitizen y Github Actions][automatizando]   | Santiago Fraire | PyConAr 2020, Remote                                 | Español  | [slides](https://woile.github.io/automating-releases-github-actions-presentation/#/)                                             |
 

From 739abd50a12f996144fc73a5f5dc0fa668b252dc Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 28 May 2025 17:52:14 +0800
Subject: [PATCH 080/169] docs(init): rewrite commands init page

---
 docs/commands/init.md | 67 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 53 insertions(+), 14 deletions(-)

diff --git a/docs/commands/init.md b/docs/commands/init.md
index a799c44810..4d92112d34 100644
--- a/docs/commands/init.md
+++ b/docs/commands/init.md
@@ -1,27 +1,66 @@
+The `cz init` command helps you set up Commitizen in your project by creating a configuration file with your preferred settings.
+
 ## Usage
 
 ![cz init --help](../images/cli_help/cz_init___help.svg)
 
-## Example
-
-To start using Commitizen, the recommended approach is to run
+## Command
 
 ```sh
 cz init
 ```
 
+## Interactive Configuration
+
+When you run `cz init`, Commitizen will guide you through an interactive setup process:
+
 ![init](../images/init.gif)
 
-This command will ask you for information about the project and will
-configure the selected file type (`pyproject.toml`, `.cz.toml`, etc.).
+## Configuration File
+
+The initialization process will create a configuration file in your project root.
+
+Choose the configuration file format based on your project type:
+
+- Use `pyproject.toml` for Python projects
+- Use `.cz.toml`, `.cz.yaml`, `.cz.json`, etc. for other projects.
+
+## Configuration Options
+
+During the initialization process, you'll be prompted to configure the following settings:
+
+1. **Convention Rules**: Select the commit message convention to follow (e.g., conventional commits)
+2. **Version Provider**: Choose how to manage versioning in your project. Commitizen supports multiple version management systems:
+    - `commitizen`: Uses Commitizen's built-in version management system
+    - `npm`: Manages version in `package.json` for Node.js projects
+    - `cargo`: Manages version in `Cargo.toml` for Rust projects
+    - `composer`: Manages version in `composer.json` for PHP projects
+    - `pep621`: Uses `pyproject.toml` with PEP 621 standard
+    - `poetry`: Uses `pyproject.toml` with Poetry configuration
+    - `uv`: Uses `pyproject.toml` and `uv.lock` for Python projects
+    - `scm`: Reads version directly from git tags without modifying files
+3. **Project Version**: The current version of your project will be detected automatically
+4. **Tag Format**: The format used for version tags in your repository
+5. **Version Type**: Choose between:
+    - `semver` or `semver2`: Semantic Versioning (MAJOR.MINOR.PATCH)
+    - `pep440`: Python Package Versioning
+6. **Changelog Generation**: Configure whether to automatically generate changelog during version bumps
+7. **Alpha Versioning**: Option to keep major version at 0 for alpha/beta software
+8. **Pre-commit Hooks**: Set up Git pre-commit hooks for automated commit message validation
+
+## Example
+
+```sh
+# Start the initialization process
+cz init
+
+# Follow the interactive prompts to configure your project
+```
+
+## Next Steps
 
-The `init` will help you with
+After initialization, you can:
 
-1. Choose a convention rules (`name`)
-2. Choosing a version provider (`commitizen` or for example `Cargo.toml`)
-3. Detecting your project's version
-4. Detecting the tag format used
-5. Choosing a version type (`semver` or `pep440`)
-6. Whether to create the changelog automatically or not during bump
-7. Whether you want to keep the major as zero while building alpha software.
-8. Whether to setup pre-commit hooks.
+1. Start using `cz commit` to create conventional commits
+2. Use `cz bump` to manage versioning
+3. Configure additional settings in your project's configuration file

From ceb2b7afef13b48a1fe978de3e4cbba472fb6609 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 28 May 2025 18:25:30 +0800
Subject: [PATCH 081/169] docs: correct case GitHub

---
 docs/commands/bump.md            | 2 +-
 docs/tutorials/github_actions.md | 2 +-
 mkdocs.yml                       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/commands/bump.md b/docs/commands/bump.md
index d399ec9eb8..8219cc3461 100644
--- a/docs/commands/bump.md
+++ b/docs/commands/bump.md
@@ -219,7 +219,7 @@ understand that the user wants to create a changelog. It is recommended to be
 explicit and use `--changelog` (or the setting `update_changelog_on_bump`).
 
 This command is useful to "transport" the newly created changelog.
-It can be sent to an auditing system, or to create a Github Release.
+It can be sent to an auditing system, or to create a GitHub Release.
 
 Example:
 
diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md
index bf08eb3bc5..2cb58cfee6 100644
--- a/docs/tutorials/github_actions.md
+++ b/docs/tutorials/github_actions.md
@@ -1,4 +1,4 @@
-## Create a new release with Github Actions
+## Create a new release with GitHub Actions
 
 ### Automatic bumping of version
 
diff --git a/mkdocs.yml b/mkdocs.yml
index a8a2fe2d44..7b1e0feb30 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -50,7 +50,7 @@ nav:
       - 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"
+      - GitHub Actions: "tutorials/github_actions.md"
       - Jenkins pipeline: "tutorials/jenkins_pipeline.md"
       - Developmental releases: "tutorials/dev_releases.md"
       - Monorepo support: "tutorials/monorepo_guidance.md"

From 4b6b3fb4f9782ed0d55db5518b1e1523e0779eea Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 28 May 2025 18:16:16 +0800
Subject: [PATCH 082/169] docs(commit): rewrite command commit page

---
 docs/commands/commit.md | 75 ++++++++++++++++++++++++-----------------
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/docs/commands/commit.md b/docs/commands/commit.md
index febaee3cf2..5a073a2644 100644
--- a/docs/commands/commit.md
+++ b/docs/commands/commit.md
@@ -1,52 +1,65 @@
+## Usage
+
+![cz commit --help](../images/cli_help/cz_commit___help.svg)
+
+## Overview
+
 ![Using Commitizen cli](../images/demo.gif)
 
-## About
+The `commit` command provides an interactive way to create structured commits. Use either:
 
-In your terminal run `cz commit` or the shortcut `cz c` to generate a guided git commit.
+- `cz commit`
+- `cz c` (shortcut)
 
-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. A possible use
-case for this is to [automatically prepare a commit message](../tutorials/auto_prepare_commit_message.md).
+By default, Commitizen uses conventional commits, but you can customize the commit rules to match your project's needs. See the [customization guide](../customization.md) for details.
 
+## Basic Usage
 
-!!! 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).
+### Interactive Commit Creation
 
-## Usage
+Simply run `cz commit` in your terminal to start the interactive commit creation process. The command will guide you through creating a properly formatted commit message according to your configured rules.
 
-![cz commit --help](../images/cli_help/cz_commit___help.svg)
+### Writing Messages to File
 
-### git options
+You can save the generated commit message to a file using:
+```sh
+cz commit --write-message-to-file COMMIT_MSG_FILE
+```
 
-`git` command options that are not implemented by Commitizen can be use via the `--` syntax for the `commit` command.
-The syntax separates Commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax:
+This can be combined with `--dry-run` to only write the message without creating a commit. This is particularly useful for [automatically preparing commit messages](../tutorials/auto_prepare_commit_message.md).
+
+## Advanced Features
+
+### Git Command Options
+
+You can pass any git commit options using the `--` syntax:
 ```sh
 cz commit <commitizen-args> -- <git-cli-args>
 
-# e.g., cz commit --dry-run -- -a -S
+# Examples:
+cz c --dry-run -- -a -S  # Stage all changes and sign the commit
+cz c -a -- -n            # Stage all changes and skip the pre-commit and commit-msg hooks
 ```
-For example, using the `-S` option on `git commit` to sign a commit is now Commitizen compatible: `cz c -- -S`
 
-!!! note
-    Deprecation warning: A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`.
-    This syntax is now deprecated in favor of the new `cz commit -- -s` syntax.
+!!! warning
+    The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated.
 
 ### Retry
 
-You can use `cz commit --retry` to reuse the last commit message when the previous commit attempt failed.
-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.
+- Use `cz commit --retry` to reuse the last commit message after a failed commit attempt
+- Set `retry_after_failure: true` in your configuration to automatically retry
+- Use `cz commit --no-retry` to force a new commit message prompt
+
+### Message Length Control
 
-### Commit message length limit
+Control the length of your commit messages using the `-l` or `--message-length-limit` option:
+```sh
+cz commit -l 72  # Limits message length to 72 characters
+```
+
+!!! note
+    The length limit only applies to the first line of the commit message. For conventional commits, this means the limit applies from the type of change through the subject. The body and footer are not counted.
 
-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.
+## Technical Notes
 
-**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.
+For platform compatibility, the `commit` command disables ANSI escaping in its output. This means pre-commit hooks coloring will be deactivated as discussed in [commitizen-tools/commitizen#417](https://github.com/commitizen-tools/commitizen/issues/417).

From 13e4aaacbde5bcfc4bd2aea1a1c060508b86dcec Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 14:03:53 +0800
Subject: [PATCH 083/169] docs(contributing): improve documentation for
 first-time contributors

---
 docs/contributing.md | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/docs/contributing.md b/docs/contributing.md
index e9e162d2df..8389e9370d 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -1,10 +1,14 @@
-## Contributing to commitizen
+## Contributing to Commitizen
 
-First, thank you for taking the time to contribute! 🎉
+First, thank you for taking the time to contribute! 🎉 Your contributions help make Commitizen better for everyone.
 
-When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change.
+When contributing to Commitizen, we encourage you to:
 
-If you're a first-time contributor, you can check the issues with the [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag.
+1. First, check out the [issues](https://github.com/commitizen-tools/commitizen/issues) and [Features we won't add](faq.md#features-we-wont-add) to see if there's already a discussion about the change you wish to make.
+2. If there's no discussion, [create an issue](https://github.com/commitizen-tools/commitizen/issues/new) to discuss your proposed changes.
+3. Follow our [development workflow](#development-workflow) and guidelines below.
+
+If you're a first-time contributor, please check out issues labeled [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) - these are specifically chosen to be beginner-friendly.
 
 ## Prerequisites & Setup
 

From 47beadc563d8272b64f88ddbf3e9a37351b8f535 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 14:15:29 +0800
Subject: [PATCH 084/169] docs(pull_request_template): add task to fix broken
 external links in the docs

---
 .github/pull_request_template.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 5686474709..e37148c0a1 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -25,7 +25,7 @@ Please fill in the following content to let us know better about this change.
 ### Documentation Changes
 
 - [ ] Run `poetry doc` locally to ensure the documentation pages renders correctly
-  - [ ] Check if there are any broken links in the documentation
+- [ ] Check and fix any broken links (internal or external) in the documentation
 
 > When running `poetry doc`, any broken internal documentation links will be reported in the console output like this:
 >

From 596038c68a51ad2485ca924d394ee1282eec9472 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 2 Jun 2025 14:59:09 +0800
Subject: [PATCH 085/169] docs(feature_request): eliminate duplicated label

---
 .github/ISSUE_TEMPLATE/feature_request.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 4bd19b9b3a..da3bd5af8a 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -26,6 +26,6 @@ body:
   - type: textarea
     id: related-issue
     attributes:
-      label: Additional context
+      label: Related issues
       description: |
         If applicable, add link to existing issue also help us know better.

From a4c87bc0ecb71028d327f82bdba64798518eacc3 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 31 May 2025 22:41:41 +0800
Subject: [PATCH 086/169] docs(contributing): add Contributing TL;DR page

---
 docs/contributing_tldr.md | 28 ++++++++++++++++++++++++++++
 mkdocs.yml                |  1 +
 2 files changed, 29 insertions(+)
 create mode 100644 docs/contributing_tldr.md

diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md
new file mode 100644
index 0000000000..278fa520a3
--- /dev/null
+++ b/docs/contributing_tldr.md
@@ -0,0 +1,28 @@
+Feel free to send a PR to update this file if you find anything useful. 🙇
+
+## Environment
+
+- Python `>=3.9`
+- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0`
+
+## Useful commands
+
+Please check the [pyproject.toml](https://github.com/commitizen-tools/commitizen/blob/master/pyproject.toml) for a comprehensive list of commands.
+
+```bash
+# Ensure you have the correct dependencies
+poetry install
+
+# Make ruff happy
+poetry format
+
+# Check if ruff and mypy are happy
+poetry lint
+
+# Check if mypy is happy in python 3.9
+mypy --python-version 3.9
+
+# Run tests in parallel.
+pytest -n auto # This may take a while.
+pytest -n auto <test_suite>
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index 7b1e0feb30..dec8d3fd5d 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -58,6 +58,7 @@ nav:
   - Exit Codes: "exit_codes.md"
   - Third-Party Commitizen Templates: "third-party-commitizen.md"
   - Contributing: "contributing.md"
+  - Contributing TL;DR: "contributing_tldr.md"
   - Resources: "external_links.md"
 
 markdown_extensions:

From 195ed16275eb197fe7b2bd78f7120f59d0e84424 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 14:26:31 +0800
Subject: [PATCH 087/169] docs(contributing-tldr): add documentation changes
 section

---
 docs/contributing_tldr.md | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md
index 278fa520a3..f5483cddcd 100644
--- a/docs/contributing_tldr.md
+++ b/docs/contributing_tldr.md
@@ -9,6 +9,8 @@ Feel free to send a PR to update this file if you find anything useful. 🙇
 
 Please check the [pyproject.toml](https://github.com/commitizen-tools/commitizen/blob/master/pyproject.toml) for a comprehensive list of commands.
 
+### Code Changes
+
 ```bash
 # Ensure you have the correct dependencies
 poetry install
@@ -26,3 +28,10 @@ mypy --python-version 3.9
 pytest -n auto # This may take a while.
 pytest -n auto <test_suite>
 ```
+
+### Documentation Changes
+
+```bash
+# Build the documentation locally and check for broken links
+poetry doc
+```

From c1884bddbeb6afa0af72c9526e18fac16c46e766 Mon Sep 17 00:00:00 2001
From: Anas Hasan <anas.hassan9417@gmail.com>
Date: Sun, 1 Jun 2025 15:45:02 +0530
Subject: [PATCH 088/169] docs(faq): explain why Pydantic is not used

---
 docs/faq.md    | 14 +++++++++++
 poetry.lock    | 68 +++++++++++++++++++++++++++++++++++++-------------
 pyproject.toml |  1 +
 3 files changed, 65 insertions(+), 18 deletions(-)

diff --git a/docs/faq.md b/docs/faq.md
index 920f533d73..ee9d97068d 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -82,6 +82,20 @@ git revert --no-commit <SHA>
 git commit -m "revert: foo bar"
 ```
 
+## Why don't we use Pydantic?
+
+While Pydantic is a powerful and popular library for data validation, we intentionally avoid using it in this project to keep our dependency tree minimal and maintainable.
+
+Including Pydantic would increase the chances of version conflicts for users - especially with major changes introduced in Pydantic v3. Because we pin dependencies tightly, adding Pydantic could unintentionally restrict what other tools or libraries users can install alongside `commitizen`.
+
+Moreover we don't rely on the full feature set of Pydantic. Simpler alternatives like Python's built-in `TypedDict` offer sufficient type safety for our use cases, without the runtime overhead or dependency burden.
+
+In short, avoiding Pydantic helps us:
+- Keep dependencies lightweight
+- Reduce compatibility issues for users
+- Maintain clarity about what contributors should and shouldn't use
+
+
 ## I got `Exception [WinError 995] The I/O operation ...` error
 
 This error was caused by a Python bug on Windows. It's been fixed by [this PR](https://github.com/python/cpython/pull/22017), and according to Python's changelog, [3.8.6rc1](https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-6-release-candidate-1) and [3.9.0rc2](https://docs.python.org/3.9/whatsnew/changelog.html#python-3-9-0-release-candidate-2) should be the accurate versions first contain this fix. In conclusion, upgrade your Python version might solve this issue.
diff --git a/poetry.lock b/poetry.lock
index cccd7a532a..64ec358bb3 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
 
 [[package]]
 name = "argcomplete"
@@ -31,8 +31,8 @@ files = [
 six = ">=1.12.0"
 
 [package.extras]
-astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
-test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
+astroid = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\""]
+test = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\"", "pytest"]
 
 [[package]]
 name = "babel"
@@ -302,7 +302,7 @@ files = [
 tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
 
 [package.extras]
-toml = ["tomli"]
+toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
 
 [[package]]
 name = "decli"
@@ -344,7 +344,7 @@ files = [
 wrapt = ">=1.10,<2"
 
 [package.extras]
-dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"]
 
 [[package]]
 name = "distlib"
@@ -402,7 +402,7 @@ files = [
 ]
 
 [package.extras]
-tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
+tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
 
 [[package]]
 name = "filelock"
@@ -419,7 +419,7 @@ files = [
 [package.extras]
 docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
 testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
-typing = ["typing-extensions (>=4.12.2)"]
+typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
 
 [[package]]
 name = "freezegun"
@@ -491,7 +491,7 @@ description = "Read metadata from Python packages"
 optional = false
 python-versions = ">=3.9"
 groups = ["main", "documentation"]
-markers = "python_version < \"3.10\""
+markers = "python_version == \"3.9\""
 files = [
     {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
     {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
@@ -501,12 +501,12 @@ files = [
 zipp = ">=3.20"
 
 [package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
 cover = ["pytest-cov"]
 doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
 enabler = ["pytest-enabler (>=2.2)"]
 perf = ["ipython"]
-test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
+test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
 type = ["pytest-mypy"]
 
 [[package]]
@@ -781,7 +781,7 @@ watchdog = ">=2.0"
 
 [package.extras]
 i18n = ["babel (>=2.9.0)"]
-min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
+min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
 
 [[package]]
 name = "mkdocs-get-deps"
@@ -971,6 +971,18 @@ files = [
 qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
 testing = ["docopt", "pytest"]
 
+[[package]]
+name = "pastel"
+version = "0.2.1"
+description = "Bring colors to your terminal."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+groups = ["dev"]
+files = [
+    {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"},
+    {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"},
+]
+
 [[package]]
 name = "pathspec"
 version = "0.12.1"
@@ -1032,6 +1044,26 @@ files = [
 dev = ["pre-commit", "tox"]
 testing = ["pytest", "pytest-benchmark"]
 
+[[package]]
+name = "poethepoet"
+version = "0.34.0"
+description = "A task runner that works well with poetry."
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+    {file = "poethepoet-0.34.0-py3-none-any.whl", hash = "sha256:c472d6f0fdb341b48d346f4ccd49779840c15b30dfd6bc6347a80d6274b5e34e"},
+    {file = "poethepoet-0.34.0.tar.gz", hash = "sha256:86203acce555bbfe45cb6ccac61ba8b16a5784264484195874da457ddabf5850"},
+]
+
+[package.dependencies]
+pastel = ">=0.2.1,<0.3.0"
+pyyaml = ">=6.0.2,<7.0"
+tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""}
+
+[package.extras]
+poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""]
+
 [[package]]
 name = "pre-commit"
 version = "4.1.0"
@@ -1304,7 +1336,7 @@ version = "6.0.2"
 description = "YAML parser and emitter for Python"
 optional = false
 python-versions = ">=3.8"
-groups = ["main", "documentation", "linters", "test"]
+groups = ["main", "dev", "documentation", "linters", "test"]
 files = [
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@@ -1785,7 +1817,7 @@ files = [
 ]
 
 [package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
 h2 = ["h2 (>=4,<5)"]
 socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
 zstd = ["zstandard (>=0.18.0)"]
@@ -1809,7 +1841,7 @@ platformdirs = ">=3.9.1,<5"
 
 [package.extras]
 docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
 
 [[package]]
 name = "watchdog"
@@ -1948,21 +1980,21 @@ description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
 python-versions = ">=3.9"
 groups = ["main", "documentation"]
-markers = "python_version < \"3.10\""
+markers = "python_version == \"3.9\""
 files = [
     {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
     {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
 ]
 
 [package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
 cover = ["pytest-cov"]
 doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
 enabler = ["pytest-enabler (>=2.2)"]
-test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
+test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
 type = ["pytest-mypy"]
 
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "e15b424a0569f939e297c8abfcf09753f1fbcc5b4ad891163cc0982accd3b372"
+content-hash = "2951e44d1ec238ddef2139c76de3c838976a018b0993f7b84c6fd686e26fc5d0"
diff --git a/pyproject.toml b/pyproject.toml
index a653ecdb5d..cff51a2094 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -107,6 +107,7 @@ packages = [{ include = "commitizen" }, { include = "commitizen/py.typed" }]
 [tool.poetry.group.dev.dependencies]
 ipython = "^8.0"
 tox = ">4"
+poethepoet = "^0.34.0"
 
 [tool.poetry.group.test.dependencies]
 pytest = ">=7.2,<9.0"

From 612ef79eec468c9cbd1b88a36aa45d036989f754 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 16 May 2025 02:03:30 +0800
Subject: [PATCH 089/169] refactor: improve readability and fix typos

---
 commitizen/bump.py      | 18 ++++++++++--------
 commitizen/changelog.py |  2 +-
 commitizen/cli.py       |  8 ++------
 commitizen/cz/utils.py  | 11 +++++------
 commitizen/defaults.py  |  6 +++---
 commitizen/tags.py      |  6 +-----
 tests/test_changelog.py |  2 +-
 7 files changed, 23 insertions(+), 30 deletions(-)

diff --git a/commitizen/bump.py b/commitizen/bump.py
index 76a8e15893..b8ae18ba64 100644
--- a/commitizen/bump.py
+++ b/commitizen/bump.py
@@ -76,7 +76,7 @@ def update_version_in_files(
     """
     # TODO: separate check step and write step
     updated = []
-    for path, regex in files_and_regexs(files, current_version):
+    for path, regex in _files_and_regexes(files, current_version):
         current_version_found, version_file = _bump_with_regex(
             path,
             current_version,
@@ -99,7 +99,7 @@ def update_version_in_files(
     return updated
 
 
-def files_and_regexs(patterns: list[str], version: str) -> list[tuple[str, str]]:
+def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str]]:
     """
     Resolve all distinct files with their regexp from a list of glob patterns with optional regexp
     """
@@ -128,13 +128,15 @@ def _bump_with_regex(
     pattern = re.compile(regex)
     with open(version_filepath, encoding=encoding) as f:
         for line in f:
-            if pattern.search(line):
-                bumped_line = line.replace(current_version, new_version)
-                if bumped_line != line:
-                    current_version_found = True
-                lines.append(bumped_line)
-            else:
+            if not pattern.search(line):
                 lines.append(line)
+                continue
+
+            bumped_line = line.replace(current_version, new_version)
+            if bumped_line != line:
+                current_version_found = True
+            lines.append(bumped_line)
+
     return current_version_found, "".join(lines)
 
 
diff --git a/commitizen/changelog.py b/commitizen/changelog.py
index 704efe6071..d8ab56069b 100644
--- a/commitizen/changelog.py
+++ b/commitizen/changelog.py
@@ -190,7 +190,7 @@ def process_commit_message(
 def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable:
     if len(set(change_type_order)) != len(change_type_order):
         raise InvalidConfigurationError(
-            f"Change types contain duplicates types ({change_type_order})"
+            f"Change types contain duplicated types ({change_type_order})"
         )
 
     sorted_tree = []
diff --git a/commitizen/cli.py b/commitizen/cli.py
index cb834c5d6f..b566fc596d 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -636,14 +636,10 @@ def main():
         extra_args = " ".join(unknown_args[1:])
         arguments["extra_cli_args"] = extra_args
 
-    if args.config:
-        conf = config.read_cfg(args.config)
-    else:
-        conf = config.read_cfg()
-
+    conf = config.read_cfg(args.config)
     if args.name:
         conf.update({"name": args.name})
-    elif not args.name and not conf.path:
+    elif not conf.path:
         conf.update({"name": "cz_conventional_commits"})
 
     if args.debug:
diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py
index 7bc89673c6..cb79d65d1a 100644
--- a/commitizen/cz/utils.py
+++ b/commitizen/cz/utils.py
@@ -5,6 +5,8 @@
 from commitizen import git
 from commitizen.cz import exceptions
 
+_RE_LOCAL_VERSION = re.compile(r"\+.+")
+
 
 def required_validator(answer, msg=None):
     if not answer:
@@ -17,16 +19,13 @@ def multiple_line_breaker(answer, sep="|"):
 
 
 def strip_local_version(version: str) -> str:
-    return re.sub(r"\+.+", "", version)
+    return _RE_LOCAL_VERSION.sub("", version)
 
 
 def get_backup_file_path() -> str:
     project_root = git.find_git_project_root()
-
-    if project_root is None:
-        project = ""
-    else:
-        project = project_root.as_posix().replace("/", "%")
+    project = project_root.as_posix().replace("/", "%") if project_root else ""
 
     user = os.environ.get("USER", "")
+
     return os.path.join(tempfile.gettempdir(), f"cz.commit%{user}%{project}.backup")
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 0b6c28e6a9..1885848618 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -141,7 +141,7 @@ class Settings(TypedDict, total=False):
 def get_tag_regexes(
     version_regex: str,
 ) -> dict[str, str]:
-    regexs = {
+    regexes = {
         "version": version_regex,
         "major": r"(?P<major>\d+)",
         "minor": r"(?P<minor>\d+)",
@@ -150,6 +150,6 @@ def get_tag_regexes(
         "devrelease": r"(?P<devrelease>\.dev\d+)?",
     }
     return {
-        **{f"${k}": v for k, v in regexs.items()},
-        **{f"${{{k}}}": v for k, v in regexs.items()},
+        **{f"${k}": v for k, v in regexes.items()},
+        **{f"${{{k}}}": v for k, v in regexes.items()},
     }
diff --git a/commitizen/tags.py b/commitizen/tags.py
index 2b9a4b091a..c5f06884fe 100644
--- a/commitizen/tags.py
+++ b/commitizen/tags.py
@@ -174,11 +174,7 @@ def include_in_changelog(self, tag: GitTag) -> bool:
             version = self.extract_version(tag)
         except InvalidVersion:
             return False
-
-        if self.merge_prereleases and version.is_prerelease:
-            return False
-
-        return True
+        return not (self.merge_prereleases and version.is_prerelease)
 
     def search_version(self, text: str, last: bool = False) -> VersionTag | None:
         """
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index b1c7c802e1..511cd1a3df 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1236,7 +1236,7 @@ def test_order_changelog_tree_raises():
     with pytest.raises(InvalidConfigurationError) as excinfo:
         changelog.order_changelog_tree(COMMITS_TREE, change_type_order)
 
-    assert "Change types contain duplicates types" in str(excinfo)
+    assert "Change types contain duplicated types" in str(excinfo)
 
 
 def test_render_changelog(

From 33453b753676161db343c56dc4a9abda31c2d864 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 23:26:54 +0800
Subject: [PATCH 090/169] refactor(version_scheme): cleanup

---
 commitizen/version_schemes.py | 94 +++++++++++++++++------------------
 1 file changed, 47 insertions(+), 47 deletions(-)

diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py
index 84ded9316e..d5370b2c6c 100644
--- a/commitizen/version_schemes.py
+++ b/commitizen/version_schemes.py
@@ -41,7 +41,9 @@
 
 Increment: TypeAlias = Literal["MAJOR", "MINOR", "PATCH"]
 Prerelease: TypeAlias = Literal["alpha", "beta", "rc"]
-DEFAULT_VERSION_PARSER = r"v?(?P<version>([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.]+)?(\w+)?)"
+_DEFAULT_VERSION_PARSER = re.compile(
+    r"v?(?P<version>([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.]+)?(\w+)?)"
+)
 
 
 @runtime_checkable
@@ -156,7 +158,7 @@ class BaseVersion(_BaseVersion):
     A base class implementing the `VersionProtocol` for PEP440-like versions.
     """
 
-    parser: ClassVar[re.Pattern] = re.compile(DEFAULT_VERSION_PARSER)
+    parser: ClassVar[re.Pattern] = _DEFAULT_VERSION_PARSER
     """Regex capturing this version scheme into a `version` group"""
 
     @property
@@ -265,39 +267,35 @@ def bump(
         if self.local and is_local_version:
             local_version = self.scheme(self.local).bump(increment)
             return self.scheme(f"{self.public}+{local_version}")  # type: ignore
-        else:
-            if not self.is_prerelease:
-                base = self.increment_base(increment)
-            elif exact_increment:
-                base = self.increment_base(increment)
-            else:
-                base = f"{self.major}.{self.minor}.{self.micro}"
-                if increment == PATCH:
-                    pass
-                elif increment == MINOR:
-                    if self.micro != 0:
-                        base = self.increment_base(increment)
-                elif increment == MAJOR:
-                    if self.minor != 0 or self.micro != 0:
-                        base = self.increment_base(increment)
-            dev_version = self.generate_devrelease(devrelease)
-
-            release = list(self.release)
-            if len(release) < 3:
-                release += [0] * (3 - len(release))
-            current_base = ".".join(str(part) for part in release)
-            if base == current_base:
-                pre_version = self.generate_prerelease(
-                    prerelease, offset=prerelease_offset
-                )
-            else:
-                base_version = cast(BaseVersion, self.scheme(base))
-                pre_version = base_version.generate_prerelease(
-                    prerelease, offset=prerelease_offset
-                )
-            build_metadata = self.generate_build_metadata(build_metadata)
-            # TODO: post version
-            return self.scheme(f"{base}{pre_version}{dev_version}{build_metadata}")  # type: ignore
+
+        base = self._get_increment_base(increment, exact_increment)
+        dev_version = self.generate_devrelease(devrelease)
+
+        release = list(self.release)
+        if len(release) < 3:
+            release += [0] * (3 - len(release))
+        current_base = ".".join(str(part) for part in release)
+
+        pre_version = (
+            self if base == current_base else cast(BaseVersion, self.scheme(base))
+        ).generate_prerelease(prerelease, offset=prerelease_offset)
+
+        # TODO: post version
+        return self.scheme(
+            f"{base}{pre_version}{dev_version}{self.generate_build_metadata(build_metadata)}"
+        )  # type: ignore
+
+    def _get_increment_base(
+        self, increment: Increment | None, exact_increment: bool
+    ) -> str:
+        if (
+            not self.is_prerelease
+            or exact_increment
+            or (increment == MINOR and self.micro != 0)
+            or (increment == MAJOR and (self.minor != 0 or self.micro != 0))
+        ):
+            return self.increment_base(increment)
+        return f"{self.major}.{self.minor}.{self.micro}"
 
 
 class Pep440(BaseVersion):
@@ -316,7 +314,7 @@ class SemVer(BaseVersion):
     """
 
     def __str__(self) -> str:
-        parts = []
+        parts: list[str] = []
 
         # Epoch
         if self.epoch != 0:
@@ -364,7 +362,7 @@ def prerelease(self) -> str | None:
         return None
 
     def __str__(self) -> str:
-        parts = []
+        parts: list[str] = []
 
         # Epoch
         if self.epoch != 0:
@@ -373,9 +371,19 @@ def __str__(self) -> str:
         # Release segment
         parts.append(".".join(str(x) for x in self.release))
 
+        if prerelease := self._get_prerelease():
+            parts.append(f"-{prerelease}")
+
+        # Local version segment
+        if self.local:
+            parts.append(f"+{self.local}")
+
+        return "".join(parts)
+
+    def _get_prerelease(self) -> str:
         # Pre-release identifiers
         # See: https://semver.org/spec/v2.0.0.html#spec-item-9
-        prerelease_parts = []
+        prerelease_parts: list[str] = []
         if self.prerelease:
             prerelease_parts.append(f"{self.prerelease}")
 
@@ -387,15 +395,7 @@ def __str__(self) -> str:
         if self.dev is not None:
             prerelease_parts.append(f"dev.{self.dev}")
 
-        if prerelease_parts:
-            parts.append("-")
-            parts.append(".".join(prerelease_parts))
-
-        # Local version segment
-        if self.local:
-            parts.append(f"+{self.local}")
-
-        return "".join(parts)
+        return ".".join(prerelease_parts)
 
 
 DEFAULT_SCHEME: VersionScheme = Pep440

From 8cfb859ffad0538cb17a1b1bb565df6c47e48ded Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 19:05:40 +0800
Subject: [PATCH 091/169] refactor(commit): simplify call

---
 commitizen/commands/commit.py | 64 +++++++++++++++--------------------
 1 file changed, 28 insertions(+), 36 deletions(-)

diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index cb34c41a50..eedf77e079 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -92,41 +92,36 @@ def manual_edit(self, message: str) -> str:
         os.unlink(file.name)
         return message
 
-    def __call__(self):
-        extra_args: str = self.arguments.get("extra_cli_args", "")
+    def _get_message(self) -> str:
+        if self.arguments.get("retry"):
+            m = self.read_backup_message()
+            if m is None:
+                raise NoCommitBackupError()
+            return m
 
-        allow_empty: bool = "--allow-empty" in extra_args
+        if self.config.settings.get("retry_after_failure") and not self.arguments.get(
+            "no_retry"
+        ):
+            return self.read_backup_message() or self.prompt_commit_questions()
+        return self.prompt_commit_questions()
 
+    def __call__(self):
+        extra_args: str = self.arguments.get("extra_cli_args", "")
         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")
+        signoff: bool = self.arguments.get("signoff")
 
-        is_all: bool = self.arguments.get("all")
-        if is_all:
-            c = git.add("-u")
+        if self.arguments.get("all"):
+            git.add("-u")
 
-        if git.is_staging_clean() and not (dry_run or allow_empty):
+        if git.is_staging_clean() and not (dry_run or "--allow-empty" in extra_args):
             raise NothingToCommitError("No files added to staging!")
 
         if write_message_to_file is not None and write_message_to_file.is_dir():
             raise NotAllowed(f"{write_message_to_file} is a directory")
 
-        retry: bool = self.arguments.get("retry")
-        no_retry: bool = self.arguments.get("no_retry")
-        retry_after_failure: bool = self.config.settings.get("retry_after_failure")
-
-        if retry:
-            m = self.read_backup_message()
-            if m is None:
-                raise NoCommitBackupError()
-        elif retry_after_failure and not no_retry:
-            m = self.read_backup_message()
-            if m is None:
-                m = self.prompt_commit_questions()
-        else:
-            m = self.prompt_commit_questions()
-
-        if manual_edit:
+        m = self._get_message()
+        if self.arguments.get("edit"):
             m = self.manual_edit(m)
 
         out.info(f"\n{m}\n")
@@ -138,19 +133,15 @@ def __call__(self):
         if dry_run:
             raise DryRunExit()
 
-        always_signoff: bool = self.config.settings["always_signoff"]
-        signoff: bool = self.arguments.get("signoff")
-
         if signoff:
             out.warn(
                 "signoff mechanic is deprecated, please use `cz commit -- -s` instead."
             )
 
-        if always_signoff or signoff:
+        if self.config.settings["always_signoff"] or signoff:
             extra_args = f"{extra_args} -s".strip()
 
         c = git.commit(m, args=extra_args)
-
         if c.return_code != 0:
             out.error(c.err)
 
@@ -160,11 +151,12 @@ def __call__(self):
 
             raise CommitError()
 
-        if "nothing added" in c.out or "no changes added to commit" in c.out:
+        if any(s in c.out for s in ("nothing added", "no changes added to commit")):
             out.error(c.out)
-        else:
-            with contextlib.suppress(FileNotFoundError):
-                os.remove(self.temp_file)
-            out.write(c.err)
-            out.write(c.out)
-            out.success("Commit successful!")
+            return
+
+        with contextlib.suppress(FileNotFoundError):
+            os.remove(self.temp_file)
+        out.write(c.err)
+        out.write(c.out)
+        out.success("Commit successful!")

From 471b778e18937cb0291bf266fa1cd2a58a53e081 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 19:43:14 +0800
Subject: [PATCH 092/169] test(commit): when nothing is added to commit

---
 tests/commands/test_commit_command.py | 31 +++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py
index 3a92f5af48..930e1a7a9b 100644
--- a/tests/commands/test_commit_command.py
+++ b/tests/commands/test_commit_command.py
@@ -523,3 +523,34 @@ def test_commit_command_shows_description_when_use_help_option(
 
     out, _ = capsys.readouterr()
     file_regression.check(out, extension=".txt")
+
+
+@pytest.mark.usefixtures("staging_is_clean")
+@pytest.mark.parametrize(
+    "out", ["no changes added to commit", "nothing added to commit"]
+)
+def test_commit_when_nothing_added_to_commit(config, mocker: MockFixture, out):
+    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(
+        out=out,
+        err="",
+        stdout=out.encode(),
+        stderr=b"",
+        return_code=0,
+    )
+    error_mock = mocker.patch("commitizen.out.error")
+
+    commands.Commit(config, {})()
+
+    commit_mock.assert_called_once()
+    error_mock.assert_called_once_with(out)

From 5b3b6efbbb30980616aa218af35e66587d79a6da Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 16 May 2025 02:18:37 +0800
Subject: [PATCH 093/169] refactor(git): code cleanup and better test coverage

---
 commitizen/git.py | 133 +++++++++++++++-------------------------------
 tests/test_git.py |  32 ++++++-----
 2 files changed, 61 insertions(+), 104 deletions(-)

diff --git a/commitizen/git.py b/commitizen/git.py
index 19ca46b6c3..d0cc901a61 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -1,34 +1,14 @@
 from __future__ import annotations
 
 import os
-from enum import Enum
-from os import linesep
 from pathlib import Path
 from tempfile import NamedTemporaryFile
 
 from commitizen import cmd, out
 from commitizen.exceptions import GitCommandError
 
-UNIX_EOL = "\n"
-WINDOWS_EOL = "\r\n"
-
-
-class EOLTypes(Enum):
-    """The EOL type from `git config core.eol`."""
-
-    LF = "lf"
-    CRLF = "crlf"
-    NATIVE = "native"
-
-    def get_eol_for_open(self) -> str:
-        """Get the EOL character for `open()`."""
-        map = {
-            EOLTypes.CRLF: WINDOWS_EOL,
-            EOLTypes.LF: UNIX_EOL,
-            EOLTypes.NATIVE: linesep,
-        }
-
-        return map[self]
+_UNIX_EOL = "\n"
+_WINDOWS_EOL = "\r\n"
 
 
 class GitObject:
@@ -37,9 +17,7 @@ class GitObject:
     date: str
 
     def __eq__(self, other) -> bool:
-        if not hasattr(other, "rev"):
-            return False
-        return self.rev == other.rev  # type: ignore
+        return hasattr(other, "rev") and self.rev == other.rev
 
 
 class GitCommit(GitObject):
@@ -63,6 +41,20 @@ def __init__(
     def message(self):
         return f"{self.title}\n\n{self.body}".strip()
 
+    @classmethod
+    def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit:
+        rev, parents, title, author, author_email, *body_list = rev_and_commit.split(
+            "\n"
+        )
+        return cls(
+            rev=rev.strip(),
+            title=title.strip(),
+            body="\n".join(body_list).strip(),
+            author=author,
+            author_email=author_email,
+            parents=[p for p in parents.strip().split(" ") if p],
+        )
+
     def __repr__(self):
         return f"{self.title} ({self.rev})"
 
@@ -101,13 +93,11 @@ def tag(
     # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging,
     # we're not able to create lightweight tag with message.
     # by adding message, we make it a annotated tags
-    c = cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"')
-    return c
+    return cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"')
 
 
 def add(*args: str) -> cmd.Command:
-    c = cmd.run(f"git add {' '.join(args)}")
-    return c
+    return cmd.run(f"git add {' '.join(args)}")
 
 
 def commit(
@@ -140,24 +130,10 @@ def get_commits(
 ) -> list[GitCommit]:
     """Get the commits between start and end."""
     git_log_entries = _get_log_as_str_list(start, end, args)
-    git_commits = []
-    for rev_and_commit in git_log_entries:
-        if not rev_and_commit:
-            continue
-        rev, parents, title, author, author_email, *body_list = rev_and_commit.split(
-            "\n"
-        )
-        if rev_and_commit:
-            git_commit = GitCommit(
-                rev=rev.strip(),
-                title=title.strip(),
-                body="\n".join(body_list).strip(),
-                author=author,
-                author_email=author_email,
-                parents=[p for p in parents.strip().split(" ") if p],
-            )
-            git_commits.append(git_commit)
-    return git_commits
+    return [
+        GitCommit.from_rev_and_commit(rev_and_commit)
+        for rev_and_commit in filter(None, git_log_entries)
+    ]
 
 
 def get_filenames_in_commit(git_reference: str = ""):
@@ -170,8 +146,7 @@ def get_filenames_in_commit(git_reference: str = ""):
     c = cmd.run(f"git show --name-only --pretty=format: {git_reference}")
     if c.return_code == 0:
         return c.out.strip().split("\n")
-    else:
-        raise GitCommandError(c.err)
+    raise GitCommandError(c.err)
 
 
 def get_tags(
@@ -197,16 +172,11 @@ def get_tags(
     if c.err:
         out.warn(f"Attempting to proceed after: {c.err}")
 
-    if not c.out:
-        return []
-
-    git_tags = [
+    return [
         GitTag.from_line(line=line, inner_delimiter=inner_delimiter)
         for line in c.out.split("\n")[:-1]
     ]
 
-    return git_tags
-
 
 def tag_exist(tag: str) -> bool:
     c = cmd.run(f"git tag --list {tag}")
@@ -231,18 +201,18 @@ def get_tag_message(tag: str) -> str | None:
     return c.out.strip()
 
 
-def get_tag_names() -> list[str | None]:
+def get_tag_names() -> list[str]:
     c = cmd.run("git tag --list")
     if c.err:
         return []
-    return [tag.strip() for tag in c.out.split("\n") if tag.strip()]
+    return list(filter(None, (tag.strip() for tag in c.out.split("\n"))))
 
 
 def find_git_project_root() -> Path | None:
     c = cmd.run("git rev-parse --show-toplevel")
-    if not c.err:
-        return Path(c.out.strip())
-    return None
+    if c.err:
+        return None
+    return Path(c.out.strip())
 
 
 def is_staging_clean() -> bool:
@@ -253,32 +223,19 @@ def is_staging_clean() -> bool:
 
 def is_git_project() -> bool:
     c = cmd.run("git rev-parse --is-inside-work-tree")
-    if c.out.strip() == "true":
-        return True
-    return False
+    return c.out.strip() == "true"
 
 
-def get_eol_style() -> EOLTypes:
+def get_eol_for_open() -> str:
+    # See: https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol
     c = cmd.run("git config core.eol")
     eol = c.out.strip().lower()
 
-    # We enumerate the EOL types of the response of
-    # `git config core.eol`, and map it to our enumration EOLTypes.
-    #
-    # It is just like the variant of the "match" syntax.
-    map = {
-        "lf": EOLTypes.LF,
-        "crlf": EOLTypes.CRLF,
-        "native": EOLTypes.NATIVE,
-    }
-
-    # If the response of `git config core.eol` is in the map:
-    if eol in map:
-        return map[eol]
-    else:
-        # The default value is "native".
-        # https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol
-        return map["native"]
+    if eol == "lf":
+        return _UNIX_EOL
+    if eol == "crlf":
+        return _WINDOWS_EOL
+    return os.linesep
 
 
 def get_core_editor() -> str | None:
@@ -288,22 +245,18 @@ def get_core_editor() -> str | None:
     return None
 
 
-def smart_open(*args, **kargs):
+def smart_open(*args, **kwargs):
     """Open a file with the EOL style determined from Git."""
-    return open(*args, newline=get_eol_style().get_eol_for_open(), **kargs)
+    return open(*args, newline=get_eol_for_open(), **kwargs)
 
 
 def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]:
     """Get string representation of each log entry"""
     delimiter = "----------commit-delimiter----------"
     log_format: str = "%H%n%P%n%s%n%an%n%ae%n%b"
-    git_log_cmd = (
-        f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args}"
-    )
-    if start:
-        command = f"{git_log_cmd} {start}..{end}"
-    else:
-        command = f"{git_log_cmd} {end}"
+    command_range = f"{start}..{end}" if start else end
+    command = f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args} {command_range}"
+
     c = cmd.run(command)
     if c.return_code != 0:
         raise GitCommandError(c.err)
diff --git a/tests/test_git.py b/tests/test_git.py
index 8b2fc2b86e..4e4b61ffe2 100644
--- a/tests/test_git.py
+++ b/tests/test_git.py
@@ -79,8 +79,7 @@ def test_get_reachable_tags_with_commits(
     monkeypatch.setenv("LANGUAGE", f"{locale}.UTF-8")
     monkeypatch.setenv("LC_ALL", f"{locale}.UTF-8")
     with tmp_commitizen_project.as_cwd():
-        tags = git.get_tags(reachable_only=True)
-        assert tags == []
+        assert git.get_tags(reachable_only=True) == []
 
 
 def test_get_tag_names(mocker: MockFixture):
@@ -271,7 +270,7 @@ def test_get_commits_with_signature():
 def test_get_tag_names_has_correct_arrow_annotation():
     arrow_annotation = inspect.getfullargspec(git.get_tag_names).annotations["return"]
 
-    assert arrow_annotation == "list[str | None]"
+    assert arrow_annotation == "list[str]"
 
 
 def test_get_latest_tag_name(tmp_commitizen_project):
@@ -317,24 +316,18 @@ def test_is_staging_clean_when_updating_file(tmp_commitizen_project):
         assert git.is_staging_clean() is False
 
 
-def test_git_eol_style(tmp_commitizen_project):
+def test_get_eol_for_open(tmp_commitizen_project):
     with tmp_commitizen_project.as_cwd():
-        assert git.get_eol_style() == git.EOLTypes.NATIVE
+        assert git.get_eol_for_open() == os.linesep
 
         cmd.run("git config core.eol lf")
-        assert git.get_eol_style() == git.EOLTypes.LF
+        assert git.get_eol_for_open() == "\n"
 
         cmd.run("git config core.eol crlf")
-        assert git.get_eol_style() == git.EOLTypes.CRLF
+        assert git.get_eol_for_open() == "\r\n"
 
         cmd.run("git config core.eol native")
-        assert git.get_eol_style() == git.EOLTypes.NATIVE
-
-
-def test_eoltypes_get_eol_for_open():
-    assert git.EOLTypes.get_eol_for_open(git.EOLTypes.NATIVE) == os.linesep
-    assert git.EOLTypes.get_eol_for_open(git.EOLTypes.LF) == "\n"
-    assert git.EOLTypes.get_eol_for_open(git.EOLTypes.CRLF) == "\r\n"
+        assert git.get_eol_for_open() == os.linesep
 
 
 def test_get_core_editor(mocker):
@@ -401,3 +394,14 @@ def test_commit_with_spaces_in_path(mocker, file_path, expected_cmd):
 
     mock_run.assert_called_once_with(expected_cmd)
     mock_unlink.assert_called_once_with(file_path)
+
+
+def test_get_filenames_in_commit_error(mocker: MockFixture):
+    """Test that GitCommandError is raised when git command fails."""
+    mocker.patch(
+        "commitizen.cmd.run",
+        return_value=FakeCommand(out="", err="fatal: bad object HEAD", return_code=1),
+    )
+    with pytest.raises(exceptions.GitCommandError) as excinfo:
+        git.get_filenames_in_commit()
+    assert str(excinfo.value) == "fatal: bad object HEAD"

From 755a04113f1a32256de86ea73d3301ba5875bd8f Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 16:04:08 +0800
Subject: [PATCH 094/169] test(git): add test for from_rev_and_commit

---
 tests/test_git.py | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/tests/test_git.py b/tests/test_git.py
index 4e4b61ffe2..cb4ea93375 100644
--- a/tests/test_git.py
+++ b/tests/test_git.py
@@ -405,3 +405,47 @@ def test_get_filenames_in_commit_error(mocker: MockFixture):
     with pytest.raises(exceptions.GitCommandError) as excinfo:
         git.get_filenames_in_commit()
     assert str(excinfo.value) == "fatal: bad object HEAD"
+
+
+def test_git_commit_from_rev_and_commit():
+    # Test data with all fields populated
+    rev_and_commit = (
+        "abc123\n"  # rev
+        "def456 ghi789\n"  # parents
+        "feat: add new feature\n"  # title
+        "John Doe\n"  # author
+        "john@example.com\n"  # author_email
+        "This is a detailed description\n"  # body
+        "of the new feature\n"
+        "with multiple lines"
+    )
+
+    commit = git.GitCommit.from_rev_and_commit(rev_and_commit)
+
+    assert commit.rev == "abc123"
+    assert commit.title == "feat: add new feature"
+    assert (
+        commit.body
+        == "This is a detailed description\nof the new feature\nwith multiple lines"
+    )
+    assert commit.author == "John Doe"
+    assert commit.author_email == "john@example.com"
+    assert commit.parents == ["def456", "ghi789"]
+
+    # Test with minimal data
+    minimal_commit = (
+        "abc123\n"  # rev
+        "\n"  # no parents
+        "feat: minimal commit\n"  # title
+        "John Doe\n"  # author
+        "john@example.com\n"  # author_email
+    )
+
+    commit = git.GitCommit.from_rev_and_commit(minimal_commit)
+
+    assert commit.rev == "abc123"
+    assert commit.title == "feat: minimal commit"
+    assert commit.body == ""
+    assert commit.author == "John Doe"
+    assert commit.author_email == "john@example.com"
+    assert commit.parents == []

From 1deffcab679bcecc619652d6175d6ef97167ba25 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 16:04:38 +0800
Subject: [PATCH 095/169] docs(git): from_rev_and_commit docstring

---
 commitizen/git.py | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/commitizen/git.py b/commitizen/git.py
index d0cc901a61..d80545bc44 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -43,6 +43,48 @@ def message(self):
 
     @classmethod
     def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit:
+        """Create a GitCommit instance from a formatted commit string.
+
+        This method parses a multi-line string containing commit information in the following format:
+        ```
+        <rev>
+        <parents>
+        <title>
+        <author>
+        <author_email>
+        <body_line_1>
+        <body_line_2>
+        ...
+        ```
+
+        Args:
+            rev_and_commit (str): A string containing commit information with fields separated by newlines.
+                - rev: The commit hash/revision
+                - parents: Space-separated list of parent commit hashes
+                - title: The commit title/message
+                - author: The commit author's name
+                - author_email: The commit author's email
+                - body: Optional multi-line commit body
+
+        Returns:
+            GitCommit: A new GitCommit instance with the parsed information.
+
+        Example:
+            >>> commit_str = '''abc123
+            ... def456 ghi789
+            ... feat: add new feature
+            ... John Doe
+            ... john@example.com
+            ... This is a detailed description
+            ... of the new feature'''
+            >>> commit = GitCommit.from_rev_and_commit(commit_str)
+            >>> commit.rev
+            'abc123'
+            >>> commit.title
+            'feat: add new feature'
+            >>> commit.parents
+            ['def456', 'ghi789']
+        """
         rev, parents, title, author, author_email, *body_list = rev_and_commit.split(
             "\n"
         )

From 1eb35c80ca731518d6f0194904ac390a5fcad021 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 18 May 2025 19:11:52 +0800
Subject: [PATCH 096/169] refactor(EOLType): add eol enum back and reorganize
 methods

---
 commitizen/git.py | 48 ++++++++++++++++++++++++++++++++---------------
 tests/test_git.py |  8 ++++----
 2 files changed, 37 insertions(+), 19 deletions(-)

diff --git a/commitizen/git.py b/commitizen/git.py
index d80545bc44..ef4e0452e3 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -1,14 +1,44 @@
 from __future__ import annotations
 
 import os
+from enum import Enum
+from functools import lru_cache
 from pathlib import Path
 from tempfile import NamedTemporaryFile
 
 from commitizen import cmd, out
 from commitizen.exceptions import GitCommandError
 
-_UNIX_EOL = "\n"
-_WINDOWS_EOL = "\r\n"
+
+class EOLType(Enum):
+    """The EOL type from `git config core.eol`."""
+
+    LF = "lf"
+    CRLF = "crlf"
+    NATIVE = "native"
+
+    @classmethod
+    def for_open(cls) -> str:
+        c = cmd.run("git config core.eol")
+        eol = c.out.strip().upper()
+        return cls._char_for_open()[cls._safe_cast(eol)]
+
+    @classmethod
+    def _safe_cast(cls, eol: str) -> EOLType:
+        try:
+            return cls[eol]
+        except KeyError:
+            return cls.NATIVE
+
+    @classmethod
+    @lru_cache
+    def _char_for_open(cls) -> dict[EOLType, str]:
+        """Get the EOL character for `open()`."""
+        return {
+            cls.LF: "\n",
+            cls.CRLF: "\r\n",
+            cls.NATIVE: os.linesep,
+        }
 
 
 class GitObject:
@@ -268,18 +298,6 @@ def is_git_project() -> bool:
     return c.out.strip() == "true"
 
 
-def get_eol_for_open() -> str:
-    # See: https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol
-    c = cmd.run("git config core.eol")
-    eol = c.out.strip().lower()
-
-    if eol == "lf":
-        return _UNIX_EOL
-    if eol == "crlf":
-        return _WINDOWS_EOL
-    return os.linesep
-
-
 def get_core_editor() -> str | None:
     c = cmd.run("git var GIT_EDITOR")
     if c.out:
@@ -289,7 +307,7 @@ def get_core_editor() -> str | None:
 
 def smart_open(*args, **kwargs):
     """Open a file with the EOL style determined from Git."""
-    return open(*args, newline=get_eol_for_open(), **kwargs)
+    return open(*args, newline=EOLType.for_open(), **kwargs)
 
 
 def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]:
diff --git a/tests/test_git.py b/tests/test_git.py
index cb4ea93375..de3130412b 100644
--- a/tests/test_git.py
+++ b/tests/test_git.py
@@ -318,16 +318,16 @@ def test_is_staging_clean_when_updating_file(tmp_commitizen_project):
 
 def test_get_eol_for_open(tmp_commitizen_project):
     with tmp_commitizen_project.as_cwd():
-        assert git.get_eol_for_open() == os.linesep
+        assert git.EOLType.for_open() == os.linesep
 
         cmd.run("git config core.eol lf")
-        assert git.get_eol_for_open() == "\n"
+        assert git.EOLType.for_open() == "\n"
 
         cmd.run("git config core.eol crlf")
-        assert git.get_eol_for_open() == "\r\n"
+        assert git.EOLType.for_open() == "\r\n"
 
         cmd.run("git config core.eol native")
-        assert git.get_eol_for_open() == os.linesep
+        assert git.EOLType.for_open() == os.linesep
 
 
 def test_get_core_editor(mocker):

From f67b29b0970bc72d36e27c6d9a50dc68d21759fd Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 18 May 2025 19:26:22 +0800
Subject: [PATCH 097/169] refactor(git): refactor get_tag_names

---
 commitizen/git.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/commitizen/git.py b/commitizen/git.py
index ef4e0452e3..fa59e34d48 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -277,7 +277,7 @@ def get_tag_names() -> list[str]:
     c = cmd.run("git tag --list")
     if c.err:
         return []
-    return list(filter(None, (tag.strip() for tag in c.out.split("\n"))))
+    return [tag for raw in c.out.split("\n") if (tag := raw.strip())]
 
 
 def find_git_project_root() -> Path | None:

From febd0d6296430e7c49f13e4522092741a102cbed Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Tue, 20 May 2025 00:46:16 +0800
Subject: [PATCH 098/169] refactor(changelog): minor cleanup

---
 commitizen/commands/changelog.py | 45 ++++++++++++++++----------------
 1 file changed, 22 insertions(+), 23 deletions(-)

diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 0e4efabfa1..9ab8fdc37c 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -2,10 +2,10 @@
 
 import os
 import os.path
+from collections.abc import Generator
 from difflib import SequenceMatcher
 from operator import itemgetter
 from pathlib import Path
-from typing import Callable, cast
 
 from commitizen import changelog, defaults, factory, git, out
 from commitizen.changelog_formats import get_changelog_format
@@ -32,9 +32,10 @@ def __init__(self, config: BaseConfig, args):
         if not git.is_git_project():
             raise NotAGitProjectError()
 
-        self.config: BaseConfig = config
-        changelog_file_name = args.get("file_name") or cast(
-            str, self.config.settings.get("changelog_file")
+        self.config = config
+
+        changelog_file_name = args.get("file_name") or self.config.settings.get(
+            "changelog_file"
         )
         if not isinstance(changelog_file_name, str):
             raise NotAllowed(
@@ -114,28 +115,28 @@ def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str:
         on our experience.
         """
         SIMILARITY_THRESHOLD = 0.89
-        tag_ratio = map(
-            lambda tag: (
-                SequenceMatcher(
+        scores_and_tag_names: Generator[tuple[float, str]] = (
+            (
+                score,
+                tag.name,
+            )
+            for tag in tags
+            if (
+                score := SequenceMatcher(
                     None, latest_version, strip_local_version(tag.name)
-                ).ratio(),
-                tag,
-            ),
-            tags,
+                ).ratio()
+            )
+            >= SIMILARITY_THRESHOLD
         )
         try:
-            score, tag = max(tag_ratio, key=itemgetter(0))
+            _, start_rev = max(scores_and_tag_names, key=itemgetter(0))
         except ValueError:
             raise NoRevisionError()
-        if score < SIMILARITY_THRESHOLD:
-            raise NoRevisionError()
-        start_rev = tag.name
         return start_rev
 
     def write_changelog(
         self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata
     ):
-        changelog_hook: Callable | None = self.cz.changelog_hook
         with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file:
             partial_changelog: str | None = None
             if self.incremental:
@@ -145,8 +146,8 @@ def write_changelog(
                 changelog_out = "".join(new_lines)
                 partial_changelog = changelog_out
 
-            if changelog_hook:
-                changelog_out = changelog_hook(changelog_out, partial_changelog)
+            if self.cz.changelog_hook:
+                changelog_out = self.cz.changelog_hook(changelog_out, partial_changelog)
 
             changelog_file.write(changelog_out)
 
@@ -221,14 +222,12 @@ def __call__(self):
         extras.update(self.extras)
         changelog_out = changelog.render_changelog(
             tree, loader=self.cz.template_loader, template=self.template, **extras
-        )
-        changelog_out = changelog_out.lstrip("\n")
+        ).lstrip("\n")
 
         # Dry_run is executed here to avoid checking and reading the files
         if self.dry_run:
-            changelog_hook: Callable | None = self.cz.changelog_hook
-            if changelog_hook:
-                changelog_out = changelog_hook(changelog_out, "")
+            if self.cz.changelog_hook:
+                changelog_out = self.cz.changelog_hook(changelog_out, "")
             out.write(changelog_out)
             raise DryRunExit()
 

From f3fa22cefe0ac681e2c6e1f80ad119d1df135903 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Tue, 20 May 2025 21:23:30 +0800
Subject: [PATCH 099/169] refactor(BaseConfig): use setter

---
 commitizen/config/base_config.py    | 12 +++++++++---
 commitizen/config/json_config.py    |  2 +-
 commitizen/config/toml_config.py    |  2 +-
 commitizen/config/yaml_config.py    |  2 +-
 tests/commands/test_init_command.py |  2 +-
 tests/test_changelog.py             |  4 +++-
 6 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py
index 478691aa14..fd034412fe 100644
--- a/commitizen/config/base_config.py
+++ b/commitizen/config/base_config.py
@@ -19,6 +19,15 @@ def settings(self) -> Settings:
     def path(self) -> Path | None:
         return self._path
 
+    @path.setter
+    def path(self, path: str | Path) -> None:
+        """
+        mypy does not like this until 1.16
+        See https://github.com/python/mypy/pull/18510
+        TODO: remove "type: ignore" from the call sites when 1.16 is available
+        """
+        self._path = Path(path)
+
     def set_key(self, key, value):
         """Set or update a key in the conf.
 
@@ -30,8 +39,5 @@ def set_key(self, key, value):
     def update(self, data: Settings) -> None:
         self._settings.update(data)
 
-    def add_path(self, path: str | Path) -> None:
-        self._path = Path(path)
-
     def _parse_setting(self, data: bytes | str) -> None:
         raise NotImplementedError()
diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py
index b6a07f4ced..d413d73383 100644
--- a/commitizen/config/json_config.py
+++ b/commitizen/config/json_config.py
@@ -13,7 +13,7 @@ class JsonConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
         super().__init__()
         self.is_empty_config = False
-        self.add_path(path)
+        self.path = path  # type: ignore
         self._parse_setting(data)
 
     def init_empty_config_content(self):
diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py
index 813389cbcf..e2cfcc9340 100644
--- a/commitizen/config/toml_config.py
+++ b/commitizen/config/toml_config.py
@@ -14,7 +14,7 @@ class TomlConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
         super().__init__()
         self.is_empty_config = False
-        self.add_path(path)
+        self.path = path  # type: ignore
         self._parse_setting(data)
 
     def init_empty_config_content(self):
diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py
index 2bb6fe3af8..c5721c8d4b 100644
--- a/commitizen/config/yaml_config.py
+++ b/commitizen/config/yaml_config.py
@@ -14,7 +14,7 @@ class YAMLConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
         super().__init__()
         self.is_empty_config = False
-        self.add_path(path)
+        self.path = path  # type: ignore
         self._parse_setting(data)
 
     def init_empty_config_content(self):
diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py
index f617c51d8f..3f12d0bd7f 100644
--- a/tests/commands/test_init_command.py
+++ b/tests/commands/test_init_command.py
@@ -86,7 +86,7 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config)
 def test_init_when_config_already_exists(config, capsys):
     # Set config path
     path = os.sep.join(["tests", "pyproject.toml"])
-    config.add_path(path)
+    config.path = path
 
     commands.Init(config)()
     captured = capsys.readouterr()
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index 511cd1a3df..d42176f09a 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1639,7 +1639,9 @@ def test_tags_rules_get_version_tags(capsys: pytest.CaptureFixture):
 
 def test_changelog_file_name_from_args_and_config():
     mock_config = Mock(spec=BaseConfig)
-    mock_config.path.parent = "/my/project"
+    mock_path = Mock(spec=Path)
+    mock_path.parent = Path("/my/project")
+    mock_config.path = mock_path
     mock_config.settings = {
         "name": "cz_conventional_commits",
         "changelog_file": "CHANGELOG.md",

From c939df1f46e8aa9c28e1ddff60d523ff540cfab5 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Tue, 20 May 2025 23:34:37 +0800
Subject: [PATCH 100/169] build(poetry): upgrade mypy version to ^1.15.0

---
 poetry.lock    | 1086 +++++++++++++++++++++++-------------------------
 pyproject.toml |   14 +-
 2 files changed, 513 insertions(+), 587 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index 64ec358bb3..ceccd3da1c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -17,60 +17,76 @@ test = ["coverage", "mypy", "pexpect", "ruff", "wheel"]
 
 [[package]]
 name = "asttokens"
-version = "2.4.1"
+version = "3.0.0"
 description = "Annotate AST trees with source code positions"
 optional = false
-python-versions = "*"
+python-versions = ">=3.8"
 groups = ["dev"]
 files = [
-    {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
-    {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
+    {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
+    {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
 ]
 
-[package.dependencies]
-six = ">=1.12.0"
-
 [package.extras]
-astroid = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\""]
-test = ["astroid (>=1,<2) ; python_version < \"3\"", "astroid (>=2,<4) ; python_version >= \"3\"", "pytest"]
+astroid = ["astroid (>=2,<4)"]
+test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"]
 
 [[package]]
 name = "babel"
-version = "2.16.0"
+version = "2.17.0"
 description = "Internationalization utilities"
 optional = false
 python-versions = ">=3.8"
 groups = ["documentation"]
 files = [
-    {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"},
-    {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"},
+    {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"},
+    {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"},
+]
+
+[package.extras]
+dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""]
+
+[[package]]
+name = "backrefs"
+version = "5.8"
+description = "A wrapper around re and regex that adds additional back references."
+optional = false
+python-versions = ">=3.9"
+groups = ["documentation"]
+files = [
+    {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"},
+    {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"},
+    {file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"},
+    {file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"},
+    {file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"},
+    {file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"},
 ]
 
 [package.extras]
-dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
+extras = ["regex"]
 
 [[package]]
 name = "cachetools"
-version = "5.5.1"
+version = "6.0.0"
 description = "Extensible memoizing collections and decorators"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.9"
 groups = ["dev"]
 files = [
-    {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"},
-    {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"},
+    {file = "cachetools-6.0.0-py3-none-any.whl", hash = "sha256:82e73ba88f7b30228b5507dce1a1f878498fc669d972aef2dde4f3a3c24f103e"},
+    {file = "cachetools-6.0.0.tar.gz", hash = "sha256:f225782b84438f828328fc2ad74346522f27e5b1440f4e9fd18b20ebfd1aa2cf"},
 ]
 
 [[package]]
 name = "certifi"
-version = "2024.8.30"
+version = "2025.4.26"
 description = "Python package for providing Mozilla's CA Bundle."
 optional = false
 python-versions = ">=3.6"
 groups = ["documentation"]
 files = [
-    {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
-    {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
+    {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"},
+    {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"},
 ]
 
 [[package]]
@@ -99,116 +115,116 @@ files = [
 
 [[package]]
 name = "charset-normalizer"
-version = "3.4.1"
+version = "3.4.2"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 optional = false
 python-versions = ">=3.7"
 groups = ["main", "documentation"]
 files = [
-    {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"},
-    {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"},
-    {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"},
-    {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"},
-    {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"},
-    {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"},
-    {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"},
-    {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"},
-    {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"},
-    {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"},
+    {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"},
+    {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"},
 ]
 
 [[package]]
 name = "click"
-version = "8.1.7"
+version = "8.1.8"
 description = "Composable command line interface toolkit"
 optional = false
 python-versions = ">=3.7"
 groups = ["documentation"]
 files = [
-    {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
-    {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
+    {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
+    {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
 ]
 
 [package.dependencies]
@@ -228,74 +244,79 @@ files = [
 
 [[package]]
 name = "coverage"
-version = "7.6.8"
+version = "7.8.2"
 description = "Code coverage measurement for Python"
 optional = false
 python-versions = ">=3.9"
 groups = ["test"]
 files = [
-    {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"},
-    {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"},
-    {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"},
-    {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"},
-    {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"},
-    {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"},
-    {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"},
-    {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"},
-    {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"},
-    {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"},
-    {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"},
-    {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"},
-    {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"},
-    {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"},
-    {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"},
-    {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"},
-    {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"},
-    {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"},
-    {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"},
-    {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"},
-    {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"},
-    {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"},
-    {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"},
-    {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"},
-    {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"},
-    {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"},
-    {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"},
-    {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"},
-    {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"},
-    {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"},
-    {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"},
-    {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"},
-    {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"},
-    {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"},
-    {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"},
-    {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"},
-    {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"},
-    {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"},
-    {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"},
-    {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"},
-    {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"},
-    {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"},
-    {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"},
-    {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"},
-    {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"},
-    {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"},
-    {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"},
-    {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"},
-    {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"},
-    {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"},
-    {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"},
-    {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"},
-    {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"},
-    {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"},
-    {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"},
-    {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"},
-    {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"},
-    {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"},
-    {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"},
-    {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"},
-    {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"},
-    {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"},
+    {file = "coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a"},
+    {file = "coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be"},
+    {file = "coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3"},
+    {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6"},
+    {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622"},
+    {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c"},
+    {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3"},
+    {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404"},
+    {file = "coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7"},
+    {file = "coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347"},
+    {file = "coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9"},
+    {file = "coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879"},
+    {file = "coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a"},
+    {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5"},
+    {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11"},
+    {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a"},
+    {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb"},
+    {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54"},
+    {file = "coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a"},
+    {file = "coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975"},
+    {file = "coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53"},
+    {file = "coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c"},
+    {file = "coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1"},
+    {file = "coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279"},
+    {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99"},
+    {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20"},
+    {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2"},
+    {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57"},
+    {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f"},
+    {file = "coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8"},
+    {file = "coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223"},
+    {file = "coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f"},
+    {file = "coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca"},
+    {file = "coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d"},
+    {file = "coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85"},
+    {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257"},
+    {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108"},
+    {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0"},
+    {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050"},
+    {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48"},
+    {file = "coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7"},
+    {file = "coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3"},
+    {file = "coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7"},
+    {file = "coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008"},
+    {file = "coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36"},
+    {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46"},
+    {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be"},
+    {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740"},
+    {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625"},
+    {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b"},
+    {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199"},
+    {file = "coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8"},
+    {file = "coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d"},
+    {file = "coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b"},
+    {file = "coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a"},
+    {file = "coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d"},
+    {file = "coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca"},
+    {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d"},
+    {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787"},
+    {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7"},
+    {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3"},
+    {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7"},
+    {file = "coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a"},
+    {file = "coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e"},
+    {file = "coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837"},
+    {file = "coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32"},
+    {file = "coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27"},
 ]
 
 [package.dependencies]
@@ -306,26 +327,26 @@ toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
 
 [[package]]
 name = "decli"
-version = "0.6.2"
+version = "0.6.3"
 description = "Minimal, easy-to-use, declarative cli tool"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.9"
 groups = ["main"]
 files = [
-    {file = "decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed"},
-    {file = "decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f"},
+    {file = "decli-0.6.3-py3-none-any.whl", hash = "sha256:5152347c7bb8e3114ad65db719e5709b28d7f7f45bdb709f70167925e55640f3"},
+    {file = "decli-0.6.3.tar.gz", hash = "sha256:87f9d39361adf7f16b9ca6e3b614badf7519da13092f2db3c80ca223c53c7656"},
 ]
 
 [[package]]
 name = "decorator"
-version = "5.1.1"
+version = "5.2.1"
 description = "Decorators for Humans"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
 groups = ["dev"]
 files = [
-    {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
-    {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
+    {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
+    {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
 ]
 
 [[package]]
@@ -360,17 +381,20 @@ files = [
 
 [[package]]
 name = "exceptiongroup"
-version = "1.2.2"
+version = "1.3.0"
 description = "Backport of PEP 654 (exception groups)"
 optional = false
 python-versions = ">=3.7"
 groups = ["dev", "test"]
 markers = "python_version < \"3.11\""
 files = [
-    {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
-    {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
+    {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
+    {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
 ]
 
+[package.dependencies]
+typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
+
 [package.extras]
 test = ["pytest (>=6)"]
 
@@ -391,14 +415,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"]
 
 [[package]]
 name = "executing"
-version = "2.1.0"
+version = "2.2.0"
 description = "Get the currently executing AST node of a frame, and other information"
 optional = false
 python-versions = ">=3.8"
 groups = ["dev"]
 files = [
-    {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"},
-    {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"},
+    {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"},
+    {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"},
 ]
 
 [package.extras]
@@ -406,31 +430,31 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth
 
 [[package]]
 name = "filelock"
-version = "3.16.1"
+version = "3.18.0"
 description = "A platform independent file lock."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["dev", "linters"]
 files = [
-    {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
-    {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
+    {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"},
+    {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"},
 ]
 
 [package.extras]
-docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
-testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
+docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
 typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
 
 [[package]]
 name = "freezegun"
-version = "1.5.1"
+version = "1.5.2"
 description = "Let your Python tests travel through time"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 groups = ["test"]
 files = [
-    {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"},
-    {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"},
+    {file = "freezegun-1.5.2-py3-none-any.whl", hash = "sha256:5aaf3ba229cda57afab5bd311f0108d86b6fb119ae89d2cd9c43ec8c1733c85b"},
+    {file = "freezegun-1.5.2.tar.gz", hash = "sha256:a54ae1d2f9c02dbf42e02c18a3ab95ab4295818b549a34dac55592d72a905181"},
 ]
 
 [package.dependencies]
@@ -456,14 +480,14 @@ dev = ["flake8", "markdown", "twine", "wheel"]
 
 [[package]]
 name = "identify"
-version = "2.6.3"
+version = "2.6.12"
 description = "File identification library for Python"
 optional = false
 python-versions = ">=3.9"
 groups = ["linters"]
 files = [
-    {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"},
-    {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"},
+    {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"},
+    {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"},
 ]
 
 [package.extras]
@@ -486,15 +510,15 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
 
 [[package]]
 name = "importlib-metadata"
-version = "8.6.1"
+version = "8.7.0"
 description = "Read metadata from Python packages"
 optional = false
 python-versions = ">=3.9"
 groups = ["main", "documentation"]
 markers = "python_version == \"3.9\""
 files = [
-    {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
-    {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
+    {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"},
+    {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"},
 ]
 
 [package.dependencies]
@@ -511,14 +535,14 @@ type = ["pytest-mypy"]
 
 [[package]]
 name = "iniconfig"
-version = "2.0.0"
+version = "2.1.0"
 description = "brain-dead simple config-ini parsing"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 groups = ["test"]
 files = [
-    {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
-    {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+    {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
+    {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
 ]
 
 [[package]]
@@ -581,14 +605,14 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
 
 [[package]]
 name = "jinja2"
-version = "3.1.5"
+version = "3.1.6"
 description = "A very fast and expressive template engine."
 optional = false
 python-versions = ">=3.7"
 groups = ["main", "documentation"]
 files = [
-    {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
-    {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
+    {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
+    {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
 ]
 
 [package.dependencies]
@@ -599,21 +623,21 @@ i18n = ["Babel (>=2.7)"]
 
 [[package]]
 name = "markdown"
-version = "3.7"
+version = "3.8"
 description = "Python implementation of John Gruber's Markdown."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["documentation"]
 files = [
-    {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"},
-    {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"},
+    {file = "markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc"},
+    {file = "markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"},
 ]
 
 [package.dependencies]
 importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
 
 [package.extras]
-docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
+docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
 testing = ["coverage", "pyyaml"]
 
 [[package]]
@@ -803,27 +827,27 @@ pyyaml = ">=5.1"
 
 [[package]]
 name = "mkdocs-material"
-version = "9.5.50"
+version = "9.6.14"
 description = "Documentation that simply works"
 optional = false
 python-versions = ">=3.8"
 groups = ["documentation"]
 files = [
-    {file = "mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385"},
-    {file = "mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825"},
+    {file = "mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b"},
+    {file = "mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754"},
 ]
 
 [package.dependencies]
 babel = ">=2.10,<3.0"
+backrefs = ">=5.7.post1,<6.0"
 colorama = ">=0.4,<1.0"
-jinja2 = ">=3.0,<4.0"
+jinja2 = ">=3.1,<4.0"
 markdown = ">=3.2,<4.0"
 mkdocs = ">=1.6,<2.0"
 mkdocs-material-extensions = ">=1.3,<2.0"
 paginate = ">=0.5,<1.0"
 pygments = ">=2.16,<3.0"
 pymdown-extensions = ">=10.2,<11.0"
-regex = ">=2022.4"
 requests = ">=2.26,<3.0"
 
 [package.extras]
@@ -845,54 +869,49 @@ files = [
 
 [[package]]
 name = "mypy"
-version = "1.14.1"
+version = "1.16.0"
 description = "Optional static typing for Python"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["linters"]
 files = [
-    {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"},
-    {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"},
-    {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"},
-    {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"},
-    {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"},
-    {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"},
-    {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"},
-    {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"},
-    {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"},
-    {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"},
-    {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"},
-    {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"},
-    {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"},
-    {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"},
-    {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"},
-    {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"},
-    {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"},
-    {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"},
-    {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"},
-    {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"},
-    {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"},
-    {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"},
-    {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"},
-    {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"},
-    {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"},
-    {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"},
-    {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"},
-    {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"},
-    {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"},
-    {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"},
-    {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"},
-    {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"},
-    {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"},
-    {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"},
-    {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"},
-    {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"},
-    {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"},
-    {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"},
+    {file = "mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c"},
+    {file = "mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571"},
+    {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491"},
+    {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777"},
+    {file = "mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b"},
+    {file = "mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93"},
+    {file = "mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab"},
+    {file = "mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2"},
+    {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff"},
+    {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666"},
+    {file = "mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c"},
+    {file = "mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b"},
+    {file = "mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13"},
+    {file = "mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090"},
+    {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1"},
+    {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8"},
+    {file = "mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730"},
+    {file = "mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec"},
+    {file = "mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b"},
+    {file = "mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0"},
+    {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b"},
+    {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d"},
+    {file = "mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52"},
+    {file = "mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb"},
+    {file = "mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3"},
+    {file = "mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92"},
+    {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436"},
+    {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2"},
+    {file = "mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20"},
+    {file = "mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21"},
+    {file = "mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031"},
+    {file = "mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab"},
 ]
 
 [package.dependencies]
 mypy_extensions = ">=1.0.0"
+pathspec = ">=0.9.0"
 tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
 typing_extensions = ">=4.6.0"
 
@@ -905,14 +924,14 @@ reports = ["lxml"]
 
 [[package]]
 name = "mypy-extensions"
-version = "1.0.0"
+version = "1.1.0"
 description = "Type system extensions for programs checked with the mypy type checker."
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
 groups = ["linters"]
 files = [
-    {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
-    {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
+    {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
+    {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
 ]
 
 [[package]]
@@ -929,14 +948,14 @@ files = [
 
 [[package]]
 name = "packaging"
-version = "24.2"
+version = "25.0"
 description = "Core utilities for Python packages"
 optional = false
 python-versions = ">=3.8"
 groups = ["main", "dev", "documentation", "test"]
 files = [
-    {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
-    {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
+    {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
+    {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
 ]
 
 [[package]]
@@ -989,7 +1008,7 @@ version = "0.12.1"
 description = "Utility library for gitignore style pattern matching of file paths."
 optional = false
 python-versions = ">=3.8"
-groups = ["documentation"]
+groups = ["documentation", "linters"]
 files = [
     {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
     {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@@ -1013,36 +1032,36 @@ ptyprocess = ">=0.5"
 
 [[package]]
 name = "platformdirs"
-version = "4.3.6"
+version = "4.3.8"
 description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["dev", "documentation", "linters"]
 files = [
-    {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
-    {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
+    {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"},
+    {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"},
 ]
 
 [package.extras]
-docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
-type = ["mypy (>=1.11.2)"]
+docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"]
+type = ["mypy (>=1.14.1)"]
 
 [[package]]
 name = "pluggy"
-version = "1.5.0"
+version = "1.6.0"
 description = "plugin and hook calling mechanisms for python"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["dev", "test"]
 files = [
-    {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
-    {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
+    {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
+    {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
 ]
 
 [package.extras]
 dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
+testing = ["coverage", "pytest", "pytest-benchmark"]
 
 [[package]]
 name = "poethepoet"
@@ -1066,14 +1085,14 @@ poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""]
 
 [[package]]
 name = "pre-commit"
-version = "4.1.0"
+version = "4.2.0"
 description = "A framework for managing and maintaining multi-language pre-commit hooks."
 optional = false
 python-versions = ">=3.9"
 groups = ["linters"]
 files = [
-    {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"},
-    {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"},
+    {file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"},
+    {file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"},
 ]
 
 [package.dependencies]
@@ -1085,14 +1104,14 @@ virtualenv = ">=20.10.0"
 
 [[package]]
 name = "prompt-toolkit"
-version = "3.0.48"
+version = "3.0.51"
 description = "Library for building powerful interactive command lines in Python"
 optional = false
-python-versions = ">=3.7.0"
+python-versions = ">=3.8"
 groups = ["main", "dev"]
 files = [
-    {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"},
-    {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"},
+    {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"},
+    {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"},
 ]
 
 [package.dependencies]
@@ -1128,14 +1147,14 @@ tests = ["pytest"]
 
 [[package]]
 name = "pygments"
-version = "2.18.0"
+version = "2.19.1"
 description = "Pygments is a syntax highlighting package written in Python."
 optional = false
 python-versions = ">=3.8"
-groups = ["dev", "documentation", "script"]
+groups = ["dev", "documentation", "script", "test"]
 files = [
-    {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
-    {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
+    {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
+    {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
 ]
 
 [package.extras]
@@ -1143,14 +1162,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
 
 [[package]]
 name = "pymdown-extensions"
-version = "10.12"
+version = "10.15"
 description = "Extension pack for Python Markdown."
 optional = false
 python-versions = ">=3.8"
 groups = ["documentation"]
 files = [
-    {file = "pymdown_extensions-10.12-py3-none-any.whl", hash = "sha256:49f81412242d3527b8b4967b990df395c89563043bc51a3d2d7d500e52123b77"},
-    {file = "pymdown_extensions-10.12.tar.gz", hash = "sha256:b0ee1e0b2bef1071a47891ab17003bfe5bf824a398e13f49f8ed653b699369a7"},
+    {file = "pymdown_extensions-10.15-py3-none-any.whl", hash = "sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f"},
+    {file = "pymdown_extensions-10.15.tar.gz", hash = "sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7"},
 ]
 
 [package.dependencies]
@@ -1158,61 +1177,62 @@ markdown = ">=3.6"
 pyyaml = "*"
 
 [package.extras]
-extra = ["pygments (>=2.12)"]
+extra = ["pygments (>=2.19.1)"]
 
 [[package]]
 name = "pyproject-api"
-version = "1.9.0"
+version = "1.9.1"
 description = "API to interact with the python pyproject.toml based projects"
 optional = false
 python-versions = ">=3.9"
 groups = ["dev"]
 files = [
-    {file = "pyproject_api-1.9.0-py3-none-any.whl", hash = "sha256:326df9d68dea22d9d98b5243c46e3ca3161b07a1b9b18e213d1e24fd0e605766"},
-    {file = "pyproject_api-1.9.0.tar.gz", hash = "sha256:7e8a9854b2dfb49454fae421cb86af43efbb2b2454e5646ffb7623540321ae6e"},
+    {file = "pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948"},
+    {file = "pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335"},
 ]
 
 [package.dependencies]
-packaging = ">=24.2"
+packaging = ">=25"
 tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""}
 
 [package.extras]
-docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=3)"]
-testing = ["covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "setuptools (>=75.8)"]
+docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=3.2)"]
+testing = ["covdefaults (>=2.3)", "pytest (>=8.3.5)", "pytest-cov (>=6.1.1)", "pytest-mock (>=3.14)", "setuptools (>=80.3.1)"]
 
 [[package]]
 name = "pytest"
-version = "8.3.4"
+version = "8.4.0"
 description = "pytest: simple powerful testing with Python"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["test"]
 files = [
-    {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"},
-    {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"},
+    {file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"},
+    {file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"},
 ]
 
 [package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
-iniconfig = "*"
-packaging = "*"
+colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
+iniconfig = ">=1"
+packaging = ">=20"
 pluggy = ">=1.5,<2"
+pygments = ">=2.7.2"
 tomli = {version = ">=1", markers = "python_version < \"3.11\""}
 
 [package.extras]
-dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
 
 [[package]]
 name = "pytest-cov"
-version = "6.0.0"
+version = "6.1.1"
 description = "Pytest plugin for measuring coverage."
 optional = false
 python-versions = ">=3.9"
 groups = ["test"]
 files = [
-    {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"},
-    {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"},
+    {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"},
+    {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"},
 ]
 
 [package.dependencies]
@@ -1224,18 +1244,22 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
 
 [[package]]
 name = "pytest-datadir"
-version = "1.5.0"
+version = "1.7.2"
 description = "pytest plugin for test data directories and files"
 optional = false
 python-versions = ">=3.8"
 groups = ["test"]
 files = [
-    {file = "pytest-datadir-1.5.0.tar.gz", hash = "sha256:1617ed92f9afda0c877e4eac91904b5f779d24ba8f5e438752e3ae39d8d2ee3f"},
-    {file = "pytest_datadir-1.5.0-py3-none-any.whl", hash = "sha256:34adf361bcc7b37961bbc1dfa8d25a4829e778bab461703c38a5c50ca9c36dc8"},
+    {file = "pytest_datadir-1.7.2-py3-none-any.whl", hash = "sha256:8392ba0e9eaf37030e663dcd91cc5123dec99c44300f0c5eac44f35f13f0e086"},
+    {file = "pytest_datadir-1.7.2.tar.gz", hash = "sha256:15f5228a35d0a3205e4968e75d3b9cca91762424e1eafc21eb637d380a48443e"},
 ]
 
 [package.dependencies]
-pytest = ">=5.0"
+pytest = ">=7.0"
+
+[package.extras]
+dev = ["pre-commit", "pytest-datadir[testing]"]
+testing = ["pytest", "tox"]
 
 [[package]]
 name = "pytest-freezer"
@@ -1255,14 +1279,14 @@ pytest = ">=3.6"
 
 [[package]]
 name = "pytest-mock"
-version = "3.14.0"
+version = "3.14.1"
 description = "Thin-wrapper around the mock package for easier use with pytest"
 optional = false
 python-versions = ">=3.8"
 groups = ["test"]
 files = [
-    {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"},
-    {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"},
+    {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"},
+    {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"},
 ]
 
 [package.dependencies]
@@ -1273,19 +1297,19 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
 
 [[package]]
 name = "pytest-regressions"
-version = "2.7.0"
+version = "2.8.0"
 description = "Easy to use fixtures to write regression tests."
 optional = false
 python-versions = ">=3.9"
 groups = ["test"]
 files = [
-    {file = "pytest_regressions-2.7.0-py3-none-any.whl", hash = "sha256:69f5e3f03493cf0ef84d96d23e50a546617c198b1d7746f2e2b9e441cbab4847"},
-    {file = "pytest_regressions-2.7.0.tar.gz", hash = "sha256:4c30064e0923929012c94f5d6f35205be06fd8709c7f0dba0228e05c460af05e"},
+    {file = "pytest_regressions-2.8.0-py3-none-any.whl", hash = "sha256:2926f37efa5fd6793806b10352e274c5284a5469a845aeab6243e86f9214766f"},
+    {file = "pytest_regressions-2.8.0.tar.gz", hash = "sha256:775044e17117f5427df2caad3ab1c66889abe770a0ce2bc3f24fdeac99af76fe"},
 ]
 
 [package.dependencies]
 pytest = ">=6.2.0"
-pytest-datadir = ">=1.2.0"
+pytest-datadir = ">=1.7.0"
 pyyaml = "*"
 
 [package.extras]
@@ -1296,14 +1320,14 @@ num = ["numpy", "pandas"]
 
 [[package]]
 name = "pytest-xdist"
-version = "3.6.1"
+version = "3.7.0"
 description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["test"]
 files = [
-    {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
-    {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
+    {file = "pytest_xdist-3.7.0-py3-none-any.whl", hash = "sha256:7d3fbd255998265052435eb9daa4e99b62e6fb9cfb6efd1f858d4d8c0c7f0ca0"},
+    {file = "pytest_xdist-3.7.0.tar.gz", hash = "sha256:f9248c99a7c15b7d2f90715df93610353a485827bc06eefb6566d23f6400f126"},
 ]
 
 [package.dependencies]
@@ -1395,14 +1419,14 @@ files = [
 
 [[package]]
 name = "pyyaml-env-tag"
-version = "0.1"
-description = "A custom YAML tag for referencing environment variables in YAML files. "
+version = "1.1"
+description = "A custom YAML tag for referencing environment variables in YAML files."
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.9"
 groups = ["documentation"]
 files = [
-    {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
-    {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
+    {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"},
+    {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"},
 ]
 
 [package.dependencies]
@@ -1423,110 +1447,6 @@ files = [
 [package.dependencies]
 prompt_toolkit = ">=2.0,<4.0"
 
-[[package]]
-name = "regex"
-version = "2024.11.6"
-description = "Alternative regular expression module, to replace re."
-optional = false
-python-versions = ">=3.8"
-groups = ["documentation"]
-files = [
-    {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"},
-    {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"},
-    {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"},
-    {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"},
-    {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"},
-    {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"},
-    {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"},
-    {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"},
-    {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"},
-    {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"},
-    {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"},
-    {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"},
-    {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"},
-    {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"},
-    {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"},
-    {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"},
-    {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"},
-    {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"},
-    {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"},
-    {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"},
-    {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"},
-    {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"},
-    {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"},
-    {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"},
-    {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"},
-    {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"},
-    {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"},
-    {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"},
-    {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"},
-    {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"},
-    {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"},
-    {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"},
-    {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"},
-    {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"},
-    {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"},
-    {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"},
-    {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"},
-    {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"},
-    {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"},
-    {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"},
-    {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"},
-    {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"},
-    {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"},
-    {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"},
-    {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"},
-    {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"},
-    {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"},
-    {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"},
-    {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"},
-    {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"},
-    {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"},
-    {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"},
-    {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"},
-    {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"},
-    {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"},
-    {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"},
-    {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"},
-    {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"},
-    {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"},
-    {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"},
-    {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"},
-    {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"},
-    {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"},
-    {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"},
-    {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"},
-    {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"},
-    {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"},
-    {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"},
-    {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"},
-    {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"},
-    {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"},
-    {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"},
-    {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"},
-    {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"},
-    {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"},
-    {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"},
-    {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"},
-    {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"},
-    {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"},
-    {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"},
-    {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"},
-    {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"},
-    {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"},
-    {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"},
-    {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"},
-    {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"},
-    {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"},
-    {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"},
-    {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"},
-    {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"},
-    {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"},
-    {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"},
-    {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"},
-    {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"},
-]
-
 [[package]]
 name = "requests"
 version = "2.32.3"
@@ -1571,42 +1491,42 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
 
 [[package]]
 name = "ruff"
-version = "0.9.4"
+version = "0.9.10"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
 groups = ["linters"]
 files = [
-    {file = "ruff-0.9.4-py3-none-linux_armv6l.whl", hash = "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706"},
-    {file = "ruff-0.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf"},
-    {file = "ruff-0.9.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b"},
-    {file = "ruff-0.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137"},
-    {file = "ruff-0.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e"},
-    {file = "ruff-0.9.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec"},
-    {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b"},
-    {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a"},
-    {file = "ruff-0.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214"},
-    {file = "ruff-0.9.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231"},
-    {file = "ruff-0.9.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b"},
-    {file = "ruff-0.9.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6"},
-    {file = "ruff-0.9.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c"},
-    {file = "ruff-0.9.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0"},
-    {file = "ruff-0.9.4-py3-none-win32.whl", hash = "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402"},
-    {file = "ruff-0.9.4-py3-none-win_amd64.whl", hash = "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e"},
-    {file = "ruff-0.9.4-py3-none-win_arm64.whl", hash = "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41"},
-    {file = "ruff-0.9.4.tar.gz", hash = "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7"},
+    {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"},
+    {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"},
+    {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"},
+    {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"},
+    {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"},
+    {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"},
+    {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"},
+    {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"},
+    {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"},
+    {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"},
+    {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"},
+    {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"},
+    {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"},
+    {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"},
+    {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"},
+    {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"},
+    {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"},
+    {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"},
 ]
 
 [[package]]
 name = "six"
-version = "1.16.0"
+version = "1.17.0"
 description = "Python 2 and 3 compatibility utilities"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-groups = ["dev", "documentation", "test"]
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["documentation", "test"]
 files = [
-    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
-    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+    {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
+    {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
 ]
 
 [[package]]
@@ -1689,30 +1609,30 @@ markers = {dev = "python_version < \"3.11\"", linters = "python_version < \"3.11
 
 [[package]]
 name = "tomlkit"
-version = "0.13.2"
+version = "0.13.3"
 description = "Style preserving TOML library"
 optional = false
 python-versions = ">=3.8"
 groups = ["main"]
 files = [
-    {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"},
-    {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"},
+    {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"},
+    {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"},
 ]
 
 [[package]]
 name = "tox"
-version = "4.24.1"
+version = "4.26.0"
 description = "tox is a generic virtualenv management and test command line tool"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["dev"]
 files = [
-    {file = "tox-4.24.1-py3-none-any.whl", hash = "sha256:57ba7df7d199002c6df8c2db9e6484f3de6ca8f42013c083ea2d4d1e5c6bdc75"},
-    {file = "tox-4.24.1.tar.gz", hash = "sha256:083a720adbc6166fff0b7d1df9d154f9d00bfccb9403b8abf6bc0ee435d6a62e"},
+    {file = "tox-4.26.0-py3-none-any.whl", hash = "sha256:75f17aaf09face9b97bd41645028d9f722301e912be8b4c65a3f938024560224"},
+    {file = "tox-4.26.0.tar.gz", hash = "sha256:a83b3b67b0159fa58e44e646505079e35a43317a62d2ae94725e0586266faeca"},
 ]
 
 [package.dependencies]
-cachetools = ">=5.5"
+cachetools = ">=5.5.1"
 chardet = ">=5.2"
 colorama = ">=0.4.6"
 filelock = ">=3.16.1"
@@ -1720,12 +1640,12 @@ packaging = ">=24.2"
 platformdirs = ">=4.3.6"
 pluggy = ">=1.5"
 pyproject-api = ">=1.8"
-tomli = {version = ">=2.1", markers = "python_version < \"3.11\""}
+tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""}
 typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""}
-virtualenv = ">=20.27.1"
+virtualenv = ">=20.31"
 
 [package.extras]
-test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"]
+test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.4)", "pytest-mock (>=3.14)"]
 
 [[package]]
 name = "traitlets"
@@ -1745,38 +1665,38 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,
 
 [[package]]
 name = "types-deprecated"
-version = "1.2.15.20241117"
+version = "1.2.15.20250304"
 description = "Typing stubs for Deprecated"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["linters"]
 files = [
-    {file = "types-Deprecated-1.2.15.20241117.tar.gz", hash = "sha256:924002c8b7fddec51ba4949788a702411a2e3636cd9b2a33abd8ee119701d77e"},
-    {file = "types_Deprecated-1.2.15.20241117-py3-none-any.whl", hash = "sha256:a0cc5e39f769fc54089fd8e005416b55d74aa03f6964d2ed1a0b0b2e28751884"},
+    {file = "types_deprecated-1.2.15.20250304-py3-none-any.whl", hash = "sha256:86a65aa550ea8acf49f27e226b8953288cd851de887970fbbdf2239c116c3107"},
+    {file = "types_deprecated-1.2.15.20250304.tar.gz", hash = "sha256:c329030553029de5cc6cb30f269c11f4e00e598c4241290179f63cda7d33f719"},
 ]
 
 [[package]]
 name = "types-python-dateutil"
-version = "2.9.0.20241206"
+version = "2.9.0.20250516"
 description = "Typing stubs for python-dateutil"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["linters"]
 files = [
-    {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"},
-    {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"},
+    {file = "types_python_dateutil-2.9.0.20250516-py3-none-any.whl", hash = "sha256:2b2b3f57f9c6a61fba26a9c0ffb9ea5681c9b83e69cd897c6b5f668d9c0cab93"},
+    {file = "types_python_dateutil-2.9.0.20250516.tar.gz", hash = "sha256:13e80d6c9c47df23ad773d54b2826bd52dbbb41be87c3f339381c1700ad21ee5"},
 ]
 
 [[package]]
 name = "types-pyyaml"
-version = "6.0.12.20241230"
+version = "6.0.12.20250516"
 description = "Typing stubs for PyYAML"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["linters"]
 files = [
-    {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"},
-    {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"},
+    {file = "types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530"},
+    {file = "types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba"},
 ]
 
 [[package]]
@@ -1793,27 +1713,27 @@ files = [
 
 [[package]]
 name = "typing-extensions"
-version = "4.12.2"
-description = "Backported and Experimental Type Hints for Python 3.8+"
+version = "4.14.0"
+description = "Backported and Experimental Type Hints for Python 3.9+"
 optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev", "linters", "script"]
+python-versions = ">=3.9"
+groups = ["main", "dev", "linters", "script", "test"]
 files = [
-    {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
-    {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
+    {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"},
+    {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"},
 ]
-markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\""}
+markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""}
 
 [[package]]
 name = "urllib3"
-version = "2.2.3"
+version = "2.4.0"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 groups = ["documentation"]
 files = [
-    {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
-    {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
+    {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"},
+    {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"},
 ]
 
 [package.extras]
@@ -1824,14 +1744,14 @@ zstd = ["zstandard (>=0.18.0)"]
 
 [[package]]
 name = "virtualenv"
-version = "20.27.1"
+version = "20.31.2"
 description = "Virtual Python Environment builder"
 optional = false
 python-versions = ">=3.8"
 groups = ["dev", "linters"]
 files = [
-    {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"},
-    {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"},
+    {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"},
+    {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"},
 ]
 
 [package.dependencies]
@@ -1841,7 +1761,7 @@ platformdirs = ">=3.9.1,<5"
 
 [package.extras]
 docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
 
 [[package]]
 name = "watchdog"
@@ -1900,90 +1820,104 @@ files = [
 
 [[package]]
 name = "wrapt"
-version = "1.17.0"
+version = "1.17.2"
 description = "Module for decorators, wrappers and monkey patching."
 optional = false
 python-versions = ">=3.8"
 groups = ["test"]
 files = [
-    {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"},
-    {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"},
-    {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"},
-    {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"},
-    {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"},
-    {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"},
-    {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"},
-    {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"},
-    {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"},
-    {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"},
-    {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"},
-    {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"},
-    {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"},
-    {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"},
-    {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"},
-    {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"},
-    {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"},
-    {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"},
-    {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"},
-    {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"},
-    {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"},
-    {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"},
-    {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"},
-    {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"},
-    {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"},
-    {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"},
-    {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"},
-    {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"},
-    {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"},
-    {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"},
-    {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"},
-    {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"},
-    {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"},
-    {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"},
-    {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"},
-    {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"},
-    {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"},
-    {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"},
-    {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"},
-    {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"},
-    {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"},
-    {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"},
-    {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"},
-    {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"},
-    {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"},
-    {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"},
-    {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"},
-    {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"},
-    {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"},
-    {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"},
-    {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"},
-    {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"},
-    {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"},
-    {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"},
-    {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"},
-    {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"},
-    {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"},
-    {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"},
-    {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"},
-    {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"},
-    {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"},
-    {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"},
-    {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"},
-    {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"},
-    {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"},
+    {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
+    {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
+    {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"},
+    {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"},
+    {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"},
+    {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"},
+    {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"},
+    {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"},
+    {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"},
+    {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"},
+    {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"},
+    {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"},
+    {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"},
+    {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"},
+    {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"},
+    {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"},
+    {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"},
+    {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"},
+    {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"},
+    {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"},
+    {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"},
+    {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"},
+    {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"},
+    {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"},
+    {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"},
+    {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"},
+    {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"},
+    {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"},
+    {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"},
+    {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"},
+    {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"},
+    {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"},
+    {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"},
+    {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"},
+    {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"},
+    {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"},
+    {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"},
+    {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"},
+    {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"},
+    {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"},
+    {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"},
+    {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"},
+    {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"},
+    {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"},
+    {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"},
+    {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"},
+    {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"},
+    {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"},
+    {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"},
+    {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"},
+    {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"},
+    {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"},
+    {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"},
+    {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"},
+    {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"},
+    {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"},
+    {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"},
+    {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"},
+    {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"},
+    {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"},
+    {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"},
+    {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"},
+    {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"},
+    {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"},
+    {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"},
+    {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"},
+    {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"},
+    {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"},
+    {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"},
+    {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"},
+    {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"},
+    {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"},
+    {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"},
+    {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"},
+    {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"},
+    {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"},
+    {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"},
+    {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"},
+    {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"},
 ]
 
 [[package]]
 name = "zipp"
-version = "3.21.0"
+version = "3.22.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
 python-versions = ">=3.9"
 groups = ["main", "documentation"]
 markers = "python_version == \"3.9\""
 files = [
-    {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
-    {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
+    {file = "zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343"},
+    {file = "zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5"},
 ]
 
 [package.extras]
@@ -1991,10 +1925,10 @@ check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"
 cover = ["pytest-cov"]
 doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
 enabler = ["pytest-enabler (>=2.2)"]
-test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
+test = ["big-O", "importlib_resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
 type = ["pytest-mypy"]
 
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "2951e44d1ec238ddef2139c76de3c838976a018b0993f7b84c6fd686e26fc5d0"
+content-hash = "7375bf9072066831425c1b72fec016c42791695338c061324fb47420dafd2c8d"
diff --git a/pyproject.toml b/pyproject.toml
index cff51a2094..8215595737 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -121,7 +121,7 @@ deprecated = "^1.2.13"
 [tool.poetry.group.linters.dependencies]
 ruff = ">=0.5.0,<0.10.0"
 pre-commit = ">=2.18,<5.0"
-mypy = "^1.4"
+mypy = "^1.15.0"
 types-deprecated = "^1.2.9.2"
 types-python-dateutil = "^2.8.19.13"
 types-PyYAML = ">=5.4.3,<7.0.0"
@@ -251,12 +251,7 @@ cover.help = "Run the test suite with coverage"
 cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen"
 
 all.help = "Run all tasks"
-all.sequence = [
-    "format",
-    "lint",
-    "cover",
-    "check-commit",
-]
+all.sequence = ["format", "lint", "cover", "check-commit"]
 
 "doc:screenshots".help = "Render documentation screenshots"
 "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots"
@@ -268,10 +263,7 @@ doc.help = "Live documentation server"
 doc.cmd = "mkdocs serve"
 
 ci.help = "Run all tasks in CI"
-ci.sequence = [
-    { cmd = "pre-commit run --all-files" },
-    "cover",
-]
+ci.sequence = [{ cmd = "pre-commit run --all-files" }, "cover"]
 ci.env = { SKIP = "no-commit-to-branch" }
 
 setup-pre-commit.help = "Install pre-commit hooks"

From 18705031c303d3264e7402fb71c31170a7db23bb Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 22:08:12 +0800
Subject: [PATCH 101/169] refactor(bump): add type for out, replace function
 with re escape

---
 commitizen/bump.py | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/commitizen/bump.py b/commitizen/bump.py
index b8ae18ba64..2fa814f7c8 100644
--- a/commitizen/bump.py
+++ b/commitizen/bump.py
@@ -103,13 +103,13 @@ def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str
     """
     Resolve all distinct files with their regexp from a list of glob patterns with optional regexp
     """
-    out = []
+    out: list[tuple[str, str]] = []
     for pattern in patterns:
         drive, tail = os.path.splitdrive(pattern)
         path, _, regex = tail.partition(":")
         filepath = drive + path
         if not regex:
-            regex = _version_to_regex(version)
+            regex = re.escape(version)
 
         for path in iglob(filepath):
             out.append((path, regex))
@@ -140,10 +140,6 @@ def _bump_with_regex(
     return current_version_found, "".join(lines)
 
 
-def _version_to_regex(version: str) -> str:
-    return version.replace(".", r"\.").replace("+", r"\+")
-
-
 def create_commit_message(
     current_version: Version | str,
     new_version: Version | str,

From 69a220c362c8a0dab364299a053dcb111c57157e Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 03:09:58 +0800
Subject: [PATCH 102/169] refactor(bump): clean up

---
 commitizen/commands/bump.py | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 0a2bbe37fc..d022599c76 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -105,19 +105,18 @@ def is_initial_tag(
         self, current_tag: git.GitTag | None, is_yes: bool = False
     ) -> bool:
         """Check if reading the whole git tree up to HEAD is needed."""
-        is_initial = False
-        if not current_tag:
-            if is_yes:
-                is_initial = True
-            else:
-                out.info("No tag matching configuration could not be found.")
-                out.info(
-                    "Possible causes:\n"
-                    "- version in configuration is not the current version\n"
-                    "- tag_format or legacy_tag_formats is missing, check them using 'git tag --list'\n"
-                )
-                is_initial = questionary.confirm("Is this the first tag created?").ask()
-        return is_initial
+        if current_tag:
+            return False
+        if is_yes:
+            return True
+
+        out.info("No tag matching configuration could not be found.")
+        out.info(
+            "Possible causes:\n"
+            "- version in configuration is not the current version\n"
+            "- tag_format or legacy_tag_formats is missing, check them using 'git tag --list'\n"
+        )
+        return bool(questionary.confirm("Is this the first tag created?").ask())
 
     def find_increment(self, commits: list[git.GitCommit]) -> Increment | None:
         # Update the bump map to ensure major version doesn't increment.
@@ -134,10 +133,7 @@ def find_increment(self, commits: list[git.GitCommit]) -> Increment | None:
             raise NoPatternMapError(
                 f"'{self.config.settings['name']}' rule does not support bump"
             )
-        increment = bump.find_increment(
-            commits, regex=bump_pattern, increments_map=bump_map
-        )
-        return increment
+        return bump.find_increment(commits, regex=bump_pattern, increments_map=bump_map)
 
     def __call__(self) -> None:  # noqa: C901
         """Steps executed to bump."""
@@ -148,7 +144,7 @@ def __call__(self) -> None:  # noqa: C901
         except TypeError:
             raise NoVersionSpecifiedError()
 
-        bump_commit_message: str = self.bump_settings["bump_message"]
+        bump_commit_message: str | None = self.bump_settings["bump_message"]
         version_files: list[str] = self.bump_settings["version_files"]
         major_version_zero: bool = self.bump_settings["major_version_zero"]
         prerelease_offset: int = self.bump_settings["prerelease_offset"]

From b0f0d40ece88ca14c05b16d8fe760269096d7ca4 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 03:14:56 +0800
Subject: [PATCH 103/169] test(bump): improve test coverage

---
 commitizen/commands/bump.py         |  4 +--
 tests/commands/test_bump_command.py | 55 +++++++++++++++++++++++++++--
 2 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index d022599c76..a2d98c2451 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -65,7 +65,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
                     "template",
                     "file_name",
                 ]
-                if arguments[key] is not None
+                if arguments.get(key) is not None
             },
         }
         self.cz = factory.committer_factory(self.config)
@@ -110,7 +110,7 @@ def is_initial_tag(
         if is_yes:
             return True
 
-        out.info("No tag matching configuration could not be found.")
+        out.info("No tag matching configuration could be found.")
         out.info(
             "Possible causes:\n"
             "- version in configuration is not the current version\n"
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index e15539d8a7..2af7bec121 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -12,8 +12,9 @@
 from pytest_mock import MockFixture
 
 import commitizen.commands.bump as bump
-from commitizen import cli, cmd, git, hooks
+from commitizen import cli, cmd, defaults, git, hooks
 from commitizen.changelog_formats import ChangelogFormat
+from commitizen.config.base_config import BaseConfig
 from commitizen.cz.base import BaseCommitizen
 from commitizen.exceptions import (
     BumpTagFailedError,
@@ -41,8 +42,8 @@
         "fix(user): username exception",
         "refactor: remove ini configuration support",
         "refactor(config): remove ini configuration support",
-        "perf: update to use multiproess",
-        "perf(worker): update to use multiproess",
+        "perf: update to use multiprocess",
+        "perf(worker): update to use multiprocess",
     ),
 )
 @pytest.mark.usefixtures("tmp_commitizen_project")
@@ -1688,3 +1689,51 @@ def test_bump_warn_but_dont_fail_on_invalid_tags(
 
     assert err.count("Invalid version tag: '0.4.3.deadbeaf'") == 1
     assert git.tag_exist("0.4.3")
+
+
+def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project):
+    """Test the is_initial_tag method behavior."""
+    # Create a commit but no tags
+    create_file_and_commit("feat: initial commit")
+
+    # Initialize Bump with minimal config
+    config = BaseConfig()
+    config.settings.update(
+        {
+            "name": defaults.DEFAULT_SETTINGS["name"],
+            "encoding": "utf-8",
+            "pre_bump_hooks": [],
+            "post_bump_hooks": [],
+        }
+    )
+
+    # Initialize with required arguments
+    arguments = {
+        "changelog": False,
+        "changelog_to_stdout": False,
+        "git_output_to_stderr": False,
+        "no_verify": False,
+        "check_consistency": False,
+        "retry": False,
+        "version_scheme": None,
+        "file_name": None,
+        "template": None,
+        "extras": None,
+    }
+
+    bump_cmd = bump.Bump(config, arguments)
+
+    # Test case 1: No current tag, not yes mode
+    mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True))
+    assert bump_cmd.is_initial_tag(None, is_yes=False) is True
+
+    # Test case 2: No current tag, yes mode
+    assert bump_cmd.is_initial_tag(None, is_yes=True) is True
+
+    # Test case 3: Has current tag
+    mock_tag = mocker.Mock()
+    assert bump_cmd.is_initial_tag(mock_tag, is_yes=False) is False
+
+    # Test case 4: No current tag, user denies
+    mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: False))
+    assert bump_cmd.is_initial_tag(None, is_yes=False) is False

From 74d3bc53d10cde3de9f56fec429416ff47fbbd18 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 22 May 2025 22:55:50 +0800
Subject: [PATCH 104/169] fix(defaults): add non-capitalized default constants
 back and deprecated warning

relates #1446
---
 commitizen/defaults.py | 29 +++++++++++++++++++++++++++++
 tests/test_defaults.py | 31 +++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)
 create mode 100644 tests/test_defaults.py

diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 1885848618..10d55b8621 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -1,6 +1,7 @@
 from __future__ import annotations
 
 import pathlib
+import warnings
 from collections import OrderedDict
 from collections.abc import Iterable, MutableMapping, Sequence
 from typing import Any, TypedDict
@@ -153,3 +154,31 @@ def get_tag_regexes(
         **{f"${k}": v for k, v in regexes.items()},
         **{f"${{{k}}}": v for k, v in regexes.items()},
     }
+
+
+def __getattr__(name: str) -> Any:
+    # PEP-562: deprecate module-level variable
+
+    # {"deprecated key": (value, "new key")}
+    deprecated_vars = {
+        "bump_pattern": (BUMP_PATTERN, "BUMP_PATTERN"),
+        "bump_map": (BUMP_MAP, "BUMP_MAP"),
+        "bump_map_major_version_zero": (
+            BUMP_MAP_MAJOR_VERSION_ZERO,
+            "BUMP_MAP_MAJOR_VERSION_ZERO",
+        ),
+        "bump_message": (BUMP_MESSAGE, "BUMP_MESSAGE"),
+        "change_type_order": (CHANGE_TYPE_ORDER, "CHANGE_TYPE_ORDER"),
+        "encoding": (ENCODING, "ENCODING"),
+        "name": (DEFAULT_SETTINGS["name"], "DEFAULT_SETTINGS['name']"),
+    }
+    if name in deprecated_vars:
+        value, replacement = deprecated_vars[name]
+        warnings.warn(
+            f"{name} is deprecated and will be removed in a future version. "
+            f"Use {replacement} instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return value
+    raise AttributeError(f"{name} is not an attribute of {__name__}")
diff --git a/tests/test_defaults.py b/tests/test_defaults.py
new file mode 100644
index 0000000000..2298068662
--- /dev/null
+++ b/tests/test_defaults.py
@@ -0,0 +1,31 @@
+import pytest
+
+from commitizen import defaults
+
+
+def test_getattr_deprecated_vars():
+    # Test each deprecated variable
+    with pytest.warns(DeprecationWarning) as record:
+        assert defaults.bump_pattern == defaults.BUMP_PATTERN
+        assert defaults.bump_map == defaults.BUMP_MAP
+        assert (
+            defaults.bump_map_major_version_zero == defaults.BUMP_MAP_MAJOR_VERSION_ZERO
+        )
+        assert defaults.bump_message == defaults.BUMP_MESSAGE
+        assert defaults.change_type_order == defaults.CHANGE_TYPE_ORDER
+        assert defaults.encoding == defaults.ENCODING
+        assert defaults.name == defaults.DEFAULT_SETTINGS["name"]
+
+    # Verify warning messages
+    assert len(record) == 7
+    for warning in record:
+        assert "is deprecated and will be removed in a future version" in str(
+            warning.message
+        )
+
+
+def test_getattr_non_existent():
+    # Test non-existent attribute
+    with pytest.raises(AttributeError) as exc_info:
+        _ = defaults.non_existent_attribute
+    assert "is not an attribute of" in str(exc_info.value)

From 815f514b00300e628fad49e0400da760ab517bfb Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 17 May 2025 20:42:34 +0800
Subject: [PATCH 105/169] refactor: misc cleanup

---
 .../conventional_commits.py                   | 21 +++++++------------
 commitizen/cz/jira/jira.py                    |  6 ++----
 2 files changed, 9 insertions(+), 18 deletions(-)

diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index af29a209fc..9a2b9016b7 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -41,7 +41,7 @@ class ConventionalCommitsCz(BaseCommitizen):
     changelog_pattern = defaults.BUMP_PATTERN
 
     def questions(self) -> Questions:
-        questions: Questions = [
+        return [
             {
                 "type": "list",
                 "name": "prefix",
@@ -146,7 +146,6 @@ def questions(self) -> Questions:
                 ),
             },
         ]
-        return questions
 
     def message(self, answers: dict) -> str:
         prefix = answers["prefix"]
@@ -165,9 +164,7 @@ def message(self, answers: dict) -> str:
         if footer:
             footer = f"\n\n{footer}"
 
-        message = f"{prefix}{scope}: {subject}{body}{footer}"
-
-        return message
+        return f"{prefix}{scope}: {subject}{body}{footer}"
 
     def example(self) -> str:
         return (
@@ -188,25 +185,21 @@ def schema(self) -> str:
         )
 
     def schema_pattern(self) -> str:
-        PATTERN = (
+        return (
             r"(?s)"  # To explicitly make . match new line
             r"(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|bump)"  # type
             r"(\(\S+\))?!?:"  # scope
             r"( [^\n\r]+)"  # subject
             r"((\n\n.*)|(\s*))?$"
         )
-        return PATTERN
 
     def info(self) -> str:
         dir_path = os.path.dirname(os.path.realpath(__file__))
         filepath = os.path.join(dir_path, "conventional_commits_info.txt")
         with open(filepath, encoding=self.config.settings["encoding"]) as f:
-            content = f.read()
-        return content
+            return f.read()
 
     def process_commit(self, commit: str) -> str:
-        pat = re.compile(self.schema_pattern())
-        m = re.match(pat, commit)
-        if m is None:
-            return ""
-        return m.group(3).strip()
+        if m := re.match(self.schema_pattern(), commit):
+            return m.group(3).strip()
+        return ""
diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py
index b8fd056a71..f43de2177c 100644
--- a/commitizen/cz/jira/jira.py
+++ b/commitizen/cz/jira/jira.py
@@ -8,7 +8,7 @@
 
 class JiraSmartCz(BaseCommitizen):
     def questions(self) -> Questions:
-        questions = [
+        return [
             {
                 "type": "input",
                 "name": "message",
@@ -42,7 +42,6 @@ def questions(self) -> Questions:
                 "filter": lambda x: "#comment " + x if x else "",
             },
         ]
-        return questions
 
     def message(self, answers: dict) -> str:
         return " ".join(
@@ -77,5 +76,4 @@ def info(self) -> str:
         dir_path = os.path.dirname(os.path.realpath(__file__))
         filepath = os.path.join(dir_path, "jira_info.txt")
         with open(filepath, encoding=self.config.settings["encoding"]) as f:
-            content = f.read()
-        return content
+            return f.read()

From 03eff5cd86399c3d1ca16c0bfdba43cd29b2f996 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Tue, 20 May 2025 00:06:03 +0800
Subject: [PATCH 106/169] refactor(git): extract _create_commit_cmd_string

---
 commitizen/git.py | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/commitizen/git.py b/commitizen/git.py
index fa59e34d48..ab2866ac48 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -181,19 +181,22 @@ def commit(
     f.write(message.encode("utf-8"))
     f.close()
 
-    command = f'git commit {args} -F "{f.name}"'
-
-    if committer_date and os.name == "nt":  # pragma: no cover
-        # Using `cmd /v /c "{command}"` sets environment variables only for that command
-        command = f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"'
-    elif committer_date:
-        command = f"GIT_COMMITTER_DATE={committer_date} {command}"
-
+    command = _create_commit_cmd_string(args, committer_date, f.name)
     c = cmd.run(command)
     os.unlink(f.name)
     return c
 
 
+def _create_commit_cmd_string(args: str, committer_date: str | None, name: str) -> str:
+    command = f'git commit {args} -F "{name}"'
+    if not committer_date:
+        return command
+    if os.name != "nt":
+        return f"GIT_COMMITTER_DATE={committer_date} {command}"
+    # Using `cmd /v /c "{command}"` sets environment variables only for that command
+    return f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"'
+
+
 def get_commits(
     start: str | None = None,
     end: str = "HEAD",

From 3153e0795142f2b6d0f863d372cb547cfcdad57f Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 23 May 2025 16:24:05 +0800
Subject: [PATCH 107/169] test(test_git): mock os

---
 tests/test_git.py | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/tests/test_git.py b/tests/test_git.py
index de3130412b..3fecaabafd 100644
--- a/tests/test_git.py
+++ b/tests/test_git.py
@@ -449,3 +449,27 @@ def test_git_commit_from_rev_and_commit():
     assert commit.author == "John Doe"
     assert commit.author_email == "john@example.com"
     assert commit.parents == []
+
+
+@pytest.mark.parametrize(
+    "os_name,committer_date,expected_cmd",
+    [
+        (
+            "nt",
+            "2024-03-20",
+            'cmd /v /c "set GIT_COMMITTER_DATE=2024-03-20&& git commit  -F "temp.txt""',
+        ),
+        (
+            "posix",
+            "2024-03-20",
+            'GIT_COMMITTER_DATE=2024-03-20 git commit  -F "temp.txt"',
+        ),
+        ("nt", None, 'git commit  -F "temp.txt"'),
+        ("posix", None, 'git commit  -F "temp.txt"'),
+    ],
+)
+def test_create_commit_cmd_string(mocker, os_name, committer_date, expected_cmd):
+    """Test the OS-specific behavior of _create_commit_cmd_string"""
+    mocker.patch("os.name", os_name)
+    result = git._create_commit_cmd_string("", committer_date, "temp.txt")
+    assert result == expected_cmd

From 876de4f8c1b1c4c3d0d4222c9affc1f2ab5f29c4 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 23 May 2025 22:20:58 +0800
Subject: [PATCH 108/169] refactor(cli): early return and improve test coverage

---
 commitizen/cli.py | 21 ++++++++---------
 tests/test_cli.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+), 10 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index b566fc596d..deb5644d27 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -553,19 +553,20 @@ def commitizen_excepthook(
     type, value, traceback, debug=False, no_raise: list[int] | None = None
 ):
     traceback = traceback if isinstance(traceback, TracebackType) else None
+    if not isinstance(value, CommitizenException):
+        original_excepthook(type, value, traceback)
+        return
+
     if not no_raise:
         no_raise = []
-    if isinstance(value, CommitizenException):
-        if value.message:
-            value.output_method(value.message)
-        if debug:
-            original_excepthook(type, value, traceback)
-        exit_code = value.exit_code
-        if exit_code in no_raise:
-            exit_code = ExitCode.EXPECTED_EXIT
-        sys.exit(exit_code)
-    else:
+    if value.message:
+        value.output_method(value.message)
+    if debug:
         original_excepthook(type, value, traceback)
+    exit_code = value.exit_code
+    if exit_code in no_raise:
+        exit_code = ExitCode.EXPECTED_EXIT
+    sys.exit(exit_code)
 
 
 commitizen_debug_excepthook = partial(commitizen_excepthook, debug=True)
diff --git a/tests/test_cli.py b/tests/test_cli.py
index a91e633128..31371caea4 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,6 +1,7 @@
 import os
 import subprocess
 import sys
+import types
 from functools import partial
 
 import pytest
@@ -182,3 +183,59 @@ def test_unknown_args_before_double_dash_raises(mocker: MockFixture):
     assert "Invalid commitizen arguments were found before -- separator" in str(
         excinfo.value
     )
+
+
+def test_commitizen_excepthook_non_commitizen_exception(mocker: MockFixture):
+    """Test that commitizen_excepthook delegates to original_excepthook for non-CommitizenException."""
+    # Mock the original excepthook
+    mock_original_excepthook = mocker.Mock()
+    mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook)
+
+    # Create a regular exception
+    test_exception = ValueError("test error")
+
+    # Call commitizen_excepthook with the regular exception
+    cli.commitizen_excepthook(ValueError, test_exception, None)
+
+    # Verify original_excepthook was called with correct arguments
+    mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None)
+
+
+def test_commitizen_excepthook_non_commitizen_exception_with_traceback(
+    mocker: MockFixture,
+):
+    """Test that commitizen_excepthook handles traceback correctly for non-CommitizenException."""
+    # Mock the original excepthook
+    mock_original_excepthook = mocker.Mock()
+    mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook)
+
+    # Create a regular exception with a traceback
+    test_exception = ValueError("test error")
+    test_traceback = mocker.Mock(spec=types.TracebackType)
+
+    # Call commitizen_excepthook with the regular exception and traceback
+    cli.commitizen_excepthook(ValueError, test_exception, test_traceback)
+
+    # Verify original_excepthook was called with correct arguments including traceback
+    mock_original_excepthook.assert_called_once_with(
+        ValueError, test_exception, test_traceback
+    )
+
+
+def test_commitizen_excepthook_non_commitizen_exception_with_invalid_traceback(
+    mocker: MockFixture,
+):
+    """Test that commitizen_excepthook handles invalid traceback correctly for non-CommitizenException."""
+    # Mock the original excepthook
+    mock_original_excepthook = mocker.Mock()
+    mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook)
+
+    # Create a regular exception with an invalid traceback
+    test_exception = ValueError("test error")
+    test_traceback = mocker.Mock()  # Not a TracebackType
+
+    # Call commitizen_excepthook with the regular exception and invalid traceback
+    cli.commitizen_excepthook(ValueError, test_exception, test_traceback)
+
+    # Verify original_excepthook was called with None as traceback
+    mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None)

From 52e44e5b4bcf62286d6545b5e3218bb54bafd6aa Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 24 May 2025 00:25:11 +0800
Subject: [PATCH 109/169] build(termcolor): remove termcolor <3 restriction

---
 poetry.lock    | 8 ++++----
 pyproject.toml | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index ceccd3da1c..b5dbcd9898 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1551,14 +1551,14 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
 
 [[package]]
 name = "termcolor"
-version = "2.5.0"
+version = "3.1.0"
 description = "ANSI color formatting for output in terminal"
 optional = false
 python-versions = ">=3.9"
 groups = ["main"]
 files = [
-    {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"},
-    {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"},
+    {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"},
+    {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"},
 ]
 
 [package.extras]
@@ -1931,4 +1931,4 @@ type = ["pytest-mypy"]
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "7375bf9072066831425c1b72fec016c42791695338c061324fb47420dafd2c8d"
+content-hash = "5e42ee6a3a2b13c6f277b29f063fd8d71bf89841b1564636dffe56f7c65c33b4"
diff --git a/pyproject.toml b/pyproject.toml
index 8215595737..ac1eab66ca 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,7 +14,7 @@ dependencies = [
     "questionary (>=2.0,<3.0)",
     "decli (>=0.6.0,<1.0)",
     "colorama (>=0.4.1,<1.0)",
-    "termcolor (>=1.1,<3)",
+    "termcolor (>=1.1.0,<4.0.0)",
     "packaging>=19",
     "tomlkit (>=0.5.3,<1.0.0)",
     "jinja2>=2.10.3",

From edb21df5703332ae7320f7266021ec26c89891aa Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 25 May 2025 23:58:56 +0800
Subject: [PATCH 110/169] build: specify importlib-metadata version to fix unit
 tests

---
 poetry.lock    | 95 ++++++++++++++++++++++++++++++++++++++++++++++----
 pyproject.toml |  4 +--
 2 files changed, 91 insertions(+), 8 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index b5dbcd9898..6a31d292fe 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -222,6 +222,7 @@ description = "Composable command line interface toolkit"
 optional = false
 python-versions = ">=3.7"
 groups = ["documentation"]
+markers = "python_version == \"3.9\""
 files = [
     {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
     {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
@@ -230,6 +231,22 @@ files = [
 [package.dependencies]
 colorama = {version = "*", markers = "platform_system == \"Windows\""}
 
+[[package]]
+name = "click"
+version = "8.2.1"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.10"
+groups = ["documentation"]
+markers = "python_version != \"3.9\""
+files = [
+    {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
+    {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
 [[package]]
 name = "colorama"
 version = "0.4.6"
@@ -510,12 +527,37 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
 
 [[package]]
 name = "importlib-metadata"
-version = "8.7.0"
+version = "8.6.1"
 description = "Read metadata from Python packages"
 optional = false
 python-versions = ">=3.9"
 groups = ["main", "documentation"]
 markers = "python_version == \"3.9\""
+files = [
+    {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
+    {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
+]
+
+[package.dependencies]
+zipp = ">=3.20"
+
+[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+enabler = ["pytest-enabler (>=2.2)"]
+perf = ["ipython"]
+test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
+type = ["pytest-mypy"]
+
+[[package]]
+name = "importlib-metadata"
+version = "8.7.0"
+description = "Read metadata from Python packages"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version != \"3.9\""
 files = [
     {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"},
     {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"},
@@ -552,6 +594,7 @@ description = "IPython: Productive Interactive Computing"
 optional = false
 python-versions = ">=3.9"
 groups = ["dev"]
+markers = "python_version == \"3.9\""
 files = [
     {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"},
     {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"},
@@ -583,6 +626,46 @@ qtconsole = ["qtconsole"]
 test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"]
 test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"]
 
+[[package]]
+name = "ipython"
+version = "8.37.0"
+description = "IPython: Productive Interactive Computing"
+optional = false
+python-versions = ">=3.10"
+groups = ["dev"]
+markers = "python_version != \"3.9\""
+files = [
+    {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"},
+    {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+decorator = "*"
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
+jedi = ">=0.16"
+matplotlib-inline = "*"
+pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
+prompt_toolkit = ">=3.0.41,<3.1.0"
+pygments = ">=2.4.0"
+stack_data = "*"
+traitlets = ">=5.13.0"
+typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
+
+[package.extras]
+all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
+black = ["black"]
+doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"]
+kernel = ["ipykernel"]
+matplotlib = ["matplotlib"]
+nbconvert = ["nbconvert"]
+nbformat = ["nbformat"]
+notebook = ["ipywidgets", "notebook"]
+parallel = ["ipyparallel"]
+qtconsole = ["qtconsole"]
+test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"]
+test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
+
 [[package]]
 name = "jedi"
 version = "0.19.2"
@@ -1021,7 +1104,7 @@ description = "Pexpect allows easy control of interactive console applications."
 optional = false
 python-versions = "*"
 groups = ["dev"]
-markers = "sys_platform != \"win32\""
+markers = "python_version == \"3.9\" and sys_platform != \"win32\" or sys_platform != \"win32\" and sys_platform != \"emscripten\""
 files = [
     {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
     {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
@@ -1124,7 +1207,7 @@ description = "Run a subprocess in a pseudo terminal"
 optional = false
 python-versions = "*"
 groups = ["dev"]
-markers = "sys_platform != \"win32\""
+markers = "python_version == \"3.9\" and sys_platform != \"win32\" or sys_platform != \"win32\" and sys_platform != \"emscripten\""
 files = [
     {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
     {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
@@ -1722,7 +1805,7 @@ files = [
     {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"},
     {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"},
 ]
-markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""}
+markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.12\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""}
 
 [[package]]
 name = "urllib3"
@@ -1914,11 +1997,11 @@ description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
 python-versions = ">=3.9"
 groups = ["main", "documentation"]
-markers = "python_version == \"3.9\""
 files = [
     {file = "zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343"},
     {file = "zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5"},
 ]
+markers = {documentation = "python_version == \"3.9\""}
 
 [package.extras]
 check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
@@ -1931,4 +2014,4 @@ type = ["pytest-mypy"]
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "5e42ee6a3a2b13c6f277b29f063fd8d71bf89841b1564636dffe56f7c65c33b4"
+content-hash = "146cae2abe0cdb8a831b9ece0aa1ee98366e3e4e17ef734f0e7001c4eb7508b3"
diff --git a/pyproject.toml b/pyproject.toml
index ac1eab66ca..144868e197 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,8 +23,8 @@ dependencies = [
     "typing-extensions (>=4.0.1,<5.0.0) ; python_version < '3.11'",
     "charset-normalizer (>=2.1.0,<4)",
     # Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility
-    "importlib_metadata (>=8.0.0,<9) ; python_version < '3.10'",
-
+    "importlib-metadata >=8.0.0,!=8.7.0,<9.0.0 ; python_version == '3.9'", # importlib-metadata@8.7.0 + python3.9 breaks our unit test
+    "importlib-metadata >=8.0.0,<9.0.0 ; python_version != '3.9'",
 ]
 keywords = ["commitizen", "conventional", "commits", "git"]
 # See also: https://pypi.org/classifiers/

From 7750f420822ff4be81e103da34051c866c5a944f Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 24 May 2025 15:50:21 +0800
Subject: [PATCH 111/169] refactor(changelog): better typing, yield

---
 commitizen/changelog.py          | 33 +++++++++++++++++---------------
 commitizen/commands/changelog.py |  4 +++-
 tests/test_changelog.py          | 16 ++++++++--------
 3 files changed, 29 insertions(+), 24 deletions(-)

diff --git a/commitizen/changelog.py b/commitizen/changelog.py
index d8ab56069b..bac4d824f9 100644
--- a/commitizen/changelog.py
+++ b/commitizen/changelog.py
@@ -29,10 +29,10 @@
 
 import re
 from collections import OrderedDict, defaultdict
-from collections.abc import Iterable
+from collections.abc import Generator, Iterable, Mapping
 from dataclasses import dataclass
 from datetime import date
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any
 
 from jinja2 import (
     BaseLoader,
@@ -84,7 +84,7 @@ def generate_tree_from_commits(
     changelog_message_builder_hook: MessageBuilderHook | None = None,
     changelog_release_hook: ChangelogReleaseHook | None = None,
     rules: TagRules | None = None,
-) -> Iterable[dict]:
+) -> Generator[dict[str, Any], None, None]:
     pat = re.compile(changelog_pattern)
     map_pat = re.compile(commit_parser, re.MULTILINE)
     body_map_pat = re.compile(commit_parser, re.MULTILINE | re.DOTALL)
@@ -187,24 +187,27 @@ def process_commit_message(
             changes[change_type].append(msg)
 
 
-def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable:
+def generate_ordered_changelog_tree(
+    tree: Iterable[Mapping[str, Any]], change_type_order: list[str]
+) -> Generator[dict[str, Any], None, None]:
     if len(set(change_type_order)) != len(change_type_order):
         raise InvalidConfigurationError(
             f"Change types contain duplicated types ({change_type_order})"
         )
 
-    sorted_tree = []
     for entry in tree:
-        ordered_change_types = change_type_order + sorted(
-            set(entry["changes"].keys()) - set(change_type_order)
-        )
-        changes = [
-            (ct, entry["changes"][ct])
-            for ct in ordered_change_types
-            if ct in entry["changes"]
-        ]
-        sorted_tree.append({**entry, **{"changes": OrderedDict(changes)}})
-    return sorted_tree
+        yield {
+            **entry,
+            "changes": _calculate_sorted_changes(change_type_order, entry["changes"]),
+        }
+
+
+def _calculate_sorted_changes(
+    change_type_order: list[str], changes: Mapping[str, Any]
+) -> OrderedDict[str, Any]:
+    remaining_change_types = set(changes.keys()) - set(change_type_order)
+    sorted_change_types = change_type_order + sorted(remaining_change_types)
+    return OrderedDict((ct, changes[ct]) for ct in sorted_change_types if ct in changes)
 
 
 def get_changelog_template(loader: BaseLoader, template: str) -> Template:
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 9ab8fdc37c..7bf644a934 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -215,7 +215,9 @@ def __call__(self):
             rules=self.tag_rules,
         )
         if self.change_type_order:
-            tree = changelog.order_changelog_tree(tree, self.change_type_order)
+            tree = changelog.generate_ordered_changelog_tree(
+                tree, self.change_type_order
+            )
 
         extras = self.cz.template_extras.copy()
         extras.update(self.config.settings["extras"])
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index d42176f09a..ed90ed08e4 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1215,26 +1215,26 @@ def test_generate_tree_from_commits_with_no_commits(tags):
         ),
     ),
 )
-def test_order_changelog_tree(change_type_order, expected_reordering):
-    tree = changelog.order_changelog_tree(COMMITS_TREE, change_type_order)
+def test_generate_ordered_changelog_tree(change_type_order, expected_reordering):
+    tree = changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order)
 
     for index, entry in enumerate(tuple(tree)):
-        version = tree[index]["version"]
+        version = entry["version"]
         if version in expected_reordering:
             # Verify that all keys are present
-            assert [*tree[index].keys()] == [*COMMITS_TREE[index].keys()]
+            assert [*entry.keys()] == [*COMMITS_TREE[index].keys()]
             # Verify that the reorder only impacted the returned dict and not the original
             expected = expected_reordering[version]
-            assert [*tree[index]["changes"].keys()] == expected["sorted"]
+            assert [*entry["changes"].keys()] == expected["sorted"]
             assert [*COMMITS_TREE[index]["changes"].keys()] == expected["original"]
         else:
-            assert [*entry["changes"].keys()] == [*tree[index]["changes"].keys()]
+            assert [*entry["changes"].keys()] == [*entry["changes"].keys()]
 
 
-def test_order_changelog_tree_raises():
+def test_generate_ordered_changelog_tree_raises():
     change_type_order = ["BREAKING CHANGE", "feat", "refactor", "feat"]
     with pytest.raises(InvalidConfigurationError) as excinfo:
-        changelog.order_changelog_tree(COMMITS_TREE, change_type_order)
+        list(changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order))
 
     assert "Change types contain duplicated types" in str(excinfo)
 

From fcd609e72ba71ec14d83fb7a64e64503f268f314 Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 13:28:42 +0800
Subject: [PATCH 112/169] build(deps-dev): bump mypy to 1.16.0

---
 poetry.lock    | 2 +-
 pyproject.toml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index 6a31d292fe..cbe0fb0e3c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2014,4 +2014,4 @@ type = ["pytest-mypy"]
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "146cae2abe0cdb8a831b9ece0aa1ee98366e3e4e17ef734f0e7001c4eb7508b3"
+content-hash = "6eea6e061a2ece897c2fb37b0b5116085057e6d97e1f22c0fcd4294fb0ad1dbf"
diff --git a/pyproject.toml b/pyproject.toml
index 144868e197..1a29ca6bf9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -121,7 +121,7 @@ deprecated = "^1.2.13"
 [tool.poetry.group.linters.dependencies]
 ruff = ">=0.5.0,<0.10.0"
 pre-commit = ">=2.18,<5.0"
-mypy = "^1.15.0"
+mypy = "^1.16.0"
 types-deprecated = "^1.2.9.2"
 types-python-dateutil = "^2.8.19.13"
 types-PyYAML = ">=5.4.3,<7.0.0"

From b4c8aa35e70e1ca6438497f658dedecfcbdc7656 Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 13:28:57 +0800
Subject: [PATCH 113/169] refactor(mypy): remove `unused-ignore`

---
 commitizen/config/json_config.py | 2 +-
 commitizen/config/toml_config.py | 2 +-
 commitizen/config/yaml_config.py | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py
index d413d73383..28fac25662 100644
--- a/commitizen/config/json_config.py
+++ b/commitizen/config/json_config.py
@@ -13,7 +13,7 @@ class JsonConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
         super().__init__()
         self.is_empty_config = False
-        self.path = path  # type: ignore
+        self.path = path
         self._parse_setting(data)
 
     def init_empty_config_content(self):
diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py
index e2cfcc9340..a7050214ba 100644
--- a/commitizen/config/toml_config.py
+++ b/commitizen/config/toml_config.py
@@ -14,7 +14,7 @@ class TomlConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
         super().__init__()
         self.is_empty_config = False
-        self.path = path  # type: ignore
+        self.path = path
         self._parse_setting(data)
 
     def init_empty_config_content(self):
diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py
index c5721c8d4b..2c384cf17d 100644
--- a/commitizen/config/yaml_config.py
+++ b/commitizen/config/yaml_config.py
@@ -14,7 +14,7 @@ class YAMLConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
         super().__init__()
         self.is_empty_config = False
-        self.path = path  # type: ignore
+        self.path = path
         self._parse_setting(data)
 
     def init_empty_config_content(self):

From 94b9796453e250c76f1bc4455c5542c2339cf7be Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 11:08:12 +0800
Subject: [PATCH 114/169] refactor(cli.py): add type hints

---
 commitizen/cli.py | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index deb5644d27..04a99e8c37 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -8,7 +8,7 @@
 from functools import partial
 from pathlib import Path
 from types import TracebackType
-from typing import Any
+from typing import TYPE_CHECKING, Any, cast
 
 import argcomplete
 from decli import cli
@@ -596,8 +596,33 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]:
     return no_raise_codes
 
 
+if TYPE_CHECKING:
+
+    class Args(argparse.Namespace):
+        config: str | None = None
+        debug: bool = False
+        name: str | None = None
+        no_raise: str | None = None
+        report: bool = False
+        project: bool = False
+        commitizen: bool = False
+        verbose: bool = False
+        func: type[
+            commands.Init  # init
+            | commands.Commit  # commit (c)
+            | commands.ListCz  # ls
+            | commands.Example  # example
+            | commands.Info  # info
+            | commands.Schema  # schema
+            | commands.Bump  # bump
+            | commands.Changelog  # changelog (ch)
+            | commands.Check  # check
+            | commands.Version  # version
+        ]
+
+
 def main():
-    parser = cli(data)
+    parser: argparse.ArgumentParser = cli(data)
     argcomplete.autocomplete(parser)
     # Show help if no arg provided
     if len(sys.argv) == 1:
@@ -611,7 +636,7 @@ def main():
         # https://github.com/commitizen-tools/commitizen/issues/429
         # argparse raises TypeError when non exist command is provided on Python < 3.9
         # but raise SystemExit with exit code == 2 on Python 3.9
-        if isinstance(e, TypeError) or (isinstance(e, SystemExit) and e.code == 2):
+        if isinstance(e, TypeError) or e.code == 2:
             raise NoCommandFoundError()
         raise e
 
@@ -638,6 +663,7 @@ def main():
         arguments["extra_cli_args"] = extra_args
 
     conf = config.read_cfg(args.config)
+    args = cast("Args", args)
     if args.name:
         conf.update({"name": args.name})
     elif not conf.path:

From a5033ab42c75bc859be94872ff80d474425a109d Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 13:55:34 +0800
Subject: [PATCH 115/169] refactor: add comment clarifying `no_raise` parsing
 to `list[int]`

---
 commitizen/cli.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index 04a99e8c37..861ba578a9 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -602,7 +602,7 @@ class Args(argparse.Namespace):
         config: str | None = None
         debug: bool = False
         name: str | None = None
-        no_raise: str | None = None
+        no_raise: str | None = None  # comma-separated string, later parsed as list[int]
         report: bool = False
         project: bool = False
         commitizen: bool = False

From 8055c9509283227def93235561b8675f504f4c18 Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 14:24:07 +0800
Subject: [PATCH 116/169] refactor: remove `TypeError` handling since `Python
 >=3.9` is required

Since the project requires Python >=3.9, argparse only raises SystemExit
when non-existent commands are provided, making TypeError handling unnecessary.
---
 commitizen/cli.py | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index 861ba578a9..d245f731e3 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -632,11 +632,8 @@ def main():
     # This is for the command required constraint in 2.0
     try:
         args, unknown_args = parser.parse_known_args()
-    except (TypeError, SystemExit) as e:
-        # https://github.com/commitizen-tools/commitizen/issues/429
-        # argparse raises TypeError when non exist command is provided on Python < 3.9
-        # but raise SystemExit with exit code == 2 on Python 3.9
-        if isinstance(e, TypeError) or e.code == 2:
+    except SystemExit as e:
+        if e.code == 2:
             raise NoCommandFoundError()
         raise e
 

From 9f67a43286c28778458faa76e66f81e588523b39 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 2 Jun 2025 01:16:21 +0800
Subject: [PATCH 117/169] fix(deprecated): mark deprecate in v5

---
 commitizen/commands/bump.py   | 2 +-
 commitizen/defaults.py        | 2 +-
 commitizen/version_schemes.py | 2 +-
 docs/commands/commit.md       | 2 +-
 tests/test_defaults.py        | 4 +---
 5 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index a2d98c2451..3f909d97cb 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -82,7 +82,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
         if deprecated_version_type:
             warnings.warn(
                 DeprecationWarning(
-                    "`--version-type` parameter is deprecated and will be removed in commitizen 4. "
+                    "`--version-type` parameter is deprecated and will be removed in v5. "
                     "Please use `--version-scheme` instead"
                 )
             )
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 10d55b8621..adbb9e6d1a 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -175,7 +175,7 @@ def __getattr__(name: str) -> Any:
     if name in deprecated_vars:
         value, replacement = deprecated_vars[name]
         warnings.warn(
-            f"{name} is deprecated and will be removed in a future version. "
+            f"{name} is deprecated and will be removed in v5. "
             f"Use {replacement} instead.",
             DeprecationWarning,
             stacklevel=2,
diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py
index d5370b2c6c..2a5f7dc6e0 100644
--- a/commitizen/version_schemes.py
+++ b/commitizen/version_schemes.py
@@ -419,7 +419,7 @@ def get_version_scheme(settings: Settings, name: str | None = None) -> VersionSc
     if deprecated_setting:
         warnings.warn(
             DeprecationWarning(
-                "`version_type` setting is deprecated and will be removed in commitizen 4. "
+                "`version_type` setting is deprecated and will be removed in v5. "
                 "Please use `version_scheme` instead"
             )
         )
diff --git a/docs/commands/commit.md b/docs/commands/commit.md
index 5a073a2644..be9d193b97 100644
--- a/docs/commands/commit.md
+++ b/docs/commands/commit.md
@@ -42,7 +42,7 @@ cz c -a -- -n            # Stage all changes and skip the pre-commit and commit-
 ```
 
 !!! warning
-    The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated.
+    The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated and will be removed in v5.
 
 ### Retry
 
diff --git a/tests/test_defaults.py b/tests/test_defaults.py
index 2298068662..73cd35b80c 100644
--- a/tests/test_defaults.py
+++ b/tests/test_defaults.py
@@ -19,9 +19,7 @@ def test_getattr_deprecated_vars():
     # Verify warning messages
     assert len(record) == 7
     for warning in record:
-        assert "is deprecated and will be removed in a future version" in str(
-            warning.message
-        )
+        assert "is deprecated and will be removed" in str(warning.message)
 
 
 def test_getattr_non_existent():

From 1f9578fcbc9c23d0ff221e3e61f96b7e69b3bb96 Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 15:25:28 +0800
Subject: [PATCH 118/169] build(deps-dev): bump ruff

---
 poetry.lock    | 40 ++++++++++++++++++++--------------------
 pyproject.toml |  3 ++-
 2 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index cbe0fb0e3c..f9f74513a8 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1574,30 +1574,30 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
 
 [[package]]
 name = "ruff"
-version = "0.9.10"
+version = "0.11.13"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
 groups = ["linters"]
 files = [
-    {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"},
-    {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"},
-    {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"},
-    {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"},
-    {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"},
-    {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"},
-    {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"},
-    {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"},
-    {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"},
-    {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"},
-    {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"},
-    {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"},
-    {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"},
-    {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"},
-    {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"},
-    {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"},
-    {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"},
-    {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"},
+    {file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"},
+    {file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"},
+    {file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"},
+    {file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"},
+    {file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"},
+    {file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"},
+    {file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"},
 ]
 
 [[package]]
@@ -2014,4 +2014,4 @@ type = ["pytest-mypy"]
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "6eea6e061a2ece897c2fb37b0b5116085057e6d97e1f22c0fcd4294fb0ad1dbf"
+content-hash = "10e298e9b4d2f2d81a82b43649a68d557ed3e9824b3b210b5b6a607935f9fe9d"
diff --git a/pyproject.toml b/pyproject.toml
index 1a29ca6bf9..d1e89cbed0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -119,7 +119,7 @@ pytest-xdist = "^3.1.0"
 deprecated = "^1.2.13"
 
 [tool.poetry.group.linters.dependencies]
-ruff = ">=0.5.0,<0.10.0"
+ruff = "^0.11.5"
 pre-commit = ">=2.18,<5.0"
 mypy = "^1.16.0"
 types-deprecated = "^1.2.9.2"
@@ -182,6 +182,7 @@ commands_pre = [["poetry", "install", "--only", "main,test"]]
 commands = [["pytest", { replace = "posargs", extend = true }]]
 
 [tool.ruff]
+required-version = ">=0.11.5"
 line-length = 88
 
 [tool.ruff.lint]

From 82cdfff22fa1bdf7ed3bf2fb673e6a07d5adf33d Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 15:30:28 +0800
Subject: [PATCH 119/169] style(ruff): resolve F401

---
 commitizen/version_schemes.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py
index 2a5f7dc6e0..96a68b05fd 100644
--- a/commitizen/version_schemes.py
+++ b/commitizen/version_schemes.py
@@ -19,7 +19,7 @@
 else:
     import importlib_metadata as metadata
 
-from packaging.version import InvalidVersion  # noqa: F401: expose the common exception
+from packaging.version import InvalidVersion  # noqa: F401 (expose the common exception)
 from packaging.version import Version as _BaseVersion
 
 from commitizen.defaults import MAJOR, MINOR, PATCH, Settings

From ac3318dd19a7569dc8e887b956d6ef447aec25c8 Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 15:40:08 +0800
Subject: [PATCH 120/169] build(ruff): add RUF022 & RUF100 rules

---
 pyproject.toml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/pyproject.toml b/pyproject.toml
index d1e89cbed0..757c72bc0a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -195,6 +195,10 @@ select = [
     "UP",
     # isort
     "I",
+    # unsorted-dunder-all
+    "RUF022",
+    # unused-noqa
+    "RUF100",
 ]
 ignore = ["E501", "D1", "D415"]
 

From 922bdc30cb9aa679f2be4fa19997d0d05b394fe9 Mon Sep 17 00:00:00 2001
From: gbaian10 <gbaian10@gmail.com>
Date: Sat, 31 May 2025 15:41:42 +0800
Subject: [PATCH 121/169] style(ruff): run `ruff check --fix`

Auto fix RUF022 and RUF100
---
 commitizen/commands/__init__.py                            | 4 ++--
 commitizen/commands/bump.py                                | 2 +-
 commitizen/cz/conventional_commits/conventional_commits.py | 2 +-
 commitizen/cz/jira/jira.py                                 | 2 +-
 commitizen/providers/__init__.py                           | 2 +-
 tests/conftest.py                                          | 2 +-
 tests/test_bump_find_increment.py                          | 4 ++--
 tests/test_cz_conventional_commits.py                      | 4 ++--
 8 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/commitizen/commands/__init__.py b/commitizen/commands/__init__.py
index 806e384522..58b18297dc 100644
--- a/commitizen/commands/__init__.py
+++ b/commitizen/commands/__init__.py
@@ -11,13 +11,13 @@
 
 __all__ = (
     "Bump",
+    "Changelog",
     "Check",
     "Commit",
-    "Changelog",
     "Example",
     "Info",
+    "Init",
     "ListCz",
     "Schema",
     "Version",
-    "Init",
 )
diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 3f909d97cb..9556492499 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -135,7 +135,7 @@ def find_increment(self, commits: list[git.GitCommit]) -> Increment | None:
             )
         return bump.find_increment(commits, regex=bump_pattern, increments_map=bump_map)
 
-    def __call__(self) -> None:  # noqa: C901
+    def __call__(self) -> None:
         """Steps executed to bump."""
         provider = get_provider(self.config)
 
diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index 9a2b9016b7..ff79119886 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -31,7 +31,7 @@ class ConventionalCommitsCz(BaseCommitizen):
     bump_pattern = defaults.BUMP_PATTERN
     bump_map = defaults.BUMP_MAP
     bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO
-    commit_parser = r"^((?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?|\w+!):\s(?P<message>.*)?"  # noqa
+    commit_parser = r"^((?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?|\w+!):\s(?P<message>.*)?"
     change_type_map = {
         "feat": "Feat",
         "fix": "Fix",
diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py
index f43de2177c..05e23e1690 100644
--- a/commitizen/cz/jira/jira.py
+++ b/commitizen/cz/jira/jira.py
@@ -67,7 +67,7 @@ def example(self) -> str:
         )
 
     def schema(self) -> str:
-        return "<ignored text> <ISSUE_KEY> <ignored text> #<COMMAND> <optional COMMAND_ARGUMENTS>"  # noqa
+        return "<ignored text> <ISSUE_KEY> <ignored text> #<COMMAND> <optional COMMAND_ARGUMENTS>"
 
     def schema_pattern(self) -> str:
         return r".*[A-Z]{2,}\-[0-9]+( #| .* #).+( #.+)*"
diff --git a/commitizen/providers/__init__.py b/commitizen/providers/__init__.py
index 9cf4ce5927..3e01fe22f8 100644
--- a/commitizen/providers/__init__.py
+++ b/commitizen/providers/__init__.py
@@ -21,7 +21,6 @@
 from commitizen.providers.uv_provider import UvProvider
 
 __all__ = [
-    "get_provider",
     "CargoProvider",
     "CommitizenProvider",
     "ComposerProvider",
@@ -30,6 +29,7 @@
     "PoetryProvider",
     "ScmProvider",
     "UvProvider",
+    "get_provider",
 ]
 
 PROVIDER_ENTRYPOINT = "commitizen.provider"
diff --git a/tests/conftest.py b/tests/conftest.py
index 60c586f2e6..1b49dcbfaa 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -169,7 +169,7 @@ class SemverCommitizen(BaseCommitizen):
         "patch": "PATCH",
     }
     changelog_pattern = r"^(patch|minor|major)"
-    commit_parser = r"^(?P<change_type>patch|minor|major)(?:\((?P<scope>[^()\r\n]*)\)|\()?:?\s(?P<message>.+)"  # noqa
+    commit_parser = r"^(?P<change_type>patch|minor|major)(?:\((?P<scope>[^()\r\n]*)\)|\()?:?\s(?P<message>.+)"
     change_type_map = {
         "major": "Breaking Changes",
         "minor": "Features",
diff --git a/tests/test_bump_find_increment.py b/tests/test_bump_find_increment.py
index ff24ff17a7..77e11c78c7 100644
--- a/tests/test_bump_find_increment.py
+++ b/tests/test_bump_find_increment.py
@@ -32,14 +32,14 @@
 MAJOR_INCREMENTS_BREAKING_CHANGE_CC = [
     "feat(cli): added version",
     "docs(README): motivation",
-    "BREAKING CHANGE: `extends` key in config file is now used for extending other config files",  # noqa
+    "BREAKING CHANGE: `extends` key in config file is now used for extending other config files",
     "fix(setup.py): future is now required for every python version",
 ]
 
 MAJOR_INCREMENTS_BREAKING_CHANGE_ALT_CC = [
     "feat(cli): added version",
     "docs(README): motivation",
-    "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files",  # noqa
+    "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files",
     "fix(setup.py): future is now required for every python version",
 ]
 
diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py
index 6d4e0f7435..7863e2f38c 100644
--- a/tests/test_cz_conventional_commits.py
+++ b/tests/test_cz_conventional_commits.py
@@ -89,7 +89,7 @@ def test_long_answer(config):
     message = conventional_commits.message(answers)
     assert (
         message
-        == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24"  # noqa
+        == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24"
     )
 
 
@@ -107,7 +107,7 @@ def test_breaking_change_in_footer(config):
     print(message)
     assert (
         message
-        == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users"  # noqa
+        == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users"
     )
 
 

From b6ad3d23cd3cd90f2f235f0fc7e501e551097087 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 12:10:17 +0800
Subject: [PATCH 122/169] refactor(BaseCommitizen): remove unused
 process_commit

---
 commitizen/cz/base.py                         |  7 ------
 .../conventional_commits.py                   |  6 -----
 tests/test_cz_base.py                         |  6 -----
 tests/test_cz_conventional_commits.py         | 23 -------------------
 4 files changed, 42 deletions(-)

diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py
index 43455a74ca..7463a67c24 100644
--- a/commitizen/cz/base.py
+++ b/commitizen/cz/base.py
@@ -99,10 +99,3 @@ def schema_pattern(self) -> str:
     def info(self) -> str:
         """Information about the standardized commit message."""
         raise NotImplementedError("Not Implemented yet")
-
-    def process_commit(self, commit: str) -> str:
-        """Process commit for changelog.
-
-        If not overwritten, it returns the first line of commit.
-        """
-        return commit.split("\n")[0]
diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index ff79119886..320aa9b324 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -1,5 +1,4 @@
 import os
-import re
 
 from commitizen import defaults
 from commitizen.cz.base import BaseCommitizen
@@ -198,8 +197,3 @@ def info(self) -> str:
         filepath = os.path.join(dir_path, "conventional_commits_info.txt")
         with open(filepath, encoding=self.config.settings["encoding"]) as f:
             return f.read()
-
-    def process_commit(self, commit: str) -> str:
-        if m := re.match(self.schema_pattern(), commit):
-            return m.group(3).strip()
-        return ""
diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py
index 4ee1cc6eda..be93b4ca0f 100644
--- a/tests/test_cz_base.py
+++ b/tests/test_cz_base.py
@@ -42,9 +42,3 @@ def test_info(config):
     cz = DummyCz(config)
     with pytest.raises(NotImplementedError):
         cz.info()
-
-
-def test_process_commit(config):
-    cz = DummyCz(config)
-    message = cz.process_commit("test(test_scope): this is test msg")
-    assert message == "test(test_scope): this is test msg"
diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py
index 7863e2f38c..89653f4c63 100644
--- a/tests/test_cz_conventional_commits.py
+++ b/tests/test_cz_conventional_commits.py
@@ -130,26 +130,3 @@ def test_info(config):
     conventional_commits = ConventionalCommitsCz(config)
     info = conventional_commits.info()
     assert isinstance(info, str)
-
-
-@pytest.mark.parametrize(
-    ("commit_message", "expected_message"),
-    [
-        (
-            "test(test_scope): this is test msg",
-            "this is test msg",
-        ),
-        (
-            "test(test_scope)!: this is test msg",
-            "this is test msg",
-        ),
-        (
-            "test!(test_scope): this is test msg",
-            "",
-        ),
-    ],
-)
-def test_process_commit(commit_message, expected_message, config):
-    conventional_commits = ConventionalCommitsCz(config)
-    message = conventional_commits.process_commit(commit_message)
-    assert message == expected_message

From 8c335705aef9acc83276d4be1c8798a1d2a0f1f6 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 29 May 2025 22:13:11 +0800
Subject: [PATCH 123/169] style(tags): improve types

---
 commitizen/tags.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/commitizen/tags.py b/commitizen/tags.py
index c5f06884fe..4231e0053e 100644
--- a/commitizen/tags.py
+++ b/commitizen/tags.py
@@ -2,7 +2,7 @@
 
 import re
 import warnings
-from collections.abc import Sequence
+from collections.abc import Iterable, Sequence
 from dataclasses import dataclass, field
 from functools import cached_property
 from string import Template
@@ -89,14 +89,14 @@ class TagRules:
     merge_prereleases: bool = False
 
     @cached_property
-    def version_regexes(self) -> Sequence[re.Pattern]:
+    def version_regexes(self) -> list[re.Pattern]:
         """Regexes for all legit tag formats, current and legacy"""
         tag_formats = [self.tag_format, *self.legacy_tag_formats]
         regexes = (self._format_regex(p) for p in tag_formats)
         return [re.compile(r) for r in regexes]
 
     @cached_property
-    def ignored_regexes(self) -> Sequence[re.Pattern]:
+    def ignored_regexes(self) -> list[re.Pattern]:
         """Regexes for known but ignored tag formats"""
         regexes = (self._format_regex(p, star=True) for p in self.ignored_tag_formats)
         return [re.compile(r) for r in regexes]
@@ -135,8 +135,8 @@ def is_ignored_tag(self, tag: str | GitTag) -> bool:
         return any(regex.match(tag) for regex in self.ignored_regexes)
 
     def get_version_tags(
-        self, tags: Sequence[GitTag], warn: bool = False
-    ) -> Sequence[GitTag]:
+        self, tags: Iterable[GitTag], warn: bool = False
+    ) -> list[GitTag]:
         """Filter in version tags and warn on unexpected tags"""
         return [tag for tag in tags if self.is_version_tag(tag, warn)]
 
@@ -236,7 +236,7 @@ def normalize_tag(
         )
 
     def find_tag_for(
-        self, tags: Sequence[GitTag], version: Version | str
+        self, tags: Iterable[GitTag], version: Version | str
     ) -> GitTag | None:
         """Find the first matching tag for a given version."""
         version = self.scheme(version) if isinstance(version, str) else version

From 28b3b9b8b553acb379a18f313ba8ec88e895ab6a Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 29 May 2025 22:18:06 +0800
Subject: [PATCH 124/169] perf(tags): use set

---
 commitizen/tags.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/commitizen/tags.py b/commitizen/tags.py
index 4231e0053e..b19bb89e09 100644
--- a/commitizen/tags.py
+++ b/commitizen/tags.py
@@ -240,11 +240,11 @@ def find_tag_for(
     ) -> GitTag | None:
         """Find the first matching tag for a given version."""
         version = self.scheme(version) if isinstance(version, str) else version
-        possible_tags = [
+        possible_tags = set(
             self.normalize_tag(version, f)
             for f in (self.tag_format, *self.legacy_tag_formats)
-        ]
-        candidates = [t for t in tags if any(t.name == p for p in possible_tags)]
+        )
+        candidates = [t for t in tags if t.name in possible_tags]
         if len(candidates) > 1:
             warnings.warn(
                 UserWarning(

From 898d054a443d6fd591e5a7e8c4156f7e95038287 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Thu, 29 May 2025 23:20:33 +0800
Subject: [PATCH 125/169] refactor: fix mypy output and better type

---
 commitizen/changelog.py                       | 10 ++++----
 commitizen/cli.py                             | 14 +++++++----
 commitizen/cmd.py                             |  5 ++--
 commitizen/commands/changelog.py              | 24 ++++++++++---------
 commitizen/commands/check.py                  | 10 ++++----
 commitizen/commands/commit.py                 | 14 +++++++----
 commitizen/commands/info.py                   |  4 ++--
 commitizen/commands/init.py                   |  8 +++----
 commitizen/commands/list_cz.py                |  4 ++--
 commitizen/commands/version.py                |  6 +++--
 commitizen/config/base_config.py              |  2 +-
 .../conventional_commits.py                   |  4 ++--
 commitizen/cz/utils.py                        |  5 ++--
 commitizen/exceptions.py                      |  7 +++---
 commitizen/git.py                             | 20 +++++++++-------
 commitizen/out.py                             |  7 +++---
 tests/test_git.py                             |  7 ++++++
 17 files changed, 90 insertions(+), 61 deletions(-)

diff --git a/commitizen/changelog.py b/commitizen/changelog.py
index bac4d824f9..6526b4c540 100644
--- a/commitizen/changelog.py
+++ b/commitizen/changelog.py
@@ -29,7 +29,7 @@
 
 import re
 from collections import OrderedDict, defaultdict
-from collections.abc import Generator, Iterable, Mapping
+from collections.abc import Generator, Iterable, Mapping, Sequence
 from dataclasses import dataclass
 from datetime import date
 from typing import TYPE_CHECKING, Any
@@ -225,7 +225,7 @@ def render_changelog(
     tree: Iterable,
     loader: BaseLoader,
     template: str,
-    **kwargs,
+    **kwargs: Any,
 ) -> str:
     jinja_template = get_changelog_template(loader, template)
     changelog: str = jinja_template.render(tree=tree, **kwargs)
@@ -282,7 +282,7 @@ def incremental_build(
 
 
 def get_smart_tag_range(
-    tags: list[GitTag], newest: str, oldest: str | None = None
+    tags: Sequence[GitTag], newest: str, oldest: str | None = None
 ) -> list[GitTag]:
     """Smart because it finds the N+1 tag.
 
@@ -308,10 +308,10 @@ def get_smart_tag_range(
 
 
 def get_oldest_and_newest_rev(
-    tags: list[GitTag],
+    tags: Sequence[GitTag],
     version: str,
     rules: TagRules,
-) -> tuple[str | None, str | None]:
+) -> tuple[str | None, str]:
     """Find the tags for the given version.
 
     `version` may come in different formats:
diff --git a/commitizen/cli.py b/commitizen/cli.py
index d245f731e3..9998691f41 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -50,7 +50,7 @@ def __call__(
         namespace: argparse.Namespace,
         kwarg: str | Sequence[Any] | None,
         option_string: str | None = None,
-    ):
+    ) -> None:
         if not isinstance(kwarg, str):
             return
         if "=" not in kwarg:
@@ -550,8 +550,12 @@ def __call__(
 
 
 def commitizen_excepthook(
-    type, value, traceback, debug=False, no_raise: list[int] | None = None
-):
+    type: type[BaseException],
+    value: BaseException,
+    traceback: TracebackType | None,
+    debug: bool = False,
+    no_raise: list[int] | None = None,
+) -> None:
     traceback = traceback if isinstance(traceback, TracebackType) else None
     if not isinstance(value, CommitizenException):
         original_excepthook(type, value, traceback)
@@ -581,7 +585,7 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]:
     represents the exit code found in exceptions.
     """
     no_raise_items: list[str] = comma_separated_no_raise.split(",")
-    no_raise_codes = []
+    no_raise_codes: list[int] = []
     for item in no_raise_items:
         if item.isdecimal():
             no_raise_codes.append(int(item))
@@ -621,7 +625,7 @@ class Args(argparse.Namespace):
         ]
 
 
-def main():
+def main() -> None:
     parser: argparse.ArgumentParser = cli(data)
     argcomplete.autocomplete(parser)
     # Show help if no arg provided
diff --git a/commitizen/cmd.py b/commitizen/cmd.py
index ba48ac7881..9d6b2f6d2a 100644
--- a/commitizen/cmd.py
+++ b/commitizen/cmd.py
@@ -1,6 +1,7 @@
 import os
 import subprocess
-from typing import NamedTuple
+from collections.abc import Mapping
+from typing import NamedTuple, Union
 
 from charset_normalizer import from_bytes
 
@@ -28,7 +29,7 @@ def _try_decode(bytes_: bytes) -> str:
             raise CharacterSetDecodeError() from e
 
 
-def run(cmd: str, env=None) -> Command:
+def run(cmd: str, env: Union[Mapping[str, str], None] = None) -> Command:
     if env is not None:
         env = {**os.environ, **env}
     process = subprocess.Popen(
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 7bf644a934..181531dee2 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -2,10 +2,11 @@
 
 import os
 import os.path
-from collections.abc import Generator
+from collections.abc import Generator, Iterable, Mapping
 from difflib import SequenceMatcher
 from operator import itemgetter
 from pathlib import Path
+from typing import Any, cast
 
 from commitizen import changelog, defaults, factory, git, out
 from commitizen.changelog_formats import get_changelog_format
@@ -28,7 +29,7 @@
 class Changelog:
     """Generate a changelog based on the commit history."""
 
-    def __init__(self, config: BaseConfig, args):
+    def __init__(self, config: BaseConfig, args: Mapping[str, Any]):
         if not git.is_git_project():
             raise NotAGitProjectError()
 
@@ -76,10 +77,11 @@ def __init__(self, config: BaseConfig, args):
         self.change_type_map = (
             self.config.settings.get("change_type_map") or self.cz.change_type_map
         )
-        self.change_type_order = (
+        self.change_type_order = cast(
+            list[str],
             self.config.settings.get("change_type_order")
             or self.cz.change_type_order
-            or defaults.CHANGE_TYPE_ORDER
+            or defaults.CHANGE_TYPE_ORDER,
         )
         self.rev_range = args.get("rev_range")
         self.tag_format: str = (
@@ -102,7 +104,7 @@ def __init__(self, config: BaseConfig, args):
         self.extras = args.get("extras") or {}
         self.export_template_to = args.get("export_template")
 
-    def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str:
+    def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str:
         """Try to find the 'start_rev'.
 
         We use a similarity approach. We know how to parse the version from the markdown
@@ -151,18 +153,18 @@ def write_changelog(
 
             changelog_file.write(changelog_out)
 
-    def export_template(self):
+    def export_template(self) -> None:
         tpl = changelog.get_changelog_template(self.cz.template_loader, self.template)
-        src = Path(tpl.filename)
-        Path(self.export_template_to).write_text(src.read_text())
+        src = Path(tpl.filename)  # type: ignore
+        Path(self.export_template_to).write_text(src.read_text())  # type: ignore
 
-    def __call__(self):
+    def __call__(self) -> None:
         commit_parser = self.cz.commit_parser
         changelog_pattern = self.cz.changelog_pattern
         start_rev = self.start_rev
         unreleased_version = self.unreleased_version
         changelog_meta = changelog.Metadata()
-        change_type_map: dict | None = self.change_type_map
+        change_type_map: dict[str, str] | None = self.change_type_map  # type: ignore
         changelog_message_builder_hook: MessageBuilderHook | None = (
             self.cz.changelog_message_builder_hook
         )
@@ -190,7 +192,7 @@ def __call__(self):
             changelog_meta = self.changelog_format.get_metadata(self.file_name)
             if changelog_meta.latest_version:
                 start_rev = self._find_incremental_rev(
-                    strip_local_version(changelog_meta.latest_version_tag), tags
+                    strip_local_version(changelog_meta.latest_version_tag or ""), tags
                 )
         if self.rev_range:
             start_rev, end_rev = changelog.get_oldest_and_newest_rev(
diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index 1e3b8464e1..6cf91bb926 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -17,7 +17,9 @@
 class Check:
     """Check if the current commit msg matches the commitizen format."""
 
-    def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd()):
+    def __init__(
+        self, config: BaseConfig, arguments: dict[str, Any], cwd: str = os.getcwd()
+    ):
         """Initial check command.
 
         Args:
@@ -48,7 +50,7 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd(
         self.encoding = config.settings["encoding"]
         self.cz = factory.committer_factory(self.config)
 
-    def _valid_command_argument(self):
+    def _valid_command_argument(self) -> None:
         num_exclusive_args_provided = sum(
             arg is not None
             for arg in (self.commit_msg_file, self.commit_msg, self.rev_range)
@@ -61,7 +63,7 @@ def _valid_command_argument(self):
                 "See 'cz check -h' for more information"
             )
 
-    def __call__(self):
+    def __call__(self) -> None:
         """Validate if commit messages follows the conventional pattern.
 
         Raises:
@@ -97,7 +99,7 @@ def _get_commit_message(self) -> str | None:
             # Get commit message from file (--commit-msg-file)
             return commit_file.read()
 
-    def _get_commits(self):
+    def _get_commits(self) -> list[git.GitCommit]:
         if (msg := self._get_commit_message()) is not None:
             return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))]
 
diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index eedf77e079..3e7a850a05 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -5,6 +5,8 @@
 import shutil
 import subprocess
 import tempfile
+from pathlib import Path
+from typing import Union, cast
 
 import questionary
 
@@ -105,11 +107,13 @@ def _get_message(self) -> str:
             return self.read_backup_message() or self.prompt_commit_questions()
         return self.prompt_commit_questions()
 
-    def __call__(self):
-        extra_args: str = self.arguments.get("extra_cli_args", "")
-        dry_run: bool = self.arguments.get("dry_run")
-        write_message_to_file: bool = self.arguments.get("write_message_to_file")
-        signoff: bool = self.arguments.get("signoff")
+    def __call__(self) -> None:
+        extra_args = cast(str, self.arguments.get("extra_cli_args", ""))
+        dry_run = cast(bool, self.arguments.get("dry_run"))
+        write_message_to_file = cast(
+            Union[Path, None], self.arguments.get("write_message_to_file")
+        )
+        signoff = cast(bool, self.arguments.get("signoff"))
 
         if self.arguments.get("all"):
             git.add("-u")
diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py
index abd4197e7f..5b649ceb5d 100644
--- a/commitizen/commands/info.py
+++ b/commitizen/commands/info.py
@@ -5,9 +5,9 @@
 class Info:
     """Show in depth explanation of your rules."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object):
         self.config: BaseConfig = config
         self.cz = factory.committer_factory(self.config)
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write(self.cz.info())
diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index 0eb3d99d17..a7e1b56665 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -207,7 +207,7 @@ def _ask_tag(self) -> str:
                 raise NoAnswersError("Tag is required!")
         return latest_tag
 
-    def _ask_tag_format(self, latest_tag) -> str:
+    def _ask_tag_format(self, latest_tag: str) -> str:
         is_correct_format = False
         if latest_tag.startswith("v"):
             tag_format = r"v$version"
@@ -302,7 +302,7 @@ def _ask_update_changelog_on_bump(self) -> bool:
         ).unsafe_ask()
         return update_changelog_on_bump
 
-    def _exec_install_pre_commit_hook(self, hook_types: list[str]):
+    def _exec_install_pre_commit_hook(self, hook_types: list[str]) -> None:
         cmd_str = self._gen_pre_commit_cmd(hook_types)
         c = cmd.run(cmd_str)
         if c.return_code != 0:
@@ -323,7 +323,7 @@ def _gen_pre_commit_cmd(self, hook_types: list[str]) -> str:
         )
         return cmd_str
 
-    def _install_pre_commit_hook(self, hook_types: list[str] | None = None):
+    def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None:
         pre_commit_config_filename = ".pre-commit-config.yaml"
         cz_hook_config = {
             "repo": "https://github.com/commitizen-tools/commitizen",
@@ -369,6 +369,6 @@ def _install_pre_commit_hook(self, hook_types: list[str] | None = None):
         self._exec_install_pre_commit_hook(hook_types)
         out.write("commitizen pre-commit hook is now installed in your '.git'\n")
 
-    def _update_config_file(self, values: dict[str, Any]):
+    def _update_config_file(self, values: dict[str, Any]) -> None:
         for key, value in values.items():
             self.config.set_key(key, value)
diff --git a/commitizen/commands/list_cz.py b/commitizen/commands/list_cz.py
index 99701865af..ff4e12ad44 100644
--- a/commitizen/commands/list_cz.py
+++ b/commitizen/commands/list_cz.py
@@ -6,8 +6,8 @@
 class ListCz:
     """List currently installed rules."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object):
         self.config: BaseConfig = config
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write("\n".join(registry.keys()))
diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py
index 45d553c710..55d0a950a3 100644
--- a/commitizen/commands/version.py
+++ b/commitizen/commands/version.py
@@ -1,5 +1,7 @@
 import platform
 import sys
+from collections.abc import Mapping
+from typing import Any
 
 from commitizen import out
 from commitizen.__version__ import __version__
@@ -10,13 +12,13 @@
 class Version:
     """Get the version of the installed commitizen or the current project."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: Mapping[str, Any]):
         self.config: BaseConfig = config
         self.parameter = args[0]
         self.operating_system = platform.system()
         self.python_version = sys.version
 
-    def __call__(self):
+    def __call__(self) -> None:
         if self.parameter.get("report"):
             out.write(f"Commitizen Version: {__version__}")
             out.write(f"Python Version: {self.python_version}")
diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py
index fd034412fe..587b72aee6 100644
--- a/commitizen/config/base_config.py
+++ b/commitizen/config/base_config.py
@@ -6,7 +6,7 @@
 
 
 class BaseConfig:
-    def __init__(self):
+    def __init__(self) -> None:
         self._settings: Settings = DEFAULT_SETTINGS.copy()
         self.encoding = self.settings["encoding"]
         self._path: Path | None = None
diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index 320aa9b324..5b4889114f 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -8,7 +8,7 @@
 __all__ = ["ConventionalCommitsCz"]
 
 
-def parse_scope(text):
+def parse_scope(text: str) -> str:
     if not text:
         return ""
 
@@ -19,7 +19,7 @@ def parse_scope(text):
     return "-".join(scope)
 
 
-def parse_subject(text):
+def parse_subject(text: str) -> str:
     if isinstance(text, str):
         text = text.strip(".").strip()
 
diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py
index cb79d65d1a..430bda2d43 100644
--- a/commitizen/cz/utils.py
+++ b/commitizen/cz/utils.py
@@ -1,6 +1,7 @@
 import os
 import re
 import tempfile
+from typing import Union
 
 from commitizen import git
 from commitizen.cz import exceptions
@@ -8,13 +9,13 @@
 _RE_LOCAL_VERSION = re.compile(r"\+.+")
 
 
-def required_validator(answer, msg=None):
+def required_validator(answer: str, msg: Union[str, None] = None) -> str:
     if not answer:
         raise exceptions.AnswerRequiredError(msg)
     return answer
 
 
-def multiple_line_breaker(answer, sep="|"):
+def multiple_line_breaker(answer: str, sep: str = "|") -> str:
     return "\n".join(line.strip() for line in answer.split(sep) if line)
 
 
diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py
index 29733b624b..87efabe2ab 100644
--- a/commitizen/exceptions.py
+++ b/commitizen/exceptions.py
@@ -1,4 +1,5 @@
 import enum
+from typing import Any
 
 from commitizen import out
 
@@ -40,7 +41,7 @@ class ExitCode(enum.IntEnum):
 
 
 class CommitizenException(Exception):
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args: str, **kwargs: Any):
         self.output_method = kwargs.get("output_method") or out.error
         self.exit_code: ExitCode = self.__class__.exit_code
         if args:
@@ -50,14 +51,14 @@ def __init__(self, *args, **kwargs):
         else:
             self.message = ""
 
-    def __str__(self):
+    def __str__(self) -> str:
         return self.message
 
 
 class ExpectedExit(CommitizenException):
     exit_code = ExitCode.EXPECTED_EXIT
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args: str, **kwargs: Any):
         output_method = kwargs.get("output_method") or out.write
         kwargs["output_method"] = output_method
         super().__init__(*args, **kwargs)
diff --git a/commitizen/git.py b/commitizen/git.py
index ab2866ac48..48fa13ec52 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -46,15 +46,15 @@ class GitObject:
     name: str
     date: str
 
-    def __eq__(self, other) -> bool:
+    def __eq__(self, other: object) -> bool:
         return hasattr(other, "rev") and self.rev == other.rev
 
 
 class GitCommit(GitObject):
     def __init__(
         self,
-        rev,
-        title,
+        rev: str,
+        title: str,
         body: str = "",
         author: str = "",
         author_email: str = "",
@@ -68,7 +68,7 @@ def __init__(
         self.parents = parents or []
 
     @property
-    def message(self):
+    def message(self) -> str:
         return f"{self.title}\n\n{self.body}".strip()
 
     @classmethod
@@ -127,23 +127,27 @@ def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit:
             parents=[p for p in parents.strip().split(" ") if p],
         )
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return f"{self.title} ({self.rev})"
 
 
 class GitTag(GitObject):
-    def __init__(self, name, rev, date):
+    def __init__(self, name: str, rev: str, date: str):
         self.rev = rev.strip()
         self.name = name.strip()
         self._date = date.strip()
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return f"GitTag('{self.name}', '{self.rev}', '{self.date}')"
 
     @property
-    def date(self):
+    def date(self) -> str:
         return self._date
 
+    @date.setter
+    def date(self, value: str) -> None:
+        self._date = value
+
     @classmethod
     def from_line(cls, line: str, inner_delimiter: str) -> GitTag:
         name, objectname, date, obj = line.split(inner_delimiter)
diff --git a/commitizen/out.py b/commitizen/out.py
index 40342e9de5..1bbfe4329d 100644
--- a/commitizen/out.py
+++ b/commitizen/out.py
@@ -1,5 +1,6 @@
 import io
 import sys
+from typing import Any
 
 from termcolor import colored
 
@@ -8,12 +9,12 @@
         sys.stdout.reconfigure(encoding="utf-8")
 
 
-def write(value: str, *args) -> None:
+def write(value: str, *args: object) -> None:
     """Intended to be used when value is multiline."""
     print(value, *args)
 
 
-def line(value: str, *args, **kwargs) -> None:
+def line(value: str, *args: object, **kwargs: Any) -> None:
     """Wrapper in case I want to do something different later."""
     print(value, *args, **kwargs)
 
@@ -33,7 +34,7 @@ def info(value: str) -> None:
     line(message)
 
 
-def diagnostic(value: str):
+def diagnostic(value: str) -> None:
     line(value, file=sys.stderr)
 
 
diff --git a/tests/test_git.py b/tests/test_git.py
index 3fecaabafd..e242b3a2ae 100644
--- a/tests/test_git.py
+++ b/tests/test_git.py
@@ -18,6 +18,13 @@
 )
 
 
+@pytest.mark.parametrize("date", ["2020-01-21", "1970-01-01"])
+def test_git_tag_date(date: str):
+    git_tag = git.GitTag(rev="sha1-code", name="0.0.1", date="2025-05-30")
+    git_tag.date = date
+    assert git_tag.date == date
+
+
 def test_git_object_eq():
     git_commit = git.GitCommit(
         rev="sha1-code", title="this is title", body="this is body"

From 1647841d83c8047421f5bc75edadd55fd74ae795 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 01:25:06 +0800
Subject: [PATCH 126/169] refactor(conventional_commits): remove unnecessary
 checks

---
 .../conventional_commits.py                   | 22 +++-----
 tests/test_cz_conventional_commits.py         | 54 +++++++++----------
 2 files changed, 30 insertions(+), 46 deletions(-)

diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index 5b4889114f..912770a726 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -8,22 +8,12 @@
 __all__ = ["ConventionalCommitsCz"]
 
 
-def parse_scope(text: str) -> str:
-    if not text:
-        return ""
+def _parse_scope(text: str) -> str:
+    return "-".join(text.strip().split())
 
-    scope = text.strip().split()
-    if len(scope) == 1:
-        return scope[0]
 
-    return "-".join(scope)
-
-
-def parse_subject(text: str) -> str:
-    if isinstance(text, str):
-        text = text.strip(".").strip()
-
-    return required_validator(text, msg="Subject is required.")
+def _parse_subject(text: str) -> str:
+    return required_validator(text.strip(".").strip(), msg="Subject is required.")
 
 
 class ConventionalCommitsCz(BaseCommitizen):
@@ -112,12 +102,12 @@ def questions(self) -> Questions:
                 "message": (
                     "What is the scope of this change? (class or file name): (press [enter] to skip)\n"
                 ),
-                "filter": parse_scope,
+                "filter": _parse_scope,
             },
             {
                 "type": "input",
                 "name": "subject",
-                "filter": parse_subject,
+                "filter": _parse_subject,
                 "message": (
                     "Write a short and imperative summary of the code changes: (lower case and no period)\n"
                 ),
diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py
index 89653f4c63..c96e036707 100644
--- a/tests/test_cz_conventional_commits.py
+++ b/tests/test_cz_conventional_commits.py
@@ -2,48 +2,42 @@
 
 from commitizen.cz.conventional_commits.conventional_commits import (
     ConventionalCommitsCz,
-    parse_scope,
-    parse_subject,
+    _parse_scope,
+    _parse_subject,
 )
 from commitizen.cz.exceptions import AnswerRequiredError
 
-valid_scopes = ["", "simple", "dash-separated", "camelCaseUPPERCASE"]
 
-scopes_transformations = [["with spaces", "with-spaces"], [None, ""]]
-
-valid_subjects = ["this is a normal text", "aword"]
-
-subjects_transformations = [["with dot.", "with dot"]]
-
-invalid_subjects = ["", "   ", ".", "   .", "", None]
-
-
-def test_parse_scope_valid_values():
-    for valid_scope in valid_scopes:
-        assert valid_scope == parse_scope(valid_scope)
+@pytest.mark.parametrize(
+    "valid_scope", ["", "simple", "dash-separated", "camelCaseUPPERCASE"]
+)
+def test_parse_scope_valid_values(valid_scope):
+    assert valid_scope == _parse_scope(valid_scope)
 
 
-def test_scopes_transformations():
-    for scopes_transformation in scopes_transformations:
-        invalid_scope, transformed_scope = scopes_transformation
-        assert transformed_scope == parse_scope(invalid_scope)
+@pytest.mark.parametrize(
+    "scopes_transformation", [["with spaces", "with-spaces"], ["", ""]]
+)
+def test_scopes_transformations(scopes_transformation):
+    invalid_scope, transformed_scope = scopes_transformation
+    assert transformed_scope == _parse_scope(invalid_scope)
 
 
-def test_parse_subject_valid_values():
-    for valid_subject in valid_subjects:
-        assert valid_subject == parse_subject(valid_subject)
+@pytest.mark.parametrize("valid_subject", ["this is a normal text", "aword"])
+def test_parse_subject_valid_values(valid_subject):
+    assert valid_subject == _parse_subject(valid_subject)
 
 
-def test_parse_subject_invalid_values():
-    for valid_subject in invalid_subjects:
-        with pytest.raises(AnswerRequiredError):
-            parse_subject(valid_subject)
+@pytest.mark.parametrize("invalid_subject", ["", "   ", ".", "   .", "\t\t."])
+def test_parse_subject_invalid_values(invalid_subject):
+    with pytest.raises(AnswerRequiredError):
+        _parse_subject(invalid_subject)
 
 
-def test_subject_transformations():
-    for subject_transformation in subjects_transformations:
-        invalid_subject, transformed_subject = subject_transformation
-        assert transformed_subject == parse_subject(invalid_subject)
+@pytest.mark.parametrize("subject_transformation", [["with dot.", "with dot"]])
+def test_subject_transformations(subject_transformation):
+    invalid_subject, transformed_subject = subject_transformation
+    assert transformed_subject == _parse_subject(invalid_subject)
 
 
 def test_questions(config):

From 37eac1f292a6593935dfe32c0389f016947116ff Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 14:25:50 +0800
Subject: [PATCH 127/169] refactor: make methods protected, better type

---
 commitizen/commands/bump.py         |  8 ++++----
 commitizen/commands/changelog.py    |  8 ++++----
 commitizen/commands/commit.py       | 10 +++++-----
 commitizen/commands/init.py         |  4 ++--
 commitizen/config/base_config.py    |  3 +++
 tests/commands/test_bump_command.py | 10 +++++-----
 6 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 9556492499..0607c7ef73 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -101,7 +101,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
         )
         self.extras = arguments["extras"]
 
-    def is_initial_tag(
+    def _is_initial_tag(
         self, current_tag: git.GitTag | None, is_yes: bool = False
     ) -> bool:
         """Check if reading the whole git tree up to HEAD is needed."""
@@ -118,7 +118,7 @@ def is_initial_tag(
         )
         return bool(questionary.confirm("Is this the first tag created?").ask())
 
-    def find_increment(self, commits: list[git.GitCommit]) -> Increment | None:
+    def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None:
         # Update the bump map to ensure major version doesn't increment.
         is_major_version_zero: bool = self.bump_settings["major_version_zero"]
         # self.cz.bump_map = defaults.bump_map_major_version_zero
@@ -227,7 +227,7 @@ def __call__(self) -> None:
             current_tag, "name", rules.normalize_tag(current_version)
         )
 
-        is_initial = self.is_initial_tag(current_tag, is_yes)
+        is_initial = self._is_initial_tag(current_tag, is_yes)
 
         if manual_version:
             try:
@@ -255,7 +255,7 @@ def __call__(self) -> None:
                         "[NO_COMMITS_FOUND]\nNo new commits found."
                     )
 
-                increment = self.find_increment(commits)
+                increment = self._find_increment(commits)
 
             # It may happen that there are commits, but they are not eligible
             # for an increment, this generates a problem when using prerelease (#281)
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 181531dee2..11f72cba18 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -136,7 +136,7 @@ def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) ->
             raise NoRevisionError()
         return start_rev
 
-    def write_changelog(
+    def _write_changelog(
         self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata
     ):
         with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file:
@@ -153,7 +153,7 @@ def write_changelog(
 
             changelog_file.write(changelog_out)
 
-    def export_template(self) -> None:
+    def _export_template(self) -> None:
         tpl = changelog.get_changelog_template(self.cz.template_loader, self.template)
         src = Path(tpl.filename)  # type: ignore
         Path(self.export_template_to).write_text(src.read_text())  # type: ignore
@@ -173,7 +173,7 @@ def __call__(self) -> None:
         )
 
         if self.export_template_to:
-            return self.export_template()
+            return self._export_template()
 
         if not changelog_pattern or not commit_parser:
             raise NoPatternMapError(
@@ -240,4 +240,4 @@ def __call__(self) -> None:
             with open(self.file_name, encoding=self.encoding) as changelog_file:
                 lines = changelog_file.readlines()
 
-        self.write_changelog(changelog_out, lines, changelog_meta)
+        self._write_changelog(changelog_out, lines, changelog_meta)
diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index 3e7a850a05..22ad642c43 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -41,7 +41,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
         self.arguments = arguments
         self.temp_file: str = get_backup_file_path()
 
-    def read_backup_message(self) -> str | None:
+    def _read_backup_message(self) -> str | None:
         # Check the commit backup file exists
         if not os.path.isfile(self.temp_file):
             return None
@@ -50,7 +50,7 @@ def read_backup_message(self) -> str | None:
         with open(self.temp_file, encoding=self.encoding) as f:
             return f.read().strip()
 
-    def prompt_commit_questions(self) -> str:
+    def _prompt_commit_questions(self) -> str:
         # Prompt user for the commit message
         cz = self.cz
         questions = cz.questions()
@@ -96,7 +96,7 @@ def manual_edit(self, message: str) -> str:
 
     def _get_message(self) -> str:
         if self.arguments.get("retry"):
-            m = self.read_backup_message()
+            m = self._read_backup_message()
             if m is None:
                 raise NoCommitBackupError()
             return m
@@ -104,8 +104,8 @@ def _get_message(self) -> str:
         if self.config.settings.get("retry_after_failure") and not self.arguments.get(
             "no_retry"
         ):
-            return self.read_backup_message() or self.prompt_commit_questions()
-        return self.prompt_commit_questions()
+            return self._read_backup_message() or self._prompt_commit_questions()
+        return self._prompt_commit_questions()
 
     def __call__(self) -> None:
         extra_args = cast(str, self.arguments.get("extra_cli_args", ""))
diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index a7e1b56665..e03103d4cb 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -85,7 +85,7 @@ def __init__(self, config: BaseConfig, *args):
         self.cz = factory.committer_factory(self.config)
         self.project_info = ProjectInfo()
 
-    def __call__(self):
+    def __call__(self) -> None:
         if self.config.path:
             out.line(f"Config file {self.config.path} already exists")
             return
@@ -120,7 +120,7 @@ def __call__(self):
             self.config = JsonConfig(data="{}", path=config_path)
         elif "yaml" in config_path:
             self.config = YAMLConfig(data="", path=config_path)
-        values_to_add = {}
+        values_to_add: dict[str, Any] = {}
         values_to_add["name"] = cz_name
         values_to_add["tag_format"] = tag_format
         values_to_add["version_scheme"] = version_scheme
diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py
index 587b72aee6..2c7bce206d 100644
--- a/commitizen/config/base_config.py
+++ b/commitizen/config/base_config.py
@@ -41,3 +41,6 @@ def update(self, data: Settings) -> None:
 
     def _parse_setting(self, data: bytes | str) -> None:
         raise NotImplementedError()
+
+    def init_empty_config_content(self) -> None:
+        raise NotImplementedError()
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index 2af7bec121..41da985704 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -1692,7 +1692,7 @@ def test_bump_warn_but_dont_fail_on_invalid_tags(
 
 
 def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project):
-    """Test the is_initial_tag method behavior."""
+    """Test the _is_initial_tag method behavior."""
     # Create a commit but no tags
     create_file_and_commit("feat: initial commit")
 
@@ -1725,15 +1725,15 @@ def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project):
 
     # Test case 1: No current tag, not yes mode
     mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True))
-    assert bump_cmd.is_initial_tag(None, is_yes=False) is True
+    assert bump_cmd._is_initial_tag(None, is_yes=False) is True
 
     # Test case 2: No current tag, yes mode
-    assert bump_cmd.is_initial_tag(None, is_yes=True) is True
+    assert bump_cmd._is_initial_tag(None, is_yes=True) is True
 
     # Test case 3: Has current tag
     mock_tag = mocker.Mock()
-    assert bump_cmd.is_initial_tag(mock_tag, is_yes=False) is False
+    assert bump_cmd._is_initial_tag(mock_tag, is_yes=False) is False
 
     # Test case 4: No current tag, user denies
     mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: False))
-    assert bump_cmd.is_initial_tag(None, is_yes=False) is False
+    assert bump_cmd._is_initial_tag(None, is_yes=False) is False

From c62aac03ee55c8d616ba078357dbbfe973cf5567 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 14:30:57 +0800
Subject: [PATCH 128/169] style: type untyped methods

---
 commitizen/changelog.py                     |  6 +++---
 commitizen/commands/changelog.py            |  2 +-
 commitizen/commands/example.py              |  4 ++--
 commitizen/commands/init.py                 |  2 +-
 commitizen/commands/schema.py               |  4 ++--
 commitizen/config/base_config.py            | 12 +++++++++++-
 commitizen/config/json_config.py            | 14 ++++++++++++--
 commitizen/config/toml_config.py            | 18 ++++++++++++++----
 commitizen/config/yaml_config.py            | 14 ++++++++++++--
 commitizen/cz/base.py                       |  4 ++--
 commitizen/git.py                           |  4 ++--
 commitizen/hooks.py                         |  5 +++--
 commitizen/providers/base_provider.py       | 10 +++++-----
 commitizen/providers/cargo_provider.py      |  2 +-
 commitizen/providers/commitizen_provider.py |  2 +-
 commitizen/providers/poetry_provider.py     |  2 +-
 commitizen/providers/scm_provider.py        |  2 +-
 17 files changed, 74 insertions(+), 33 deletions(-)

diff --git a/commitizen/changelog.py b/commitizen/changelog.py
index 6526b4c540..ba6fbbc6b3 100644
--- a/commitizen/changelog.py
+++ b/commitizen/changelog.py
@@ -63,7 +63,7 @@ class Metadata:
     latest_version_position: int | None = None
     latest_version_tag: str | None = None
 
-    def __post_init__(self):
+    def __post_init__(self) -> None:
         if self.latest_version and not self.latest_version_tag:
             # Test syntactic sugar
             # latest version tag is optional if same as latest version
@@ -169,8 +169,8 @@ def process_commit_message(
     commit: GitCommit,
     changes: dict[str | None, list],
     change_type_map: dict[str, str] | None = None,
-):
-    message: dict = {
+) -> None:
+    message: dict[str, Any] = {
         "sha1": commit.rev,
         "parents": commit.parents,
         "author": commit.author,
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 11f72cba18..5a1270dfd8 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -138,7 +138,7 @@ def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) ->
 
     def _write_changelog(
         self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata
-    ):
+    ) -> None:
         with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file:
             partial_changelog: str | None = None
             if self.incremental:
diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py
index a28ad85f16..818e217530 100644
--- a/commitizen/commands/example.py
+++ b/commitizen/commands/example.py
@@ -5,9 +5,9 @@
 class Example:
     """Show an example so people understands the rules."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object):
         self.config: BaseConfig = config
         self.cz = factory.committer_factory(self.config)
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write(self.cz.example())
diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index e03103d4cb..ca6426b0bd 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -79,7 +79,7 @@ def is_pre_commit_installed(self) -> bool:
 
 
 class Init:
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object):
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
         self.cz = factory.committer_factory(self.config)
diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py
index 4af5679cf5..5c77898d8a 100644
--- a/commitizen/commands/schema.py
+++ b/commitizen/commands/schema.py
@@ -5,9 +5,9 @@
 class Schema:
     """Show structure of the rule."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object):
         self.config: BaseConfig = config
         self.cz = factory.committer_factory(self.config)
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write(self.cz.schema())
diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py
index 2c7bce206d..10a26e3f0c 100644
--- a/commitizen/config/base_config.py
+++ b/commitizen/config/base_config.py
@@ -1,9 +1,19 @@
 from __future__ import annotations
 
 from pathlib import Path
+from typing import TYPE_CHECKING, Any
 
 from commitizen.defaults import DEFAULT_SETTINGS, Settings
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class BaseConfig:
     def __init__(self) -> None:
@@ -28,7 +38,7 @@ def path(self, path: str | Path) -> None:
         """
         self._path = Path(path)
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py
index 28fac25662..83e0a928a0 100644
--- a/commitizen/config/json_config.py
+++ b/commitizen/config/json_config.py
@@ -2,12 +2,22 @@
 
 import json
 from pathlib import Path
+from typing import TYPE_CHECKING, Any
 
 from commitizen.exceptions import InvalidConfigurationError
 from commitizen.git import smart_open
 
 from .base_config import BaseConfig
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class JsonConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
@@ -16,11 +26,11 @@ def __init__(self, *, data: bytes | str, path: Path | str):
         self.path = path
         self._parse_setting(data)
 
-    def init_empty_config_content(self):
+    def init_empty_config_content(self) -> None:
         with smart_open(self.path, "a", encoding=self.encoding) as json_file:
             json.dump({"commitizen": {}}, json_file)
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py
index a7050214ba..9bab994fd5 100644
--- a/commitizen/config/toml_config.py
+++ b/commitizen/config/toml_config.py
@@ -2,6 +2,7 @@
 
 import os
 from pathlib import Path
+from typing import TYPE_CHECKING, Any
 
 from tomlkit import exceptions, parse, table
 
@@ -9,6 +10,15 @@
 
 from .base_config import BaseConfig
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class TomlConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
@@ -17,7 +27,7 @@ def __init__(self, *, data: bytes | str, path: Path | str):
         self.path = path
         self._parse_setting(data)
 
-    def init_empty_config_content(self):
+    def init_empty_config_content(self) -> None:
         if os.path.isfile(self.path):
             with open(self.path, "rb") as input_toml_file:
                 parser = parse(input_toml_file.read())
@@ -27,10 +37,10 @@ def init_empty_config_content(self):
         with open(self.path, "wb") as output_toml_file:
             if parser.get("tool") is None:
                 parser["tool"] = table()
-            parser["tool"]["commitizen"] = table()
+            parser["tool"]["commitizen"] = table()  # type: ignore
             output_toml_file.write(parser.as_string().encode(self.encoding))
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
@@ -39,7 +49,7 @@ def set_key(self, key, value):
         with open(self.path, "rb") as f:
             parser = parse(f.read())
 
-        parser["tool"]["commitizen"][key] = value
+        parser["tool"]["commitizen"][key] = value  # type: ignore
         with open(self.path, "wb") as f:
             f.write(parser.as_string().encode(self.encoding))
         return self
diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py
index 2c384cf17d..8eac9bb785 100644
--- a/commitizen/config/yaml_config.py
+++ b/commitizen/config/yaml_config.py
@@ -1,6 +1,7 @@
 from __future__ import annotations
 
 from pathlib import Path
+from typing import TYPE_CHECKING, Any
 
 import yaml
 
@@ -9,6 +10,15 @@
 
 from .base_config import BaseConfig
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class YAMLConfig(BaseConfig):
     def __init__(self, *, data: bytes | str, path: Path | str):
@@ -17,7 +27,7 @@ def __init__(self, *, data: bytes | str, path: Path | str):
         self.path = path
         self._parse_setting(data)
 
-    def init_empty_config_content(self):
+    def init_empty_config_content(self) -> None:
         with smart_open(self.path, "a", encoding=self.encoding) as json_file:
             yaml.dump({"commitizen": {}}, json_file, explicit_start=True)
 
@@ -41,7 +51,7 @@ def _parse_setting(self, data: bytes | str) -> None:
         except (KeyError, TypeError):
             self.is_empty_config = True
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py
index 7463a67c24..9e803c3d01 100644
--- a/commitizen/cz/base.py
+++ b/commitizen/cz/base.py
@@ -76,13 +76,13 @@ def message(self, answers: dict) -> str:
         """Format your git message."""
 
     @property
-    def style(self):
+    def style(self) -> Style:
         return merge_styles(
             [
                 Style(BaseCommitizen.default_style_config),
                 Style(self.config.settings["style"]),
             ]
-        )
+        )  # type: ignore[return-value]
 
     def example(self) -> str:
         """Example of the commit message."""
diff --git a/commitizen/git.py b/commitizen/git.py
index 48fa13ec52..54d57f69bb 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -215,7 +215,7 @@ def get_commits(
     ]
 
 
-def get_filenames_in_commit(git_reference: str = ""):
+def get_filenames_in_commit(git_reference: str = "") -> list[str]:
     """Get the list of files that were committed in the requested git reference.
 
     :param git_reference: a git reference as accepted by `git show`, default: the last commit
@@ -312,7 +312,7 @@ def get_core_editor() -> str | None:
     return None
 
 
-def smart_open(*args, **kwargs):
+def smart_open(*args, **kwargs):  # type: ignore[no-untyped-def,unused-ignore]
     """Open a file with the EOL style determined from Git."""
     return open(*args, newline=EOLType.for_open(), **kwargs)
 
diff --git a/commitizen/hooks.py b/commitizen/hooks.py
index f5505d0e82..f60bd9b43e 100644
--- a/commitizen/hooks.py
+++ b/commitizen/hooks.py
@@ -1,12 +1,13 @@
 from __future__ import annotations
 
 import os
+from collections.abc import Mapping
 
 from commitizen import cmd, out
 from commitizen.exceptions import RunHookError
 
 
-def run(hooks, _env_prefix="CZ_", **env):
+def run(hooks: str | list[str], _env_prefix: str = "CZ_", **env: object) -> None:
     if isinstance(hooks, str):
         hooks = [hooks]
 
@@ -24,7 +25,7 @@ def run(hooks, _env_prefix="CZ_", **env):
             raise RunHookError(f"Running hook '{hook}' failed")
 
 
-def _format_env(prefix: str, env: dict[str, str]) -> dict[str, str]:
+def _format_env(prefix: str, env: Mapping[str, object]) -> dict[str, str]:
     """_format_env() prefixes all given environment variables with the given
     prefix so it can be passed directly to cmd.run()."""
     penv = dict(os.environ)
diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py
index 34048318e2..9c1e0f7a98 100644
--- a/commitizen/providers/base_provider.py
+++ b/commitizen/providers/base_provider.py
@@ -29,7 +29,7 @@ def get_version(self) -> str:
         """
 
     @abstractmethod
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         """
         Set the new current version
         """
@@ -58,7 +58,7 @@ def get_version(self) -> str:
         document = json.loads(self.file.read_text())
         return self.get(document)
 
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         document = json.loads(self.file.read_text())
         self.set(document, version)
         self.file.write_text(json.dumps(document, indent=self.indent) + "\n")
@@ -66,7 +66,7 @@ def set_version(self, version: str):
     def get(self, document: dict[str, Any]) -> str:
         return document["version"]  # type: ignore
 
-    def set(self, document: dict[str, Any], version: str):
+    def set(self, document: dict[str, Any], version: str) -> None:
         document["version"] = version
 
 
@@ -79,7 +79,7 @@ def get_version(self) -> str:
         document = tomlkit.parse(self.file.read_text())
         return self.get(document)
 
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         document = tomlkit.parse(self.file.read_text())
         self.set(document, version)
         self.file.write_text(tomlkit.dumps(document))
@@ -87,5 +87,5 @@ def set_version(self, version: str):
     def get(self, document: tomlkit.TOMLDocument) -> str:
         return document["project"]["version"]  # type: ignore
 
-    def set(self, document: tomlkit.TOMLDocument, version: str):
+    def set(self, document: tomlkit.TOMLDocument, version: str) -> None:
         document["project"]["version"] = version  # type: ignore
diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py
index 2e73ff35a1..87e45cd71c 100644
--- a/commitizen/providers/cargo_provider.py
+++ b/commitizen/providers/cargo_provider.py
@@ -28,7 +28,7 @@ def get(self, document: tomlkit.TOMLDocument) -> str:
             ...
         return document["workspace"]["package"]["version"]  # type: ignore
 
-    def set(self, document: tomlkit.TOMLDocument, version: str):
+    def set(self, document: tomlkit.TOMLDocument, version: str) -> None:
         try:
             document["workspace"]["package"]["version"] = version  # type: ignore
             return
diff --git a/commitizen/providers/commitizen_provider.py b/commitizen/providers/commitizen_provider.py
index a1da25ff72..7ce177a604 100644
--- a/commitizen/providers/commitizen_provider.py
+++ b/commitizen/providers/commitizen_provider.py
@@ -11,5 +11,5 @@ class CommitizenProvider(VersionProvider):
     def get_version(self) -> str:
         return self.config.settings["version"]  # type: ignore
 
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         self.config.set_key("version", version)
diff --git a/commitizen/providers/poetry_provider.py b/commitizen/providers/poetry_provider.py
index 7aa28f56d9..1dd33f053e 100644
--- a/commitizen/providers/poetry_provider.py
+++ b/commitizen/providers/poetry_provider.py
@@ -15,5 +15,5 @@ class PoetryProvider(TomlProvider):
     def get(self, pyproject: tomlkit.TOMLDocument) -> str:
         return pyproject["tool"]["poetry"]["version"]  # type: ignore
 
-    def set(self, pyproject: tomlkit.TOMLDocument, version: str):
+    def set(self, pyproject: tomlkit.TOMLDocument, version: str) -> None:
         pyproject["tool"]["poetry"]["version"] = version  # type: ignore
diff --git a/commitizen/providers/scm_provider.py b/commitizen/providers/scm_provider.py
index cb575148cb..3085b16efa 100644
--- a/commitizen/providers/scm_provider.py
+++ b/commitizen/providers/scm_provider.py
@@ -23,6 +23,6 @@ def get_version(self) -> str:
             return "0.0.0"
         return str(versions[-1])
 
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         # Not necessary
         pass

From 2e65d17cd086443f9f848755dd6d673052bb3189 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 18:48:20 +0800
Subject: [PATCH 129/169] style: remove Union

---
 commitizen/cmd.py      | 6 ++++--
 commitizen/cz/utils.py | 3 +--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/commitizen/cmd.py b/commitizen/cmd.py
index 9d6b2f6d2a..3f13087233 100644
--- a/commitizen/cmd.py
+++ b/commitizen/cmd.py
@@ -1,7 +1,9 @@
+from __future__ import annotations
+
 import os
 import subprocess
 from collections.abc import Mapping
-from typing import NamedTuple, Union
+from typing import NamedTuple
 
 from charset_normalizer import from_bytes
 
@@ -29,7 +31,7 @@ def _try_decode(bytes_: bytes) -> str:
             raise CharacterSetDecodeError() from e
 
 
-def run(cmd: str, env: Union[Mapping[str, str], None] = None) -> Command:
+def run(cmd: str, env: Mapping[str, str] | None = None) -> Command:
     if env is not None:
         env = {**os.environ, **env}
     process = subprocess.Popen(
diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py
index 430bda2d43..a6f687226c 100644
--- a/commitizen/cz/utils.py
+++ b/commitizen/cz/utils.py
@@ -1,7 +1,6 @@
 import os
 import re
 import tempfile
-from typing import Union
 
 from commitizen import git
 from commitizen.cz import exceptions
@@ -9,7 +8,7 @@
 _RE_LOCAL_VERSION = re.compile(r"\+.+")
 
 
-def required_validator(answer: str, msg: Union[str, None] = None) -> str:
+def required_validator(answer: str, msg: object = None) -> str:
     if not answer:
         raise exceptions.AnswerRequiredError(msg)
     return answer

From 59531a6482330d96ebe58fc7eb286a53b52eca74 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 19:00:05 +0800
Subject: [PATCH 130/169] style: better type

---
 commitizen/cli.py                     | 4 ++--
 commitizen/providers/base_provider.py | 5 +++--
 commitizen/providers/npm_provider.py  | 5 +++--
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index 9998691f41..fa05fb2f73 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -8,7 +8,7 @@
 from functools import partial
 from pathlib import Path
 from types import TracebackType
-from typing import TYPE_CHECKING, Any, cast
+from typing import TYPE_CHECKING, cast
 
 import argcomplete
 from decli import cli
@@ -48,7 +48,7 @@ def __call__(
         self,
         parser: argparse.ArgumentParser,
         namespace: argparse.Namespace,
-        kwarg: str | Sequence[Any] | None,
+        kwarg: str | Sequence[object] | None,
         option_string: str | None = None,
     ) -> None:
         if not isinstance(kwarg, str):
diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py
index 9c1e0f7a98..7a7e1205b4 100644
--- a/commitizen/providers/base_provider.py
+++ b/commitizen/providers/base_provider.py
@@ -2,6 +2,7 @@
 
 import json
 from abc import ABC, abstractmethod
+from collections.abc import Mapping
 from pathlib import Path
 from typing import Any, ClassVar
 
@@ -63,8 +64,8 @@ def set_version(self, version: str) -> None:
         self.set(document, version)
         self.file.write_text(json.dumps(document, indent=self.indent) + "\n")
 
-    def get(self, document: dict[str, Any]) -> str:
-        return document["version"]  # type: ignore
+    def get(self, document: Mapping[str, str]) -> str:
+        return document["version"]
 
     def set(self, document: dict[str, Any], version: str) -> None:
         document["version"] = version
diff --git a/commitizen/providers/npm_provider.py b/commitizen/providers/npm_provider.py
index 12900ff7de..3125447250 100644
--- a/commitizen/providers/npm_provider.py
+++ b/commitizen/providers/npm_provider.py
@@ -1,6 +1,7 @@
 from __future__ import annotations
 
 import json
+from collections.abc import Mapping
 from pathlib import Path
 from typing import Any, ClassVar
 
@@ -58,8 +59,8 @@ def set_version(self, version: str) -> None:
                 json.dumps(shrinkwrap_document, indent=self.indent) + "\n"
             )
 
-    def get_package_version(self, document: dict[str, Any]) -> str:
-        return document["version"]  # type: ignore
+    def get_package_version(self, document: Mapping[str, str]) -> str:
+        return document["version"]
 
     def set_package_version(
         self, document: dict[str, Any], version: str

From e86d80ead8b1684c389f8ef76edb2723ee339e20 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 22:12:30 +0800
Subject: [PATCH 131/169] style: add `-> None` to __init__

---
 commitizen/changelog_formats/__init__.py | 2 +-
 commitizen/changelog_formats/base.py     | 2 +-
 commitizen/commands/bump.py              | 2 +-
 commitizen/commands/changelog.py         | 2 +-
 commitizen/commands/check.py             | 2 +-
 commitizen/commands/commit.py            | 2 +-
 commitizen/commands/example.py           | 2 +-
 commitizen/commands/info.py              | 2 +-
 commitizen/commands/init.py              | 2 +-
 commitizen/commands/list_cz.py           | 2 +-
 commitizen/commands/schema.py            | 2 +-
 commitizen/commands/version.py           | 2 +-
 commitizen/config/json_config.py         | 2 +-
 commitizen/config/toml_config.py         | 2 +-
 commitizen/config/yaml_config.py         | 2 +-
 commitizen/cz/customize/customize.py     | 2 +-
 commitizen/exceptions.py                 | 4 ++--
 commitizen/git.py                        | 4 ++--
 commitizen/providers/base_provider.py    | 2 +-
 commitizen/version_schemes.py            | 2 +-
 20 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py
index 782bfb24cb..b7b3cac01d 100644
--- a/commitizen/changelog_formats/__init__.py
+++ b/commitizen/changelog_formats/__init__.py
@@ -25,7 +25,7 @@ class ChangelogFormat(Protocol):
 
     config: BaseConfig
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         self.config = config
 
     @property
diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py
index f69cf8f00f..cb5d385bf8 100644
--- a/commitizen/changelog_formats/base.py
+++ b/commitizen/changelog_formats/base.py
@@ -20,7 +20,7 @@ class BaseFormat(ChangelogFormat, metaclass=ABCMeta):
     extension: ClassVar[str] = ""
     alternative_extensions: ClassVar[set[str]] = set()
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         # Constructor needs to be redefined because `Protocol` prevent instantiation by default
         # See: https://bugs.python.org/issue44807
         self.config = config
diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 0607c7ef73..0aee74a817 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -40,7 +40,7 @@
 class Bump:
     """Show prompt for the user to create a guided commit."""
 
-    def __init__(self, config: BaseConfig, arguments: dict):
+    def __init__(self, config: BaseConfig, arguments: dict) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 5a1270dfd8..59d799b037 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -29,7 +29,7 @@
 class Changelog:
     """Generate a changelog based on the commit history."""
 
-    def __init__(self, config: BaseConfig, args: Mapping[str, Any]):
+    def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index 6cf91bb926..82a8039fa3 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -19,7 +19,7 @@ class Check:
 
     def __init__(
         self, config: BaseConfig, arguments: dict[str, Any], cwd: str = os.getcwd()
-    ):
+    ) -> None:
         """Initial check command.
 
         Args:
diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index 22ad642c43..68095d0e0c 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -31,7 +31,7 @@
 class Commit:
     """Show prompt for the user to create a guided commit."""
 
-    def __init__(self, config: BaseConfig, arguments: dict):
+    def __init__(self, config: BaseConfig, arguments: dict) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py
index 818e217530..ba9f34adc4 100644
--- a/commitizen/commands/example.py
+++ b/commitizen/commands/example.py
@@ -5,7 +5,7 @@
 class Example:
     """Show an example so people understands the rules."""
 
-    def __init__(self, config: BaseConfig, *args: object):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
         self.cz = factory.committer_factory(self.config)
 
diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py
index 5b649ceb5d..5ea8227313 100644
--- a/commitizen/commands/info.py
+++ b/commitizen/commands/info.py
@@ -5,7 +5,7 @@
 class Info:
     """Show in depth explanation of your rules."""
 
-    def __init__(self, config: BaseConfig, *args: object):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
         self.cz = factory.committer_factory(self.config)
 
diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index ca6426b0bd..1207cd02ee 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -79,7 +79,7 @@ def is_pre_commit_installed(self) -> bool:
 
 
 class Init:
-    def __init__(self, config: BaseConfig, *args: object):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
         self.cz = factory.committer_factory(self.config)
diff --git a/commitizen/commands/list_cz.py b/commitizen/commands/list_cz.py
index ff4e12ad44..412266f6c3 100644
--- a/commitizen/commands/list_cz.py
+++ b/commitizen/commands/list_cz.py
@@ -6,7 +6,7 @@
 class ListCz:
     """List currently installed rules."""
 
-    def __init__(self, config: BaseConfig, *args: object):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
 
     def __call__(self) -> None:
diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py
index 5c77898d8a..a7aeb53569 100644
--- a/commitizen/commands/schema.py
+++ b/commitizen/commands/schema.py
@@ -5,7 +5,7 @@
 class Schema:
     """Show structure of the rule."""
 
-    def __init__(self, config: BaseConfig, *args: object):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
         self.cz = factory.committer_factory(self.config)
 
diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py
index 55d0a950a3..ecf1f03129 100644
--- a/commitizen/commands/version.py
+++ b/commitizen/commands/version.py
@@ -12,7 +12,7 @@
 class Version:
     """Get the version of the installed commitizen or the current project."""
 
-    def __init__(self, config: BaseConfig, *args: Mapping[str, Any]):
+    def __init__(self, config: BaseConfig, *args: Mapping[str, Any]) -> None:
         self.config: BaseConfig = config
         self.parameter = args[0]
         self.operating_system = platform.system()
diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py
index 83e0a928a0..be1f1c36b0 100644
--- a/commitizen/config/json_config.py
+++ b/commitizen/config/json_config.py
@@ -20,7 +20,7 @@
 
 
 class JsonConfig(BaseConfig):
-    def __init__(self, *, data: bytes | str, path: Path | str):
+    def __init__(self, *, data: bytes | str, path: Path | str) -> None:
         super().__init__()
         self.is_empty_config = False
         self.path = path
diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py
index 9bab994fd5..3571c9c882 100644
--- a/commitizen/config/toml_config.py
+++ b/commitizen/config/toml_config.py
@@ -21,7 +21,7 @@
 
 
 class TomlConfig(BaseConfig):
-    def __init__(self, *, data: bytes | str, path: Path | str):
+    def __init__(self, *, data: bytes | str, path: Path | str) -> None:
         super().__init__()
         self.is_empty_config = False
         self.path = path
diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py
index 8eac9bb785..f2a79e6937 100644
--- a/commitizen/config/yaml_config.py
+++ b/commitizen/config/yaml_config.py
@@ -21,7 +21,7 @@
 
 
 class YAMLConfig(BaseConfig):
-    def __init__(self, *, data: bytes | str, path: Path | str):
+    def __init__(self, *, data: bytes | str, path: Path | str) -> None:
         super().__init__()
         self.is_empty_config = False
         self.path = path
diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py
index 53ada4b2c0..8f844501ec 100644
--- a/commitizen/cz/customize/customize.py
+++ b/commitizen/cz/customize/customize.py
@@ -26,7 +26,7 @@ class CustomizeCommitsCz(BaseCommitizen):
     bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO
     change_type_order = defaults.CHANGE_TYPE_ORDER
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         super().__init__(config)
 
         if "customize" not in self.config.settings:
diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py
index 87efabe2ab..8c0956be53 100644
--- a/commitizen/exceptions.py
+++ b/commitizen/exceptions.py
@@ -41,7 +41,7 @@ class ExitCode(enum.IntEnum):
 
 
 class CommitizenException(Exception):
-    def __init__(self, *args: str, **kwargs: Any):
+    def __init__(self, *args: str, **kwargs: Any) -> None:
         self.output_method = kwargs.get("output_method") or out.error
         self.exit_code: ExitCode = self.__class__.exit_code
         if args:
@@ -58,7 +58,7 @@ def __str__(self) -> str:
 class ExpectedExit(CommitizenException):
     exit_code = ExitCode.EXPECTED_EXIT
 
-    def __init__(self, *args: str, **kwargs: Any):
+    def __init__(self, *args: str, **kwargs: Any) -> None:
         output_method = kwargs.get("output_method") or out.write
         kwargs["output_method"] = output_method
         super().__init__(*args, **kwargs)
diff --git a/commitizen/git.py b/commitizen/git.py
index 54d57f69bb..b8528e3f77 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -59,7 +59,7 @@ def __init__(
         author: str = "",
         author_email: str = "",
         parents: list[str] | None = None,
-    ):
+    ) -> None:
         self.rev = rev.strip()
         self.title = title.strip()
         self.body = body.strip()
@@ -132,7 +132,7 @@ def __repr__(self) -> str:
 
 
 class GitTag(GitObject):
-    def __init__(self, name: str, rev: str, date: str):
+    def __init__(self, name: str, rev: str, date: str) -> None:
         self.rev = rev.strip()
         self.name = name.strip()
         self._date = date.strip()
diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py
index 7a7e1205b4..27c3123416 100644
--- a/commitizen/providers/base_provider.py
+++ b/commitizen/providers/base_provider.py
@@ -20,7 +20,7 @@ class VersionProvider(ABC):
 
     config: BaseConfig
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         self.config = config
 
     @abstractmethod
diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py
index 96a68b05fd..a59d3c0aa0 100644
--- a/commitizen/version_schemes.py
+++ b/commitizen/version_schemes.py
@@ -51,7 +51,7 @@ class VersionProtocol(Protocol):
     parser: ClassVar[re.Pattern]
     """Regex capturing this version scheme into a `version` group"""
 
-    def __init__(self, version: str):
+    def __init__(self, version: str) -> None:
         """
         Initialize a version object from its string representation.
 

From 44123c41b8afb58b67d889fd25b90d935f14399a Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 22:35:44 +0800
Subject: [PATCH 132/169] build(ruff,mypy): more strict rules

---
 commitizen/git.py |  2 +-
 pyproject.toml    | 21 ++++++++++++++++-----
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/commitizen/git.py b/commitizen/git.py
index b8528e3f77..fb59750eaf 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -312,7 +312,7 @@ def get_core_editor() -> str | None:
     return None
 
 
-def smart_open(*args, **kwargs):  # type: ignore[no-untyped-def,unused-ignore]
+def smart_open(*args, **kwargs):  # type: ignore[no-untyped-def,unused-ignore] # noqa: ANN201
     """Open a file with the EOL style determined from Git."""
     return open(*args, newline=EOLType.for_open(), **kwargs)
 
diff --git a/pyproject.toml b/pyproject.toml
index 757c72bc0a..9961a2f409 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -187,6 +187,9 @@ line-length = 88
 
 [tool.ruff.lint]
 select = [
+    # flake8-annotations
+    "ANN001",
+    "ANN2",
     # pycodestyle
     "E",
     # Pyflakes
@@ -202,6 +205,9 @@ select = [
 ]
 ignore = ["E501", "D1", "D415"]
 
+[tool.ruff.lint.per-file-ignores]
+"tests/*" = ["ANN"]
+
 [tool.ruff.lint.isort]
 known-first-party = ["commitizen", "tests"]
 
@@ -209,7 +215,8 @@ known-first-party = ["commitizen", "tests"]
 convention = "google"
 
 [tool.mypy]
-files = "commitizen"
+files = ["commitizen", "tests"]
+disallow_untyped_defs = true
 disallow_untyped_decorators = true
 disallow_subclassing_any = true
 warn_return_any = true
@@ -217,6 +224,10 @@ warn_redundant_casts = true
 warn_unused_ignores = true
 warn_unused_configs = true
 
+[[tool.mypy.overrides]]
+module = "tests/*"
+disallow_untyped_defs = false
+
 [[tool.mypy.overrides]]
 module = "py.*"               # Legacy pytest dependencies
 ignore_missing_imports = true
@@ -233,14 +244,14 @@ poetry_command = ""
 [tool.poe.tasks]
 format.help = "Format the code"
 format.sequence = [
-    { cmd = "ruff check --fix commitizen tests" },
-    { cmd = "ruff format commitizen tests" },
+    { cmd = "ruff check --fix" },
+    { cmd = "ruff format" },
 ]
 
 lint.help = "Lint the code"
 lint.sequence = [
-    { cmd = "ruff check commitizen/ tests/ --fix" },
-    { cmd = "mypy commitizen/ tests/" },
+    { cmd = "ruff check" },
+    { cmd = "mypy" },
 ]
 
 check-commit.help = "Check the commit message"

From 20dda31eb1da62eab4839dcfc910d3130f7dfe7b Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 31 May 2025 18:20:41 +0800
Subject: [PATCH 133/169] fix(BaseConfig): mypy error

---
 commitizen/config/base_config.py | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py
index 10a26e3f0c..59c0d16a06 100644
--- a/commitizen/config/base_config.py
+++ b/commitizen/config/base_config.py
@@ -26,16 +26,11 @@ def settings(self) -> Settings:
         return self._settings
 
     @property
-    def path(self) -> Path | None:
-        return self._path
+    def path(self) -> Path:
+        return self._path  # type: ignore
 
     @path.setter
     def path(self, path: str | Path) -> None:
-        """
-        mypy does not like this until 1.16
-        See https://github.com/python/mypy/pull/18510
-        TODO: remove "type: ignore" from the call sites when 1.16 is available
-        """
         self._path = Path(path)
 
     def set_key(self, key: str, value: Any) -> Self:

From 001fe8d2711f50fa093cb0f9e230856da660c316 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 31 May 2025 22:04:10 +0800
Subject: [PATCH 134/169] build(mypy): remove disallow_untyped_defs because
 it's already done by ruff rules

---
 pyproject.toml | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 9961a2f409..ee0ecc9ced 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -216,7 +216,6 @@ convention = "google"
 
 [tool.mypy]
 files = ["commitizen", "tests"]
-disallow_untyped_defs = true
 disallow_untyped_decorators = true
 disallow_subclassing_any = true
 warn_return_any = true
@@ -224,10 +223,6 @@ warn_redundant_casts = true
 warn_unused_ignores = true
 warn_unused_configs = true
 
-[[tool.mypy.overrides]]
-module = "tests/*"
-disallow_untyped_defs = false
-
 [[tool.mypy.overrides]]
 module = "py.*"               # Legacy pytest dependencies
 ignore_missing_imports = true

From f66e577442305255b7f2fdc9ce4e26c742921538 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 31 May 2025 00:45:15 +0800
Subject: [PATCH 135/169] refactor(bump): TypedDict for bump argument

---
 commitizen/cli.py                   |   2 +-
 commitizen/commands/bump.py         | 129 ++++++++++++++++------------
 commitizen/defaults.py              |  38 ++++----
 tests/commands/test_bump_command.py |   2 +-
 4 files changed, 97 insertions(+), 74 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index fa05fb2f73..e1bd57b523 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -680,7 +680,7 @@ def main() -> None:
         )
         sys.excepthook = no_raise_debug_excepthook
 
-    args.func(conf, arguments)()
+    args.func(conf, arguments)()  # type: ignore
 
 
 if __name__ == "__main__":
diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 0aee74a817..beed384b78 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -37,37 +37,65 @@
 logger = getLogger("commitizen")
 
 
+class BumpArguments(Settings, total=False):
+    allow_no_commit: bool | None
+    annotated_tag_message: str | None
+    annotated_tag: bool
+    build_metadata: str | None
+    changelog_to_stdout: bool
+    changelog: bool
+    check_consistency: bool
+    devrelease: int | None
+    dry_run: bool
+    file_name: str
+    files_only: bool | None
+    get_next: bool
+    git_output_to_stderr: bool
+    gpg_sign: bool
+    increment_mode: str
+    increment: Increment | None
+    local_version: bool
+    manual_version: str | None
+    no_verify: bool
+    prerelease: Prerelease | None
+    retry: bool
+    yes: bool
+
+
 class Bump:
     """Show prompt for the user to create a guided commit."""
 
-    def __init__(self, config: BaseConfig, arguments: dict) -> None:
+    def __init__(self, config: BaseConfig, arguments: BumpArguments) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.arguments: dict = arguments
-        self.bump_settings: dict = {
-            **config.settings,
-            **{
-                key: arguments[key]
-                for key in [
-                    "tag_format",
-                    "prerelease",
-                    "increment",
-                    "increment_mode",
-                    "bump_message",
-                    "gpg_sign",
-                    "annotated_tag",
-                    "annotated_tag_message",
-                    "major_version_zero",
-                    "prerelease_offset",
-                    "template",
-                    "file_name",
-                ]
-                if arguments.get(key) is not None
+        self.arguments = arguments
+        self.bump_settings = cast(
+            BumpArguments,
+            {
+                **config.settings,
+                **{
+                    k: v
+                    for k, v in {
+                        "annotated_tag_message": arguments.get("annotated_tag_message"),
+                        "annotated_tag": arguments.get("annotated_tag"),
+                        "bump_message": arguments.get("bump_message"),
+                        "file_name": arguments.get("file_name"),
+                        "gpg_sign": arguments.get("gpg_sign"),
+                        "increment_mode": arguments.get("increment_mode"),
+                        "increment": arguments.get("increment"),
+                        "major_version_zero": arguments.get("major_version_zero"),
+                        "prerelease_offset": arguments.get("prerelease_offset"),
+                        "prerelease": arguments.get("prerelease"),
+                        "tag_format": arguments.get("tag_format"),
+                        "template": arguments.get("template"),
+                    }.items()
+                    if v is not None
+                },
             },
-        }
+        )
         self.cz = factory.committer_factory(self.config)
         self.changelog_flag = arguments["changelog"]
         self.changelog_config = self.config.settings.get("update_changelog_on_bump")
@@ -120,11 +148,10 @@ def _is_initial_tag(
 
     def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None:
         # Update the bump map to ensure major version doesn't increment.
-        is_major_version_zero: bool = self.bump_settings["major_version_zero"]
         # self.cz.bump_map = defaults.bump_map_major_version_zero
         bump_map = (
             self.cz.bump_map_major_version_zero
-            if is_major_version_zero
+            if self.bump_settings["major_version_zero"]
             else self.cz.bump_map
         )
         bump_pattern = self.cz.bump_pattern
@@ -144,23 +171,14 @@ def __call__(self) -> None:
         except TypeError:
             raise NoVersionSpecifiedError()
 
-        bump_commit_message: str | None = self.bump_settings["bump_message"]
-        version_files: list[str] = self.bump_settings["version_files"]
-        major_version_zero: bool = self.bump_settings["major_version_zero"]
-        prerelease_offset: int = self.bump_settings["prerelease_offset"]
-
-        dry_run: bool = self.arguments["dry_run"]
-        is_yes: bool = self.arguments["yes"]
-        increment: Increment | None = self.arguments["increment"]
-        prerelease: Prerelease | None = self.arguments["prerelease"]
-        devrelease: int | None = self.arguments["devrelease"]
-        is_files_only: bool | None = self.arguments["files_only"]
-        is_local_version: bool = self.arguments["local_version"]
+        increment = self.arguments["increment"]
+        prerelease = self.arguments["prerelease"]
+        devrelease = self.arguments["devrelease"]
+        is_local_version = self.arguments["local_version"]
         manual_version = self.arguments["manual_version"]
         build_metadata = self.arguments["build_metadata"]
-        increment_mode: str = self.arguments["increment_mode"]
-        get_next: bool = self.arguments["get_next"]
-        allow_no_commit: bool | None = self.arguments["allow_no_commit"]
+        get_next = self.arguments["get_next"]
+        allow_no_commit = self.arguments["allow_no_commit"]
 
         if manual_version:
             if increment:
@@ -182,7 +200,7 @@ def __call__(self) -> None:
                     "--build-metadata cannot be combined with MANUAL_VERSION"
                 )
 
-            if major_version_zero:
+            if self.bump_settings["major_version_zero"]:
                 raise NotAllowed(
                     "--major-version-zero cannot be combined with MANUAL_VERSION"
                 )
@@ -190,7 +208,7 @@ def __call__(self) -> None:
             if get_next:
                 raise NotAllowed("--get-next cannot be combined with MANUAL_VERSION")
 
-        if major_version_zero:
+        if self.bump_settings["major_version_zero"]:
             if not current_version.release[0] == 0:
                 raise NotAllowed(
                     f"--major-version-zero is meaningless for current version {current_version}"
@@ -215,7 +233,7 @@ def __call__(self) -> None:
         else:
             # If user specified changelog_to_stdout, they probably want the
             # changelog to be generated as well, this is the most intuitive solution
-            self.changelog_flag = (
+            self.changelog_flag = bool(
                 self.changelog_flag
                 or bool(self.changelog_to_stdout)
                 or self.changelog_config
@@ -227,7 +245,7 @@ def __call__(self) -> None:
             current_tag, "name", rules.normalize_tag(current_version)
         )
 
-        is_initial = self._is_initial_tag(current_tag, is_yes)
+        is_initial = self._is_initial_tag(current_tag, self.arguments["yes"])
 
         if manual_version:
             try:
@@ -273,16 +291,16 @@ def __call__(self) -> None:
             new_version = current_version.bump(
                 increment,
                 prerelease=prerelease,
-                prerelease_offset=prerelease_offset,
+                prerelease_offset=self.bump_settings["prerelease_offset"],
                 devrelease=devrelease,
                 is_local_version=is_local_version,
                 build_metadata=build_metadata,
-                exact_increment=increment_mode == "exact",
+                exact_increment=self.arguments["increment_mode"] == "exact",
             )
 
         new_tag_version = rules.normalize_tag(new_version)
         message = bump.create_commit_message(
-            current_version, new_version, bump_commit_message
+            current_version, new_version, self.bump_settings["bump_message"]
         )
 
         if get_next:
@@ -314,6 +332,7 @@ def __call__(self) -> None:
             )
 
         files: list[str] = []
+        dry_run = self.arguments["dry_run"]
         if self.changelog_flag:
             args = {
                 "unreleased_version": new_tag_version,
@@ -342,7 +361,7 @@ def __call__(self) -> None:
             bump.update_version_in_files(
                 str(current_version),
                 str(new_version),
-                version_files,
+                self.bump_settings["version_files"],
                 check_consistency=self.check_consistency,
                 encoding=self.encoding,
             )
@@ -366,7 +385,7 @@ def __call__(self) -> None:
                 else None,
             )
 
-        if is_files_only:
+        if self.arguments["files_only"]:
             raise ExpectedExit()
 
         # FIXME: check if any changes have been staged
@@ -395,11 +414,15 @@ def __call__(self) -> None:
 
         c = git.tag(
             new_tag_version,
-            signed=self.bump_settings.get("gpg_sign", False)
-            or bool(self.config.settings.get("gpg_sign", False)),
-            annotated=self.bump_settings.get("annotated_tag", False)
-            or bool(self.config.settings.get("annotated_tag", False))
-            or bool(self.bump_settings.get("annotated_tag_message", False)),
+            signed=bool(
+                self.bump_settings.get("gpg_sign")
+                or self.config.settings.get("gpg_sign")
+            ),
+            annotated=bool(
+                self.bump_settings.get("annotated_tag")
+                or self.config.settings.get("annotated_tag")
+                or self.bump_settings.get("annotated_tag_message")
+            ),
             msg=self.bump_settings.get("annotated_tag_message", None),
             # TODO: also get from self.config.settings?
         )
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index adbb9e6d1a..4ad7ae4d8f 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -29,36 +29,36 @@ class CzSettings(TypedDict, total=False):
 
 
 class Settings(TypedDict, total=False):
-    name: str
-    version: str | None
-    version_files: list[str]
-    version_provider: str | None
-    version_scheme: str | None
-    version_type: str | None
-    tag_format: str
-    legacy_tag_formats: Sequence[str]
-    ignored_tag_formats: Sequence[str]
-    bump_message: str | None
-    retry_after_failure: bool
     allow_abort: bool
     allowed_prefixes: list[str]
+    always_signoff: bool
+    bump_message: str | None
     changelog_file: str
     changelog_format: str | None
     changelog_incremental: bool
-    changelog_start_rev: str | None
     changelog_merge_prerelease: bool
-    update_changelog_on_bump: bool
-    use_shortcuts: bool
-    style: list[tuple[str, str]]
+    changelog_start_rev: str | None
     customize: CzSettings
+    encoding: str
+    extras: dict[str, Any]
+    ignored_tag_formats: Sequence[str]
+    legacy_tag_formats: Sequence[str]
     major_version_zero: bool
-    pre_bump_hooks: list[str] | None
+    name: str
     post_bump_hooks: list[str] | None
+    pre_bump_hooks: list[str] | None
     prerelease_offset: int
-    encoding: str
-    always_signoff: bool
+    retry_after_failure: bool
+    style: list[tuple[str, str]]
+    tag_format: str
     template: str | None
-    extras: dict[str, Any]
+    update_changelog_on_bump: bool
+    use_shortcuts: bool
+    version_files: list[str]
+    version_provider: str | None
+    version_scheme: str | None
+    version_type: str | None
+    version: str | None
 
 
 CONFIG_FILES: list[str] = [
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index 41da985704..64b810e4de 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -1721,7 +1721,7 @@ def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project):
         "extras": None,
     }
 
-    bump_cmd = bump.Bump(config, arguments)
+    bump_cmd = bump.Bump(config, arguments)  # type: ignore
 
     # Test case 1: No current tag, not yes mode
     mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True))

From 8e88d8cf1766cc78c6975c7663480cc5752b64d5 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 00:57:10 +0800
Subject: [PATCH 136/169] refactor(changelog): type untyped arguments

---
 commitizen/commands/bump.py      |  4 ++--
 commitizen/commands/changelog.py | 38 +++++++++++++++++++++++---------
 commitizen/defaults.py           |  1 +
 3 files changed, 30 insertions(+), 13 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index beed384b78..65827d406e 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -342,14 +342,14 @@ def __call__(self) -> None:
                 "dry_run": dry_run,
             }
             if self.changelog_to_stdout:
-                changelog_cmd = Changelog(self.config, {**args, "dry_run": True})
+                changelog_cmd = Changelog(self.config, {**args, "dry_run": True})  # type: ignore
                 try:
                     changelog_cmd()
                 except DryRunExit:
                     pass
 
             args["file_name"] = self.file_name
-            changelog_cmd = Changelog(self.config, args)
+            changelog_cmd = Changelog(self.config, args)  # type: ignore
             changelog_cmd()
             files.append(changelog_cmd.file_name)
 
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 59d799b037..6d150b03f9 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -2,11 +2,11 @@
 
 import os
 import os.path
-from collections.abc import Generator, Iterable, Mapping
+from collections.abc import Generator, Iterable
 from difflib import SequenceMatcher
 from operator import itemgetter
 from pathlib import Path
-from typing import Any, cast
+from typing import Any, TypedDict, cast
 
 from commitizen import changelog, defaults, factory, git, out
 from commitizen.changelog_formats import get_changelog_format
@@ -26,10 +26,28 @@
 from commitizen.version_schemes import get_version_scheme
 
 
+class ChangelogArgs(TypedDict, total=False):
+    change_type_map: dict[str, str]
+    change_type_order: list[str]
+    current_version: str
+    dry_run: bool
+    file_name: str
+    incremental: bool
+    merge_prerelease: bool
+    rev_range: str
+    start_rev: str
+    tag_format: str
+    unreleased_version: str | None
+    version_scheme: str
+    template: str
+    extras: dict[str, Any]
+    export_template: str
+
+
 class Changelog:
     """Generate a changelog based on the commit history."""
 
-    def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None:
+    def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
@@ -59,17 +77,17 @@ def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None:
 
         self.changelog_format = get_changelog_format(self.config, self.file_name)
 
-        self.incremental = args["incremental"] or self.config.settings.get(
-            "changelog_incremental"
+        self.incremental = bool(
+            args.get("incremental") or self.config.settings.get("changelog_incremental")
         )
-        self.dry_run = args["dry_run"]
+        self.dry_run = bool(args.get("dry_run"))
 
         self.scheme = get_version_scheme(
             self.config.settings, args.get("version_scheme")
         )
 
         current_version = (
-            args.get("current_version", config.settings.get("version")) or ""
+            args.get("current_version") or self.config.settings.get("version") or ""
         )
         self.current_version = self.scheme(current_version) if current_version else None
 
@@ -84,9 +102,7 @@ def __init__(self, config: BaseConfig, args: Mapping[str, Any]) -> None:
             or defaults.CHANGE_TYPE_ORDER,
         )
         self.rev_range = args.get("rev_range")
-        self.tag_format: str = (
-            args.get("tag_format") or self.config.settings["tag_format"]
-        )
+        self.tag_format = args.get("tag_format") or self.config.settings["tag_format"]
         self.tag_rules = TagRules(
             scheme=self.scheme,
             tag_format=self.tag_format,
@@ -164,7 +180,7 @@ def __call__(self) -> None:
         start_rev = self.start_rev
         unreleased_version = self.unreleased_version
         changelog_meta = changelog.Metadata()
-        change_type_map: dict[str, str] | None = self.change_type_map  # type: ignore
+        change_type_map: dict[str, str] | None = self.change_type_map
         changelog_message_builder_hook: MessageBuilderHook | None = (
             self.cz.changelog_message_builder_hook
         )
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 4ad7ae4d8f..29c9f2a494 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -37,6 +37,7 @@ class Settings(TypedDict, total=False):
     changelog_format: str | None
     changelog_incremental: bool
     changelog_merge_prerelease: bool
+    change_type_map: dict[str, str]
     changelog_start_rev: str | None
     customize: CzSettings
     encoding: str

From 49bb8547a3366a22ddd10973fad5623a5662c0a9 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 00:59:21 +0800
Subject: [PATCH 137/169] refactor(check): remove unused argument

---
 commitizen/commands/check.py | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index 82a8039fa3..a293b6f855 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -1,6 +1,5 @@
 from __future__ import annotations
 
-import os
 import re
 import sys
 from typing import Any
@@ -17,9 +16,7 @@
 class Check:
     """Check if the current commit msg matches the commitizen format."""
 
-    def __init__(
-        self, config: BaseConfig, arguments: dict[str, Any], cwd: str = os.getcwd()
-    ) -> None:
+    def __init__(self, config: BaseConfig, arguments: dict[str, Any]) -> None:
         """Initial check command.
 
         Args:

From 296b1b6281ce843d99d9f9c76c51675683c086c4 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:01:08 +0800
Subject: [PATCH 138/169] style(bump): rename class for consistency

---
 commitizen/commands/bump.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 65827d406e..4bad83f5d6 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -37,7 +37,7 @@
 logger = getLogger("commitizen")
 
 
-class BumpArguments(Settings, total=False):
+class BumpArgs(Settings, total=False):
     allow_no_commit: bool | None
     annotated_tag_message: str | None
     annotated_tag: bool
@@ -65,7 +65,7 @@ class BumpArguments(Settings, total=False):
 class Bump:
     """Show prompt for the user to create a guided commit."""
 
-    def __init__(self, config: BaseConfig, arguments: BumpArguments) -> None:
+    def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
@@ -73,7 +73,7 @@ def __init__(self, config: BaseConfig, arguments: BumpArguments) -> None:
         self.encoding = config.settings["encoding"]
         self.arguments = arguments
         self.bump_settings = cast(
-            BumpArguments,
+            BumpArgs,
             {
                 **config.settings,
                 **{

From 1ded688bf13fd13ab465b4236290ac0ed8aaea8b Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:08:03 +0800
Subject: [PATCH 139/169] refactor(check): type CheckArgs arguments

---
 commitizen/commands/check.py | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index a293b6f855..ba0beeeb4e 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -2,7 +2,7 @@
 
 import re
 import sys
-from typing import Any
+from typing import TypedDict
 
 from commitizen import factory, git, out
 from commitizen.config import BaseConfig
@@ -13,10 +13,20 @@
 )
 
 
+class CheckArgs(TypedDict, total=False):
+    commit_msg_file: str
+    commit_msg: str
+    rev_range: str
+    allow_abort: bool
+    message_length_limit: int
+    allowed_prefixes: list[str]
+    message: str
+
+
 class Check:
     """Check if the current commit msg matches the commitizen format."""
 
-    def __init__(self, config: BaseConfig, arguments: dict[str, Any]) -> None:
+    def __init__(self, config: BaseConfig, arguments: CheckArgs) -> None:
         """Initial check command.
 
         Args:
@@ -24,16 +34,15 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any]) -> None:
             arguments: All the flags provided by the user
             cwd: Current work directory
         """
-        self.commit_msg_file: str | None = arguments.get("commit_msg_file")
-        self.commit_msg: str | None = arguments.get("message")
-        self.rev_range: str | None = arguments.get("rev_range")
-        self.allow_abort: bool = bool(
+        self.commit_msg_file = arguments.get("commit_msg_file")
+        self.commit_msg = arguments.get("message")
+        self.rev_range = arguments.get("rev_range")
+        self.allow_abort = bool(
             arguments.get("allow_abort", config.settings["allow_abort"])
         )
-        self.max_msg_length: int = arguments.get("message_length_limit", 0)
+        self.max_msg_length = arguments.get("message_length_limit", 0)
 
         # we need to distinguish between None and [], which is a valid value
-
         allowed_prefixes = arguments.get("allowed_prefixes")
         self.allowed_prefixes: list[str] = (
             allowed_prefixes

From e32778afb257706c7bea839ec784d5fc5b9020b3 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:16:52 +0800
Subject: [PATCH 140/169] refactor(commit): type commit args

---
 commitizen/commands/commit.py | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index 68095d0e0c..43feb272ba 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -6,7 +6,7 @@
 import subprocess
 import tempfile
 from pathlib import Path
-from typing import Union, cast
+from typing import TypedDict
 
 import questionary
 
@@ -28,10 +28,22 @@
 from commitizen.git import smart_open
 
 
+class CommitArgs(TypedDict, total=False):
+    all: bool
+    dry_run: bool
+    edit: bool
+    extra_cli_args: str
+    message_length_limit: int
+    no_retry: bool
+    signoff: bool
+    write_message_to_file: Path | None
+    retry: bool
+
+
 class Commit:
     """Show prompt for the user to create a guided commit."""
 
-    def __init__(self, config: BaseConfig, arguments: dict) -> None:
+    def __init__(self, config: BaseConfig, arguments: CommitArgs) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
@@ -69,7 +81,7 @@ def _prompt_commit_questions(self) -> str:
 
         message = cz.message(answers)
         message_len = len(message.partition("\n")[0].strip())
-        message_length_limit: int = self.arguments.get("message_length_limit", 0)
+        message_length_limit = self.arguments.get("message_length_limit", 0)
         if 0 < message_length_limit < message_len:
             raise CommitMessageLengthExceededError(
                 f"Length of commit message exceeds limit ({message_len}/{message_length_limit})"
@@ -108,12 +120,10 @@ def _get_message(self) -> str:
         return self._prompt_commit_questions()
 
     def __call__(self) -> None:
-        extra_args = cast(str, self.arguments.get("extra_cli_args", ""))
-        dry_run = cast(bool, self.arguments.get("dry_run"))
-        write_message_to_file = cast(
-            Union[Path, None], self.arguments.get("write_message_to_file")
-        )
-        signoff = cast(bool, self.arguments.get("signoff"))
+        extra_args = self.arguments.get("extra_cli_args", "")
+        dry_run = bool(self.arguments.get("dry_run"))
+        write_message_to_file = self.arguments.get("write_message_to_file")
+        signoff = bool(self.arguments.get("signoff"))
 
         if self.arguments.get("all"):
             git.add("-u")

From c60a3b877336c119cbece2e923a1cacfc4dcb974 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:30:59 +0800
Subject: [PATCH 141/169] refactor(commands): remove unused args, type version
 command args

---
 commitizen/commands/version.py         | 13 +++++++++----
 tests/commands/test_version_command.py |  1 -
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py
index ecf1f03129..6b0aa331ad 100644
--- a/commitizen/commands/version.py
+++ b/commitizen/commands/version.py
@@ -1,7 +1,6 @@
 import platform
 import sys
-from collections.abc import Mapping
-from typing import Any
+from typing import TypedDict
 
 from commitizen import out
 from commitizen.__version__ import __version__
@@ -9,12 +8,18 @@
 from commitizen.providers import get_provider
 
 
+class VersionArgs(TypedDict, total=False):
+    report: bool
+    project: bool
+    verbose: bool
+
+
 class Version:
     """Get the version of the installed commitizen or the current project."""
 
-    def __init__(self, config: BaseConfig, *args: Mapping[str, Any]) -> None:
+    def __init__(self, config: BaseConfig, arguments: VersionArgs) -> None:
         self.config: BaseConfig = config
-        self.parameter = args[0]
+        self.parameter = arguments
         self.operating_system = platform.system()
         self.python_version = sys.version
 
diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py
index 927cf55f25..3dcbed168b 100644
--- a/tests/commands/test_version_command.py
+++ b/tests/commands/test_version_command.py
@@ -97,7 +97,6 @@ def test_version_use_version_provider(
         {
             "report": False,
             "project": project,
-            "commitizen": False,
             "verbose": not project,
         },
     )()

From 289198e938d28348d21dc4b52d0d93ab3e20ba40 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:38:22 +0800
Subject: [PATCH 142/169] style(cli): shorten arg type

---
 commitizen/cli.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index e1bd57b523..b5c03527d3 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -3,7 +3,6 @@
 import argparse
 import logging
 import sys
-from collections.abc import Sequence
 from copy import deepcopy
 from functools import partial
 from pathlib import Path
@@ -48,7 +47,7 @@ def __call__(
         self,
         parser: argparse.ArgumentParser,
         namespace: argparse.Namespace,
-        kwarg: str | Sequence[object] | None,
+        kwarg: object,
         option_string: str | None = None,
     ) -> None:
         if not isinstance(kwarg, str):

From e9758e1dad46f514901851d68d624c3d73e50523 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:41:39 +0800
Subject: [PATCH 143/169] docs(bump): comment on a stupid looking pattern

---
 commitizen/commands/bump.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 4bad83f5d6..fa49d37d5e 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -79,6 +79,7 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None:
                 **{
                     k: v
                     for k, v in {
+                        # All these are for making mypy happy
                         "annotated_tag_message": arguments.get("annotated_tag_message"),
                         "annotated_tag": arguments.get("annotated_tag"),
                         "bump_message": arguments.get("bump_message"),

From b04ebdf70b2c9e04f69a9b36f893db12532c231d Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:50:37 +0800
Subject: [PATCH 144/169] fix(Check): make parameters backward compatiable

---
 commitizen/commands/check.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index ba0beeeb4e..8a7d0dd019 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -26,7 +26,7 @@ class CheckArgs(TypedDict, total=False):
 class Check:
     """Check if the current commit msg matches the commitizen format."""
 
-    def __init__(self, config: BaseConfig, arguments: CheckArgs) -> None:
+    def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> None:
         """Initial check command.
 
         Args:

From b9a181cc70ab60c736a91cbf042a12cc9f12b30e Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:52:12 +0800
Subject: [PATCH 145/169] style(changelog): rename parameter for consistency

---
 commitizen/commands/changelog.py | 33 ++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 6d150b03f9..f0dd1c516c 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -47,13 +47,13 @@ class ChangelogArgs(TypedDict, total=False):
 class Changelog:
     """Generate a changelog based on the commit history."""
 
-    def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None:
+    def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
         self.config = config
 
-        changelog_file_name = args.get("file_name") or self.config.settings.get(
+        changelog_file_name = arguments.get("file_name") or self.config.settings.get(
             "changelog_file"
         )
         if not isinstance(changelog_file_name, str):
@@ -71,27 +71,30 @@ def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None:
         self.encoding = self.config.settings["encoding"]
         self.cz = factory.committer_factory(self.config)
 
-        self.start_rev = args.get("start_rev") or self.config.settings.get(
+        self.start_rev = arguments.get("start_rev") or self.config.settings.get(
             "changelog_start_rev"
         )
 
         self.changelog_format = get_changelog_format(self.config, self.file_name)
 
         self.incremental = bool(
-            args.get("incremental") or self.config.settings.get("changelog_incremental")
+            arguments.get("incremental")
+            or self.config.settings.get("changelog_incremental")
         )
-        self.dry_run = bool(args.get("dry_run"))
+        self.dry_run = bool(arguments.get("dry_run"))
 
         self.scheme = get_version_scheme(
-            self.config.settings, args.get("version_scheme")
+            self.config.settings, arguments.get("version_scheme")
         )
 
         current_version = (
-            args.get("current_version") or self.config.settings.get("version") or ""
+            arguments.get("current_version")
+            or self.config.settings.get("version")
+            or ""
         )
         self.current_version = self.scheme(current_version) if current_version else None
 
-        self.unreleased_version = args["unreleased_version"]
+        self.unreleased_version = arguments["unreleased_version"]
         self.change_type_map = (
             self.config.settings.get("change_type_map") or self.cz.change_type_map
         )
@@ -101,24 +104,26 @@ def __init__(self, config: BaseConfig, args: ChangelogArgs) -> None:
             or self.cz.change_type_order
             or defaults.CHANGE_TYPE_ORDER,
         )
-        self.rev_range = args.get("rev_range")
-        self.tag_format = args.get("tag_format") or self.config.settings["tag_format"]
+        self.rev_range = arguments.get("rev_range")
+        self.tag_format = (
+            arguments.get("tag_format") or self.config.settings["tag_format"]
+        )
         self.tag_rules = TagRules(
             scheme=self.scheme,
             tag_format=self.tag_format,
             legacy_tag_formats=self.config.settings["legacy_tag_formats"],
             ignored_tag_formats=self.config.settings["ignored_tag_formats"],
-            merge_prereleases=args.get("merge_prerelease")
+            merge_prereleases=arguments.get("merge_prerelease")
             or self.config.settings["changelog_merge_prerelease"],
         )
 
         self.template = (
-            args.get("template")
+            arguments.get("template")
             or self.config.settings.get("template")
             or self.changelog_format.template
         )
-        self.extras = args.get("extras") or {}
-        self.export_template_to = args.get("export_template")
+        self.extras = arguments.get("extras") or {}
+        self.export_template_to = arguments.get("export_template")
 
     def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str:
         """Try to find the 'start_rev'.

From 15a090069d0324f52a4ecf9b29719310ca942848 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 01:55:38 +0800
Subject: [PATCH 146/169] refactor(bump): improve readability and still bypass
 mypy check

---
 commitizen/commands/bump.py | 31 +++++++++++++++----------------
 1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index fa49d37d5e..5edf59e9e8 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -78,22 +78,21 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None:
                 **config.settings,
                 **{
                     k: v
-                    for k, v in {
-                        # All these are for making mypy happy
-                        "annotated_tag_message": arguments.get("annotated_tag_message"),
-                        "annotated_tag": arguments.get("annotated_tag"),
-                        "bump_message": arguments.get("bump_message"),
-                        "file_name": arguments.get("file_name"),
-                        "gpg_sign": arguments.get("gpg_sign"),
-                        "increment_mode": arguments.get("increment_mode"),
-                        "increment": arguments.get("increment"),
-                        "major_version_zero": arguments.get("major_version_zero"),
-                        "prerelease_offset": arguments.get("prerelease_offset"),
-                        "prerelease": arguments.get("prerelease"),
-                        "tag_format": arguments.get("tag_format"),
-                        "template": arguments.get("template"),
-                    }.items()
-                    if v is not None
+                    for k in (
+                        "annotated_tag_message",
+                        "annotated_tag",
+                        "bump_message",
+                        "file_name",
+                        "gpg_sign",
+                        "increment_mode",
+                        "increment",
+                        "major_version_zero",
+                        "prerelease_offset",
+                        "prerelease",
+                        "tag_format",
+                        "template",
+                    )
+                    if (v := arguments.get(k)) is not None
                 },
             },
         )

From 48f06e313724438794a3027262aa921deb1715f3 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 1 Jun 2025 02:05:23 +0800
Subject: [PATCH 147/169] refactor: remove unnecessary bool() and remove Any
 type from TypedDict get

---
 commitizen/commands/bump.py | 8 ++------
 commitizen/defaults.py      | 4 +++-
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 5edf59e9e8..9f8bc08244 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -40,7 +40,6 @@
 class BumpArgs(Settings, total=False):
     allow_no_commit: bool | None
     annotated_tag_message: str | None
-    annotated_tag: bool
     build_metadata: str | None
     changelog_to_stdout: bool
     changelog: bool
@@ -51,7 +50,6 @@ class BumpArgs(Settings, total=False):
     files_only: bool | None
     get_next: bool
     git_output_to_stderr: bool
-    gpg_sign: bool
     increment_mode: str
     increment: Increment | None
     local_version: bool
@@ -222,7 +220,7 @@ def __call__(self) -> None:
 
         if get_next:
             # if trying to use --get-next, we should not allow --changelog or --changelog-to-stdout
-            if self.changelog_flag or bool(self.changelog_to_stdout):
+            if self.changelog_flag or self.changelog_to_stdout:
                 raise NotAllowed(
                     "--changelog or --changelog-to-stdout is not allowed with --get-next"
                 )
@@ -234,9 +232,7 @@ def __call__(self) -> None:
             # If user specified changelog_to_stdout, they probably want the
             # changelog to be generated as well, this is the most intuitive solution
             self.changelog_flag = bool(
-                self.changelog_flag
-                or bool(self.changelog_to_stdout)
-                or self.changelog_config
+                self.changelog_flag or self.changelog_to_stdout or self.changelog_config
             )
 
         rules = TagRules.from_settings(cast(Settings, self.bump_settings))
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 29c9f2a494..7a51389b7a 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -32,16 +32,18 @@ class Settings(TypedDict, total=False):
     allow_abort: bool
     allowed_prefixes: list[str]
     always_signoff: bool
+    annotated_tag: bool
     bump_message: str | None
+    change_type_map: dict[str, str]
     changelog_file: str
     changelog_format: str | None
     changelog_incremental: bool
     changelog_merge_prerelease: bool
-    change_type_map: dict[str, str]
     changelog_start_rev: str | None
     customize: CzSettings
     encoding: str
     extras: dict[str, Any]
+    gpg_sign: bool
     ignored_tag_formats: Sequence[str]
     legacy_tag_formats: Sequence[str]
     major_version_zero: bool

From 8e9d40fbc8842651674257b7bb3c42969ae53418 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 4 Jun 2025 23:24:59 +0800
Subject: [PATCH 148/169] style(changelog): add TODO to fixable type ignores

---
 commitizen/commands/changelog.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index f0dd1c516c..8ca36bdb9a 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -176,6 +176,7 @@ def _write_changelog(
 
     def _export_template(self) -> None:
         tpl = changelog.get_changelog_template(self.cz.template_loader, self.template)
+        # TODO: fix the following type ignores
         src = Path(tpl.filename)  # type: ignore
         Path(self.export_template_to).write_text(src.read_text())  # type: ignore
 

From c49a55c27bc09ba5f18ff2fe056d387233e52018 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 4 Jun 2025 23:26:37 +0800
Subject: [PATCH 149/169] style(cli): more specific type ignore

---
 commitizen/cli.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index b5c03527d3..c13f713a27 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -679,7 +679,7 @@ def main() -> None:
         )
         sys.excepthook = no_raise_debug_excepthook
 
-    args.func(conf, arguments)()  # type: ignore
+    args.func(conf, arguments)()  # type: ignore[arg-type]
 
 
 if __name__ == "__main__":

From 150b35dc8e2aad836077887a0086a5c5f5f4fbe8 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Wed, 4 Jun 2025 23:35:19 +0800
Subject: [PATCH 150/169] style(cli): rename kwarg to values

---
 commitizen/cli.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index c13f713a27..12db01711c 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -47,17 +47,17 @@ def __call__(
         self,
         parser: argparse.ArgumentParser,
         namespace: argparse.Namespace,
-        kwarg: object,
+        values: object,
         option_string: str | None = None,
     ) -> None:
-        if not isinstance(kwarg, str):
+        if not isinstance(values, str):
             return
-        if "=" not in kwarg:
+        if "=" not in values:
             raise InvalidCommandArgumentError(
                 f"Option {option_string} expect a key=value format"
             )
         kwargs = getattr(namespace, self.dest, None) or {}
-        key, value = kwarg.split("=", 1)
+        key, value = values.split("=", 1)
         if not key:
             raise InvalidCommandArgumentError(
                 f"Option {option_string} expect a key=value format"

From 773575598eac2ab520e698935ded1896f14da137 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 6 Jun 2025 11:24:46 +0800
Subject: [PATCH 151/169] refactor(bump): use any to replace 'or' chain

---
 commitizen/commands/bump.py | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 9f8bc08244..a4024eee35 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -231,8 +231,8 @@ def __call__(self) -> None:
         else:
             # If user specified changelog_to_stdout, they probably want the
             # changelog to be generated as well, this is the most intuitive solution
-            self.changelog_flag = bool(
-                self.changelog_flag or self.changelog_to_stdout or self.changelog_config
+            self.changelog_flag = any(
+                (self.changelog_flag, self.changelog_to_stdout, self.changelog_config)
             )
 
         rules = TagRules.from_settings(cast(Settings, self.bump_settings))
@@ -410,14 +410,18 @@ def __call__(self) -> None:
 
         c = git.tag(
             new_tag_version,
-            signed=bool(
-                self.bump_settings.get("gpg_sign")
-                or self.config.settings.get("gpg_sign")
+            signed=any(
+                (
+                    self.bump_settings.get("gpg_sign"),
+                    self.config.settings.get("gpg_sign"),
+                )
             ),
-            annotated=bool(
-                self.bump_settings.get("annotated_tag")
-                or self.config.settings.get("annotated_tag")
-                or self.bump_settings.get("annotated_tag_message")
+            annotated=any(
+                (
+                    self.bump_settings.get("annotated_tag"),
+                    self.config.settings.get("annotated_tag"),
+                    self.bump_settings.get("annotated_tag_message"),
+                )
             ),
             msg=self.bump_settings.get("annotated_tag_message", None),
             # TODO: also get from self.config.settings?

From 2966cf938b17a37537949e4ee2bbee99919f8999 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 6 Jun 2025 00:46:21 +0800
Subject: [PATCH 152/169] perf(bump): avoid unnecessary list construction and
 rename variable to avoid confusion

---
 commitizen/bump.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/commitizen/bump.py b/commitizen/bump.py
index 2fa814f7c8..6d6b6dc069 100644
--- a/commitizen/bump.py
+++ b/commitizen/bump.py
@@ -3,6 +3,7 @@
 import os
 import re
 from collections import OrderedDict
+from collections.abc import Iterable
 from glob import iglob
 from logging import getLogger
 from string import Template
@@ -61,7 +62,7 @@ def find_increment(
 def update_version_in_files(
     current_version: str,
     new_version: str,
-    files: list[str],
+    files: Iterable[str],
     *,
     check_consistency: bool = False,
     encoding: str = ENCODING,
@@ -99,11 +100,11 @@ def update_version_in_files(
     return updated
 
 
-def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str]]:
+def _files_and_regexes(patterns: Iterable[str], version: str) -> list[tuple[str, str]]:
     """
     Resolve all distinct files with their regexp from a list of glob patterns with optional regexp
     """
-    out: list[tuple[str, str]] = []
+    out: set[tuple[str, str]] = set()
     for pattern in patterns:
         drive, tail = os.path.splitdrive(pattern)
         path, _, regex = tail.partition(":")
@@ -111,9 +112,10 @@ def _files_and_regexes(patterns: list[str], version: str) -> list[tuple[str, str
         if not regex:
             regex = re.escape(version)
 
-        for path in iglob(filepath):
-            out.append((path, regex))
-    return sorted(list(set(out)))
+        for file in iglob(filepath):
+            out.add((file, regex))
+
+    return sorted(out)
 
 
 def _bump_with_regex(

From 303d2ebc356f8753a1d7ebbffd68d51d924971a9 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 7 Jun 2025 23:29:10 +0800
Subject: [PATCH 153/169] refactor(bump): eliminate similar patterns in code

---
 commitizen/commands/bump.py | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index a4024eee35..7fe01393a2 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -397,16 +397,10 @@ def __call__(self) -> None:
             err = c.err.strip() or c.out
             raise BumpCommitFailedError(f'2nd git.commit error: "{err}"')
 
-        if c.out:
-            if self.git_output_to_stderr:
-                out.diagnostic(c.out)
-            else:
-                out.write(c.out)
-        if c.err:
-            if self.git_output_to_stderr:
-                out.diagnostic(c.err)
-            else:
-                out.write(c.err)
+        for msg in (c.out, c.err):
+            if msg:
+                out_func = out.diagnostic if self.git_output_to_stderr else out.write
+                out_func(msg)
 
         c = git.tag(
             new_tag_version,

From af50d8572b611667690110bd540b10afafd4684a Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 6 Jun 2025 02:03:32 +0800
Subject: [PATCH 154/169] refactor(git): simplify tag logic

---
 commitizen/git.py | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/commitizen/git.py b/commitizen/git.py
index fb59750eaf..0f5f820eb2 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -160,16 +160,14 @@ def from_line(cls, line: str, inner_delimiter: str) -> GitTag:
 def tag(
     tag: str, annotated: bool = False, signed: bool = False, msg: str | None = None
 ) -> cmd.Command:
-    _opt = ""
-    if annotated:
-        _opt = f"-a {tag} -m"
-    if signed:
-        _opt = f"-s {tag} -m"
+    if not annotated and not signed:
+        return cmd.run(f"git tag {tag}")
 
     # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging,
     # we're not able to create lightweight tag with message.
     # by adding message, we make it a annotated tags
-    return cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"')
+    option = "-s" if signed else "-a"  # The else case is for annotated tags
+    return cmd.run(f'git tag {option} {tag} -m "{msg or tag}"')
 
 
 def add(*args: str) -> cmd.Command:

From 654f4a03b34c094b5b718f3b2a66f0905689c15c Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 6 Jun 2025 01:50:14 +0800
Subject: [PATCH 155/169] refactor(git): retype get_commits parameter to make
 it more friendly to call sites

---
 commitizen/commands/bump.py  | 5 +----
 commitizen/commands/check.py | 2 +-
 commitizen/git.py            | 7 +++++--
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 7fe01393a2..ec4cb0b0b6 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -253,10 +253,7 @@ def __call__(self) -> None:
                 ) from exc
         else:
             if increment is None:
-                if current_tag:
-                    commits = git.get_commits(current_tag.name)
-                else:
-                    commits = git.get_commits()
+                commits = git.get_commits(current_tag.name if current_tag else None)
 
                 # No commits, there is no need to create an empty tag.
                 # Unless we previously had a prerelease.
diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index 8a7d0dd019..ba003cf3cc 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -110,7 +110,7 @@ def _get_commits(self) -> list[git.GitCommit]:
             return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))]
 
         # Get commit messages from git log (--rev-range)
-        return git.get_commits(end=self.rev_range or "HEAD")
+        return git.get_commits(end=self.rev_range)
 
     @staticmethod
     def _filter_comments(msg: str) -> str:
diff --git a/commitizen/git.py b/commitizen/git.py
index 0f5f820eb2..8025041abb 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -201,15 +201,18 @@ def _create_commit_cmd_string(args: str, committer_date: str | None, name: str)
 
 def get_commits(
     start: str | None = None,
-    end: str = "HEAD",
+    end: str | None = None,
     *,
     args: str = "",
 ) -> list[GitCommit]:
     """Get the commits between start and end."""
+    if end is None:
+        end = "HEAD"
     git_log_entries = _get_log_as_str_list(start, end, args)
     return [
         GitCommit.from_rev_and_commit(rev_and_commit)
-        for rev_and_commit in filter(None, git_log_entries)
+        for rev_and_commit in git_log_entries
+        if rev_and_commit
     ]
 
 

From 6abbc530b02e3d6d5c3def020b18fd771ed111c3 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 6 Jun 2025 01:33:04 +0800
Subject: [PATCH 156/169] refactor(bump): simplify nested if

---
 commitizen/commands/bump.py | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index ec4cb0b0b6..ba252fcf21 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -206,17 +206,13 @@ def __call__(self) -> None:
             if get_next:
                 raise NotAllowed("--get-next cannot be combined with MANUAL_VERSION")
 
-        if self.bump_settings["major_version_zero"]:
-            if not current_version.release[0] == 0:
-                raise NotAllowed(
-                    f"--major-version-zero is meaningless for current version {current_version}"
-                )
+        if self.bump_settings["major_version_zero"] and current_version.release[0]:
+            raise NotAllowed(
+                f"--major-version-zero is meaningless for current version {current_version}"
+            )
 
-        if build_metadata:
-            if is_local_version:
-                raise NotAllowed(
-                    "--local-version cannot be combined with --build-metadata"
-                )
+        if build_metadata and is_local_version:
+            raise NotAllowed("--local-version cannot be combined with --build-metadata")
 
         if get_next:
             # if trying to use --get-next, we should not allow --changelog or --changelog-to-stdout

From f4fe43c363bf7d37e02e8fb633e197e3eab85f1e Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 2 Jun 2025 00:11:57 +0800
Subject: [PATCH 157/169] fix(commit): emit deprecated warning of cz commit -s

---
 commitizen/commands/commit.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index 43feb272ba..bb4c2fdc94 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -125,6 +125,11 @@ def __call__(self) -> None:
         write_message_to_file = self.arguments.get("write_message_to_file")
         signoff = bool(self.arguments.get("signoff"))
 
+        if signoff:
+            out.warn(
+                "Deprecated warning: `cz commit -s` is deprecated and will be removed in v5, please use `cz commit -- -s` instead."
+            )
+
         if self.arguments.get("all"):
             git.add("-u")
 
@@ -147,11 +152,6 @@ def __call__(self) -> None:
         if dry_run:
             raise DryRunExit()
 
-        if signoff:
-            out.warn(
-                "signoff mechanic is deprecated, please use `cz commit -- -s` instead."
-            )
-
         if self.config.settings["always_signoff"] or signoff:
             extra_args = f"{extra_args} -s".strip()
 

From a9c4e0a87eb3004cb02181d536fcb9d117aff2d6 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 2 Jun 2025 00:26:32 +0800
Subject: [PATCH 158/169] fix(cli): update description for deprecate warning

Closes #1135
---
 commitizen/cli.py                                             | 4 ++--
 ...st_bump_command_shows_description_when_use_help_option.txt | 2 +-
 ..._commit_command_shows_description_when_use_help_option.txt | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/commitizen/cli.py b/commitizen/cli.py
index 12db01711c..6f7556df49 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -147,7 +147,7 @@ def __call__(
                     {
                         "name": ["-s", "--signoff"],
                         "action": "store_true",
-                        "help": "sign off the commit",
+                        "help": "Deprecated, use 'cz commit -- -s' instead",
                     },
                     {
                         "name": ["-a", "--all"],
@@ -347,7 +347,7 @@ def __call__(
                     },
                     {
                         "name": ["--version-type"],
-                        "help": "Deprecated, use --version-scheme",
+                        "help": "Deprecated, use --version-scheme instead",
                         "default": None,
                         "choices": version_schemes.KNOWN_SCHEMES,
                     },
diff --git a/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt
index 5d4438875d..4cf8e6c91b 100644
--- a/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt
+++ b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt
@@ -74,7 +74,7 @@ options:
   --version-scheme {pep440,semver,semver2}
                         choose version scheme
   --version-type {pep440,semver,semver2}
-                        Deprecated, use --version-scheme
+                        Deprecated, use --version-scheme instead
   --build-metadata BUILD_METADATA
                         Add additional build-metadata to the version-number
   --get-next            Determine the next version and write to stdout
diff --git a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt
index dd1f53f3da..ba531042aa 100644
--- a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt
+++ b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt
@@ -12,7 +12,7 @@ options:
   --write-message-to-file FILE_PATH
                         write message to file before committing (can be
                         combined with --dry-run)
-  -s, --signoff         sign off the commit
+  -s, --signoff         Deprecated, use 'cz commit -- -s' instead
   -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.

From fa5f3c8062bfde36fa3bdb79d9e06c5a3dfe7dd6 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sat, 31 May 2025 23:30:27 +0800
Subject: [PATCH 159/169] refactor(questions): type questions with TypedDict

---
 commitizen/commands/commit.py                 |  2 +-
 commitizen/cz/base.py                         |  4 +--
 .../conventional_commits.py                   |  6 ++--
 commitizen/cz/customize/customize.py          |  7 ++--
 commitizen/cz/jira/jira.py                    |  4 +--
 commitizen/defaults.py                        |  6 ++--
 commitizen/question.py                        | 32 +++++++++++++++++++
 tests/conftest.py                             |  3 +-
 8 files changed, 50 insertions(+), 14 deletions(-)
 create mode 100644 commitizen/question.py

diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index bb4c2fdc94..19bb72fb00 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -66,7 +66,7 @@ def _prompt_commit_questions(self) -> str:
         # Prompt user for the commit message
         cz = self.cz
         questions = cz.questions()
-        for question in filter(lambda q: q["type"] == "list", questions):
+        for question in (q for q in questions if q["type"] == "list"):
             question["use_shortcuts"] = self.config.settings["use_shortcuts"]
         try:
             answers = questionary.prompt(questions, style=cz.style)
diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py
index 9e803c3d01..a30540cae6 100644
--- a/commitizen/cz/base.py
+++ b/commitizen/cz/base.py
@@ -9,7 +9,7 @@
 
 from commitizen import git
 from commitizen.config.base_config import BaseConfig
-from commitizen.defaults import Questions
+from commitizen.question import CzQuestion
 
 
 class MessageBuilderHook(Protocol):
@@ -68,7 +68,7 @@ def __init__(self, config: BaseConfig) -> None:
             self.config.settings.update({"style": BaseCommitizen.default_style_config})
 
     @abstractmethod
-    def questions(self) -> Questions:
+    def questions(self) -> Iterable[CzQuestion]:
         """Questions regarding the commit message."""
 
     @abstractmethod
diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index 912770a726..ca4809a90e 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -3,7 +3,7 @@
 from commitizen import defaults
 from commitizen.cz.base import BaseCommitizen
 from commitizen.cz.utils import multiple_line_breaker, required_validator
-from commitizen.defaults import Questions
+from commitizen.question import CzQuestion
 
 __all__ = ["ConventionalCommitsCz"]
 
@@ -29,7 +29,7 @@ class ConventionalCommitsCz(BaseCommitizen):
     }
     changelog_pattern = defaults.BUMP_PATTERN
 
-    def questions(self) -> Questions:
+    def questions(self) -> list[CzQuestion]:
         return [
             {
                 "type": "list",
@@ -122,8 +122,8 @@ def questions(self) -> Questions:
             },
             {
                 "type": "confirm",
-                "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer",
                 "name": "is_breaking_change",
+                "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer",
                 "default": False,
             },
             {
diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py
index 8f844501ec..6581ad444e 100644
--- a/commitizen/cz/customize/customize.py
+++ b/commitizen/cz/customize/customize.py
@@ -2,6 +2,8 @@
 
 from typing import TYPE_CHECKING
 
+from commitizen.question import CzQuestion
+
 if TYPE_CHECKING:
     from jinja2 import Template
 else:
@@ -14,7 +16,6 @@
 from commitizen import defaults
 from commitizen.config import BaseConfig
 from commitizen.cz.base import BaseCommitizen
-from commitizen.defaults import Questions
 from commitizen.exceptions import MissingCzCustomizeConfigError
 
 __all__ = ["CustomizeCommitsCz"]
@@ -45,8 +46,8 @@ def __init__(self, config: BaseConfig) -> None:
             if value := self.custom_settings.get(attr_name):
                 setattr(self, attr_name, value)
 
-    def questions(self) -> Questions:
-        return self.custom_settings.get("questions", [{}])
+    def questions(self) -> list[CzQuestion]:
+        return self.custom_settings.get("questions", [{}])  # type: ignore
 
     def message(self, answers: dict) -> str:
         message_template = Template(self.custom_settings.get("message_template", ""))
diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py
index 05e23e1690..eafded615e 100644
--- a/commitizen/cz/jira/jira.py
+++ b/commitizen/cz/jira/jira.py
@@ -1,13 +1,13 @@
 import os
 
 from commitizen.cz.base import BaseCommitizen
-from commitizen.defaults import Questions
+from commitizen.question import CzQuestion
 
 __all__ = ["JiraSmartCz"]
 
 
 class JiraSmartCz(BaseCommitizen):
-    def questions(self) -> Questions:
+    def questions(self) -> list[CzQuestion]:
         return [
             {
                 "type": "input",
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 7a51389b7a..a49d6d9428 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -6,8 +6,10 @@
 from collections.abc import Iterable, MutableMapping, Sequence
 from typing import Any, TypedDict
 
+from commitizen.question import CzQuestion
+
 # Type
-Questions = Iterable[MutableMapping[str, Any]]
+Questions = Iterable[MutableMapping[str, Any]]  # TODO: deprecate this?
 
 
 class CzSettings(TypedDict, total=False):
@@ -16,7 +18,7 @@ class CzSettings(TypedDict, total=False):
     bump_map_major_version_zero: OrderedDict[str, str]
     change_type_order: list[str]
 
-    questions: Questions
+    questions: Iterable[CzQuestion]
     example: str | None
     schema_pattern: str | None
     schema: str | None
diff --git a/commitizen/question.py b/commitizen/question.py
new file mode 100644
index 0000000000..393e386092
--- /dev/null
+++ b/commitizen/question.py
@@ -0,0 +1,32 @@
+from typing import Callable, Literal, TypedDict, Union
+
+
+class Choice(TypedDict, total=False):
+    value: str
+    name: str
+    key: str
+
+
+class ListQuestion(TypedDict, total=False):
+    type: Literal["list"]
+    name: str
+    message: str
+    choices: list[Choice]
+    use_shortcuts: bool
+
+
+class InputQuestion(TypedDict, total=False):
+    type: Literal["input"]
+    name: str
+    message: str
+    filter: Callable[[str], str]
+
+
+class ConfirmQuestion(TypedDict):
+    type: Literal["confirm"]
+    name: str
+    message: str
+    default: bool
+
+
+CzQuestion = Union[ListQuestion, InputQuestion, ConfirmQuestion]
diff --git a/tests/conftest.py b/tests/conftest.py
index 1b49dcbfaa..a4815b3d5a 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -17,6 +17,7 @@
 from commitizen.config import BaseConfig
 from commitizen.cz import registry
 from commitizen.cz.base import BaseCommitizen
+from commitizen.question import CzQuestion
 from tests.utils import create_file_and_commit
 
 SIGNER = "GitHub Action"
@@ -222,7 +223,7 @@ def use_cz_semver(mocker):
 
 
 class MockPlugin(BaseCommitizen):
-    def questions(self) -> defaults.Questions:
+    def questions(self) -> list[CzQuestion]:
         return []
 
     def message(self, answers: dict) -> str:

From 547836617d8fbae1197a7e7306f554c4446dea35 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 12:31:14 +0800
Subject: [PATCH 160/169] refactor(check): compile once and rename variable

---
 commitizen/commands/check.py | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index ba003cf3cc..69147bcfbe 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -79,20 +79,19 @@ def __call__(self) -> None:
         if not commits:
             raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'")
 
-        pattern = self.cz.schema_pattern()
-        displayed_msgs_content = "\n".join(
-            [
-                f'commit "{commit.rev}": "{commit.message}"'
-                for commit in commits
-                if not self.validate_commit_message(commit.message, pattern)
-            ]
+        pattern = re.compile(self.cz.schema_pattern())
+        invalid_msgs_content = "\n".join(
+            f'commit "{commit.rev}": "{commit.message}"'
+            for commit in commits
+            if not self._validate_commit_message(commit.message, pattern)
         )
-        if displayed_msgs_content:
+        if invalid_msgs_content:
+            # TODO: capitalize the first letter of the error message for consistency in v5
             raise InvalidCommitMessageError(
                 "commit validation: failed!\n"
                 "please enter a commit message in the commitizen format.\n"
-                f"{displayed_msgs_content}\n"
-                f"pattern: {pattern}"
+                f"{invalid_msgs_content}\n"
+                f"pattern: {pattern.pattern}"
             )
         out.success("Commit validation: successful!")
 
@@ -143,14 +142,18 @@ def _filter_comments(msg: str) -> str:
                 lines.append(line)
         return "\n".join(lines)
 
-    def validate_commit_message(self, commit_msg: str, pattern: str) -> bool:
+    def _validate_commit_message(
+        self, commit_msg: str, pattern: re.Pattern[str]
+    ) -> bool:
         if not commit_msg:
             return self.allow_abort
 
         if any(map(commit_msg.startswith, self.allowed_prefixes)):
             return True
+
         if self.max_msg_length:
             msg_len = len(commit_msg.partition("\n")[0].strip())
             if msg_len > self.max_msg_length:
                 return False
-        return bool(re.match(pattern, commit_msg))
+
+        return bool(pattern.match(commit_msg))

From 7fc35151829335d09a9428e306ad18a161810a91 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 21:10:05 +0800
Subject: [PATCH 161/169] refactor: do not guess if changelog format is
 provided

---
 commitizen/changelog_formats/__init__.py       | 17 +++++++++++------
 commitizen/factory.py                          |  4 +---
 tests/test_changelog_formats.py                |  8 ++++----
 tests/{test_defaults.py => test_deprecated.py} |  6 +++++-
 4 files changed, 21 insertions(+), 14 deletions(-)
 rename tests/{test_defaults.py => test_deprecated.py} (84%)

diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py
index b7b3cac01d..9a5eea7ab2 100644
--- a/commitizen/changelog_formats/__init__.py
+++ b/commitizen/changelog_formats/__init__.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 import sys
-from typing import ClassVar, Protocol
+from typing import Callable, ClassVar, Protocol
 
 if sys.version_info >= (3, 10):
     from importlib import metadata
@@ -64,10 +64,9 @@ def get_changelog_format(
     :raises FormatUnknown: if a non-empty name is provided but cannot be found in the known formats
     """
     name: str | None = config.settings.get("changelog_format")
-    format: type[ChangelogFormat] | None = guess_changelog_format(filename)
-
-    if name and name in KNOWN_CHANGELOG_FORMATS:
-        format = KNOWN_CHANGELOG_FORMATS[name]
+    format = (
+        name and KNOWN_CHANGELOG_FORMATS.get(name) or _guess_changelog_format(filename)
+    )
 
     if not format:
         raise ChangelogFormatUnknown(f"Unknown changelog format '{name}'")
@@ -75,7 +74,7 @@ def get_changelog_format(
     return format(config)
 
 
-def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None:
+def _guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None:
     """
     Try guessing the file format from the filename.
 
@@ -91,3 +90,9 @@ def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None
             if filename.endswith(f".{alt_extension}"):
                 return format
     return None
+
+
+def __getattr__(name: str) -> Callable[[str], type[ChangelogFormat] | None]:
+    if name == "guess_changelog_format":
+        return _guess_changelog_format
+    raise AttributeError(f"module {__name__} has no attribute {name}")
diff --git a/commitizen/factory.py b/commitizen/factory.py
index b5d665b65e..d9e99fb771 100644
--- a/commitizen/factory.py
+++ b/commitizen/factory.py
@@ -8,12 +8,10 @@ def committer_factory(config: BaseConfig) -> BaseCommitizen:
     """Return the correct commitizen existing in the registry."""
     name: str = config.settings["name"]
     try:
-        _cz = registry[name](config)
+        return registry[name](config)
     except KeyError:
         msg_error = (
             "The committer has not been found in the system.\n\n"
             f"Try running 'pip install {name}'\n"
         )
         raise NoCommitizenFoundException(msg_error)
-    else:
-        return _cz
diff --git a/tests/test_changelog_formats.py b/tests/test_changelog_formats.py
index dec23720dc..e0d99e0325 100644
--- a/tests/test_changelog_formats.py
+++ b/tests/test_changelog_formats.py
@@ -6,8 +6,8 @@
 from commitizen.changelog_formats import (
     KNOWN_CHANGELOG_FORMATS,
     ChangelogFormat,
+    _guess_changelog_format,
     get_changelog_format,
-    guess_changelog_format,
 )
 from commitizen.config.base_config import BaseConfig
 from commitizen.exceptions import ChangelogFormatUnknown
@@ -15,14 +15,14 @@
 
 @pytest.mark.parametrize("format", KNOWN_CHANGELOG_FORMATS.values())
 def test_guess_format(format: type[ChangelogFormat]):
-    assert guess_changelog_format(f"CHANGELOG.{format.extension}") is format
+    assert _guess_changelog_format(f"CHANGELOG.{format.extension}") is format
     for ext in format.alternative_extensions:
-        assert guess_changelog_format(f"CHANGELOG.{ext}") is format
+        assert _guess_changelog_format(f"CHANGELOG.{ext}") is format
 
 
 @pytest.mark.parametrize("filename", ("CHANGELOG", "NEWS", "file.unknown", None))
 def test_guess_format_unknown(filename: str):
-    assert guess_changelog_format(filename) is None
+    assert _guess_changelog_format(filename) is None
 
 
 @pytest.mark.parametrize(
diff --git a/tests/test_defaults.py b/tests/test_deprecated.py
similarity index 84%
rename from tests/test_defaults.py
rename to tests/test_deprecated.py
index 73cd35b80c..41bea81a73 100644
--- a/tests/test_defaults.py
+++ b/tests/test_deprecated.py
@@ -1,6 +1,6 @@
 import pytest
 
-from commitizen import defaults
+from commitizen import changelog_formats, defaults
 
 
 def test_getattr_deprecated_vars():
@@ -15,6 +15,10 @@ def test_getattr_deprecated_vars():
         assert defaults.change_type_order == defaults.CHANGE_TYPE_ORDER
         assert defaults.encoding == defaults.ENCODING
         assert defaults.name == defaults.DEFAULT_SETTINGS["name"]
+        assert (
+            changelog_formats._guess_changelog_format
+            == changelog_formats.guess_changelog_format
+        )
 
     # Verify warning messages
     assert len(record) == 7

From 2d23942fe77e547bb0ecb65e883a9921e094eca9 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 12:37:11 +0800
Subject: [PATCH 162/169] refactor(conventional_commits): make schema_pattern
 more readable

---
 .../conventional_commits.py                   | 22 ++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index ca4809a90e..8e6caac1f5 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -174,11 +174,27 @@ def schema(self) -> str:
         )
 
     def schema_pattern(self) -> str:
+        change_types = (
+            "build",
+            "bump",
+            "chore",
+            "ci",
+            "docs",
+            "feat",
+            "fix",
+            "perf",
+            "refactor",
+            "revert",
+            "style",
+            "test",
+        )
         return (
             r"(?s)"  # To explicitly make . match new line
-            r"(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|bump)"  # type
-            r"(\(\S+\))?!?:"  # scope
-            r"( [^\n\r]+)"  # subject
+            r"(" + "|".join(change_types) + r")"  # type
+            r"(\(\S+\))?"  # scope
+            r"!?"
+            r": "
+            r"([^\n\r]+)"  # subject
             r"((\n\n.*)|(\s*))?$"
         )
 

From 451e0fc50577d9081266b8bd0c36b82d903bc21d Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 21:46:10 +0800
Subject: [PATCH 163/169] refactor(conventional_commits): use TypedDict for
 answers

---
 .../cz/conventional_commits/conventional_commits.py  | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index 8e6caac1f5..6893423478 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -1,4 +1,5 @@
 import os
+from typing import TypedDict
 
 from commitizen import defaults
 from commitizen.cz.base import BaseCommitizen
@@ -16,6 +17,15 @@ def _parse_subject(text: str) -> str:
     return required_validator(text.strip(".").strip(), msg="Subject is required.")
 
 
+class ConventionalCommitsAnswers(TypedDict):
+    prefix: str
+    scope: str
+    subject: str
+    body: str
+    footer: str
+    is_breaking_change: bool
+
+
 class ConventionalCommitsCz(BaseCommitizen):
     bump_pattern = defaults.BUMP_PATTERN
     bump_map = defaults.BUMP_MAP
@@ -136,7 +146,7 @@ def questions(self) -> list[CzQuestion]:
             },
         ]
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: ConventionalCommitsAnswers) -> str:  # type: ignore[override]
         prefix = answers["prefix"]
         scope = answers["scope"]
         subject = answers["subject"]

From 18b9729640b7c311831475af7c91d0360de527d7 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 21:46:31 +0800
Subject: [PATCH 164/169] refactor(jira): refactor message

---
 commitizen/cz/jira/jira.py | 15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py
index eafded615e..8bf8c210b3 100644
--- a/commitizen/cz/jira/jira.py
+++ b/commitizen/cz/jira/jira.py
@@ -43,18 +43,11 @@ def questions(self) -> list[CzQuestion]:
             },
         ]
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: dict[str, str]) -> str:
         return " ".join(
-            filter(
-                bool,
-                [
-                    answers["message"],
-                    answers["issues"],
-                    answers["workflow"],
-                    answers["time"],
-                    answers["comment"],
-                ],
-            )
+            x
+            for k in ("message", "issues", "workflow", "time", "comment")
+            if (x := answers.get(k))
         )
 
     def example(self) -> str:

From c1eea63240c1b24f39699f7001717bcc7cd80e93 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Fri, 30 May 2025 22:04:34 +0800
Subject: [PATCH 165/169] style: replace dict with Mapping

---
 commitizen/cz/base.py                | 4 ++--
 commitizen/cz/customize/customize.py | 5 +++--
 commitizen/cz/jira/jira.py           | 3 ++-
 tests/conftest.py                    | 6 +++---
 tests/test_cz_base.py                | 4 +++-
 5 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py
index a30540cae6..cdc1476694 100644
--- a/commitizen/cz/base.py
+++ b/commitizen/cz/base.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 from abc import ABCMeta, abstractmethod
-from collections.abc import Iterable
+from collections.abc import Iterable, Mapping
 from typing import Any, Callable, Protocol
 
 from jinja2 import BaseLoader, PackageLoader
@@ -72,7 +72,7 @@ def questions(self) -> Iterable[CzQuestion]:
         """Questions regarding the commit message."""
 
     @abstractmethod
-    def message(self, answers: dict) -> str:
+    def message(self, answers: Mapping[str, Any]) -> str:
         """Format your git message."""
 
     @property
diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py
index 6581ad444e..d6e9f9a5c3 100644
--- a/commitizen/cz/customize/customize.py
+++ b/commitizen/cz/customize/customize.py
@@ -1,6 +1,7 @@
 from __future__ import annotations
 
-from typing import TYPE_CHECKING
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, Any
 
 from commitizen.question import CzQuestion
 
@@ -49,7 +50,7 @@ def __init__(self, config: BaseConfig) -> None:
     def questions(self) -> list[CzQuestion]:
         return self.custom_settings.get("questions", [{}])  # type: ignore
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: Mapping[str, Any]) -> str:
         message_template = Template(self.custom_settings.get("message_template", ""))
         if getattr(Template, "substitute", None):
             return message_template.substitute(**answers)  # type: ignore
diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py
index 8bf8c210b3..4e6024ff74 100644
--- a/commitizen/cz/jira/jira.py
+++ b/commitizen/cz/jira/jira.py
@@ -1,4 +1,5 @@
 import os
+from collections.abc import Mapping
 
 from commitizen.cz.base import BaseCommitizen
 from commitizen.question import CzQuestion
@@ -43,7 +44,7 @@ def questions(self) -> list[CzQuestion]:
             },
         ]
 
-    def message(self, answers: dict[str, str]) -> str:
+    def message(self, answers: Mapping[str, str]) -> str:
         return " ".join(
             x
             for k in ("message", "issues", "workflow", "time", "comment")
diff --git a/tests/conftest.py b/tests/conftest.py
index a4815b3d5a..324ef9bebb 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,7 +3,7 @@
 import os
 import re
 import tempfile
-from collections.abc import Iterator
+from collections.abc import Iterator, Mapping
 from pathlib import Path
 
 import pytest
@@ -210,7 +210,7 @@ def questions(self) -> list:
             },
         ]
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: Mapping) -> str:
         prefix = answers["prefix"]
         subject = answers.get("subject", "default message").trim()
         return f"{prefix}: {subject}"
@@ -226,7 +226,7 @@ class MockPlugin(BaseCommitizen):
     def questions(self) -> list[CzQuestion]:
         return []
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: Mapping) -> str:
         return ""
 
 
diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py
index be93b4ca0f..0ee5a23fb8 100644
--- a/tests/test_cz_base.py
+++ b/tests/test_cz_base.py
@@ -1,3 +1,5 @@
+from collections.abc import Mapping
+
 import pytest
 
 from commitizen.cz.base import BaseCommitizen
@@ -7,7 +9,7 @@ class DummyCz(BaseCommitizen):
     def questions(self):
         return [{"type": "input", "name": "commit", "message": "Initial commit:\n"}]
 
-    def message(self, answers: dict):
+    def message(self, answers: Mapping):
         return answers["commit"]
 
 

From 5df4b1651d58037d604fda1af7b3eca724dfc2c9 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 2 Jun 2025 13:33:19 +0800
Subject: [PATCH 166/169] ci(pyproject.toml): strict check for invalid commit
 messages

---
 pyproject.toml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index ee0ecc9ced..58425a81ac 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -249,8 +249,8 @@ lint.sequence = [
     { cmd = "mypy" },
 ]
 
-check-commit.help = "Check the commit message"
-check-commit.cmd = "cz -nr 3 check --rev-range origin/master.."
+check-commit.help = "Check the commit messages"
+check-commit.cmd = "poetry run cz --no-raise 3 check --rev-range origin/master.."
 
 test.help = "Run the test suite"
 test.cmd = "pytest -n 3 --dist=loadfile"
@@ -262,7 +262,7 @@ cover.help = "Run the test suite with coverage"
 cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen"
 
 all.help = "Run all tasks"
-all.sequence = ["format", "lint", "cover", "check-commit"]
+all.sequence = ["check-commit", "format", "lint", "cover"]
 
 "doc:screenshots".help = "Render documentation screenshots"
 "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots"
@@ -274,7 +274,7 @@ doc.help = "Live documentation server"
 doc.cmd = "mkdocs serve"
 
 ci.help = "Run all tasks in CI"
-ci.sequence = [{ cmd = "pre-commit run --all-files" }, "cover"]
+ci.sequence = ["check-commit", { cmd = "pre-commit run --all-files" }, "cover"]
 ci.env = { SKIP = "no-commit-to-branch" }
 
 setup-pre-commit.help = "Install pre-commit hooks"

From 188bc3faf96ea2f7eac34b5eee65bf9fcc2648c3 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Mon, 2 Jun 2025 14:30:22 +0800
Subject: [PATCH 167/169] docs(pyproject.toml): move check-commit before cover

---
 pyproject.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index 58425a81ac..1de17464a3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -262,7 +262,7 @@ cover.help = "Run the test suite with coverage"
 cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen"
 
 all.help = "Run all tasks"
-all.sequence = ["check-commit", "format", "lint", "cover"]
+all.sequence = ["format", "lint", "check-commit", "cover"]
 
 "doc:screenshots".help = "Render documentation screenshots"
 "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots"

From 8675c289df0e5c37b2867bf16c7c2c41f7eef438 Mon Sep 17 00:00:00 2001
From: Yu-Ting Hsiung <bear890707@gmail.com>
Date: Sun, 8 Jun 2025 23:10:04 +0800
Subject: [PATCH 168/169] build: add PGH003 and PGH004, add types-colorama

---
 commitizen/__init__.py                      |  2 +-
 commitizen/commands/bump.py                 |  4 ++--
 commitizen/commands/changelog.py            |  4 ++--
 commitizen/config/base_config.py            |  2 +-
 commitizen/config/toml_config.py            |  6 +++---
 commitizen/cz/customize/__init__.py         |  2 +-
 commitizen/cz/customize/customize.py        |  4 ++--
 commitizen/providers/base_provider.py       |  4 ++--
 commitizen/providers/cargo_provider.py      | 12 ++++++------
 commitizen/providers/commitizen_provider.py |  2 +-
 commitizen/providers/poetry_provider.py     |  4 ++--
 commitizen/version_schemes.py               |  4 ++--
 poetry.lock                                 | 14 +++++++++++++-
 pyproject.toml                              | 14 ++++++--------
 tests/commands/test_bump_command.py         |  2 +-
 15 files changed, 45 insertions(+), 35 deletions(-)

diff --git a/commitizen/__init__.py b/commitizen/__init__.py
index f16def4441..6db9e6e7db 100644
--- a/commitizen/__init__.py
+++ b/commitizen/__init__.py
@@ -1,7 +1,7 @@
 import logging
 import logging.config
 
-from colorama import init  # type: ignore
+from colorama import init
 
 from commitizen.cz.base import BaseCommitizen
 
diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index ba252fcf21..2a84483c94 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -331,14 +331,14 @@ def __call__(self) -> None:
                 "dry_run": dry_run,
             }
             if self.changelog_to_stdout:
-                changelog_cmd = Changelog(self.config, {**args, "dry_run": True})  # type: ignore
+                changelog_cmd = Changelog(self.config, {**args, "dry_run": True})  # type: ignore[typeddict-item]
                 try:
                     changelog_cmd()
                 except DryRunExit:
                     pass
 
             args["file_name"] = self.file_name
-            changelog_cmd = Changelog(self.config, args)  # type: ignore
+            changelog_cmd = Changelog(self.config, args)  # type: ignore[arg-type]
             changelog_cmd()
             files.append(changelog_cmd.file_name)
 
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 8ca36bdb9a..dc50eb3ad2 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -177,8 +177,8 @@ def _write_changelog(
     def _export_template(self) -> None:
         tpl = changelog.get_changelog_template(self.cz.template_loader, self.template)
         # TODO: fix the following type ignores
-        src = Path(tpl.filename)  # type: ignore
-        Path(self.export_template_to).write_text(src.read_text())  # type: ignore
+        src = Path(tpl.filename)  # type: ignore[arg-type]
+        Path(self.export_template_to).write_text(src.read_text())  # type: ignore[arg-type]
 
     def __call__(self) -> None:
         commit_parser = self.cz.commit_parser
diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py
index 59c0d16a06..4b8f5f05fc 100644
--- a/commitizen/config/base_config.py
+++ b/commitizen/config/base_config.py
@@ -27,7 +27,7 @@ def settings(self) -> Settings:
 
     @property
     def path(self) -> Path:
-        return self._path  # type: ignore
+        return self._path  # type: ignore[return-value]
 
     @path.setter
     def path(self, path: str | Path) -> None:
diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py
index 3571c9c882..2164d3f992 100644
--- a/commitizen/config/toml_config.py
+++ b/commitizen/config/toml_config.py
@@ -37,7 +37,7 @@ def init_empty_config_content(self) -> None:
         with open(self.path, "wb") as output_toml_file:
             if parser.get("tool") is None:
                 parser["tool"] = table()
-            parser["tool"]["commitizen"] = table()  # type: ignore
+            parser["tool"]["commitizen"] = table()  # type: ignore[index]
             output_toml_file.write(parser.as_string().encode(self.encoding))
 
     def set_key(self, key: str, value: Any) -> Self:
@@ -49,7 +49,7 @@ def set_key(self, key: str, value: Any) -> Self:
         with open(self.path, "rb") as f:
             parser = parse(f.read())
 
-        parser["tool"]["commitizen"][key] = value  # type: ignore
+        parser["tool"]["commitizen"][key] = value  # type: ignore[index]
         with open(self.path, "wb") as f:
             f.write(parser.as_string().encode(self.encoding))
         return self
@@ -68,6 +68,6 @@ def _parse_setting(self, data: bytes | str) -> None:
             raise InvalidConfigurationError(f"Failed to parse {self.path}: {e}")
 
         try:
-            self.settings.update(doc["tool"]["commitizen"])  # type: ignore
+            self.settings.update(doc["tool"]["commitizen"])  # type: ignore[index,typeddict-item] # TODO: fix this
         except exceptions.NonExistentKey:
             self.is_empty_config = True
diff --git a/commitizen/cz/customize/__init__.py b/commitizen/cz/customize/__init__.py
index c5af001a79..0aedb9337a 100644
--- a/commitizen/cz/customize/__init__.py
+++ b/commitizen/cz/customize/__init__.py
@@ -1 +1 @@
-from .customize import CustomizeCommitsCz  # noqa
+from .customize import CustomizeCommitsCz  # noqa: F401
diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py
index d6e9f9a5c3..dde2685496 100644
--- a/commitizen/cz/customize/customize.py
+++ b/commitizen/cz/customize/customize.py
@@ -48,12 +48,12 @@ def __init__(self, config: BaseConfig) -> None:
                 setattr(self, attr_name, value)
 
     def questions(self) -> list[CzQuestion]:
-        return self.custom_settings.get("questions", [{}])  # type: ignore
+        return self.custom_settings.get("questions", [{}])  # type: ignore[return-value]
 
     def message(self, answers: Mapping[str, Any]) -> str:
         message_template = Template(self.custom_settings.get("message_template", ""))
         if getattr(Template, "substitute", None):
-            return message_template.substitute(**answers)  # type: ignore
+            return message_template.substitute(**answers)  # type: ignore[attr-defined,no-any-return] # pragma: no cover # TODO: check if we can fix this
         return message_template.render(**answers)
 
     def example(self) -> str:
diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py
index 27c3123416..c91bfdae20 100644
--- a/commitizen/providers/base_provider.py
+++ b/commitizen/providers/base_provider.py
@@ -86,7 +86,7 @@ def set_version(self, version: str) -> None:
         self.file.write_text(tomlkit.dumps(document))
 
     def get(self, document: tomlkit.TOMLDocument) -> str:
-        return document["project"]["version"]  # type: ignore
+        return document["project"]["version"]  # type: ignore[index,return-value]
 
     def set(self, document: tomlkit.TOMLDocument, version: str) -> None:
-        document["project"]["version"] = version  # type: ignore
+        document["project"]["version"] = version  # type: ignore[index]
diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py
index 87e45cd71c..d453a4aa5b 100644
--- a/commitizen/providers/cargo_provider.py
+++ b/commitizen/providers/cargo_provider.py
@@ -23,18 +23,18 @@ def lock_file(self) -> Path:
 
     def get(self, document: tomlkit.TOMLDocument) -> str:
         try:
-            return document["package"]["version"]  # type: ignore
+            return document["package"]["version"]  # type: ignore[index,return-value]
         except tomlkit.exceptions.NonExistentKey:
             ...
-        return document["workspace"]["package"]["version"]  # type: ignore
+        return document["workspace"]["package"]["version"]  # type: ignore[index,return-value]
 
     def set(self, document: tomlkit.TOMLDocument, version: str) -> None:
         try:
-            document["workspace"]["package"]["version"] = version  # type: ignore
+            document["workspace"]["package"]["version"] = version  # type: ignore[index]
             return
         except tomlkit.exceptions.NonExistentKey:
             ...
-        document["package"]["version"] = version  # type: ignore
+        document["package"]["version"] = version  # type: ignore[index]
 
     def set_version(self, version: str) -> None:
         super().set_version(version)
@@ -44,9 +44,9 @@ def set_version(self, version: str) -> None:
     def set_lock_version(self, version: str) -> None:
         cargo_toml_content = tomlkit.parse(self.file.read_text())
         try:
-            package_name = cargo_toml_content["package"]["name"]  # type: ignore
+            package_name = cargo_toml_content["package"]["name"]  # type: ignore[index]
         except tomlkit.exceptions.NonExistentKey:
-            package_name = cargo_toml_content["workspace"]["package"]["name"]  # type: ignore
+            package_name = cargo_toml_content["workspace"]["package"]["name"]  # type: ignore[index]
 
         cargo_lock_content = tomlkit.parse(self.lock_file.read_text())
         packages: tomlkit.items.AoT = cargo_lock_content["package"]  # type: ignore[assignment]
diff --git a/commitizen/providers/commitizen_provider.py b/commitizen/providers/commitizen_provider.py
index 7ce177a604..d9207b19ff 100644
--- a/commitizen/providers/commitizen_provider.py
+++ b/commitizen/providers/commitizen_provider.py
@@ -9,7 +9,7 @@ class CommitizenProvider(VersionProvider):
     """
 
     def get_version(self) -> str:
-        return self.config.settings["version"]  # type: ignore
+        return self.config.settings["version"]  # type: ignore[return-value] # TODO: check if we can fix this by tweaking the `Settings` type
 
     def set_version(self, version: str) -> None:
         self.config.set_key("version", version)
diff --git a/commitizen/providers/poetry_provider.py b/commitizen/providers/poetry_provider.py
index 1dd33f053e..f63b13b793 100644
--- a/commitizen/providers/poetry_provider.py
+++ b/commitizen/providers/poetry_provider.py
@@ -13,7 +13,7 @@ class PoetryProvider(TomlProvider):
     filename = "pyproject.toml"
 
     def get(self, pyproject: tomlkit.TOMLDocument) -> str:
-        return pyproject["tool"]["poetry"]["version"]  # type: ignore
+        return pyproject["tool"]["poetry"]["version"]  # type: ignore[index,return-value]
 
     def set(self, pyproject: tomlkit.TOMLDocument, version: str) -> None:
-        pyproject["tool"]["poetry"]["version"] = version  # type: ignore
+        pyproject["tool"]["poetry"]["version"] = version  # type: ignore[index]
diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py
index a59d3c0aa0..e9f99c5514 100644
--- a/commitizen/version_schemes.py
+++ b/commitizen/version_schemes.py
@@ -266,7 +266,7 @@ def bump(
 
         if self.local and is_local_version:
             local_version = self.scheme(self.local).bump(increment)
-            return self.scheme(f"{self.public}+{local_version}")  # type: ignore
+            return self.scheme(f"{self.public}+{local_version}")  # type: ignore[return-value]
 
         base = self._get_increment_base(increment, exact_increment)
         dev_version = self.generate_devrelease(devrelease)
@@ -283,7 +283,7 @@ def bump(
         # TODO: post version
         return self.scheme(
             f"{base}{pre_version}{dev_version}{self.generate_build_metadata(build_metadata)}"
-        )  # type: ignore
+        )  # type: ignore[return-value]
 
     def _get_increment_base(
         self, increment: Increment | None, exact_increment: bool
diff --git a/poetry.lock b/poetry.lock
index f9f74513a8..c5c893931a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1746,6 +1746,18 @@ files = [
 docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
 test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
 
+[[package]]
+name = "types-colorama"
+version = "0.4.15.20240311"
+description = "Typing stubs for colorama"
+optional = false
+python-versions = ">=3.8"
+groups = ["linters"]
+files = [
+    {file = "types-colorama-0.4.15.20240311.tar.gz", hash = "sha256:a28e7f98d17d2b14fb9565d32388e419f4108f557a7d939a66319969b2b99c7a"},
+    {file = "types_colorama-0.4.15.20240311-py3-none-any.whl", hash = "sha256:6391de60ddc0db3f147e31ecb230006a6823e81e380862ffca1e4695c13a0b8e"},
+]
+
 [[package]]
 name = "types-deprecated"
 version = "1.2.15.20250304"
@@ -2014,4 +2026,4 @@ type = ["pytest-mypy"]
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9,<4.0"
-content-hash = "10e298e9b4d2f2d81a82b43649a68d557ed3e9824b3b210b5b6a607935f9fe9d"
+content-hash = "f35cf57718e28836cf1e70b33edab9ce623b01a7f2d31d712585554721f6d403"
diff --git a/pyproject.toml b/pyproject.toml
index 1de17464a3..0d135954a5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -126,6 +126,7 @@ types-deprecated = "^1.2.9.2"
 types-python-dateutil = "^2.8.19.13"
 types-PyYAML = ">=5.4.3,<7.0.0"
 types-termcolor = "^0.1.1"
+types-colorama = "^0.4.15.20240311"
 
 [tool.poetry.group.documentation.dependencies]
 mkdocs = "^1.4.2"
@@ -198,6 +199,9 @@ select = [
     "UP",
     # isort
     "I",
+    # pygrep-hooks
+    "PGH003",
+    "PGH004",
     # unsorted-dunder-all
     "RUF022",
     # unused-noqa
@@ -238,16 +242,10 @@ poetry_command = ""
 
 [tool.poe.tasks]
 format.help = "Format the code"
-format.sequence = [
-    { cmd = "ruff check --fix" },
-    { cmd = "ruff format" },
-]
+format.sequence = [{ cmd = "ruff check --fix" }, { cmd = "ruff format" }]
 
 lint.help = "Lint the code"
-lint.sequence = [
-    { cmd = "ruff check" },
-    { cmd = "mypy" },
-]
+lint.sequence = [{ cmd = "ruff check" }, { cmd = "mypy" }]
 
 check-commit.help = "Check the commit messages"
 check-commit.cmd = "poetry run cz --no-raise 3 check --rev-range origin/master.."
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index 64b810e4de..59297b1726 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -1721,7 +1721,7 @@ def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project):
         "extras": None,
     }
 
-    bump_cmd = bump.Bump(config, arguments)  # type: ignore
+    bump_cmd = bump.Bump(config, arguments)  # type: ignore[arg-type]
 
     # Test case 1: No current tag, not yes mode
     mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True))

From c710c9f541ae452547fdce5c360929f007ec4867 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 9 Jun 2025 14:18:28 +0000
Subject: [PATCH 169/169] =?UTF-8?q?bump:=20version=204.8.2=20=E2=86=92=204?=
 =?UTF-8?q?.8.3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .pre-commit-config.yaml   |  2 +-
 CHANGELOG.md              | 60 +++++++++++++++++++++++++++++++++++++++
 commitizen/__version__.py |  2 +-
 pyproject.toml            |  4 +--
 4 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4e108eb9b9..9e09fb63e8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -48,7 +48,7 @@ repos:
           - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.8.2 # automatically updated by Commitizen
+    rev: v4.8.3 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b737a851a..76b6e36e94 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,63 @@
+## v4.8.3 (2025-06-09)
+
+### Fix
+
+- **cli**: update description for deprecate warning
+- **commit**: emit deprecated warning of cz commit -s
+- **Check**: make parameters backward compatiable
+- **BaseConfig**: mypy error
+- **deprecated**: mark deprecate in v5
+- **defaults**: add non-capitalized default constants back and deprecated warning
+
+### Refactor
+
+- **jira**: refactor message
+- **conventional_commits**: use TypedDict for answers
+- **conventional_commits**: make schema_pattern more readable
+- do not guess if changelog format is provided
+- **check**: compile once and rename variable
+- **questions**: type questions with TypedDict
+- **bump**: simplify nested if
+- **git**: retype get_commits parameter to make it more friendly to call sites
+- **git**: simplify tag logic
+- **bump**: eliminate similar patterns in code
+- **bump**: use any to replace 'or' chain
+- remove unnecessary bool() and remove Any type from TypedDict get
+- **bump**: improve readability and still bypass mypy check
+- **commands**: remove unused args, type version command args
+- **commit**: type commit args
+- **check**: type CheckArgs arguments
+- **check**: remove unused argument
+- **changelog**: type untyped arguments
+- **bump**: TypedDict for bump argument
+- make methods protected, better type
+- **conventional_commits**: remove unnecessary checks
+- fix mypy output and better type
+- **BaseCommitizen**: remove unused process_commit
+- remove `TypeError` handling since `Python >=3.9` is required
+- add comment clarifying `no_raise` parsing to `list[int]`
+- **cli.py**: add type hints
+- **mypy**: remove `unused-ignore`
+- **changelog**: better typing, yield
+- **cli**: early return and improve test coverage
+- **git**: extract _create_commit_cmd_string
+- misc cleanup
+- **bump**: clean up
+- **bump**: add type for out, replace function with re escape
+- **BaseConfig**: use setter
+- **changelog**: minor cleanup
+- **git**: refactor get_tag_names
+- **EOLType**: add eol enum back and reorganize methods
+- **git**: code cleanup and better test coverage
+- **commit**: simplify call
+- **version_scheme**: cleanup
+- improve readability and fix typos
+
+### Perf
+
+- **bump**: avoid unnecessary list construction and rename variable to avoid confusion
+- **tags**: use set
+
 ## v4.8.2 (2025-05-22)
 
 ### Refactor
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 8e288a597c..af82c57223 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "4.8.2"
+__version__ = "4.8.3"
diff --git a/pyproject.toml b/pyproject.toml
index 0d135954a5..6990741957 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "commitizen"
-version = "4.8.2"
+version = "4.8.3"
 description = "Python commitizen client tool"
 authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
 maintainers = [
@@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api"
 
 
 [tool.commitizen]
-version = "4.8.2"
+version = "4.8.3"
 tag_format = "v$version"
 version_files = [
     "pyproject.toml:version",
