Ejemplo n.º 1
0
        static IEnumerable <IBuildServer> DefaultSelector(Authentication authentication)
        {
            if (BuildServers == null)
            {
                BuildServers = new List <IBuildServer>
                {
                    new ContinuaCi(authentication),
                    new TeamCity(authentication),
                    new AppVeyor(authentication)
                };
            }

            var buildServices = new List <IBuildServer>();

            foreach (var buildServer in BuildServers)
            {
                try
                {
                    if (buildServer.CanApplyToCurrentContext())
                    {
                        Logger.WriteInfo(string.Format("Applicable build agent found: '{0}'.", buildServer.GetType().Name));
                        buildServices.Add(buildServer);
                    }
                }
                catch (Exception ex)
                {
                    Logger.WriteWarning(string.Format("Failed to check build server '{0}': {1}", buildServer.GetType().Name, ex.Message));
                }
            }

            return(buildServices);
        }
        public static SemanticVersionPreReleaseTag Parse(string preReleaseTag)
        {
            if (string.IsNullOrEmpty(preReleaseTag))
            {
                return(new SemanticVersionPreReleaseTag());
            }

            var match = Regex.Match(preReleaseTag, @"(?<name>.*?)\.?(?<number>\d+)?$");

            if (!match.Success)
            {
                Logger.WriteWarning($"Unable to successfully parse semver tag {preReleaseTag}");
                return(new SemanticVersionPreReleaseTag());
            }

            var value  = match.Groups["name"].Value;
            var number = match.Groups["number"].Success ? int.Parse(match.Groups["number"].Value) : (int?)null;

            if (value.EndsWith("-"))
            {
                return(new SemanticVersionPreReleaseTag(preReleaseTag, null));
            }

            return(new SemanticVersionPreReleaseTag(value, number));
        }
        /// <summary>
        /// Gets the <see cref="BranchConfig"/> for the current commit.
        /// </summary>
        public static BranchConfig GetBranchConfiguration(GitVersionContext context, Branch targetBranch, IList <Branch> excludedInheritBranches = null)
        {
            var matchingBranches = LookupBranchConfiguration(context.FullConfiguration, targetBranch).ToArray();

            BranchConfig branchConfiguration;

            if (matchingBranches.Length > 0)
            {
                branchConfiguration = matchingBranches[0];

                if (matchingBranches.Length > 1)
                {
                    Logger.WriteWarning(string.Format(
                                            "Multiple branch configurations match the current branch branchName of '{0}'. Using the first matching configuration, '{1}'. Matching configurations include: '{2}'",
                                            targetBranch.FriendlyName,
                                            branchConfiguration.Name,
                                            string.Join("', '", matchingBranches.Select(b => b.Name))));
                }
            }
            else
            {
                Logger.WriteInfo(string.Format(
                                     "No branch configuration found for branch {0}, falling back to default configuration",
                                     targetBranch.FriendlyName));

                branchConfiguration = new BranchConfig {
                    Name = string.Empty
                };
                ConfigurationProvider.ApplyBranchDefaults(context.FullConfiguration, branchConfiguration, "");
            }

            return(branchConfiguration.Increment == IncrementStrategy.Inherit ?
                   InheritBranchConfiguration(context, targetBranch, branchConfiguration, excludedInheritBranches) :
                   branchConfiguration);
        }
Ejemplo n.º 4
0
        public BranchConfig GetConfigForBranch(string branchName)
        {
            if (branchName == null)
            {
                throw new ArgumentNullException(nameof(branchName));
            }
            var matches = Branches
                          .Where(b => Regex.IsMatch(branchName, "^" + b.Value.Regex, RegexOptions.IgnoreCase));

            try
            {
                return(matches
                       .Select(kvp => kvp.Value)
                       .SingleOrDefault());
            }
            catch (InvalidOperationException)
            {
                var matchingConfigs = string.Join("\n - ", matches.Select(m => m.Key));
                var picked          = matches
                                      .Select(kvp => kvp.Value)
                                      .First();

                Logger.WriteWarning(
                    $"Multiple branch configurations match the current branch branchName of '{branchName}'. " +
                    $"Using the first matching configuration, '{picked}'. Matching configurations include: '{matchingConfigs}'");

                return(picked);
            }
        }
        List <BranchCommit> 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);
        }
