Question: How would I use bump2version (with regards to its invocation and/or its configuration) to increment: 1.0.0.a2.post0 # post-release of a pre-release a2  to 1.0.0.a3 # pre-release a3  Reproducible example: $ python3 -m pip install 'bump2version==1.0.*'  __init__.py: __version__ = "1.0.0.a2.post0"  setup.cfg: [bumpversion] current_version = 1.0.0.a2.post0 parse = ^ (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+) # minimum major.minor.patch, 1.2.3 (?: \. (?P<prerel>a|alpha|b|beta|d|dev|rc) # pre-release segment (?P<prerelversion>\d+) # pre-release version num )? (?:\.post(?P<post>\d+))? # post-release serialize = {major}.{minor}.{patch}.{prerel}{prerelversion}.post{post} {major}.{minor}.{patch}.{prerel}{prerelversion} {major}.{minor}.{patch}.post{post} {major}.{minor}.{patch} [bumpversion:file:__init__.py] [bumpversion:part:prerel] optional_value = dev values = dev d alpha a beta b rc  Examples of valid versions from this scheme, which takes some but not all rules from PEP 440: 1.2.3 # (1) final 1.2.3.dev0 # (2) prerelease 1.2.3.a0 1.2.3.alpha0 1.2.3.b0 1.2.3.beta0 1.2.3.rc0 1.2.3.rc3.post0 # (3) postrelease (of a pre-release version) 1.2.3.post0 # (4) postrelease (of a final version)  I've tried, for example, bump2version --verbose prerelversion or alternatively with --new-version=1.0.0.a3 explicitly. Both of those attempts retain the .post0 rather than dropping it.  Note: I asked this as a usage question issue in the bump2version repo a few weeks back with no luck.

Answer: We had to wrestle with that (and, back in the days, with the original bumpversion, not the nicer bump2version). With the config below, you can use bumpversion pre to go from 1.0.0.a2.post0 to 1.0.0.a3. Explanation: Since pre and post each have both a string prefix and a number, I believe it is necessary to split them accordingly. For example, the pre part could be split into a prekind (the string) and a pre (the number). Then, the nice thing is that you can increment prekind ('dev' to 'alpha' to 'beta' etc.) independently of the issue number (a sequence, as usual). Below, I've put both a complete configuration and an example with a number of invocations in sequence to show the various mutations that are possible. I'm sure the setup below can be further customized, but hopefully it will put you and others landing here on the right track. cat > .bumpversion.cfg << "EOF" [bumpversion] current_version = 1.0.0.a2.post0 files = __init__.py commit = False parse = ^ (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+) (\.(?P<prekind>a|alpha|b|beta|d|dev|rc) (?P<pre>\d+) # pre-release version num )? (\.(?P<postkind>post)(?P<post>\d+))? # post-release serialize = {major}.{minor}.{patch}.{prekind}{pre}.{postkind}{post} {major}.{minor}.{patch}.{prekind}{pre} {major}.{minor}.{patch}.{postkind}{post} {major}.{minor}.{patch} [bumpversion:part:prekind] optional_value = _ values = _ dev d alpha a beta b rc [bumpversion:part:postkind] optional_value = _ values = _ post EOF echo '__version__ = "1.0.0.a2.post0"' > __init__.py  Tests: These perform a sequence of bumpversion operations to demonstrate some of the mutations that are possible. And of course, you can use --new-version=... to forcefully set a new version. for op in \ start post post pre pre prekind prekind pre postkind post prekind minor \ postkind post pre postkind prekind postkind post major prekind postkind post; do if [[ $op == 'start' ]]; then printf "starting from: %s\n" $(perl -ne 'print "$1\n" if /"(.*)"/' __init__.py) else bumpversion $op printf "%10s --> %s\n" $op $(perl -ne 'print "$1\n" if /"(.*)"/' __init__.py) fi done  Output (commented): starting from: 1.0.0.a2.post0 post --> 1.0.0.a2.post1 # no issue incrementing post post --> 1.0.0.a2.post2 pre --> 1.0.0.a3 # can move to the next 'pre'release pre --> 1.0.0.a4 prekind --> 1.0.0.beta0 # can upgrade the kind of prerelease prekind --> 1.0.0.b0 pre --> 1.0.0.b1 # and keep incrementing postkind --> 1.0.0.b1.post0 # bring a post component again post --> 1.0.0.b1.post1 # and incrementing prekind --> 1.0.0.rc0 # upgrade pre kind directly minor --> 1.1.0 # patch/minor/major cut the optional parts postkind --> 1.1.0.post0 # but we can bring a post component (without pre) post --> 1.1.0.post1 pre --> 1.1.0 # BAD & silent: cannot increment a missing part postkind --> 1.1.0.post0 prekind --> 1.1.0.dev0 # default: pre part starts at 'dev' postkind --> 1.1.0.dev0.post0 # can add post part to a pre part post --> 1.1.0.dev0.post1 # etc... major --> 2.0.0 prekind --> 2.0.0.dev0 postkind --> 2.0.0.dev0.post0 post --> 2.0.0.dev0.post1