// TODO I think we need to take a fresh approach to this.. it's getting really complex with heaps of edge cases
        private BranchConfig InheritBranchConfiguration(Branch targetBranch, BranchConfig branchConfiguration, Commit currentCommit, Config configuration, IList <Branch> excludedInheritBranches)
        {
            using (log.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 = currentCommit.Parents.Count();
                if (parentCount == 2)
                {
                    excludedBranches = CalculateWhenMultipleParents(currentCommit, ref targetBranch, excludedBranches);
                }

                excludedInheritBranches ??= repositoryMetadataProvider.GetExcludedInheritBranches(configuration);
                // Add new excluded branches.
                foreach (var excludedBranch in excludedBranches.ExcludingBranches(excludedInheritBranches))
                {
                    excludedInheritBranches.Add(excludedBranch);
                }
                var branchesToEvaluate = repositoryMetadataProvider.ExcludingBranches(excludedInheritBranches).ToList();

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

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

                if (possibleParents.Count == 1)
                {
                    var branchConfig = GetBranchConfiguration(possibleParents[0], currentCommit, configuration, excludedInheritBranches);
                    // If we have resolved a fallback config we should not return that we have got config
                    if (branchConfig.Name != FallbackConfigName)
                    {
                        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
                var errorMessage = possibleParents.Count == 0
                    ? "Failed to inherit Increment branch configuration, no branches found."
                    : "Failed to inherit Increment branch configuration, ended up with: " + string.Join(", ", possibleParents.Select(p => p.FriendlyName));

                var chosenBranch = repositoryMetadataProvider.GetChosenBranch(configuration);
                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;
                log.Warning(errorMessage + System.Environment.NewLine + "Falling back to " + branchName + " branch config");

                // To prevent infinite loops, make sure that a new branch was chosen.
                if (targetBranch.IsSameBranch(chosenBranch))
                {
                    var developOrMasterConfig =
                        ChooseMasterOrDevelopIncrementStrategyIfTheChosenBranchIsOneOfThem(
                            chosenBranch, branchConfiguration, configuration);
                    if (developOrMasterConfig != null)
                    {
                        return(developOrMasterConfig);
                    }

                    log.Warning("Fallback branch wants to inherit Increment branch configuration from itself. Using patch increment instead.");
                    return(new BranchConfig(branchConfiguration)
                    {
                        Increment = IncrementStrategy.Patch
                    });
                }

                var inheritingBranchConfig = GetBranchConfiguration(chosenBranch, currentCommit, configuration, excludedInheritBranches);
                var configIncrement        = inheritingBranchConfig.Increment;
                if (inheritingBranchConfig.Name.IsEquivalentTo(FallbackConfigName) && configIncrement == IncrementStrategy.Inherit)
                {
                    log.Warning("Fallback config inherits by default, dropping to patch increment");
                    configIncrement = IncrementStrategy.Patch;
                }
                return(new BranchConfig(branchConfiguration)
                {
                    Increment = configIncrement,
                    PreventIncrementOfMergedBranchVersion = inheritingBranchConfig.PreventIncrementOfMergedBranchVersion,
                    // If we are inheriting from develop then we should behave like develop
                    TracksReleaseBranches = inheritingBranchConfig.TracksReleaseBranches
                });
            }
        }
        private static Config CreateDefaultConfiguration()
        {
            var config = new Config
            {
                AssemblyVersioningScheme     = AssemblyVersioningScheme.MajorMinorPatch,
                AssemblyFileVersioningScheme = AssemblyFileVersioningScheme.MajorMinorPatch,
                TagPrefix      = Config.DefaultTagPrefix,
                VersioningMode = VersioningMode.ContinuousDelivery,
                ContinuousDeploymentFallbackTag = "ci",
                MajorVersionBumpMessage         = IncrementStrategyFinder.DefaultMajorPattern,
                MinorVersionBumpMessage         = IncrementStrategyFinder.DefaultMinorPattern,
                PatchVersionBumpMessage         = IncrementStrategyFinder.DefaultPatchPattern,
                NoBumpMessage                    = IncrementStrategyFinder.DefaultNoBumpPattern,
                CommitMessageIncrementing        = CommitMessageIncrementMode.Enabled,
                LegacySemVerPadding              = 4,
                BuildMetaDataPadding             = 4,
                CommitsSinceVersionSourcePadding = 4,
                CommitDateFormat                 = "yyyy-MM-dd",
                UpdateBuildNumber                = true,
                TagPreReleaseWeight              = DefaultTagPreReleaseWeight
            };

            AddBranchConfig(Config.DevelopBranchKey,
                            new BranchConfig
            {
                Regex                 = Config.DevelopBranchRegex,
                SourceBranches        = new HashSet <string>(),
                Tag                   = "alpha",
                Increment             = IncrementStrategy.Minor,
                TrackMergeTarget      = true,
                TracksReleaseBranches = true,
                PreReleaseWeight      = 0,
            });

            AddBranchConfig(Config.MasterBranchKey,
                            new BranchConfig
            {
                Regex          = Config.MasterBranchRegex,
                SourceBranches = new HashSet <string> {
                    Config.DevelopBranchKey, Config.ReleaseBranchKey
                },
                Tag = string.Empty,
                PreventIncrementOfMergedBranchVersion = true,
                Increment        = IncrementStrategy.Patch,
                IsMainline       = true,
                PreReleaseWeight = 55000,
            });

            AddBranchConfig(Config.ReleaseBranchKey,
                            new BranchConfig
            {
                Regex          = Config.ReleaseBranchRegex,
                SourceBranches = new HashSet <string> {
                    Config.DevelopBranchKey, Config.MasterBranchKey, Config.SupportBranchKey, Config.ReleaseBranchKey
                },
                Tag = "beta",
                PreventIncrementOfMergedBranchVersion = true,
                Increment        = IncrementStrategy.None,
                IsReleaseBranch  = true,
                PreReleaseWeight = 30000,
            });

            AddBranchConfig(Config.FeatureBranchKey,
                            new BranchConfig
            {
                Regex          = Config.FeatureBranchRegex,
                SourceBranches = new HashSet <string> {
                    Config.DevelopBranchKey, Config.MasterBranchKey, Config.ReleaseBranchKey, Config.FeatureBranchKey, Config.SupportBranchKey, Config.HotfixBranchKey
                },
                Increment        = IncrementStrategy.Inherit,
                PreReleaseWeight = 30000,
            });

            AddBranchConfig(Config.PullRequestBranchKey,
                            new BranchConfig
            {
                Regex          = Config.PullRequestRegex,
                SourceBranches = new HashSet <string> {
                    Config.DevelopBranchKey, Config.MasterBranchKey, Config.ReleaseBranchKey, Config.FeatureBranchKey, Config.SupportBranchKey, Config.HotfixBranchKey
                },
                Tag = "PullRequest",
                TagNumberPattern = @"[/-](?<number>\d+)",
                Increment        = IncrementStrategy.Inherit,
                PreReleaseWeight = 30000,
            });

            AddBranchConfig(Config.HotfixBranchKey,
                            new BranchConfig
            {
                Regex          = Config.HotfixBranchRegex,
                SourceBranches = new HashSet <string> {
                    Config.DevelopBranchKey, Config.MasterBranchKey, Config.SupportBranchKey
                },
                Tag              = "beta",
                Increment        = IncrementStrategy.Patch,
                PreReleaseWeight = 30000,
            });

            AddBranchConfig(Config.SupportBranchKey,
                            new BranchConfig
            {
                Regex          = Config.SupportBranchRegex,
                SourceBranches = new HashSet <string> {
                    Config.MasterBranchKey
                },
                Tag = string.Empty,
                PreventIncrementOfMergedBranchVersion = true,
                Increment        = IncrementStrategy.Patch,
                IsMainline       = true,
                PreReleaseWeight = 55000,
            });

            return(config);

            void AddBranchConfig(string name, BranchConfig overrides)
            {
                config.Branches[name] = BranchConfig.CreateDefaultBranchConfig(name).Apply(overrides);
            }
        }
        private static BranchConfig ChooseMasterOrDevelopIncrementStrategyIfTheChosenBranchIsOneOfThem(Branch chosenBranch, BranchConfig branchConfiguration, Config config)
        {
            BranchConfig masterOrDevelopConfig = null;
            var          developBranchRegex    = config.Branches[Config.DevelopBranchKey].Regex;
            var          masterBranchRegex     = config.Branches[Config.MasterBranchKey].Regex;

            if (Regex.IsMatch(chosenBranch.FriendlyName, developBranchRegex, RegexOptions.IgnoreCase))
            {
                // Normally we would not expect this to happen but for safety we add a check
                if (config.Branches[Config.DevelopBranchKey].Increment !=
                    IncrementStrategy.Inherit)
                {
                    masterOrDevelopConfig = new BranchConfig(branchConfiguration)
                    {
                        Increment = config.Branches[Config.DevelopBranchKey].Increment
                    };
                }
            }
            else if (Regex.IsMatch(chosenBranch.FriendlyName, masterBranchRegex, RegexOptions.IgnoreCase))
            {
                // Normally we would not expect this to happen but for safety we add a check
                if (config.Branches[Config.MasterBranchKey].Increment !=
                    IncrementStrategy.Inherit)
                {
                    masterOrDevelopConfig = new BranchConfig(branchConfiguration)
                    {
                        Increment = config.Branches[Config.DevelopBranchKey].Increment
                    };
                }
            }
            return(masterOrDevelopConfig);
        }
        public static EffectiveConfiguration CalculateEffectiveConfiguration(this Config configuration, BranchConfig currentBranchConfig)
        {
            var name = currentBranchConfig.Name;

            if (!currentBranchConfig.VersioningMode.HasValue)
            {
                throw new Exception($"Configuration value for 'Versioning mode' for branch {name} has no value. (this should not happen, please report an issue)");
            }
            if (!currentBranchConfig.Increment.HasValue)
            {
                throw new Exception($"Configuration value for 'Increment' for branch {name} has no value. (this should not happen, please report an issue)");
            }
            if (!currentBranchConfig.PreventIncrementOfMergedBranchVersion.HasValue)
            {
                throw new Exception($"Configuration value for 'PreventIncrementOfMergedBranchVersion' for branch {name} has no value. (this should not happen, please report an issue)");
            }
            if (!currentBranchConfig.TrackMergeTarget.HasValue)
            {
                throw new Exception($"Configuration value for 'TrackMergeTarget' for branch {name} has no value. (this should not happen, please report an issue)");
            }
            if (!currentBranchConfig.TracksReleaseBranches.HasValue)
            {
                throw new Exception($"Configuration value for 'TracksReleaseBranches' for branch {name} has no value. (this should not happen, please report an issue)");
            }
            if (!currentBranchConfig.IsReleaseBranch.HasValue)
            {
                throw new Exception($"Configuration value for 'IsReleaseBranch' for branch {name} has no value. (this should not happen, please report an issue)");
            }

            if (!configuration.AssemblyVersioningScheme.HasValue)
            {
                throw new Exception("Configuration value for 'AssemblyVersioningScheme' has no value. (this should not happen, please report an issue)");
            }
            if (!configuration.AssemblyFileVersioningScheme.HasValue)
            {
                throw new Exception("Configuration value for 'AssemblyFileVersioningScheme' has no value. (this should not happen, please report an issue)");
            }
            if (!configuration.CommitMessageIncrementing.HasValue)
            {
                throw new Exception("Configuration value for 'CommitMessageIncrementing' has no value. (this should not happen, please report an issue)");
            }
            if (!configuration.LegacySemVerPadding.HasValue)
            {
                throw new Exception("Configuration value for 'LegacySemVerPadding' has no value. (this should not happen, please report an issue)");
            }
            if (!configuration.BuildMetaDataPadding.HasValue)
            {
                throw new Exception("Configuration value for 'BuildMetaDataPadding' has no value. (this should not happen, please report an issue)");
            }
            if (!configuration.CommitsSinceVersionSourcePadding.HasValue)
            {
                throw new Exception("Configuration value for 'CommitsSinceVersionSourcePadding' has no value. (this should not happen, please report an issue)");
            }

            var versioningMode    = currentBranchConfig.VersioningMode.Value;
            var tag               = currentBranchConfig.Tag;
            var tagNumberPattern  = currentBranchConfig.TagNumberPattern;
            var incrementStrategy = currentBranchConfig.Increment.Value;
            var preventIncrementForMergedBranchVersion = currentBranchConfig.PreventIncrementOfMergedBranchVersion.Value;
            var trackMergeTarget = currentBranchConfig.TrackMergeTarget.Value;
            var preReleaseWeight = currentBranchConfig.PreReleaseWeight ?? 0;

            var nextVersion = configuration.NextVersion;
            var assemblyVersioningScheme     = configuration.AssemblyVersioningScheme.Value;
            var assemblyFileVersioningScheme = configuration.AssemblyFileVersioningScheme.Value;
            var assemblyInformationalFormat  = configuration.AssemblyInformationalFormat;
            var assemblyVersioningFormat     = configuration.AssemblyVersioningFormat;
            var assemblyFileVersioningFormat = configuration.AssemblyFileVersioningFormat;
            var gitTagPrefix     = configuration.TagPrefix;
            var majorMessage     = configuration.MajorVersionBumpMessage;
            var minorMessage     = configuration.MinorVersionBumpMessage;
            var patchMessage     = configuration.PatchVersionBumpMessage;
            var noBumpMessage    = configuration.NoBumpMessage;
            var commitDateFormat = configuration.CommitDateFormat;

            var commitMessageVersionBump = currentBranchConfig.CommitMessageIncrementing ?? configuration.CommitMessageIncrementing.Value;

            return(new EffectiveConfiguration(
                       assemblyVersioningScheme, assemblyFileVersioningScheme, assemblyInformationalFormat, assemblyVersioningFormat, assemblyFileVersioningFormat, versioningMode, gitTagPrefix,
                       tag, nextVersion, incrementStrategy,
                       currentBranchConfig.Regex,
                       preventIncrementForMergedBranchVersion,
                       tagNumberPattern, configuration.ContinuousDeploymentFallbackTag,
                       trackMergeTarget,
                       majorMessage, minorMessage, patchMessage, noBumpMessage,
                       commitMessageVersionBump,
                       configuration.LegacySemVerPadding.Value,
                       configuration.BuildMetaDataPadding.Value,
                       configuration.CommitsSinceVersionSourcePadding.Value,
                       configuration.Ignore.ToFilters(),
                       currentBranchConfig.TracksReleaseBranches.Value,
                       currentBranchConfig.IsReleaseBranch.Value,
                       commitDateFormat,
                       preReleaseWeight));
        }