IndentLog() public static method

public static IndentLog ( string operationDescription ) : IDisposable
operationDescription string
return IDisposable
Example #1
0
        /// <summary>
        /// Find the commit where the given branch was branched from another branch.
        /// If there are multiple such commits and branches, tries to guess based on commit histories.
        /// </summary>
        public BranchCommit FindCommitBranchWasBranchedFrom(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);
                }

                var possibleBranches = GetMergeCommitsForBranch(branch, excludedBranches)
                                       .Where(b => !branch.IsSameBranch(b.Branch))
                                       .ToList();

                if (possibleBranches.Count > 1)
                {
                    var first = possibleBranches.First();
                    Logger.WriteInfo($"Multiple source branches have been found, picking the first one ({first.Branch.FriendlyName}).\n" +
                                     "This may result in incorrect commit counting.\nOptions were:\n " +
                                     string.Join(", ", possibleBranches.Select(b => b.Branch.FriendlyName)));
                    return(first);
                }

                return(possibleBranches.SingleOrDefault());
            }
        }
Example #2
0
        public void WriteVariablesToDiskCache(GitPreparer gitPreparer, GitVersionCacheKey cacheKey, VersionVariables variablesFromCache)
        {
            var cacheDir      = PrepareCacheDirectory(gitPreparer);
            var cacheFileName = GetCacheFileName(cacheKey, cacheDir);

            variablesFromCache.FileName = cacheFileName;

            Dictionary <string, string> dictionary;

            using (Logger.IndentLog("Creating dictionary"))
            {
                dictionary = variablesFromCache.ToDictionary(x => x.Key, x => x.Value);
            }

            Action writeCacheOperation = () =>
            {
                using (var stream = fileSystem.OpenWrite(cacheFileName))
                {
                    using (var sw = new StreamWriter(stream))
                    {
                        using (Logger.IndentLog("Storing version variables to cache file " + cacheFileName))
                        {
                            var serializer = new Serializer();
                            serializer.Serialize(sw, dictionary);
                        }
                    }
                }
            };

            var retryOperation = new OperationWithExponentialBackoff <IOException>(new ThreadSleep(), writeCacheOperation, maxRetries: 6);

            retryOperation.ExecuteAsync().Wait();
        }
        public IEnumerable <SemanticVersion> 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);
            }
        }
Example #4
0
        static string CreateDynamicRepository(string targetPath, AuthenticationInfo authentication, string repositoryUrl, string targetBranch, bool noFetch)
        {
            if (string.IsNullOrWhiteSpace(targetBranch))
            {
                throw new Exception("Dynamic Git repositories must have a target branch (/b)");
            }

            using (Logger.IndentLog($"Creating dynamic repository at '{targetPath}'"))
            {
                var gitDirectory = Path.Combine(targetPath, ".git");
                if (Directory.Exists(targetPath))
                {
                    Logger.WriteInfo("Git repository already exists");
                    using (Logger.IndentLog($"Normalizing git directory for branch '{targetBranch}'"))
                    {
                        GitRepositoryHelper.NormalizeGitDirectory(gitDirectory, authentication, noFetch, targetBranch, true);
                    }

                    return(gitDirectory);
                }

                CloneRepository(repositoryUrl, gitDirectory, authentication);

                using (Logger.IndentLog($"Normalizing git directory for branch '{targetBranch}'"))
                {
                    // Normalize (download branches) before using the branch
                    GitRepositoryHelper.NormalizeGitDirectory(gitDirectory, authentication, noFetch, targetBranch, true);
                }

                return(gitDirectory);
            }
        }
Example #5
0
        public IEnumerable <SemanticVersion> 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 = new List <Tuple <Tag, SemanticVersion> >();
                foreach (var t in this.Repository.Tags)
                {
                    SemanticVersion semver;
                    if (SemanticVersion.TryParse(t.FriendlyName, tagPrefixRegex, out semver))
                    {
                        tags.Add(Tuple.Create(t, semver));
                    }
                }

                var versionTags = branch.Commits.SelectMany(c => tags.Where(t => c.Sha == t.Item1.Target.Sha).Select(t => t.Item2)).ToList();

                semanticVersionTagsOnBranchCache.Add(branch, versionTags);
                return(versionTags);
            }
        }
        /// <summary>
        /// Find the merge base of the two branches, i.e. the best common ancestor of the two branches' tips.
        /// </summary>
        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);
            }
        }
