diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java index cb9251b1e17e..4aa801693bb2 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/VisibleGraphImpl.java @@ -65,7 +65,7 @@ public class VisibleGraphImpl implements VisibleGraph { public RowInfo getRowInfo(final int visibleRow) { final int nodeId = myGraphController.getCompiledGraph().getNodeId(visibleRow); assert nodeId >= 0; // todo remake for all id - return new MyRowInfo(nodeId, visibleRow); + return new RowInfoImpl(nodeId, visibleRow); } @Override @@ -254,15 +254,19 @@ public class VisibleGraphImpl implements VisibleGraph { } } - private class MyRowInfo implements RowInfo { + public class RowInfoImpl implements RowInfo { private final int myNodeId; private final int myVisibleRow; - public MyRowInfo(int nodeId, int visibleRow) { + public RowInfoImpl(int nodeId, int visibleRow) { myNodeId = nodeId; myVisibleRow = visibleRow; } + public int getNodeId() { + return myNodeId; + } + @NotNull @Override public CommitId getCommit() { diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.java new file mode 100644 index 000000000000..b9df5392464d --- /dev/null +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/BfsUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2017 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.utils; + +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.graph.api.LiteLinearGraph; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +public class BfsUtil { + public static int getCorrespondingParentIndex(@NotNull LiteLinearGraph graph, int startNode, int endNode, @NotNull Flags visited) { + List candidates = graph.getNodes(startNode, LiteLinearGraph.NodeFilter.DOWN); + if (candidates.size() == 1) return 0; + if (candidates.contains(endNode)) return candidates.indexOf(endNode); + + List> queues = new ArrayList<>(candidates.size()); + for (int candidate : candidates) { + queues.add(ContainerUtil.newLinkedList(candidate)); + } + + int emptyCount; + visited.setAll(false); + do { + emptyCount = 0; + for (Queue queue : queues) { + if (queue.isEmpty()) { + emptyCount++; + } + else { + boolean found = runNextBfsStep(graph, queue, visited, endNode); + if (found) { + return queues.indexOf(queue); + } + } + } + } + while (emptyCount < queues.size()); + + return 0; + } + + private static boolean runNextBfsStep(@NotNull LiteLinearGraph graph, @NotNull Queue queue, @NotNull Flags visited, int target) { + while (!queue.isEmpty()) { + Integer node = queue.poll(); + if (!visited.get(node)) { + visited.set(node, true); + List next = graph.getNodes(node, LiteLinearGraph.NodeFilter.DOWN); + if (next.contains(target)) return true; + queue.addAll(next); + return false; + } + } + return false; + } +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java index 6927706f0728..9c6e25be6341 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/utils/DfsUtil.java @@ -32,7 +32,7 @@ public class DfsUtil { } public interface NodeVisitor { - void enterNode(int node); + void enterNode(int node, int previousNode); void exitNode(int node); } @@ -44,12 +44,10 @@ public class DfsUtil { * And when a node is entered from down-sibling, goes to the up-siblings first. * Then goes to the other down-siblings. * When a node is entered the first time, enterNode is called. - * When a node is passes in the same direction, exitNode is called. - * Nothing is called when a all the siblings of the node are visited. + * When a all the siblings of the node are visited, exitNode is called. */ public static void walk(@NotNull LiteLinearGraph graph, int start, @NotNull NodeVisitor visitor) { BitSetFlags visited = new BitSetFlags(graph.nodesCount(), false); - BitSetFlags visitedInSameDirection = new BitSetFlags(graph.nodesCount(), false); Stack> stack = new Stack<>(); stack.push(new Pair<>(start, true)); // commit + direction of travel @@ -60,7 +58,7 @@ public class DfsUtil { boolean down = stack.peek().second; if (!visited.get(currentNode)) { visited.set(currentNode, true); - visitor.enterNode(currentNode); + visitor.enterNode(currentNode, getPreviousNode(stack)); } for (int nextNode : graph.getNodes(currentNode, down ? LiteLinearGraph.NodeFilter.DOWN : LiteLinearGraph.NodeFilter.UP)) { @@ -70,11 +68,6 @@ public class DfsUtil { } } - if (!visitedInSameDirection.get(currentNode)) { - visitedInSameDirection.set(currentNode, true); - visitor.exitNode(currentNode); - } - for (int nextNode : graph.getNodes(currentNode, down ? LiteLinearGraph.NodeFilter.UP : LiteLinearGraph.NodeFilter.DOWN)) { if (!visited.get(nextNode)) { stack.push(new Pair<>(nextNode, !down)); @@ -82,10 +75,18 @@ public class DfsUtil { } } + visitor.exitNode(currentNode); stack.pop(); } } + private static int getPreviousNode(@NotNull Stack> stack) { + if (stack.size() < 2) { + return NextNode.NODE_NOT_FOUND; + } + return stack.get(stack.size() - 2).first; + } + public static void walk(int startRowIndex, @NotNull NextNode nextNodeFun) { IntStack stack = new IntStack(); stack.push(startRowIndex); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/IndexDataGetter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/IndexDataGetter.java index 74fd5c15069b..3de5a13e6560 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/IndexDataGetter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/IndexDataGetter.java @@ -15,28 +15,27 @@ */ package com.intellij.vcs.log.data.index; -import com.google.common.primitives.Ints; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Couple; -import com.intellij.openapi.util.UnorderedPair; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Interner; -import com.intellij.util.containers.SmartHashSet; import com.intellij.util.indexing.StorageException; import com.intellij.vcs.log.impl.FatalErrorHandler; import com.intellij.vcsUtil.VcsUtil; import gnu.trove.TIntObjectHashMap; -import gnu.trove.TIntObjectIterator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Set; public class IndexDataGetter { + private static final Logger LOG = Logger.getInstance(IndexDataGetter.class); + @NotNull private final Project myProject; @NotNull private final Set myRoots; @NotNull private final VcsLogPersistentIndex.IndexStorage myIndexStorage; @@ -85,8 +84,7 @@ public class IndexDataGetter { VirtualFile root = VcsUtil.getVcsRootFor(myProject, path); if (myRoots.contains(root)) { try { - myIndexStorage.paths.iterateCommits(Collections.singleton(path), (paths, commit) -> result.add(commit, paths)); - result.pack(); + myIndexStorage.paths.iterateCommits(path, (changes, commit) -> result.add(commit, changes.first, changes.second)); } catch (IOException | StorageException e) { myFatalErrorsConsumer.consume(this, e); @@ -96,117 +94,113 @@ public class IndexDataGetter { return result; } - public static class FileNamesData { - @NotNull private final Interner> myPathsInterner = new Interner<>(); - @NotNull private final TIntObjectHashMap> myCommitsToPaths; - @NotNull private final TIntObjectHashMap>> myCommitsToRenames; - - public FileNamesData() { - myCommitsToPaths = new TIntObjectHashMap<>(); - myCommitsToRenames = new TIntObjectHashMap<>(); - } + public class FileNamesData { + @NotNull private final TIntObjectHashMap>> myCommitToChanges = + new TIntObjectHashMap<>(); + private boolean myHasRenames = false; public boolean hasRenames() { - return !myCommitsToRenames.isEmpty(); + return myHasRenames; } - private void addPath(int commit, @NotNull FilePath path) { - Set paths = myCommitsToPaths.get(commit); - if (paths == null) { - paths = new SmartHashSet<>(); - myCommitsToPaths.put(commit, paths); + public void add(int commit, @NotNull FilePath path, @NotNull List changes) { + Map> map = myCommitToChanges.get(commit); + if (map == null) { + map = ContainerUtil.newHashMap(); + myCommitToChanges.put(commit, map); } - paths.add(path); - } - private void addRename(int commit, @NotNull Couple path) { - Set> paths = myCommitsToRenames.get(commit); - if (paths == null) { - paths = ContainerUtil.newHashSet(); - myCommitsToRenames.put(commit, paths); - } - paths.add(new UnorderedPair<>(path.first, path.second)); - } - - private void add(int commit, @NotNull Couple paths) { - if (paths.second == null) { - addPath(commit, paths.first); - } - else { - addRename(commit, paths); - } - } - - public boolean affects(int commit, @NotNull FilePath path) { - Set paths = myCommitsToPaths.get(commit); - if (paths != null && paths.contains(path)) return true; - return getRenamedPath(commit, path) != null; - } - - @Nullable - public FilePath getRenamedPath(int commit, @Nullable FilePath newName) { - Set> renames = myCommitsToRenames.get(commit); - if (renames == null) return null; - - for (UnorderedPair rename : renames) { - if (rename.first.equals(newName)) return rename.second; - if (rename.second.equals(newName)) return rename.first; - } - return null; - } - - @Nullable - public FilePath getPreviousPath(int commit, @Nullable FilePath path) { - Set paths = myCommitsToPaths.get(commit); - if (paths != null && paths.contains(path)) return path; - return getRenamedPath(commit, path); - } - - public void remove(int commit) { - myCommitsToPaths.remove(commit); - myCommitsToRenames.remove(commit); - } - - public void retain(int commit, @NotNull FilePath path, @NotNull FilePath previousPath) { - if (path.equals(previousPath)) { - myCommitsToPaths.put(commit, myPathsInterner.intern(ContainerUtil.set(path))); - myCommitsToRenames.remove(commit); - } - else { - myCommitsToPaths.remove(commit); - myCommitsToRenames.put(commit, ContainerUtil.set(new UnorderedPair<>(path, previousPath))); - } - } - - @NotNull - public Set getAffectedPaths(int commit) { - Set result = new SmartHashSet<>(); - - Set paths = myCommitsToPaths.get(commit); - if (paths != null) result.addAll(paths); - - Set> renames = myCommitsToRenames.get(commit); - if (renames != null) { - for (UnorderedPair rename : renames) { - result.add(rename.first); - result.add(rename.second); + if (!myHasRenames) { + for (VcsLogPathsIndex.ChangeData data : changes) { + if (data == null) continue; + if (data.kind.equals(VcsLogPathsIndex.ChangeKind.RENAMED_FROM) || data.kind.equals(VcsLogPathsIndex.ChangeKind.RENAMED_TO)) { + myHasRenames = true; + break; + } } } - - return result; + map.put(path, changes); } - void pack() { - TIntObjectIterator> iterator = myCommitsToPaths.iterator(); - while (iterator.hasNext()) { - iterator.advance(); - iterator.setValue(myPathsInterner.intern(iterator.value())); + @Nullable + public FilePath getPathInParentRevision(int commit, int parentIndex, @NotNull FilePath childPath) { + Map> filesToChangesMap = myCommitToChanges.get(commit); + LOG.assertTrue(filesToChangesMap != null); + List changes = filesToChangesMap.get(childPath); + if (changes == null) return childPath; + + VcsLogPathsIndex.ChangeData change = changes.get(parentIndex); + if (change == null) { + LOG.assertTrue(changes.size() > 1); + return childPath; } + if (change.kind.equals(VcsLogPathsIndex.ChangeKind.RENAMED_FROM)) return null; + if (change.kind.equals(VcsLogPathsIndex.ChangeKind.RENAMED_TO)) { + return VcsUtil.getFilePath(myIndexStorage.paths.getPath(change.otherPath)); + } + return childPath; + } + + @Nullable + public FilePath getPathInChildRevision(int commit, int parentIndex, @NotNull FilePath parentPath) { + Map> filesToChangesMap = myCommitToChanges.get(commit); + LOG.assertTrue(filesToChangesMap != null); + List changes = filesToChangesMap.get(parentPath); + if (changes == null) return parentPath; + + VcsLogPathsIndex.ChangeData change = changes.get(parentIndex); + if (change == null) return parentPath; + if (change.kind.equals(VcsLogPathsIndex.ChangeKind.RENAMED_TO)) return null; + if (change.kind.equals(VcsLogPathsIndex.ChangeKind.RENAMED_FROM)) { + return VcsUtil.getFilePath(myIndexStorage.paths.getPath(change.otherPath)); + } + return parentPath; + } + + public boolean affects(int id, @NotNull FilePath path) { + return myCommitToChanges.containsKey(id) && myCommitToChanges.get(id).containsKey(path); } @NotNull public Set getCommits() { - return ContainerUtil.union(Ints.asList(myCommitsToPaths.keys()), Ints.asList(myCommitsToRenames.keys())); + Set result = ContainerUtil.newHashSet(); + myCommitToChanges.forEach(result::add); + return result; + } + + @NotNull + public Map buildPathsMap() { + Map result = ContainerUtil.newHashMap(); + + myCommitToChanges.forEachEntry((commit, filesToChanges) -> { + if (filesToChanges.size() == 1) { + result.put(commit, ContainerUtil.getFirstItem(filesToChanges.keySet())); + } + else { + for (Map.Entry> fileToChange : filesToChanges.entrySet()) { + VcsLogPathsIndex.ChangeData changeData = + ContainerUtil.find(fileToChange.getValue(), ch -> ch != null && !ch.kind.equals(VcsLogPathsIndex.ChangeKind.RENAMED_FROM)); + if (changeData != null) { + result.put(commit, fileToChange.getKey()); + break; + } + } + } + + return true; + }); + + return result; + } + + public boolean isTrivialMerge(int commit, @NotNull FilePath path) { + if (!myCommitToChanges.containsKey(commit)) return false; + List data = myCommitToChanges.get(commit).get(path); + // strictly speaking, the criteria for merge triviality is a little bit more tricky than this: + // some merges have just reverted changes in one of the branches + // they need to be displayed + // but we skip them instead + return data != null && data.size() > 1 && data.contains(null); } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java index 38979f359bb3..aebfba3c30b5 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPathsIndex.java @@ -18,6 +18,7 @@ package com.intellij.vcs.log.data.index; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; @@ -76,6 +77,17 @@ public class VcsLogPathsIndex extends VcsLogFullDetailsIndex paths, @NotNull ObjIntConsumer> consumer) + public void iterateCommits(@NotNull FilePath path, @NotNull ObjIntConsumer>> consumer) throws IOException, StorageException { - Set startIds = getPathIds(paths); + Set startIds = getPathIds(Collections.singleton(path)); Set allIds = ContainerUtil.newHashSet(startIds); Set newIds = ContainerUtil.newHashSet(); while (!startIds.isEmpty()) { @@ -162,17 +174,9 @@ public class VcsLogPathsIndex extends VcsLogFullDetailsIndex> map(@NotNull VcsFullCommitDetails inputData) { Map> result = new THashMap<>(); - for (int parent = 0; parent < inputData.getParents().size(); parent++) { + int size = inputData.getParents().isEmpty() ? 1 : inputData.getParents().size(); + for (int parent = 0; parent < size; parent++) { Collection> moves; Collection changedPaths; if (inputData instanceof VcsIndexableDetails) { @@ -285,7 +290,7 @@ public class VcsLogPathsIndex extends VcsLogFullDetailsIndex data = fillDataWithNulls(result, parent, afterId); if (beforePath == null) { - data.add(null); + data.add(new ChangeData(ChangeKind.MODIFIED, -1)); } else { int beforeId = myPathsEnumerator.enumerate(beforePath); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryFilterer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryFilterer.java index 2830994ae338..3528ed2cd585 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryFilterer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryFilterer.java @@ -28,14 +28,19 @@ import com.intellij.vcs.log.data.DataPack; import com.intellij.vcs.log.data.VcsLogData; import com.intellij.vcs.log.data.index.IndexDataGetter; import com.intellij.vcs.log.graph.PermanentGraph; +import com.intellij.vcs.log.graph.RowInfo; import com.intellij.vcs.log.graph.VisibleGraph; import com.intellij.vcs.log.graph.api.LinearGraph; +import com.intellij.vcs.log.graph.api.LiteLinearGraph; +import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo; import com.intellij.vcs.log.graph.impl.facade.PermanentGraphImpl; import com.intellij.vcs.log.graph.impl.facade.ReachableNodes; import com.intellij.vcs.log.graph.impl.facade.VisibleGraphImpl; import com.intellij.vcs.log.graph.impl.permanent.PermanentCommitsInfoImpl; import com.intellij.vcs.log.graph.utils.DfsUtil; import com.intellij.vcs.log.graph.utils.LinearGraphUtils; +import com.intellij.vcs.log.graph.utils.BfsUtil; +import com.intellij.vcs.log.graph.utils.impl.BitSetFlags; import com.intellij.vcs.log.visible.CommitCountStage; import com.intellij.vcs.log.visible.VcsLogFilterer; import com.intellij.vcs.log.visible.VisiblePack; @@ -73,21 +78,29 @@ class FileHistoryFilterer extends VcsLogFilterer { VisibleGraph visibleGraph = createVisibleGraph(dataPack, sortType, matchingHeads, filterResult.matchingCommits); IndexDataGetter.FileNamesData namesData = ((FilteredByFileResult)filterResult).fileNamesData; + Map pathsMap = null; if (namesData.hasRenames() && visibleGraph.getVisibleCommitCount() > 0) { if (visibleGraph instanceof VisibleGraphImpl) { int row = getCurrentRow(dataPack, visibleGraph, namesData); if (row >= 0) { - FileHistoryRefiner refiner = new FileHistoryRefiner(visibleGraph, namesData); - if (refiner.refine(((VisibleGraphImpl)visibleGraph).getLinearGraph(), row, myFilePath)) { + FileHistoryRefiner refiner = new FileHistoryRefiner((VisibleGraphImpl)visibleGraph, + ((PermanentGraphInfo)dataPack.getPermanentGraph()).getLinearGraph(), + namesData); + if (refiner.refine(row, myFilePath)) { // creating a vg is the most expensive task, so trying to avoid that when unnecessary - visibleGraph = createVisibleGraph(dataPack, sortType, matchingHeads, refiner.getMatchingCommits()); + visibleGraph = createVisibleGraph(dataPack, sortType, matchingHeads, refiner.getPathsForCommits().keySet()); + pathsMap = refiner.getPathsForCommits(); } } } } - return new FileHistoryVisiblePack(dataPack, visibleGraph, filterResult.canRequestMore, filters, namesData); + if (pathsMap == null) { + pathsMap = namesData.buildPathsMap(); + } + + return new FileHistoryVisiblePack(dataPack, visibleGraph, filterResult.canRequestMore, filters, pathsMap); } @NotNull @@ -159,55 +172,85 @@ class FileHistoryFilterer extends VcsLogFilterer { } private static class FileHistoryRefiner implements DfsUtil.NodeVisitor { - @NotNull private final VisibleGraph myVisibleGraph; + @NotNull private final VisibleGraphImpl myVisibleGraph; + @NotNull private final LiteLinearGraph myPermanentGraph; + @NotNull private final LiteLinearGraph myLinearVisibleGraph; @NotNull private final IndexDataGetter.FileNamesData myNamesData; - @NotNull private final Stack myPaths; - @NotNull private final Set myMatchingCommits; - private boolean myWasChanged; + @NotNull private final Stack myPaths = new Stack<>(); + @NotNull private final BitSetFlags myVisibilityBuffer; // a reusable buffer for bfs + @NotNull private final Map myPathsForCommits = ContainerUtil.newHashMap(); + @NotNull private final Set myExcluded = ContainerUtil.newHashSet(); - public FileHistoryRefiner(@NotNull VisibleGraph visibleGraph, + public FileHistoryRefiner(@NotNull VisibleGraphImpl visibleGraph, + @NotNull LinearGraph permanentGraph, @NotNull IndexDataGetter.FileNamesData namesData) { myVisibleGraph = visibleGraph; + myPermanentGraph = LinearGraphUtils.asLiteLinearGraph(permanentGraph); + myLinearVisibleGraph = LinearGraphUtils.asLiteLinearGraph(myVisibleGraph.getLinearGraph()); myNamesData = namesData; - myPaths = new Stack<>(); - myMatchingCommits = ContainerUtil.newHashSet(); - myWasChanged = false; + myVisibilityBuffer = new BitSetFlags(myPermanentGraph.nodesCount()); } - public boolean refine(@NotNull LinearGraph graph, int row, @NotNull FilePath startPath) { + public boolean refine(int row, @NotNull FilePath startPath) { myPaths.push(startPath); - DfsUtil.walk(LinearGraphUtils.asLiteLinearGraph(graph), row, this); - return myWasChanged; + DfsUtil.walk(myLinearVisibleGraph, row, this); + + for (int commit : myPathsForCommits.keySet()) { + FilePath path = myPathsForCommits.get(commit); + if (path != null) { + if (!myNamesData.affects(commit, path)) myExcluded.add(commit); + if (myNamesData.isTrivialMerge(commit, path)) myExcluded.add(commit); + } + } + + myExcluded.forEach(myPathsForCommits::remove); + return !myExcluded.isEmpty(); } @NotNull - public Set getMatchingCommits() { - return myMatchingCommits; + public Map getPathsForCommits() { + return myPathsForCommits; } @Override - public void enterNode(int node) { - FilePath currentPath = myPaths.peek(); - Integer commit = myVisibleGraph.getRowInfo(node).getCommit(); + public void enterNode(int node, int previousNode) { + FilePath previousPath = myPaths.peek(); + RowInfo currentRowInfo = myVisibleGraph.getRowInfo(node); + int currentCommit = currentRowInfo.getCommit(); + int currentNodeId = ((VisibleGraphImpl.RowInfoImpl)currentRowInfo).getNodeId(); - FilePath previousPath = myNamesData.getPreviousPath(commit, currentPath); - if (previousPath != null) { - myMatchingCommits.add(commit); + if (previousNode == DfsUtil.NextNode.NODE_NOT_FOUND) { + myPathsForCommits.put(currentCommit, previousPath); myPaths.push(previousPath); - myNamesData.retain(commit, currentPath, previousPath); } else { - myNamesData.remove(commit); - myWasChanged = true; + FilePath currentPath; + RowInfo previousRowInfo = myVisibleGraph.getRowInfo(previousNode); + int previousCommit = previousRowInfo.getCommit(); + int previousNodeId = ((VisibleGraphImpl.RowInfoImpl)previousRowInfo).getNodeId(); + + // checking which node is the parent and which is the child + if (myLinearVisibleGraph.getNodes(node, LiteLinearGraph.NodeFilter.DOWN).contains(previousNode)) { + // since in reality there is no edge between the nodes, but the whole path, we need to know, which parent is affected by this path + int parentIndex = BfsUtil.getCorrespondingParentIndex(myPermanentGraph, currentNodeId, previousNodeId, myVisibilityBuffer); + currentPath = myNamesData.getPathInChildRevision(currentCommit, parentIndex, previousPath); + } + else { + int parentIndex = BfsUtil.getCorrespondingParentIndex(myPermanentGraph, previousNodeId, currentNodeId, myVisibilityBuffer); + currentPath = myNamesData.getPathInParentRevision(previousCommit, parentIndex, previousPath); + } + + myPathsForCommits.put(currentCommit, currentPath); + if (currentPath != null) myPaths.push(currentPath); } } @Override public void exitNode(int node) { Integer commit = myVisibleGraph.getRowInfo(node).getCommit(); - if (myMatchingCommits.contains(commit)) { + if (myPathsForCommits.containsKey(commit) && myPathsForCommits.get(commit) != null) { myPaths.pop(); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java index 9c4c503ef56b..dade82b4dffa 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java @@ -50,10 +50,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; -import java.util.Arrays; -import java.util.Collections; +import java.util.*; import java.util.List; -import java.util.Set; import static com.intellij.util.ObjectUtils.chooseNotNull; import static com.intellij.util.ObjectUtils.notNull; @@ -161,8 +159,8 @@ public class FileHistoryUi extends AbstractVcsLogUi { int commitIndex = myLogData.getStorage().getCommitIndex(details.getId(), details.getRoot()); Set names; if (myVisiblePack instanceof FileHistoryVisiblePack) { - IndexDataGetter.FileNamesData namesData = ((FileHistoryVisiblePack)myVisiblePack).getNamesData(); - names = namesData.getAffectedPaths(commitIndex); + Map namesData = ((FileHistoryVisiblePack)myVisiblePack).getNamesData(); + names = Collections.singleton(namesData.get(commitIndex)); } else { names = myIndexDataGetter.getFileNames(myPath, commitIndex); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryVisiblePack.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryVisiblePack.java index b541cd1401d5..91a8c1315ba1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryVisiblePack.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryVisiblePack.java @@ -15,27 +15,29 @@ */ package com.intellij.vcs.log.history; +import com.intellij.openapi.vcs.FilePath; import com.intellij.vcs.log.VcsLogFilterCollection; import com.intellij.vcs.log.data.DataPackBase; -import com.intellij.vcs.log.data.index.IndexDataGetter; import com.intellij.vcs.log.graph.VisibleGraph; import com.intellij.vcs.log.visible.VisiblePack; import org.jetbrains.annotations.NotNull; +import java.util.Map; + public class FileHistoryVisiblePack extends VisiblePack { - @NotNull private final IndexDataGetter.FileNamesData myNamesData; + @NotNull private final Map myNamesData; public FileHistoryVisiblePack(@NotNull DataPackBase dataPack, @NotNull VisibleGraph graph, boolean canRequestMore, @NotNull VcsLogFilterCollection filters, - @NotNull IndexDataGetter.FileNamesData namesData) { + @NotNull Map namesData) { super(dataPack, graph, canRequestMore, filters); myNamesData = namesData; } @NotNull - public IndexDataGetter.FileNamesData getNamesData() { + public Map getNamesData() { return myNamesData; } }