Skip to content

Commit

Permalink
Merge pull request #950 from JakeGinnivan/MainlineDevelopmentMode
Browse files Browse the repository at this point in the history
Mainline development mode
  • Loading branch information
JakeGinnivan authored Jul 20, 2016
2 parents 93f2646 + 03e5f91 commit 22e0342
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 19 deletions.
4 changes: 3 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The global configuration options are:

- **`assembly-informational-format:`** Set this to any of the available [variables](/more-info/variables) to change the value of the `AssemblyInformationalVersion` attribute. Default set to `{InformationalVersion}`. It also supports string interpolation (`{MajorMinorPatch}+{Branch}`)

- **`mode:`** Sets the mode of how GitVersion should create a new version. Can be set to either `ContinuousDelivery` or `ContinuousDeployment`. Read more about [ContinuousDelivery](/reference/continuous-delivery/) or [ContinuousDeployment](/reference/continuous-deployment/).
- **`mode:`** Sets the mode of how GitVersion should create a new version. Read more at [versioning mode](./versioning-mode.md)

- **`continuous-delivery-fallback-tag:`** When using `mode: ContinuousDeployment`, the value specified will be used as the pre-release tag for branches which do not have one specified. Default set to `ci`.

Expand All @@ -36,6 +36,8 @@ The global configuration options are:

- **`patch-version-bump-message:`** The regex to match commit messages with to perform a patch version increment. Default set to `'\+semver:\s?(fix|patch)'`, which will match occurrences of `+semver: fix` and `+semver: patch` in a commit message.

- **`no-bump-message:`** Used to tell GitVersion not to increment when in Mainline development mode. Default `\+semver:\s?(none|skip)`, which will match occurrences of `+semver: none` and `+semver: skip`

- **`legacy-semver-padding:`** The number of characters to pad `LegacySemVer` to in the `LegacySemVerPadded` [variable](/more-info/variables). Is default set to `4`, which will pad the `LegacySemVer` value of `3.0.0-beta1` to `3.0.0-beta0001`.

- **`build-metadata-padding:`** The number of characters to pad `BuildMetaData` to in the `BuildMetaDataPadded` [variable](/more-info/variables). Is default set to `4`, which will pad the `BuildMetaData` value of `1` to `0001`.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/mainline-mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions docs/versioning-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Versioning modes
GitVersion has multiple modes to fit different different ways of working.


## Continuous Delivery
This is the default mode, GitVersion calculates the next version and will use that until that is released. For instance:

- 1.1.0+5
- 1.1.0+6
- 1.1.0+7 <-- This is the artifact we release, tag the commit which created this version
- 1.1.1+0

Tags are required in this mode to communicate when the release is done as it's an external manual process.

## Continuous deployment
Sometimes you just want the version to keep changing and continuously deploy. A good case for this is when using Octopus deploy, as you cannot publish the same version of a package into the same feed.

For this mode we followed the logic in this blog post by Xavier Decoster on the issues of incrementing automatically - http://www.xavierdecoster.com/semantic-versioning-auto-incremented-nuget-package-versions

As such we force a pre-release tag on all branches, this is fine for applications but can cause problems for libraries. As such this mode may or may not work for you, which leads us into a new mode in v4. Mainline development.

## Mainline development
Mainline development works more like the Continuous Delivery mode, except that it tells GitVersion to *infer* releases from merges and commits to `master`.

This mode is great if you do not want to tag each release because you simply deploy every commit to master. The behaviour of this mode is as follows:

1. Calclate a base version (likely a tag in this mode)
1. Walk all commits from the base version commit
1. When a merge commit is found:
- Calculate increments for each direct commit on master
- Calculate the increment for the branch
1. Calculate increments for each remaining direct commit
1. For feature branches then calculate increment for the commits so far on your feature branch.

If you *do not want* GitVersion to treat a commit or a pull request as a release and increment the version you can use `+semver: none` or `+semver: skip` in a commit message to skip incrementing for that commit.

Here is an example of what mainline development looks like:

![Mainline mode](./img/mainline-mode.png)


