Skip to content

Commit 22e0342

Browse files
authored
Merge pull request #950 from JakeGinnivan/MainlineDevelopmentMode
Mainline development mode
2 parents 93f2646 + 03e5f91 commit 22e0342

16 files changed

+300
-19
lines changed

docs/configuration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The global configuration options are:
2424

2525
- **`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}`)
2626

27-
- **`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/).
27+
- **`mode:`** Sets the mode of how GitVersion should create a new version. Read more at [versioning mode](./versioning-mode.md)
2828

2929
- **`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`.
3030

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

3737
- **`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.
3838

39+
- **`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`
40+
3941
- **`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`.
4042

4143
- **`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`.
Loading

docs/img/mainline-mode.png

69.5 KB
Loading

docs/versioning-mode.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Versioning modes
2+
GitVersion has multiple modes to fit different different ways of working.
3+
4+
5+
## Continuous Delivery
6+
This is the default mode, GitVersion calculates the next version and will use that until that is released. For instance:
7+
8+
- 1.1.0+5
9+
- 1.1.0+6
10+
- 1.1.0+7 <-- This is the artifact we release, tag the commit which created this version
11+
- 1.1.1+0
12+
13+
Tags are required in this mode to communicate when the release is done as it's an external manual process.
14+
15+
## Continuous deployment
16+
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.
17+
18+
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
19+
20+
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.
21+
22+
## Mainline development
23+
Mainline development works more like the Continuous Delivery mode, except that it tells GitVersion to *infer* releases from merges and commits to `master`.
24+
25+
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:
26+
27+
1. Calclate a base version (likely a tag in this mode)
28+
1. Walk all commits from the base version commit
29+
1. When a merge commit is found:
30+
- Calculate increments for each direct commit on master
31+
- Calculate the increment for the branch
32+
1. Calculate increments for each remaining direct commit
33+
1. For feature branches then calculate increment for the commits so far on your feature branch.
34+
35+
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.
36+
37+
Here is an example of what mainline development looks like:
38+
39+
![Mainline mode](./img/mainline-mode.png)
40+
41+
42+
**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.

src/GitVersionCore.Tests/ConfigProviderTests.CanWriteOutEffectiveConfiguration.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ continuous-delivery-fallback-tag: ci
55
major-version-bump-message: '\+semver:\s?(breaking|major)'
66
minor-version-bump-message: '\+semver:\s?(feature|minor)'
77
patch-version-bump-message: '\+semver:\s?(fix|patch)'
8+
no-bump-message: '\+semver:\s?(none|skip)'
89
legacy-semver-padding: 4
910
build-metadata-padding: 4
1011
commits-since-version-source-padding: 4