Example #7
0
        static void CloneRepository(string repositoryUrl, string gitDirectory, AuthenticationInfo authentication)
        {
            Credentials credentials = null;

            if (authentication != null)
            {
                if (!string.IsNullOrWhiteSpace(authentication.Username) && !string.IsNullOrWhiteSpace(authentication.Password))
                {
                    Logger.WriteInfo($"Setting up credentials using name '{authentication.Username}'");

                    credentials = new UsernamePasswordCredentials
                    {
                        Username = authentication.Username,
                        Password = authentication.Password
                    };
                }
            }


            try
            {
                using (Logger.IndentLog($"Cloning repository from url '{repositoryUrl}'"))
                {
                    var cloneOptions = new CloneOptions
                    {
                        Checkout            = false,
                        CredentialsProvider = (url, usernameFromUrl, types) => credentials
                    };

                    var returnedPath = Repository.Clone(repositoryUrl, gitDirectory, cloneOptions);
                    Logger.WriteInfo($"Returned path after repository clone: {returnedPath}");
                }
            }
            catch (LibGit2SharpException ex)
            {
                var message = ex.Message;
                if (message.Contains("401"))
                {
                    throw new Exception("Unauthorised: Incorrect username/password");
                }
                if (message.Contains("403"))
                {
                    throw new Exception("Forbidden: Possbily Incorrect username/password");
                }
                if (message.Contains("404"))
                {
                    throw new Exception("Not found: The repository was not found");
                }

                throw new Exception("There was an unknown problem with the Git repository you provided", ex);
            }
        }
Example #8
0
        // TODO Should we cache this?
        public IEnumerable <Branch> GetBranchesContainingCommit(Commit commit, IList <Branch> 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);
                }
            }
        }
Example #9
0
        public static Commit 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("Finding branch source"))
            {
                if (branch.Tip == null)
                {
                    Logger.WriteWarning(string.Format(missingTipFormat, branch.FriendlyName));
                    return(null);
                }

                var otherBranches = repository.Branches
                                    .Except(excludedBranches)
                                    .Where(b => IsSameBranch(branch, b))
                                    .ToList();
                var mergeBases = otherBranches.Select(otherBranch =>
                {
                    if (otherBranch.Tip == null)
                    {
                        Logger.WriteWarning(string.Format(missingTipFormat, otherBranch.FriendlyName));
                        return(null);
                    }

                    var findMergeBase = FindMergeBase(branch, otherBranch, repository);
                    return(new
                    {
                        mergeBaseCommit = findMergeBase,
                        branch = otherBranch
                    });
                }).Where(b => b.mergeBaseCommit != null).OrderByDescending(b => b.mergeBaseCommit.Committer.When).ToList();

                var firstOrDefault = mergeBases.FirstOrDefault();
                if (firstOrDefault != null)
                {
                    return(firstOrDefault.mergeBaseCommit);
                }
                return(null);
            }
        }
Example #10
0
        public IEnumerable <SemanticVersion> GetVersionTagsOnBranch(Branch branch, string tagPrefixRegex)
        {
            if (semanticVersionTagsOnBranchCache.ContainsKey(branch))
            {
                Logger.WriteDebug($"Cache hit for version tags on branch '{branch.CanonicalName}");
                return(semanticVersionTagsOnBranchCache[branch]);
            }

            using (Logger.IndentLog($"Getting version tags from branch '{branch.CanonicalName}'."))
            {
                var tags = GetValidVersionTags(Repository, tagPrefixRegex);

                var versionTags = branch.Commits.SelectMany(c => tags.Where(t => c.Sha == t.Item1.Target.Sha).Select(t => t.Item2)).ToList();

                semanticVersionTagsOnBranchCache.Add(branch, versionTags);
                return(versionTags);
            }
        }
