public void CanSpecifyConflictFileStrategy(CheckoutFileConflictStrategy conflictStrategy)
        {
            const string conflictFile       = "a.txt";
            const string conflictBranchName = "conflicts";

            string path = SandboxMergeTestRepo();

            using (var repo = new Repository(path))
            {
                Branch branch = repo.Branches[conflictBranchName];
                Assert.NotNull(branch);

                CherryPickOptions cherryPickOptions = new CherryPickOptions()
                {
                    FileConflictStrategy = conflictStrategy,
                };

                CherryPickResult result = repo.CherryPick(branch.Tip, Constants.Signature, cherryPickOptions);
                Assert.Equal(CherryPickStatus.Conflicts, result.Status);

                // Get the information on the conflict.
                Conflict conflict = repo.Index.Conflicts[conflictFile];

                Assert.NotNull(conflict);
                Assert.NotNull(conflict.Theirs);
                Assert.NotNull(conflict.Ours);

                // Get the blob containing the expected content.
                Blob expectedBlob = null;
                switch (conflictStrategy)
                {
                case CheckoutFileConflictStrategy.Theirs:
                    expectedBlob = repo.Lookup <Blob>(conflict.Theirs.Id);
                    break;

                case CheckoutFileConflictStrategy.Ours:
                    expectedBlob = repo.Lookup <Blob>(conflict.Ours.Id);
                    break;

                default:
                    throw new Exception("Unexpected FileConflictStrategy");
                }

                Assert.NotNull(expectedBlob);

                // Check the content of the file on disk matches what is expected.
                string expectedContent = expectedBlob.GetContentText(new FilteringOptions(conflictFile));
                Assert.Equal(expectedContent, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, conflictFile)));
            }
        }
        public void CanSpecifyConflictFileStrategy(CheckoutFileConflictStrategy conflictStrategy)
        {
            const string conflictFile = "a.txt";
            const string conflictBranchName = "conflicts";

            string path = SandboxMergeTestRepo();
            using (var repo = new Repository(path))
            {
                Branch branch = repo.Branches[conflictBranchName];
                Assert.NotNull(branch);

                CherryPickOptions cherryPickOptions = new CherryPickOptions()
                {
                    FileConflictStrategy = conflictStrategy,
                };

                CherryPickResult result = repo.CherryPick(branch.Tip, Constants.Signature, cherryPickOptions);
                Assert.Equal(CherryPickStatus.Conflicts, result.Status);

                // Get the information on the conflict.
                Conflict conflict = repo.Index.Conflicts[conflictFile];

                Assert.NotNull(conflict);
                Assert.NotNull(conflict.Theirs);
                Assert.NotNull(conflict.Ours);

                // Get the blob containing the expected content.
                Blob expectedBlob = null;
                switch (conflictStrategy)
                {
                    case CheckoutFileConflictStrategy.Theirs:
                        expectedBlob = repo.Lookup<Blob>(conflict.Theirs.Id);
                        break;
                    case CheckoutFileConflictStrategy.Ours:
                        expectedBlob = repo.Lookup<Blob>(conflict.Ours.Id);
                        break;
                    default:
                        throw new Exception("Unexpected FileConflictStrategy");
                }

                Assert.NotNull(expectedBlob);

                // Check the content of the file on disk matches what is expected.
                string expectedContent = expectedBlob.GetContentText(new FilteringOptions(conflictFile));
                Assert.Equal(expectedContent, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, conflictFile)));
            }
        }
        private ShallowCommit CherryPickCommit(IRepository repo, ShallowCommit original)
        {
            if (this.config.DryRun)
            {
                return(original);
            }

            var commit  = repo.Lookup(original.Sha) as Commit;
            var options = new CherryPickOptions();

            if (commit.Parents.Count() > 1)
            {
                options.Mainline = 1;
            }

            try
            {
                return(ShallowCommit.FromCommit(repo.CherryPick(commit, new Signature(commit.Author.Name, commit.Author.Email, commit.Author.When), options).Commit));
            }
            catch (EmptyCommitException)
            {
                return(ShallowCommit.FromCommit(this.CommitWorktree(repo, commit)));
            }
            catch (Exception e)
            {
                if (!config.Retry)
                {
                    throw;
                }

                this.logger.Log("An error occurred: \n" + e);
                this.logger.Log("Press any key after fixing conflicts manually.", true);
                Console.ReadKey();

                return(ShallowCommit.FromCommit(this.CommitWorktree(repo, commit)));
            }
        }
