Skip to content

Commit 2cdef55

Browse files
committed
Refactor RepositoryStore.GetMergeCommitsForBranch
Refactor `RepositoryStore.GetMergeCommitsForBranch` into the two classes `SourceBranchFinder` and `MergeCommitFinder` to improve readability, provide better stack traces if an exception occurs and provide better protection against `null`.
1 parent b601568 commit 2cdef55

File tree

3 files changed

+139
-57
lines changed

3 files changed

+139
-57
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using GitVersion.Extensions;
2+
using GitVersion.Logging;
3+
using GitVersion.Model.Configuration;
4+
5+
namespace GitVersion;
6+
7+
internal class MergeCommitFinder
8+
{
9+
private readonly IEnumerable<IBranch> excludedBranches;
10+
private readonly ILog log;
11+
private readonly Dictionary<IBranch?, List<BranchCommit>> mergeBaseCommitsCache = new();
12+
private readonly RepositoryStore repositoryStore;
13+
private readonly Config configuration;
14+
15+
public MergeCommitFinder(RepositoryStore repositoryStore, Config configuration, IEnumerable<IBranch> excludedBranches, ILog log)
16+
{
17+
this.repositoryStore = repositoryStore.NotNull();
18+
this.configuration = configuration.NotNull();
19+
this.excludedBranches = repositoryStore.ExcludingBranches(excludedBranches.NotNull());
20+
this.log = log.NotNull();
21+
}
22+
23+
public IEnumerable<BranchCommit> FindMergeCommitsFor(IBranch branch)
24+
{
25+
branch = branch.NotNull();
26+
27+
if (this.mergeBaseCommitsCache.ContainsKey(branch))
28+
{
29+
this.log.Debug($"Cache hit for getting merge commits for branch {branch?.Name.Canonical}.");
30+
return this.mergeBaseCommitsCache[branch];
31+
}
32+
33+
var branchMergeBases = FindMergeBases(branch)
34+
.OrderByDescending(b => b.Commit.When)
35+
.ToList();
36+
37+
this.mergeBaseCommitsCache.Add(branch, branchMergeBases);
38+
39+
return branchMergeBases.Where(b => !branch.Name.EquivalentTo(b.Branch.Name.WithoutRemote));
40+
}
41+
42+
private IEnumerable<BranchCommit> FindMergeBases(IBranch branch)
43+
{
44+
var sourceBranches = new SourceBranchFinder(this.excludedBranches, this.configuration)
45+
.FindSourceBranchesOf(branch);
46+
47+
foreach (var sourceBranch in sourceBranches)
48+
{
49+
if (sourceBranch.Tip == null)
50+
{
51+
this.log.Warning(string.Format(RepositoryStore.MissingTipFormat, sourceBranch));
52+
continue;
53+
}
54+
55+
var findMergeBase = this.repositoryStore.FindMergeBase(branch, sourceBranch);
56+
if (findMergeBase != null)
57+
yield return new BranchCommit(findMergeBase, sourceBranch);
58+
}
59+
}
60+
}

src/GitVersion.Core/Core/RepositoryStore.cs

Lines changed: 13 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ namespace GitVersion;
1010