Example #11
0
 public static Commit FindCommitBranchWasBranchedFrom(this Branch branch, IRepository repository, params Branch[] excludedBranches)
 {
     using (Logger.IndentLog("Finding branch source"))
     {
         var otherBranches = repository.Branches.Except(excludedBranches).Where(b => IsSameBranch(branch, b)).ToList();
         var mergeBases    = otherBranches.Select(b =>
         {
             var otherCommit = b.Tip;
             if (b.Tip.Parents.Contains(branch.Tip))
             {
                 otherCommit = b.Tip.Parents.First();
             }
             var mergeBase = repository.Commits.FindMergeBase(otherCommit, branch.Tip);
             return(mergeBase);
         }).Where(b => b != null).ToList();
         return(mergeBases.OrderByDescending(b => b.Committer.When).FirstOrDefault());
     }
 }
Example #12
0
        public void Initialise(bool normaliseGitDirectory, string currentBranch)
        {
            if (string.IsNullOrWhiteSpace(targetUrl))
            {
                if (normaliseGitDirectory)
                {
                    using (Logger.IndentLog(string.Format("Normalizing git directory for branch '{0}'", currentBranch)))
                    {
                        GitRepositoryHelper.NormalizeGitDirectory(GetDotGitDirectory(), authentication, noFetch, currentBranch);
                    }
                }
                return;
            }

            var tempRepositoryPath = CalculateTemporaryRepositoryPath(targetUrl, dynamicRepositoryLocation);

            DynamicGitRepositoryPath = CreateDynamicRepository(tempRepositoryPath, authentication, targetUrl, currentBranch, noFetch);
        }
        /// <summary>
        /// Find the commit where the given branch was branched from another branch.
        /// If there are multiple such commits and branches, returns the newest commit.
        /// </summary>
        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)));
            }
        }
Example #14
0
        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 mergeBaseWasFowardMerge;
                    do
                    {
                        // Now make sure that the merge base is not a forward merge
                        mergeBaseWasFowardMerge = otherBranch.Commits
                                                  .SkipWhile(c => c != commitToFindCommonBase)
                                                  .TakeWhile(c => c != findMergeBase)
                                                  .Any(c => c.Parents.Contains(findMergeBase));
                        if (mergeBaseWasFowardMerge)
                        {
                            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 (mergeBaseWasFowardMerge);
                }
                return(findMergeBase);
            }
        }
Example #15
0
        public VersionVariables LoadVersionVariablesFromDiskCache(IRepository repo, string gitDir)
        {
            using (Logger.IndentLog("Loading version variables from disk cache"))
            {
                // If the cacheDir already exists, CreateDirectory just won't do anything (it won't fail). @asbjornu

                var cacheDir = GetCacheDir(gitDir);
                fileSystem.CreateDirectory(cacheDir);
                var cacheFileName   = GetCacheFileName(GetKey(repo, gitDir), cacheDir);
                VersionVariables vv = null;
                if (fileSystem.Exists(cacheFileName))
                {
                    using (Logger.IndentLog("Deserializing version variables from cache file " + cacheFileName))
                    {
                        try
                        {
                            vv = VersionVariables.FromFile(cacheFileName, fileSystem);
                        }
                        catch (Exception ex)
                        {
                            Logger.WriteWarning("Unable to read cache file " + cacheFileName + ", deleting it.");
                            Logger.WriteInfo(ex.ToString());
                            try
                            {
                                fileSystem.Delete(cacheFileName);
                            }
                            catch (Exception deleteEx)
                            {
                                Logger.WriteWarning(string.Format("Unable to delete corrupted version cache file {0}. Got {1} exception.", cacheFileName, deleteEx.GetType().FullName));
                            }
                        }
                    }
                }
                else
                {
                    Logger.WriteInfo("Cache file " + cacheFileName + " not found.");
                }

                return(vv);
            }
        }