Ejemplo n.º 6
0
        bool EnsureVersionAssemblyInfoFile(bool ensureAssemblyInfo, IFileSystem fileSystem, string fullPath)
        {
            if (fileSystem.Exists(fullPath))
            {
                return(true);
            }

            if (!ensureAssemblyInfo)
            {
                return(false);
            }

            var assemblyInfoSource = templateManager.GetTemplateFor(Path.GetExtension(fullPath));

            if (!string.IsNullOrWhiteSpace(assemblyInfoSource))
            {
                var fileInfo = new FileInfo(fullPath);

                if (!fileSystem.DirectoryExists(fileInfo.Directory.FullName))
                {
                    fileSystem.CreateDirectory(fileInfo.Directory.FullName);
                }

                fileSystem.WriteAllText(fullPath, assemblyInfoSource);
                return(true);
            }

            Logger.WriteWarning($"No version assembly info template available to create source file '{fullPath}'");
            return(false);
        }
Ejemplo n.º 7
0
        static bool EnsureVersionAssemblyInfoFile(Arguments arguments, IFileSystem fileSystem, string fullPath)
        {
            if (fileSystem.Exists(fullPath))
            {
                return(true);
            }

            if (!arguments.EnsureAssemblyInfo)
            {
                return(false);
            }

            var assemblyInfoSource = AssemblyVersionInfoTemplates.GetAssemblyInfoTemplateFor(fullPath);

            if (!string.IsNullOrWhiteSpace(assemblyInfoSource))
            {
                var fileInfo = new FileInfo(fullPath);
                if (!fileSystem.DirectoryExists(fileInfo.Directory.FullName))
                {
                    fileSystem.CreateDirectory(fileInfo.Directory.FullName);
                }
                fileSystem.WriteAllText(fullPath, assemblyInfoSource);
                return(true);
            }
            Logger.WriteWarning(string.Format("No version assembly info template available to create source file '{0}'", arguments.UpdateAssemblyInfoFileName));
            return(false);
        }
Ejemplo n.º 8
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());
            }
        }
Ejemplo n.º 9
0
        public GitVersionContext(IRepository repository, Branch currentBranch, Config configuration, bool onlyEvaluateTrackedBranches = true, string commitId = null, string pathFilter = null)
        {
            Repository = repository;
            RepositoryMetadataProvider  = new GitRepoMetadataProvider(repository, configuration);
            FullConfiguration           = configuration;
            OnlyEvaluateTrackedBranches = onlyEvaluateTrackedBranches;
            PathFilter = pathFilter;

            if (currentBranch == null)
            {
                throw new InvalidOperationException("Need a branch to operate on");
            }

            if (!string.IsNullOrWhiteSpace(commitId))
            {
                Logger.WriteInfo(string.Format("Searching for specific commit '{0}'", commitId));

                var commit = repository.Commits.FirstOrDefault(c => string.Equals(c.Sha, commitId, StringComparison.OrdinalIgnoreCase));
                if (commit != null)
                {
                    CurrentCommit = commit;
                }
                else
                {
                    Logger.WriteWarning(string.Format("Commit '{0}' specified but not found", commitId));
                }
            }

            if (CurrentCommit == null)
            {
                Logger.WriteInfo("Using latest commit on specified branch");
                CurrentCommit = currentBranch.GetTip(PathFilter);
            }

            if (currentBranch.IsDetachedHead())
            {
                CurrentBranch = RepositoryMetadataProvider.GetBranchesContainingCommit(CurrentCommit, repository.Branches.ToList(), OnlyEvaluateTrackedBranches).OnlyOrDefault() ?? currentBranch;
            }
            else
            {
                CurrentBranch = currentBranch;
            }

            CalculateEffectiveConfiguration();

            CurrentCommitTaggedVersion = repository.Tags
                                         .SelectMany(t =>
            {
                SemanticVersion version;
                if (t.PeeledTarget() == CurrentCommit && SemanticVersion.TryParse(t.FriendlyName, Configuration.GitTagPrefix, out version))
                {
                    return new[] { version }
                }
                ;
                return(new SemanticVersion[0]);
            })
                                         .Max();
            IsCurrentCommitTagged = CurrentCommitTaggedVersion != null;
        }
