internal static IEnumerable <LogGraphNode> CreateGraph( IRepositoryWrapper repository, IEnumerable <RefSelection> Refs) { using var delayDispose = repository.TryAddRef(); if (delayDispose != null) { if (repository.Head is null) { yield break; } var selectedRefs = Refs .Select(@ref => repository.References[@ref.CanonicalName]) .Where(@ref => !(@ref is null)) .ToList(); var allTags = repository.Tags.ToList(); var allBranches = repository.Branches.ToList(); var detachedHead = repository.Info.IsHeadDetached ? repository.Head : null; var commitFilter = new CommitFilter() { SortBy = CommitSortStrategies.Topological, IncludeReachableFrom = selectedRefs }; var query = repository.QueryCommits(commitFilter); var head = repository.Head; bool headSelected = selectedRefs.Any(@ref => @ref.CanonicalName == head.CanonicalName); var firstCommit = headSelected ? null : query.FirstOrDefault(); if (!headSelected && firstCommit is null) { throw new NoBranchSelected("No branch is selected."); } var expectedIds = firstCommit is null ? new List <ObjectId?>() { repository.Head.Tip.Id } : firstCommit.Parents.Select(c => c.Id).ToList <ObjectId?>(); var directions = new List <List <TGraphPos> >() { headSelected ? new List <TGraphPos>() { 0 } : expectedIds.Select((_, i) => (TGraphPos)i).ToList() }; for (int i = 1; i < directions.First().Count; ++i) { directions.Add(new List <TGraphPos>()); } var lastDirections = new List <List <TGraphPos> >(); TGraphPos lastPosition = 0; var lastCommit = firstCommit is null ? new Variant <Commit, DiffTargets>(DiffTargets.WorkingDirectory) : new Variant <Commit, DiffTargets>(firstCommit); bool lastMerge = expectedIds.Count > 1; foreach (Commit c in query.Skip(headSelected ? 0 : 1)) { var foundNextPosition = expectedIds.FindIndex((id) => id == c.Id); if (foundNextPosition == -1) { // commit without visible children foundNextPosition = expectedIds.FindIndex((id) => id is null); if (foundNextPosition == -1) { foundNextPosition = expectedIds.Count; expectedIds.Add(null); directions.Add(new List <TGraphPos>()); } } TGraphPos nextPosition = (TGraphPos)foundNextPosition; var currentDirections = directions; directions = currentDirections.Select((dir) => dir.Take(0).ToList()).ToList(); for (TGraphPos i = 0; i < directions.Count; ++i) { if (expectedIds[i] != null) { directions[i].Add(i); } } for (TGraphPos i = 0; i < currentDirections.Count; ++i) { if (expectedIds[i] == c.Id && i != nextPosition) { foreach (var direction in currentDirections) { if (direction.Remove(i)) { direction.Add(nextPosition); } } directions[i].Clear(); } } var parents = c.Parents.ToList(); TGraphPos parentIndex = 0; for (TGraphPos i = 0; i < expectedIds.Count; ++i) { if (expectedIds[i] == c.Id || expectedIds[i] is null) { if (parents.Count > parentIndex) { TGraphPos indexToChange = i; if (expectedIds[i] is null && expectedIds[nextPosition] == c.Id) { indexToChange = nextPosition; --i; } expectedIds[indexToChange] = parents[parentIndex++].Id; if (!directions[nextPosition].Contains(indexToChange)) { directions[nextPosition].Add(indexToChange); } } else { expectedIds[i] = null; if (parents.Count == 0) { directions[nextPosition].Clear(); } } } } for (; parentIndex < parents.Count; ++parentIndex) { expectedIds.Add(parents[parentIndex].Id); directions.Add(new List <TGraphPos>()); directions[nextPosition].Add((TGraphPos)(directions.Count - 1)); } yield return(new LogGraphNode( lastPosition, CreateGraphDirections(lastDirections, currentDirections), lastCommit, lastMerge, allBranches .Where(branch => lastCommit.Is <Commit>() && branch.Tip == lastCommit.Get <Commit>()) .Select(CreateBranchInfo) .ToList(), allTags .Where(tag => lastCommit.Is <Commit>() && tag.Target == lastCommit.Get <Commit>()) .Select(CreateTagInfo) .ToList(), !(detachedHead is null) && lastCommit.Is <Commit>() && lastCommit.Get <Commit>() == detachedHead.Tip)); lastDirections = currentDirections; lastPosition = nextPosition; lastCommit = c; lastMerge = parents.Count > 1; } yield return(new LogGraphNode( lastPosition, CreateGraphDirections(lastDirections, new List <List <TGraphPos> >()), lastCommit, false, allBranches .Where(branch => lastCommit.Is <Commit>() && branch.Tip == lastCommit.Get <Commit>()) .Select(CreateBranchInfo) .ToList(), allTags .Where(tag => lastCommit.Is <Commit>() && tag.Target == lastCommit.Get <Commit>()) .Select(CreateTagInfo) .ToList(), !(detachedHead is null) && lastCommit.Is <Commit>() && lastCommit.Get <Commit>() == detachedHead.Tip)); } }