Example #16
0
        public void Initialise(bool normaliseGitDirectory, string currentBranch, bool shouldCleanUpRemotes = false)
        {
            if (string.IsNullOrWhiteSpace(targetUrl))
            {
                if (normaliseGitDirectory)
                {
                    using (Logger.IndentLog($"Normalizing git directory for branch '{currentBranch}'"))
                    {
                        if (shouldCleanUpRemotes)
                        {
                            CleanupDuplicateOrigin();
                        }
                        GitRepositoryHelper.NormalizeGitDirectory(GetDotGitDirectory(), authentication, noFetch, currentBranch, IsDynamicGitRepository);
                    }
                }
                return;
            }

            var tempRepositoryPath = CalculateTemporaryRepositoryPath(targetUrl, dynamicRepositoryLocation);

            DynamicGitRepositoryPath = CreateDynamicRepository(tempRepositoryPath, authentication, targetUrl, currentBranch, noFetch);
        }
Example #17
0
        public VersionVariables LoadVersionVariablesFromDiskCache(GitPreparer gitPreparer, GitVersionCacheKey key)
        {
            using (Logger.IndentLog("Loading version variables from disk cache"))
            {
                var cacheDir = PrepareCacheDirectory(gitPreparer);

                var cacheFileName = GetCacheFileName(key, cacheDir);
                if (!fileSystem.Exists(cacheFileName))
                {
                    Logger.WriteInfo("Cache file " + cacheFileName + " not found.");
                    return(null);
                }

                using (Logger.IndentLog("Deserializing version variables from cache file " + cacheFileName))
                {
                    try
                    {
                        var loadedVariables = VersionVariables.FromFile(cacheFileName, fileSystem);
                        return(loadedVariables);
                    }
                    catch (Exception ex)
                    {
                        Logger.WriteWarning("Unable to read cache file " + cacheFileName + ", deleting it.");
                        Logger.WriteInfo(ex.ToString());
                        try
                        {
                            fileSystem.Delete(cacheFileName);
                        }
                        catch (Exception deleteEx)
                        {
                            Logger.WriteWarning($"Unable to delete corrupted version cache file {cacheFileName}. Got {deleteEx.GetType().FullName} exception.");
                        }

                        return(null);
                    }
                }
            }
        }
        public static Commit 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("Finding branch source"))
            {
                if (branch.Tip == null)
                {
                    Logger.WriteWarning(String.Format(missingTipFormat, branch.Name));
                    return(null);
                }

                var otherBranches = repository.Branches.Except(excludedBranches).Where(b => IsSameBranch(branch, b)).ToList();
                var mergeBases    = otherBranches.Select(b =>
                {
                    if (b.Tip == null)
                    {
                        Logger.WriteWarning(String.Format(missingTipFormat, b.Name));
                        return(null);
                    }

                    var otherCommit = b.Tip;
                    if (b.Tip.Parents.Contains(branch.Tip))
                    {
                        otherCommit = b.Tip.Parents.First();
                    }
                    var mergeBase = repository.Commits.FindMergeBase(otherCommit, branch.Tip);
                    return(mergeBase);
                }).Where(b => b != null).ToList();
                return(mergeBases.OrderByDescending(b => b.Committer.When).FirstOrDefault());
            }
        }
