예제 #1
0
    /// <summary>
    /// Normalization of a git directory turns all remote branches into local branches,
    /// turns pull request refs into a real branch and a few other things.
    /// This is designed to be run *only on the build server* which checks out repositories in different ways.
    /// It is not recommended to run normalization against a local repository
    /// </summary>
    private void NormalizeGitDirectory(bool noFetch, string?currentBranchName, bool isDynamicRepository)
    {
        var authentication = this.options.Value.Authentication;
        // Need to ensure the HEAD does not move, this is essentially a BugCheck
        var expectedSha        = this.repository.Head.Tip?.Sha;
        var expectedBranchName = this.repository.Head.Name.Canonical;

        try
        {
            var remote = EnsureOnlyOneRemoteIsDefined();

            FetchRemotesIfRequired(remote, noFetch, authentication);
            EnsureLocalBranchExistsForCurrentBranch(remote, currentBranchName);
            CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(remote.Name);

            var currentBranch = this.repository.Branches.FirstOrDefault(x => x.Name.EquivalentTo(currentBranchName));
            // Bug fix for https://github.com/GitTools/GitVersion/issues/1754, head maybe have been changed
            // if this is a dynamic repository. But only allow this in case the branches are different (branch switch)
            if (expectedSha != this.repository.Head.Tip?.Sha)
            {
                if (isDynamicRepository || currentBranch is null || !this.repository.Head.Equals(currentBranch))
                {
                    var newExpectedSha        = this.repository.Head.Tip?.Sha;
                    var newExpectedBranchName = this.repository.Head.Name.Canonical;

                    this.log.Info($"Head has moved from '{expectedBranchName} | {expectedSha}' => '{newExpectedBranchName} | {newExpectedSha}', allowed since this is a dynamic repository");

                    expectedSha = newExpectedSha;
                }
            }

            EnsureHeadIsAttachedToBranch(currentBranchName, authentication);
        }
        finally
        {
            if (this.repository.Head.Tip?.Sha != expectedSha)
            {
                if (this.environment.GetEnvironmentVariable("IGNORE_NORMALISATION_GIT_HEAD_MOVE") != "1")
                {
                    // Whoa, HEAD has moved, it shouldn't have. We need to blow up because there is a bug in normalisation
                    throw new BugException($@"GitVersion has a bug, your HEAD has moved after repo normalisation.

To disable this error set an environmental variable called IGNORE_NORMALISATION_GIT_HEAD_MOVE to 1

Please run `git {GitExtensions.CreateGitLogArgs(100)}` and submit it along with your build log (with personal info removed) in a new issue at https://github.com/GitTools/GitVersion");
                }
            }
        }
    }
예제 #2
0
    /// <summary>
    /// Normalization of a git directory turns all remote branches into local branches,
    /// turns pull request refs into a real branch and a few other things.
    /// This is designed to be run *only on the build server* which checks out repositories in different ways.
    /// It is not recommended to run normalization against a local repository
    /// </summary>
    private void NormalizeGitDirectory(bool noFetch, string?currentBranchName, bool isDynamicRepository)
    {
        var authentication = this.options.Value.Authentication;
        // Need to ensure the HEAD does not move, this is essentially a BugCheck
        var expectedSha        = this.repository.Head.Tip?.Sha;
        var expectedBranchName = this.repository.Head.Name.Canonical;

        try
        {
            var remote = EnsureOnlyOneRemoteIsDefined();

            //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)
            {
                this.log.Info("Skipping fetching, if GitVersion does not calculate your version as expected you might need to allow fetching or use dynamic repositories");
            }
            else
            {
                var refSpecs = string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification));
                this.log.Info($"Fetching from remote '{remote.Name}' using the following refspecs: {refSpecs}.");
                this.retryAction.Execute(() => this.repository.Fetch(remote.Name, Enumerable.Empty <string>(), authentication, null));
            }

            EnsureLocalBranchExistsForCurrentBranch(remote, currentBranchName);
            CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(remote.Name);

            var currentBranch = this.repositoryStore.FindBranch(currentBranchName);
            // Bug fix for https://github.com/GitTools/GitVersion/issues/1754, head maybe have been changed
            // if this is a dynamic repository. But only allow this in case the branches are different (branch switch)
            if (expectedSha != this.repository.Head.Tip?.Sha &&
                (isDynamicRepository || currentBranch is null || !this.repository.Head.Equals(currentBranch)))
            {
                var newExpectedSha        = this.repository.Head.Tip?.Sha;
                var newExpectedBranchName = this.repository.Head.Name.Canonical;

                this.log.Info($"Head has moved from '{expectedBranchName} | {expectedSha}' => '{newExpectedBranchName} | {newExpectedSha}', allowed since this is a dynamic repository");

                expectedSha = newExpectedSha;
            }

            var headSha = this.repository.Refs.Head?.TargetIdentifier;

            if (!this.repository.IsHeadDetached)
            {
                this.log.Info($"HEAD points at branch '{headSha}'.");
                return;
            }

            this.log.Info($"HEAD is detached and points at commit '{headSha}'.");
            this.log.Info($"Local Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, this.repository.Refs.FromGlob("*").Select(r => $"{r.Name.Canonical} ({r.TargetIdentifier})")));

            // 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 = this.repository.Branches.Where(b => !b.IsRemote && b.Tip?.Sha == headSha).ToList();

            var matchingCurrentBranch = !currentBranchName.IsNullOrEmpty()
                ? localBranchesWhereCommitShaIsHead.SingleOrDefault(b => b.Name.Canonical.Replace("/heads/", "/") == currentBranchName.Replace("/heads/", "/"))
                : null;
            if (matchingCurrentBranch != null)
            {
                this.log.Info($"Checking out local branch '{currentBranchName}'.");
                Checkout(matchingCurrentBranch.Name.Canonical);
            }
            else if (localBranchesWhereCommitShaIsHead.Count > 1)
            {
                var          branchNames   = localBranchesWhereCommitShaIsHead.Select(r => r.Name.Canonical);
                var          csvNames      = string.Join(", ", branchNames);
                const string moveBranchMsg = "Move one of the branches along a commit to remove warning";

                this.log.Warning($"Found more than one local branch pointing at the commit '{headSha}' ({csvNames}).");
                var main = localBranchesWhereCommitShaIsHead.SingleOrDefault(n => n.Name.EquivalentTo(Config.MainBranchKey));
                if (main != null)
                {
                    this.log.Warning("Because one of the branches is 'main', will build main." + moveBranchMsg);
                    Checkout(Config.MainBranchKey);
                }
                else
                {
                    var branchesWithoutSeparators = localBranchesWhereCommitShaIsHead.Where(b => !b.Name.Friendly.Contains('/') && !b.Name.Friendly.Contains('-')).ToList();
                    if (branchesWithoutSeparators.Count == 1)
                    {
                        var branchWithoutSeparator = branchesWithoutSeparators[0];
                        this.log.Warning($"Choosing {branchWithoutSeparator.Name.Canonical} as it is the only branch without / or - in it. " + moveBranchMsg);
                        Checkout(branchWithoutSeparator.Name.Canonical);
                    }
                    else
                    {
                        throw new WarningException("Failed to try and guess branch to use. " + moveBranchMsg);
                    }
                }
            }
            else if (localBranchesWhereCommitShaIsHead.Count == 0)
            {
                this.log.Info($"No local branch pointing at the commit '{headSha}'. Fake branch needs to be created.");
                this.retryAction.Execute(() => this.repository.CreateBranchForPullRequestBranch(authentication));
            }
            else
            {
                this.log.Info($"Checking out local branch 'refs/heads/{localBranchesWhereCommitShaIsHead[0]}'.");
                Checkout(localBranchesWhereCommitShaIsHead[0].Name.Friendly);
            }
        }
        finally
        {
            if (this.repository.Head.Tip?.Sha != expectedSha)
            {
                if (this.environment.GetEnvironmentVariable("IGNORE_NORMALISATION_GIT_HEAD_MOVE") != "1")
                {
                    // Whoa, HEAD has moved, it shouldn't have. We need to blow up because there is a bug in normalisation
                    throw new BugException($@"GitVersion has a bug, your HEAD has moved after repo normalisation.

To disable this error set an environmental variable called IGNORE_NORMALISATION_GIT_HEAD_MOVE to 1

Please run `git {GitExtensions.CreateGitLogArgs(100)}` and submit it along with your build log (with personal info removed) in a new issue at https://github.com/GitTools/GitVersion");
                }
            }
        }
    }