Ejemplo n.º 10
0
        public VersionVariables ExecuteGitVersion(string targetUrl, string dynamicRepositoryLocation, Authentication authentication, string targetBranch, bool noFetch, string workingDirectory, string commitId, Config overrideConfig = null, bool noCache = false, bool noNormalize = false)
        {
            BuildServerList.Init(environment);
            // Normalise if we are running on build server
            var  applicableBuildServers = BuildServerList.GetApplicableBuildServers();
            var  buildServer            = applicableBuildServers.FirstOrDefault();
            bool normaliseGitDirectory  = !noNormalize && (buildServer != null);
            var  fetch = noFetch || (buildServer != null && buildServer.PreventFetch());
            var  shouldCleanUpRemotes = buildServer != null && buildServer.ShouldCleanUpRemotes();
            var  gitPreparer          = new GitPreparer(targetUrl, dynamicRepositoryLocation, authentication, fetch, workingDirectory);

            gitPreparer.Initialise(normaliseGitDirectory, ResolveCurrentBranch(buildServer, targetBranch, !string.IsNullOrWhiteSpace(dynamicRepositoryLocation)), shouldCleanUpRemotes);
            var dotGitDirectory = gitPreparer.GetDotGitDirectory();
            var projectRoot     = gitPreparer.GetProjectRootDirectory();

            // TODO Can't use this, it still needs work
            //var gitRepository = GitRepositoryFactory.CreateRepository(new RepositoryInfo
            //{
            //    Url = targetUrl,
            //    Branch = targetBranch,
            //    Authentication = new AuthenticationInfo
            //    {
            //        Username = authentication.Username,
            //        Password = authentication.Password
            //    },
            //    Directory = workingDirectory
            //});
            Logger.WriteInfo($"Project root is: {projectRoot}");
            Logger.WriteInfo($"DotGit directory is: {dotGitDirectory}");
            if (string.IsNullOrEmpty(dotGitDirectory) || string.IsNullOrEmpty(projectRoot))
            {
                // TODO Link to wiki article
                throw new Exception($"Failed to prepare or find the .git directory in path '{workingDirectory}'.");
            }

            var cacheKey = GitVersionCacheKeyFactory.Create(fileSystem, gitPreparer, overrideConfig, configFileLocator);

            var versionVariables = noCache ? default : gitVersionCache.LoadVersionVariablesFromDiskCache(gitPreparer, cacheKey);

                                   if (versionVariables == null)
                                   {
                                       versionVariables = ExecuteInternal(targetBranch, commitId, gitPreparer, buildServer, overrideConfig);

                                       if (!noCache)
                                       {
                                           try
                                           {
                                               gitVersionCache.WriteVariablesToDiskCache(gitPreparer, cacheKey, versionVariables);
                                           }
                                           catch (AggregateException e)
                                           {
                                               Logger.WriteWarning($"One or more exceptions during cache write:{Environment.NewLine}{e}");
                                           }
                                       }
                                   }

                                   return(versionVariables);
        }