src/GitVersionCore.Tests/GitVersionCore.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
<Compile Include="ConfigProviderTests.cs" />
114114
<Compile Include="GitVersionContextTests.cs" />
115115
<Compile Include="Helpers\DirectoryHelper.cs" />
116+
<Compile Include="IntegrationTests\MainlineDevelopmentMode.cs" />
116117
<Compile Include="Mocks\MockThreadSleep.cs" />
117118
<Compile Include="OperationWithExponentialBackoffTests.cs" />
118119
<Compile Include="Init\InitScenarios.cs" />
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
using System.Reflection;
3+
using System.Text;
4+
using GitTools.Testing;
5+
using GitVersion;
6+
using GitVersionCore.Tests;
7+
using LibGit2Sharp;
8+
using NUnit.Framework;
9+
10+
public class MainlineDevelopmentMode
11+
{
12+
private Config config = new Config
13+
{
14+
VersioningMode = VersioningMode.Mainline
15+
};
16+
17+
[Test]
18+
public void MergedFeatureBranchesToMasterImpliesRelease()
19+
{
20+
using (var fixture = new EmptyRepositoryFixture())
21+
{
22+
fixture.Repository.MakeACommit("1");
23+
fixture.MakeATaggedCommit("1.0.0");
24+
25+
fixture.BranchTo("feature/foo", "foo");
26+
fixture.MakeACommit("2");
27+
fixture.AssertFullSemver(config, "1.0.1-foo.1+1");
28+
fixture.Checkout("master");
29+
fixture.MergeNoFF("feature/foo");
30+
31+
fixture.AssertFullSemver(config, "1.0.1+2");
32+
33+
fixture.BranchTo("feature/foo2", "foo2");
34+
fixture.MakeACommit("3 +semver: minor");
35+
fixture.AssertFullSemver(config, "1.1.0-foo2.1+3");
36+
fixture.Checkout("master");
37+
fixture.MergeNoFF("feature/foo2");
38+
fixture.AssertFullSemver(config, "1.1.0+4");
39+
40+
fixture.BranchTo("feature/foo3", "foo3");
41+
fixture.MakeACommit("4");
42+
fixture.Checkout("master");
43+
fixture.MergeNoFF("feature/foo3");
44+
fixture.SequenceDiagram.NoteOver("Merge message contains '+semver: minor'", "master");
45+
var commit = fixture.Repository.Head.Tip;
46+
// Put semver increment in merge message
47+
fixture.Repository.Commit(commit.Message + " +semver: minor", commit.Author, commit.Committer, new CommitOptions
48+
{
49+
AmendPreviousCommit = true
50+
});
51+
fixture.AssertFullSemver(config, "1.2.0+6");
52+
53+
fixture.BranchTo("feature/foo4", "foo4");
54+
fixture.MakeACommit("5 +semver: major");
55+
fixture.AssertFullSemver(config, "2.0.0-foo4.1+7");
56+
fixture.Checkout("master");
57+
fixture.MergeNoFF("feature/foo4");
58+
fixture.AssertFullSemver(config, "2.0.0+8");
59+
60+
// We should evaluate any commits not included in merge commit calculations for direct commit/push or squash to merge commits
61+
fixture.MakeACommit("6 +semver: major");
62+
fixture.AssertFullSemver(config, "3.0.0+9");
63+
fixture.MakeACommit("7 +semver: minor");
64+
fixture.AssertFullSemver(config, "3.1.0+10");
65+
fixture.MakeACommit("8");
66+
fixture.AssertFullSemver(config, "3.1.1+11");
67+
68+
// Finally verify that the merge commits still function properly
69+
fixture.BranchTo("feature/foo5", "foo5");
70+
fixture.MakeACommit("9 +semver: minor");
71+
fixture.AssertFullSemver(config, "3.2.0-foo5.1+12");
72+
fixture.Checkout("master");
73+
fixture.MergeNoFF("feature/foo5");
74+
fixture.AssertFullSemver(config, "3.2.0+13");
75+
76+
// One more direct commit for good measure
77+
fixture.MakeACommit("10 +semver: minor");
78+
fixture.AssertFullSemver(config, "3.3.0+14");
79+
// And we can commit without bumping semver
80+
fixture.MakeACommit("11 +semver: none");
81+
fixture.AssertFullSemver(config, "3.3.0+15");
82+
Console.WriteLine(fixture.SequenceDiagram.GetDiagram());
83+
}
84+
}
85+
// Write test which has a forward merge into a feature branch
86+
}
87+
88+
static class CommitExtensions
89+
{
90+
public static void MakeACommit(this RepositoryFixtureBase fixture, string commitMsg)
91+
{
92+
fixture.Repository.MakeACommit(commitMsg);
93+
var diagramBuilder = (StringBuilder)typeof(SequenceDiagram)
94+
.GetField("_diagramBuilder", BindingFlags.Instance | BindingFlags.NonPublic)
95+
.GetValue(fixture.SequenceDiagram);
96+
Func<string, string> getParticipant = participant => (string)typeof(SequenceDiagram)
97+
.GetMethod("GetParticipant", BindingFlags.Instance | BindingFlags.NonPublic)
98+
.Invoke(fixture.SequenceDiagram, new object[]
99+
{
100+
participant
101+
});
102+
diagramBuilder.AppendLineFormat("{0} -> {0}: Commit '{1}'", getParticipant(fixture.Repository.Head.FriendlyName),
103+
commitMsg);
104+
}
105+
}

