示例#1
0
        /// <summary>
        /// Normalisation 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 normalisation against a local repository
        /// </summary>
        public static void NormalizeGitDirectory(string gitDirectory, AuthenticationInfo authentication,
                                                 bool noFetch, string currentBranch, bool isDynamicRepository)
        {
            using (var repo = new Repository(gitDirectory))
            {
                // Need to ensure the HEAD does not move, this is essentially a BugCheck
                var expectedSha        = repo.Head.Tip.Sha;
                var expectedBranchName = repo.Head.CanonicalName;

                try
                {
                    var remote = EnsureOnlyOneRemoteIsDefined(repo);

                    AddMissingRefSpecs(repo, remote);

                    //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, if GitVersion does not calculate your version as expected you might need to allow fetching or use dynamic repositories");
                    }
                    else
                    {
                        Fetch(authentication, remote, repo);
                    }

                    EnsureLocalBranchExistsForCurrentBranch(repo, remote, currentBranch);
                    CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(repo, remote.Name);

                    // 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 != repo.Head.Tip.Sha &&
                        (isDynamicRepository || !expectedBranchName.IsBranch(currentBranch)))
                    {
                        var newExpectedSha        = repo.Head.Tip.Sha;
                        var newExpectedBranchName = repo.Head.CanonicalName;

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

                        expectedSha        = newExpectedSha;
                        expectedBranchName = newExpectedBranchName;
                    }

                    var headSha = repo.Refs.Head.TargetIdentifier;

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

                    Logger.WriteInfo($"HEAD is detached and points at commit '{headSha}'.");
                    Logger.WriteInfo(string.Format("Local Refs:\r\n" + string.Join(Environment.NewLine, repo.Refs.FromGlob("*").Select(r => $"{r.CanonicalName} ({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 = repo.Branches.Where(b => !b.IsRemote && b.Tip.Sha == headSha).ToList();

                    var matchingCurrentBranch = !string.IsNullOrEmpty(currentBranch)
                        ? localBranchesWhereCommitShaIsHead.SingleOrDefault(b => b.CanonicalName.Replace("/heads/", "/") == currentBranch.Replace("/heads/", "/"))
                        : null;
                    if (matchingCurrentBranch != null)
                    {
                        Logger.WriteInfo($"Checking out local branch '{currentBranch}'.");
                        Commands.Checkout(repo, matchingCurrentBranch);
                    }
                    else 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($"Found more than one local branch pointing at the commit '{headSha}' ({csvNames}).");
                        var master = localBranchesWhereCommitShaIsHead.SingleOrDefault(n => n.FriendlyName == "master");
                        if (master != null)
                        {
                            Logger.WriteWarning("Because one of the branches is 'master', will build master." + moveBranchMsg);
                            Commands.Checkout(repo, master);
                        }
                        else
                        {
                            var branchesWithoutSeparators = localBranchesWhereCommitShaIsHead.Where(b => !b.FriendlyName.Contains('/') && !b.FriendlyName.Contains('-')).ToList();
                            if (branchesWithoutSeparators.Count == 1)
                            {
                                var branchWithoutSeparator = branchesWithoutSeparators[0];
                                Logger.WriteWarning($"Choosing {branchWithoutSeparator.CanonicalName} as it is the only branch without / or - in it. " + moveBranchMsg);
                                Commands.Checkout(repo, branchWithoutSeparator);
                            }
                            else
                            {
                                throw new WarningException("Failed to try and guess branch to use. " + moveBranchMsg);
                            }
                        }
                    }
                    else if (localBranchesWhereCommitShaIsHead.Count == 0)
                    {
                        Logger.WriteInfo($"No local branch pointing at the commit '{headSha}'. Fake branch needs to be created.");
                        CreateFakeBranchPointingAtThePullRequestTip(repo, authentication);
                    }
                    else
                    {
                        Logger.WriteInfo($"Checking out local branch 'refs/heads/{localBranchesWhereCommitShaIsHead[0].FriendlyName}'.");
                        Commands.Checkout(repo, repo.Branches[localBranchesWhereCommitShaIsHead[0].FriendlyName]);
                    }
                }
                finally
                {
                    if (repo.Head.Tip.Sha != expectedSha)
                    {
                        if (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 {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
 public static void Fetch(AuthenticationInfo authentication, Remote remote, Repository repo)
 {
     Logger.WriteInfo($"Fetching from remote '{remote.Name}' using the following refspecs: {string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))}.");
     Commands.Fetch(repo, remote.Name, new string[0], authentication.ToFetchOptions(), null);
 }
示例#3
0
 public void Fetch(string remote, IEnumerable <string> refSpecs, AuthenticationInfo auth, string logMessage) =>
 Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage);