diff --git a/platform/vcs-log/api/resources/messages/VcsLogBundle.properties b/platform/vcs-log/api/resources/messages/VcsLogBundle.properties
index 7cfac4bcf3f4..4584ced49663 100644
--- a/platform/vcs-log/api/resources/messages/VcsLogBundle.properties
+++ b/platform/vcs-log/api/resources/messages/VcsLogBundle.properties
@@ -95,6 +95,8 @@ action.Vcs.Log.CompareRevisions.text=Compare Versions
action.Vcs.Log.CompareRevisions.description=Compare selected versions
action.Vcs.Log.ResumeIndexing.text=Resume Indexing
action.Vcs.Log.ResumeIndexing.description=Indexing was paused as it took longer than expected. Resume.
+action.Vcs.Log.SelectInLog.text=Show in Vcs Log
+action.Vcs.Log.SelectInLog.text.template=Show in {0} Log
group.Vcs.Log.PresentationSettings.text=View Options
group.Vcs.Log.PresentationSettings.description=Configure presentation of the Log
group.Vcs.Log.ChangesBrowser.PresentationSettings.text=View Options
diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml
index 8e65eb96393a..5ae7ff764411 100644
--- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml
+++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml
@@ -157,6 +157,9 @@
+
+
+
+
{
+ return getLogProviders(project).values.mapTo(mutableSetOf()) { it.supportedVcs }
+ }
+
@JvmStatic
fun getInstance(project: Project): VcsProjectLog = project.service()
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowCommitInLogAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowCommitInLogAction.java
new file mode 100644
index 000000000000..b493f77e2cd0
--- /dev/null
+++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowCommitInLogAction.java
@@ -0,0 +1,114 @@
+// 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.ui.actions;
+
+import com.intellij.openapi.actionSystem.ActionUpdateThread;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.PerformInBackgroundOption;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.NlsActions;
+import com.intellij.openapi.vcs.AbstractVcs;
+import com.intellij.openapi.vcs.VcsDataKeys;
+import com.intellij.openapi.vcs.VcsKey;
+import com.intellij.openapi.vcs.history.VcsFileRevision;
+import com.intellij.openapi.vcs.history.VcsRevisionNumber;
+import com.intellij.vcs.log.Hash;
+import com.intellij.vcs.log.VcsLogBundle;
+import com.intellij.vcs.log.impl.HashImpl;
+import com.intellij.vcs.log.impl.VcsLogContentUtil;
+import com.intellij.vcs.log.impl.VcsLogNavigationUtil;
+import com.intellij.vcs.log.impl.VcsProjectLog;
+import com.intellij.vcs.log.ui.VcsLogUiEx;
+import com.intellij.vcsUtil.VcsUtil;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+public class ShowCommitInLogAction extends DumbAwareAction {
+ private static final Logger LOG = Logger.getInstance(ShowCommitInLogAction.class);
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent event) {
+ Project project = event.getData(CommonDataKeys.PROJECT);
+ if (project == null) return;
+ VcsRevisionNumber revision = getRevisionNumber(event);
+ if (revision == null) return;
+
+ jumpToRevision(project, HashImpl.build(revision.asString()));
+ }
+
+ protected @Nullable VcsRevisionNumber getRevisionNumber(@NotNull AnActionEvent event) {
+ VcsRevisionNumber revision = event.getData(VcsDataKeys.VCS_REVISION_NUMBER);
+ if (revision == null) {
+ VcsFileRevision fileRevision = event.getData(VcsDataKeys.VCS_FILE_REVISION);
+ if (fileRevision != null) {
+ revision = fileRevision.getRevisionNumber();
+ }
+ }
+ return revision;
+ }
+
+ protected @Nullable VcsKey getVcsKey(@NotNull AnActionEvent event) {
+ return event.getData(VcsDataKeys.VCS);
+ }
+
+ @Override
+ public @NotNull ActionUpdateThread getActionUpdateThread() {
+ return ActionUpdateThread.BGT;
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ Project project = e.getProject();
+ VcsKey vcsKey = getVcsKey(e);
+ e.getPresentation().setEnabled(project != null && getRevisionNumber(e) != null && vcsKey != null
+ && VcsProjectLog.getSupportedVcs(project).contains(vcsKey));
+ e.getPresentation().setText(getActionText(project, vcsKey));
+ }
+
+ private static @NlsActions.ActionText @NotNull String getActionText(@Nullable Project project, @Nullable VcsKey vcsKey) {
+ if (project != null && vcsKey != null) {
+ AbstractVcs vcs = VcsUtil.findVcsByKey(project, vcsKey);
+ if (vcs != null) return VcsLogBundle.message("action.Vcs.Log.SelectInLog.text.template", vcs.getDisplayName());
+ }
+ return VcsLogBundle.message("action.Vcs.Log.SelectInLog.text");
+ }
+
+ @ApiStatus.Internal
+ public static void jumpToRevision(@NotNull Project project, @NotNull Hash hash) {
+ VcsLogContentUtil.runInMainLog(project, logUi -> jumpToRevisionUnderProgress(project, logUi, hash));
+ }
+
+ private static void jumpToRevisionUnderProgress(@NotNull Project project,
+ @NotNull VcsLogUiEx logUi,
+ @NotNull Hash hash) {
+ Future future = VcsLogNavigationUtil.jumpToHash(logUi, hash.asString(), false, true);
+ if (!future.isDone()) {
+ ProgressManager.getInstance().run(new Task.Backgroundable(project,
+ VcsLogBundle.message("vcs.log.show.commit.in.log.process", hash.asString()),
+ false/*can not cancel*/,
+ PerformInBackgroundOption.ALWAYS_BACKGROUND) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ try {
+ future.get();
+ }
+ catch (CancellationException | InterruptedException ignored) {
+ }
+ catch (ExecutionException e) {
+ LOG.error(e);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/VcsLogUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/VcsLogUtil.java
index ccc301d2f8f6..d605b9213b4b 100644
--- a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/VcsLogUtil.java
+++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/VcsLogUtil.java
@@ -337,6 +337,10 @@ public final class VcsLogUtil {
@NotNull Collection extends VcsLogProvider> logProviders) {
Set vcs = ContainerUtil.map2SetNotNull(logProviders,
provider -> VcsUtil.findVcsByKey(project, provider.getSupportedVcs()));
+ return getVcsDisplayName(vcs);
+ }
+
+ public static @Nls @NotNull String getVcsDisplayName(@NotNull Set vcs) {
if (vcs.size() != 1) return VcsLogBundle.message("vcs");
return Objects.requireNonNull(getFirstItem(vcs)).getDisplayName();
}
diff --git a/plugins/git4idea/resources/META-INF/plugin.xml b/plugins/git4idea/resources/META-INF/plugin.xml
index 96a044852567..8b1e93fc766e 100644
--- a/plugins/git4idea/resources/META-INF/plugin.xml
+++ b/plugins/git4idea/resources/META-INF/plugin.xml
@@ -38,9 +38,6 @@
-
-
-
@@ -289,8 +286,6 @@