Example #19
0
        public void WriteVariablesToDiskCache(IRepository repo, string gitDir, VersionVariables variablesFromCache)
        {
            var cacheFileName = GetCacheFileName(GetKey(repo, gitDir), GetCacheDir(gitDir));

            variablesFromCache.FileName = cacheFileName;

            using (var stream = fileSystem.OpenWrite(cacheFileName))
            {
                using (var sw = new StreamWriter(stream))
                {
                    Dictionary <string, string> dictionary;
                    using (Logger.IndentLog("Creating dictionary"))
                    {
                        dictionary = variablesFromCache.ToDictionary(x => x.Key, x => x.Value);
                    }

                    using (Logger.IndentLog("Storing version variables to cache file " + cacheFileName))
                    {
                        var serializer = new Serializer();
                        serializer.Serialize(sw, dictionary);
                    }
                }
            }
        }
        static BranchConfig InheritBranchConfiguration(GitVersionContext context, Branch targetBranch, BranchConfig branchConfiguration, IList <Branch> 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 = context.CurrentCommit.Parents.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.FriendlyName);

                        return(branchConfig != null && branchConfig.Increment == IncrementStrategy.Inherit);
                    }).ToList();
                }
                // Add new excluded branches.
                foreach (var excludedBranch in excludedBranches.ExcludingBranches(excludedInheritBranches))
                {
                    excludedInheritBranches.Add(excludedBranch);
                }
                var branchesToEvaluate = repository.Branches.Except(excludedInheritBranches).ToList();

                var branchPoint = context.RepositoryMetadataProvider
                                  .FindCommitBranchWasBranchedFrom(targetBranch, excludedInheritBranches.ToArray());
                List <Branch> possibleParents;
                if (branchPoint == BranchCommit.Empty)
                {
                    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)
                                      .ToList();
                }
                else
                {
                    var branches = context.RepositoryMetadataProvider
                                   .GetBranchesContainingCommit(branchPoint.Commit, branchesToEvaluate, true).ToList();
                    if (branches.Count > 1)
                    {
                        var currentTipBranches = context.RepositoryMetadataProvider
                                                 .GetBranchesContainingCommit(context.CurrentCommit, branchesToEvaluate, true).ToList();
                        possibleParents = branches.Except(currentTipBranches).ToList();
                    }
                    else
                    {
                        possibleParents = branches;
                    }
                }

                Logger.WriteInfo("Found possible parent branches: " + string.Join(", ", possibleParents.Select(p => p.FriendlyName)));

                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 master 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.FriendlyName));
                }

                var developBranchRegex = config.Branches[ConfigurationProvider.DevelopBranchKey].Regex;
                var masterBranchRegex  = config.Branches[ConfigurationProvider.MasterBranchKey].Regex;

                var chosenBranch = repository.Branches.FirstOrDefault(b => Regex.IsMatch(b.FriendlyName, developBranchRegex, RegexOptions.IgnoreCase) ||
                                                                      Regex.IsMatch(b.FriendlyName, masterBranchRegex, 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 'develop' or 'master' branch, neither locally nor remotely.");
                }

                var branchName = chosenBranch.FriendlyName;
                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 (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,
                    PreventIncrementOfMergedBranchVersion = inheritingBranchConfig.PreventIncrementOfMergedBranchVersion,
                    // If we are inheriting from develop then we should behave like develop
                    TracksReleaseBranches = inheritingBranchConfig.TracksReleaseBranches
                });
            }
        }
        static KeyValuePair <string, BranchConfig> InheritBranchConfiguration(bool onlyEvaluateTrackedBranches, IRepository repository, Commit currentCommit, Branch currentBranch, KeyValuePair <string, BranchConfig> keyValuePair, BranchConfig branchConfiguration, Config config, IList <Branch> excludedInheritBranches)
        {
            using (Logger.IndentLog("Attempting to inherit branch configuration from parent branch"))
            {
                var excludedBranches = new[] { currentBranch };
                // Check if we are a merge commit. If so likely we are a pull request
                var parentCount = currentCommit.Parents.Count();
                if (parentCount == 2)
                {
                    var parents = currentCommit.Parents.ToArray();
                    var branch  = repository.Branches.SingleOrDefault(b => !b.IsRemote && b.Tip == parents[1]);
                    if (branch != null)
                    {
                        excludedBranches = new[]
                        {
                            currentBranch,
                            branch
                        };
                        currentBranch = branch;
                    }
                    else
                    {
                        var possibleTargetBranches = repository.Branches.Where(b => !b.IsRemote && b.Tip == parents[0]).ToList();
                        if (possibleTargetBranches.Count() > 1)
                        {
                            currentBranch = possibleTargetBranches.FirstOrDefault(b => b.Name == "master") ?? possibleTargetBranches.First();
                        }
                        else
                        {
                            currentBranch = possibleTargetBranches.FirstOrDefault() ?? currentBranch;
                        }
                    }

                    Logger.WriteInfo("HEAD is merge commit, this is likely a pull request using " + currentBranch.Name + " as base");
                }
                if (excludedInheritBranches == null)
                {
                    excludedInheritBranches = repository.Branches.Where(b =>
                    {
                        var branchConfig = LookupBranchConfiguration(config, b);
                        return(branchConfig.Length == 1 && branchConfig[0].Value.Increment == IncrementStrategy.Inherit);
                    }).ToList();
                }
                excludedBranches.ToList().ForEach(excludedInheritBranches.Add);

                var branchPoint = currentBranch.FindCommitBranchWasBranchedFrom(repository, excludedInheritBranches.ToArray());

                List <Branch> possibleParents;
                if (branchPoint == null)
                {
                    possibleParents = currentCommit.GetBranchesContainingCommit(repository, true).Except(excludedInheritBranches).ToList();
                }
                else
                {
                    var branches = branchPoint.GetBranchesContainingCommit(repository, true).Except(excludedInheritBranches).ToList();
                    if (branches.Count > 1)
                    {
                        var currentTipBranches = currentCommit.GetBranchesContainingCommit(repository, true).Except(excludedInheritBranches).ToList();
                        possibleParents = branches.Except(currentTipBranches).ToList();
                    }
                    else
                    {
                        possibleParents = branches;
                    }
                }

                Logger.WriteInfo("Found possible parent branches: " + string.Join(", ", possibleParents.Select(p => p.Name)));

                if (possibleParents.Count == 1)
                {
                    var branchConfig = GetBranchConfiguration(currentCommit, repository, onlyEvaluateTrackedBranches, config, possibleParents[0], excludedInheritBranches).Value;
                    return(new KeyValuePair <string, BranchConfig>(
                               keyValuePair.Key,
                               new BranchConfig(branchConfiguration)
                    {
                        Increment = branchConfig.Increment,
                        PreventIncrementOfMergedBranchVersion = branchConfig.PreventIncrementOfMergedBranchVersion
                    }));
                }

                // 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 master 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 developBranch = repository.Branches.FirstOrDefault(b => Regex.IsMatch(b.Name, "^develop", RegexOptions.IgnoreCase));
                var branchName    = developBranch != null ? developBranch.Name : "master";

                Logger.WriteWarning(errorMessage + Environment.NewLine + Environment.NewLine + "Falling back to " + branchName + " branch config");
                var value = GetBranchConfiguration(currentCommit, repository, onlyEvaluateTrackedBranches, config, repository.Branches[branchName]).Value;
                return(new KeyValuePair <string, BranchConfig>(
                           keyValuePair.Key,
                           new BranchConfig(branchConfiguration)
                {
                    Increment = value.Increment,
                    PreventIncrementOfMergedBranchVersion = value.PreventIncrementOfMergedBranchVersion
                }));
            }
        }
