public async Task <bool> NeedsCreate(string latestBranchName, ImmutableList <NeededMerge> upstreamBranches)
        {
            if (await normalStrategy.NeedsCreate(latestBranchName, upstreamBranches) ||
                (await normalStrategy.FindNeededMerges(latestBranchName, upstreamBranches)).Count > 0)
            {
                return(true);
            }


            // It's okay if there are still other "upstreamBranches" that didn't get merged, because we already did that check
            var neededRefs = (await(from branchName in upstreamBranches.ToObservable()
                                    from branchRef in cli.ShowRef(branchName.BranchName).FirstOutputMessage()
                                    select branchRef).ToArray()).ToImmutableHashSet();

            var currentHead = await cli.ShowRef(latestBranchName).FirstOutputMessage();

            if (!neededRefs.Contains(currentHead))
            {
                // The latest commit of the branch isn't one that we needed. That means it's probably a merge commit!
                Func <string, IObservable <string[]> > getParents = (commitish) =>
                                                                    cli.GetCommitParents(commitish).FirstOutputMessage().Select(commit => commit.Split(' '));
                var parents = await getParents(cli.RemoteBranch(latestBranchName));

                while (parents.Length > 1)
                {
                    // Figure out what the other commits are, if any
                    var other = parents.Where(p => !neededRefs.Contains(p)).ToArray();
                    if (other.Length != 1)
                    {
                        break;
                    }
                    else
                    {
                        // If there's another commit, it might be a merge of two of our other requireds. Check it!
                        parents = await getParents(other[0]);
                    }
                }
                // If this isn't a merge, then we had a non-merge commit in there; we don't care about the parent.
                // If it is a merge, we either have 2 "others" or no "others", so looking at one parent will tell us:
                // If it's a required commit, we're good. If it's not, we need to recreate the branch.
                return(parents.Length < 2 || !neededRefs.Contains(parents[0]));
            }
            return(false);
        }
        private async Task <string> GetRefFor(string branchName, IGitCli cli)
        {
            await cli.Fetch(branchName).ActiveState;

            return((await cli.ShowRef(branchName).ActiveOutput.FirstOrDefaultAsync()).Message);
        }
Exemplo n.º 3
0
 public static IObservable <bool> HasOutstandingCommits(this IGitCli cli, string upstreamBranch, string downstreamBranch) =>
 Observable.CombineLatest(
     cli.MergeBase(upstreamBranch, downstreamBranch).FirstOutputMessage(),
     cli.ShowRef(upstreamBranch).FirstOutputMessage(),
     (mergeBaseResult, showRefResult) => mergeBaseResult != showRefResult
     );
Exemplo n.º 4
0
            protected override async Task RunProcess()
            {
                if (isReadOnly)
                {
                    return;
                }

                await detailsTask;
                await latestBranchName;

                if (Details.UpstreamMergePolicy != UpstreamMergePolicy.None && LatestBranchName != null && await repository.IsBadBranch(LatestBranchName))
                {
                    await AppendMessage($"{LatestBranchName} was marked bad and is set to recreate, blocking downstream merge. Hit 'Retry' from the branch page to resume merging.", isError : true);

                    return;
                }

                var upstreamBranchThatIsQueued = await UpstreamQueued();

                if (upstreamBranchThatIsQueued != null)
                {
                    if (upstreamBranchThatIsQueued == Details.GroupName)
                    {
                        await AppendMessage($"{Details.GroupName} was in the queue multiple times.", isError : false);
                    }
                    else
                    {
                        // abort, but queue another attempt
#pragma warning disable CS4014
                        orchestration.EnqueueAction(new MergeDownstreamAction(downstreamBranchGroup), skipDuplicateCheck: true);
#pragma warning restore
                        await AppendMessage($"An upstream branch ({upstreamBranchThatIsQueued}) from this branch ({Details.GroupName}) is still in queue. Retrying later.", isError : true);
                    }
                    return;
                }

                using (var work = workFactory.CreateUnitOfWork())
                {
                    if (await repository.AddAdditionalIntegrationBranches(Details, work))
                    {
                        await work.CommitAsync();

                        // abort, but queue another attempt
#pragma warning disable CS4014
                        orchestration.EnqueueAction(new MergeDownstreamAction(downstreamBranchGroup), skipDuplicateCheck: true);
#pragma warning restore
                        await AppendMessage($"{downstreamBranchGroup} was missing integration branches, which were added before merge was attempted. Retrying...", isError : true);

                        return;
                    }
                }
                var allConfigured = await repository.GetConfiguredBranchGroups().FirstOrDefaultAsync();

                var upstreamBranches = (await ToUpstreamBranchNames(Details.DirectUpstreamBranchGroups.Select(groupName => allConfigured.Find(g => g.GroupName == groupName)).ToImmutableList())).ToImmutableList();
                var needsCreate      = await Strategy.NeedsCreate(LatestBranchName, upstreamBranches);

                var neededUpstreamMerges = await Strategy.FindNeededMerges(LatestBranchName, upstreamBranches);

                var badBranches = await BadBranches(neededUpstreamMerges.Select(t => t.BranchName));

                if (badBranches.Any())
                {
                    await AppendMessage($"{badBranches.First().BranchName} is marked as bad; aborting", isError : true);

                    return;
                }

                if (needsCreate)
                {
                    await AppendMessage($"{Details.GroupName} needs a new branch to be created from {string.Join(",", neededUpstreamMerges.Select(up => up.GroupName))}");

                    var(created, branchName, badReason) = await CreateDownstreamBranch(upstreamBranches);

                    if (created)
                    {
                        await PushBranch(branchName);

                        await Strategy.AfterCreate(Details, LatestBranchName, branchName, AppendProcess);
                    }

                    if (badReason != null)
                    {
                        var output = await cli.ShowRef(branchName).FirstOutputMessage();

                        repository.FlagBadGitRef(branchName, output, badReason);
                        await AppendMessage($"{branchName} is now marked as bad at `{output}` for `{badReason}`.", isError : true);
                    }
                }
                else if (neededUpstreamMerges.Any())
                {
                    await AppendMessage($"{LatestBranchName} needs merges from {string.Join(",", neededUpstreamMerges.Select(up => up.GroupName))}");

                    var(shouldPush, badReason) = await MergeToBranch(LatestBranchName, neededUpstreamMerges);

                    if (shouldPush)
                    {
                        await PushBranch(LatestBranchName);
                    }
                    if (badReason != null)
                    {
                        var output = await cli.ShowRef(LatestBranchName).FirstOutputMessage();

                        repository.FlagBadGitRef(LatestBranchName, output, badReason);
                        await AppendMessage($"{LatestBranchName} is now marked as bad at `{output}`.", isError : true);
                    }
                }
                else
                {
                    await AppendMessage($"{Details.GroupName} is up-to-date.");
                }
            }