diff --git a/src/GitVersionCore.Tests/GitRepoInformation/QueryRequirements.cs b/src/GitVersionCore.Tests/GitRepoInformation/QueryRequirements.cs new file mode 100644 index 0000000000..1665237d10 --- /dev/null +++ b/src/GitVersionCore.Tests/GitRepoInformation/QueryRequirements.cs @@ -0,0 +1,33 @@ +using GitTools; +using GitTools.Testing; +using GitVersion; +using GitVersion.GitRepoInformation; +using NUnit.Framework; +using Shouldly; +using System; + +[TestFixture] +public class QueryRequirements +{ + [Test] + public void ListsAllMergeCommits() + { + var config = new Config(); + ConfigurationProvider.ApplyDefaultsTo(config); + using (var fixture = new EmptyRepositoryFixture()) + { + fixture.MakeACommit(); + fixture.MakeACommit(); + fixture.BranchTo("feature/foo"); + fixture.MakeACommit(); + fixture.Checkout("master"); + fixture.MergeNoFF("feature/foo"); + + fixture.Repository.DumpGraph(); + + var metadata = Libgit2RepoMetadataProvider.ReadMetadata(new GitVersionContext(fixture.Repository, config)); + + metadata.CurrentBranch.MergeMessages.Count.ShouldBe(1); + } + } +} \ No newline at end of file diff --git a/src/GitVersionCore.Tests/GitVersionContextBuilder.cs b/src/GitVersionCore.Tests/GitVersionContextBuilder.cs index 85bd100906..7d60b9a993 100644 --- a/src/GitVersionCore.Tests/GitVersionContextBuilder.cs +++ b/src/GitVersionCore.Tests/GitVersionContextBuilder.cs @@ -74,7 +74,8 @@ IRepository CreateRepository() mockBranch }, Tags = new MockTagCollection(), - Head = mockBranch + Head = mockBranch, + ObjectDatabase = new MockObjectDatabase() }; return mockRepository; diff --git a/src/GitVersionCore.Tests/GitVersionContextTests.cs b/src/GitVersionCore.Tests/GitVersionContextTests.cs index d095ae6208..965a754cf3 100644 --- a/src/GitVersionCore.Tests/GitVersionContextTests.cs +++ b/src/GitVersionCore.Tests/GitVersionContextTests.cs @@ -22,6 +22,7 @@ public void CanInheritVersioningMode(VersioningMode mode) var mockBranch = new MockBranch("master") { new MockCommit { CommitterEx = Generate.SignatureNow() } }; var mockRepository = new MockRepository { + Head = mockBranch, Branches = new MockBranchCollection { mockBranch @@ -81,6 +82,7 @@ public void UsesBranchSpecificConfigOverTopLevelDefaults() var develop = new MockBranch("develop") { new MockCommit { CommitterEx = Generate.SignatureNow() } }; var mockRepository = new MockRepository { + Head = develop, Branches = new MockBranchCollection { new MockBranch("master") { new MockCommit { CommitterEx = Generate.SignatureNow() } }, @@ -109,6 +111,7 @@ public void UsesFirstBranchConfigWhenMultipleMatch() var mockRepository = new MockRepository { + Head = releaseLatestBranch, Branches = new MockBranchCollection { releaseLatestBranch, diff --git a/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj b/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj index a3c44e6cc2..c5e1406560 100644 --- a/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj +++ b/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj @@ -1,5 +1,6 @@  + @@ -58,9 +59,8 @@ ..\packages\NSubstitute.1.10.0.0\lib\net45\NSubstitute.dll True - - ..\packages\NUnit.3.6.0\lib\net45\nunit.framework.dll - True + + ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll ..\packages\Shouldly.2.7.0\lib\net40\Shouldly.dll @@ -94,6 +94,7 @@ + @@ -103,6 +104,7 @@ + @@ -190,6 +192,7 @@ + \ No newline at end of file diff --git a/src/GitVersionCore.Tests/IntegrationTests/OtherScenarios.cs b/src/GitVersionCore.Tests/IntegrationTests/OtherScenarios.cs index 4ebda3317b..4d39544131 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/OtherScenarios.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/OtherScenarios.cs @@ -6,6 +6,7 @@ using LibGit2Sharp; using NUnit.Framework; using System.Collections.Generic; + using System; [TestFixture] public class OtherScenarios @@ -91,5 +92,28 @@ public void DoNotBlowUpWhenDevelopAndFeatureBranchPointAtSameCommit() fixture.AssertFullSemver("1.1.0-alpha.1"); } } + + [Test] + public void AllowUnrelatedBranchesInRepo() + { + // This test unsures we handle when GitVersion cannot find mergebases etc + using (var fixture = new EmptyRepositoryFixture()) + { + fixture.Repository.MakeACommit(); + fixture.Repository.MakeACommit(); + + // Create a new root commit and then a branch pointing at that commit + var treeDefinition = new TreeDefinition(); + var tree = fixture.Repository.ObjectDatabase.CreateTree(treeDefinition); + var commit = fixture.Repository.ObjectDatabase.CreateCommit( + new Signature("name", "mail", DateTimeOffset.Now), + new Signature("name", "mail", DateTimeOffset.Now), + "Create new empty branch", + tree, new Commit[0], false); + fixture.Repository.Branches.Add("gh-pages", commit); + + fixture.AssertFullSemver("0.1.0+1"); + } + } } } \ No newline at end of file diff --git a/src/GitVersionCore.Tests/MockObjectDatabase.cs b/src/GitVersionCore.Tests/MockObjectDatabase.cs new file mode 100644 index 0000000000..416cf7503f --- /dev/null +++ b/src/GitVersionCore.Tests/MockObjectDatabase.cs @@ -0,0 +1,9 @@ +using LibGit2Sharp; + +public class MockObjectDatabase : ObjectDatabase +{ + public override Commit FindMergeBase(Commit first, Commit second) + { + return second; + } +} \ No newline at end of file diff --git a/src/GitVersionCore.Tests/Mocks/MockRepository.cs b/src/GitVersionCore.Tests/Mocks/MockRepository.cs index 33e8709a4d..4f21deacf9 100644 --- a/src/GitVersionCore.Tests/Mocks/MockRepository.cs +++ b/src/GitVersionCore.Tests/Mocks/MockRepository.cs @@ -10,6 +10,7 @@ public MockRepository() { Tags = new MockTagCollection(); Refs = new MockReferenceCollection(); + ObjectDatabase = new MockObjectDatabase(); } public void Dispose() diff --git a/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs b/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs index 6890b1080f..cc7f2b50be 100644 --- a/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs +++ b/src/GitVersionCore.Tests/VersionCalculation/Strategies/MergeMessageBaseVersionStrategyTests.cs @@ -17,11 +17,15 @@ public void ShouldNotAllowIncrementOfVersion() // So we shouldn't bump the version var context = new GitVersionContextBuilder().WithRepository(new MockRepository { - Head = new MockBranch("master") { new MockCommit + Head = new MockBranch("master") { - MessageEx = "Merge branch 'hotfix-0.1.5'", - ParentsEx = GetParents(true) - } } + new MockCommit + { + MessageEx = "Merge branch 'hotfix-0.1.5'", + ParentsEx = GetParents(true) + } + }, + Branches = new MockBranchCollection() }).Build(); var sut = new MergeMessageBaseVersionStrategy(); @@ -108,7 +112,8 @@ static void AssertMergeMessage(string message, string expectedVersion, List - - + + diff --git a/src/GitVersionCore/GitRepoInformation/GitRepoMetadata.cs b/src/GitVersionCore/GitRepoInformation/GitRepoMetadata.cs new file mode 100644 index 0000000000..aa1708920b --- /dev/null +++ b/src/GitVersionCore/GitRepoInformation/GitRepoMetadata.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; + +namespace GitVersion.GitRepoInformation +{ + public class GitRepoMetadata + { + public GitRepoMetadata( + List tags, MBranch currentBranch, MBranch master, + List releaseBranches) + { + Tags = tags; + MasterBranch = master; + CurrentBranch = currentBranch; + ReleaseBranches = releaseBranches; + } + + // Wonder if this can be 'mainline' + public MBranch MasterBranch { get; } + public List ReleaseBranches { get; } + public MBranch CurrentBranch { get; } + public List Tags { get; private set; } + } + + public class MTag + { + public MTag(string sha, string friendlyName, Config config, bool createdAfterHead) + { + Sha = sha; + Name = friendlyName; + CreatedAfterHead = createdAfterHead; + SemanticVersion version; + if (SemanticVersion.TryParse(friendlyName, config.TagPrefix, out version)) + { + Version = version; + } + } + + public string Sha { get; } + public string Name { get; } + public bool CreatedAfterHead { get; } + public SemanticVersion Version { get; } + } + + public class MBranch + { + public MBranch( + string name, + string tipSha, + MParent parent, + List tags, + List mergeMessages) + { + Name = name; + TipSha = tipSha; + Parent = parent; + MergeMessages = mergeMessages; + Tags = tags; + } + + public string Name { get; } + public string TipSha { get; } + public MParent Parent { get; } + public List MergeMessages { get; } + public List Tags { get; } + } + + public class MParent + { + public MParent(string mergeBase) + { + MergeBase = mergeBase; + } + + public string MergeBase { get; } + } +} diff --git a/src/GitVersionCore/GitRepoMetadataProvider.cs b/src/GitVersionCore/GitRepoInformation/GitRepoMetadataProvider.cs similarity index 84% rename from src/GitVersionCore/GitRepoMetadataProvider.cs rename to src/GitVersionCore/GitRepoInformation/GitRepoMetadataProvider.cs index 9927ed899d..b3096ae49b 100644 --- a/src/GitVersionCore/GitRepoMetadataProvider.cs +++ b/src/GitVersionCore/GitRepoInformation/GitRepoMetadataProvider.cs @@ -9,7 +9,7 @@ namespace GitVersion public class GitRepoMetadataProvider { private Dictionary> mergeBaseCommitsCache; - private Dictionary, MergeBaseData> mergeBaseCache; + private Dictionary, MergeBaseData> mergeBaseCache; private Dictionary> semanticVersionTagsOnBranchCache; private IRepository Repository { get; set; } const string missingTipFormat = "{0} has no tip. Please see http://example.com/docs for information on how to fix this."; @@ -17,7 +17,7 @@ public class GitRepoMetadataProvider public GitRepoMetadataProvider(IRepository repository, Config configuration) { - mergeBaseCache = new Dictionary, MergeBaseData>(); + mergeBaseCache = new Dictionary, MergeBaseData>(); mergeBaseCommitsCache = new Dictionary>(); semanticVersionTagsOnBranchCache = new Dictionary>(); Repository = repository; @@ -110,22 +110,36 @@ public IEnumerable GetBranchesContainingCommit(Commit commit, IList public Commit FindMergeBase(Branch branch, Branch otherBranch) { - var key = Tuple.Create(branch, otherBranch); + return FindMergeBase(branch.Tip.Sha, otherBranch.Tip.Sha, branch.FriendlyName, otherBranch.FriendlyName); + } + + /// + /// Find the merge base of the two branches, i.e. the best common ancestor of the two branches' tips. + /// + public Commit FindMergeBase(string sha1, string sha2, string sha1Desc = null, string sha2Desc = null) + { + var key = Tuple.Create(sha1, sha2); + var source1 = Repository.Lookup(sha1); + var source2 = Repository.Lookup(sha2); + var sha1Name = sha1Desc ?? sha1; + var sha2Name = sha2Desc ?? sha2; if (mergeBaseCache.ContainsKey(key)) { - Logger.WriteDebug($"Cache hit for merge base between '{branch.FriendlyName}' and '{otherBranch.FriendlyName}'."); - return mergeBaseCache[key].MergeBase; + Logger.WriteDebug($"Cache hit for merge base between '{sha1Name}' and '{sha2Name}'."); + var mergeBase = mergeBaseCache[key].MergeBase; + if (mergeBase == null) { return null; } + return Repository.Lookup(mergeBase); } - using (Logger.IndentLog($"Finding merge base between '{branch.FriendlyName}' and '{otherBranch.FriendlyName}'.")) + using (Logger.IndentLog($"Finding merge base between '{sha1Name}' and '{sha2Name}'.")) { - // Otherbranch tip is a forward merge - var commitToFindCommonBase = otherBranch.Tip; - var commit = branch.Tip; - if (otherBranch.Tip.Parents.Contains(commit)) + // source2 is a forward merge (Jake: I have no idea what this means right now....) + var commitToFindCommonBase = source2; + var commit = source1; + if (source2.Parents.Contains(commit)) { - commitToFindCommonBase = otherBranch.Tip.Parents.First(); + commitToFindCommonBase = source2.Parents.First(); } var findMergeBase = Repository.ObjectDatabase.FindMergeBase(commit, commitToFindCommonBase); @@ -172,9 +186,9 @@ public Commit FindMergeBase(Branch branch, Branch otherBranch) } // Store in cache. - mergeBaseCache.Add(key, new MergeBaseData(branch, otherBranch, Repository, findMergeBase)); + mergeBaseCache.Add(key, new MergeBaseData(sha1, sha2, findMergeBase == null ? null : findMergeBase.Sha)); - Logger.WriteInfo($"Merge base of {branch.FriendlyName}' and '{otherBranch.FriendlyName} is {findMergeBase}"); + Logger.WriteInfo($"Merge base of {sha1Name}' and '{sha2Name} is {findMergeBase}"); return findMergeBase; } } @@ -198,7 +212,7 @@ public BranchCommit FindCommitBranchWasBranchedFrom(Branch branch, params Branch return BranchCommit.Empty; } - var possibleBranches = GetMergeCommitsForBranch(branch) + var possibleBranches = GetMergeBasesForBranch(branch) .ExcludingBranches(excludedBranches) .Where(b => !branch.IsSameBranch(b.Branch)) .ToList(); @@ -216,7 +230,7 @@ public BranchCommit FindCommitBranchWasBranchedFrom(Branch branch, params Branch } } - List GetMergeCommitsForBranch(Branch branch) + List GetMergeBasesForBranch(Branch branch) { if (mergeBaseCommitsCache.ContainsKey(branch)) { @@ -259,17 +273,15 @@ List GetMergeCommitsForBranch(Branch branch) private class MergeBaseData { - public Branch Branch { get; private set; } - public Branch OtherBranch { get; private set; } - public IRepository Repository { get; private set; } + public string Sha1 { get; private set; } + public string Sha2 { get; private set; } - public Commit MergeBase { get; private set; } + public string MergeBase { get; private set; } - public MergeBaseData(Branch branch, Branch otherBranch, IRepository repository, Commit mergeBase) + public MergeBaseData(string sha1, string sha2, string mergeBase) { - Branch = branch; - OtherBranch = otherBranch; - Repository = repository; + Sha1 = sha1; + Sha2 = sha2; MergeBase = mergeBase; } } diff --git a/src/GitVersionCore/GitRepoInformation/Libgit2RepoMetadataProvider.cs b/src/GitVersionCore/GitRepoInformation/Libgit2RepoMetadataProvider.cs new file mode 100644 index 0000000000..118f675b76 --- /dev/null +++ b/src/GitVersionCore/GitRepoInformation/Libgit2RepoMetadataProvider.cs @@ -0,0 +1,87 @@ +using LibGit2Sharp; +using System.Collections.Generic; +using System.Linq; +using System; +using System.Text.RegularExpressions; + +namespace GitVersion.GitRepoInformation +{ + public class Libgit2RepoMetadataProvider + { + public static GitRepoMetadata ReadMetadata(GitVersionContext context) + { + var tags = ReadRepoTags(context); + var currentBranchInfo = ReadBranchInfo(context, context.CurrentBranch, context.CurrentCommit, tags); + var releaseBranches = ReadReleaseBranches(context, tags); + var masterBranch = context.Repository.Branches["master"]; + var masterBranchInfo = masterBranch != null ? ReadBranchInfo(context, masterBranch, masterBranch.Tip, tags) : null; + return new GitRepoMetadata( + tags, + currentBranchInfo, + masterBranchInfo, + releaseBranches); + } + + private static List ReadReleaseBranches(GitVersionContext context, List allTags) + { + var releaseBranchConfig = context.FullConfiguration.Branches + .Where(b => b.Value.IsReleaseBranch == true) + .ToList(); + + return context.Repository + .Branches + .Where(b => releaseBranchConfig.Any(c => Regex.IsMatch(b.FriendlyName, c.Key))) + .Select(b => ReadBranchInfo(context, b, b.Tip, allTags)) + .ToList(); + } + + private static List ReadRepoTags(GitVersionContext context) + { + var olderThan = context.CurrentCommit.When(); + return context.Repository + .Tags + .Where(tag => + { + var commit = tag.PeeledTarget() as Commit; + return commit != null; + }) + .Select(gitTag => + { + var commit = gitTag.PeeledTarget() as Commit; + if (commit == null) return null; + + return new MTag(gitTag.Target.Sha, gitTag.FriendlyName, context.FullConfiguration, commit.When() > olderThan); + }) + .Where(t => t != null) + .ToList(); + } + + private static MBranch ReadBranchInfo(GitVersionContext context, Branch branch, Commit at, List allTags) + { + var filter = new CommitFilter + { + IncludeReachableFrom = at ?? branch.Tip + }; + + var mergeMessages = new List(); + var branchTags = new List(); + var commits = context.Repository.Commits.QueryBy(filter); + foreach (var branchCommit in commits) + { + if (branchCommit.Parents.Count() >= 2) + { + mergeMessages.Add(new MergeMessage(branchCommit.Message, branchCommit.Sha, context.FullConfiguration)); + } + + // Adding range because the same commit may have two tags + branchTags.AddRange(allTags.Where(t => t.Sha == branchCommit.Sha)); + } + + var parentCommit = context.RepositoryMetadataProvider.FindCommitBranchWasBranchedFrom(branch); + var parent = parentCommit == null || parentCommit.Commit == null + ? null + : new MParent(parentCommit.Commit.Sha); + return new MBranch(branch.FriendlyName, at.Sha, parent, branchTags, mergeMessages); + } + } +} diff --git a/src/GitVersionCore/GitVersionContext.cs b/src/GitVersionCore/GitVersionContext.cs index 27cd14f527..4aa87d45de 100644 --- a/src/GitVersionCore/GitVersionContext.cs +++ b/src/GitVersionCore/GitVersionContext.cs @@ -1,5 +1,6 @@ namespace GitVersion { + using GitVersion.GitRepoInformation; using LibGit2Sharp; using System; using System.Linq; @@ -66,6 +67,7 @@ public GitVersionContext(IRepository repository, Branch currentBranch, Config co }) .Max(); IsCurrentCommitTagged = CurrentCommitTaggedVersion != null; + RepositoryMetadata = Libgit2RepoMetadataProvider.ReadMetadata(this); } /// @@ -80,6 +82,13 @@ public GitVersionContext(IRepository repository, Branch currentBranch, Config co public Commit CurrentCommit { get; private set; } public bool IsCurrentCommitTagged { get; private set; } public GitRepoMetadataProvider RepositoryMetadataProvider { get; private set; } + /// + /// This is a new concept which is static metadata + /// It has no runtime dependencies on libgit2 and all information is pre-calculated. + /// We should move to use this, that way libgit2 is used when bootstrapping + /// this will be far easier to optimise then all the logic for gitversion will be entirely in memory + /// + public GitRepoMetadata RepositoryMetadata { get; private set; } void CalculateEffectiveConfiguration() { diff --git a/src/GitVersionCore/GitVersionCore.csproj b/src/GitVersionCore/GitVersionCore.csproj index fea4f65af9..6f99e45bf2 100644 --- a/src/GitVersionCore/GitVersionCore.csproj +++ b/src/GitVersionCore/GitVersionCore.csproj @@ -115,7 +115,9 @@ - + + + @@ -134,6 +136,7 @@ + @@ -147,7 +150,6 @@ - diff --git a/src/GitVersionCore/MergeMessage.cs b/src/GitVersionCore/MergeMessage.cs index 1dd5ecc3ac..8acb60d54d 100644 --- a/src/GitVersionCore/MergeMessage.cs +++ b/src/GitVersionCore/MergeMessage.cs @@ -4,7 +4,7 @@ namespace GitVersion { - class MergeMessage + public class MergeMessage { static Regex parseMergeMessage = new Regex( @"^Merge (branch|tag) '(?[^']*)'", @@ -15,12 +15,12 @@ class MergeMessage static Regex smartGitMergeMessage = new Regex( @"^Finish (?.*)", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private string mergeMessage; private Config config; - public MergeMessage(string mergeMessage, Config config) + public MergeMessage(string mergeMessage, string sourceCommitSha, Config config) { - this.mergeMessage = mergeMessage; + Message = mergeMessage; + SourceCommitSha = sourceCommitSha; this.config = config; var lastIndexOf = mergeMessage.LastIndexOf("into", StringComparison.OrdinalIgnoreCase); @@ -51,19 +51,19 @@ public MergeMessage(string mergeMessage, Config config) private string ParseBranch() { - var match = parseMergeMessage.Match(mergeMessage); + var match = parseMergeMessage.Match(Message); if (match.Success) { return match.Groups["Branch"].Value; } - match = smartGitMergeMessage.Match(mergeMessage); + match = smartGitMergeMessage.Match(Message); if (match.Success) { return match.Groups["Branch"].Value; } - match = parseGitHubPullMergeMessage.Match(mergeMessage); + match = parseGitHubPullMergeMessage.Match(Message); if (match.Success) { IsMergedPullRequest = true; @@ -80,10 +80,12 @@ private string ParseBranch() return ""; } + public string Message { get; } public string TargetBranch { get; } public string MergedBranch { get; } public bool IsMergedPullRequest { get; private set; } public int? PullRequestNumber { get; private set; } public SemanticVersion Version { get; } + public string SourceCommitSha { get; private set; } } } diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageBaseVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageBaseVersionStrategy.cs index e9fac9c84c..e3fa106c5c 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageBaseVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/MergeMessageBaseVersionStrategy.cs @@ -13,40 +13,16 @@ public class MergeMessageBaseVersionStrategy : BaseVersionStrategy { public override IEnumerable GetVersions(GitVersionContext context) { - var commitsPriorToThan = context.CurrentBranch - .CommitsPriorToThan(context.CurrentCommit.When()); - var baseVersions = commitsPriorToThan - .SelectMany(c => + return context + .RepositoryMetadata + .CurrentBranch + .MergeMessages + .Where(m => m.Version != null) + .Select(m => { - SemanticVersion semanticVersion; - if (TryParse(c, context, out semanticVersion)) - { - var shouldIncrement = !context.Configuration.PreventIncrementForMergedBranchVersion; - return new[] - { - new BaseVersion(context, string.Format("Merge message '{0}'", c.Message.Trim()), shouldIncrement, semanticVersion, c, null) - }; - } - return Enumerable.Empty(); - }).ToList(); - return baseVersions; - } - - static bool TryParse(Commit mergeCommit, GitVersionContext context, out SemanticVersion semanticVersion) - { - semanticVersion = Inner(mergeCommit, context); - return semanticVersion != null; - } - - static SemanticVersion Inner(Commit mergeCommit, GitVersionContext context) - { - if (mergeCommit.Parents.Count() < 2) - { - return null; - } - - var mergeMessage = new MergeMessage(mergeCommit.Message, context.FullConfiguration); - return mergeMessage.Version; + var shouldIncrement = !context.Configuration.PreventIncrementForMergedBranchVersion; + return new BaseVersion(context, string.Format("Merge message '{0}'", m.Message.Trim()), shouldIncrement, m.Version, context.Repository.Lookup(m.SourceCommitSha), null); + }); } } } \ No newline at end of file diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs index 4955be0203..c22301fd8f 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/TaggedCommitVersionStrategy.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using LibGit2Sharp; + using GitVersion.GitRepoInformation; /// /// Version is extracted from all tags on the branch which are valid, and not newer than the current commit. @@ -14,63 +15,21 @@ public class TaggedCommitVersionStrategy : BaseVersionStrategy { public override IEnumerable GetVersions(GitVersionContext context) { - return GetTaggedVersions(context, context.CurrentBranch, context.CurrentCommit.When()); + return GetTaggedVersions(context, context.RepositoryMetadata.CurrentBranch); } - public IEnumerable GetTaggedVersions(GitVersionContext context, Branch currentBranch, DateTimeOffset? olderThan) + public IEnumerable GetTaggedVersions(GitVersionContext context, MBranch currentBranch) { - var allTags = context.Repository.Tags - .Where(tag => !olderThan.HasValue || ((Commit) tag.PeeledTarget()).When() <= olderThan.Value) - .ToList(); - var tagsOnBranch = currentBranch - .Commits - .SelectMany(commit => { return allTags.Where(t => IsValidTag(t, commit)); }) - .Select(t => - { - SemanticVersion version; - if (SemanticVersion.TryParse(t.FriendlyName, context.Configuration.GitTagPrefix, out version)) - { - var commit = t.PeeledTarget() as Commit; - if (commit != null) - return new VersionTaggedCommit(commit, version, t.FriendlyName); - } - return null; - }) - .Where(a => a != null) - .ToList(); - - return tagsOnBranch.Select(t => CreateBaseVersion(context, t)); - } - - BaseVersion CreateBaseVersion(GitVersionContext context, VersionTaggedCommit version) - { - var shouldUpdateVersion = version.Commit.Sha != context.CurrentCommit.Sha; - var baseVersion = new BaseVersion(context, FormatSource(version), shouldUpdateVersion, version.SemVer, version.Commit, null); - return baseVersion; + return currentBranch + .Tags + .Where(t => t.Version != null) + .Select(t => CreateBaseVersion(context, t)); } - protected virtual string FormatSource(VersionTaggedCommit version) + BaseVersion CreateBaseVersion(GitVersionContext context, MTag tag) { - return string.Format("Git tag '{0}'", version.Tag); - } - - protected virtual bool IsValidTag(Tag tag, Commit commit) - { - return tag.PeeledTarget() == commit; - } - - protected class VersionTaggedCommit - { - public string Tag; - public Commit Commit; - public SemanticVersion SemVer; - - public VersionTaggedCommit(Commit commit, SemanticVersion semVer, string tag) - { - Tag = tag; - Commit = commit; - SemVer = semVer; - } + var shouldUpdateVersion = tag.Sha != context.CurrentCommit.Sha; + return new BaseVersion(context, $"Git tag '{tag.Name}'", shouldUpdateVersion, tag.Version, context.Repository.Lookup(tag.Sha), null); } } } \ No newline at end of file diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs index db2546e06a..59959cf4d5 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using LibGit2Sharp; + using GitVersion.GitRepoInformation; /// /// Version is extracted from the name of the branch. @@ -13,21 +14,21 @@ public class VersionInBranchNameBaseVersionStrategy : BaseVersionStrategy { public override IEnumerable GetVersions(GitVersionContext context) { - var currentBranch = context.CurrentBranch; + var currentBranch = context.RepositoryMetadata.CurrentBranch; var tagPrefixRegex = context.Configuration.GitTagPrefix; var repository = context.Repository; return GetVersions(context, tagPrefixRegex, currentBranch, repository); } - public IEnumerable GetVersions(GitVersionContext context, string tagPrefixRegex, Branch currentBranch, IRepository repository) + public IEnumerable GetVersions(GitVersionContext context, string tagPrefixRegex, MBranch currentBranch, IRepository repository) { - var branchName = currentBranch.FriendlyName; + var branchName = currentBranch.Name; var versionInBranch = GetVersionInBranch(branchName, tagPrefixRegex); if (versionInBranch != null) { - var commitBranchWasBranchedFrom = context.RepositoryMetadataProvider.FindCommitBranchWasBranchedFrom(currentBranch); var branchNameOverride = branchName.RegexReplace("[-/]" + versionInBranch.Item1, string.Empty); - yield return new BaseVersion(context, "Version in branch name", false, versionInBranch.Item2, commitBranchWasBranchedFrom.Commit, branchNameOverride); + var baseCommit = repository.Lookup(currentBranch.Parent.MergeBase); + yield return new BaseVersion(context, "Version in branch name", false, versionInBranch.Item2, baseCommit, branchNameOverride); } } diff --git a/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs index 4d27c77d79..fd5d871213 100644 --- a/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/MainlineVersionCalculator.cs @@ -199,7 +199,7 @@ private static VersionField TryFindIncrementFromMergeMessage(Commit mergeCommit, { if (mergeCommit != null) { - var mergeMessage = new MergeMessage(mergeCommit.Message, context.FullConfiguration); + var mergeMessage = new MergeMessage(mergeCommit.Message, mergeCommit.Sha, context.FullConfiguration); if (mergeMessage.MergedBranch != null) { var config = context.FullConfiguration.GetConfigForBranch(mergeMessage.MergedBranch); diff --git a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs index 3cb2a5e145..0385b1651e 100644 --- a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs @@ -19,7 +19,7 @@ public NextVersionCalculator(IBaseVersionCalculator baseVersionCalculator = null new TaggedCommitVersionStrategy(), new MergeMessageBaseVersionStrategy(), new VersionInBranchNameBaseVersionStrategy(), - new TrackReleaseBranchesVersionStrategy()); + new TrackReleaseBranchesVersionStrategy(() => baseVersionFinder)); } public SemanticVersion FindVersion(GitVersionContext context) diff --git a/src/GitVersionCore/VersionCalculation/DevelopVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/TrackReleaseBranchesVersionStrategy.cs similarity index 62% rename from src/GitVersionCore/VersionCalculation/DevelopVersionStrategy.cs rename to src/GitVersionCore/VersionCalculation/TrackReleaseBranchesVersionStrategy.cs index 99cea3298d..4a2defe722 100644 --- a/src/GitVersionCore/VersionCalculation/DevelopVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/TrackReleaseBranchesVersionStrategy.cs @@ -6,6 +6,8 @@ namespace GitVersion.VersionCalculation using BaseVersionCalculators; using GitTools; using LibGit2Sharp; + using System; + using GitVersion.GitRepoInformation; /// /// Active only when the branch is marked as IsDevelop. @@ -27,11 +29,28 @@ public class TrackReleaseBranchesVersionStrategy : BaseVersionStrategy { VersionInBranchNameBaseVersionStrategy releaseVersionStrategy = new VersionInBranchNameBaseVersionStrategy(); TaggedCommitVersionStrategy taggedCommitVersionStrategy = new TaggedCommitVersionStrategy(); + Func getBaseVersionCalculator; + + public TrackReleaseBranchesVersionStrategy(Func getBaseVersionCalculator) + { + this.getBaseVersionCalculator = getBaseVersionCalculator; + } public override IEnumerable GetVersions(GitVersionContext context) { if (context.Configuration.TracksReleaseBranches) { + + + // I feel this is actually a recursive path for GitVersion and rather than + // having all this logic in here, we should just run it for each release branch and master + + // Something like this, but will need to refactor BaseVersionStrategy + //var baseVersionCalculator = getBaseVersionCalculator(); + //context.RepositoryMetadata.ReleaseBranches + // .Select(r => baseVersionCalculator.GetBaseVersion() + + return ReleaseBranchBaseVersions(context).Union(MasterTagsVersions(context)); } @@ -40,10 +59,10 @@ public override IEnumerable GetVersions(GitVersionContext context) private IEnumerable MasterTagsVersions(GitVersionContext context) { - var master = context.Repository.FindBranch("master"); + var master = context.RepositoryMetadata.MasterBranch; if (master != null) { - return taggedCommitVersionStrategy.GetTaggedVersions(context, master, null); + return taggedCommitVersionStrategy.GetTaggedVersions(context, master); } return new BaseVersion[0]; @@ -51,40 +70,32 @@ private IEnumerable MasterTagsVersions(GitVersionContext context) private IEnumerable ReleaseBranchBaseVersions(GitVersionContext context) { - var releaseBranchConfig = context.FullConfiguration.Branches - .Where(b => b.Value.IsReleaseBranch == true) + return context + .RepositoryMetadata + .ReleaseBranches + .SelectMany(b => GetReleaseVersion(context, b)) + .Select(baseVersion => + { + // Need to drop branch overrides and give a bit more context about + // where this version came from + var source1 = "Release branch exists -> " + baseVersion.Source; + return new BaseVersion(context, + source1, + baseVersion.ShouldIncrement, + baseVersion.SemanticVersion, + baseVersion.BaseVersionSource, + null); + }) .ToList(); - if (releaseBranchConfig.Any()) - { - var releaseBranches = context.Repository.Branches - .Where(b => releaseBranchConfig.Any(c => Regex.IsMatch(b.FriendlyName, c.Key))); - - return releaseBranches - .SelectMany(b => GetReleaseVersion(context, b)) - .Select(baseVersion => - { - // Need to drop branch overrides and give a bit more context about - // where this version came from - var source1 = "Release branch exists -> " + baseVersion.Source; - return new BaseVersion(context, - source1, - baseVersion.ShouldIncrement, - baseVersion.SemanticVersion, - baseVersion.BaseVersionSource, - null); - }) - .ToList(); - } - return new BaseVersion[0]; } - IEnumerable GetReleaseVersion(GitVersionContext context, Branch releaseBranch) + IEnumerable GetReleaseVersion(GitVersionContext context, MBranch releaseBranch) { var tagPrefixRegex = context.Configuration.GitTagPrefix; var repository = context.Repository; // Find the commit where the child branch was created. - var baseSource = context.RepositoryMetadataProvider.FindMergeBase(releaseBranch, context.CurrentBranch); + var baseSource = context.RepositoryMetadataProvider.FindMergeBase(releaseBranch.TipSha, context.CurrentCommit.Sha); if (baseSource == context.CurrentCommit) { // Ignore the branch if it has no commits. diff --git a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj index 0de79326e5..95d4eb488b 100644 --- a/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj +++ b/src/GitVersionExe.Tests/GitVersionExe.Tests.csproj @@ -1,5 +1,6 @@  + @@ -67,9 +68,8 @@ ..\packages\NSubstitute.1.10.0.0\lib\net45\NSubstitute.dll True - - ..\packages\NUnit.3.6.0\lib\net45\nunit.framework.dll - True + + ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll ..\packages\Shouldly.2.7.0\lib\net40\Shouldly.dll @@ -147,5 +147,6 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + \ No newline at end of file diff --git a/src/GitVersionExe.Tests/packages.config b/src/GitVersionExe.Tests/packages.config index 30641d4d15..22e547e4ae 100644 --- a/src/GitVersionExe.Tests/packages.config +++ b/src/GitVersionExe.Tests/packages.config @@ -6,7 +6,7 @@ - - + + \ No newline at end of file diff --git a/src/GitVersionTask.Tests/GitVersionTask.Tests.csproj b/src/GitVersionTask.Tests/GitVersionTask.Tests.csproj index 8fcf5aa054..415eafcfe1 100644 --- a/src/GitVersionTask.Tests/GitVersionTask.Tests.csproj +++ b/src/GitVersionTask.Tests/GitVersionTask.Tests.csproj @@ -1,5 +1,6 @@  + Debug @@ -87,9 +88,8 @@ ..\packages\NSubstitute.1.10.0.0\lib\net45\NSubstitute.dll True - - ..\packages\NUnit.3.6.0\lib\net45\nunit.framework.dll - True + + ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll ..\packages\ObjectApproval.1.3.0\lib\NET40\ObjectApproval.dll @@ -191,6 +191,7 @@ + \ No newline at end of file diff --git a/src/GitVersionTask.Tests/packages.config b/src/GitVersionTask.Tests/packages.config index 856ed3b632..b41a4ea0d7 100644 --- a/src/GitVersionTask.Tests/packages.config +++ b/src/GitVersionTask.Tests/packages.config @@ -14,8 +14,8 @@ - - + +