From 3655c29472983b07a4e89dfd0f4909093c311ad4 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 28 Mar 2024 03:18:20 +0100 Subject: [PATCH] [vcs-log-graph] include only important branch heads into GraphLayout Layout indexes for branch heads are saved in the GraphLayoutImpl#startLayoutIndexForHead field, and binary search is used to find a head commit by layout index. Since all branch heads are used for building graph layout, some branch heads are contained in other branches. If a less important branch is contained in a more important branch, the less important branch does not receive its own layout index and is assigned a layout index from the more important branch. This means that GraphLayoutImpl#startLayoutIndexForHead is no longer sorted, and binary search may return incorrect results on it. This commit includes only important branch heads with own layout index into the GraphLayout. follow-up: 1cd228ff6107d0ca03d5f3172f9940e0d55d9e4f GitOrigin-RevId: f3d5bf4cc85b6b8dbea81895a650e4eca8985cad --- .../impl/permanent/GraphLayoutBuilder.kt | 6 +- .../GraphLayoutBuilderHeadOrderTest.kt | 58 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilderHeadOrderTest.kt diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilder.kt b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilder.kt index 4bcb2e0074a6..06135ea9d1d4 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilder.kt +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilder.kt @@ -55,10 +55,14 @@ object GraphLayoutBuilder { private fun build(graph: LinearGraph, sortedHeads: IntList): GraphLayoutImpl { val layoutIndex = IntArray(graph.nodesCount()) + val importantHeads = IntArrayList() var currentLayoutIndex = 1 for (i in sortedHeads.indices) { val head = sortedHeads.getInt(i) + if (layoutIndex[head] != 0) continue + + importantHeads.add(head) walk(head) { currentNode: Int -> val firstVisit = layoutIndex[currentNode] == 0 @@ -73,6 +77,6 @@ object GraphLayoutBuilder { } } - return GraphLayoutImpl(layoutIndex, sortedHeads) + return GraphLayoutImpl(layoutIndex, importantHeads) } } \ No newline at end of file diff --git a/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilderHeadOrderTest.kt b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilderHeadOrderTest.kt new file mode 100644 index 000000000000..acaec51c6603 --- /dev/null +++ b/platform/vcs-log/graph/test/com/intellij/vcs/log/graph/impl/permanent/GraphLayoutBuilderHeadOrderTest.kt @@ -0,0 +1,58 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.vcs.log.graph.impl.permanent + +import com.intellij.vcs.log.graph.api.LinearGraph +import com.intellij.vcs.log.graph.graph +import it.unimi.dsi.fastutil.ints.IntComparator +import org.junit.Assert.assertEquals +import org.junit.Test + +class GraphLayoutBuilderHeadOrderTest { + @Test + fun linearGraph() { + val graph = graph { + 0(1) + 1(2) + 2(3) + 3() + } + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(0, 2), expectedHeads = listOf(0, 0, 0, 0)) + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(2, 0), expectedHeads = listOf(0, 0, 2, 2)) + } + + @Test + fun branchingGraph() { + val graph = graph { + 0(2) + 1(3) + 2(3) + 3(4) + 4() + } + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(0, 1, 2), expectedHeads = listOf(0, 1, 0, 0, 0)) + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(0, 2, 1), expectedHeads = listOf(0, 1, 0, 0, 0)) + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(1, 0, 2), expectedHeads = listOf(0, 1, 0, 1, 1)) + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(1, 2, 0), expectedHeads = listOf(0, 1, 2, 1, 1)) + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(2, 0, 1), expectedHeads = listOf(0, 1, 2, 2, 2)) + graph.assertHeadsForCommits(branchHeads = setOf(0, 2), headComparator = order(2, 1, 0), expectedHeads = listOf(0, 1, 2, 2, 2)) + } + + /** + * Assert that [com.intellij.vcs.log.graph.api.GraphLayout] built for this graph has correct head commits for each commit in the graph. + */ + private fun LinearGraph.assertHeadsForCommits(branchHeads: Set, headComparator: IntComparator, expectedHeads: List) { + val graphLayout = GraphLayoutBuilder.build(this, branchHeads, headComparator) + assertEquals(expectedHeads, getHeadNodeIndices(this, graphLayout)) + } + + /** + * For each commit in the graph, computes corresponding head node. + */ + private fun getHeadNodeIndices(graph: LinearGraph, graphLayout: GraphLayoutImpl): List { + return List(graph.nodesCount()) { graphLayout.getOneOfHeadNodeIndex(it) } + } + + private fun order(vararg list: Int): IntComparator { + return IntComparator { i, j -> list.indexOf(i) - list.indexOf(j) } + } +} \ No newline at end of file