diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/HashImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/HashImpl.java index 6e4b060f1df6..3debe53fda31 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/HashImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/HashImpl.java @@ -31,6 +31,7 @@ public class HashImpl implements Hash { private static final int BASE = 16; private static final int SHORT_HASH_LENGTH = 7; + public static final int FULL_HASH_LENGTH = 40; @NotNull private final byte[] myData; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/CommitPresentationUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/CommitPresentationUtil.java index d9d2519fcb5c..ebc155f165bb 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/CommitPresentationUtil.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/CommitPresentationUtil.java @@ -4,10 +4,12 @@ package com.intellij.vcs.log.ui.frame; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.ui.FontUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ColorUtil; import com.intellij.ui.JBColor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.MultiMap; import com.intellij.util.text.DateFormatUtil; import com.intellij.vcs.log.CommitId; import com.intellij.vcs.log.VcsFullCommitDetails; @@ -18,15 +20,16 @@ import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; import java.awt.*; +import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer.formatTextWithLinks; import static com.intellij.openapi.vcs.history.VcsHistoryUtil.getCommitDetailsFont; +import static com.intellij.util.containers.ContainerUtil.getFirstItem; public class CommitPresentationUtil { @NotNull private static final Pattern HASH_PATTERN = Pattern.compile("[0-9a-f]{7,40}", Pattern.CASE_INSENSITIVE); @@ -75,15 +78,14 @@ public class CommitPresentationUtil { } @NotNull - private static String replaceHashes(@NotNull String s, @NotNull Map resolvedHashes) { + private static String replaceHashes(@NotNull String s, @NotNull Set resolvedHashes) { Matcher matcher = HASH_PATTERN.matcher(s); StringBuffer result = new StringBuffer(); while (matcher.find()) { String hash = matcher.group(); - CommitId commitId = resolvedHashes.get(hash); - if (commitId != null) { + if (resolvedHashes.contains(hash)) { hash = "" + hash + ""; } matcher.appendReplacement(result, hash); @@ -112,7 +114,7 @@ public class CommitPresentationUtil { @NotNull String subject, @NotNull String description, @NotNull String hashAndAuthor, - @NotNull Map resolvedHashes) { + @NotNull Set resolvedHashes) { Convertor convertor = s -> replaceHashes(s, resolvedHashes); return "" + getHtmlWithFonts(escapeMultipleSpaces(formatTextWithLinks(project, subject, convertor)), Font.BOLD) + @@ -268,13 +270,13 @@ public class CommitPresentationUtil { String description = fullMessage.substring(subject.length()); Set unresolvedHashesForCommit = findHashes(project, subject, description); - String text = formatCommitText(project, subject, description, hashAndAuthor, ContainerUtil.newHashMap()); + String text = formatCommitText(project, subject, description, hashAndAuthor, Collections.emptySet()); if (unresolvedHashesForCommit.isEmpty()) { - return new CommitPresentation(text, Collections.emptyMap()); + return new CommitPresentation(text, commit.getRoot(), MultiMap.empty()); } unresolvedHashes.addAll(unresolvedHashesForCommit); - return new UnresolvedPresentation(project, subject, description, hashAndAuthor, text); + return new UnresolvedPresentation(project, commit.getRoot(), subject, description, hashAndAuthor, text); } private static class UnresolvedPresentation extends CommitPresentation { @@ -284,11 +286,12 @@ public class CommitPresentationUtil { private final String myHashAndAuthor; public UnresolvedPresentation(@NotNull Project project, + @NotNull VirtualFile root, @NotNull String subject, @NotNull String description, @NotNull String hashAndAuthor, @NotNull String text) { - super(text, Collections.emptyMap()); + super(text, root, MultiMap.empty()); myProject = project; mySubject = subject; myDescription = description; @@ -296,9 +299,9 @@ public class CommitPresentationUtil { } @NotNull - public CommitPresentation resolve(@NotNull Map resolvedHashes) { - String text = formatCommitText(myProject, mySubject, myDescription, myHashAndAuthor, resolvedHashes); - return new CommitPresentation(text, resolvedHashes); + public CommitPresentation resolve(@NotNull MultiMap resolvedHashes) { + String text = formatCommitText(myProject, mySubject, myDescription, myHashAndAuthor, resolvedHashes.keySet()); + return new CommitPresentation(text, myRoot, resolvedHashes); } @Override @@ -309,10 +312,13 @@ public class CommitPresentationUtil { public static class CommitPresentation { @NotNull protected final String myText; - @NotNull private final Map myResolvedHashes; + @NotNull protected final VirtualFile myRoot; + @NotNull private final MultiMap myResolvedHashes; - public CommitPresentation(@NotNull String text, @NotNull Map resolvedHashes) { + public CommitPresentation(@NotNull String text, + @NotNull VirtualFile root, @NotNull MultiMap resolvedHashes) { myText = text; + myRoot = root; myResolvedHashes = resolvedHashes; } @@ -325,11 +331,18 @@ public class CommitPresentationUtil { public CommitId parseTargetCommit(@NotNull HyperlinkEvent e) { if (!e.getDescription().startsWith(GO_TO_HASH)) return null; String hash = e.getDescription().substring(GO_TO_HASH.length()); - return myResolvedHashes.get(hash); + Collection ids = myResolvedHashes.get(hash); + if (ids.size() <= 1) return getFirstItem(ids); + for (CommitId id : ids) { + if (myRoot.equals(id.getRoot())) { + return id; + } + } + return getFirstItem(ids); } @NotNull - public CommitPresentation resolve(@NotNull Map resolvedHashes) { + public CommitPresentation resolve(@NotNull MultiMap resolvedHashes) { return this; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java index 4b5658a86550..a14e6b21ada7 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java @@ -32,18 +32,22 @@ import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.history.VcsHistoryUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.SeparatorComponent; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBLoadingPanel; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.StatusText; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.CommitId; +import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.data.VcsLogData; +import com.intellij.vcs.log.impl.HashImpl; import com.intellij.vcs.log.ui.VcsLogColorManager; import com.intellij.vcs.log.ui.frame.CommitPresentationUtil.CommitPresentation; import com.intellij.vcs.log.ui.table.CommitSelectionListener; @@ -55,8 +59,10 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.Set; import static com.intellij.vcs.log.ui.frame.CommitPresentationUtil.buildPresentation; @@ -182,20 +188,32 @@ public class DetailsPanel extends JPanel implements EditorColorsListener, Dispos @NotNull Condition expired) { if (!unResolvedHashes.isEmpty()) { myResolveIndicator = BackgroundTaskUtil.executeOnPooledThread(this, () -> { - Map resolvedHashes = ContainerUtil.newHashMap(); - myLogData.getStorage().iterateCommits(commitId -> { - Set found = ContainerUtil.newHashSet(); - for (String hashString : unResolvedHashes) { + MultiMap resolvedHashes = MultiMap.createSmart(); - if (StringUtil.startsWithIgnoreCase(commitId.getHash().asString(), hashString)) { - resolvedHashes.put(hashString, commitId); - found.add(hashString); - // do not break, check all hashes (we can have several substrings of the same hash) + Set fullHashes = + ContainerUtil.newHashSet(ContainerUtil.filter(unResolvedHashes, h -> h.length() == HashImpl.FULL_HASH_LENGTH)); + for (String fullHash : fullHashes) { + Hash hash = HashImpl.build(fullHash); + for (VirtualFile root : myLogData.getRoots()) { + CommitId id = new CommitId(hash, root); + if (myLogData.getStorage().containsCommit(id)) { + resolvedHashes.putValue(fullHash, id); } } - unResolvedHashes.removeAll(found); - return unResolvedHashes.isEmpty(); - }); + } + unResolvedHashes.removeAll(fullHashes); + + if (!unResolvedHashes.isEmpty()) { + myLogData.getStorage().iterateCommits(commitId -> { + + for (String hashString : unResolvedHashes) { + if (StringUtil.startsWithIgnoreCase(commitId.getHash().asString(), hashString)) { + resolvedHashes.putValue(hashString, commitId); + } + } + return false; + }); + } List resolvedPresentations = ContainerUtil.map2List(presentations, presentation -> presentation.resolve(resolvedHashes));