public async Task AfterCreate(BranchGroup group, string latestBranchName, string createdBranchName, Func <IReactiveProcess, RepositoryActionReactiveProcessEntry> appendProcess)
        {
            var allRefs = await repository.GetAllBranchRefs().FirstOrDefaultAsync();

            var targetUpdateBranchName = createdBranchName == group.GroupName
                ? await repository.GetNextCandidateBranch(group).FirstOrDefaultAsync()
                : group.GroupName;

            await appendProcess(cli.Push(createdBranchName, targetUpdateBranchName, force: true));

            var createdRef = await repository.GetBranchRef(createdBranchName).FirstOrDefaultAsync();

            await repository.BranchUpdated(targetUpdateBranchName, createdRef, await repository.GetBranchRef(targetUpdateBranchName).FirstOrDefaultAsync());
        }
Ejemplo n.º 2
0
            private async Task <IEnumerable <NeededMerge> > ToUpstreamBranchNames(ImmutableList <BranchGroup> directUpstreamBranchGroups)
            {
                var hierarchy = (await repository.AllBranchesHierarchy().Take(1)).ToDictionary(branch => branch.GroupName, branch => branch.HierarchyDepth);
                var remotes   = await repository.GetAllBranchRefs().FirstOrDefaultAsync();

                // Put integration branches first; they might be needed for conflict resolution in other branches!
                // FIXME - this is using the GroupName rather than the BranchName!
                return(from branch in directUpstreamBranchGroups
                       orderby branch.BranchType == BranchGroupType.Integration ? 0 : 1, -hierarchy[branch.GroupName], branch.GroupName
                       select new NeededMerge
                {
                    GroupName = branch.GroupName,
                    BranchName = branchIteration.GetLatestBranchNameIteration(branch.GroupName, remotes.Select(r => r.Name))
                });
            }