Ejemplo n.º 11
0
        static void WriteBranchEnvVariableWarning()
        {
            Logger.WriteWarning(@"TeamCity doesn't make the current branch available through environmental variables.

Depending on your authentication and transport setup of your git VCS root things may work. In that case, ignore this warning.

In your TeamCity build configuration, add a parameter called `env.Git_Branch` with value %teamcity.build.vcs.branch.<vcsid>%

See http://gitversion.readthedocs.org/en/latest/build-server-support/build-server/teamcity for more info");
        }
Ejemplo n.º 12
0
        public GitVersionContext(IRepository repository, Branch currentBranch, Config configuration, bool onlyEvaluateTrackedBranches = true, string commitId = null)
        {
            Repository                  = repository;
            this.configuration          = configuration;
            OnlyEvaluateTrackedBranches = onlyEvaluateTrackedBranches;

            if (currentBranch == null)
            {
                throw new InvalidOperationException("Need a branch to operate on");
            }

            if (!string.IsNullOrWhiteSpace(commitId))
            {
                Logger.WriteInfo(string.Format("Searching for specific commit '{0}'", commitId));

                var commit = repository.Commits.FirstOrDefault(c => string.Equals(c.Sha, commitId, StringComparison.OrdinalIgnoreCase));
                if (commit != null)
                {
                    CurrentCommit = commit;
                }
            }

            if (CurrentCommit == null)
            {
                Logger.WriteWarning("No specific commit specified or found, falling back to latest commit on specified branch");

                CurrentCommit = currentBranch.Tip;
            }

            if (currentBranch.IsDetachedHead())
            {
                CurrentBranch = CurrentCommit.GetBranchesContainingCommit(repository, OnlyEvaluateTrackedBranches).OnlyOrDefault() ?? currentBranch;
            }
            else
            {
                CurrentBranch = currentBranch;
            }

            CalculateEffectiveConfiguration();

            CurrentCommitTaggedVersion = repository.Tags
                                         .SelectMany(t =>
            {
                SemanticVersion version;
                if (t.PeeledTarget() == CurrentCommit && SemanticVersion.TryParse(t.Name, Configuration.GitTagPrefix, out version))
                {
                    return new[] { version }
                }
                ;
                return(new SemanticVersion[0]);
            })
                                         .Max();
            IsCurrentCommitTagged = CurrentCommitTaggedVersion != null;
        }
Ejemplo n.º 13
0
 public bool TryGetVersion(string directory, out VersionVariables versionVariables, bool noFetch, Authentication authentication)
 {
     try
     {
         versionVariables = ExecuteGitVersion(null, null, authentication, null, noFetch, directory, null);
         return(true);
     }
     catch (Exception ex)
     {
         Logger.WriteWarning("Could not determine assembly version: " + ex);
         versionVariables = null;
         return(false);
     }
 }
Ejemplo n.º 14
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);
            }
        }
Ejemplo n.º 15
0
        List <BranchCommit> GetMergeCommitsForBranch(Branch branch, Branch[] excludedBranches)
        {
            if (mergeBaseCommitsCache.ContainsKey(branch))
            {
                Logger.WriteDebug(string.Format(
                                      "Cache hit for getting merge commits for branch {0}.",
                                      branch.CanonicalName));
                return(mergeBaseCommitsCache[branch]);
            }

            var currentBranchConfig = configuration.GetConfigForBranch(branch.NameWithoutRemote());
            var regexesToCheck      = currentBranchConfig == null
                ? new [] { ".*" } // Match anything if we can't find a branch config
                : currentBranchConfig.SourceBranches.Select(sb => configuration.Branches[sb].Regex);
            var branchMergeBases = Repository.Branches
                                   .ExcludingBranches(excludedBranches)
                                   .Where(b =>
            {
                if (b == branch)
                {
                    return(false);
                }
                var branchCanBeMergeBase = regexesToCheck.Any(regex => Regex.IsMatch(b.FriendlyName, regex));

                return(branchCanBeMergeBase);
            })
                                   .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);
        }
        /// <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)));
            }
        }
        public static Config Provide(string gitDirectory, IFileSystem fileSystem)
        {
            var configFilePath = GetConfigFilePath(gitDirectory);

            if (fileSystem.Exists(configFilePath))
            {
                var readAllText = fileSystem.ReadAllText(configFilePath);
                if (oldAssemblyVersioningScheme.IsMatch(readAllText))
                {
                    readAllText = oldAssemblyVersioningScheme.Replace(readAllText, "assembly-versioning-scheme");
                    fileSystem.WriteAllText(configFilePath, readAllText);
                    Logger.WriteWarning("Found legacy configuration value 'assemblyVersioningScheme', replaced with 'assembly-versioning-scheme");
                }

                return(ConfigReader.Read(new StringReader(readAllText)));
            }

            return(new Config());
        }
        static void WarnAboutObsoleteConfigFile(string workingDirectory, IFileSystem fileSystem)
        {
            var deprecatedConfigFilePath = Path.Combine(workingDirectory, ObsoleteConfigFileName);

            if (!fileSystem.Exists(deprecatedConfigFilePath))
            {
                return;
            }

            var defaultConfigFilePath = Path.Combine(workingDirectory, DefaultConfigFileName);

            if (fileSystem.Exists(defaultConfigFilePath))
            {
                Logger.WriteWarning(string.Format("Ambiguous config files at '{0}': '{1}' (deprecated) and '{2}'. Will be used '{2}'", workingDirectory, ObsoleteConfigFileName, DefaultConfigFileName));
                return;
            }

            Logger.WriteWarning(string.Format("'{0}' is deprecated, use '{1}' instead.", deprecatedConfigFilePath, DefaultConfigFileName));
        }