1111
public class RepositoryStore : IRepositoryStore
1212
{
13-
private const string MissingTipFormat = "{0} has no tip. Please see https://example.com/docs for information on how to fix this.";
13+
internal const string MissingTipFormat = "{0} has no tip. Please see https://example.com/docs for information on how to fix this.";
1414
private readonly IIncrementStrategyFinder incrementStrategyFinder;
1515
private readonly ILog log;
1616
private readonly Dictionary<Tuple<IBranch, IBranch?>, ICommit?> mergeBaseCache = new();
17-
private readonly Dictionary<IBranch?, List<BranchCommit>> mergeBaseCommitsCache = new();
1817
private readonly IGitRepository repository;
1918
private readonly Dictionary<IBranch, List<SemanticVersion>> semanticVersionTagsOnBranchCache = new();
2019

@@ -30,8 +29,7 @@ public RepositoryStore(ILog log, IGitRepository repository, IIncrementStrategyFi
3029
/// </summary>
3130
public ICommit? FindMergeBase(IBranch? branch, IBranch? otherBranch)
3231
{
33-
if (branch == null)
34-
throw new ArgumentNullException(nameof(branch));
32+
branch = branch.NotNull();
3533

3634
var key = Tuple.Create(branch, otherBranch);
3735

@@ -318,20 +316,19 @@ public BranchCommit FindCommitBranchWasBranchedFrom(IBranch? branch, Config conf
318316
return BranchCommit.Empty;
319317
}
320318

321-
var possibleBranches = GetMergeCommitsForBranch(branch, configuration, excludedBranches)
322-
.Where(b => !branch.Name.EquivalentTo(b.Branch.Name.WithoutRemote))
323-
.ToList();
319+
var possibleBranches =
320+
new MergeCommitFinder(this, configuration, excludedBranches, this.log)
321+
.FindMergeCommitsFor(branch)
322+
.ToList();
324323

325-
if (possibleBranches.Count > 1)
326-
{
327-
var first = possibleBranches.First();
328-
this.log.Info($"Multiple source branches have been found, picking the first one ({first.Branch}).{System.Environment.NewLine}" +
329-
$"This may result in incorrect commit counting.{System.Environment.NewLine}Options were:{System.Environment.NewLine}" +
330-
string.Join(", ", possibleBranches.Select(b => b.Branch.ToString())));
331-
return first;
332-
}
324+
if (possibleBranches.Count <= 1)
325+
return possibleBranches.SingleOrDefault();
333326

334-
return possibleBranches.SingleOrDefault();
327+
var first = possibleBranches.First();
328+
this.log.Info($"Multiple source branches have been found, picking the first one ({first.Branch}).{System.Environment.NewLine}" +
329+
$"This may result in incorrect commit counting.{System.Environment.NewLine}Options were:{System.Environment.NewLine}" +
330+
string.Join(", ", possibleBranches.Select(b => b.Branch.ToString())));
331+
return first;
335332
}
336333
}
337334

@@ -466,47 +463,6 @@ private bool BranchMatchesMainlineConfig(INamedReference branch, KeyValuePair<st
466463
return null;
467464
}
468465

469-
private IEnumerable<BranchCommit> GetMergeCommitsForBranch(IBranch branch, Config configuration, IEnumerable<IBranch> excludedBranches)
470-
{
471-
branch = branch.NotNull();
472-
473-
if (this.mergeBaseCommitsCache.ContainsKey(branch))
474-
{
475-
this.log.Debug($"Cache hit for getting merge commits for branch {branch?.Name.Canonical}.");
476-
return this.mergeBaseCommitsCache[branch];
477-
}
478-
479-
var currentBranchConfig = configuration.GetConfigForBranch(branch.Name.WithoutRemote);
480-
var regexesToCheck = currentBranchConfig?.SourceBranches == null
481-
? new[] { ".*" } // Match anything if we can't find a branch config
482-
: currentBranchConfig.SourceBranches.Select(sb => configuration.Branches[sb]?.Regex);
483-
var branchMergeBases = ExcludingBranches(excludedBranches)
484-
.Where(b =>
485-
{
486-
if (Equals(b, branch))
487-
return false;
488-
489-
var branchCanBeMergeBase = regexesToCheck.Any(regex => regex != null && Regex.IsMatch(b.Name.Friendly, regex));
490-
491-
return branchCanBeMergeBase;
492-
})
493-
.Select(otherBranch =>
494-
{
495-
if (otherBranch.Tip == null)
496-
{
497-
this.log.Warning(string.Format(MissingTipFormat, otherBranch));
498-
return BranchCommit.Empty;
499-
}
500-
501-
var findMergeBase = FindMergeBase(branch, otherBranch);
502-
return findMergeBase == null ? BranchCommit.Empty : new BranchCommit(findMergeBase, otherBranch);
503-
})
504-
.OrderByDescending(b => b.Commit.When)
505-
.ToList();
506-
this.mergeBaseCommitsCache.Add(branch, branchMergeBases);
507-
508-
return branchMergeBases;
509-
}
510466

511467
private static IEnumerable<ICommit> GetCommitsReacheableFrom(IGitRepository repository, IGitObject commit, IBranch branch)
512468
{
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System.Text.RegularExpressions;
2+
using GitVersion.Configuration;
3+
using GitVersion.Extensions;
4+
using GitVersion.Model.Configuration;
5+
6+
namespace GitVersion;
7+
8+
internal class SourceBranchFinder
9+
{
10+
private readonly Config configuration;
11+
private readonly IEnumerable<IBranch> excludedBranches;
12+
13+
public SourceBranchFinder(IEnumerable<IBranch> excludedBranches, Config configuration)
14+
{
15+
this.excludedBranches = excludedBranches.NotNull();
16+
this.configuration = configuration.NotNull();
17+
}
18+
19+
public IEnumerable<IBranch> FindSourceBranchesOf(IBranch branch)
20+
{
21+
var predicate = new SourceBranchPredicate(branch, this.configuration);
22+
return this.excludedBranches.Where(predicate.IsSourceBranch);
23+
}
24+
25+
private class SourceBranchPredicate
26+
{
27+
private readonly IBranch branch;
28+
private readonly IEnumerable<string> sourceBranchRegexes;
29+
30+
public SourceBranchPredicate(IBranch branch, Config configuration)
31+
{
32+
this.branch = branch;
33+
this.sourceBranchRegexes = GetSourceBranchRegexes(branch, configuration);
34+
}
35+
36+
public bool IsSourceBranch(INamedReference sourceBranchCandidate)
37+
{
38+
if (Equals(sourceBranchCandidate, this.branch))
39+
return false;
40+
41+
var branchName = sourceBranchCandidate.Name.Friendly;
42+
43+
return this.sourceBranchRegexes
44+
.Any(regex => Regex.IsMatch(branchName, regex));
45+
}
46+
47+
private static IEnumerable<string> GetSourceBranchRegexes(INamedReference branch, Config configuration)
48+
{
49+
var branchName = branch.Name.WithoutRemote;
50+
var currentBranchConfig = configuration.GetConfigForBranch(branchName);
51+
if (currentBranchConfig?.SourceBranches == null)
52+
{
53+
yield return ".*";
54+
}
55+
else
56+
{
57+
foreach (var sourceBranch in currentBranchConfig.SourceBranches)
58+
{
59+
var regex = configuration.Branches[sourceBranch]?.Regex;
60+
if (regex != null)
61+
yield return regex;
62+
}
63+
}
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)