From 9774870c9b2fb38a41a118d9b3f48d8f2ff3a162 Mon Sep 17 00:00:00 2001 From: Erokhin Stanislav Date: Mon, 5 May 2014 16:33:23 +0400 Subject: [PATCH] [log] Create BekSorter. --- .../graph/impl/facade/PermanentGraphImpl.java | 27 ++++- .../log/graph/impl/facade/bek/BekBranch.java | 101 +++++++++++++++++ .../impl/facade/bek/BekBranchCreator.java | 107 ++++++++++++++++++ .../impl/facade/bek/BekBranchMerger.java | 92 +++++++++++++++ .../log/graph/impl/facade/bek/BekChecker.java | 40 +++++++ .../impl/facade/bek/BekEdgeRestrictions.java | 43 +++++++ .../log/graph/impl/facade/bek/BekSorter.java | 106 +++++++++++++++++ 7 files changed, 513 insertions(+), 3 deletions(-) create mode 100644 platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranch.java create mode 100644 platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchCreator.java create mode 100644 platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchMerger.java create mode 100644 platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekChecker.java create mode 100644 platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekEdgeRestrictions.java create mode 100644 platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekSorter.java diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java index 04819cf2d2b6..bfcead2cc565 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/PermanentGraphImpl.java @@ -22,6 +22,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.graph.*; import com.intellij.vcs.log.graph.api.LinearGraph; import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo; +import com.intellij.vcs.log.graph.impl.facade.bek.*; import com.intellij.vcs.log.graph.impl.permanent.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,7 +46,7 @@ public class PermanentGraphImpl implements PermanentGraph, P public int compare(@NotNull Integer nodeIndex1, @NotNull Integer nodeIndex2) { CommitId commitId1 = commitIdPermanentCommitsInfo.getCommitId(nodeIndex1); CommitId commitId2 = commitIdPermanentCommitsInfo.getCommitId(nodeIndex2); - return graphColorManager.compareHeads(commitId1, commitId2); + return graphColorManager.compareHeads(commitId2, commitId1); } }); @@ -69,6 +70,8 @@ public class PermanentGraphImpl implements PermanentGraph, P private final Map> myCommitsWithNotLoadParent; @NotNull private final ContainingBranchesGetter myBranchesGetter; + @NotNull + private final PermanentGraphInfo myBekGraphInfo; public PermanentGraphImpl(@NotNull PermanentLinearGraphImpl permanentLinearGraph, @NotNull GraphLayoutImpl permanentGraphLayout, @@ -84,6 +87,7 @@ public class PermanentGraphImpl implements PermanentGraph, P myCommitsWithNotLoadParent = commitsWithNotLoadParent; myBranchNodeIndexes = permanentCommitsInfo.convertToCommitIndexes(branchesCommitId); myBranchesGetter = new ContainingBranchesGetter(permanentLinearGraph, myBranchNodeIndexes); + myBekGraphInfo = createBekSort(); } @NotNull @@ -92,12 +96,29 @@ public class PermanentGraphImpl implements PermanentGraph, P @Nullable Set headsOfVisibleBranches, @Nullable Condition filter) { if (filter == null) { - return CollapsedVisibleGraph.newInstance(this, headsOfVisibleBranches); + return CollapsedVisibleGraph.newInstance(getBekPermanentGraphInfo(sortType), headsOfVisibleBranches); } else { - return FilterVisibleGraph.newInstance(this, headsOfVisibleBranches, filter); + return FilterVisibleGraph.newInstance(getBekPermanentGraphInfo(sortType), headsOfVisibleBranches, filter); } } + @NotNull + private PermanentGraphInfo getBekPermanentGraphInfo(@NotNull SortType sortType) { + if (sortType == SortType.Normal) + return this; + else + return myBekGraphInfo; + } + + @NotNull + private PermanentGraphInfo createBekSort() { + BekIntMap bekMap = BekSorter.createBekMap(myPermanentLinearGraph, myPermanentGraphLayout, myPermanentCommitsInfo.getTimestampGetter()); + + DelegatedPermanentGraphInfo graphInfo = new DelegatedPermanentGraphInfo(this, bekMap); + assert BekChecker.checkLinearGraph(graphInfo.getPermanentLinearGraph()); + return graphInfo; + } + @NotNull @Override public List> getAllCommits() { diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranch.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranch.java new file mode 100644 index 000000000000..9d92a1ce205f --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranch.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.graph.impl.facade.bek; + +import com.intellij.vcs.log.graph.api.LinearGraph; +import com.intellij.vcs.log.graph.utils.TimestampGetter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +class BekBranch { + + private static final int MAX_BLOCK_SIZE = 20; + private static final int MAX_DELTA_TIME = 60 * 60 * 24 * 3 * 1000; + private static final int SMALL_DELTA_TIME = 60 * 60 * 4 * 1000; + + + @NotNull + private final LinearGraph myPermanentGraph; + @NotNull + private final List myNodeIndexes; + + private int myNoInsertSize; + + @Nullable + private List myPrepareForInsertPart = null; + + public BekBranch(@NotNull LinearGraph permanentGraph, @NotNull List nodeIndexes) { + myPermanentGraph = permanentGraph; + myNodeIndexes = nodeIndexes; + myNoInsertSize = myNodeIndexes.size(); + } + + public void updatePrepareForInsertPart(@NotNull TimestampGetter timestampGetter, @NotNull BekEdgeRestrictions edgeRestrictions) { + assert myPrepareForInsertPart == null; + int currentNode = myNodeIndexes.get(myNoInsertSize - 1); + + if (edgeRestrictions.hasRestriction(currentNode)) + return; + + int prevIndex; + for (prevIndex = myNoInsertSize - 1; prevIndex > 0; prevIndex--) { + int upNode = myNodeIndexes.get(prevIndex - 1); + int downNode = myNodeIndexes.get(prevIndex); + + // for correct topological order + if (edgeRestrictions.hasRestriction(upNode)) + break; + + // upNode is mergeCommit + List downNodes = myPermanentGraph.getDownNodes(upNode); + if (downNodes.size() > 1 && downNodes.contains(downNode)) + continue; + + // division + if (!downNodes.contains(downNode)) + break; + + long delta = Math.abs(timestampGetter.getTimestamp(upNode) - timestampGetter.getTimestamp(downNode)); + + // long time between commits + if (delta > MAX_DELTA_TIME) + break; + + // if block so long + if (prevIndex < myNoInsertSize - MAX_BLOCK_SIZE && delta > SMALL_DELTA_TIME) + break; + } + + myPrepareForInsertPart = myNodeIndexes.subList(prevIndex, myNoInsertSize); + } + + @Nullable + public List getPrepareForInsertPart() { + return myPrepareForInsertPart; + } + + public void doneInsertPreparedPart() { + assert myPrepareForInsertPart != null; + myNoInsertSize -= myPrepareForInsertPart.size(); + myPrepareForInsertPart = null; + } + + public boolean isDone() { + return myNoInsertSize == 0; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchCreator.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchCreator.java new file mode 100644 index 000000000000..bbc248983e7a --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchCreator.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.graph.impl.facade.bek; + +import com.intellij.openapi.util.Pair; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.graph.api.LinearGraph; +import com.intellij.vcs.log.graph.impl.permanent.GraphLayoutImpl; +import com.intellij.vcs.log.graph.utils.DfsUtil; +import com.intellij.vcs.log.graph.utils.Flags; +import com.intellij.vcs.log.graph.utils.impl.BitSetFlags; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +class BekBranchCreator { + @NotNull + private final LinearGraph myPermanentGraph; + @NotNull + private final GraphLayoutImpl myGraphLayout; + @NotNull + private final Flags myDoneNodes; + + @NotNull + private final DfsUtil myDfsUtil = new DfsUtil(); + @NotNull + private final BekEdgeRestrictions myEdgeRestrictions = new BekEdgeRestrictions(); + + public BekBranchCreator(@NotNull LinearGraph permanentGraph, @NotNull GraphLayoutImpl graphLayout) { + myPermanentGraph = permanentGraph; + myGraphLayout = graphLayout; + myDoneNodes = new BitSetFlags(permanentGraph.nodesCount(), false); + } + + @NotNull + public Pair, BekEdgeRestrictions> getResult() { + List bekBranches = ContainerUtil.newArrayList(); + + for (int headNode : myGraphLayout.getHeadNodeIndex()) { + List nextBranch = createNextBranch(headNode); + bekBranches.add(new BekBranch(myPermanentGraph, nextBranch)); + } + return Pair.create(bekBranches, myEdgeRestrictions); + } + + public List createNextBranch(int headNode) { + final List nodeIndexes = ContainerUtil.newArrayList(); + + assert !myDoneNodes.get(headNode); + myDoneNodes.set(headNode, true); + nodeIndexes.add(headNode); + + final int startLayout = myGraphLayout.getLayoutIndex(headNode); + + myDfsUtil.nodeDfsIterator(headNode, new DfsUtil.NextNode() { + @Override + public int fun(int currentNode) { + int currentLayout = myGraphLayout.getLayoutIndex(currentNode); + List downNodes = myPermanentGraph.getDownNodes(currentNode); + for (int i = downNodes.size() - 1; i >= 0; i--) { + int downNode = downNodes.get(i); + if (downNode == LinearGraph.NOT_LOAD_COMMIT) + continue; + + if (myDoneNodes.get(downNode)) { + if (myGraphLayout.getLayoutIndex(downNode) < startLayout) + myEdgeRestrictions.addRestriction(currentNode, downNode); + } + else if (currentLayout <= myGraphLayout.getLayoutIndex(downNode)) { + + // almost ok node, except (may be) up nodes + boolean hasUndoneUpNodes = false; + for (int upNode : myPermanentGraph.getUpNodes(downNode)) { + if (!myDoneNodes.get(upNode) && myGraphLayout.getLayoutIndex(upNode) <= currentLayout) { + hasUndoneUpNodes = true; + break; + } + } + + if (!hasUndoneUpNodes) { + myDoneNodes.set(downNode, true); + nodeIndexes.add(downNode); + return downNode; + } + } + } + return NODE_NOT_FOUND; + } + }); + + return nodeIndexes; + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchMerger.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchMerger.java new file mode 100644 index 000000000000..552f5d33e852 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekBranchMerger.java @@ -0,0 +1,92 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.graph.impl.facade.bek; + +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.graph.utils.TimestampGetter; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +class BekBranchMerger { + @NotNull + private final List myBekBranches; + @NotNull + private final BekEdgeRestrictions myEdgeRestrictions; + @NotNull + private final TimestampGetter myTimestampGetter; + + @NotNull + private final List myInverseResultList = ContainerUtil.newArrayList(); + + public BekBranchMerger(@NotNull List bekBranches, + @NotNull BekEdgeRestrictions edgeRestrictions, + @NotNull TimestampGetter timestampGetter) { + myBekBranches = bekBranches; + myEdgeRestrictions = edgeRestrictions; + myTimestampGetter = timestampGetter; + } + + // return true, if exist some undone branch + private boolean prepareLastPartsForBranches() { + boolean hasUndoneBranches = false; + for (BekBranch bekBranch : myBekBranches) { + if (!bekBranch.isDone()){ + hasUndoneBranches = true; + if (bekBranch.getPrepareForInsertPart() == null) { + bekBranch.updatePrepareForInsertPart(myTimestampGetter, myEdgeRestrictions); + } + } + } + return hasUndoneBranches; + } + + private long getBranchLastPartTimestamp(BekBranch bekBranch) { + List prepareForInsertPart = bekBranch.getPrepareForInsertPart(); + if (prepareForInsertPart == null) + return Long.MAX_VALUE; + + assert !prepareForInsertPart.isEmpty(); + int nodeIndex = prepareForInsertPart.get(0); + return myTimestampGetter.getTimestamp(nodeIndex); + } + + private void step() { + BekBranch selectBranch = myBekBranches.get(0); + for (BekBranch bekBranch : myBekBranches) { + if (getBranchLastPartTimestamp(selectBranch) > getBranchLastPartTimestamp(bekBranch)) { + selectBranch = bekBranch; + } + } + + List prepareForInsertPart = selectBranch.getPrepareForInsertPart(); + assert prepareForInsertPart != null; + for (int insertedNode: prepareForInsertPart) + myEdgeRestrictions.removeRestriction(insertedNode); + + myInverseResultList.addAll(ContainerUtil.reverse(prepareForInsertPart)); + selectBranch.doneInsertPreparedPart(); + } + + @NotNull + public List getResult() { + while (prepareLastPartsForBranches()) { + step(); + } + + return ContainerUtil.reverse(myInverseResultList); + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekChecker.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekChecker.java new file mode 100644 index 000000000000..ae08100ea92c --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekChecker.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.graph.impl.facade.bek; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.vcs.log.graph.api.LinearGraph; +import org.jetbrains.annotations.NotNull; + +public class BekChecker { + private final static Logger LOG = Logger.getInstance("#com.intellij.vcs.log.graph.impl.facade.bek.BekChecker"); + + public static boolean checkLinearGraph(@NotNull LinearGraph linearGraph) { + for (int i = 0; i < linearGraph.nodesCount(); i++) { + for (int downNode : linearGraph.getDownNodes(i)) { + if (downNode <= i) + LOG.error("Illegal node: " + i + ", with downNode: " + downNode); + } + + for (int upNode : linearGraph.getUpNodes(i)) { + if (upNode >= i) + LOG.error("Illegal node: " + i + ", with upNode: " + upNode); + } + } + return true; + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekEdgeRestrictions.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekEdgeRestrictions.java new file mode 100644 index 000000000000..e68fe8d143df --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekEdgeRestrictions.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.graph.impl.facade.bek; + +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; + +class BekEdgeRestrictions { + @NotNull + private final MultiMap myUpToEdge = new MultiMap(); + + @NotNull + private final MultiMap myDownToEdge = new MultiMap(); + + void addRestriction(int upNode, int downNode) { + myUpToEdge.putValue(upNode, downNode); + myDownToEdge.putValue(downNode, upNode); + } + + void removeRestriction(int downNode) { + for (int upNode: myDownToEdge.get(downNode)) { + myUpToEdge.remove(upNode, downNode); + } + } + + boolean hasRestriction(int upNode) { + return myUpToEdge.containsKey(upNode); + } + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekSorter.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekSorter.java new file mode 100644 index 000000000000..86d7a9084be8 --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/bek/BekSorter.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.graph.impl.facade.bek; + +import com.intellij.openapi.util.Pair; +import com.intellij.vcs.log.graph.api.LinearGraph; +import com.intellij.vcs.log.graph.impl.permanent.GraphLayoutImpl; +import com.intellij.vcs.log.graph.utils.IntList; +import com.intellij.vcs.log.graph.utils.TimestampGetter; +import com.intellij.vcs.log.graph.utils.impl.CompressedIntList; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class BekSorter { + + @NotNull + public static BekIntMap createBekMap(@NotNull LinearGraph permanentGraph, + @NotNull GraphLayoutImpl graphLayout, + @NotNull TimestampGetter timestampGetter) { + BekSorter bekSorter = new BekSorter(permanentGraph, graphLayout, timestampGetter); + + List result = bekSorter.getResult(); + assert result.size() == permanentGraph.nodesCount(); + return createBekIntMap(result); + } + + private static BekIntMap createBekIntMap(final List result) { + + final int[] reverseMap = new int[result.size()]; + for (int i = 0; i < result.size(); i++) { + reverseMap[result.get(i)] = i; + } + + final IntList compressedBekMap = CompressedIntList.newInstance(new IntList() { + @Override + public int size() { + return result.size(); + } + + @Override + public int get(int index) { + return result.get(index); + } + }, CompressedIntList.DEFAULT_BLOCK_SIZE); + + final IntList compressedReverseMap = CompressedIntList.newInstance(reverseMap); + return new BekIntMap() { + @Override + public int size() { + return compressedBekMap.size(); + } + + @Override + public int getBekIndex(int usualIndex) { + if (usualIndex == LinearGraph.NOT_LOAD_COMMIT) + return LinearGraph.NOT_LOAD_COMMIT; + return compressedReverseMap.get(usualIndex); + } + + @Override + public int getUsualIndex(int bekIndex) { + if (bekIndex == LinearGraph.NOT_LOAD_COMMIT) + return LinearGraph.NOT_LOAD_COMMIT; + return compressedBekMap.get(bekIndex); + } + }; + } + + @NotNull + private final LinearGraph myPermanentGraph; + + @NotNull + private final GraphLayoutImpl myGraphLayout; + + @NotNull + private final TimestampGetter myTimestampGetter; + + private BekSorter(@NotNull LinearGraph permanentGraph, @NotNull GraphLayoutImpl graphLayout, @NotNull TimestampGetter timestampGetter) { + myPermanentGraph = permanentGraph; + myGraphLayout = graphLayout; + myTimestampGetter = timestampGetter; + } + + public List getResult() { + BekBranchCreator bekBranchCreator = new BekBranchCreator(myPermanentGraph, myGraphLayout); + Pair, BekEdgeRestrictions> branches = bekBranchCreator.getResult(); + + BekBranchMerger bekBranchMerger = new BekBranchMerger(branches.first, branches.second, myTimestampGetter); + return bekBranchMerger.getResult(); + } + +}