From 48721ce88d6ea5b175880eea5a82d38a8604e9f4 Mon Sep 17 00:00:00 2001 From: Tzoiker Date: Fri, 3 Nov 2023 15:21:27 +0300 Subject: [PATCH 1/6] feat: add pep 440 support --- poem_plugins/config/git.py | 9 ++ poem_plugins/general/version/__init__.py | 27 ++++-- poem_plugins/general/version/drivers/git.py | 99 +++++++++++++-------- 3 files changed, 91 insertions(+), 44 deletions(-) diff --git a/poem_plugins/config/git.py b/poem_plugins/config/git.py index 2e1bca1..1ee0949 100644 --- a/poem_plugins/config/git.py +++ b/poem_plugins/config/git.py @@ -12,14 +12,23 @@ class GitVersionFormatEnum(StrEnum): SHORT = "short" +@unique +class VersionSegmentBumpEnum(StrEnum): + RELEASE = "release" + POST_RELEASE = "post" + DEV = "dev" + + @dataclass class GitProviderSettings(BaseConfig): MAPPERS = MappingProxyType( { "format": GitVersionFormatEnum, "version_prefix": str, + "bump_segment": VersionSegmentBumpEnum, }, ) format: GitVersionFormatEnum = GitVersionFormatEnum.SHORT version_prefix: str = "v" + bump_segment: VersionSegmentBumpEnum = VersionSegmentBumpEnum.RELEASE diff --git a/poem_plugins/general/version/__init__.py b/poem_plugins/general/version/__init__.py index aeba6fd..64ef3f6 100644 --- a/poem_plugins/general/version/__init__.py +++ b/poem_plugins/general/version/__init__.py @@ -1,13 +1,26 @@ -from typing import NamedTuple, Optional +import re +from typing import NamedTuple, Optional, Tuple + +_REGEX_PRE = re.compile(r"^(?P(a|b|rc))(?P\d+)$") class Version(NamedTuple): - major: int - minor: int - patch: int + release: Tuple[int, ...] + epoch: Optional[int] = None + pre: Optional[str] = None + post: Optional[int] = None + dev: Optional[int] = None commit: Optional[str] = None def __str__(self) -> str: - if not self.commit: - return f"{self.major}.{self.minor}.{self.patch}" - return f"{self.major}.{self.minor}.{self.patch}+g{self.commit}" + epoch = "%s!" % self.epoch if self.epoch is not None else "" + pre = self.pre or "" + post = ".post%s" % self.post if self.post is not None else "" + dev = ".dev%s" % self.dev if self.dev is not None else "" + commit = "+%s" % self.commit if self.commit is not None else "" + version = ( + epoch + + '.'.join(map(str, self.release)) + + ''.join((pre, post, dev, commit)) + ) + return version diff --git a/poem_plugins/general/version/drivers/git.py b/poem_plugins/general/version/drivers/git.py index 8a60960..8b63fdb 100644 --- a/poem_plugins/general/version/drivers/git.py +++ b/poem_plugins/general/version/drivers/git.py @@ -3,14 +3,13 @@ import warnings from dataclasses import dataclass from shutil import which -from types import MappingProxyType -from typing import Any, Callable, ClassVar, Mapping, Match, Optional -from poem_plugins.config.git import GitProviderSettings, GitVersionFormatEnum +from poem_plugins.config.git import ( + GitProviderSettings, GitVersionFormatEnum, VersionSegmentBumpEnum, +) from poem_plugins.general.version import Version from poem_plugins.general.version.drivers import IVersionDriver - GIT_BIN = which("git") WARNING_TEXT = ( '"git" binary was not found, this plugin this will not work properly' @@ -32,17 +31,58 @@ class GitVersionDriver(IVersionDriver): '__version__ = "{version}"\n' ) - CONVERTERS: ClassVar[ - Mapping[str, Callable[[Any], Any]] - ] = MappingProxyType( - { - "major": int, - "minor": int, - "patch": int, - }, - ) + def _get_version_pep_440(self, raw_version: str) -> Version: + if raw_version.startswith(self.settings.version_prefix): + raw_version = raw_version.removeprefix(self.settings.version_prefix) + else: + raise RuntimeError( + f"Version tag must start with '{self.settings.version_prefix}' as " + f"defined in the plugin's config (or by default)." + ) + regex = ( + r'^((?P\d+)!)?(?P\d+(\.\d+)*)' + r'(?P
(a|b|rc)\d+)?(\.post(?P\d+))?(\.dev(?P\d+))?'
+            r'-(?P\d+)-(?P\S+)$'
+        )
+        match = re.match(regex, raw_version)
+        if not match:
+            raise RuntimeError(
+                f"Failed to parse git version: '{raw_version}' must conform to PEP 440",
+            )
+        groups = match.groupdict()
+        epoch = groups["epoch"]
+        pre, post, dev = groups["pre"], groups["post"], groups["dev"]
+        commit_count = int(groups["commit_count"])
+        commit = groups["commit"]
+        release = list(map(int, groups["release"].split(".")))
+        if len(release) < 3:
+            release += [0] * (3 - len(release))
 
