diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt index debe18104151..8ff9805d5977 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistory.kt @@ -2,7 +2,6 @@ package com.intellij.vcs.log.history import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.util.Ref import com.intellij.openapi.vcs.FilePath import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.Stack @@ -51,7 +50,7 @@ internal class FileHistoryBuilder(private val startCommit: Int?, val visibleLinearGraph = controller.compiledGraph val row = startCommit?.let { - findAncestorRowAffectingFile(startCommit, startPath, visibleLinearGraph, permanentGraphInfo, fileNamesData) + findAncestorRowAffectingFile(startCommit, visibleLinearGraph, permanentGraphInfo) } ?: 0 if (row >= 0) { val refiner = FileHistoryRefiner(visibleLinearGraph, permanentGraphInfo, fileNamesData) @@ -66,6 +65,14 @@ internal class FileHistoryBuilder(private val startCommit: Int?, return fileNamesData.buildPathsMap() } + private fun findAncestorRowAffectingFile(commitId: Int, + visibleLinearGraph: LinearGraph, + permanentGraphInfo: PermanentGraphInfo): Int { + return findVisibleAncestorRow(commitId, visibleLinearGraph, permanentGraphInfo) { nodeId -> + fileNamesData.affects(permanentGraphInfo.permanentCommitsInfo.getCommitId(nodeId), startPath) + } ?: -1 + } + companion object { private val LOG = Logger.getInstance(FileHistoryBuilder::class.java) } @@ -110,33 +117,6 @@ private fun hideTrivialMerge(collapsedGraph: CollapsedGraph, graph: LiteLinearGr } } -internal fun findAncestorRowAffectingFile(commitId: Int, - filePath: FilePath, - visibleLinearGraph: LinearGraph, - permanentGraphInfo: PermanentGraphInfo, - fileNamesData: FileNamesData): Int { - val resultNodeId = Ref() - - val commitsInfo = permanentGraphInfo.permanentCommitsInfo - val reachableNodes = ReachableNodes(LinearGraphUtils.asLiteLinearGraph(permanentGraphInfo.linearGraph)) - reachableNodes.walk(setOf(commitsInfo.getNodeId(commitId)), true) { currentNode -> - val id = commitsInfo.getCommitId(currentNode) - if (fileNamesData.affects(id, filePath)) { - resultNodeId.set(currentNode) - false // stop walk, we have found it - } - else { - true // continue walk - } - } - - if (!resultNodeId.isNull) { - return visibleLinearGraph.getNodeIndex(resultNodeId.get())!! - } - - return -1 -} - internal class FileHistoryRefiner(private val visibleLinearGraph: LinearGraph, permanentGraphInfo: PermanentGraphInfo, private val namesData: FileNamesData) : DfsUtil.NodeVisitor { 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 529b2d2a200e..959d0cca3283 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 @@ -33,12 +33,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ObjectUtils; import com.intellij.util.PairFunction; import com.intellij.util.containers.ContainerUtil; -import com.intellij.vcs.log.Hash; -import com.intellij.vcs.log.VcsFullCommitDetails; -import com.intellij.vcs.log.VcsLogFilterCollection; -import com.intellij.vcs.log.VcsLogFilterUi; -import com.intellij.vcs.log.data.LoadingDetails; -import com.intellij.vcs.log.data.VcsLogData; +import com.intellij.vcs.log.*; +import com.intellij.vcs.log.data.*; import com.intellij.vcs.log.data.index.IndexDataGetter; import com.intellij.vcs.log.impl.CommonUiProperties; import com.intellij.vcs.log.impl.VcsLogContentUtil; @@ -72,6 +68,7 @@ public class FileHistoryUi extends AbstractVcsLogUi { @NotNull private final FileHistoryFilterUi myFilterUi; @NotNull private final FilePath myPath; @Nullable private final Hash myRevision; + @NotNull private final VirtualFile myRoot; @NotNull private final FileHistoryPanel myFileHistoryPanel; @NotNull private final IndexDataGetter myIndexDataGetter; @NotNull private final MyPropertiesChangeListener myPropertiesChangeListener; @@ -88,6 +85,7 @@ public class FileHistoryUi extends AbstractVcsLogUi { myIndexDataGetter = ObjectUtils.assertNotNull(logData.getIndex().getDataGetter()); myRevision = revision; + myRoot = root; myFilterUi = new FileHistoryFilterUi(path, revision, root, uiProperties); myPath = path; myFileHistoryPanel = new FileHistoryPanel(this, logData, myVisiblePack, path); @@ -250,14 +248,20 @@ public class FileHistoryUi extends AbstractVcsLogUi { } } - @NotNull - public FilePath getPath() { - return myPath; + public void jumpToNearestCommit(@NotNull Hash hash) { + jumpTo(hash, (model, h) -> { + if (!myLogData.getStorage().containsCommit(new CommitId(h, myRoot))) return GraphTableModel.COMMIT_NOT_FOUND; + int commitIndex = myLogData.getCommitIndex(h, myRoot); + Integer rowIndex = myVisiblePack.getVisibleGraph().getVisibleRowIndex(commitIndex); + if (rowIndex == null) { + rowIndex = ReachableNodesUtilKt.findVisibleAncestorRow(commitIndex, myVisiblePack); + } + return rowIndex == null ? GraphTableModel.COMMIT_DOES_NOT_MATCH : rowIndex; + }, SettableFuture.create()); } - @Nullable - public Hash getRevision() { - return myRevision; + public boolean matches(@NotNull FilePath targetPath, @Nullable Hash targetRevision) { + return myPath.equals(targetPath) && Objects.equals(myRevision, targetRevision); } @NotNull diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/ReachableNodesUtil.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/ReachableNodesUtil.kt new file mode 100644 index 000000000000..3434a0662bb5 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/ReachableNodesUtil.kt @@ -0,0 +1,47 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.vcs.log.history + +import com.intellij.openapi.util.Ref +import com.intellij.vcs.log.data.DataPack +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.ReachableNodes +import com.intellij.vcs.log.graph.impl.facade.VisibleGraphImpl +import com.intellij.vcs.log.graph.utils.LinearGraphUtils +import com.intellij.vcs.log.visible.VisiblePack + +fun findMatchingAncestorNodeId(commitId: Int, permanentGraphInfo: PermanentGraphInfo, condition: (Int) -> Boolean): Int? { + val resultNodeId = Ref() + + val startNodeId = permanentGraphInfo.permanentCommitsInfo.getNodeId(commitId) + val reachableNodes = ReachableNodes(LinearGraphUtils.asLiteLinearGraph(permanentGraphInfo.linearGraph)) + reachableNodes.walk(setOf(startNodeId), true) { currentNodeId -> + if (condition(currentNodeId)) { + resultNodeId.set(currentNodeId) + false // stop walk, we have found it + } + else { + true // continue walk + } + } + return resultNodeId.get() +} + +fun findVisibleAncestorRow(commitId: Int, visiblePack: VisiblePack): Int? { + val dataPack = visiblePack.dataPack + val visibleGraph = visiblePack.visibleGraph + if (dataPack is DataPack && dataPack.permanentGraph is PermanentGraphInfo<*> && visibleGraph is VisibleGraphImpl) { + return findVisibleAncestorRow(commitId, visibleGraph.linearGraph, dataPack.permanentGraph as PermanentGraphInfo) { _ -> true } + } + return null +} + +fun findVisibleAncestorRow(commitId: Int, + visibleLinearGraph: LinearGraph, + permanentGraphInfo: PermanentGraphInfo, + condition: (Int) -> Boolean): Int? { + val nodeId = findMatchingAncestorNodeId(commitId, permanentGraphInfo) { nodeId -> + condition(nodeId) && visibleLinearGraph.getNodeIndex(nodeId) != null + } ?: return null + return visibleLinearGraph.getNodeIndex(nodeId) +} \ No newline at end of file diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/VcsLogFileHistoryProviderImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/VcsLogFileHistoryProviderImpl.java index f3f01d863652..71b3b5dec235 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/VcsLogFileHistoryProviderImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/VcsLogFileHistoryProviderImpl.java @@ -22,6 +22,7 @@ import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsLogFileHistoryProvider; +import com.intellij.vcs.log.VcsLogFilterCollection; import com.intellij.vcs.log.data.VcsLogData; import com.intellij.vcs.log.impl.HashImpl; import com.intellij.vcs.log.impl.VcsLogContentUtil; @@ -30,8 +31,6 @@ import com.intellij.vcs.log.impl.VcsProjectLog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Objects; - public class VcsLogFileHistoryProviderImpl implements VcsLogFileHistoryProvider { @NotNull public static final String TAB_NAME = "History"; @@ -52,12 +51,18 @@ public class VcsLogFileHistoryProviderImpl implements VcsLogFileHistoryProvider @Override public void showFileHistory(@NotNull Project project, @NotNull FilePath path, @Nullable String revisionNumber) { Hash hash = (revisionNumber != null) ? HashImpl.build(revisionNumber) : null; - if (!VcsLogContentUtil.findAndSelectContent(project, FileHistoryUi.class, - ui -> ui.getPath().equals(path) && Objects.equals(ui.getRevision(), hash))) { + FileHistoryUi fileHistoryUi = VcsLogContentUtil.findAndSelect(project, FileHistoryUi.class, + ui -> ui.matches(path, hash)); + if (fileHistoryUi == null) { VcsLogManager logManager = VcsProjectLog.getInstance(project).getLogManager(); assert logManager != null; String suffix = hash != null ? " (" + hash.toShortString() + ")" : ""; - VcsLogContentUtil.openLogTab(project, logManager, TAB_NAME, path.getName() + suffix, new FileHistoryUiFactory(path, hash), true); + fileHistoryUi = VcsLogContentUtil.openLogTab(project, logManager, TAB_NAME, path.getName() + suffix, + new FileHistoryUiFactory(path, hash), true); + } + + if (hash != null && fileHistoryUi.getFilterUi().getFilters().get(VcsLogFilterCollection.REVISION_FILTER) == null) { + fileHistoryUi.jumpToNearestCommit(hash); } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentUtil.java index 0c5bbaacbf0f..667e5f049a28 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentUtil.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentUtil.java @@ -65,6 +65,13 @@ public class VcsLogContentUtil { public static boolean findAndSelectContent(@NotNull Project project, @NotNull Class clazz, @NotNull Condition condition) { + return findAndSelect(project, clazz, condition) != null; + } + + @Nullable + public static U findAndSelect(@NotNull Project project, + @NotNull Class clazz, + @NotNull Condition condition) { ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.VCS); ContentManager manager = toolWindow.getContentManager(); @@ -76,10 +83,12 @@ public class VcsLogContentUtil { } return false; }); - if (component == null) return false; + if (component == null) return null; if (!toolWindow.isVisible()) toolWindow.activate(null); - return ContentUtilEx.selectContent(manager, component, true); + if (!ContentUtilEx.selectContent(manager, component, true)) return null; + //noinspection unchecked + return (U)getLogUi(component); } public static boolean selectLogUi(@NotNull Project project, @NotNull VcsLogUi ui) {