public ReleaseInfo(ReleaseBranchInfo currentBranch, ReleaseBranchInfo newBranch)
            {
                Requires.NotNull(currentBranch, nameof(currentBranch));
                // skip null check for newBranch, it is allowed to be null.

                this.CurrentBranch = currentBranch;
                this.NewBranch     = newBranch;
            }
        /// <summary>
        /// Prepares a release for the specified directory by creating a release branch and incrementing the version in the current branch.
        /// </summary>
        /// <exception cref="ReleasePreparationException">Thrown when the release could not be created.</exception>
        /// <param name="projectDirectory">
        /// The path to the directory which may (or its ancestors may) define the version file.
        /// </param>
        /// <param name="releaseUnstableTag">
        /// The prerelease tag to add to the version on the release branch. Pass <c>null</c> to omit/remove the prerelease tag.
        /// The leading hyphen may be specified or omitted.
        /// </param>
        /// <param name="nextVersion">
        /// The next version to save to the version file on the current branch. Pass <c>null</c> to automatically determine the next
        /// version based on the current version and the <c>versionIncrement</c> setting in <c>version.json</c>.
        /// Parameter will be ignored if the current branch is a release branch.
        /// </param>
        /// <param name="versionIncrement">
        /// The increment to apply in order to determine the next version on the current branch.
        /// If specified, value will be used instead of the increment specified in <c>version.json</c>.
        /// Parameter will be ignored if the current branch is a release branch.
        /// </param>
        /// <param name="outputMode">
        /// The output format to use for writing to stdout.
        /// </param>
        public void PrepareRelease(string projectDirectory, string releaseUnstableTag = null, Version nextVersion = null, VersionOptions.ReleaseVersionIncrement?versionIncrement = null, ReleaseManagerOutputMode outputMode = default)
        {
            Requires.NotNull(projectDirectory, nameof(projectDirectory));

            // open the git repository
            var repository = this.GetRepository(projectDirectory);

            if (repository.Info.IsHeadDetached)
            {
                this.stderr.WriteLine("Detached head. Check out a branch first.");
                throw new ReleasePreparationException(ReleasePreparationError.DetachedHead);
            }

            // get the current version
            var versionOptions = VersionFile.GetVersion(projectDirectory);

            if (versionOptions == null)
            {
                this.stderr.WriteLine($"Failed to load version file for directory '{projectDirectory}'.");
                throw new ReleasePreparationException(ReleasePreparationError.NoVersionFile);
            }

            var releaseBranchName  = this.GetReleaseBranchName(versionOptions);
            var originalBranchName = repository.Head.FriendlyName;
            var releaseVersion     = string.IsNullOrEmpty(releaseUnstableTag)
                ? versionOptions.Version.WithoutPrepreleaseTags()
                : versionOptions.Version.SetFirstPrereleaseTag(releaseUnstableTag);

            // check if the current branch is the release branch
            if (string.Equals(originalBranchName, releaseBranchName, StringComparison.OrdinalIgnoreCase))
            {
                if (outputMode == ReleaseManagerOutputMode.Text)
                {
                    this.stdout.WriteLine($"{releaseBranchName} branch advanced from {versionOptions.Version} to {releaseVersion}.");
                }
                else
                {
                    var releaseInfo = new ReleaseInfo(new ReleaseBranchInfo(releaseBranchName, repository.Head.Tip.Id.ToString(), releaseVersion));
                    this.WriteToOutput(releaseInfo);
                }
                this.UpdateVersion(projectDirectory, repository, versionOptions.Version, releaseVersion);
                return;
            }

            var nextDevVersion = this.GetNextDevVersion(versionOptions, nextVersion, versionIncrement);

            // check if the release branch already exists
            if (repository.Branches[releaseBranchName] != null)
            {
                this.stderr.WriteLine($"Cannot create branch '{releaseBranchName}' because it already exists.");
                throw new ReleasePreparationException(ReleasePreparationError.BranchAlreadyExists);
            }

            // create release branch and update version
            var releaseBranch = repository.CreateBranch(releaseBranchName);

            Commands.Checkout(repository, releaseBranch);
            this.UpdateVersion(projectDirectory, repository, versionOptions.Version, releaseVersion);

            if (outputMode == ReleaseManagerOutputMode.Text)
            {
                this.stdout.WriteLine($"{releaseBranchName} branch now tracks v{releaseVersion} stabilization and release.");
            }

            // update version on main branch
            Commands.Checkout(repository, originalBranchName);
            this.UpdateVersion(projectDirectory, repository, versionOptions.Version, nextDevVersion);

            if (outputMode == ReleaseManagerOutputMode.Text)
            {
                this.stdout.WriteLine($"{originalBranchName} branch now tracks v{nextDevVersion} development.");
            }

            // Merge release branch back to main branch
            var mergeOptions = new MergeOptions()
            {
                CommitOnSuccess = true,
                MergeFileFavor  = MergeFileFavor.Ours,
            };

            repository.Merge(releaseBranch, this.GetSignature(repository), mergeOptions);

            if (outputMode == ReleaseManagerOutputMode.Json)
            {
                var originalBranchInfo = new ReleaseBranchInfo(originalBranchName, repository.Head.Tip.Sha, nextDevVersion);
                var releaseBranchInfo  = new ReleaseBranchInfo(releaseBranchName, repository.Branches[releaseBranchName].Tip.Id.ToString(), releaseVersion);
                var releaseInfo        = new ReleaseInfo(originalBranchInfo, releaseBranchInfo);

                this.WriteToOutput(releaseInfo);
            }
        }
 /// <summary>
 /// Initializes a new instance of <see cref="ReleaseInfo"/>.
 /// </summary>
 /// <param name="currentBranch">Information on the branch the release was created from.</param>
 public ReleaseInfo(ReleaseBranchInfo currentBranch) : this(currentBranch, null)
 {
 }