-    def get_version(self) -> Version:
+        segments = {
+            "epoch": int(epoch) if epoch is not None else None,
+            "pre": pre,
+            "post": int(post) if post is not None else None,
+            "dev": int(dev) if dev is not None else None,
+            "commit": commit,
+        }
+
+        if commit_count:
+            bump_segment = self.settings.bump_segment
+            if bump_segment == VersionSegmentBumpEnum.RELEASE:
+                print(release)
+                release[-1] += commit_count
+            else:
+                segments[bump_segment.value] = segments[bump_segment.value] or 0
+                segments[bump_segment.value] += commit_count
+
+        segments["release"] = tuple(release)
+
+        if self.settings.format == GitVersionFormatEnum.SHORT:
+            segments["commit"] = None
+
+        return Version(**segments)
+
+    def _git_describe(self) -> str:
         if GIT_BIN is None:
             raise RuntimeError(WARNING_TEXT)
 
@@ -53,27 +93,12 @@ def get_version(self) -> Version:
         )
 
         if result.returncode != 0:
-            raise RuntimeError("Cannot found git version")
-        raw_version = result.stdout.strip()
-        regex = (
-            r"(?P\d+)\.(?P\d+)-(?P\d+)-(?P\S+)"
-        )
-        match: Optional[Match[str]] = re.match(
-            self.settings.version_prefix + regex,
-            raw_version,
-        )
+            raise RuntimeError("Failed to find git version tag")
+        return result.stdout.strip()
 
