diff --git a/plugins/git4idea/shared/BUILD.bazel b/plugins/git4idea/shared/BUILD.bazel
index 555d2623e15f..c61d5654c698 100644
--- a/plugins/git4idea/shared/BUILD.bazel
+++ b/plugins/git4idea/shared/BUILD.bazel
@@ -25,6 +25,7 @@ jvm_library(
"//platform/dvcs-impl/shared",
"//platform/core-ui",
"//platform/util/coroutines",
+ "@lib//:caffeine",
],
runtime_deps = [":shared_resources"]
)
diff --git a/plugins/git4idea/shared/intellij.vcs.git.shared.iml b/plugins/git4idea/shared/intellij.vcs.git.shared.iml
index 883dffec8147..1792af2c3733 100644
--- a/plugins/git4idea/shared/intellij.vcs.git.shared.iml
+++ b/plugins/git4idea/shared/intellij.vcs.git.shared.iml
@@ -46,5 +46,6 @@
+
\ No newline at end of file
diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchesClippedNamesCache.kt b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/branch/GitBranchesClippedNamesCache.kt
similarity index 86%
rename from plugins/git4idea/src/git4idea/ui/branch/GitBranchesClippedNamesCache.kt
rename to plugins/git4idea/shared/src/com/intellij/vcs/git/shared/branch/GitBranchesClippedNamesCache.kt
index a9dd8f539b25..d9cf4905b617 100644
--- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchesClippedNamesCache.kt
+++ b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/branch/GitBranchesClippedNamesCache.kt
@@ -1,5 +1,5 @@
-// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
-package git4idea.ui.branch
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.vcs.git.shared.branch
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
@@ -7,7 +7,7 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
-import git4idea.ui.branch.GitBranchPopupActions.truncateBranchName
+import com.intellij.vcs.git.shared.ui.GitBranchPresentation
import java.util.concurrent.TimeUnit
@Service(Service.Level.PROJECT)
@@ -42,7 +42,7 @@ class GitBranchesClippedNamesCache(private val project: Project) : Disposable {
}
private fun truncateBranchName(branchName: String, maxBranchNameLength: Int): ClippedBranch {
- return ClippedBranch(truncateBranchName(project, branchName, maxBranchNameLength, 0, 0), maxBranchNameLength)
+ return ClippedBranch(GitBranchPresentation.truncateBranchName(project, branchName, maxBranchNameLength, 0, 0), maxBranchNameLength)
}
fun clear() = cache.invalidateAll()
@@ -55,4 +55,4 @@ class GitBranchesClippedNamesCache(private val project: Project) : Disposable {
private const val CACHE_MAX_SIZE = 1_000L
private const val CACHE_ACCESS_EXPIRATION_MIN = 5L
}
-}
+}
\ No newline at end of file
diff --git a/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/ui/GitBranchPresentation.java b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/ui/GitBranchPresentation.java
new file mode 100644
index 000000000000..b8ecc6b7ad00
--- /dev/null
+++ b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/ui/GitBranchPresentation.java
@@ -0,0 +1,91 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.vcs.git.shared.ui;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.NlsSafe;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vcs.IssueNavigationConfiguration;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+@ApiStatus.Internal
+public final class GitBranchPresentation {
+ public static final int BRANCH_NAME_LENGTH_DELTA = 4;
+ public static final int BRANCH_NAME_SUFFIX_LENGTH = 5;
+
+ public static @NlsSafe @NotNull String truncateBranchName(@NotNull Project project,
+ @NotNull @NlsSafe String branchName,
+ int maxBranchNameLength) {
+ return truncateBranchName(project, branchName, maxBranchNameLength, BRANCH_NAME_SUFFIX_LENGTH, BRANCH_NAME_LENGTH_DELTA);
+ }
+
+ public static @NlsSafe @NotNull String truncateBranchName(@NotNull Project project, @NotNull @NlsSafe String branchName,
+ int maxBranchNameLength, int suffixLength, int delta) {
+ int branchNameLength = branchName.length();
+
+ if (branchNameLength <= maxBranchNameLength + delta) {
+ return branchName;
+ }
+
+ IssueNavigationConfiguration issueNavigationConfiguration = IssueNavigationConfiguration.getInstance(project);
+ List issueMatches = issueNavigationConfiguration.findIssueLinks(branchName);
+ int affectedMaxBranchNameLength = maxBranchNameLength - StringUtil.ELLIPSIS.length();
+ if (!issueMatches.isEmpty()) {
+ // never truncate the first occurrence of the issue id
+ IssueNavigationConfiguration.LinkMatch firstMatch = issueMatches.get(0);
+ TextRange firstMatchRange = firstMatch.getRange();
+ return truncateAndSaveIssueId(firstMatchRange, branchName, affectedMaxBranchNameLength, suffixLength, delta);
+ }
+
+ if (affectedMaxBranchNameLength - suffixLength - StringUtil.ELLIPSIS.length() < 0) {
+ return branchName;
+ }
+
+ return StringUtil.shortenTextWithEllipsis(branchName, affectedMaxBranchNameLength, suffixLength, true);
+ }
+
+ private static @NlsSafe @NotNull String truncateAndSaveIssueId(@NotNull TextRange issueIdRange,
+ @NotNull String branchName,
+ int maxBranchNameLength,
+ int suffixLength, int delta) {
+ String truncatedByDefault = StringUtil.shortenTextWithEllipsis(branchName,
+ maxBranchNameLength,
+ suffixLength, true);
+ String issueId = issueIdRange.substring(branchName);
+
+ if (truncatedByDefault.contains(issueId)) return truncatedByDefault;
+
+ try {
+ int branchNameLength = branchName.length();
+ int endOffset = issueIdRange.getEndOffset();
+ int startOffset = issueIdRange.getStartOffset();
+
+ // suffix intersects with the issue id
+ if (endOffset >= branchNameLength - suffixLength - delta) {
+ return StringUtil.shortenTextWithEllipsis(branchName,
+ maxBranchNameLength,
+ branchNameLength - startOffset, true);
+ }
+
+ String suffix = branchName.substring(branchNameLength - suffixLength);
+ int prefixLength = maxBranchNameLength - suffixLength - issueId.length();
+
+ String prefixAndIssue;
+ if (Math.abs(startOffset - prefixLength) <= delta) {
+ prefixAndIssue = branchName.substring(0, endOffset);
+ }
+ else {
+ String prefix = branchName.substring(0, prefixLength);
+ prefixAndIssue = prefix + StringUtil.ELLIPSIS + issueId;
+ }
+
+ return prefixAndIssue + StringUtil.ELLIPSIS + suffix;
+ }
+ catch (Throwable e) {
+ return truncatedByDefault;
+ }
+ }
+}
diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java
index 966b7ea4039a..3bf1427e50a0 100644
--- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java
+++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java
@@ -12,11 +12,10 @@ import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
-import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vcs.IssueNavigationConfiguration;
import com.intellij.util.concurrency.annotations.RequiresEdt;
import com.intellij.util.ui.EmptyIcon;
+import com.intellij.vcs.git.shared.ui.GitBranchPresentation;
import git4idea.*;
import git4idea.branch.GitBranchIncomingOutgoingManager;
import git4idea.branch.GitBrancher;
@@ -48,8 +47,6 @@ public final class GitBranchPopupActions {
public static final @NonNls String EXPERIMENTAL_BRANCH_POPUP_ACTION_GROUP = "Git.Experimental.Branch.Popup.Actions";
private static final int MAX_BRANCH_NAME_LENGTH = 40;
- public static final int BRANCH_NAME_LENGTH_DELTA = 4;
- public static final int BRANCH_NAME_SUFFIX_LENGTH = 5;
private GitBranchPopupActions() { }
@@ -95,75 +92,7 @@ public final class GitBranchPopupActions {
}
public static @NlsSafe @NotNull String truncateBranchName(@NotNull @NlsSafe String branchName, @NotNull Project project) {
- return truncateBranchName(project, branchName,
- MAX_BRANCH_NAME_LENGTH, BRANCH_NAME_SUFFIX_LENGTH, BRANCH_NAME_LENGTH_DELTA);
- }
-
- public static @NlsSafe @NotNull String truncateBranchName(@NotNull Project project, @NotNull @NlsSafe String branchName,
- int maxBranchNameLength, int suffixLength, int delta) {
- int branchNameLength = branchName.length();
-
- if (branchNameLength <= maxBranchNameLength + delta) {
- return branchName;
- }
-
- IssueNavigationConfiguration issueNavigationConfiguration = IssueNavigationConfiguration.getInstance(project);
- List issueMatches = issueNavigationConfiguration.findIssueLinks(branchName);
- int affectedMaxBranchNameLength = maxBranchNameLength - StringUtil.ELLIPSIS.length();
- if (!issueMatches.isEmpty()) {
- // never truncate the first occurrence of the issue id
- IssueNavigationConfiguration.LinkMatch firstMatch = issueMatches.get(0);
- TextRange firstMatchRange = firstMatch.getRange();
- return truncateAndSaveIssueId(firstMatchRange, branchName, affectedMaxBranchNameLength, suffixLength, delta);
- }
-
- if (affectedMaxBranchNameLength - suffixLength - StringUtil.ELLIPSIS.length() < 0) {
- return branchName;
- }
-
- return StringUtil.shortenTextWithEllipsis(branchName, affectedMaxBranchNameLength, suffixLength, true);
- }
-
- private static @NlsSafe @NotNull String truncateAndSaveIssueId(@NotNull TextRange issueIdRange,
- @NotNull String branchName,
- int maxBranchNameLength,
- int suffixLength, int delta) {
- String truncatedByDefault = StringUtil.shortenTextWithEllipsis(branchName,
- maxBranchNameLength,
- suffixLength, true);
- String issueId = issueIdRange.substring(branchName);
-
- if (truncatedByDefault.contains(issueId)) return truncatedByDefault;
-
- try {
- int branchNameLength = branchName.length();
- int endOffset = issueIdRange.getEndOffset();
- int startOffset = issueIdRange.getStartOffset();
-
- // suffix intersects with the issue id
- if (endOffset >= branchNameLength - suffixLength - delta) {
- return StringUtil.shortenTextWithEllipsis(branchName,
- maxBranchNameLength,
- branchNameLength - startOffset, true);
- }
-
- String suffix = branchName.substring(branchNameLength - suffixLength);
- int prefixLength = maxBranchNameLength - suffixLength - issueId.length();
-
- String prefixAndIssue;
- if (Math.abs(startOffset - prefixLength) <= delta) {
- prefixAndIssue = branchName.substring(0, endOffset);
- }
- else {
- String prefix = branchName.substring(0, prefixLength);
- prefixAndIssue = prefix + StringUtil.ELLIPSIS + issueId;
- }
-
- return prefixAndIssue + StringUtil.ELLIPSIS + suffix;
- }
- catch (Throwable e) {
- return truncatedByDefault;
- }
+ return GitBranchPresentation.truncateBranchName(project, branchName, MAX_BRANCH_NAME_LENGTH);
}
private static @NlsSafe @NotNull String wrapWithQuotes(@NlsSafe @NotNull String branchName) {
diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt b/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt
index 4ae4dd22e709..a82d0291b5fa 100644
--- a/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt
+++ b/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt
@@ -11,6 +11,7 @@ import com.intellij.openapi.util.text.HtmlBuilder
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.messages.Topic
import com.intellij.vcs.git.shared.branch.calcTooltip
+import com.intellij.vcs.git.shared.ui.GitBranchPresentation
import git4idea.GitTag
import git4idea.GitUtil
import git4idea.GitVcs
@@ -84,10 +85,7 @@ private fun getDefaultPresentation(repository: GitRepository): GitCurrentBranchP
private fun calcText(repository: GitRepository): @NlsSafe String =
StringUtil.escapeMnemonics(GitBranchUtil.getDisplayableBranchText(repository) { branchName ->
- GitBranchPopupActions.truncateBranchName(repository.project, branchName,
- GitToolbarWidgetAction.BRANCH_NAME_MAX_LENGTH,
- GitBranchPopupActions.BRANCH_NAME_SUFFIX_LENGTH,
- GitBranchPopupActions.BRANCH_NAME_LENGTH_DELTA)
+ GitBranchPresentation.truncateBranchName(repository.project, branchName, GitToolbarWidgetAction.BRANCH_NAME_MAX_LENGTH)
})
private fun GitRepository.calcIcon(): Icon? {
diff --git a/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt b/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt
index 363263e84ce6..1c0529ec5fcf 100644
--- a/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt
+++ b/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt
@@ -17,11 +17,11 @@ import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UpdateScaleHelper
import com.intellij.util.ui.components.BorderLayoutPanel
import com.intellij.util.ui.tree.TreeUtil
+import com.intellij.vcs.git.shared.branch.GitBranchesClippedNamesCache
import com.intellij.vcs.git.shared.repo.GitRepositoryFrontendModel
import com.intellij.vcs.git.shared.ui.GitBranchesTreeIconProvider
import git4idea.GitBranch
import git4idea.GitReference
-import git4idea.ui.branch.GitBranchesClippedNamesCache
import git4idea.ui.branch.popup.GitBranchesTreePopupBase
import git4idea.ui.branch.popup.GitBranchesTreePopupStepBase
import git4idea.ui.branch.tree.GitBranchesTreeModel.RefUnderRepository