Skip to content

Commit 5265fad

Browse files
authored
Merge pull request #1005 from JakeGinnivan/MainlineFixes
Mainline fixes
2 parents 68981b1 + 06e4e84 commit 5265fad

File tree

7 files changed

+142
-28
lines changed

7 files changed

+142
-28
lines changed

docs/configuration.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,4 +256,7 @@ branch. For example `develop` → `release/1.0.0` → merge into `master` and ta
256256
Indicates this branch config represents develop in GitFlow.
257257

258258
### is-release-branch
259-
Indicates this branch config represents a release branch in GitFlow.
259+
Indicates this branch config represents a release branch in GitFlow.
260+
261+
### is-mainline
262+
When using Mainline mode, this indicates that this branch is a mainline. By default support/ and master are mainlines.

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ branches:
1919
track-merge-target: false
2020
is-develop: false
2121
is-release-branch: false
22+
is-mainline: true
2223
releases?[/-]:
2324
mode: ContinuousDelivery
2425
tag: beta
@@ -27,6 +28,7 @@ branches:
2728
track-merge-target: false
2829
is-develop: false
2930
is-release-branch: true
31+
is-mainline: false
3032
features?[/-]:
3133
mode: ContinuousDelivery
3234
tag: useBranchName
@@ -35,6 +37,7 @@ branches:
3537
track-merge-target: false
3638
is-develop: false
3739
is-release-branch: false
40+
is-mainline: false
3841
(pull|pull\-requests|pr)[/-]:
3942
mode: ContinuousDelivery
4043
tag: PullRequest
@@ -44,6 +47,7 @@ branches:
4447
track-merge-target: false
4548
is-develop: false
4649
is-release-branch: false
50+
is-mainline: false
4751
hotfix(es)?[/-]:
4852
mode: ContinuousDelivery
4953
tag: beta
@@ -52,6 +56,7 @@ branches:
5256
track-merge-target: false
5357
is-develop: false
5458
is-release-branch: false
59+
is-mainline: false
5560
support[/-]:
5661
mode: ContinuousDelivery
5762
tag: ''
@@ -60,6 +65,7 @@ branches:
6065
track-merge-target: false
6166
is-develop: false
6267
is-release-branch: false
68+
is-mainline: true
6369
dev(elop)?(ment)?$:
6470
mode: ContinuousDeployment
6571
tag: alpha
@@ -68,5 +74,6 @@ branches:
6874
track-merge-target: true
6975
is-develop: true
7076
is-release-branch: false
77+
is-mainline: false
7178
ignore:
7279
sha: []

src/GitVersionCore.Tests/IntegrationTests/MainlineDevelopmentMode.cs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using GitVersionCore.Tests;
77
using LibGit2Sharp;
88
using NUnit.Framework;
9-
using LibGitExtensions = GitTools.LibGitExtensions;
109

1110
public class MainlineDevelopmentMode
1211
{
@@ -105,7 +104,41 @@ public void VerifyPullRequestsActLikeContinuousDelivery()
105104
}
106105

107106
[Test]
108-
[Ignore("Not passing yet")]
107+
public void SupportBranches()
108+
{
109+
using (var fixture = new EmptyRepositoryFixture())
110+
{
111+
fixture.Repository.MakeACommit("1");
112+
fixture.MakeATaggedCommit("1.0.0");
113+
fixture.MakeACommit(); // 1.0.1
114+
fixture.MakeACommit(); // 1.0.2
115+
fixture.AssertFullSemver(config, "1.0.2");
116+
117+
fixture.BranchTo("support/1.0", "support10");
118+
fixture.AssertFullSemver(config, "1.0.3");
119+
120+
// Move master on
121+
fixture.Checkout("master");
122+
fixture.MakeACommit("+semver: major"); // 2.0.0 (on master)
123+
fixture.AssertFullSemver(config, "2.0.0");
124+
125+
// Continue on support/1.0
126+
fixture.Checkout("support/1.0");
127+
fixture.MakeACommit(); // 1.0.4
128+
fixture.MakeACommit(); // 1.0.5
129+
fixture.AssertFullSemver(config, "1.0.5");
130+
fixture.BranchTo("feature/foo", "foo");
131+
fixture.AssertFullSemver(config, "1.0.5-foo.0");
132+
fixture.MakeACommit();
133+
fixture.AssertFullSemver(config, "1.0.5-foo.1");
134+
fixture.MakeACommit();
135+
fixture.AssertFullSemver(config, "1.0.5-foo.2");
136+
fixture.Repository.CreatePullRequestRef("feature/foo", "support/1.0", normalise: true, prNumber: 7);
137+
fixture.AssertFullSemver(config, "1.0.5-PullRequest0007.3");
138+
}
139+
}
140+
141+
[Test]
109142
public void VerifyForwardMerge()
110143
{
111144
using (var fixture = new EmptyRepositoryFixture())
@@ -124,7 +157,7 @@ public void VerifyForwardMerge()
124157
fixture.AssertFullSemver(config, "1.0.2");
125158
fixture.Checkout("feature/foo");
126159
fixture.MergeNoFF("master");
127-
fixture.AssertFullSemver(config, "1.0.3-foo.3");
160+
fixture.AssertFullSemver(config, "1.0.4-foo.3");
128161
}
129162
}
130163
}