-        if match is None:
-            raise RuntimeError("Cannot parse git version")
-        raw_kwargs = dict(match.groupdict())
-        if self.settings.format == GitVersionFormatEnum.SHORT:
-            raw_kwargs.pop("commit", None)
-        kwargs = {
-            k: self.CONVERTERS.get(k, lambda x: x)(v)
-            for k, v in raw_kwargs.items()
-        }
-
-        return Version(**kwargs)
+    def get_version(self) -> Version:
+        raw_version = self._git_describe()
+        return self._get_version_pep_440(raw_version)
 
     def render_version_file(
         self,
@@ -81,8 +106,8 @@ def render_version_file(
     ) -> str:
         return self.VERSION_TEMPLATE.format(
             whoami='poem-plugins "git" plugin',
-            major=version.major,
-            minor=version.minor,
-            patch=version.patch,
+            major=version.release[0],
+            minor=version.release[1],
+            patch=version.release[2],
             version=str(version),
         )

From 06902835cd92fb50813ea74526b5ae6be2f34350 Mon Sep 17 00:00:00 2001
From: Tzoiker 
Date: Fri, 3 Nov 2023 15:22:59 +0300
Subject: [PATCH 2/6] style: run formatters

---
 poem_plugins/general/version/__init__.py    | 13 +++++++------
 poem_plugins/general/version/drivers/git.py |  9 +++++----
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/poem_plugins/general/version/__init__.py b/poem_plugins/general/version/__init__.py
index 64ef3f6..b6de326 100644
--- a/poem_plugins/general/version/__init__.py
+++ b/poem_plugins/general/version/__init__.py
@@ -1,6 +1,7 @@
 import re
 from typing import NamedTuple, Optional, Tuple
 
+
 _REGEX_PRE = re.compile(r"^(?P(a|b|rc))(?P\d+)$")
 
 
@@ -13,14 +14,14 @@ class Version(NamedTuple):
     commit: Optional[str] = None
 
     def __str__(self) -> str:
-        epoch = "%s!" % self.epoch if self.epoch is not None else ""
+        epoch = f"{self.epoch}!" if self.epoch is not None else ""
         pre = self.pre or ""
-        post = ".post%s" % self.post if self.post is not None else ""
-        dev = ".dev%s" % self.dev if self.dev is not None else ""
-        commit = "+%s" % self.commit if self.commit is not None else ""
+        post = f".post{self.post}" if self.post is not None else ""
+        dev = f".dev{self.dev}" if self.dev is not None else ""
+        commit = f"+{self.commit}" if self.commit is not None else ""
         version = (
             epoch +
-            '.'.join(map(str, self.release)) +
-            ''.join((pre, post, dev, commit))
+            ".".join(map(str, self.release)) +
+            "".join((pre, post, dev, commit))
         )
         return version
diff --git a/poem_plugins/general/version/drivers/git.py b/poem_plugins/general/version/drivers/git.py
index 8b63fdb..a09509a 100644
--- a/poem_plugins/general/version/drivers/git.py
+++ b/poem_plugins/general/version/drivers/git.py
@@ -10,6 +10,7 @@
 from poem_plugins.general.version import Version
 from poem_plugins.general.version.drivers import IVersionDriver
 
+
 GIT_BIN = which("git")
 WARNING_TEXT = (
     '"git" binary was not found, this plugin this will not work properly'
@@ -37,12 +38,12 @@ def _get_version_pep_440(self, raw_version: str) -> Version:
         else:
             raise RuntimeError(
                 f"Version tag must start with '{self.settings.version_prefix}' as "
-                f"defined in the plugin's config (or by default)."
+                "defined in the plugin's config (or by default).",
             )
         regex = (
-            r'^((?P\d+)!)?(?P\d+(\.\d+)*)'
-            r'(?P
(a|b|rc)\d+)?(\.post(?P\d+))?(\.dev(?P\d+))?'
-            r'-(?P\d+)-(?P\S+)$'
+            r"^((?P\d+)!)?(?P\d+(\.\d+)*)"
+            r"(?P
(a|b|rc)\d+)?(\.post(?P\d+))?(\.dev(?P\d+))?"
+            r"-(?P\d+)-(?P\S+)$"
         )
         match = re.match(regex, raw_version)
         if not match:

From fbbe19f59181b94a474674114efaab8aba2943df Mon Sep 17 00:00:00 2001
From: Tzoiker 
Date: Fri, 3 Nov 2023 15:23:42 +0300
Subject: [PATCH 3/6] test: pep 440

---
 tests/conftest.py                             |  5 +-
 tests/general/versions/drivers/conftest.py    | 20 ++++
 .../versions/drivers/git/test_bump_segment.py | 84 +++++++++++++++++
 .../general/versions/drivers/git/test_long.py | 85 ++++++++++++++++-
 .../versions/drivers/git/test_short.py        | 92 +++++++++++++++++++
 .../drivers/git/test_version_prefix.py        | 31 +++++++
 tests/plugins/versions/test_git.py            |  6 +-
 7 files changed, 315 insertions(+), 8 deletions(-)
 create mode 100644 tests/general/versions/drivers/git/test_bump_segment.py
 create mode 100644 tests/general/versions/drivers/git/test_version_prefix.py

diff --git a/tests/conftest.py b/tests/conftest.py
index 0c1ee88..a3d95c3 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -38,8 +38,9 @@ def simple_project(tmpdir) -> Iterator[Path]:
 
 @pytest.fixture
 def expected_long_version() -> Version:
-    return Version(1, 2, 0, "g3c3e199")
+    return Version(release=(1, 2, 0), commit="g3c3e199")
+
 
 @pytest.fixture
 def expected_version() -> Version:
-    return Version(1, 2, 0)
+    return Version(release=(1, 2, 0))
diff --git a/tests/general/versions/drivers/conftest.py b/tests/general/versions/drivers/conftest.py
index cbcff6b..b404d9c 100644
--- a/tests/general/versions/drivers/conftest.py
+++ b/tests/general/versions/drivers/conftest.py
@@ -8,6 +8,26 @@
 def git_settings() -> GitProviderSettings:
     return GitProviderSettings()
 