Example #22
0
        /// <summary>
        /// Find the merge base of the two branches, i.e. the best common ancestor of the two branches' tips.
        /// </summary>
        public Commit FindMergeBase(Branch branch, Branch otherBranch)
        {
            var key = Tuple.Create(branch, otherBranch);

            if (mergeBaseCache.ContainsKey(key))
            {
                Logger.WriteDebug($"Cache hit for merge base between '{branch.FriendlyName}' and '{otherBranch.FriendlyName}'.");
                return(mergeBaseCache[key].MergeBase);
            }

            using (Logger.IndentLog($"Finding merge base between '{branch.FriendlyName}' and '{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($"Found merge base of {findMergeBase.Sha}");
                    // We do not want to include merge base commits which got forward merged into the other branch
                    Commit forwardMerge;
                    do
                    {
                        // Now make sure that the merge base is not a forward merge
                        forwardMerge = Repository.Commits
                                       .QueryBy(new CommitFilter
                        {
                            IncludeReachableFrom = commitToFindCommonBase,
                            ExcludeReachableFrom = findMergeBase
                        })
                                       .FirstOrDefault(c => c.Parents.Contains(findMergeBase));

                        if (forwardMerge != null)
                        {
                            // TODO Fix the logging up in this section
                            var second = forwardMerge.Parents.First();
                            Logger.WriteDebug("Second " + second.Sha);
                            var mergeBase = Repository.ObjectDatabase.FindMergeBase(commit, second);
                            if (mergeBase == null)
                            {
                                Logger.WriteWarning("Could not find mergbase for " + commit);
                            }
                            else
                            {
                                Logger.WriteDebug("New Merge base " + mergeBase.Sha);
                            }
                            if (mergeBase == findMergeBase)
                            {
                                Logger.WriteDebug("Breaking");
                                break;
                            }
                            findMergeBase          = mergeBase;
                            commitToFindCommonBase = second;
                            Logger.WriteInfo($"Merge base was due to a forward merge, next merge base is {findMergeBase}");
                        }
                    } while (forwardMerge != null);
                }

                // Store in cache.
                mergeBaseCache.Add(key, new MergeBaseData(branch, otherBranch, Repository, findMergeBase));

                Logger.WriteInfo($"Merge base of {branch.FriendlyName}' and '{otherBranch.FriendlyName} is {findMergeBase}");
                return(findMergeBase);
            }
        }