src/GitVersionCore/Configuration/BranchConfig.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public BranchConfig(BranchConfig branchConfiguration)
1919
CommitMessageIncrementing = branchConfiguration.CommitMessageIncrementing;
2020
IsDevelop = branchConfiguration.IsDevelop;
2121
IsReleaseBranch = branchConfiguration.IsReleaseBranch;
22+
IsMainline = branchConfiguration.IsMainline;
2223
}
2324

2425
[YamlMember(Alias = "mode")]
@@ -50,5 +51,8 @@ public BranchConfig(BranchConfig branchConfiguration)
5051

5152
[YamlMember(Alias = "is-release-branch")]
5253
public bool? IsReleaseBranch { get; set; }
54+
55+
[YamlMember(Alias = "is-mainline")]
56+
public bool? IsMainline { get; set; }
5357
}
5458
}

src/GitVersionCore/Configuration/ConfigurationProvider.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace GitVersion
22
{
3+
using System;
34
using GitVersion.Configuration.Init.Wizard;
45
using GitVersion.Helpers;
56
using System.ComponentModel;
@@ -43,13 +44,28 @@ public static string SelectConfigFilePath(GitPreparer gitPreparer, IFileSystem f
4344
public static Config Provide(string workingDirectory, IFileSystem fileSystem, bool applyDefaults = true, Config overrideConfig = null)
4445
{
4546
var readConfig = ReadConfig(workingDirectory, fileSystem);
47+
VerifyConfiguration(readConfig);
48+
4649
if (applyDefaults)
4750
ApplyDefaultsTo(readConfig);
4851
if (null != overrideConfig)
4952
ApplyOverridesTo(readConfig, overrideConfig);
5053
return readConfig;
5154
}
5255

56+
static void VerifyConfiguration(Config readConfig)
57+
{
58+
// Verify no branches are set to mainline mode
59+
if (readConfig.Branches.Any(b => b.Value.VersioningMode == VersioningMode.Mainline))
60+
{
61+
throw new Exception(@"Mainline mode only works at the repository level, a single branch cannot be put into mainline mode
62+
63+
This is because mainline mode treats your entire git repository as an event source with each merge into the 'mainline' incrementing the version.
64+
65+
If the docs do not help you decide on the mode open an issue to discuss what you are trying to do.");
66+
}
67+
}
68+
5369
public static void ApplyDefaultsTo(Config config)
5470
{
5571
MigrateBranches(config);
@@ -70,15 +86,21 @@ public static void ApplyDefaultsTo(Config config)
7086

7187
var configBranches = config.Branches.ToList();
7288

73-
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "master"), defaultTag: string.Empty, defaultPreventIncrement: true);
89+
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "master"),
90+
defaultTag: string.Empty,
91+
defaultPreventIncrement: true,
92+
isMainline: true);
7493
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "releases?[/-]"), defaultTag: "beta", defaultPreventIncrement: true, isReleaseBranch: true);
7594
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "features?[/-]"), defaultIncrementStrategy: IncrementStrategy.Inherit);
7695
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, @"(pull|pull\-requests|pr)[/-]"),
7796
defaultTag: "PullRequest",
7897
defaultTagNumberPattern: @"[/-](?<number>\d+)[-/]",
7998
defaultIncrementStrategy: IncrementStrategy.Inherit);
8099
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "hotfix(es)?[/-]"), defaultTag: "beta");
81-
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "support[/-]"), defaultTag: string.Empty, defaultPreventIncrement: true);
100+
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "support[/-]"),
101+
defaultTag: string.Empty,
102+
defaultPreventIncrement: true,
103+
isMainline: true);
82104
ApplyBranchDefaults(config, GetOrCreateBranchDefaults(config, "dev(elop)?(ment)?$"),
83105
defaultTag: "alpha",
84106
defaultIncrementStrategy: IncrementStrategy.Minor,
@@ -144,7 +166,8 @@ public static void ApplyBranchDefaults(Config config,
144166
bool defaultTrackMergeTarget = false,
145167
string defaultTagNumberPattern = null,
146168
bool isDevelop = false,
147-
bool isReleaseBranch = false)
169+
bool isReleaseBranch = false,
170+
bool isMainline = false)
148171
{
149172
branchConfig.Tag = branchConfig.Tag ?? defaultTag;
150173
branchConfig.TagNumberPattern = branchConfig.TagNumberPattern ?? defaultTagNumberPattern;
@@ -154,6 +177,7 @@ public static void ApplyBranchDefaults(Config config,
154177
branchConfig.VersioningMode = branchConfig.VersioningMode ?? defaultVersioningMode ?? config.VersioningMode;
155178
branchConfig.IsDevelop = branchConfig.IsDevelop ?? isDevelop;
156179
branchConfig.IsReleaseBranch = branchConfig.IsReleaseBranch ?? isReleaseBranch;
180+
branchConfig.IsMainline = branchConfig.IsMainline ?? isMainline;
157181
}
158182

159183
static Config ReadConfig(string workingDirectory, IFileSystem fileSystem)

src/GitVersionCore/Configuration/Init/SetConfig/GlobalModeSetting.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ protected override StepResult HandleResult(string result, Queue<ConfigInitWizard
2828
config.VersioningMode = VersioningMode.ContinuousDeployment;
2929
steps.Enqueue(returnToStep);
3030
return StepResult.Ok();
31-
case "0":
3231
case "3":
32+
config.VersioningMode = VersioningMode.Mainline;
33+
steps.Enqueue(returnToStep);
34+
return StepResult.Ok();
35+
case "0":
36+
case "4":
3337
steps.Enqueue(returnToStep);
3438
return StepResult.Ok();
3539
}
@@ -43,14 +47,15 @@ protected override string GetPrompt(Config config, string workingDirectory)
4347
{0}
4448
1) Follow SemVer and only increment when a release has been tagged (continuous delivery mode)
4549
2) Increment based on branch config every commit (continuous deployment mode)
50+
3) Each merged branch against master will increment the version (mainline mode)
4651
{1}",
4752
!isPartOfWizard ? "0) Go Back" : string.Empty,
48-
isPartOfWizard ? "3) Skip" : string.Empty);
53+
isPartOfWizard ? "4) Skip" : string.Empty);
4954
}
5055

