mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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.
This commit is contained in:
@@ -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;
|
||||
|
||||
+29
-16
@@ -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<String, CommitId> resolvedHashes) {
|
||||
private static String replaceHashes(@NotNull String s, @NotNull Set<String> 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 = "<a href=\"" + GO_TO_HASH + hash + "\">" + hash + "</a>";
|
||||
}
|
||||
matcher.appendReplacement(result, hash);
|
||||
@@ -112,7 +114,7 @@ public class CommitPresentationUtil {
|
||||
@NotNull String subject,
|
||||
@NotNull String description,
|
||||
@NotNull String hashAndAuthor,
|
||||
@NotNull Map<String, CommitId> resolvedHashes) {
|
||||
@NotNull Set<String> resolvedHashes) {
|
||||
Convertor<String, String> convertor = s -> replaceHashes(s, resolvedHashes);
|
||||
return "<b>" +
|
||||
getHtmlWithFonts(escapeMultipleSpaces(formatTextWithLinks(project, subject, convertor)), Font.BOLD) +
|
||||
@@ -268,13 +270,13 @@ public class CommitPresentationUtil {
|
||||
String description = fullMessage.substring(subject.length());
|
||||
|
||||
Set<String> 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<String, CommitId> resolvedHashes) {
|
||||
String text = formatCommitText(myProject, mySubject, myDescription, myHashAndAuthor, resolvedHashes);
|
||||
return new CommitPresentation(text, resolvedHashes);
|
||||
public CommitPresentation resolve(@NotNull MultiMap<String, CommitId> 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<String, CommitId> myResolvedHashes;
|
||||
@NotNull protected final VirtualFile myRoot;
|
||||
@NotNull private final MultiMap<String, CommitId> myResolvedHashes;
|
||||
|
||||
public CommitPresentation(@NotNull String text, @NotNull Map<String, CommitId> resolvedHashes) {
|
||||
public CommitPresentation(@NotNull String text,
|
||||
@NotNull VirtualFile root, @NotNull MultiMap<String, CommitId> 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<CommitId> 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<String, CommitId> resolvedHashes) {
|
||||
public CommitPresentation resolve(@NotNull MultiMap<String, CommitId> resolvedHashes) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Object> expired) {
|
||||
if (!unResolvedHashes.isEmpty()) {
|
||||
myResolveIndicator = BackgroundTaskUtil.executeOnPooledThread(this, () -> {
|
||||
Map<String, CommitId> resolvedHashes = ContainerUtil.newHashMap();
|
||||
myLogData.getStorage().iterateCommits(commitId -> {
|
||||
Set<String> found = ContainerUtil.newHashSet();
|
||||
for (String hashString : unResolvedHashes) {
|
||||
MultiMap<String, CommitId> 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<String> 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<CommitPresentation> resolvedPresentations = ContainerUtil.map2List(presentations,
|
||||
presentation -> presentation.resolve(resolvedHashes));
|
||||
|
||||
Reference in New Issue
Block a user