Exemple #4
0
 public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options) =>
 repository.CherryPick(commit, committer, options);
Exemple #5
0
 public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options)
 {
     return(repositoryInstance.CherryPick(commit, committer, options));
 }
Exemple #6
0
 public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options = null)
 {
     throw new NotImplementedException();
 }
Exemple #7
0
        public static bool Run(CompressimagesParameters parameters, ICollector <CompressImagesMessage> compressImagesMessages, ILogger logger)
        {
            CredentialsHandler credentialsProvider =
                (url, user, cred) =>
                new UsernamePasswordCredentials {
                Username = KnownGitHubs.Username, Password = parameters.Password
            };

            // clone
            var cloneOptions = new CloneOptions
            {
                CredentialsProvider = credentialsProvider,
            };

            Repository.Clone(parameters.CloneUrl, parameters.LocalPath, cloneOptions);

            var repo           = new Repository(parameters.LocalPath);
            var remote         = repo.Network.Remotes["origin"];
            var isWikiCompress = parameters.CloneUrl.EndsWith(".wiki.git");

            // check if we have the branch already or this is empty repo
            try
            {
                if (repo.Network.ListReferences(remote, credentialsProvider).Any() == false)
                {
                    logger.LogInformation("CompressImagesFunction: no references found for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
                    return(false);
                }

                if (!parameters.IsRebase && repo.Network.ListReferences(remote, credentialsProvider).Any(x => x.CanonicalName == $"refs/heads/{KnownGitHubs.BranchName}"))
                {
                    logger.LogInformation("CompressImagesFunction: branch already exists for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
                    return(false);
                }
            }
            catch (Exception e)
            {
                // log + ignore
                logger.LogWarning(e, "CompressImagesFunction: issue checking for existing branch or empty repo for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
            }

            // check if we should switch away from the default branch
            if (!isWikiCompress && parameters.Settings != null && !string.IsNullOrEmpty(parameters.Settings.DefaultBranchOverride))
            {
                logger.LogInformation(
                    "CompressImagesFunction: default branch override for {Owner}/{RepoName} is {DefaultBranchOverride}",
                    parameters.RepoOwner,
                    parameters.RepoName,
                    parameters.Settings.DefaultBranchOverride);

                var baseBranch = repo.Branches[$"refs/remotes/origin/{parameters.Settings.DefaultBranchOverride}"];
                if (baseBranch == null)
                {
                    logger.LogWarning(
                        "CompressImagesFunction: default branch ({DefaultBranchOverride}) not found for {Owner}/{RepoName}",
                        parameters.Settings.DefaultBranchOverride,
                        parameters.RepoOwner,
                        parameters.RepoName);
                    return(false);
                }

                Commands.Checkout(repo, baseBranch);
            }

            var repoConfiguration = new RepoConfiguration();

            try
            {
                // see if .imgbotconfig exists in repo root
                var repoConfigJson = File.ReadAllText(parameters.LocalPath + Path.DirectorySeparatorChar + ".imgbotconfig");
                if (!string.IsNullOrEmpty(repoConfigJson))
                {
                    repoConfiguration = JsonConvert.DeserializeObject <RepoConfiguration>(repoConfigJson);
                }
            }
            catch
            {
                // ignore
            }

            // Add new compressMessage if we should compress Wiki
            if (repoConfiguration.CompressWiki && isWikiCompress == false)
            {
                logger.LogInformation("CompressImagesFunction: Adding Wiki image compression to queue for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
                compressImagesMessages.Add(new CompressImagesMessage()
                {
                    InstallationId = parameters.CompressImagesMessage.InstallationId,
                    RepoName       = parameters.CompressImagesMessage.RepoName,
                    Owner          = parameters.RepoOwner,
                    CloneUrl       = $"https://github.com/{parameters.RepoOwner}/{parameters.RepoName}.wiki.git"
                });
            }

            if (Schedule.ShouldOptimizeImages(repoConfiguration, repo) == false)
            {
                logger.LogInformation("CompressImagesFunction: skipping optimization due to schedule for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
                return(false);
            }

            // Should not create branch if we are compressing Wiki or performing rebase
            if (isWikiCompress == false && !parameters.IsRebase)
            {
                // check out the branch
                repo.CreateBranch(KnownGitHubs.BranchName);
                var branch = Commands.Checkout(repo, KnownGitHubs.BranchName);
            }

            // reset any mean files
            repo.Reset(ResetMode.Mixed, repo.Head.Tip);

            // optimize images
            string[]      imagePaths;
            List <string> addedOrModifiedImagePaths = new List <string>();
            List <string> deletedImagePaths         = new List <string>();

            if (parameters.IsRebase)
            {
                var refspec = string.Format("{0}:{0}", KnownGitHubs.BranchName);
                Commands.Fetch(repo, "origin", new List <string> {
                    refspec
                }, null, "fetch");

                var diff = repo.Diff.Compare <TreeChanges>(repo.Branches[KnownGitHubs.BranchName].Commits.ElementAt(1).Tree, repo.Head.Tip.Tree);

                if (diff == null)
                {
                    logger.LogInformation("Something went wrong while doing rebase");
                    return(false);
                }

                foreach (TreeEntryChanges c in diff)
                {
                    if (KnownImgPatterns.ImgExtensions.Contains(Path.GetExtension(c.Path)))
                    {
                        var path    = parameters.LocalPath + "/" + c.Path;
                        var oldpath = parameters.LocalPath + "/" + c.OldPath;

                        switch (c.Status)
                        {
                        case ChangeKind.Added:
                        case ChangeKind.Modified:
                            addedOrModifiedImagePaths.Add(path.Replace("\\", "/"));
                            break;

                        case ChangeKind.Renamed:
                            addedOrModifiedImagePaths.Add(path.Replace("\\", "/"));
                            deletedImagePaths.Add(oldpath.Replace("\\", "/"));
                            break;

                        case ChangeKind.Deleted:
                            deletedImagePaths.Add(path.Replace("\\", "/"));
                            break;
                        }
                    }
                }

                imagePaths = ImageQuery.FilterOutIgnoredFiles(addedOrModifiedImagePaths, repoConfiguration);
            }
            else
            {
                imagePaths = ImageQuery.FindImages(parameters.LocalPath, repoConfiguration);
            }

            var optimizedImages = OptimizeImages(repo, parameters.LocalPath, imagePaths, logger, repoConfiguration.AggressiveCompression);

            if (optimizedImages.Length == 0)
            {
                return(false);
            }

            // create commit message based on optimizations
            foreach (var image in optimizedImages)
            {
                Commands.Stage(repo, image.OriginalPath);
            }

            var commitMessage = CommitMessage.Create(optimizedImages);
            var signature     = new Signature(KnownGitHubs.ImgBotLogin, KnownGitHubs.ImgBotEmail, DateTimeOffset.Now);

            repo.Commit(commitMessage, signature, signature);

            if (parameters.IsRebase)
            {
                var baseBranch = repo.Head;
                var newCommit  = baseBranch.Tip;
                var oldCommit  = repo.Branches[KnownGitHubs.BranchName].Tip;

                // we need to reset the default branch so that we can
                // rebase to it later.
                repo.Reset(ResetMode.Hard, repo.Head.Commits.ElementAt(1));

                // checkout to imgbot branch. TODO: remove because this is needed earlier on diff
                Commands.Checkout(repo, KnownGitHubs.BranchName);

                // cherry-pick
                var cherryPickOptions = new CherryPickOptions()
                {
                    MergeFileFavor = MergeFileFavor.Theirs,
                };
                var cherryPickResult = repo.CherryPick(newCommit, signature, cherryPickOptions);

                if (cherryPickResult.Status == CherryPickStatus.Conflicts)
                {
                    var status = repo.RetrieveStatus(new LibGit2Sharp.StatusOptions()
                    {
                    });
                    foreach (var item in status)
                    {
                        if (item.State == FileStatus.Conflicted)
                        {
                            Commands.Stage(repo, item.FilePath);
                        }
                    }

                    repo.Commit(commitMessage, signature, signature);
                }

                // New commit message creation
                var previousCommitResults = CompressionResult.ParseCommitMessage(oldCommit.Message);
                var mergedResults         = CompressionResult.Merge(optimizedImages, previousCommitResults);
                var filteredResults       = CompressionResult.Filter(mergedResults, deletedImagePaths.ToArray());
                var squashCommitMessage   = CommitMessage.Create(filteredResults);

                // squash
                var baseCommit = repo.Head.Commits.ElementAt(2);
                repo.Reset(ResetMode.Soft, baseCommit);
                repo.Commit(squashCommitMessage, signature, signature);

                // rebase
                var rebaseOptions = new RebaseOptions()
                {
                    FileConflictStrategy = CheckoutFileConflictStrategy.Theirs,
                };

                var rebaseResult = repo.Rebase.Start(repo.Head, baseBranch, null, new Identity(KnownGitHubs.ImgBotLogin, KnownGitHubs.ImgBotEmail), rebaseOptions);

                while (rebaseResult.Status == RebaseStatus.Conflicts)
                {
                    var status = repo.RetrieveStatus(new LibGit2Sharp.StatusOptions()
                    {
                    });
                    foreach (var item in status)
                    {
                        if (item.State == FileStatus.Conflicted)
                        {
                            if (imagePaths.Contains(parameters.LocalPath + "/" + item.FilePath))
                            {
                                Commands.Stage(repo, item.FilePath);
                            }
                            else
                            {
                                Commands.Remove(repo, item.FilePath);
                            }
                        }
                    }

                    rebaseResult = repo.Rebase.Continue(new Identity(KnownGitHubs.ImgBotLogin, KnownGitHubs.ImgBotEmail), rebaseOptions);
                }
            }

            // We just made a normal commit, now we are going to capture all the values generated from that commit
            // then rewind and make a signed commit
            var commitBuffer = Commit.CreateBuffer(
                repo.Head.Tip.Author,
                repo.Head.Tip.Committer,
                repo.Head.Tip.Message,
                repo.Head.Tip.Tree,
                repo.Head.Tip.Parents,
                true,
                null);

            var signedCommitData = CommitSignature.Sign(commitBuffer + "\n", parameters.PgpPrivateKey, parameters.PgPPassword);

            repo.Reset(ResetMode.Soft, repo.Head.Commits.Skip(1).First().Sha);
            var commitToKeep = repo.ObjectDatabase.CreateCommitWithSignature(commitBuffer, signedCommitData);

            repo.Refs.UpdateTarget(repo.Refs.Head, commitToKeep);

            // Should use "master" if we are compressing Wiki
            if (isWikiCompress)
            {
                var branchAgain = Commands.Checkout(repo, "master");
            }
            else
            {
                var branchAgain = Commands.Checkout(repo, KnownGitHubs.BranchName);
            }

            repo.Reset(ResetMode.Hard, commitToKeep.Sha);

            // verify images are not corrupted by reading from git
            // see https://github.com/dabutvin/ImgBot/issues/273
            try
            {
                foreach (var image in optimizedImages)
                {
                    if (image.OriginalPath.EndsWith(".svg"))
                    {
                        // do not use ImageMagick to verify SVGs
                        continue;
                    }

                    new MagickImage(image.OriginalPath).Dispose();
                }
            }
            catch (MagickErrorException)
            {
                logger.LogError("Corrupt images after reset!");
                return(false);
            }

            // push to GitHub
            if (isWikiCompress)
            {
                repo.Network.Push(remote, "refs/heads/master", new PushOptions
                {
                    CredentialsProvider = credentialsProvider,
                });
            }
            else
            {
                var refs = $"refs/heads/{KnownGitHubs.BranchName}";
                if (parameters.IsRebase)
                {
                    refs = refs.Insert(0, "+");
                }
                logger.LogInformation("refs: {refs}", refs);

                repo.Network.Push(remote, refs, new PushOptions
                {
                    CredentialsProvider = credentialsProvider,
                });
            }

            return(true);
        }
Exemple #8
0
        public static bool Run(CompressimagesParameters parameters, ICollector <CompressImagesMessage> compressImagesMessages, ILogger logger)
        {
            var storageAccount = CloudStorageAccount.Parse(Common.KnownEnvironmentVariables.AzureWebJobsStorage);
            var paidPlan       = PaidPlan(storageAccount, parameters.RepoOwner);
            CredentialsHandler credentialsProvider =
                (url, user, cred) =>
                new UsernamePasswordCredentials {
                Username = KnownGitHubs.Username, Password = parameters.Password
            };

            // clone
            var cloneOptions = new CloneOptions
            {
                CredentialsProvider = credentialsProvider,
            };

            Repository.Clone(parameters.CloneUrl, parameters.LocalPath, cloneOptions);

            var repo           = new Repository(parameters.LocalPath);
            var remote         = repo.Network.Remotes["origin"];
            var isWikiCompress = parameters.CloneUrl.EndsWith(".wiki.git");

            // check if we have the branch already or this is empty repo
            try
            {
                if (repo.Network.ListReferences(remote, credentialsProvider).Any() == false)
                {
                    logger.LogInformation("CompressImagesFunction: no references found for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
                    return(false);
                }
            }
            catch (Exception e)
            {
                // log + ignore
                logger.LogWarning(e, "CompressImagesFunction: issue checking for existing branch or empty repo for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
            }

            // check if the branch exists and has been modified by the user
            if (parameters.IsRebase && repo.Branches[$"refs/remotes/origin/{KnownGitHubs.BranchName}"].Tip.Author.Name != KnownGitHubs.ImgBotLogin)
            {
                logger.LogInformation("CompressImagesFunction: imgbot branch has been modified by the user.");
                return(false);
            }

            // check if we should switch away from the default branch
            if (!isWikiCompress && parameters.Settings != null && !string.IsNullOrEmpty(parameters.Settings.DefaultBranchOverride))
            {
                logger.LogInformation(
                    "CompressImagesFunction: default branch override for {Owner}/{RepoName} is {DefaultBranchOverride}",
                    parameters.RepoOwner,
                    parameters.RepoName,
                    parameters.Settings.DefaultBranchOverride);

                var baseBranch = repo.Branches[$"refs/remotes/origin/{parameters.Settings.DefaultBranchOverride}"];
                if (baseBranch == null)
                {
                    logger.LogWarning(
                        "CompressImagesFunction: default branch ({DefaultBranchOverride}) not found for {Owner}/{RepoName}",
                        parameters.Settings.DefaultBranchOverride,
                        parameters.RepoOwner,
                        parameters.RepoName);
                    return(false);
                }

                Commands.Checkout(repo, baseBranch);
            }

            var repoConfiguration = new RepoConfiguration();

            try
            {
                // see if .imgbotconfig exists in repo root
                var repoConfigJson = File.ReadAllText(parameters.LocalPath + Path.DirectorySeparatorChar + ".imgbotconfig");
                if (!string.IsNullOrEmpty(repoConfigJson))
                {
                    repoConfiguration = JsonConvert.DeserializeObject <RepoConfiguration>(repoConfigJson);

                    // for now we are not adding the labels functionality || repoConfiguration.Labels.Any() TODO: add it when adding the labels feature
                    if (paidPlan && (repoConfiguration.PrBody != null || repoConfiguration.PrTitle != null))
                    {
                        var settingsTable = storageAccount.CreateCloudTableClient().GetTableReference("settings");

                        // Labels = repoConfiguration.Labels TODO: add it when adding the labels feature
                        var settings = new Common.TableModels.Settings(
                            parameters.CompressImagesMessage.InstallationId.ToString(),
                            parameters.CompressImagesMessage.RepoName)
                        {
                            PrBody  = repoConfiguration.PrBody,
                            PrTitle = repoConfiguration.PrTitle,
                        };

                        settingsTable.ExecuteAsync(TableOperation.InsertOrReplace(settings)).Wait();
                    }
                }
            }
            catch
            {
                // ignore
            }

            // Add new compressMessage if we should compress Wiki
            if (repoConfiguration.CompressWiki && isWikiCompress == false)
            {
                logger.LogInformation("CompressImagesFunction: Adding Wiki image compression to queue for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
                compressImagesMessages.Add(new CompressImagesMessage()
                {
                    InstallationId = parameters.CompressImagesMessage.InstallationId,
                    RepoName       = parameters.CompressImagesMessage.RepoName,
                    Owner          = parameters.RepoOwner,
                    CloneUrl       = $"https://github.com/{parameters.RepoOwner}/{parameters.RepoName}.wiki.git"
                });
            }

            if (Schedule.ShouldOptimizeImages(repoConfiguration, repo) == false)
            {
                logger.LogInformation("CompressImagesFunction: skipping optimization due to schedule for {Owner}/{RepoName}", parameters.RepoOwner, parameters.RepoName);
                return(false);
            }

            // Should not create branch if we are compressing Wiki or performing rebase
            if (isWikiCompress == false && !parameters.IsRebase)
            {
                // check out the branch
                repo.CreateBranch(KnownGitHubs.BranchName);
                var branch = Commands.Checkout(repo, KnownGitHubs.BranchName);
            }
            else if (parameters.IsRebase)
            {
                // if rebasing, fetch the branch
                var refspec = string.Format("{0}:{0}", KnownGitHubs.BranchName);
                Commands.Fetch(repo, "origin", new List <string> {
                    refspec
                }, null, "fetch");
            }

            // reset any mean files
            repo.Reset(ResetMode.Mixed, repo.Head.Tip);

            // optimize images
            var imagePaths = ImageQuery.FindImages(parameters.LocalPath, repoConfiguration);

            var optimizedImages = OptimizeImages(repo, parameters.LocalPath, imagePaths, logger, repoConfiguration.AggressiveCompression);

            if (optimizedImages.Length == 0)
            {
                return(false);
            }

            if (!Threshold.MeetsThreshold(repoConfiguration, optimizedImages))
            {
                logger.LogInformation($"Did not meet threshold. {parameters.RepoOwner}/{parameters.RepoName}");
                return(false);
            }

            // create commit message based on optimizations
            foreach (var image in optimizedImages)
            {
                Commands.Stage(repo, image.OriginalPath);
            }

            var commitMessage = CommitMessage.Create(optimizedImages);
            var signature     = new Signature(KnownGitHubs.ImgBotLogin, KnownGitHubs.ImgBotEmail, DateTimeOffset.Now);

            repo.Commit(commitMessage, signature, signature);

            if (parameters.IsRebase)
            {
                var baseBranch = repo.Head;
                var newCommit  = baseBranch.Tip;

                // we need to reset the default branch so that we can
                // rebase to it later.
                repo.Reset(ResetMode.Hard, repo.Head.Commits.ElementAt(1));

                Commands.Checkout(repo, KnownGitHubs.BranchName);

                // reset imgbot branch removing old commit
                repo.Reset(ResetMode.Hard, repo.Head.Commits.ElementAt(1));

                // cherry-pick
                var cherryPickOptions = new CherryPickOptions()
                {
                    MergeFileFavor = MergeFileFavor.Theirs,
                };
                var cherryPickResult = repo.CherryPick(newCommit, signature, cherryPickOptions);

                if (cherryPickResult.Status == CherryPickStatus.Conflicts)
                {
                    var status = repo.RetrieveStatus(new LibGit2Sharp.StatusOptions()
                    {
                    });
                    foreach (var item in status)
                    {
                        if (item.State == FileStatus.Conflicted)
                        {
                            Commands.Stage(repo, item.FilePath);
                        }
                    }

                    repo.Commit(commitMessage, signature, signature);
                }

                // rebase
                var rebaseOptions = new RebaseOptions()
                {
                    FileConflictStrategy = CheckoutFileConflictStrategy.Theirs,
                };

                var rebaseResult = repo.Rebase.Start(repo.Head, baseBranch, null, new Identity(KnownGitHubs.ImgBotLogin, KnownGitHubs.ImgBotEmail), rebaseOptions);

                while (rebaseResult.Status == RebaseStatus.Conflicts)
                {
                    var status = repo.RetrieveStatus(new LibGit2Sharp.StatusOptions()
                    {
                    });
                    foreach (var item in status)
                    {
                        if (item.State == FileStatus.Conflicted)
                        {
                            if (imagePaths.Contains(Path.Combine(parameters.LocalPath, item.FilePath)))
                            {
                                Commands.Stage(repo, item.FilePath);
                            }
                            else
                            {
                                Commands.Remove(repo, item.FilePath);
                            }
                        }
                    }

                    rebaseResult = repo.Rebase.Continue(new Identity(KnownGitHubs.ImgBotLogin, KnownGitHubs.ImgBotEmail), rebaseOptions);
                }
            }

            // We just made a normal commit, now we are going to capture all the values generated from that commit
            // then rewind and make a signed commit
            var commitBuffer = Commit.CreateBuffer(
                repo.Head.Tip.Author,
                repo.Head.Tip.Committer,
                repo.Head.Tip.Message,
                repo.Head.Tip.Tree,
                repo.Head.Tip.Parents,
                true,
                null);

            var signedCommitData = CommitSignature.Sign(commitBuffer + "\n", parameters.PgpPrivateKey, parameters.PgPPassword);

            repo.Reset(ResetMode.Soft, repo.Head.Commits.Skip(1).First().Sha);
            var commitToKeep = repo.ObjectDatabase.CreateCommitWithSignature(commitBuffer, signedCommitData);

            repo.Refs.UpdateTarget(repo.Refs.Head, commitToKeep);

            // Should use "master" if we are compressing Wiki
            if (isWikiCompress)
            {
                var branchAgain = Commands.Checkout(repo, "master");
            }
            else
            {
                var branchAgain = Commands.Checkout(repo, KnownGitHubs.BranchName);
            }

            repo.Reset(ResetMode.Hard, commitToKeep.Sha);

            // verify images are not corrupted by reading from git
            // see https://github.com/dabutvin/ImgBot/issues/273
            try
            {
                foreach (var image in optimizedImages)
                {
                    if (image.OriginalPath.EndsWith(".svg"))
                    {
                        // do not use ImageMagick to verify SVGs
                        continue;
                    }

                    new MagickImage(image.OriginalPath).Dispose();
                }
            }
            catch (MagickErrorException)
            {
                logger.LogError("Corrupt images after reset!");
                return(false);
            }

            // push to GitHub
            if (isWikiCompress)
            {
                repo.Network.Push(remote, "refs/heads/master", new PushOptions
                {
                    CredentialsProvider = credentialsProvider,
                });
            }
            else
            {
                var refs = $"refs/heads/{KnownGitHubs.BranchName}";
                if (parameters.IsRebase)
                {
                    refs = refs.Insert(0, "+");
                }
                logger.LogInformation("refs: {refs}", refs);

                repo.Network.Push(remote, refs, new PushOptions
                {
                    CredentialsProvider = credentialsProvider,
                });
            }

            try
            {
                Directory.Delete(parameters.LocalPath, true);
            }
            catch (Exception e)
            {
                logger.LogError(e, $"Delete issue with repository {parameters.LocalPath}");
            }

            return(true);
        }
        static int Main(string[] args)
        {
            // Get configration settings from the appsettings.json file.
            ConfigureApplication();

            // Validate and cache arguments.
            if (!CheckArguments(args))
            {
                Console.WriteLine(Usage);
                return(ErrorCode);
            }

            // Create a Repository instance on the local path to the git repo.
            using (var repo = new Repository(RepoLocalPath))
            {
                // Fetch the state of the remote repo, which is typically named "upstream".
                PrintMessage($"Fetching state of {RemoteRepoName}");
                var    remote     = repo.Network.Remotes[RemoteRepoName];
                var    refSpecs   = remote.FetchRefSpecs.Select(x => x.Specification);
                string logMessage = "";
                Commands.Fetch(repo, remote.Name, refSpecs, null, logMessage);

                // Get the branch to cherry-pick from.
                var sourceBranch = repo.Branches[SourceBranchName];
                if (sourceBranch == null)
                {
                    // Repository returns a null object when the requested branch doesn't exist.
                    PrintMessage($"Source branch {SourceBranchName} not found in {RemoteRepoName}, exiting.");
                    return(ErrorCode);
                }
                else
                {
                    PrintMessage($"Found branch {sourceBranch.FriendlyName} in {RemoteRepoName}");
                }

                // Find the commit in the git log of the source branch.
                var sourceCommit = sourceBranch.Commits.FirstOrDefault(c => c.Sha == CommitSha);
                if (sourceCommit == null)
                {
                    PrintMessage($"Commit {CommitSha} not found in {sourceBranch.FriendlyName}, no action taken, exiting.");
                    return(ErrorCode);
                }

                // Get the branches to merge to, which is a list of the available
                // branches minus the source branch for the commit.
                var branchesToMerge = AvailableBranches.Where(b => b != SourceBranchName);

                // Assign cherry-pick options.
                CherryPickOptions options = CreateCherryPickOptions();

                // Set up the signature for the cherry-pick message.
                Signature sig = new Signature(SigName, SigEmail, DateTime.Now);

                // Create local branches that track the available remote branches.
                // In each local branch, do the cherry-pick and push to the remote repo.
                PrintMessage($"Cherry-picking from {sourceBranch.FriendlyName} to available branches.");
                foreach (var trackedBranchName in branchesToMerge)
                {
                    // Create and check out the local branch, which is equivalent to the command:
                    //
                    // git checkout -b <local-branch-name> -t <remote-repo-name>/<tracked-branch-name>
                    //
                    // For example, the following command creates a local branch
                    // named "docs-2358" which tracks the remote branch named "0.8.1-ksqldb"
                    // in the remote repo named "upstream":
                    //
                    // git checkout -b docs-2358 -t upstream/0.8.1-ksqldb

                    // Name the local branch by appending the remote branch name to the provided base name.
                    // For example, if the base name is "docs-2358" and the tracked branch is named
                    // "0.8.1-ksqldb", the local branch name is "docs-2358-0.8.1-ksqldb".
                    string localBranchName = $"{LocalBranchBaseName}-{trackedBranchName}";

                    // For future reference, this is the "objectish" representation:
                    // string objectish = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", localBranch.CanonicalName, localBranch.UpstreamBranchCanonicalName);

                    // Get a reference on the remote tracking branch.
                    Branch trackedBranch = repo.Branches[$"{RemoteRepoName}/{trackedBranchName}"];

                    // If a local branch with the same name exists, probably from a
                    // previous MyPintMerge session, delete it.
                    if (repo.Branches.Any(b => b.FriendlyName == localBranchName))
                    {
                        DeleteBranch(repo, repo.Branches[localBranchName]);
                    }

                    // Create and check out the local branch.
                    PrintMessage($"Checking out local branch {localBranchName} tracking remote branch {trackedBranch.FriendlyName}");
                    Branch localBranch   = repo.CreateBranch(localBranchName, trackedBranch.Tip);
                    Branch updatedBranch = repo.Branches.Update(
                        localBranch,
                        b => b.TrackedBranch = trackedBranch.CanonicalName);
                    CheckoutOptions checkoutOptions = CreateCheckoutOptions();
                    checkoutCounter = 0;
                    Commands.Checkout(repo, localBranch, checkoutOptions);

                    // Cherry-pick to the currently checked out branch.
                    PrintMessage($"Cherry-picking commit {sourceCommit.Sha} to local branch {updatedBranch.FriendlyName}");
                    try
                    {
                        var pickResult = repo.CherryPick(sourceCommit, sig, options);

                        // Check the return value from the CherryPick method,
                        // which can fail without throwing or sending a notification
                        // to the callbacks.
                        if (pickResult.Status == CherryPickStatus.Conflicts)
                        {
                            // If there are merge conflcts, exit.
                            PrintMessage($"CONFLICT in local branch {updatedBranch.FriendlyName}, exiting.");
                            return(ErrorCode);
                        }
                    }
                    catch (EmptyCommitException ecex)
                    {
                        // Oddly, when there's nothing to do, i.e., when the commit
                        // exists already in the tracked branch, libgit2sharp
                        // throws an EmptyCommitException instead of reutrning
                        // a CherryPickResult.
                        PrintMessage($"No changes detected, no action taken in local branch {updatedBranch.FriendlyName}, continuing.");

                        if (DeleteLocalBranches)
                        {
                            DeleteBranch(repo, localBranch);
                        }

                        continue;
                    }
                    catch (Exception ex)
                    {
                        PrintMessage($"Exception during cherry-pick {ex}, no action taken in local branch {updatedBranch.FriendlyName}, continuing.");

                        //if (DeleteLocalBranches)
                        //{
                        //    DeleteBranch(repo, localBranch);
                        //}

                        continue;
                    }

                    // Prepare to push the changes to the tracked remote branch.
                    // Assign the configuration options for the push.
                    PushOptions pushOptions = CreatePushOptions();

                    // Push the branch to the remote repo.
                    PrintMessage($"Pushing local branch {localBranchName} to remote {trackedBranch.FriendlyName}");
                    repo.Network.Push(localBranch, pushOptions);

                    // Optionally, clean up by deleting the local branch.
                    if (DeleteLocalBranches)
                    {
                        DeleteBranch(repo, localBranch);
                    }
                }
            }

            return(SuccessCode);
        }