src/GitVersionCore.Tests/TestEffectiveConfiguration.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public TestEffectiveConfiguration(
2222
string majorMessage = null,
2323
string minorMessage = null,
2424
string patchMessage = null,
25+
string noBumpMessage = null,
2526
CommitMessageIncrementMode commitMessageMode = CommitMessageIncrementMode.Enabled,
2627
int legacySemVerPadding = 4,
2728
int buildMetaDataPadding = 4,
@@ -32,7 +33,7 @@ public TestEffectiveConfiguration(
3233
base(assemblyVersioningScheme, assemblyInformationalFormat, versioningMode, gitTagPrefix, tag, nextVersion, IncrementStrategy.Patch,
3334
branchPrefixToTrim, preventIncrementForMergedBranchVersion, tagNumberPattern, continuousDeploymentFallbackTag,
3435
trackMergeTarget,
35-
majorMessage, minorMessage, patchMessage,
36+
majorMessage, minorMessage, patchMessage, noBumpMessage,
3637
commitMessageMode, legacySemVerPadding, buildMetaDataPadding, commitsSinceVersionSourcePadding,
3738
versionFilters ?? Enumerable.Empty<IVersionFilter>(),
3839
isDevelop, isRelease)

src/GitVersionCore/Configuration/Config.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public string NextVersion
5252
[YamlMember(Alias = "patch-version-bump-message")]
5353
public string PatchVersionBumpMessage { get; set; }
5454

55+
[YamlMember(Alias = "no-bump-message")]
56+
public string NoBumpMessage { get; set; }
57+
5558
[YamlMember(Alias = "legacy-semver-padding")]
5659
public int? LegacySemVerPadding { get; set; }
5760

src/GitVersionCore/Configuration/ConfigurationProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public static void ApplyDefaultsTo(Config config)
6262
config.MajorVersionBumpMessage = config.MajorVersionBumpMessage ?? IncrementStrategyFinder.DefaultMajorPattern;
6363
config.MinorVersionBumpMessage = config.MinorVersionBumpMessage ?? IncrementStrategyFinder.DefaultMinorPattern;
6464
config.PatchVersionBumpMessage = config.PatchVersionBumpMessage ?? IncrementStrategyFinder.DefaultPatchPattern;
65+
config.NoBumpMessage = config.NoBumpMessage ?? IncrementStrategyFinder.DefaultNoBumpPattern;
6566
config.CommitMessageIncrementing = config.CommitMessageIncrementing ?? CommitMessageIncrementMode.Enabled;
6667
config.LegacySemVerPadding = config.LegacySemVerPadding ?? 4;
6768
config.BuildMetaDataPadding = config.BuildMetaDataPadding ?? 4;

src/GitVersionCore/EffectiveConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public EffectiveConfiguration(
2121
string majorVersionBumpMessage,
2222
string minorVersionBumpMessage,
2323
string patchVersionBumpMessage,
24+
string noBumpMessage,
2425
CommitMessageIncrementMode commitMessageIncrementing,
2526
int legacySemVerPaddding,
2627
int buildMetaDataPadding,
@@ -44,6 +45,7 @@ public EffectiveConfiguration(
4445
MajorVersionBumpMessage = majorVersionBumpMessage;
4546
MinorVersionBumpMessage = minorVersionBumpMessage;
4647
PatchVersionBumpMessage = patchVersionBumpMessage;
48+
NoBumpMessage = noBumpMessage;
4749
CommitMessageIncrementing = commitMessageIncrementing;
4850
LegacySemVerPadding = legacySemVerPaddding;
4951
BuildMetaDataPadding = buildMetaDataPadding;
@@ -91,6 +93,7 @@ public EffectiveConfiguration(
9193

9294
public string PatchVersionBumpMessage { get; private set; }
9395

96+
public string NoBumpMessage { get; private set; }
9497
public int LegacySemVerPadding { get; private set; }
9598
public int BuildMetaDataPadding { get; private set; }
9699

src/GitVersionCore/GitVersionContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ void CalculateEffectiveConfiguration()
123123
var majorMessage = FullConfiguration.MajorVersionBumpMessage;
124124
var minorMessage = FullConfiguration.MinorVersionBumpMessage;
125125
var patchMessage = FullConfiguration.PatchVersionBumpMessage;
126+
var noBumpMessage = FullConfiguration.NoBumpMessage;
126127

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

@@ -132,7 +133,7 @@ void CalculateEffectiveConfiguration()
132133
preventIncrementForMergedBranchVersion,
133134
tagNumberPattern, FullConfiguration.ContinuousDeploymentFallbackTag,
134135
trackMergeTarget,
135-
majorMessage, minorMessage, patchMessage,
136+
majorMessage, minorMessage, patchMessage, noBumpMessage,
136137
commitMessageVersionBump,
137138
FullConfiguration.LegacySemVerPadding.Value,
138139
FullConfiguration.BuildMetaDataPadding.Value,

src/GitVersionCore/IncrementStrategyFinder.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static class IncrementStrategyFinder
1919
public const string DefaultMajorPattern = @"\+semver:\s?(breaking|major)";
2020
public const string DefaultMinorPattern = @"\+semver:\s?(feature|minor)";
2121
public const string DefaultPatchPattern = @"\+semver:\s?(fix|patch)";
22+
public const string DefaultNoBumpPattern = @"\+semver:\s?(none|skip)";
2223

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

65+
return GetIncrementForCommits(context, commits);
66+
}
67+
68+
public static VersionField? GetIncrementForCommits(GitVersionContext context, IEnumerable<Commit> commits)
69+
{
6470
var majorRegex = CreateRegex(context.Configuration.MajorVersionBumpMessage ?? DefaultMajorPattern);
6571
var minorRegex = CreateRegex(context.Configuration.MinorVersionBumpMessage ?? DefaultMinorPattern);
6672
var patchRegex = CreateRegex(context.Configuration.PatchVersionBumpMessage ?? DefaultPatchPattern);
73+
var none = CreateRegex(context.Configuration.NoBumpMessage ?? DefaultNoBumpPattern);
6774

6875
var increments = commits
69-
.Select(c => FindIncrementFromMessage(c.Message, majorRegex, minorRegex, patchRegex))
76+
.Select(c => FindIncrementFromMessage(c.Message, majorRegex, minorRegex, patchRegex, none))
7077
.Where(v => v != null)
7178
.Select(v => v.Value)
7279
.ToList();
@@ -78,7 +85,7 @@ public static class IncrementStrategyFinder
7885

7986
return null;
8087
}
81-
88+
8289
private static IEnumerable<Commit> GetIntermediateCommits(IRepository repo, Commit baseCommit, Commit headCommit)
8390
{
8491
if (baseCommit == null) yield break;
@@ -105,11 +112,12 @@ private static IEnumerable<Commit> GetIntermediateCommits(IRepository repo, Comm
105112
}
106113
}
107114

108-
private static VersionField? FindIncrementFromMessage(string message, Regex major, Regex minor, Regex patch)
115+
private static VersionField? FindIncrementFromMessage(string message, Regex major, Regex minor, Regex patch, Regex none)
109116
{
110117
if (major.IsMatch(message)) return VersionField.Major;
111118
if (minor.IsMatch(message)) return VersionField.Minor;
112119
if (patch.IsMatch(message)) return VersionField.Patch;
120+
if (none.IsMatch(message)) return VersionField.None;
113121

114122
return null;
115123
}

src/GitVersionCore/LibGitExtensions.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,16 @@ public static Commit FindMergeBase(this Branch branch, Branch otherBranch, IRepo
8585
{
8686
// Otherbranch tip is a forward merge
8787
var commitToFindCommonBase = otherBranch.Tip;
88-
if (otherBranch.Tip.Parents.Contains(branch.Tip))
88+
var commit = branch.Tip;
89+
if (otherBranch.Tip.Parents.Contains(commit))
8990
{
9091
commitToFindCommonBase = otherBranch.Tip.Parents.First();
9192
}
9293

93-
var findMergeBase = repository.ObjectDatabase.FindMergeBase(branch.Tip, commitToFindCommonBase);
94+
var findMergeBase = repository.ObjectDatabase.FindMergeBase(commit, commitToFindCommonBase);
9495
if (findMergeBase != null)
9596
{
96-
Logger.WriteInfo(string.Format("Found merge base of {0} against {1}", findMergeBase.Sha, otherBranch.FriendlyName));
97+
Logger.WriteInfo(string.Format("Found merge base of {0}", findMergeBase.Sha));
9798
// We do not want to include merge base commits which got forward merged into the other branch
9899
bool mergeBaseWasFowardMerge;
99100
do
@@ -106,7 +107,7 @@ public static Commit FindMergeBase(this Branch branch, Branch otherBranch, IRepo
106107
if (mergeBaseWasFowardMerge)
107108
{
108109
var second = commitToFindCommonBase.Parents.First();
109-
var mergeBase = repository.ObjectDatabase.FindMergeBase(branch.Tip, second);
110+
var mergeBase = repository.ObjectDatabase.FindMergeBase(commit, second);
110111
if (mergeBase == findMergeBase)
111112
{
112113
break;

0 commit comments

Comments
 (0)