Ejemplo n.º 3
0
        public async Task <IntegrationBranchResult> FindConflicts(string targetBranch, IEnumerable <string> _, AttemptMergeDelegate doMerge)
        {
            // When finding branches that conflict, we should go to the earliest point of conflict... so we need to know full ancestry.
            // Except... we can start at the direct upstream, and if those conflict, then move up; a double breadth-first search.
            //
            // A & B -> C
            // D & E & F -> G
            // H & I -> J
            // C and G do not conflict.
            // C and J conflict.
            // G and J do not conflict.
            // A and J conflict.
            // B and J conflict.
            // A and H conflict.
            // B and H do not conflict.
            // A and I do not conflict.
            // B and I do not conflict.
            //
            // Two integration branches are needed: A-H and B-J. A-H is already found, so only B-J is created.
            // A-H and B-J are added to the downstream branch, if A-H was not already added to the downstream branch.

            var remoteBranches = await repository.GetAllBranchRefs().Select(branches => branches.Select(branch => branch.Name).ToImmutableList()).FirstOrDefaultAsync();

            Func <string, LatestBranchGroup> groupToLatest = group => new LatestBranchGroup
            {
                GroupName        = group,
                LatestBranchName = branchIteration.GetLatestBranchNameIteration(group, remoteBranches)
            };


            var upstreamBranchListings      = new Dictionary <string, ImmutableList <string> >();
            var initialUpstreamBranchGroups = await GetUpstreamBranches(targetBranch, upstreamBranchListings);

            await GetUpstreamBranches(targetBranch, upstreamBranchListings);

            var leafConflicts      = new HashSet <ConflictingBranches>();
            var unflippedConflicts = new HashSet <ConflictingBranches>();
            // Remove from `middleConflicts` if we find a deeper one that conflicts
            var middleConflicts = new HashSet <ConflictingBranches>();
            var target          = groupToLatest(targetBranch);

            var possibleConflicts = new Stack <PossibleConflictingBranches>(
                (
                    from branchA in initialUpstreamBranchGroups.Select(groupToLatest)
                    from branchB in initialUpstreamBranchGroups.Select(groupToLatest)
                    where branchA.CompareTo(branchB) < 0
                    select new PossibleConflictingBranches {
                BranchA = branchA, BranchB = branchB, ConflictWhenSuccess = null
            }
                ).Concat(
                    from branch in initialUpstreamBranchGroups.Select(groupToLatest)
                    where target.LatestBranchName != null
                    select new PossibleConflictingBranches {
                BranchA = branch, BranchB = target, ConflictWhenSuccess = null
            }
                    )
                );

            Func <PossibleConflictingBranches, Task <bool> > digDeeper = async(possibleConflict) =>
            {
                var upstreamBranches = (await GetUpstreamBranches(possibleConflict.BranchA.GroupName, upstreamBranchListings)).Select(groupToLatest).ToImmutableList();
                if (upstreamBranches.Count > 0)
                {
                    // go deeper on the left side
                    foreach (var possible in upstreamBranches.Where(b => b.GroupName != possibleConflict.BranchB.GroupName))
                    {
                        possibleConflicts.Push(new PossibleConflictingBranches
                        {
                            BranchA             = possible,
                            BranchB             = possibleConflict.BranchB,
                            ConflictWhenSuccess = new ConflictingBranches {
                                BranchA = possibleConflict.BranchA, BranchB = possibleConflict.BranchB
                            }
                        });
                    }
                    return(true);
                }
                return(false);
            };

            while (possibleConflicts.Count > 0)
            {
                if (possibleConflicts.Any(p => p.BranchA.LatestBranchName == null || p.BranchB.LatestBranchName == null))
                {
                    return(new IntegrationBranchResult
                    {
                        PendingUpdates = true,
                    });
                }
                var possibleConflict = possibleConflicts.Pop();

                if (await repository.IsBadBranch(possibleConflict.BranchA.LatestBranchName) || await repository.IsBadBranch(possibleConflict.BranchB.LatestBranchName))
                {
                    // At least one bad branch was found
                    return(new IntegrationBranchResult
                    {
                        PendingUpdates = true,
                    });
                }
                if (leafConflicts.Contains(new ConflictingBranches {
                    BranchA = possibleConflict.BranchA, BranchB = possibleConflict.BranchB
                }))
                {
                    continue;
                }
                var isSuccessfulMerge = await repository.CanMerge(possibleConflict.BranchA.LatestBranchName, possibleConflict.BranchB.LatestBranchName)
                                        ?? await doMerge(possibleConflict.BranchA.LatestBranchName, possibleConflict.BranchB.LatestBranchName, "CONFLICT TEST; DO NOT PUSH");

                if (isSuccessfulMerge)
                {
                    // successful, not a conflict
                    await repository.MarkCanMerge(possibleConflict.BranchA.LatestBranchName, possibleConflict.BranchB.LatestBranchName, true);
                }
                else
                {
                    await repository.MarkCanMerge(possibleConflict.BranchA.LatestBranchName, possibleConflict.BranchB.LatestBranchName, false);

                    // there was a conflict
                    if (possibleConflict.ConflictWhenSuccess.HasValue && unflippedConflicts.Contains(possibleConflict.ConflictWhenSuccess.Value))
                    {
                        // so remove the intermediary
                        unflippedConflicts.Remove(possibleConflict.ConflictWhenSuccess.Value);
                    }
                    // ...and check deeper
                    var conflict = new ConflictingBranches {
                        BranchA = possibleConflict.BranchA, BranchB = possibleConflict.BranchB
                    };
                    unflippedConflicts.Add(conflict);
                    if (await digDeeper(possibleConflict))
                    {
                        // succeeded to dig deeper on branchA
                    }
                }
            }

            foreach (var conflict in unflippedConflicts)
            {
                if (await digDeeper(new PossibleConflictingBranches
                {
                    BranchA = conflict.BranchB,
                    BranchB = conflict.BranchA,
                    ConflictWhenSuccess = null,
                }))
                {
                    middleConflicts.Add(new ConflictingBranches {
                        BranchA = conflict.BranchB, BranchB = conflict.BranchA
                    });
                }
                else
                {
                    // Nothing deeper; this is our conflict
                    leafConflicts.Add(conflict);
                }
            }

            while (possibleConflicts.Count > 0)
            {
                var possibleConflict = possibleConflicts.Pop();
                if (await repository.IsBadBranch(possibleConflict.BranchA.LatestBranchName) || await repository.IsBadBranch(possibleConflict.BranchB.LatestBranchName))
                {
                    // At least one bad branch was found
                    return(new IntegrationBranchResult
                    {
                        PendingUpdates = true,
                    });
                }
                if (leafConflicts.Contains(new ConflictingBranches {
                    BranchA = possibleConflict.BranchA, BranchB = possibleConflict.BranchB
                }))
                {
                    continue;
                }
                var isSuccessfulMerge = await doMerge(possibleConflict.BranchA.LatestBranchName, possibleConflict.BranchB.LatestBranchName, "CONFLICT TEST; DO NOT PUSH");

                if (isSuccessfulMerge)
                {
                    // successful, not a conflict
                }
                else
                {
                    // there was a conflict
                    if (possibleConflict.ConflictWhenSuccess.HasValue && middleConflicts.Contains(possibleConflict.ConflictWhenSuccess.Value))
                    {
                        // so remove the intermediary
                        middleConflicts.Remove(possibleConflict.ConflictWhenSuccess.Value);
                    }
                    // ...and check deeper
                    var conflict = new ConflictingBranches {
                        BranchA = possibleConflict.BranchA, BranchB = possibleConflict.BranchB
                    };
                    if (await digDeeper(possibleConflict))
                    {
                        // succeeded to dig deeper on branchA
                        middleConflicts.Add(conflict);
                    }
                    else
                    {
                        // Nothing deeper; this is our conflict
                        leafConflicts.Add(conflict);
                    }
                }
            }

            return(new IntegrationBranchResult
            {
                Conflicts = leafConflicts.Concat(middleConflicts).Select(c => c.Normalize()).Distinct(),
            });
        }