Ejemplo n.º 19
0
        static string GetConfigFilePath(string workingDirectory, IFileSystem fileSystem)
        {
            var ymlPath = Path.Combine(workingDirectory, "GitVersion.yml");

            if (fileSystem.Exists(ymlPath))
            {
                return(ymlPath);
            }

            var deprecatedPath = Path.Combine(workingDirectory, "GitVersionConfig.yaml");

            if (fileSystem.Exists(deprecatedPath))
            {
                Logger.WriteWarning("'GitVersionConfig.yaml' is deprecated, use 'GitVersion.yml' instead.");

                return(deprecatedPath);
            }

            return(ymlPath);
        }
Ejemplo n.º 20
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);
            }
        }
Ejemplo n.º 21
0
        public static void CheckoutFilesIfExist(this IRepository repository, params string[] fileNames)
        {
            if (fileNames == null || fileNames.Length == 0)
            {
                return;
            }

            Logger.WriteInfo("Checking out files that might be needed later in dynamic repository");

            foreach (var fileName in fileNames)
            {
                try
                {
                    Logger.WriteInfo(string.Format("  Trying to check out '{0}'", fileName));

                    var headBranch = repository.Head;
                    var tip        = headBranch.Tip;

                    var treeEntry = tip[fileName];
                    if (treeEntry == null)
                    {
                        continue;
                    }

                    var fullPath = Path.Combine(repository.GetRepositoryDirectory(), fileName);
                    using (var stream = ((Blob)treeEntry.Target).GetContentStream())
                    {
                        using (var streamReader = new BinaryReader(stream))
                        {
                            File.WriteAllBytes(fullPath, streamReader.ReadBytes((int)stream.Length));
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.WriteWarning(string.Format("  An error occurred while checking out '{0}': '{1}'", fileName, ex.Message));
                }
            }
        }
Ejemplo n.º 22
0
        public static IEnumerable <IBuildServer> GetApplicableBuildServers()
        {
            var buildServices = new List <IBuildServer>();

            foreach (var buildServer in BuildServers)
            {
                try
                {
                    if (buildServer.CanApplyToCurrentContext())
                    {
                        Logger.WriteInfo(string.Format("Applicable build agent found: '{0}'.", buildServer.GetType().Name));
                        buildServices.Add(buildServer);
                    }
                }
                catch (Exception ex)
                {
                    Logger.WriteWarning(string.Format("Failed to check build server '{0}': {1}", buildServer.GetType().Name, ex.Message));
                }
            }

            return(buildServices);
        }
Ejemplo n.º 23
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);
                    }
                }
            }
        }
