Skip to content

Commit 891eaba

Browse files
authored
Merge pull request #1224 from JakeGinnivan/feature/fix-mainline-feature-branch-bump
Feature/fix mainline feature branch bump
2 parents cf29e64 + 83e150f commit 891eaba

File tree

9 files changed

+402
-255
lines changed

9 files changed

+402
-255
lines changed

src/GitVersionCore.Tests/IntegrationTests/MainlineDevelopmentMode.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using GitVersionCore.Tests;
77
using LibGit2Sharp;
88
using NUnit.Framework;
9+
using System.Collections.Generic;
910

1011
public class MainlineDevelopmentMode
1112
{
@@ -206,7 +207,7 @@ public void VerifyMergingMasterToFeatureDoesNotCauseBranchCommitsToIncrementVers
206207

207208
fixture.BranchTo("feature/foo", "foo");
208209
fixture.MakeACommit("first in foo");
209-
210+
210211
fixture.Checkout("master");
211212
fixture.MakeACommit("second in master");
212213

@@ -303,6 +304,41 @@ public void VerifyMergingMasterIntoAFeatureBranchWorksWithMultipleBranches()
303304
fixture.AssertFullSemver(config, "1.0.2");
304305
}
305306
}
307+
308+
[Test]
309+
public void MergingFeatureBranchThatIncrementsMinorNumberIncrementsMinorVersionOfMaster()
310+
{
311+
var currentConfig = new Config
312+
{
313+
VersioningMode = VersioningMode.Mainline,
314+
Branches = new Dictionary<string, BranchConfig>
315+
{
316+
{
317+
"feature", new BranchConfig
318+
{
319+
VersioningMode = VersioningMode.ContinuousDeployment,
320+
Increment = IncrementStrategy.Minor
321+
}
322+
}
323+
}
324+
};
325+
326+
using (var fixture = new EmptyRepositoryFixture())
327+
{
328+
fixture.MakeACommit("first in master");
329+
fixture.MakeATaggedCommit("1.0.0");
330+
fixture.AssertFullSemver(currentConfig, "1.0.0");
331+
332+
fixture.BranchTo("feature/foo", "foo");
333+
fixture.MakeACommit("first in foo");
334+
fixture.MakeACommit("second in foo");
335+
fixture.AssertFullSemver(currentConfig, "1.1.0-foo.2");
336+
337+
fixture.Checkout("master");
338+
fixture.MergeNoFF("feature/foo");
339+
fixture.AssertFullSemver(currentConfig, "1.1.0");
340+
}
341+
}
306342
}
307343

308344
static class CommitExtensions

src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,12 @@ Another commit message
8282
Commit message including a IP-number https://10.50.1.1
8383
A commit message")]
8484
[TestCase(@"Merge branch 'release/Sprint_2.0_Holdings_Computed_Balances'")]
85-
[TestCase(@"Merge branch 'feature/fix-for-08.14-push'")]
8685
[TestCase(@"Merge branch 'develop' of http://10.0.6.3/gitblit/r/... into develop")]
8786
[TestCase(@"Merge branch 'master' of http://172.16.3.10:8082/r/asu_tk/p_sd")]
8887
[TestCase(@"Merge branch 'master' of http://212.248.89.56:8082/r/asu_tk/p_sd")]
8988
[TestCase(@"Merge branch 'DEMO' of http://10.10.10.121/gitlab/mtolland/orcid into DEMO")]
9089
public void MergeMessagesThatIsNotRelatedToGitVersion(string commitMessage)
9190
{
92-
9391
var parents = GetParents(true);
9492

9593
AssertMergeMessage(commitMessage, null, parents);

src/GitVersionCore/BranchConfigurationCalculator.cs

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,21 @@ public class BranchConfigurationCalculator
1313
/// </summary>
1414
public static BranchConfig GetBranchConfiguration(GitVersionContext context, Branch targetBranch, IList<Branch> excludedInheritBranches = null)
1515
{
16-
var matchingBranches = LookupBranchConfiguration(context.FullConfiguration, targetBranch).ToArray();
17-
18-
BranchConfig branchConfiguration;
19-
if (matchingBranches.Length > 0)
20-
{
21-
branchConfiguration = matchingBranches[0];
22-
23-
if (matchingBranches.Length > 1)
24-
{
25-
Logger.WriteWarning(string.Format(
26-
"Multiple branch configurations match the current branch branchName of '{0}'. Using the first matching configuration, '{1}'. Matching configurations include: '{2}'",
27-
targetBranch.FriendlyName,
28-
branchConfiguration.Name,
29-
string.Join("', '", matchingBranches.Select(b => b.Name))));
30-
}
31-
}
32-
else
16+
var matchingBranches = context.FullConfiguration.GetConfigForBranch(targetBranch.FriendlyName);
17+
18+
if (matchingBranches == null)
3319
{
3420
Logger.WriteInfo(string.Format(
3521
"No branch configuration found for branch {0}, falling back to default configuration",
3622
targetBranch.FriendlyName));
3723

38-
branchConfiguration = new BranchConfig { Name = string.Empty };
39-
ConfigurationProvider.ApplyBranchDefaults(context.FullConfiguration, branchConfiguration, "");
40-
}
41-
42-
return branchConfiguration.Increment == IncrementStrategy.Inherit ?
43-
InheritBranchConfiguration(context, targetBranch, branchConfiguration, excludedInheritBranches) :
44-
branchConfiguration;
45-
}
46-
47-
static IEnumerable<BranchConfig> LookupBranchConfiguration(Config config, Branch currentBranch)
48-
{
49-
if (config == null)
50-
{
51-
throw new ArgumentNullException(nameof(config));
52-
}
53-
54-
if (currentBranch == null)
55-
{
56-
throw new ArgumentNullException(nameof(currentBranch));
24+
matchingBranches = new BranchConfig { Name = string.Empty };
25+
ConfigurationProvider.ApplyBranchDefaults(context.FullConfiguration, matchingBranches, "");
5726
}
5827

59-
return config.Branches.Where(b => Regex.IsMatch(currentBranch.FriendlyName, "^" + b.Value.Regex, RegexOptions.IgnoreCase)).Select(kvp => kvp.Value);
28+
return matchingBranches.Increment == IncrementStrategy.Inherit ?
29+
InheritBranchConfiguration(context, targetBranch, matchingBranches, excludedInheritBranches) :
30+
matchingBranches;
6031
}
6132

6233
static BranchConfig InheritBranchConfiguration(GitVersionContext context, Branch targetBranch, BranchConfig branchConfiguration, IList<Branch> excludedInheritBranches)
@@ -77,11 +48,9 @@ static BranchConfig InheritBranchConfiguration(GitVersionContext context, Branch
7748
{
7849
excludedInheritBranches = repository.Branches.Where(b =>
7950
{
80-
var branchConfig = LookupBranchConfiguration(config, b).ToArray();
51+
var branchConfig = config.GetConfigForBranch(b.FriendlyName);
8152

82-
// NOTE: if length is 0 we couldn't find the configuration for the branch e.g. "origin/master"
83-
// NOTE: if the length is greater than 1 we cannot decide which merge strategy to pick
84-
return (branchConfig.Length != 1) || (branchConfig.Length == 1 && branchConfig[0].Increment == IncrementStrategy.Inherit);
53+
return branchConfig != null && branchConfig.Increment == IncrementStrategy.Inherit;
8554
}).ToList();
8655
}
8756
// Add new excluded branches.

src/GitVersionCore/Configuration/Config.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
namespace GitVersion
22
{
3+
using System;
34
using System.Collections.Generic;
45
using System.Globalization;
56
using System.Linq;
7+
using System.Text.RegularExpressions;
68
using YamlDotNet.Serialization;
79

810
public class Config
@@ -89,6 +91,33 @@ public Dictionary<string, BranchConfig> Branches
8991
}
9092
}
9193

94+
public BranchConfig GetConfigForBranch(string branchName)
95+
{
96+
if (branchName == null) throw new ArgumentNullException(nameof(branchName));
97+
var matches = Branches
98+
.Where(b => Regex.IsMatch(branchName, "^" + b.Value.Regex, RegexOptions.IgnoreCase));
99+
100+
try
101+
{
102+
return matches
103+
.Select(kvp => kvp.Value)
104+
.SingleOrDefault();
105+
}
106+
catch (InvalidOperationException)
107+
{
108+
var matchingConfigs = string.Join("\n - ", matches.Select(m => m.Key));
109+
var picked = matches
110+
.Select(kvp => kvp.Value)
111+
.First();
112+
113+
Logger.WriteWarning(
114+
$"Multiple branch configurations match the current branch branchName of '{branchName}'. " +
115+
$"Using the first matching configuration, '{picked}'. Matching configurations include: '{matchingConfigs}'");
116+
117+
return picked;
118+
}
119+
}
120+
92121
T MergeObjects<T>(T target, T source)
93122
{
94123
typeof(T).GetProperties()

src/GitVersionCore/GitVersionCore.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,12 @@
128128
<Compile Include="Helpers\ThreadSleep.cs" />
129129
<Compile Include="IncrementStrategyFinder.cs" />
130130
<Compile Include="LoggerWrapper.cs" />
131+
<Compile Include="MergeMessage.cs" />
131132
<Compile Include="OutputVariables\VersionVariables.cs" />
132133
<Compile Include="SemanticVersionExtensions.cs" />
133134
<Compile Include="SemanticVersionFormatValues.cs" />
134135
<Compile Include="VerbosityLevel.cs" />
136+
<Compile Include="VersionCalculation\MainlineVersionCalculator.cs" />
135137
<Compile Include="VersionFilters\MinDateVersionFilter.cs" />
136138
<Compile Include="VersionFilters\IVersionFilter.cs" />
137139
<Compile Include="VersionFilters\ShaVersionFilter.cs" />

src/GitVersionCore/MergeMessage.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Linq;
3+
using System.Text.RegularExpressions;
4+
5+
namespace GitVersion
6+
{
7+
class MergeMessage
8+
{
9+
static Regex parseMergeMessage = new Regex(
10+
@"^Merge (branch|tag) '(?<Branch>[^']*)'",
11+
RegexOptions.IgnoreCase | RegexOptions.Compiled);
12+
static Regex parseGitHubPullMergeMessage = new Regex(
13+
@"^Merge pull request #(?<PullRequestNumber>\d*) (from|in) (?<Source>.*)",
14+
RegexOptions.IgnoreCase | RegexOptions.Compiled);
15+
static Regex smartGitMergeMessage = new Regex(
16+
@"^Finish (?<Branch>.*)",
17+
RegexOptions.IgnoreCase | RegexOptions.Compiled);
18+
private string mergeMessage;
19+
private Config config;
20+
21+
public MergeMessage(string mergeMessage, Config config)
22+
{
23+
this.mergeMessage = mergeMessage;
24+
this.config = config;
25+
26+
var lastIndexOf = mergeMessage.LastIndexOf("into", StringComparison.OrdinalIgnoreCase);
27+
if (lastIndexOf != -1)
28+
{
29+
// If we have into in the merge message the rest should be the target branch
30+
TargetBranch = mergeMessage.Substring(lastIndexOf + 5);
31+
}
32+
33+
MergedBranch = ParseBranch();
34+
35+
// Remove remotes and branch prefixes like release/ feature/ hotfix/ etc
36+
var toMatch = Regex.Replace(MergedBranch, @"^(\w+[-/])*", "", RegexOptions.IgnoreCase);
37+
toMatch = Regex.Replace(toMatch, $"^{config.TagPrefix}", "");
38+
// We don't match if the version is likely an ip (i.e starts with http://)
39+
var versionMatch = new Regex(@"^(?<!://)\d+\.\d+(\.*\d+)*");
40+
var version = versionMatch.Match(toMatch);
41+
42+
if (version.Success)
43+
{
44+
SemanticVersion val;
45+
if (SemanticVersion.TryParse(version.Value, config.TagPrefix, out val))
46+
{
47+
Version = val;
48+
}
49+
}
50+
}
51+
52+
private string ParseBranch()
53+
{
54+
var match = parseMergeMessage.Match(mergeMessage);
55+
if (match.Success)
56+
{
57+
return match.Groups["Branch"].Value;
58+
}
59+
60+
match = smartGitMergeMessage.Match(mergeMessage);
61+
if (match.Success)
62+
{
63+
return match.Groups["Branch"].Value;
64+
}
65+
66+
match = parseGitHubPullMergeMessage.Match(mergeMessage);
67+
if (match.Success)
68+
{
69+
IsMergedPullRequest = true;
70+
int pullNumber;
71+
if (int.TryParse(match.Groups["PullRequestNumber"].Value, out pullNumber))
72+
{
73+
PullRequestNumber = pullNumber;
74+
}
75+
var from = match.Groups["Source"].Value;
76+
// We could remove/separate the remote name at this point?
77+
return from;
78+
}
79+
80+
return "";
81+
}
82+
83+
public string TargetBranch { get; }
84+
public string MergedBranch { get; }
85+
public bool IsMergedPullRequest { get; private set; }
86+
public int? PullRequestNumber { get; private set; }
87+
public SemanticVersion Version { get; }
88+
}
89+
}
Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
namespace GitVersion.VersionCalculation.BaseVersionCalculators
22
{
3-
using System;
43
using System.Collections.Generic;
54
using System.Linq;
6-
using System.Text.RegularExpressions;
75
using LibGit2Sharp;
86

97
/// <summary>
@@ -21,7 +19,7 @@ public override IEnumerable<BaseVersion> GetVersions(GitVersionContext context)
2119
.SelectMany(c =>
2220
{
2321
SemanticVersion semanticVersion;
24-
if (TryParse(c, context.Configuration, out semanticVersion))
22+
if (TryParse(c, context, out semanticVersion))
2523
{
2624
var shouldIncrement = !context.Configuration.PreventIncrementForMergedBranchVersion;
2725
return new[]
@@ -34,35 +32,21 @@ public override IEnumerable<BaseVersion> GetVersions(GitVersionContext context)
3432
return baseVersions;
3533
}
3634

37-
static bool TryParse(Commit mergeCommit, EffectiveConfiguration configuration, out SemanticVersion semanticVersion)
35+
static bool TryParse(Commit mergeCommit, GitVersionContext context, out SemanticVersion semanticVersion)
3836
{
39-
semanticVersion = Inner(mergeCommit, configuration);
37+
semanticVersion = Inner(mergeCommit, context);
4038
return semanticVersion != null;
4139
}
4240

43-
static SemanticVersion Inner(Commit mergeCommit, EffectiveConfiguration configuration)
41+
static SemanticVersion Inner(Commit mergeCommit, GitVersionContext context)
4442
{
4543
if (mergeCommit.Parents.Count() < 2)
4644
{
4745
return null;
4846
}
4947

50-
var commitMessage = mergeCommit.Message;
51-
var lastIndexOf = commitMessage.LastIndexOf("into", StringComparison.OrdinalIgnoreCase);
52-
if (lastIndexOf != -1)
53-
commitMessage = commitMessage.Substring(0, lastIndexOf);
54-
55-
//TODO: Make the version prefixes customizable
56-
var possibleVersions = Regex.Matches(commitMessage, @"^.*?(([rR]elease|[hH]otfix|[aA]lpha)-|-v|/|/v|'|Finish )(?<PossibleVersions>(?<!://)\d+\.\d+(\.*\d+)*)")
57-
.Cast<Match>()
58-
.Select(m => m.Groups["PossibleVersions"].Value);
59-
60-
return possibleVersions
61-
.Select(part =>
62-
{
63-
SemanticVersion v;
64-
return SemanticVersion.TryParse(part, configuration.GitTagPrefix, out v) ? v : null;
65-
}).FirstOrDefault(v => v != null);
48+
var mergeMessage = new MergeMessage(mergeCommit.Message, context.FullConfiguration);
49+
return mergeMessage.Version;
6650
}
6751
}
6852
}

0 commit comments

Comments
 (0)