+
 @pytest.fixture
 def git_version_driver(git_settings) -> GitVersionDriver:
     return GitVersionDriver(git_settings)
+
+
+class MockedGitVersionDriver(GitVersionDriver):
+
+    _describe: str
+
+    def __init__(self, *args, describe: str, **kwargs):
+        super().__init__(*args, **kwargs)
+        self._describe = describe
+
+    def _git_describe(self) -> str:
+        return self._describe
+
+
+@pytest.fixture
+def get_mocked_git_version_driver(git_settings):
+    return lambda describe, git_settings=git_settings: (
+        MockedGitVersionDriver(git_settings, describe=describe)
+    )
diff --git a/tests/general/versions/drivers/git/test_bump_segment.py b/tests/general/versions/drivers/git/test_bump_segment.py
new file mode 100644
index 0000000..a0ed60a
--- /dev/null
+++ b/tests/general/versions/drivers/git/test_bump_segment.py
@@ -0,0 +1,84 @@
+import pytest
+
+from poem_plugins.config.git import (
+    GitProviderSettings, GitVersionFormatEnum, VersionSegmentBumpEnum,
+)
+from poem_plugins.general.version import Version
+
+
+@pytest.fixture
+def git_settings() -> GitProviderSettings:
+    return GitProviderSettings(format=GitVersionFormatEnum.SHORT)
+
+
+@pytest.mark.parametrize(
+    "describe,segment,expected", (
+        (
+            "1",
+            VersionSegmentBumpEnum.RELEASE,
+            Version(release=(1, 0, 10)),
+        ),
+        (
+            "1.2",
+            VersionSegmentBumpEnum.RELEASE,
+            Version(release=(1, 2, 10)),
+        ),
+        (
+            "1.2.3",
+            VersionSegmentBumpEnum.RELEASE,
+            Version(release=(1, 2, 13)),
+        ),
+        (
+            "1.2.3.4",
+            VersionSegmentBumpEnum.RELEASE,
+            Version(release=(1, 2, 3, 14)),
+        ),
+        (
+            "1.2.post3",
+            VersionSegmentBumpEnum.POST_RELEASE,
+            Version(release=(1, 2, 0), post=13),
+        ),
+        (
+            "1.2.dev3",
+            VersionSegmentBumpEnum.DEV,
+            Version(release=(1, 2, 0), dev=13),
+        ),
+    ),
+)
+def test_pep_440(
+    get_mocked_git_version_driver, describe, segment, expected,
+) -> None:
+    describe = "v" + describe + "-10-g3c3e199"
+
+    git_settings = GitProviderSettings(bump_segment=segment)
+    driver = get_mocked_git_version_driver(describe=describe, git_settings=git_settings)
+    assert driver.get_version() == expected
+
+
+@pytest.mark.parametrize(
+    "describe,segment,expected", (
+        (
+            "1.2.3.4",
+            VersionSegmentBumpEnum.RELEASE,
+            Version(release=(1, 2, 3, 4)),
+        ),
+        (
+            "1.2",
+            VersionSegmentBumpEnum.POST_RELEASE,
+            Version(release=(1, 2, 0)),
+        ),
+        (
+            "1.2",
+            VersionSegmentBumpEnum.DEV,
+            Version(release=(1, 2, 0)),
+        ),
+    ),
+)
+def test_pep_440_zero_commits(
+    get_mocked_git_version_driver, describe, segment, expected,
+) -> None:
+    describe = "v" + describe + "-0-g3c3e199"
+
+    git_settings = GitProviderSettings(bump_segment=segment)
+    driver = get_mocked_git_version_driver(describe=describe, git_settings=git_settings)
+    assert driver.get_version() == expected
diff --git a/tests/general/versions/drivers/git/test_long.py b/tests/general/versions/drivers/git/test_long.py
index 807f136..d459482 100644
--- a/tests/general/versions/drivers/git/test_long.py
+++ b/tests/general/versions/drivers/git/test_long.py
@@ -17,6 +17,65 @@ def test_get_version(
     assert git_version_driver.get_version() == expected_long_version
 
 
+@pytest.mark.parametrize(
+    "describe,expected", (
+        (
+            "1",
+            Version(release=(1, 0, 10), commit="g3c3e199"),
+        ),
+        (
+            "1.2",
+            Version(release=(1, 2, 10), commit="g3c3e199"),
+        ),
+        (
+            "1.2.3",
+            Version(release=(1, 2, 13), commit="g3c3e199"),
+        ),
+        (
+            "1.2.3.0",
+            Version(release=(1, 2, 3, 10), commit="g3c3e199"),
+        ),
+        (
+            "1.2a1",
+            Version(release=(1, 2, 10), pre="a1", commit="g3c3e199"),
+        ),
+        (
+            "1.2b1",
+            Version(release=(1, 2, 10), pre="b1", commit="g3c3e199"),
+        ),
+        (
+            "1.2rc1",
+            Version(release=(1, 2, 10), pre="rc1", commit="g3c3e199"),
+        ),
+        (
+            "1.2.post1",
+            Version(release=(1, 2, 10), post=1, commit="g3c3e199"),
+        ),
+        (
+            "1.2.dev1",
+            Version(release=(1, 2, 10), dev=1, commit="g3c3e199"),
+        ),
+        (
+            "1!1.2",
+            Version(epoch=1, release=(1, 2, 10), commit="g3c3e199"),
+        ),
+        (
+            "1!1.2.3a1.post1.dev1",
+            Version(
+                epoch=1, release=(1, 2, 13), pre="a1", post=1, dev=1, commit="g3c3e199",
+            ),
+        ),
+    ),
+)
+def test_pep_440(
+    get_mocked_git_version_driver, describe, expected,
+) -> None:
+    describe = "v" + describe + "-10-g3c3e199"
+
+    driver = get_mocked_git_version_driver(describe=describe)
+    assert driver.get_version() == expected
+
+
 def test_render_version(
     git_version_driver: IVersionDriver,
     expected_long_version: Version,
@@ -29,7 +88,31 @@ def test_render_version(
             "# NEVER EDIT THIS FILE MANUALLY",
             "",
             "version_info = (1, 2, 0)",
-            '__version__ = "1.2.0+gg3c3e199"',
+            '__version__ = "1.2.0+g3c3e199"',
+            "",
+        ),
+    )
+    assert content == expected
+
+
+def test_render_version_pep440(git_version_driver: IVersionDriver) -> None:
+    version = Version(
+        release=(1, 2, 3, 4),
+        epoch=1,
+        pre="a2",
+        post=3,
+        dev=4,
+        commit="g3c3e199",
+    )
+    content = git_version_driver.render_version_file(version)
+    expected = "\n".join(
+        (
+            "# THIS FILE WAS GENERATED AUTOMATICALLY",
+            '# BY: poem-plugins "git" plugin',
+            "# NEVER EDIT THIS FILE MANUALLY",
+            "",
+            "version_info = (1, 2, 3)",
+            '__version__ = "1!1.2.3.4a2.post3.dev4+g3c3e199"',
             "",
         ),
     )
diff --git a/tests/general/versions/drivers/git/test_short.py b/tests/general/versions/drivers/git/test_short.py
index ac7f877..47d5283 100644
--- a/tests/general/versions/drivers/git/test_short.py
+++ b/tests/general/versions/drivers/git/test_short.py
@@ -17,6 +17,74 @@ def test_get_version(
     assert git_version_driver.get_version() == expected_version
 
 
+@pytest.mark.parametrize(
+    "describe,expected", (
+        (
+            "1",
+            Version(release=(1, 0, 10)),
+        ),
+        (
+            "1.2",
+            Version(release=(1, 2, 10)),
+        ),
+        (
+            "1.2.3",
+            Version(release=(1, 2, 13)),
+        ),
+        (
+            "1.2.3.0",
+            Version(release=(1, 2, 3, 10)),
+        ),
+        (
+            "1.2a1",
+            Version(release=(1, 2, 10), pre="a1"),
+        ),
+        (
+            "1.2b1",
+            Version(release=(1, 2, 10), pre="b1"),
+        ),
+        (
+            "1.2rc1",
+            Version(release=(1, 2, 10), pre="rc1"),
+        ),
+        (
+            "1.2.post1",
+            Version(release=(1, 2, 10), post=1),
+        ),
+        (
+            "1.2.dev1",
+            Version(release=(1, 2, 10), dev=1),
+        ),
+        (
+            "1!1.2",
+            Version(epoch=1, release=(1, 2, 10)),
+        ),
+        (
+            "1!1.2.3a1.post1.dev1",
+            Version(
+                epoch=1, release=(1, 2, 13), pre="a1", post=1, dev=1,
+            ),
+        ),
+    ),
+)
+def test_get_version_pep_440(
+    get_mocked_git_version_driver, describe, expected,
+) -> None:
+    describe = "v" + describe + "-10-g3c3e199"
+    driver = get_mocked_git_version_driver(describe=describe)
+    assert driver.get_version() == expected
+
+
+def test_get_version_malformed(get_mocked_git_version_driver) -> None:
+    describe = "v1post123"
+    driver = get_mocked_git_version_driver(describe=describe)
+    with pytest.raises(
+        RuntimeError,
+        match="Failed to parse git version: '1post123' must conform to PEP 440",
+    ):
+        driver.get_version()
+
+
 def test_render_version(
     git_version_driver: IVersionDriver,
     expected_version: Version,
@@ -34,3 +102,27 @@ def test_render_version(
         ),
     )
     assert content == expected
+
+
+def test_render_version_pep440(git_version_driver: IVersionDriver) -> None:
+    version = Version(
+        release=(1, 2, 3, 4),
+        epoch=1,
+        pre="a2",
+        post=3,
+        dev=4,
+        commit=None,
+    )
+    content = git_version_driver.render_version_file(version)
+    expected = "\n".join(
+        (
+            "# THIS FILE WAS GENERATED AUTOMATICALLY",
+            '# BY: poem-plugins "git" plugin',
+            "# NEVER EDIT THIS FILE MANUALLY",
+            "",
+            "version_info = (1, 2, 3)",
+            '__version__ = "1!1.2.3.4a2.post3.dev4"',
+            "",
+        ),
+    )
+    assert content == expected
diff --git a/tests/general/versions/drivers/git/test_version_prefix.py b/tests/general/versions/drivers/git/test_version_prefix.py
new file mode 100644
index 0000000..bc8f1c4
--- /dev/null
+++ b/tests/general/versions/drivers/git/test_version_prefix.py
@@ -0,0 +1,31 @@
+import re
+
+import pytest
+
+from poem_plugins.config.git import GitProviderSettings, GitVersionFormatEnum
+from poem_plugins.general.version import Version
+
+
+@pytest.fixture
+def git_settings() -> GitProviderSettings:
+    return GitProviderSettings(format=GitVersionFormatEnum.SHORT)
+
+
+def test_version_prefix_ok(get_mocked_git_version_driver, git_settings) -> None:
+    describe = "v1.2.3-10-g3c3e199"
+    driver = get_mocked_git_version_driver(describe=describe, git_settings=git_settings)
+    assert driver.get_version() == Version(release=(1, 2, 13))
+
+
+def test_version_prefix_fail(get_mocked_git_version_driver) -> None:
+    describe = "1.2.3-10-g3c3e199"
+    git_settings = GitProviderSettings(version_prefix="v")
+    driver = get_mocked_git_version_driver(describe=describe, git_settings=git_settings)
+    with pytest.raises(
+        RuntimeError,
+        match=re.escape(
+            "Version tag must start with 'v' as defined in the plugin's config "
+            "(or by default).",
+        ),
+    ):
+        driver.get_version()
diff --git a/tests/plugins/versions/test_git.py b/tests/plugins/versions/test_git.py
index 95c7151..55d447e 100644
--- a/tests/plugins/versions/test_git.py
+++ b/tests/plugins/versions/test_git.py
@@ -51,11 +51,7 @@ def test_file_version(
     version_module = module_from_spec(spec)
     spec.loader.exec_module(version_module)
     assert version_module.__version__ == str(expected_version)
-    assert version_module.version_info == (
-        expected_version.major,
-        expected_version.minor,
-        expected_version.patch,
-    )
+    assert version_module.version_info == expected_version.release[:3]
 
 
 def test_pyproject_version(

From 12daf47f0212a40e63a7a75ff20dd1dcf9fc5671 Mon Sep 17 00:00:00 2001
From: Tzoiker 
Date: Fri, 3 Nov 2023 15:24:03 +0300
Subject: [PATCH 4/6] doc: update readme

---
 README.md | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/README.md b/README.md
index d3c77ec..dac3665 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,8 @@ Here are some of the arguments that you can use for `git` provider:
 |-------|-------------|---------|
 | `version_prefix` | filter tags only starts with this prefix | `v` |
 | `format` | plugin will use commit hash (long) or not (short) to build a project version | `short` |
+| `bump_segment` | plugin will bump the selected version's segment with the number of commits after the last tag (allowed `release`, `post`, `dev`) | `release` |
+
 
 Example:
 
@@ -72,6 +74,19 @@ Building awesome_package (0.1.0)
   - Built awesome_package-0.1.0-py3-none-any.whl
 ```
 
+Logic behind `bump_segment` with 10 commits:
+* `release`: bumps `patch` (as in `..`) or the lowest
+  * `1 -> 1.0.10`
+  * `1.0 -> 1.0.10`
+  * `1.0.2 -> 1.0.12`  
+  * `1.0.0.2 -> 1.0.0.12`
+* `post`: bumps `post`
+  * `1.0 -> 1.0.post10`
+  * `1.0.post2 -> 1.0.post12`
+* `dev`: bumps `dev`
+  * `1.0 -> 1.0.dev10`
+  * `1.0.dev2 -> 1.0.dev12`
+
 ## How to develop
 
 Before getting started with development, you'll need to have poetry installed.

From f8aa792e530419f9dc25b558806d743f8b3e0b28 Mon Sep 17 00:00:00 2001
From: Tzoiker 
Date: Fri, 3 Nov 2023 19:18:55 +0300
Subject: [PATCH 5/6] style: fix mypy errors

---
 poem_plugins/general/version/drivers/git.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/poem_plugins/general/version/drivers/git.py b/poem_plugins/general/version/drivers/git.py
index a09509a..3774ae2 100644
--- a/poem_plugins/general/version/drivers/git.py
+++ b/poem_plugins/general/version/drivers/git.py
@@ -74,14 +74,14 @@ def _get_version_pep_440(self, raw_version: str) -> Version:
                 release[-1] += commit_count
             else:
                 segments[bump_segment.value] = segments[bump_segment.value] or 0
-                segments[bump_segment.value] += commit_count
+                segments[bump_segment.value] += commit_count  # type: ignore
 
         segments["release"] = tuple(release)
 
         if self.settings.format == GitVersionFormatEnum.SHORT:
             segments["commit"] = None
 
-        return Version(**segments)
+        return Version(**segments)  # type: ignore
 
     def _git_describe(self) -> str:
         if GIT_BIN is None:

From 446d1848ab7936a540625558954c0781884102ac Mon Sep 17 00:00:00 2001
From: Tzoiker 
Date: Fri, 3 Nov 2023 21:11:21 +0300
Subject: [PATCH 6/6] style: fix pylama error

---
 poem_plugins/general/version/drivers/git.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/poem_plugins/general/version/drivers/git.py b/poem_plugins/general/version/drivers/git.py
index 3774ae2..7d9a375 100644
--- a/poem_plugins/general/version/drivers/git.py
+++ b/poem_plugins/general/version/drivers/git.py
@@ -37,8 +37,8 @@ def _get_version_pep_440(self, raw_version: str) -> Version:
             raw_version = raw_version.removeprefix(self.settings.version_prefix)
         else:
             raise RuntimeError(
-                f"Version tag must start with '{self.settings.version_prefix}' as "
-                "defined in the plugin's config (or by default).",
+                f"Version tag must start with '{self.settings.version_prefix}' "
+                f"as defined in the plugin's config (or by default).",
             )
         regex = (
             r"^((?P\d+)!)?(?P\d+(\.\d+)*)"
@@ -48,7 +48,8 @@ def _get_version_pep_440(self, raw_version: str) -> Version:
         match = re.match(regex, raw_version)
         if not match:
             raise RuntimeError(
-                f"Failed to parse git version: '{raw_version}' must conform to PEP 440",
+                f"Failed to parse git version: '{raw_version}' must conform to "
+                f"PEP 440",
             )
         groups = match.groupdict()
         epoch = groups["epoch"]