/// <summary>
        ///     Updates local copies of the files.
        /// </summary>
        /// <param name="filesToCommit">Files to update locally</param>
        /// <param name="repoUri">Base path of the repo</param>
        /// <param name="branch">Unused</param>
        /// <param name="commitMessage">Unused</param>
        /// <returns></returns>
        public async Task CommitFilesAsync(List <GitFile> filesToCommit, string repoUri, string branch, string commitMessage)
        {
            string repoDir = LocalHelpers.GetRootDir(_gitExecutable, _logger);

            try
            {
                using (LibGit2Sharp.Repository localRepo = new LibGit2Sharp.Repository(repoDir))
                {
                    foreach (GitFile file in filesToCommit)
                    {
                        switch (file.Operation)
                        {
                        case GitFileOperation.Add:
                            string parentDirectory = Directory.GetParent(file.FilePath).FullName;

                            if (!Directory.Exists(parentDirectory))
                            {
                                Directory.CreateDirectory(parentDirectory);
                            }

                            string fullPath = Path.Combine(repoUri, file.FilePath);
                            using (var streamWriter = new StreamWriter(fullPath))
                            {
                                string finalContent;
                                switch (file.ContentEncoding)
                                {
                                case ContentEncoding.Utf8:
                                    finalContent = file.Content;
                                    break;

                                case ContentEncoding.Base64:
                                    byte[] bytes = Convert.FromBase64String(file.Content);
                                    finalContent = Encoding.UTF8.GetString(bytes);
                                    break;

                                default:
                                    throw new DarcException($"Unknown file content encoding {file.ContentEncoding}");
                                }
                                finalContent = NormalizeLineEndings(fullPath, finalContent);
                                await streamWriter.WriteAsync(finalContent);

                                LibGit2SharpHelpers.AddFileToIndex(localRepo, file, fullPath, _logger);
                            }
                            break;

                        case GitFileOperation.Delete:
                            if (File.Exists(file.FilePath))
                            {
                                File.Delete(file.FilePath);
                            }
                            break;
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                throw new DarcException($"Something went wrong when checking out {repoUri} in {repoDir}", exc);
            }
        }
        /// <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);
                }
            }
        }