**WARNING:** This approach can slow down over time, we recommend to tag intermitently (maybe for minor or major releases) because then GitVersion will start the version calculation from that point. Much like a snapshot in an event sourced system. We will probably add in warnings to tag when things are slowing down.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ continuous-delivery-fallback-tag: ci
major-version-bump-message: '\+semver:\s?(breaking|major)'
minor-version-bump-message: '\+semver:\s?(feature|minor)'
patch-version-bump-message: '\+semver:\s?(fix|patch)'
no-bump-message: '\+semver:\s?(none|skip)'
legacy-semver-padding: 4
build-metadata-padding: 4
commits-since-version-source-padding: 4
Expand Down
1 change: 1 addition & 0 deletions src/GitVersionCore.Tests/GitVersionCore.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
<Compile Include="ConfigProviderTests.cs" />
<Compile Include="GitVersionContextTests.cs" />
<Compile Include="Helpers\DirectoryHelper.cs" />
<Compile Include="IntegrationTests\MainlineDevelopmentMode.cs" />
<Compile Include="Mocks\MockThreadSleep.cs" />
<Compile Include="OperationWithExponentialBackoffTests.cs" />
<Compile Include="Init\InitScenarios.cs" />
Expand Down
105 changes: 105 additions & 0 deletions src/GitVersionCore.Tests/IntegrationTests/MainlineDevelopmentMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using System.Reflection;
using System.Text;
using GitTools.Testing;
using GitVersion;
using GitVersionCore.Tests;
using LibGit2Sharp;
using NUnit.Framework;

public class MainlineDevelopmentMode
{
private Config config = new Config
{
VersioningMode = VersioningMode.Mainline
};

[Test]
public void MergedFeatureBranchesToMasterImpliesRelease()
{
using (var fixture = new EmptyRepositoryFixture())
{
fixture.Repository.MakeACommit("1");
fixture.MakeATaggedCommit("1.0.0");

fixture.BranchTo("feature/foo", "foo");
fixture.MakeACommit("2");
fixture.AssertFullSemver(config, "1.0.1-foo.1+1");
fixture.Checkout("master");
fixture.MergeNoFF("feature/foo");

fixture.AssertFullSemver(config, "1.0.1+2");

fixture.BranchTo("feature/foo2", "foo2");
fixture.MakeACommit("3 +semver: minor");
fixture.AssertFullSemver(config, "1.1.0-foo2.1+3");
fixture.Checkout("master");
fixture.MergeNoFF("feature/foo2");
fixture.AssertFullSemver(config, "1.1.0+4");

fixture.BranchTo("feature/foo3", "foo3");
fixture.MakeACommit("4");
fixture.Checkout("master");
fixture.MergeNoFF("feature/foo3");
fixture.SequenceDiagram.NoteOver("Merge message contains '+semver: minor'", "master");
var commit = fixture.Repository.Head.Tip;
// Put semver increment in merge message
fixture.Repository.Commit(commit.Message + " +semver: minor", commit.Author, commit.Committer, new CommitOptions
{
AmendPreviousCommit = true
});
fixture.AssertFullSemver(config, "1.2.0+6");

fixture.BranchTo("feature/foo4", "foo4");
fixture.MakeACommit("5 +semver: major");
fixture.AssertFullSemver(config, "2.0.0-foo4.1+7");
fixture.Checkout("master");
fixture.MergeNoFF("feature/foo4");
fixture.AssertFullSemver(config, "2.0.0+8");

// We should evaluate any commits not included in merge commit calculations for direct commit/push or squash to merge commits
fixture.MakeACommit("6 +semver: major");
fixture.AssertFullSemver(config, "3.0.0+9");
fixture.MakeACommit("7 +semver: minor");
fixture.AssertFullSemver(config, "3.1.0+10");
fixture.MakeACommit("8");
fixture.AssertFullSemver(config, "3.1.1+11");

// Finally verify that the merge commits still function properly
fixture.BranchTo("feature/foo5", "foo5");
fixture.MakeACommit("9 +semver: minor");
fixture.AssertFullSemver(config, "3.2.0-foo5.1+12");
fixture.Checkout("master");
fixture.MergeNoFF("feature/foo5");
fixture.AssertFullSemver(config, "3.2.0+13");

// One more direct commit for good measure
fixture.MakeACommit("10 +semver: minor");
fixture.AssertFullSemver(config, "3.3.0+14");
// And we can commit without bumping semver
fixture.MakeACommit("11 +semver: none");
fixture.AssertFullSemver(config, "3.3.0+15");
Console.WriteLine(fixture.SequenceDiagram.GetDiagram());
}
}
// Write test which has a forward merge into a feature branch
}