Ejemplo n.º 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(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());
            }
        }
Ejemplo n.º 25
0
        static void Main()
        {
            int?exitCode = null;

            try
            {
                Arguments arguments;
                var       argumentsWithoutExeName = GetArgumentsWithoutExeName();
                try
                {
                    arguments = ArgumentParser.ParseArguments(argumentsWithoutExeName);
                }
                catch (Exception)
                {
                    Console.WriteLine("Failed to parse arguments: {0}", string.Join(" ", argumentsWithoutExeName));

                    HelpWriter.Write();
                    return;
                }

                if (arguments.IsHelp)
                {
                    HelpWriter.Write();
                    return;
                }

                if (!string.IsNullOrEmpty(arguments.Proj) || !string.IsNullOrEmpty(arguments.Exec))
                {
                    arguments.Output = OutputType.BuildServer;
                }

                ConfigureLogging(arguments);

                var gitPreparer  = new GitPreparer(arguments);
                var gitDirectory = gitPreparer.Prepare();
                if (string.IsNullOrEmpty(gitDirectory))
                {
                    Console.Error.WriteLine("Failed to prepare or find the .git directory in path '{0}'", arguments.TargetPath);
                    Environment.Exit(1);
                }

                var workingDirectory = Directory.GetParent(gitDirectory).FullName;
                Logger.WriteInfo("Working directory: " + workingDirectory);
                var applicableBuildServers = GetApplicableBuildServers(arguments).ToList();

                foreach (var buildServer in applicableBuildServers)
                {
                    buildServer.PerformPreProcessingSteps(gitDirectory);
                }

                var semanticVersion = VersionCache.GetVersion(gitDirectory);

                if (arguments.Output == OutputType.BuildServer)
                {
                    foreach (var buildServer in applicableBuildServers)
                    {
                        buildServer.WriteIntegration(semanticVersion, Console.WriteLine);
                    }
                }

                var variables = VariableProvider.GetVariablesFor(semanticVersion);
                if (arguments.Output == OutputType.Json)
                {
                    switch (arguments.VersionPart)
                    {
                    case null:
                        Console.WriteLine(JsonOutputFormatter.ToJson(variables));
                        break;

                    default:
                        string part;
                        if (!variables.TryGetValue(arguments.VersionPart, out part))
                        {
                            throw new WarningException(string.Format("Could not extract '{0}' from the available parts.", arguments.VersionPart));
                        }
                        Console.WriteLine(part);
                        break;
                    }
                }

                using (var assemblyInfoUpdate = new AssemblyInfoFileUpdate(arguments, workingDirectory, variables))
                {
                    var execRun    = RunExecCommandIfNeeded(arguments, workingDirectory, variables);
                    var msbuildRun = RunMsBuildIfNeeded(arguments, workingDirectory, variables);
                    if (!execRun && !msbuildRun)
                    {
                        assemblyInfoUpdate.DoNotRestoreAssemblyInfo();
                        //TODO Put warning back
                        //if (!context.CurrentBuildServer.IsRunningInBuildAgent())
                        //{
                        //    Console.WriteLine("WARNING: Not running in build server and /ProjectFile or /Exec arguments not passed");
                        //    Console.WriteLine();
                        //    Console.WriteLine("Run GitVersion.exe /? for help");
                        //}
                    }
                }

                if (gitPreparer.IsDynamicGitRepository)
                {
                    DeleteHelper.DeleteGitRepository(gitPreparer.DynamicGitRepositoryPath);
                }
            }
            catch (WarningException exception)
            {
                var error = string.Format("An error occurred:\r\n{0}", exception.Message);
                Logger.WriteWarning(error);

                exitCode = 1;
            }
            catch (Exception exception)
            {
                var error = string.Format("An unexpected error occurred:\r\n{0}", exception);
                Logger.WriteError(error);

                exitCode = 1;
            }

            if (Debugger.IsAttached)
            {
                Console.ReadKey();
            }

            if (!exitCode.HasValue)
            {
                exitCode = 0;
            }

            Environment.Exit(exitCode.Value);
        }
        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
                }));
            }
        }
