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))); } }
public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options) => repository.CherryPick(commit, committer, options);
public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options) { return(repositoryInstance.CherryPick(commit, committer, options)); }
public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options = null) { throw new NotImplementedException(); }
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); }
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); }