/// <inheritdoc /> public IEnumerable <SemanticVersion> GetVersionTagsOnBranch(IBranchHead branch, string tagPrefixRegex) { if (_semanticVersionTagsOnBranchCache.ContainsKey(branch)) { Logger.WriteDebug($"Cache hit for version tags on branch '{branch.Name}"); return(_semanticVersionTagsOnBranchCache[branch]); } using (Logger.IndentLog($"Getting version tags from branch '{branch.Name}'.")) { var builder = new HgLogQueryBuilder(); var tags = _repository .Log(builder.TaggedBranchCommits(branch.Name)) .SelectMany(commit => commit.Tags) .ToList(); var versionTags = tags .SelectMany(tag => { if (SemanticVersion.TryParse(tag, tagPrefixRegex, out var semver)) { return new[] { semver } } ; return(Enumerable.Empty <SemanticVersion>()); }) .ToList(); _semanticVersionTagsOnBranchCache.Add(branch, versionTags); return(versionTags); } }
/// <inheritdoc /> public ICommit FindMergeBase(IBranchHead branch, IBranchHead otherBranch) { var key = Tuple.Create(branch, otherBranch); if (_mergeBaseCache.ContainsKey(key)) { Logger.WriteDebug($"Cache hit for merge base between '{branch.Name}' and '{otherBranch.Name}'."); return(_mergeBaseCache[key].MergeBase); } using (Logger.IndentLog($"Finding merge base between '{branch.Name}' and '{otherBranch.Name}'.")) { var select = new HgLogQueryBuilder(); var findMergeBase = _repository .Log(select.CommonAncestorOf( select.ByBranch(branch.Name), select.ByBranch(branch.Name))) .FirstOrDefault(); // Store in cache. _mergeBaseCache.Add(key, new MergeBaseData(branch, otherBranch, _repository, findMergeBase)); Logger.WriteInfo($"Merge base of {branch.Name}' and '{otherBranch.Name} is {findMergeBase}"); return(findMergeBase); } }
public MergeBaseData(IBranchHead branch, IBranchHead otherBranch, IRepository repository, ICommit mergeBase) { Branch = branch; OtherBranch = otherBranch; Repository = repository; MergeBase = mergeBase; }
/// <inheritdoc /> public ICommit FindCommitWasBranchedFrom(IBranchHead branch, params IBranchHead[] excludedBranches) { if (branch == null) { throw new ArgumentNullException(nameof(branch)); } using (Logger.IndentLog($"Finding branch source of '{branch.Name}'")) { var builder = new HgLogQueryBuilder(); var possibleBranches = _repository .Log(builder.ByBranch(branch.Name) .Limit(1) .Parents() .Except(excludedBranches.Select(b => b.Name).ToArray())) .ToList(); if (possibleBranches.Count > 1) { var first = possibleBranches.First(); Logger.WriteInfo( $"Multiple source branches have been found, picking the first one ({first.Branch}).\n" + "This may result in incorrect commit counting.\nOptions were:\n " + string.Join(", ", possibleBranches.Select(b => b.Branch))); return(first); } return(possibleBranches.SingleOrDefault()); } }
/// <summary> /// Gets the <see cref="BranchConfig"/> for the current commit. /// </summary> public static BranchConfig GetBranchConfiguration(HgVersionContext context, IBranchHead targetBranch, IList <IBranchHead> excludedInheritBranches = null) { var matchingBranches = context.FullConfiguration.GetConfigForBranch(targetBranch.Name); if (matchingBranches == null) { Logger.WriteInfo( $"No branch configuration found for branch '{targetBranch.Name}', " + "falling back to default configuration"); matchingBranches = new BranchConfig { Name = string.Empty }; HgConfigurationProvider.ApplyBranchDefaults(context.FullConfiguration, matchingBranches, "", new List <string>()); } return(matchingBranches.Increment == IncrementStrategyType.Inherit ? InheritBranchConfiguration(context, targetBranch, matchingBranches, excludedInheritBranches) : matchingBranches); }
/// <inheritdoc /> public IEnumerable <SemanticVersion> GetVersionTagsOnBranch(IBranchHead branch, string tagPrefixRegex) { if (_semanticVersionTagsOnBranchCache.ContainsKey(branch)) { Logger.WriteDebug($"Cache hit for version tags on branch '{branch.Name}"); return(_semanticVersionTagsOnBranchCache[branch]); } using (Logger.IndentLog($"Getting version tags from branch '{branch.Name}'.")) { var tags = _repository .Log(select => select.Intersect( select.TaggedWithVersion(tagPrefixRegex), select.ByBranch(branch.Name) ) .Last(_configuration.TaggedCommitsLimit.GetValueOrDefault()) ) .SelectMany(commit => commit.Tags) .ToList(); var versionTags = tags .SelectMany(tag => { if (SemanticVersion.TryParse(tag.Name, tagPrefixRegex, out var semver)) { return new[] { semver } } ; return(Enumerable.Empty <SemanticVersion>()); }) .ToList(); _semanticVersionTagsOnBranchCache.Add(branch, versionTags); return(versionTags); } }
static BranchConfig InheritBranchConfiguration(HgVersionContext context, IBranchHead targetBranch, BranchConfig branchConfiguration, IList <IBranchHead> excludedInheritBranches) { var repository = context.Repository; var config = context.FullConfiguration; using (Logger.IndentLog("Attempting to inherit branch configuration from parent branch")) { var excludedBranches = new[] { targetBranch }; // Check if we are a merge commit. If so likely we are a pull request var parentCount = repository.Parents(context.CurrentCommit).Count(); if (parentCount == 2) { excludedBranches = CalculateWhenMultipleParents(repository, context.CurrentCommit, ref targetBranch, excludedBranches); } if (excludedInheritBranches == null) { excludedInheritBranches = repository.Branches() .Where(b => { var branchConfig = config.GetConfigForBranch(b.Name); return(branchConfig != null && branchConfig.Increment == IncrementStrategyType.Inherit); }) .ToList(); } // Add new excluded branches. foreach (var excludedBranch in excludedBranches.Except(excludedInheritBranches)) { excludedInheritBranches.Add(excludedBranch); } var branchesToEvaluate = repository .Branches() .Except(excludedInheritBranches) .ToList(); var branchPoint = context.RepositoryMetadataProvider .FindCommitWasBranchedFrom(targetBranch, excludedInheritBranches.ToArray()); var possibleParents = CalculatePossibleParents(context, branchPoint, branchesToEvaluate); Logger.WriteInfo("Found possible parent branches: " + string.Join(", ", possibleParents.Select(p => p.Name))); if (possibleParents.Count == 1) { var branchConfig = GetBranchConfiguration(context, possibleParents[0], excludedInheritBranches); return(new BranchConfig(branchConfiguration) { Increment = branchConfig.Increment, PreventIncrementOfMergedBranchVersion = branchConfig.PreventIncrementOfMergedBranchVersion, // If we are inheriting from develop then we should behave like develop TracksReleaseBranches = branchConfig.TracksReleaseBranches }); } // If we fail to inherit it is probably because the branch has been merged and we can't do much. So we will fall back to develop's config // if develop exists and default if not string errorMessage; if (possibleParents.Count == 0) { errorMessage = "Failed to inherit Increment branch configuration, no branches found."; } else { errorMessage = "Failed to inherit Increment branch configuration, ended up with: " + string.Join(", ", possibleParents.Select(p => p.Name)); } var developBranchRegex = config.Branches[HgConfigurationProvider.DevelopBranchKey].Regex; var defaultBranchRegex = config.Branches[HgConfigurationProvider.DefaultBranchKey].Regex; var chosenBranch = repository.Branches() .FirstOrDefault(b => Regex.IsMatch(b.Name, developBranchRegex, RegexOptions.IgnoreCase) || Regex.IsMatch(b.Name, defaultBranchRegex, RegexOptions.IgnoreCase)); if (chosenBranch == null) { // TODO We should call the build server to generate this exception, each build server works differently // for fetch issues and we could give better warnings. throw new InvalidOperationException( $"Could not find a '{HgConfigurationProvider.DevelopBranchKey}' or " + $"'{HgConfigurationProvider.DefaultBranchKey}' branch, neither locally nor remotely."); } var branchName = chosenBranch.Name; Logger.WriteWarning(errorMessage + Environment.NewLine + Environment.NewLine + "Falling back to " + branchName + " branch config"); // To prevent infinite loops, make sure that a new branch was chosen. if (Equals(targetBranch, chosenBranch)) { Logger.WriteWarning( "Fallback branch wants to inherit Increment branch configuration from itself. " + "Using patch increment instead."); return(new BranchConfig(branchConfiguration) { Increment = IncrementStrategyType.Patch }); } var inheritingBranchConfig = GetBranchConfiguration(context, chosenBranch, excludedInheritBranches); return(new BranchConfig(branchConfiguration) { Increment = inheritingBranchConfig.Increment, PreventIncrementOfMergedBranchVersion = inheritingBranchConfig.PreventIncrementOfMergedBranchVersion, // If we are inheriting from develop then we should behave like develop TracksReleaseBranches = inheritingBranchConfig.TracksReleaseBranches }); } }
static IBranchHead[] CalculateWhenMultipleParents(IRepository repository, ICommit currentCommit, ref IBranchHead currentBranch, IBranchHead[] excludedBranches) { var parents = repository .Parents(currentCommit) .ToArray(); var branches = repository.Branches() .Where(b => Equals(b.Commit, parents[1])) .ToList(); if (branches.Count == 1) { var branch = branches[0]; excludedBranches = new[] { currentBranch, branch }; currentBranch = branch; } else if (branches.Count > 1) { currentBranch = branches.FirstOrDefault(b => b.Name == HgConfigurationProvider.DefaultBranchKey) ?? branches.First(); } else { var possibleTargetBranches = repository.Branches() .Where(b => Equals(b.Commit, parents[0])) .ToList(); if (possibleTargetBranches.Count > 1) { currentBranch = possibleTargetBranches.FirstOrDefault(b => b.Name == HgConfigurationProvider.DefaultBranchKey) ?? possibleTargetBranches.First(); } else { currentBranch = possibleTargetBranches.FirstOrDefault() ?? currentBranch; } } Logger.WriteInfo("HEAD is merge commit, this is likely a pull request using " + $"{currentBranch.Name} as base"); return(excludedBranches); }
/// <summary> /// todo: add summary /// </summary> public static IEnumerable <ICommit> Commits(this IBranchHead branch, IVersionContext context) => context.Repository.Log(select => select.ByBranch(branch.Name));
/// <summary> /// todo: add summary /// </summary> public static IEnumerable <ICommit> CommitsPriorToThan(this IBranchHead branch, IVersionContext context, DateTimeOffset olderThan) => branch.Commits(context).SkipWhile(c => c.When > olderThan);