Ejemplo n.º 27
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);
            }
        }
Ejemplo n.º 28
0
        public static void NormalizeGitDirectory(string gitDirectory, Authentication authentication, bool noFetch)
        {
            //If noFetch is enabled, then GitVersion will assume that the git repository is normalized before execution, so that fetching from remotes is not required.
            if (noFetch)
            {
                Logger.WriteInfo("Skipping fetching");
                return;
            }

            using (var repo = new Repository(gitDirectory))
            {
                var remote = EnsureOnlyOneRemoteIsDefined(repo);

                AddMissingRefSpecs(repo, remote);

                Logger.WriteInfo(string.Format("Fetching from remote '{0}' using the following refspecs: {1}.",
                                               remote.Name, string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))));

                var fetchOptions = BuildFetchOptions(authentication.Username, authentication.Password);
                repo.Network.Fetch(remote, fetchOptions);

                CreateMissingLocalBranchesFromRemoteTrackingOnes(repo, remote.Name);

                var headSha = repo.Refs.Head.TargetIdentifier;

                if (!repo.Info.IsHeadDetached)
                {
                    Logger.WriteInfo(string.Format("HEAD points at branch '{0}'.", headSha));
                    return;
                }

                Logger.WriteInfo(string.Format("HEAD is detached and points at commit '{0}'.", headSha));

                // In order to decide whether a fake branch is required or not, first check to see if any local branches have the same commit SHA of the head SHA.
                // If they do, go ahead and checkout that branch
                // If no, go ahead and check out a new branch, using the known commit SHA as the pointer
                var localBranchesWhereCommitShaIsHead = repo.Branches.Where(b => !b.IsRemote && b.Tip.Sha == headSha).ToList();

                if (localBranchesWhereCommitShaIsHead.Count > 1)
                {
                    var          branchNames   = localBranchesWhereCommitShaIsHead.Select(r => r.CanonicalName);
                    var          csvNames      = string.Join(", ", branchNames);
                    const string moveBranchMsg = "Move one of the branches along a commit to remove warning";

                    Logger.WriteWarning(string.Format("Found more than one local branch pointing at the commit '{0}' ({1}).", headSha, csvNames));
                    var master = localBranchesWhereCommitShaIsHead.SingleOrDefault(n => n.Name == "master");
                    if (master != null)
                    {
                        Logger.WriteWarning("Because one of the branches is 'master', will build master." + moveBranchMsg);
                        master.Checkout();
                    }
                    else
                    {
                        var branchesWithoutSeparators = localBranchesWhereCommitShaIsHead.Where(b => !b.Name.Contains('/') && !b.Name.Contains('-')).ToList();
                        if (branchesWithoutSeparators.Count == 1)
                        {
                            var branchWithoutSeparator = branchesWithoutSeparators[0];
                            Logger.WriteWarning(string.Format("Choosing {0} as it is the only branch without / or - in it. " + moveBranchMsg, branchWithoutSeparator.CanonicalName));
                            branchWithoutSeparator.Checkout();
                        }
                        else
                        {
                            throw new WarningException("Failed to try and guess branch to use. " + moveBranchMsg);
                        }
                    }
                }
                else if (localBranchesWhereCommitShaIsHead.Count == 0)
                {
                    Logger.WriteInfo(string.Format("No local branch pointing at the commit '{0}'. Fake branch needs to be created.", headSha));
                    CreateFakeBranchPointingAtThePullRequestTip(repo, authentication);
                }
                else
                {
                    Logger.WriteInfo(string.Format("Checking out local branch 'refs/heads/{0}'.", localBranchesWhereCommitShaIsHead[0].Name));
                    repo.Branches[localBranchesWhereCommitShaIsHead[0].Name].Checkout();
                }
            }
        }
Ejemplo n.º 29
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
                }));
            }
        }
Ejemplo n.º 30
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);
            }
        }