diff --git a/src/GitVersionCore.Tests/ConfigProviderTests.cs b/src/GitVersionCore.Tests/ConfigProviderTests.cs index 008862d405..e4dfdd5710 100644 --- a/src/GitVersionCore.Tests/ConfigProviderTests.cs +++ b/src/GitVersionCore.Tests/ConfigProviderTests.cs @@ -322,7 +322,7 @@ public void WarnOnObsoleteIsDevelopBranchConfigurationSetting() LegacyConfigNotifier.Notify(new StringReader(text)); }); - var expecedMessage = string.Format("'is-develop' is deprecated, use 'track-release-branches' instead."); - exception.Message.ShouldContain(expecedMessage); + const string expectedMessage = @"'is-develop' is deprecated, use 'track-release-branches' instead."; + exception.Message.ShouldContain(expectedMessage); } } diff --git a/src/GitVersionCore.Tests/IntegrationTests/FeatureBranchScenarios.cs b/src/GitVersionCore.Tests/IntegrationTests/FeatureBranchScenarios.cs index 5e65ab2c53..1867740b0c 100644 --- a/src/GitVersionCore.Tests/IntegrationTests/FeatureBranchScenarios.cs +++ b/src/GitVersionCore.Tests/IntegrationTests/FeatureBranchScenarios.cs @@ -514,9 +514,8 @@ public void PickUpVersionFromMasterMarkedWithIsTracksReleaseBranches() fixture.AssertFullSemver(config, "0.10.1-pre.1+1"); // create a feature branch from master and verify the version - // TODO this will pass once default becomes inherit - //fixture.BranchTo("MyFeatureD"); - //fixture.AssertFullSemver(config, "0.10.1-MyFeatureD.1+1"); + fixture.BranchTo("MyFeatureD"); + fixture.AssertFullSemver(config, "0.10.1-MyFeatureD.1+1"); } } } diff --git a/src/GitVersionCore/BranchConfigurationCalculator.cs b/src/GitVersionCore/BranchConfigurationCalculator.cs index 14f314cf94..e6e05d3489 100644 --- a/src/GitVersionCore/BranchConfigurationCalculator.cs +++ b/src/GitVersionCore/BranchConfigurationCalculator.cs @@ -12,9 +12,9 @@ public class BranchConfigurationCalculator /// /// Gets the for the current commit. /// - public static BranchConfig GetBranchConfiguration(Commit currentCommit, IRepository repository, bool onlyEvaluateTrackedBranches, Config config, Branch currentBranch, IList excludedInheritBranches = null) + public static BranchConfig GetBranchConfiguration(GitVersionContext context, Branch targetBranch, IList excludedInheritBranches = null) { - var matchingBranches = LookupBranchConfiguration(config, currentBranch).ToArray(); + var matchingBranches = LookupBranchConfiguration(context.FullConfiguration, targetBranch).ToArray(); BranchConfig branchConfiguration; if (matchingBranches.Length > 0) @@ -25,7 +25,7 @@ public static BranchConfig GetBranchConfiguration(Commit currentCommit, IReposit { Logger.WriteWarning(string.Format( "Multiple branch configurations match the current branch branchName of '{0}'. Using the first matching configuration, '{1}'. Matching configurations include: '{2}'", - currentBranch.FriendlyName, + targetBranch.FriendlyName, branchConfiguration.Name, string.Join("', '", matchingBranches.Select(b => b.Name)))); } @@ -34,14 +34,14 @@ public static BranchConfig GetBranchConfiguration(Commit currentCommit, IReposit { Logger.WriteInfo(string.Format( "No branch configuration found for branch {0}, falling back to default configuration", - currentBranch.FriendlyName)); + targetBranch.FriendlyName)); branchConfiguration = new BranchConfig { Name = string.Empty }; - ConfigurationProvider.ApplyBranchDefaults(config, branchConfiguration, ""); + ConfigurationProvider.ApplyBranchDefaults(context.FullConfiguration, branchConfiguration, ""); } return branchConfiguration.Increment == IncrementStrategy.Inherit ? - InheritBranchConfiguration(onlyEvaluateTrackedBranches, repository, currentCommit, currentBranch, branchConfiguration, config, excludedInheritBranches) : + InheritBranchConfiguration(context, targetBranch, branchConfiguration, excludedInheritBranches) : branchConfiguration; } @@ -60,16 +60,18 @@ static IEnumerable LookupBranchConfiguration([NotNull] Config conf return config.Branches.Where(b => Regex.IsMatch(currentBranch.FriendlyName, "^" + b.Value.Regex, RegexOptions.IgnoreCase)).Select(kvp => kvp.Value); } - static BranchConfig InheritBranchConfiguration(bool onlyEvaluateTrackedBranches, IRepository repository, Commit currentCommit, Branch currentBranch, BranchConfig branchConfiguration, Config config, IList excludedInheritBranches) + static BranchConfig InheritBranchConfiguration(GitVersionContext context, Branch targetBranch, BranchConfig branchConfiguration, IList excludedInheritBranches) { + var repository = context.Repository; + var config = context.FullConfiguration; using (Logger.IndentLog("Attempting to inherit branch configuration from parent branch")) { - var excludedBranches = new[] { currentBranch }; + var excludedBranches = new[] { targetBranch }; // Check if we are a merge commit. If so likely we are a pull request - var parentCount = currentCommit.Parents.Count(); + var parentCount = context.CurrentCommit.Parents.Count(); if (parentCount == 2) { - excludedBranches = CalculateWhenMultipleParents(repository, currentCommit, ref currentBranch, excludedBranches); + excludedBranches = CalculateWhenMultipleParents(repository, context.CurrentCommit, ref targetBranch, excludedBranches); } if (excludedInheritBranches == null) @@ -83,14 +85,19 @@ static BranchConfig InheritBranchConfiguration(bool onlyEvaluateTrackedBranches, return (branchConfig.Length != 1) || (branchConfig.Length == 1 && branchConfig[0].Increment == IncrementStrategy.Inherit); }).ToList(); } - excludedBranches.ToList().ForEach(excludedInheritBranches.Add); + // Add new excluded branches. + foreach (var excludedBranch in excludedBranches.ExcludingBranches(excludedInheritBranches)) + { + excludedInheritBranches.Add(excludedBranch); + } var branchesToEvaluate = repository.Branches.Except(excludedInheritBranches).ToList(); - var branchPoint = currentBranch.FindCommitBranchWasBranchedFrom(repository, excludedInheritBranches.ToArray()); + var branchPoint = context.RepositoryMetadataProvider + .FindCommitBranchWasBranchedFrom(targetBranch, excludedInheritBranches.ToArray()); List possibleParents; if (branchPoint == BranchCommit.Empty) { - possibleParents = currentCommit.GetBranchesContainingCommit(repository, branchesToEvaluate, true) + possibleParents = context.RepositoryMetadataProvider.GetBranchesContainingCommit(context.CurrentCommit, branchesToEvaluate, true) // It fails to inherit Increment branch configuration if more than 1 parent; // therefore no point to get more than 2 parents .Take(2) @@ -98,10 +105,12 @@ static BranchConfig InheritBranchConfiguration(bool onlyEvaluateTrackedBranches, } else { - var branches = branchPoint.Commit.GetBranchesContainingCommit(repository, branchesToEvaluate, true).ToList(); + var branches = context.RepositoryMetadataProvider + .GetBranchesContainingCommit(branchPoint.Commit, branchesToEvaluate, true).ToList(); if (branches.Count > 1) { - var currentTipBranches = currentCommit.GetBranchesContainingCommit(repository, branchesToEvaluate, true).ToList(); + var currentTipBranches = context.RepositoryMetadataProvider + .GetBranchesContainingCommit(context.CurrentCommit, branchesToEvaluate, true).ToList(); possibleParents = branches.Except(currentTipBranches).ToList(); } else @@ -114,7 +123,7 @@ static BranchConfig InheritBranchConfiguration(bool onlyEvaluateTrackedBranches, if (possibleParents.Count == 1) { - var branchConfig = GetBranchConfiguration(currentCommit, repository, onlyEvaluateTrackedBranches, config, possibleParents[0], excludedInheritBranches); + var branchConfig = GetBranchConfiguration(context, possibleParents[0], excludedInheritBranches); return new BranchConfig(branchConfiguration) { Increment = branchConfig.Increment, @@ -144,7 +153,17 @@ static BranchConfig InheritBranchConfiguration(bool onlyEvaluateTrackedBranches, var branchName = chosenBranch.FriendlyName; Logger.WriteWarning(errorMessage + Environment.NewLine + Environment.NewLine + "Falling back to " + branchName + " branch config"); - var inheritingBranchConfig = GetBranchConfiguration(currentCommit, repository, onlyEvaluateTrackedBranches, config, chosenBranch); + // To prevent infinite loops, make sure that a new branch was chosen. + if (targetBranch.IsSameBranch(chosenBranch)) + { + Logger.WriteWarning("Fallback branch wants to inherit Increment branch configuration from itself. Using patch increment instead."); + return new BranchConfig(branchConfiguration) + { + Increment = IncrementStrategy.Patch + }; + } + + var inheritingBranchConfig = GetBranchConfiguration(context, chosenBranch, excludedInheritBranches); return new BranchConfig(branchConfiguration) { Increment = inheritingBranchConfig.Increment, diff --git a/src/GitVersionCore/Configuration/ConfigurationProvider.cs b/src/GitVersionCore/Configuration/ConfigurationProvider.cs index 4801c7d879..3d803d3fd3 100644 --- a/src/GitVersionCore/Configuration/ConfigurationProvider.cs +++ b/src/GitVersionCore/Configuration/ConfigurationProvider.cs @@ -2,7 +2,6 @@ namespace GitVersion { using Configuration.Init.Wizard; using GitVersion.Helpers; - using System; using System.IO; using System.Linq; using System.Text; diff --git a/src/GitVersionCore/GitRepoMetadataProvider.cs b/src/GitVersionCore/GitRepoMetadataProvider.cs new file mode 100644 index 0000000000..4f329e22f9 --- /dev/null +++ b/src/GitVersionCore/GitRepoMetadataProvider.cs @@ -0,0 +1,231 @@ +using JetBrains.Annotations; +using LibGit2Sharp; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GitVersion +{ + public class GitRepoMetadataProvider + { + private Dictionary> mergeBaseCommitsCache; + 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."; + + public GitRepoMetadataProvider(IRepository repository) + { + mergeBaseCache = new Dictionary, MergeBaseData>(); + mergeBaseCommitsCache = new Dictionary>(); + semanticVersionTagsOnBranchCache = new Dictionary>(); + this.Repository = repository; + } + + public IEnumerable GetVersionTagsOnBranch(Branch branch, string tagPrefixRegex) + { + if (semanticVersionTagsOnBranchCache.ContainsKey(branch)) + { + Logger.WriteDebug(string.Format("Cache hit for version tags on branch '{0}", branch.CanonicalName)); + return semanticVersionTagsOnBranchCache[branch]; + } + + using (Logger.IndentLog(string.Format("Getting version tags from branch '{0}'.", branch.CanonicalName))) + { + var tags = this.Repository.Tags.Select(t => t).ToList(); + + var versionTags = this.Repository.Commits.QueryBy(new CommitFilter + { + IncludeReachableFrom = branch.Tip + }) + .SelectMany(c => tags.Where(t => c.Sha == t.Target.Sha).SelectMany(t => + { + SemanticVersion semver; + if (SemanticVersion.TryParse(t.FriendlyName, tagPrefixRegex, out semver)) + return new[] { semver }; + return new SemanticVersion[0]; + })).ToList(); + + semanticVersionTagsOnBranchCache.Add(branch, versionTags); + return versionTags; + } + } + + // TODO Should we cache this? + public IEnumerable GetBranchesContainingCommit([NotNull] Commit commit, IList branches, bool onlyTrackedBranches) + { + if (commit == null) + { + throw new ArgumentNullException("commit"); + } + + using (Logger.IndentLog(string.Format("Getting branches containing the commit '{0}'.", commit.Id))) + { + var directBranchHasBeenFound = false; + Logger.WriteInfo("Trying to find direct branches."); + // TODO: It looks wasteful looping through the branches twice. Can't these loops be merged somehow? @asbjornu + foreach (var branch in branches) + { + if (branch.Tip != null && branch.Tip.Sha != commit.Sha || (onlyTrackedBranches && !branch.IsTracking)) + { + continue; + } + + directBranchHasBeenFound = true; + Logger.WriteInfo(string.Format("Direct branch found: '{0}'.", branch.FriendlyName)); + yield return branch; + } + + if (directBranchHasBeenFound) + { + yield break; + } + + Logger.WriteInfo(string.Format("No direct branches found, searching through {0} branches.", onlyTrackedBranches ? "tracked" : "all")); + foreach (var branch in branches.Where(b => onlyTrackedBranches && !b.IsTracking)) + { + Logger.WriteInfo(string.Format("Searching for commits reachable from '{0}'.", branch.FriendlyName)); + + var commits = this.Repository.Commits.QueryBy(new CommitFilter + { + IncludeReachableFrom = branch + }).Where(c => c.Sha == commit.Sha); + + if (!commits.Any()) + { + Logger.WriteInfo(string.Format("The branch '{0}' has no matching commits.", branch.FriendlyName)); + continue; + } + + Logger.WriteInfo(string.Format("The branch '{0}' has a matching commit.", branch.FriendlyName)); + yield return branch; + } + } + } + + /// + /// Find the merge base of the two branches, i.e. the best common ancestor of the two branches' tips. + /// + public Commit FindMergeBase(Branch branch, Branch otherBranch) + { + var key = Tuple.Create(branch, otherBranch); + + if (mergeBaseCache.ContainsKey(key)) + { + Logger.WriteDebug(string.Format( + "Cache hit for merge base between '{0}' and '{1}'.", + branch.FriendlyName, otherBranch.FriendlyName)); + return mergeBaseCache[key].MergeBase; + } + + using (Logger.IndentLog(string.Format("Finding merge base between '{0}' and '{1}'.", branch.FriendlyName, otherBranch.FriendlyName))) + { + // Otherbranch tip is a forward merge + var commitToFindCommonBase = otherBranch.Tip; + var commit = branch.Tip; + if (otherBranch.Tip.Parents.Contains(commit)) + { + commitToFindCommonBase = otherBranch.Tip.Parents.First(); + } + + var findMergeBase = this.Repository.ObjectDatabase.FindMergeBase(commit, commitToFindCommonBase); + if (findMergeBase != null) + { + Logger.WriteInfo(string.Format("Found merge base of {0}", findMergeBase.Sha)); + // We do not want to include merge base commits which got forward merged into the other branch + bool mergeBaseWasForwardMerge; + do + { + // Now make sure that the merge base is not a forward merge + mergeBaseWasForwardMerge = otherBranch.Commits + .SkipWhile(c => c != commitToFindCommonBase) + .TakeWhile(c => c != findMergeBase) + .Any(c => c.Parents.Contains(findMergeBase)); + if (mergeBaseWasForwardMerge) + { + var second = commitToFindCommonBase.Parents.First(); + var mergeBase = this.Repository.ObjectDatabase.FindMergeBase(commit, second); + if (mergeBase == findMergeBase) + { + break; + } + findMergeBase = mergeBase; + Logger.WriteInfo(string.Format("Merge base was due to a forward merge, next merge base is {0}", findMergeBase)); + } + } while (mergeBaseWasForwardMerge); + } + + // Store in cache. + mergeBaseCache.Add(key, new MergeBaseData(branch, otherBranch, this.Repository, findMergeBase)); + + return findMergeBase; + } + } + + /// + /// Find the commit where the given branch was branched from another branch. + /// If there are multiple such commits and branches, returns the newest commit. + /// + public BranchCommit FindCommitBranchWasBranchedFrom([NotNull] Branch branch, params Branch[] excludedBranches) + { + if (branch == null) + { + throw new ArgumentNullException("branch"); + } + + using (Logger.IndentLog(string.Format("Finding branch source of '{0}'", branch.FriendlyName))) + { + if (branch.Tip == null) + { + Logger.WriteWarning(string.Format(missingTipFormat, branch.FriendlyName)); + return BranchCommit.Empty; + } + + return GetMergeCommitsForBranch(branch).ExcludingBranches(excludedBranches).FirstOrDefault(b => !branch.IsSameBranch(b.Branch)); + } + } + + List GetMergeCommitsForBranch(Branch branch) + { + if (mergeBaseCommitsCache.ContainsKey(branch)) + { + Logger.WriteDebug(string.Format( + "Cache hit for getting merge commits for branch {0}.", + branch.CanonicalName)); + return mergeBaseCommitsCache[branch]; + } + + var branchMergeBases = Repository.Branches.Select(otherBranch => + { + if (otherBranch.Tip == null) + { + Logger.WriteWarning(string.Format(missingTipFormat, otherBranch.FriendlyName)); + return BranchCommit.Empty; + } + + var findMergeBase = FindMergeBase(branch, otherBranch); + return new BranchCommit(findMergeBase, otherBranch); + }).Where(b => b.Commit != null).OrderByDescending(b => b.Commit.Committer.When).ToList(); + mergeBaseCommitsCache.Add(branch, branchMergeBases); + + return branchMergeBases; + } + + private class MergeBaseData + { + public Branch Branch { get; private set; } + public Branch OtherBranch { get; private set; } + public IRepository Repository { get; private set; } + + public Commit MergeBase { get; private set; } + + public MergeBaseData(Branch branch, Branch otherBranch, IRepository repository, Commit mergeBase) + { + Branch = branch; + OtherBranch = otherBranch; + Repository = repository; + MergeBase = mergeBase; + } + } + } +} \ No newline at end of file diff --git a/src/GitVersionCore/GitVersionCacheKeyFactory.cs b/src/GitVersionCore/GitVersionCacheKeyFactory.cs index f81dea37ee..a918d67001 100644 --- a/src/GitVersionCore/GitVersionCacheKeyFactory.cs +++ b/src/GitVersionCore/GitVersionCacheKeyFactory.cs @@ -123,7 +123,15 @@ static List CalculateDirectoryContents(string root) private static string GetRepositorySnapshotHash(GitPreparer gitPreparer) { - var repositorySnapshot = gitPreparer.WithRepository(repo => string.Join(":", repo.Head.CanonicalName, repo.Head.Tip.Sha)); + var repositorySnapshot = gitPreparer.WithRepository(repo => { + var head = repo.Head; + if (head.Tip == null) + { + return head.CanonicalName; + } + var hash = string.Join(":", head.CanonicalName, head.Tip.Sha); + return hash; + }); return GetHash(repositorySnapshot); } diff --git a/src/GitVersionCore/GitVersionContext.cs b/src/GitVersionCore/GitVersionContext.cs index 1e8c869866..1b5f57e195 100644 --- a/src/GitVersionCore/GitVersionContext.cs +++ b/src/GitVersionCore/GitVersionContext.cs @@ -17,6 +17,7 @@ public GitVersionContext(IRepository repository, Config configuration, bool isFo public GitVersionContext(IRepository repository, Branch currentBranch, Config configuration, bool onlyEvaluateTrackedBranches = true, string commitId = null) { Repository = repository; + RepositoryMetadataProvider = new GitRepoMetadataProvider(repository); FullConfiguration = configuration; OnlyEvaluateTrackedBranches = onlyEvaluateTrackedBranches; @@ -46,7 +47,7 @@ public GitVersionContext(IRepository repository, Branch currentBranch, Config co if (currentBranch.IsDetachedHead()) { - CurrentBranch = CurrentCommit.GetBranchesContainingCommit(repository, repository.Branches.ToList(), OnlyEvaluateTrackedBranches).OnlyOrDefault() ?? currentBranch; + CurrentBranch = RepositoryMetadataProvider.GetBranchesContainingCommit(CurrentCommit, repository.Branches.ToList(), OnlyEvaluateTrackedBranches).OnlyOrDefault() ?? currentBranch; } else { @@ -78,10 +79,11 @@ public GitVersionContext(IRepository repository, Branch currentBranch, Config co public Branch CurrentBranch { get; private set; } public Commit CurrentCommit { get; private set; } public bool IsCurrentCommitTagged { get; private set; } + public GitRepoMetadataProvider RepositoryMetadataProvider { get; private set; } void CalculateEffectiveConfiguration() { - var currentBranchConfig = BranchConfigurationCalculator.GetBranchConfiguration(CurrentCommit, Repository, OnlyEvaluateTrackedBranches, FullConfiguration, CurrentBranch); + var currentBranchConfig = BranchConfigurationCalculator.GetBranchConfiguration(this, CurrentBranch); if (!currentBranchConfig.VersioningMode.HasValue) throw new Exception(string.Format("Configuration value for 'Versioning mode' for branch {0} has no value. (this should not happen, please report an issue)", currentBranchConfig.Name)); diff --git a/src/GitVersionCore/GitVersionCore.csproj b/src/GitVersionCore/GitVersionCore.csproj index 57d35345e1..ef715fea33 100644 --- a/src/GitVersionCore/GitVersionCore.csproj +++ b/src/GitVersionCore/GitVersionCore.csproj @@ -122,6 +122,7 @@ + diff --git a/src/GitVersionCore/GitVersionFinder.cs b/src/GitVersionCore/GitVersionFinder.cs index 926a421933..4163d23a62 100644 --- a/src/GitVersionCore/GitVersionFinder.cs +++ b/src/GitVersionCore/GitVersionFinder.cs @@ -8,7 +8,10 @@ public class GitVersionFinder { public SemanticVersion FindVersion(GitVersionContext context) { - Logger.WriteInfo(string.Format("Running against branch: {0} ({1})", context.CurrentBranch.FriendlyName, context.CurrentCommit.Sha)); + Logger.WriteInfo(string.Format( + "Running against branch: {0} ({1})", + context.CurrentBranch.FriendlyName, + context.CurrentCommit == null ? "-" : context.CurrentCommit.Sha)); EnsureMainTopologyConstraints(context); var filePath = Path.Combine(context.Repository.GetRepositoryDirectory(), "NextVersion.txt"); diff --git a/src/GitVersionCore/LibGitExtensions.cs b/src/GitVersionCore/LibGitExtensions.cs index 130cc91c76..3f009112f0 100644 --- a/src/GitVersionCore/LibGitExtensions.cs +++ b/src/GitVersionCore/LibGitExtensions.cs @@ -15,112 +15,10 @@ public static DateTimeOffset When(this Commit commit) return commit.Committer.When; } - public static IEnumerable GetVersionTagsOnBranch(this Branch branch, IRepository repository, string tagPrefixRegex) - { - var tags = repository.Tags.Select(t => t).ToList(); - - return repository.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = branch.Tip - }) - .SelectMany(c => tags.Where(t => c.Sha == t.Target.Sha).SelectMany(t => - { - SemanticVersion semver; - if (SemanticVersion.TryParse(t.FriendlyName, tagPrefixRegex, out semver)) - return new [] { semver }; - return new SemanticVersion[0]; - })); - } - - /// - /// Find the commit where the given branch was branched from another branch. - /// If there are multiple such commits and branches, returns the newest commit. - /// - public static BranchCommit FindCommitBranchWasBranchedFrom([NotNull] this Branch branch, IRepository repository, params Branch[] excludedBranches) - { - const string missingTipFormat = "{0} has no tip. Please see http://example.com/docs for information on how to fix this."; - - if (branch == null) - { - throw new ArgumentNullException("branch"); - } - - using (Logger.IndentLog(string.Format("Finding branch source of '{0}'", branch.FriendlyName))) - { - if (branch.Tip == null) - { - Logger.WriteWarning(string.Format(missingTipFormat, branch.FriendlyName)); - return BranchCommit.Empty; - } - - var otherBranches = repository.Branches - .ExcludingBranches(excludedBranches) - .Where(b => !IsSameBranch(branch, b)); - var mergeBases = otherBranches.Select(otherBranch => - { - if (otherBranch.Tip == null) - { - Logger.WriteWarning(string.Format(missingTipFormat, otherBranch.FriendlyName)); - return BranchCommit.Empty; - } - - var findMergeBase = FindMergeBase(branch, otherBranch, repository); - return new BranchCommit(findMergeBase,otherBranch); - }).Where(b => b.Commit != null).OrderByDescending(b => b.Commit.Committer.When); - - return mergeBases.FirstOrDefault(); - } - } - - /// - /// Find the merge base of the two branches, i.e. the best common ancestor of the two branches' tips. - /// - public static Commit FindMergeBase(this Branch branch, Branch otherBranch, IRepository repository) - { - using (Logger.IndentLog(string.Format("Finding merge base between '{0}' and '{1}'.", branch.FriendlyName, otherBranch.FriendlyName))) - { - // Otherbranch tip is a forward merge - var commitToFindCommonBase = otherBranch.Tip; - var commit = branch.Tip; - if (otherBranch.Tip.Parents.Contains(commit)) - { - commitToFindCommonBase = otherBranch.Tip.Parents.First(); - } - - var findMergeBase = repository.ObjectDatabase.FindMergeBase(commit, commitToFindCommonBase); - if (findMergeBase != null) - { - Logger.WriteInfo(string.Format("Found merge base of {0}", findMergeBase.Sha)); - // We do not want to include merge base commits which got forward merged into the other branch - bool mergeBaseWasForwardMerge; - do - { - // Now make sure that the merge base is not a forward merge - mergeBaseWasForwardMerge = otherBranch.Commits - .SkipWhile(c => c != commitToFindCommonBase) - .TakeWhile(c => c != findMergeBase) - .Any(c => c.Parents.Contains(findMergeBase)); - if (mergeBaseWasForwardMerge) - { - var second = commitToFindCommonBase.Parents.First(); - var mergeBase = repository.ObjectDatabase.FindMergeBase(commit, second); - if (mergeBase == findMergeBase) - { - break; - } - findMergeBase = mergeBase; - Logger.WriteInfo(string.Format("Merge base was due to a forward merge, next merge base is {0}", findMergeBase)); - } - } while (mergeBaseWasForwardMerge); - } - return findMergeBase; - } - } - /// /// Checks if the two branch objects refer to the same branch (have the same friendly name). /// - public static bool IsSameBranch(Branch branch, Branch otherBranch) + public static bool IsSameBranch(this Branch branch, Branch otherBranch) { // For each branch, fixup the friendly name if the branch is remote. var otherBranchFriendlyName = otherBranch.IsRemote ? @@ -136,78 +34,27 @@ public static bool IsSameBranch(Branch branch, Branch otherBranch) /// /// Exclude the given branches (by value equality according to friendly name). /// - public static IEnumerable ExcludingBranches([NotNull] this IEnumerable branches, [NotNull] IEnumerable branchesToExclude) + public static IEnumerable ExcludingBranches([NotNull] this IEnumerable branches, [NotNull] IEnumerable branchesToExclude) { - return branches.Where(b => branchesToExclude.All(bte => !IsSameBranch(b, bte))); + return branches.Where(b => branchesToExclude.All(bte => !IsSameBranch(b.Branch, bte))); } - public static IEnumerable GetBranchesContainingCommit([NotNull] this Commit commit, IRepository repository, IList branches, bool onlyTrackedBranches) + /// + /// Exclude the given branches (by value equality according to friendly name). + /// + public static IEnumerable ExcludingBranches([NotNull] this IEnumerable branches, [NotNull] IEnumerable branchesToExclude) { - if (commit == null) - { - throw new ArgumentNullException("commit"); - } - - using (Logger.IndentLog(string.Format("Getting branches containing the commit '{0}'.", commit.Id))) - { - var directBranchHasBeenFound = false; - Logger.WriteInfo("Trying to find direct branches."); - // TODO: It looks wasteful looping through the branches twice. Can't these loops be merged somehow? @asbjornu - foreach (var branch in branches) - { - if (branch.Tip != null && branch.Tip.Sha != commit.Sha || (onlyTrackedBranches && !branch.IsTracking)) - { - continue; - } - - directBranchHasBeenFound = true; - Logger.WriteInfo(string.Format("Direct branch found: '{0}'.", branch.FriendlyName)); - yield return branch; - } - - if (directBranchHasBeenFound) - { - yield break; - } - - Logger.WriteInfo(string.Format("No direct branches found, searching through {0} branches.", onlyTrackedBranches ? "tracked" : "all")); - foreach (var branch in branches.Where(b => onlyTrackedBranches && !b.IsTracking)) - { - Logger.WriteInfo(string.Format("Searching for commits reachable from '{0}'.", branch.FriendlyName)); - - var commits = repository.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = branch - }).Where(c => c.Sha == commit.Sha); - - if (!commits.Any()) - { - Logger.WriteInfo(string.Format("The branch '{0}' has no matching commits.", branch.FriendlyName)); - continue; - } - - Logger.WriteInfo(string.Format("The branch '{0}' has a matching commit.", branch.FriendlyName)); - yield return branch; - } - } + return branches.Where(b => branchesToExclude.All(bte => !IsSameBranch(b, bte))); } - private static Dictionary _cachedPeeledTarget = new Dictionary(); - public static GitObject PeeledTarget(this Tag tag) { - GitObject cachedTarget; - if(_cachedPeeledTarget.TryGetValue(tag.Target.Sha, out cachedTarget)) - { - return cachedTarget; - } var target = tag.Target; while (target is TagAnnotation) { target = ((TagAnnotation)(target)).Target; } - _cachedPeeledTarget.Add(tag.Target.Sha, target); return target; } @@ -261,7 +108,7 @@ public static void CheckoutFilesIfExist(this IRepository repository, params stri } var fullPath = Path.Combine(repository.GetRepositoryDirectory(), fileName); - using (var stream = ((Blob) treeEntry.Target).GetContentStream()) + using (var stream = ((Blob)treeEntry.Target).GetContentStream()) { using (var streamReader = new BinaryReader(stream)) { diff --git a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs index 89a6ff7cf8..db2546e06a 100644 --- a/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/BaseVersionCalculators/VersionInBranchNameBaseVersionStrategy.cs @@ -25,7 +25,7 @@ public IEnumerable GetVersions(GitVersionContext context, string ta var versionInBranch = GetVersionInBranch(branchName, tagPrefixRegex); if (versionInBranch != null) { - var commitBranchWasBranchedFrom = currentBranch.FindCommitBranchWasBranchedFrom(repository); + 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); } diff --git a/src/GitVersionCore/VersionCalculation/DevelopVersionStrategy.cs b/src/GitVersionCore/VersionCalculation/DevelopVersionStrategy.cs index 71e1591bb5..99cea3298d 100644 --- a/src/GitVersionCore/VersionCalculation/DevelopVersionStrategy.cs +++ b/src/GitVersionCore/VersionCalculation/DevelopVersionStrategy.cs @@ -66,7 +66,7 @@ private IEnumerable ReleaseBranchBaseVersions(GitVersionContext con // 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, + return new BaseVersion(context, source1, baseVersion.ShouldIncrement, baseVersion.SemanticVersion, @@ -84,7 +84,7 @@ IEnumerable GetReleaseVersion(GitVersionContext context, Branch rel var repository = context.Repository; // Find the commit where the child branch was created. - var baseSource = releaseBranch.FindMergeBase(context.CurrentBranch, repository); + var baseSource = context.RepositoryMetadataProvider.FindMergeBase(releaseBranch, context.CurrentBranch); if (baseSource == context.CurrentCommit) { // Ignore the branch if it has no commits. diff --git a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs index 03ac5325eb..d06bbeb358 100644 --- a/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs +++ b/src/GitVersionCore/VersionCalculation/NextVersionCalculator.cs @@ -258,8 +258,8 @@ void UpdatePreReleaseTag(GitVersionContext context, SemanticVersion semanticVers int? number = null; - var lastTag = context.CurrentBranch - .GetVersionTagsOnBranch(context.Repository, context.Configuration.GitTagPrefix) + var lastTag = context.RepositoryMetadataProvider + .GetVersionTagsOnBranch(context.CurrentBranch, context.Configuration.GitTagPrefix) .FirstOrDefault(v => v.PreReleaseTag.Name == tagToUse); if (lastTag != null && diff --git a/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs b/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs index 497b59526d..9ba0c96cfc 100644 --- a/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs +++ b/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs @@ -61,6 +61,7 @@ public void Finds_GitDirectory_In_Parent() } catch (Exception ex) { + // TODO I think this test is wrong.. It throws a different exception // `RepositoryNotFoundException` means that it couldn't find the .git directory, // any other exception means that the .git was found but there was some other issue that this test doesn't care about. Assert.IsNotAssignableFrom(ex);