Example #23
0
        static KeyValuePair <string, BranchConfig> InheritBranchConfiguration(bool onlyEvaluateTrackedBranches, IRepository repository, Commit currentCommit, Branch currentBranch, KeyValuePair <string, BranchConfig> keyValuePair, BranchConfig branchConfiguration, Config config, IList <Branch> excludedInheritBranches)
        {
            using (Logger.IndentLog("Attempting to inherit branch configuration from parent branch"))
            {
                var excludedBranches = new[] { currentBranch };
                // Check if we are a merge commit. If so likely we are a pull request
                var parentCount = currentCommit.Parents.Count();
                if (parentCount == 2)
                {
                    excludedBranches = CalculateWhenMultipleParents(repository, currentCommit, ref currentBranch, excludedBranches);
                }

                if (excludedInheritBranches == null)
                {
                    excludedInheritBranches = repository.Branches.Where(b =>
                    {
                        var branchConfig = LookupBranchConfiguration(config, b);

                        // NOTE: if length is 0 we couldn't find the configuration for the branch e.g. "origin/master"
                        // NOTE: if the length is greater than 1 we cannot decide which merge strategy to pick
                        return((branchConfig.Length != 1) || (branchConfig.Length == 1 && branchConfig[0].Value.Increment == IncrementStrategy.Inherit));
                    }).ToList();
                }
                excludedBranches.ToList().ForEach(excludedInheritBranches.Add);
                var branchesToEvaluate = repository.Branches.Except(excludedInheritBranches).ToList();

                var           branchPoint = currentBranch.FindCommitBranchWasBranchedFrom(repository, excludedInheritBranches.ToArray());
                List <Branch> possibleParents;
                if (branchPoint == null)
                {
                    possibleParents = currentCommit.GetBranchesContainingCommit(repository, 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)
                                      .ToList();
                }
                else
                {
                    var branches = branchPoint.GetBranchesContainingCommit(repository, branchesToEvaluate, true).ToList();
                    if (branches.Count > 1)
                    {
                        var currentTipBranches = currentCommit.GetBranchesContainingCommit(repository, branchesToEvaluate, true).ToList();
                        possibleParents = branches.Except(currentTipBranches).ToList();
                    }
                    else
                    {
                        possibleParents = branches;
                    }
                }

                Logger.WriteInfo("Found possible parent branches: " + string.Join(", ", possibleParents.Select(p => p.FriendlyName)));

                if (possibleParents.Count == 1)
                {
                    var branchConfig = GetBranchConfiguration(currentCommit, repository, onlyEvaluateTrackedBranches, config, possibleParents[0], excludedInheritBranches).Value;
                    return(new KeyValuePair <string, BranchConfig>(
                               keyValuePair.Key,
                               new BranchConfig(branchConfiguration)
                    {
                        Increment = branchConfig.Increment,
                        PreventIncrementOfMergedBranchVersion = branchConfig.PreventIncrementOfMergedBranchVersion
                    }));
                }

                // 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 master 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.FriendlyName));
                }

                var chosenBranch = repository.Branches.FirstOrDefault(b => Regex.IsMatch(b.FriendlyName, "^develop", RegexOptions.IgnoreCase) ||
                                                                      Regex.IsMatch(b.FriendlyName, "master$", RegexOptions.IgnoreCase));
                if (chosenBranch == null)
                {
                    throw new InvalidOperationException("Could not find a 'develop' or 'master' branch, neither locally nor remotely.");
                }

                var branchName = chosenBranch.FriendlyName;
                Logger.WriteWarning(errorMessage + Environment.NewLine + Environment.NewLine + "Falling back to " + branchName + " branch config");

                var value = GetBranchConfiguration(currentCommit, repository, onlyEvaluateTrackedBranches, config, chosenBranch).Value;
                return(new KeyValuePair <string, BranchConfig>(
                           keyValuePair.Key,
                           new BranchConfig(branchConfiguration)
                {
                    Increment = value.Increment,
                    PreventIncrementOfMergedBranchVersion = value.PreventIncrementOfMergedBranchVersion
                }));
            }
        }