5156
protected override string DefaultResult
5257
{
53-
get { return "3"; }
58+
get { return "4"; }
5459
}
5560
}
5661
}

src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Linq;
66
using System.Text.RegularExpressions;
77
using BaseVersionCalculators;
8-
using GitTools;
98
using LibGit2Sharp;
109

1110
public class NextVersionCalculator
@@ -91,6 +90,11 @@ SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion, GitVersionConte
9190
{
9291
var mainlineVersion = baseVersion.SemanticVersion;
9392

93+
// Forward merge / PR
94+
// * feature/foo
95+
// / |
96+
// master * *
97+
//
9498
var commitLog = context.Repository.Commits.QueryBy(new CommitFilter
9599
{
96100
IncludeReachableFrom = context.CurrentBranch,
@@ -99,34 +103,21 @@ SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion, GitVersionConte
99103
}).Where(c => c.Sha != baseVersion.BaseVersionSource.Sha).ToList();
100104
var directCommits = new List<Commit>();
101105

106+
// Scans commit log in reverse, aggregating merge commits
102107
foreach (var commit in commitLog)
103108
{
104109
directCommits.Add(commit);
105110
if (commit.Parents.Count() > 1)
106111
{
107-
// Merge commit, process all merged commits as a batch
108-
var mergeCommit = commit;
109-
var mergedHead = GetMergedHead(mergeCommit);
110-
var findMergeBase = context.Repository.ObjectDatabase.FindMergeBase(mergeCommit.Parents.First(), mergedHead);
111-
var findMessageIncrement = FindMessageIncrement(context, mergeCommit, mergedHead, findMergeBase, directCommits);
112-
113-
// If this collection is not empty there has been some direct commits against master
114-
// Treat each commit as it's own 'release', we need to do this before we increment the branch
115-
mainlineVersion = IncrementForEachCommit(context, directCommits, mainlineVersion);
116-
directCommits.Clear();
117-
118-
// Finally increment for the branch
119-
mainlineVersion = mainlineVersion.IncrementVersion(findMessageIncrement);
120-
Logger.WriteInfo(string.Format("Merge commit {0} incremented base versions {1}, now {2}",
121-
mergeCommit.Sha, findMessageIncrement, mainlineVersion));
112+
mainlineVersion = AggregateMergeCommitIncrement(context, commit, directCommits, mainlineVersion);
122113
}
123114
}
124115

125116
if (context.CurrentBranch.FriendlyName != "master")
126117
{
127118
var mergedHead = context.CurrentCommit;
128-
var second = context.Repository.FindBranch("master").Tip;
129-
var findMergeBase = context.Repository.ObjectDatabase.FindMergeBase(context.CurrentCommit, second);
119+
var mainlineTip = GetMainlineTip(context);
120+
var findMergeBase = context.Repository.ObjectDatabase.FindMergeBase(context.CurrentCommit, mainlineTip);
130121
Logger.WriteInfo(string.Format("Current branch ({0}) was branch from {1}", context.CurrentBranch.FriendlyName, findMergeBase));
131122

132123
var branchIncrement = FindMessageIncrement(context, null, mergedHead, findMergeBase, directCommits);
@@ -151,6 +142,53 @@ SemanticVersion FindMainlineModeVersion(BaseVersion baseVersion, GitVersionConte
151142
}
152143
}
153144

145+
SemanticVersion AggregateMergeCommitIncrement(GitVersionContext context, Commit commit, List<Commit> directCommits, SemanticVersion mainlineVersion)
146+
{
147+
// Merge commit, process all merged commits as a batch
148+
var mergeCommit = commit;
149+
var mergedHead = GetMergedHead(mergeCommit);
150+
var findMergeBase = context.Repository.ObjectDatabase.FindMergeBase(mergeCommit.Parents.First(), mergedHead);
151+
var findMessageIncrement = FindMessageIncrement(context, mergeCommit, mergedHead, findMergeBase, directCommits);
152+
153+
// If this collection is not empty there has been some direct commits against master
154+
// Treat each commit as it's own 'release', we need to do this before we increment the branch
155+
mainlineVersion = IncrementForEachCommit(context, directCommits, mainlineVersion);
156+
directCommits.Clear();
157+
158+
// Finally increment for the branch
159+
mainlineVersion = mainlineVersion.IncrementVersion(findMessageIncrement);
160+
Logger.WriteInfo(string.Format("Merge commit {0} incremented base versions {1}, now {2}",
161+
mergeCommit.Sha, findMessageIncrement, mainlineVersion));
162+
return mainlineVersion;
163+
}
164+
165+
static Commit GetMainlineTip(GitVersionContext context)
166+
{
167+
var mainlineBranchConfigs = context.FullConfiguration.Branches.Where(b => b.Value.IsMainline == true).ToList();
168+
var seenMainlineTips = new List<string>();
169+
var mainlineBranches = context.Repository.Branches.Where(b =>
170+
{
171+
return mainlineBranchConfigs.Any(c => Regex.IsMatch(b.FriendlyName, c.Key));
172+
}).Where(b =>
173+
{
174+
if (seenMainlineTips.Contains(b.Tip.Sha))
175+
{
176+
Logger.WriteInfo("Multiple possible mainlines pointing at the same commit, dropping " + b.FriendlyName);
177+
return false;
178+
}
179+
seenMainlineTips.Add(b.Tip.Sha);
180+
return true;
181+
}).ToDictionary(b => context.Repository.ObjectDatabase.FindMergeBase(b.Tip, context.CurrentCommit).Sha, b => b);
182+
Logger.WriteInfo("Found possible mainline branches: " + string.Join(", ", mainlineBranches.Values.Select(b => b.FriendlyName)));
183+
184+
// Find closest mainline branch
185+
var firstMatchingCommit = context.CurrentBranch.Commits.First(c => mainlineBranches.ContainsKey(c.Sha));
186+
var mainlineBranch = mainlineBranches[firstMatchingCommit.Sha];
187+
Logger.WriteInfo("Mainline for current branch is " + mainlineBranch.FriendlyName);
188+
189+
return mainlineBranch.Tip;
190+
}
191+
154192
private static SemanticVersion IncrementForEachCommit(GitVersionContext context, List<Commit> directCommits, SemanticVersion mainlineVersion)
155193
{
156194
foreach (var directCommit in directCommits)

0 commit comments

Comments
 (0)