diff --git a/src/GitVersion.Core.Tests/IntegrationTests/FullReleaseGitFlowWithCherryPickWorkaround.cs b/src/GitVersion.Core.Tests/IntegrationTests/FullReleaseGitFlowWithCherryPickWorkaround.cs
new file mode 100644
index 0000000000..20b76f3b5c
--- /dev/null
+++ b/src/GitVersion.Core.Tests/IntegrationTests/FullReleaseGitFlowWithCherryPickWorkaround.cs
@@ -0,0 +1,148 @@
+using GitTools.Testing;
+using GitVersion.Model.Configuration;
+using LibGit2Sharp;
+using NUnit.Framework;
+using Shouldly;
+
+namespace GitVersion.Core.Tests.IntegrationTests;
+
+///
+/// This demonstrates a full cycle to the release of 1.0.0 with the git flow workflow.
+/// Since merging stuff to release/* from develop has a bug then decrements versions on develop
+/// (as shown in the failing test ),
+/// we use cherry picking instead of merging to get stuff from develop to release/*.
+/// For simplicity, we ignore the fact that the develop branch is usually updated via feature/* branches.
+///
+public class FullReleaseGitFlowWithCherryPickWorkaround : IDisposable
+{
+ private readonly EmptyRepositoryFixture _fixture = new();
+
+ private readonly Config _config = new()
+ {
+ // ❓ In my GitVersion.yml I actually have set the version to "1.0"
+ // but that will cause an exception when I do it here in the tests
+ NextVersion = "1.0.0"
+ };
+
+ public void Dispose() => _fixture.Dispose();
+
+ [Test]
+ public void Demonstrate()
+ {
+ // create main and develop branches
+ // develop is one commits ahead of main
+ MakeACommit("Commit 1 (made on main)");
+ CreateAndCheckoutBranch("develop");
+ MakeACommit("Commit 2 (made on develop)");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-alpha.1");
+
+ // now we are ready to start with the preparation of the 1.0.0 release
+ CreateAndCheckoutBranch("release/1.0.0");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.1");
+ GetCurrentSemVer("develop").ShouldBe("1.0.0-alpha.1");
+
+ // make another commit on release/1.0.0 to prepare the actual beta1 release
+ MakeACommit("Commit 3 (made on release/1.0.0)");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.1");
+ GetCurrentSemVer("develop").ShouldBe("1.0.0-alpha.1");
+
+ // now we makes changes on develop that may or may not end up in the 1.0.0 release
+ CheckoutBranch("develop");
+ MakeACommit("Commit 4 (made on develop)");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.1.0-alpha.1");
+ GetCurrentSemVer("release/1.0.0").ShouldBe("1.0.0-beta.1");
+
+ // now we do the actual release of beta 1
+ CheckoutBranch("release/1.0.0");
+ ApplyTag("1.0.0-beta1");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.1");
+ GetCurrentSemVer("develop").ShouldBe("1.1.0-alpha.1");
+
+ // continue with more work on develop that may or may not end up in the 1.0.0 release
+ CheckoutBranch("develop");
+ MakeACommit("Commit 5 (made on develop)");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.1.0-alpha.2");
+ GetCurrentSemVer("release/1.0.0").ShouldBe("1.0.0-beta.1");
+
+ // now we decide that Commit 5 made on develop should be part of the beta 2 release
+ // se we cherry pick it
+ CheckoutBranch("release/1.0.0");
+ CherryPickLatestCommitFromBranch("develop");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.2");
+ GetCurrentSemVer("develop").ShouldBe("1.1.0-alpha.2");
+
+ // now we do an important bugfix that we found while preparing beta 2
+ MakeACommit("Commit 6 (made on release/1.0.0)");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.2");
+ GetCurrentSemVer("develop").ShouldBe("1.1.0-alpha.2");
+
+ // we want everything (Commit 3 and 6) that we made only on release/1.0.0 be in develop
+ CheckoutBranch("develop");
+ MergeWithNoFF("release/1.0.0");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.1.0-alpha.6");
+ GetCurrentSemVer("release/1.0.0").ShouldBe("1.0.0-beta.2");
+
+ // now we do the actual 1.0.0 release
+ CheckoutBranch("release/1.0.0");
+ MakeACommit("Commit 7 (made on release/1.0.0)");
+ CheckoutBranch("main");
+ MergeWithNoFF("release/1.0.0");
+ ApplyTag("1.0.0");
+ CheckoutBranch("develop");
+ MergeWithNoFF("release/1.0.0");
+ DeleteBranch("relesase/1.0.0");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.1.0-alpha.8");
+ GetCurrentSemVer("main").ShouldBe("1.0.0");
+
+
+ }
+
+ private void DeleteBranch(string branch) => _fixture.Repository.Branches.Remove(branch);
+
+ private void MakeACommit(string? message = null) => _fixture.Repository.MakeACommit(message);
+
+ private void CheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.Branches[branchName]);
+
+ private void CreateAndCheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.CreateBranch(branchName));
+
+ private string GetCurrentSemVer(string? branch = null)
+ {
+ if (branch == null)
+ {
+ return _fixture.GetVersion(_config).SemVer;
+ }
+
+ if (_fixture.Repository.Branches.All(b => b.FriendlyName != branch))
+ {
+ throw new InvalidOperationException($"Branch {branch} does not exist");
+ }
+
+ return _fixture.GetVersion(_config, branch: branch).SemVer;
+ }
+
+ private void ApplyTag(string tag) => _fixture.Repository.ApplyTag(tag);
+
+ private void MergeWithNoFF(string sourceBranch) => _fixture.Repository.MergeNoFF(sourceBranch);
+
+ private void CherryPickLatestCommitFromBranch(string sourceBranch) => this._fixture.Repository.CherryPick(this._fixture.Repository.Branches[sourceBranch].Commits.First(), Generate.SignatureNow());
+}
diff --git a/src/GitVersion.Core.Tests/IntegrationTests/GitFlowScenarioFromDiscussion.cs b/src/GitVersion.Core.Tests/IntegrationTests/GitFlowScenarioFromDiscussion.cs
new file mode 100644
index 0000000000..af8a3696aa
--- /dev/null
+++ b/src/GitVersion.Core.Tests/IntegrationTests/GitFlowScenarioFromDiscussion.cs
@@ -0,0 +1,120 @@
+using GitTools.Testing;
+using GitVersion.Model.Configuration;
+using GitVersion.OutputVariables;
+using LibGit2Sharp;
+using NUnit.Framework;
+using Shouldly;
+
+namespace GitVersion.Core.Tests.IntegrationTests;
+
+///
+/// This demonstrates exactly the git flow described in https://github.com/GitTools/GitVersion/discussions/3177
+/// The assertions expect that the version on develop always increments on develop.
+/// The eventually fail (at the end of this test) after we merged develop -> release/1.0.0.
+/// For simplicity, we ignore the fact that the develop branch is usually updated via feature/* branches.
+///
+public class GitFlowScenarioFromDiscussion
+{
+ [Test]
+ public void Demonstrate()
+ {
+ var configuration = new Config
+ {
+ // Settings the below NextVersion results in an exception
+ // I wanted to set it to globally start with version 1
+ // Setting the version to 1.0 (which is not a valid semantic version)
+ // works in GitVersion.yml but not here.
+
+ // NextVersion = "1.0"
+ };
+
+ // create main and develop branch, develop is two commits ahead of main
+ using var fixture = new EmptyRepositoryFixture();
+ fixture.Repository.MakeACommit();
+ Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("develop"));
+ fixture.Repository.MakeACommit();
+ fixture.Repository.MakeACommit();
+
+ var previousVersion = GetSemVer(fixture.GetVersion(configuration));
+
+ // make a 3rd commit on develop
+ fixture.Repository.MakeACommit();
+
+ var currentVersion = GetSemVer(fixture.GetVersion(configuration));
+
+ currentVersion.ShouldBeGreaterThan(previousVersion,
+ "the semver should be incremented after a commit on develop");
+
+ // we are ready to prepare the 1.0.0 release, create and checkout release/1.0.0
+ Commands.Checkout(fixture.Repository, fixture.Repository.CreateBranch("release/1.0.0"));
+
+ fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.1",
+ "the first semver on release/1.0.0 should be beta1");
+
+ // make another commit on release/1.0.0 to prepare the actual beta1 release
+ fixture.Repository.MakeACommit();
+
+ fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.1",
+ "the semver on release/1.0.0 should still be be beta1");
+
+ Commands.Checkout(fixture.Repository, fixture.Repository.Branches["develop"]);
+
+ previousVersion = currentVersion;
+ currentVersion = GetSemVer(fixture.GetVersion(configuration));
+
+ currentVersion.ShouldBe(previousVersion,
+ "the semver on develop should not have changed " +
+ "even when release/1.0.0 has new commits due to beta 1 preparations");
+
+ // now some other team member makes changes on develop that may or may not end up in 1.0.0
+ fixture.Repository.MakeACommit();
+ fixture.Repository.MakeACommit();
+
+ previousVersion = currentVersion;
+ currentVersion = GetSemVer(fixture.GetVersion(configuration));
+
+ currentVersion.ShouldBeGreaterThan(previousVersion,
+ "the semver should be incremented after a even more commit on develop");
+
+ Commands.Checkout(fixture.Repository, fixture.Repository.Branches["release/1.0.0"]);
+
+ // now we release the beta 1
+ fixture.Repository.ApplyTag("1.0.0-beta1");
+
+ fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.1",
+ "the on release/1.0.0 should still be beta1 after the beta 1 tag");
+
+ // continue with more work on develop that may or may not end up in 1.0.0
+ Commands.Checkout(fixture.Repository, fixture.Repository.Branches["develop"]);
+ fixture.Repository.MakeACommit();
+ fixture.Repository.MakeACommit();
+
+ previousVersion = currentVersion;
+ currentVersion = GetSemVer(fixture.GetVersion(configuration));
+
+ currentVersion.ShouldBeGreaterThan(previousVersion,
+ "the semver should be incremented after a even more commit on develop");
+
+ // now we decide that the three commits on develop should be part of the beta 2 release
+ // se we merge it into release/1.0.0 with --no-ff because it is a protected branch
+ Commands.Checkout(fixture.Repository, fixture.Repository.Branches["release/1.0.0"]);
+ fixture.Repository.Merge(
+ fixture.Repository.Branches["develop"],
+ Generate.SignatureNow(),
+ new MergeOptions {FastForwardStrategy = FastForwardStrategy.NoFastForward});
+
+ fixture.GetVersion(configuration).SemVer.ShouldBe("1.0.0-beta.2",
+ "the next semver on release/1.0.0 should be beta2");
+
+ Commands.Checkout(fixture.Repository, fixture.Repository.Branches["develop"]);
+ previousVersion = currentVersion;
+ currentVersion = GetSemVer(fixture.GetVersion(configuration));
+
+ currentVersion.ShouldBeGreaterThanOrEqualTo(previousVersion,
+ "the semver should be incremented (or unchanged) " +
+ "after we merged develop into release/1.0.0");
+
+ static SemanticVersion GetSemVer(VersionVariables ver)
+ => SemanticVersion.Parse(ver.FullSemVer, null);
+ }
+}
diff --git a/src/GitVersion.Core.Tests/IntegrationTests/ReducedGitFlowWithMerge.cs b/src/GitVersion.Core.Tests/IntegrationTests/ReducedGitFlowWithMerge.cs
new file mode 100644
index 0000000000..dd9e14612d
--- /dev/null
+++ b/src/GitVersion.Core.Tests/IntegrationTests/ReducedGitFlowWithMerge.cs
@@ -0,0 +1,108 @@
+using GitTools.Testing;
+using GitVersion.Model.Configuration;
+using LibGit2Sharp;
+using NUnit.Framework;
+using Shouldly;
+
+namespace GitVersion.Core.Tests.IntegrationTests;
+
+///
+/// This demonstrates a bug that decrements the version on develop when we merge stuff from develop to release/*
+/// in the default git flow workflow.
+/// For simplicity, we ignore the fact that the develop branch is usually updated via feature/* branches.
+///
+public class ReducedGitFlowWithMerge : IDisposable
+{
+ private static readonly EmptyRepositoryFixture _fixture = new();
+
+ private static readonly Config _config = new()
+ {
+ // ❓ In my GitVersion.yml I actually have set the version to "1.0"
+ // but that will cause an exception when I do it here in the tests
+ NextVersion = "1.0.0"
+ };
+
+ public void Dispose() => _fixture.Dispose();
+
+ [Test]
+ public void Demonstrate()
+ {
+ // create main and develop branches
+ // develop is one commits ahead of main
+ MakeACommit();
+ CreateAndCheckoutBranch("develop");
+ MakeACommit();
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-alpha.1");
+
+ // now we are ready to start with the preparation of the 1.0.0 release
+ CreateAndCheckoutBranch("release/1.0.0");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.1");
+
+ // make another commit on release/1.0.0 to prepare the actual beta1 release
+ MakeACommit();
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.1");
+
+ // now we makes changes on develop that may or may not end up in the 1.0.0 release
+ CheckoutBranch("develop");
+ MakeACommit();
+
+ // ❌ fails! actual: "1.1.0-alpha.1"
+ // We have not released 1.0.0, not even a beta, so why increment to 1.1.0?
+ // Even though this surprising, it might actually be OK,
+ // at least the version is incremented, which is needed for the CI nuget feed
+ GetCurrentSemVer().ShouldBe("1.0.0-alpha.2");
+
+ // now we do the actual release of beta 1
+ CheckoutBranch("release/1.0.0");
+ ApplyTag("1.0.0-beta1");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.1");
+
+ // continue with more work on develop that may or may not end up in the 1.0.0 release
+ CheckoutBranch("develop");
+ MakeACommit();
+
+ // ❌ fails! actual: "1.1.0-alpha.2"
+ // We still have not finally released 1.0.0 yet, only a beta, so why increment to 1.1.0?
+ // Even though this surprising, it might actually be OK,
+ // at least the version is incremented, which is needed for the CI nuget feed
+ GetCurrentSemVer().ShouldBe("1.0.0-alpha.3");
+
+ // now we decide that the new changes on develop should be part of the beta 2 release
+ // se we merge it into release/1.0.0 with --no-ff because it is a protected branch
+ // but we don't do the release of beta 2 jus yet
+ CheckoutBranch("release/1.0.0");
+ MergeWithNoFF("develop");
+
+ // ✅ succeeds as expected
+ GetCurrentSemVer().ShouldBe("1.0.0-beta.2");
+
+ CheckoutBranch("develop");
+
+ // ❌ fails! actual "1.0.0-alpha.3"
+ // This is now really a problem. Why did it decrement the minor version?
+ // All subsequent changes on develop will now a lower version that previously
+ // and the nuget packages end up on the CI feed with a lower version
+ // so users of that feed would need to _downgrade_ to get a _newer_ version.
+ GetCurrentSemVer().ShouldBe("1.1.0-alpha.2");
+ }
+
+ private static void MakeACommit() => _fixture.Repository.MakeACommit();
+
+ private void CheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.Branches[branchName]);
+
+ private void CreateAndCheckoutBranch(string branchName) => Commands.Checkout(_fixture.Repository, _fixture.Repository.CreateBranch(branchName));
+
+ private string GetCurrentSemVer() => _fixture.GetVersion(_config).SemVer;
+
+ private void ApplyTag(string tag) => _fixture.Repository.ApplyTag(tag);
+
+ private void MergeWithNoFF(string sourceBranch) => _fixture.Repository.MergeNoFF(sourceBranch);
+}