/// <summary> /// Clone a remote git repo. /// </summary> /// <param name="repoUri">Repository uri to clone</param> /// <param name="commit">Branch, commit, or tag to checkout</param> /// <param name="targetDirectory">Target directory to clone to</param> /// <param name="gitDirectory">Location for the .git directory, or null for default</param> /// <returns></returns> protected void Clone(string repoUri, string commit, string targetDirectory, ILogger _logger, string pat, string gitDirectory) { string dotnetMaestro = "dotnet-maestro"; LibGit2Sharp.CloneOptions cloneOptions = new LibGit2Sharp.CloneOptions { Checkout = false, CredentialsProvider = (url, user, cred) => new LibGit2Sharp.UsernamePasswordCredentials { // The PAT is actually the only thing that matters here, the username // will be ignored. Username = dotnetMaestro, Password = pat }, }; using (_logger.BeginScope("Cloning {repoUri} to {targetDirectory}", repoUri, targetDirectory)) { try { _logger.LogDebug($"Cloning {repoUri} to {targetDirectory}"); string repoPath = LibGit2Sharp.Repository.Clone( repoUri, targetDirectory, cloneOptions); LibGit2Sharp.CheckoutOptions checkoutOptions = new LibGit2Sharp.CheckoutOptions { CheckoutModifiers = LibGit2Sharp.CheckoutModifiers.Force, }; _logger.LogDebug($"Reading local repo from {repoPath}"); using (LibGit2Sharp.Repository localRepo = new LibGit2Sharp.Repository(repoPath)) { if (commit == null) { commit = localRepo.Head.Reference.TargetIdentifier; _logger.LogInformation($"Repo {localRepo.Info.WorkingDirectory} has no commit to clone at, assuming it's {commit}"); } _logger.LogDebug($"Attempting to checkout {commit} as commit in {localRepo.Info.WorkingDirectory}"); LibGit2SharpHelpers.SafeCheckout(localRepo, commit, checkoutOptions, _logger); } // LibGit2Sharp doesn't support a --git-dir equivalent yet (https://github.com/libgit2/libgit2sharp/issues/1467), so we do this manually if (gitDirectory != null) { Directory.Move(repoPath, gitDirectory); File.WriteAllText(repoPath.TrimEnd('\\', '/'), $"gitdir: {gitDirectory}"); } using (LibGit2Sharp.Repository localRepo = new LibGit2Sharp.Repository(targetDirectory)) { CheckoutSubmodules(localRepo, cloneOptions, gitDirectory, _logger); } } catch (Exception exc) { throw new Exception($"Something went wrong when cloning repo {repoUri} at {commit ?? "<default branch>"} into {targetDirectory}", exc); } } }
/// <summary> /// Checkout the repo to the specified state. /// </summary> /// <param name="commit">Tag, branch, or commit to checkout.</param> public void Checkout(string repoDir, string commit, bool force = false) { using (_logger.BeginScope("Checking out {commit}", commit ?? "default commit")) { _logger.LogDebug($"Checking out {commit}", commit ?? "default commit"); LibGit2Sharp.CheckoutOptions checkoutOptions = new LibGit2Sharp.CheckoutOptions { CheckoutModifiers = force ? LibGit2Sharp.CheckoutModifiers.Force : LibGit2Sharp.CheckoutModifiers.None, }; try { _logger.LogDebug($"Checking out {commit ?? "default commit"}"); _logger.LogDebug($"Reading local repo from {repoDir}"); using (LibGit2Sharp.Repository localRepo = new LibGit2Sharp.Repository(repoDir)) { if (commit == null) { commit = localRepo.Head.Reference.TargetIdentifier; _logger.LogInformation($"Repo {localRepo.Info.WorkingDirectory} default commit to checkout is {commit}"); } try { _logger.LogDebug($"Attempting to check out {commit} in {repoDir}"); LibGit2SharpHelpers.SafeCheckout(localRepo, commit, checkoutOptions, _logger); if (force) { CleanRepoAndSubmodules(localRepo, _logger); } } catch (LibGit2Sharp.NotFoundException) { _logger.LogWarning($"Couldn't find commit {commit} in {repoDir} locally. Attempting fetch."); try { foreach (LibGit2Sharp.Remote r in localRepo.Network.Remotes) { IEnumerable <string> refSpecs = r.FetchRefSpecs.Select(x => x.Specification); _logger.LogDebug($"Fetching {string.Join(";", refSpecs)} from {r.Url} in {repoDir}"); try { LibGit2Sharp.Commands.Fetch(localRepo, r.Name, refSpecs, new LibGit2Sharp.FetchOptions(), $"Fetching from {r.Url}"); } catch { _logger.LogWarning($"Fetching failed, are you offline or missing a remote?"); } } _logger.LogDebug($"After fetch, attempting to checkout {commit} in {repoDir}"); LibGit2SharpHelpers.SafeCheckout(localRepo, commit, checkoutOptions, _logger); if (force) { CleanRepoAndSubmodules(localRepo, _logger); } } catch // Most likely network exception, could also be no remotes. We can't do anything about any error here. { _logger.LogError($"After fetch, still couldn't find commit or treeish {commit} in {repoDir}. Are you offline or missing a remote?"); throw; } } } } catch (Exception exc) { throw new Exception($"Something went wrong when checking out {commit} in {repoDir}", exc); } } }