Skip to content

Enable custom merge message formats via configuration #1488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ commit-date-format: 'yyyy-MM-dd'
ignore:
sha: []
commits-before: yyyy-MM-ddTHH:mm:ss
merge-message-formats: {}
```

And the description of the available options are:
Expand Down Expand Up @@ -179,6 +180,24 @@ Date and time in the format `yyyy-MM-ddTHH:mm:ss` (eg `commits-before:
2015-10-23T12:23:15`) to setup an exclusion range. Effectively any commit before
`commits-before` will be ignored.

### merge-message-formats
Custom merge message formats to enable identification of merge messages that do not
follow the built-in conventions. Entries should be added as key-value pairs where
the value is a regular expression.
e.g.

```
merge-message-formats:
tfs: ^Merged (?:PR (?<PullRequestNumber>\d+)): Merge (?<SourceBranch>.+) to (?<TargetBranch>.+)
```

The regular expression should contain the following capture groups:
+ SourceBranch - Identifies the source branch of the merge
+ TargetBranch - Identifies the target of the merge
+ PullRequestNumber - Captures the pull-request number

Custom merge message formats are evalauted _before_ any built in formats.

## Branch configuration
Then we have branch specific configuration, which looks something like this:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,4 @@ branches:
ignore:
sha: []
commit-date-format: yyyy-MM-dd
merge-message-formats: {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ next-version: 2.0.0
branches: {}
ignore:
sha: []
merge-message-formats: {}
254 changes: 149 additions & 105 deletions src/GitVersionCore.Tests/MergeMessageTests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ public void ShouldNotAllowIncrementOfVersion()
[TestCase("Merge branch 'Release-v2.2'", true, "2.2.0")]
[TestCase("Merge remote-tracking branch 'origin/release/0.8.0' into develop/master", true, "0.8.0")]
[TestCase("Merge remote-tracking branch 'refs/remotes/origin/release/2.0.0'", true, "2.0.0")]
[TestCase("Merge release/5.1.0 to master", true, "5.1.0")] // Team Foundation Server 2017 default merge message (en-US)
[TestCase("Zusammengeführter PR \"9\": release/5.1.0 mit master mergen", true, "5.1.0")] // Team Foundation Server 2017 default merge message (de-DE)
public void TakesVersionFromMergeOfReleaseBranch(string message, bool isMergeCommit, string expectedVersion)
{
var parents = GetParents(isMergeCommit);
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 @@ -145,5 +145,8 @@ T MergeObjects<T>(T target, T source)

[YamlMember(Alias = "commit-date-format")]
public string CommitDateFormat { get; set; }

[YamlMember(Alias = "merge-message-formats")]
public Dictionary<string, string> MergeMessageFormats { get; set; } = new Dictionary<string, string>();
}
}
44 changes: 24 additions & 20 deletions src/GitVersionCore/MergeMessage.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace GitVersion
{
class MergeMessage
public class MergeMessage
{
private static readonly IList<MergeMessagePattern> Patterns = new List<MergeMessagePattern>
private static readonly IList<MergeMessageFormat> DefaultFormats = new List<MergeMessageFormat>
{
new MergeMessagePattern("Default", @"^Merge (branch|tag) '(?<SourceBranch>[^']*)'(?: into (?<TargetBranch>[^\s]*))*"),
new MergeMessagePattern("SmartGit", @"^Finish (?<SourceBranch>[^\s]*)(?: into (?<TargetBranch>[^\s]*))*"),
new MergeMessagePattern("BitBucketPull", @"^Merge pull request #(?<PullRequestNumber>\d+) (from|in) (?<Source>.*) from (?<SourceBranch>[^\s]*) to (?<TargetBranch>[^\s]*)"),
new MergeMessagePattern("GitHubPull", @"^Merge pull request #(?<PullRequestNumber>\d+) (from|in) (?:(?<SourceBranch>[^\s]*))(?: into (?<TargetBranch>[^\s]*))*"),
new MergeMessagePattern("RemoteTracking", @"^Merge remote-tracking branch '(?<SourceBranch>[^\s]*)'(?: into (?<TargetBranch>[^\s]*))*"),
new MergeMessagePattern("TfsMergeMessageEnglishUS", @"^Merge (?<SourceBranch>[^\s]*) to (?<TargetBranch>[^\s]*)"),
new MergeMessagePattern("TfsMergeMessageGermanDE",@"^Zusammengeführter PR ""(?<PullRequestNumber>\d+)""\: (?<SourceBranch>.*) mit (?<TargetBranch>.*) mergen")
new MergeMessageFormat("Default", @"^Merge (branch|tag) '(?<SourceBranch>[^']*)'(?: into (?<TargetBranch>[^\s]*))*"),
new MergeMessageFormat("SmartGit", @"^Finish (?<SourceBranch>[^\s]*)(?: into (?<TargetBranch>[^\s]*))*"),
new MergeMessageFormat("BitBucketPull", @"^Merge pull request #(?<PullRequestNumber>\d+) (from|in) (?<Source>.*) from (?<SourceBranch>[^\s]*) to (?<TargetBranch>[^\s]*)"),
new MergeMessageFormat("GitHubPull", @"^Merge pull request #(?<PullRequestNumber>\d+) (from|in) (?:(?<SourceBranch>[^\s]*))(?: into (?<TargetBranch>[^\s]*))*"),
new MergeMessageFormat("RemoteTracking", @"^Merge remote-tracking branch '(?<SourceBranch>[^\s]*)'(?: into (?<TargetBranch>[^\s]*))*")
};

public MergeMessage(string mergeMessage, Config config)
{
if (mergeMessage == null)
throw new NullReferenceException();

foreach (var pattern in Patterns)
// Concat config formats with the defaults.
// Ensure configs are processed first.
var allFormats = config.MergeMessageFormats
.Select(x => new MergeMessageFormat(x.Key, x.Value))
.Concat(DefaultFormats);

foreach (var format in allFormats)
{
var match = pattern.Format.Match(mergeMessage);
var match = format.Pattern.Match(mergeMessage);
if (match.Success)
{
MatchDefinition = pattern.Name;
FormatName = format.Name;
MergedBranch = match.Groups["SourceBranch"].Value;

if (match.Groups["TargetBranch"].Success)
Expand All @@ -40,22 +45,21 @@ public MergeMessage(string mergeMessage, Config config)
PullRequestNumber = pullNumber;
}

Version = ParseVersion(MergedBranch, config.TagPrefix);
Version = ParseVersion(config.TagPrefix);

break;
}
}
}

public string MatchDefinition { get; }
public string FormatName { get; }
public string TargetBranch { get; }
public string MergedBranch { get; } = "";
public bool IsMergedPullRequest => PullRequestNumber != null;
public int? PullRequestNumber { get; }
public SemanticVersion Version { get; }


private SemanticVersion ParseVersion(string branchName, string tagPrefix)
private SemanticVersion ParseVersion(string tagPrefix)
{
// Remove remotes and branch prefixes like release/ feature/ hotfix/ etc
var toMatch = Regex.Replace(MergedBranch, @"^(\w+[-/])*", "", RegexOptions.IgnoreCase);
Expand All @@ -72,17 +76,17 @@ private SemanticVersion ParseVersion(string branchName, string tagPrefix)
return null;
}

private class MergeMessagePattern
private class MergeMessageFormat
{
public MergeMessagePattern(string name, string format)
public MergeMessageFormat(string name, string pattern)
{
Name = name;
Format = new Regex(format, RegexOptions.IgnoreCase | RegexOptions.Compiled);
Pattern = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
}

public string Name { get; }

public Regex Format { get; }
public Regex Pattern { get; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public override IEnumerable<BaseVersion> GetVersions(GitVersionContext context)
mergeMessage.Version != null &&
context.FullConfiguration.IsReleaseBranch(TrimRemote(mergeMessage.MergedBranch)))
{
Logger.WriteInfo($"Found commit [{context.CurrentCommit.Sha}] matching merge message format: {mergeMessage.FormatName}");
var shouldIncrement = !context.Configuration.PreventIncrementForMergedBranchVersion;
return new[]
{
Expand Down