static class CommitExtensions
{
public static void MakeACommit(this RepositoryFixtureBase fixture, string commitMsg)
{
fixture.Repository.MakeACommit(commitMsg);
var diagramBuilder = (StringBuilder)typeof(SequenceDiagram)
.GetField("_diagramBuilder", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(fixture.SequenceDiagram);
Func<string, string> getParticipant = participant => (string)typeof(SequenceDiagram)
.GetMethod("GetParticipant", BindingFlags.Instance | BindingFlags.NonPublic)
.Invoke(fixture.SequenceDiagram, new object[]
{
participant
});
diagramBuilder.AppendLineFormat("{0} -> {0}: Commit '{1}'", getParticipant(fixture.Repository.Head.FriendlyName),
commitMsg);
}
}
3 changes: 2 additions & 1 deletion src/GitVersionCore.Tests/TestEffectiveConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public TestEffectiveConfiguration(
string majorMessage = null,
string minorMessage = null,
string patchMessage = null,
string noBumpMessage = null,
CommitMessageIncrementMode commitMessageMode = CommitMessageIncrementMode.Enabled,
int legacySemVerPadding = 4,
int buildMetaDataPadding = 4,
Expand All @@ -32,7 +33,7 @@ public TestEffectiveConfiguration(
base(assemblyVersioningScheme, assemblyInformationalFormat, versioningMode, gitTagPrefix, tag, nextVersion, IncrementStrategy.Patch,
branchPrefixToTrim, preventIncrementForMergedBranchVersion, tagNumberPattern, continuousDeploymentFallbackTag,
trackMergeTarget,
majorMessage, minorMessage, patchMessage,
majorMessage, minorMessage, patchMessage, noBumpMessage,
commitMessageMode, legacySemVerPadding, buildMetaDataPadding, commitsSinceVersionSourcePadding,
versionFilters ?? Enumerable.Empty<IVersionFilter>(),
isDevelop, isRelease)
Expand Down
3 changes: 3 additions & 0 deletions src/GitVersionCore/Configuration/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public string NextVersion
[YamlMember(Alias = "patch-version-bump-message")]
public string PatchVersionBumpMessage { get; set; }

[YamlMember(Alias = "no-bump-message")]
public string NoBumpMessage { get; set; }

[YamlMember(Alias = "legacy-semver-padding")]
public int? LegacySemVerPadding { get; set; }

Expand Down
1 change: 1 addition & 0 deletions src/GitVersionCore/Configuration/ConfigurationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public static void ApplyDefaultsTo(Config config)
config.MajorVersionBumpMessage = config.MajorVersionBumpMessage ?? IncrementStrategyFinder.DefaultMajorPattern;
config.MinorVersionBumpMessage = config.MinorVersionBumpMessage ?? IncrementStrategyFinder.DefaultMinorPattern;
config.PatchVersionBumpMessage = config.PatchVersionBumpMessage ?? IncrementStrategyFinder.DefaultPatchPattern;
config.NoBumpMessage = config.NoBumpMessage ?? IncrementStrategyFinder.DefaultNoBumpPattern;
config.CommitMessageIncrementing = config.CommitMessageIncrementing ?? CommitMessageIncrementMode.Enabled;
config.LegacySemVerPadding = config.LegacySemVerPadding ?? 4;
config.BuildMetaDataPadding = config.BuildMetaDataPadding ?? 4;
Expand Down
3 changes: 3 additions & 0 deletions src/GitVersionCore/EffectiveConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public EffectiveConfiguration(
string majorVersionBumpMessage,
string minorVersionBumpMessage,
string patchVersionBumpMessage,
string noBumpMessage,
CommitMessageIncrementMode commitMessageIncrementing,
int legacySemVerPaddding,
int buildMetaDataPadding,
Expand All @@ -44,6 +45,7 @@ public EffectiveConfiguration(
MajorVersionBumpMessage = majorVersionBumpMessage;
MinorVersionBumpMessage = minorVersionBumpMessage;
PatchVersionBumpMessage = patchVersionBumpMessage;
NoBumpMessage = noBumpMessage;
CommitMessageIncrementing = commitMessageIncrementing;
LegacySemVerPadding = legacySemVerPaddding;
BuildMetaDataPadding = buildMetaDataPadding;
Expand Down Expand Up @@ -91,6 +93,7 @@ public EffectiveConfiguration(

public string PatchVersionBumpMessage { get; private set; }

public string NoBumpMessage { get; private set; }
public int LegacySemVerPadding { get; private set; }
public int BuildMetaDataPadding { get; private set; }

Expand Down
3 changes: 2 additions & 1 deletion src/GitVersionCore/GitVersionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ void CalculateEffectiveConfiguration()
var majorMessage = FullConfiguration.MajorVersionBumpMessage;
var minorMessage = FullConfiguration.MinorVersionBumpMessage;
var patchMessage = FullConfiguration.PatchVersionBumpMessage;
var noBumpMessage = FullConfiguration.NoBumpMessage;

var commitMessageVersionBump = currentBranchConfig.Value.CommitMessageIncrementing ?? FullConfiguration.CommitMessageIncrementing.Value;

Expand All @@ -132,7 +133,7 @@ void CalculateEffectiveConfiguration()
preventIncrementForMergedBranchVersion,
tagNumberPattern, FullConfiguration.ContinuousDeploymentFallbackTag,
trackMergeTarget,
majorMessage, minorMessage, patchMessage,
majorMessage, minorMessage, patchMessage, noBumpMessage,
commitMessageVersionBump,
FullConfiguration.LegacySemVerPadding.Value,
FullConfiguration.BuildMetaDataPadding.Value,
Expand Down
14 changes: 11 additions & 3 deletions src/GitVersionCore/IncrementStrategyFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public static class IncrementStrategyFinder
public const string DefaultMajorPattern = @"\+semver:\s?(breaking|major)";
public const string DefaultMinorPattern = @"\+semver:\s?(feature|minor)";
public const string DefaultPatchPattern = @"\+semver:\s?(fix|patch)";
public const string DefaultNoBumpPattern = @"\+semver:\s?(none|skip)";

public static VersionField? DetermineIncrementedField(GitVersionContext context, BaseVersion baseVersion)
{
Expand Down Expand Up @@ -61,12 +62,18 @@ public static class IncrementStrategyFinder
commits = commits.Where(c => c.Parents.Count() > 1);
}

return GetIncrementForCommits(context, commits);
}

public static VersionField? GetIncrementForCommits(GitVersionContext context, IEnumerable<Commit> commits)
{
var majorRegex = CreateRegex(context.Configuration.MajorVersionBumpMessage ?? DefaultMajorPattern);
var minorRegex = CreateRegex(context.Configuration.MinorVersionBumpMessage ?? DefaultMinorPattern);
var patchRegex = CreateRegex(context.Configuration.PatchVersionBumpMessage ?? DefaultPatchPattern);
var none = CreateRegex(context.Configuration.NoBumpMessage ?? DefaultNoBumpPattern);

var increments = commits
.Select(c => FindIncrementFromMessage(c.Message, majorRegex, minorRegex, patchRegex))
.Select(c => FindIncrementFromMessage(c.Message, majorRegex, minorRegex, patchRegex, none))
.Where(v => v != null)
.Select(v => v.Value)
.ToList();
Expand All @@ -78,7 +85,7 @@ public static class IncrementStrategyFinder

return null;
}

private static IEnumerable<Commit> GetIntermediateCommits(IRepository repo, Commit baseCommit, Commit headCommit)
{
if (baseCommit == null) yield break;
Expand All @@ -105,11 +112,12 @@ private static IEnumerable<Commit> GetIntermediateCommits(IRepository repo, Comm
}
}

private static VersionField? FindIncrementFromMessage(string message, Regex major, Regex minor, Regex patch)
private static VersionField? FindIncrementFromMessage(string message, Regex major, Regex minor, Regex patch, Regex none)
{
if (major.IsMatch(message)) return VersionField.Major;
if (minor.IsMatch(message)) return VersionField.Minor;
if (patch.IsMatch(message)) return VersionField.Patch;
if (none.IsMatch(message)) return VersionField.None;

return null;
}
Expand Down
9 changes: 5 additions & 4 deletions src/GitVersionCore/LibGitExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,16 @@ public static Commit FindMergeBase(this Branch branch, Branch otherBranch, IRepo
{
// Otherbranch tip is a forward merge
var commitToFindCommonBase = otherBranch.Tip;
if (otherBranch.Tip.Parents.Contains(branch.Tip))
var commit = branch.Tip;
if (otherBranch.Tip.Parents.Contains(commit))
{
commitToFindCommonBase = otherBranch.Tip.Parents.First();
}

var findMergeBase = repository.ObjectDatabase.FindMergeBase(branch.Tip, commitToFindCommonBase);
var findMergeBase = repository.ObjectDatabase.FindMergeBase(commit, commitToFindCommonBase);
if (findMergeBase != null)
{
Logger.WriteInfo(string.Format("Found merge base of {0} against {1}", findMergeBase.Sha, otherBranch.FriendlyName));
Logger.WriteInfo(string.Format("Found merge base of {0}", findMergeBase.Sha));
// We do not want to include merge base commits which got forward merged into the other branch
bool mergeBaseWasFowardMerge;
do
Expand All @@ -106,7 +107,7 @@ public static Commit FindMergeBase(this Branch branch, Branch otherBranch, IRepo
if (mergeBaseWasFowardMerge)
{
var second = commitToFindCommonBase.Parents.First();
var mergeBase = repository.ObjectDatabase.FindMergeBase(branch.Tip, second);
var mergeBase = repository.ObjectDatabase.FindMergeBase(commit, second);
if (mergeBase == findMergeBase)
{
break;
Expand Down
Loading

0 comments on commit 22e0342

Please sign in to comment.