/// <summary>
        /// Creates a new commit.
        /// </summary>
        private async Task <Octokit.Commit> CreateCommitAsync(
            GitHubRepository repository,
            string commitMessage,
            IList <IArchiveFile> fileContents,
            Octokit.Commit parentCommit)
        {
            var newTreeResponse = await RetryGitHubOperationIfNeededAsync
                                  (
                async() => await _client.Git.Tree.Create
                (
                    repository.Owner,
                    repository.Name,
                    await GetTreeToPushAsync
                    (
                        repository,
                        fileContents,
                        parentCommit?.Tree?.Sha
                    )
                )
                                  );

            return(await RetryGitHubOperationIfNeededAsync
                   (
                       () => _client.Git.Commit.Create
                       (
                           repository.Owner,
                           repository.Name,
                           parentCommit != null
                                                ? new NewCommit(commitMessage, newTreeResponse.Sha, parentCommit.Sha)
                                                : new NewCommit(commitMessage, newTreeResponse.Sha)
                       )
                   ));
        }
        /// <summary>
        /// Adds a push webhook for the repository.
        /// </summary>
        public Task EnsurePushWebhookAsync(GitHubRepository repository, string url)
        {
            return(RetryGitHubOperationIfNeededAsync
                   (
                       async() =>
            {
                var webhooks = await _client.Repository.Hooks.GetAll
                               (
                    repository.Owner,
                    repository.Name
                               );

                if (!webhooks.Any(webhook => webhook.Config["url"] == url))
                {
                    await RetryGitHubOperationIfNeededAsync
                    (
                        () => _client.Repository.Hooks.Create
                        (
                            repository.Owner,
                            repository.Name,
                            new NewRepositoryHook
                            (
                                "web",
                                new Dictionary <string, string>()
                    {
                        { "url", url },
                        { "content_type", "json" },
                        { "secret", _webhookSecret.Value },
                        { "insecure_ssl", "0" }
                    }
                            )
                    {
                        Active = true,
                        Events = new[] { "push" }
                    }
                        )
                    );

                    webhooks = await _client.Repository.Hooks.GetAll
                               (
                        repository.Owner,
                        repository.Name
                               );

                    if (!webhooks.Any(webhook => webhook.Config["url"] == url))
                    {
                        throw new ApiException
                        (
                            $"Webhook not found for repository {repository.Name}",
                            HttpStatusCode.NotFound
                        );
                    }
                }

                return true;
            }
                   ));
        }
        /// <summary>
        /// Overwrites an existing repository with an existing archive.
        /// All existing commits will be erased, and the repository will
        /// be populated with two commits.
        /// </summary>
        public async Task OverwriteRepositoryAsync(
            GitHubRepository repository,
            string commitMessage,
            IArchive contents,
            Func <IArchiveFile, bool> includeFile,
            Func <IArchiveFile, bool> includeInFirstCommit)
        {
            var firstBatch = contents.Files
                             .Where(file => includeFile(file) && includeInFirstCommit(file))
                             .ToList();

            Commit firstCommit = null;

            if (firstBatch.Count > 0)
            {
                firstCommit = await RetryGitHubOperationIfNeededAsync
                              (
                    () => CreateCommitAsync
                    (
                        repository,
                        $"{commitMessage}: Part 1",
                        firstBatch,
                        parentCommit : null
                    )
                              );
            }

            var secondBatch = contents.Files
                              .Where(file => includeFile(file) && !includeInFirstCommit(file))
                              .ToList();

            var starterCommit = await RetryGitHubOperationIfNeededAsync
                                (
                () => CreateCommitAsync
                (
                    repository,
                    firstCommit != null
                                                ? $"{commitMessage}: Part 2"
                                                : commitMessage,
                    secondBatch,
                    firstCommit
                )
                                );

            await RetryGitHubOperationIfNeededAsync
            (
                () => _client.Git.Reference.Update
                (
                    repository.Owner,
                    repository.Name,
                    "heads/master",
                    new ReferenceUpdate(starterCommit.Sha, force: true)
                )
            );
        }
		/// <summary>
		/// Returns a list of push events for the given project.
		/// </summary>
		private async Task<StudentRepoPushEvents> GetAllPushEventsAsync(
			ClassroomMembership student,
			GitHubRepository repository)
		{
			var pushEvents = await _repoClient.GetPushEventsAsync
			(
				repository.Owner, 
				repository.Name
			);

			return new StudentRepoPushEvents(student, pushEvents);
		}
        /// <summary>
        /// Creates a new project tree.
        /// </summary>
        private async Task <NewTree> GetTreeToPushAsync(
            GitHubRepository repository,
            IList <IArchiveFile> fileContents,
            string baseTreeSha)
        {
            NewTree newTree = new NewTree()
            {
                BaseTree = baseTreeSha
            };

            foreach (var entry in fileContents)
            {
                var newTreeItem = new NewTreeItem()
                {
                    Path = entry.FullPath,
                    Mode = "100644" /*blob*/,
                    Type = TreeType.Blob
                };

                if (entry.Ascii)
                {
                    newTreeItem.Content = entry.GetEncodedData();
                }
                else
                {
                    var blobRef = await RetryGitHubOperationIfNeededAsync
                                  (
                        () => _client.Git.Blob.Create
                        (
                            repository.Owner,
                            repository.Name,
                            new NewBlob()
                    {
                        Encoding = EncodingType.Base64,
                        Content  = entry.GetEncodedData()
                    }
                        )
                                  );

                    newTreeItem.Sha = blobRef.Sha;
                }

                newTree.Tree.Add(newTreeItem);
            }

            return(newTree);
        }
		/// <summary>
		/// Ensures that the webhook is present for the repository.
		/// </summary>
		private async Task<bool> EnsureWebHookPresentAsync(
			GitHubRepository repo,
			string webhookUrl)
		{
			try
			{
				await _repoClient.EnsurePushWebhookAsync(repo, webhookUrl);

				return true;
			}
			catch (Exception ex)
			{
				_logger.LogError(0, ex, "Exception ensuring that webhook is present for {orgName}/{repoName}",
					repo.Owner,
					repo.Name);

				return false;
			}
		}
		/// <summary>
		/// Sets up CreateRepositoryAsync.
		/// </summary>
		private static void SetupCreateRepositoryAsync(
			Mock<IGitHubRepositoryClient> repoClient)
		{
			var repo = new GitHubRepository(1, "GitHubUser", "Project1_LastNameFirstName");

			repoClient
				.Setup(GetCreateRepositoryExpression())
				.ReturnsAsync(repo);
		}
		/// <summary>
		/// Sets up GetAllRepositoriesAsync.
		/// </summary>
		private static void SetupGetAllRepositoriesAsync(
			Mock<IGitHubRepositoryClient> repoClient,
			bool existingStudentRepo)
		{
			var repo = new GitHubRepository(1, "GitHubUser", "Project1_LastNameFirstName");

			repoClient
				.Setup(rc => rc.GetAllRepositoriesAsync("Class1GitHubOrg"))
				.ReturnsAsync
				(
					existingStudentRepo
						? Collections.CreateList(repo)
						: new List<GitHubRepository>()
				);
		}