From d867dbfb0dfcb79a6f35c769d8236ad16c7a073b Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Fri, 19 Jan 2018 20:14:10 +0300 Subject: [PATCH] [vcs-log] take collision possibility into account while resolving hashes There could be several commits with the same prefix, in the same or in different repositories. In order to take that into account while resolving commits: 1. If a full commit hash is provided, check its existence in all repositories with containsCommit method. 2. If a prefix is provided, iterate and check all commits in all repositories. 3. On click select a commit in the same root, or just some commit when a commit in the same root is not available. --- .../com/intellij/vcs/log/impl/HashImpl.java | 1 + .../log/ui/frame/CommitPresentationUtil.java | 45 ++++++++++++------- .../vcs/log/ui/frame/DetailsPanel.java | 42 ++++++++++++----- 3 files changed, 60 insertions(+), 28 deletions(-) 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));