Ejemplo n.º 1
0
        /// <summary>
        /// Validates the command-line arguments.
        /// </summary>
        /// <param name="args">The command-line arguments to validate.</param>
        /// <returns>true if arguments are valid; otherwise, false.</returns>
        /// <remarks><see cref="MyPintMerge"/> expects exactly the number of
        /// command-line arguments specified in <see cref="NumRequiredArgs"/>.
        /// </remarks>
        private static bool CheckArguments(string[] args)
        {
            bool argsAreValid = false;

            if (args != null)
            {
                if (args.Length == NumRequiredArgs)
                {
                    var missingArgs = args.Where(a => string.IsNullOrEmpty(a));
                    if (missingArgs.Count() == 0)
                    {
                        string localBranchArg  = args[LocalBranchNameArgIndex];
                        string sourceBranchArg = args[SourceBranchNameArgIndex];
                        string commmitShaArg   = args[CommitShaArgIndex];
                        string login           = args[LoginArgIndex];
                        string password        = args[PasswordArgIndex];

                        if (AvailableBranches.Contains(sourceBranchArg))
                        {
                            if (commmitShaArg.Length == CommitShaLength)
                            {
                                LocalBranchBaseName = localBranchArg;
                                SourceBranchName    = sourceBranchArg;
                                CommitSha           = commmitShaArg;
                                Login        = login;
                                Token        = password;
                                argsAreValid = true;
                            }
                        }
                    }
                }
            }

            return(argsAreValid);
        }
Ejemplo n.º 2
0
        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);
        }