Example #24
0
        public static Commit 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("Finding branch source"))
            {
                if (branch.Tip == null)
                {
                    Logger.WriteWarning(string.Format(missingTipFormat, branch.Name));
                    return(null);
                }

                var otherBranches = repository.Branches
                                    .Except(excludedBranches)
                                    .Where(b => IsSameBranch(branch, b))
                                    .ToList();
                var mergeBases = otherBranches.Select(otherBranch =>
                {
                    if (otherBranch.Tip == null)
                    {
                        Logger.WriteWarning(string.Format(missingTipFormat, otherBranch.Name));
                        return(null);
                    }

                    // Otherbranch tip is a forward merge
                    var commitToFindCommonBase = otherBranch.Tip;
                    if (otherBranch.Tip.Parents.Contains(branch.Tip))
                    {
                        commitToFindCommonBase = otherBranch.Tip.Parents.First();
                    }

                    var findMergeBase = repository.Commits.FindMergeBase(branch.Tip, commitToFindCommonBase);
                    if (findMergeBase != null)
                    {
                        using (Logger.IndentLog(string.Format("Found merge base of {0} against {1}", findMergeBase.Sha, otherBranch.Name)))
                        {
                            // We do not want to include merge base commits which got forward merged into the other branch
                            bool mergeBaseWasFowardMerge;

                            do
                            {
                                // Now make sure that the merge base is not a forward merge
                                mergeBaseWasFowardMerge = otherBranch.Commits
                                                          .SkipWhile(c => c != commitToFindCommonBase)
                                                          .TakeWhile(c => c != findMergeBase)
                                                          .Any(c => c.Parents.Contains(findMergeBase));
                                if (mergeBaseWasFowardMerge)
                                {
                                    Logger.WriteInfo("Merge base was due to a forward merge, moving to next merge base");
                                    var second    = commitToFindCommonBase.Parents.First();
                                    var mergeBase = repository.Commits.FindMergeBase(branch.Tip, second);
                                    if (mergeBase == findMergeBase)
                                    {
                                        break;
                                    }
                                    findMergeBase = mergeBase;
                                }
                            } while (mergeBaseWasFowardMerge);
                        }
                    }
                    return(new
                    {
                        mergeBaseCommit = findMergeBase,
                        branch = otherBranch
                    });
                }).Where(b => b != null).OrderByDescending(b => b.mergeBaseCommit.Committer.When).ToList();

                var firstOrDefault = mergeBases.FirstOrDefault();
                if (firstOrDefault != null)
                {
                    return(firstOrDefault.mergeBaseCommit);
                }
                return(null);
            }
        }