[git] Add stats collection for background remote checks (IJPL-75190)

GitOrigin-RevId: de9f50ccc7dbd9a9f58635d943d7e93bdda6945c
This commit is contained in:
Ilia.Shulgin
2026-02-13 13:37:36 +00:00
committed by intellij-monorepo-bot
parent ce8b8d56f8
commit ca69caaa3b
5 changed files with 65 additions and 23 deletions
@@ -10,6 +10,7 @@ import com.intellij.openapi.project.Project
import git4idea.actions.workingTree.GitWorkingTreeDialogData
import git4idea.branch.GitRebaseParams
import git4idea.commands.GitCommandResult
import git4idea.config.GitIncomingRemoteCheckStrategy
import git4idea.inMemory.rebase.InMemoryRebaseResult
import git4idea.merge.GitMergeOption
import git4idea.pull.GitPullOption
@@ -23,7 +24,7 @@ import git4idea.repo.GitRepository
internal object GitOperationsCollector : CounterUsagesCollector() {
override fun getGroup(): EventLogGroup = GROUP
private val GROUP: EventLogGroup = EventLogGroup(id = "git.operations", version = 10)
private val GROUP: EventLogGroup = EventLogGroup(id = "git.operations", version = 11)
internal val UPDATE_FORCE_PUSHED_BRANCH_ACTIVITY = GROUP.registerIdeActivity("update.force.pushed")
@@ -312,5 +313,18 @@ internal object GitOperationsCollector : CounterUsagesCollector() {
val trackedBranch = repository.getBranchTrackInfo(currentBranch.name)?.remoteBranch ?: return false
return trackedBranch == selectedBranch
}
//endregion
internal val REMOTE_CHECK_STRATEGY = EventFields.Enum<GitIncomingRemoteCheckStrategy>("remoteCheckStrategy")
private val REMOTE_INFO_SUCCESS = EventFields.Boolean("success")
private val REMOTE_INFO_REQUEST_EVENT = GROUP.registerVarargEvent(
"remote.info.request",
"Remote state requested in background",
REMOTE_CHECK_STRATEGY,
REMOTE_INFO_SUCCESS,
)
@JvmStatic
fun logRemoteInfoRequest(project: Project, strategy: GitIncomingRemoteCheckStrategy, success: Boolean) {
REMOTE_INFO_REQUEST_EVENT.log(project, REMOTE_CHECK_STRATEGY.with(strategy), REMOTE_INFO_SUCCESS.with(success))
}
}
@@ -29,12 +29,14 @@ import com.intellij.vcs.log.impl.VcsLogUiProperties
import com.intellij.vcs.log.impl.VcsProjectLog
import com.intellij.vcs.log.ui.MainVcsLogUi
import com.intellij.vcsUtil.VcsUtil
import git4idea.GitOperationsCollector.REMOTE_CHECK_STRATEGY
import git4idea.branch.GitBranchUtil
import git4idea.commands.Git
import git4idea.commands.GitCommand
import git4idea.commands.GitLineHandler
import git4idea.config.GitConfigUtil
import git4idea.config.GitExecutableManager
import git4idea.config.GitIncomingRemoteCheckStrategy
import git4idea.config.GitSaveChangesPolicy
import git4idea.config.GitVcsApplicationSettings
import git4idea.config.GitVcsSettings
@@ -57,7 +59,7 @@ import kotlin.io.path.isDirectory
import kotlin.io.path.isRegularFile
internal class GitStatisticsCollector : ProjectUsagesCollector() {
private val GROUP = EventLogGroup("git.configuration", 23)
private val GROUP = EventLogGroup("git.configuration", 24)
override fun getGroup(): EventLogGroup = GROUP
@@ -76,6 +78,7 @@ internal class GitStatisticsCollector : ProjectUsagesCollector() {
addIfDiffers(set, settings, defaultSettings, { it.syncSetting }, REPO_SYNC, REPO_SYNC_VALUE)
addIfDiffers(set, settings, defaultSettings, { it.updateMethod }, UPDATE_TYPE, UPDATE_TYPE_VALUE)
addIfDiffers(set, settings, defaultSettings, { it.saveChangesPolicy }, SAVE_POLICY, SAVE_POLICY_VALUE)
addIfDiffers(set, settings, defaultSettings, { it.incomingCommitsCheckStrategy }, INCOMING_COMMITS_CHECK_STRATEGY, GitOperationsCollector.REMOTE_CHECK_STRATEGY)
addBoolIfDiffers(set, settings, defaultSettings, { it.autoUpdateIfPushRejected() }, PUSH_AUTO_UPDATE)
addBoolIfDiffers(set, settings, defaultSettings, { it.warnAboutCrlf() }, WARN_CRLF)
@@ -231,6 +234,11 @@ internal class GitStatisticsCollector : ProjectUsagesCollector() {
private val SAVE_POLICY_VALUE = EventFields.Enum("value", GitSaveChangesPolicy::class.java) { it.name.lowercase() }
private val SAVE_POLICY = GROUP.registerVarargEvent("save.policy", SAVE_POLICY_VALUE)
private val INCOMING_COMMITS_CHECK_STRATEGY =
GROUP.registerVarargEvent("incoming_commits_check_strategy",
"Non-default value for incoming commits check strategy",
REMOTE_CHECK_STRATEGY)
private val PUSH_AUTO_UPDATE = GROUP.registerVarargEvent("push.autoupdate", EventFields.Enabled)
private val WARN_CRLF = GROUP.registerVarargEvent("warn.about.crlf", EventFields.Enabled)
@@ -31,6 +31,7 @@ import com.intellij.vcs.git.branch.GitInOutProjectState;
import com.intellij.vcs.log.Hash;
import com.intellij.vcsUtil.VcsFileUtil;
import git4idea.GitLocalBranch;
import git4idea.GitOperationsCollector;
import git4idea.GitRemoteBranch;
import git4idea.commands.Git;
import git4idea.commands.GitAuthenticationListener;
@@ -72,6 +73,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import com.intellij.externalProcessAuthHelper.AuthenticationMode;
@@ -293,25 +295,33 @@ public final class GitBranchIncomingOutgoingManager implements GitRepositoryChan
private void requestRemoteInfo(GitIncomingRemoteCheckStrategy remoteCheckStrategy, List<GitRepository> repositories) {
myLocalBranchesToFetch.remove(repositories);
switch (remoteCheckStrategy) {
case FETCH -> {
LOG.info("Fetching %d repositories".formatted(repositories.size()));
List<GitFetchSpec> remotesToFetch = new ArrayList<>();
for (GitRepository repository : repositories) {
for (GitRemote remote : repository.getRemotes()) {
remotesToFetch.add(new GitFetchSpec(repository, remote, getAuthenticationMode(repository, remote)));
if (remoteCheckStrategy == GitIncomingRemoteCheckStrategy.NONE) {
LOG.debug("Remote check disabled");
return;
}
AtomicBoolean success = new AtomicBoolean(false);
try {
switch (remoteCheckStrategy) {
case FETCH -> {
LOG.info("Fetching %d repositories".formatted(repositories.size()));
List<GitFetchSpec> remotesToFetch = new ArrayList<>();
for (GitRepository repository : repositories) {
for (GitRemote remote : repository.getRemotes()) {
remotesToFetch.add(new GitFetchSpec(repository, remote, getAuthenticationMode(repository, remote)));
}
}
success.set(GitFetchSupport.fetchSupport(myProject).fetch(remotesToFetch).isSuccessful());
}
case LS_REMOTE -> {
LOG.info("Listing remote info for %d repositories".formatted(repositories.size()));
repositories.forEach(r -> myLocalBranchesToFetch.put(r, calculateBranchesToFetch(r, () -> success.set(false))));
}
GitFetchSupport.fetchSupport(myProject).fetch(remotesToFetch);
}
case LS_REMOTE -> {
LOG.info("Listing remote info for %d repositories".formatted(repositories.size()));
repositories.forEach(r -> myLocalBranchesToFetch.put(r, calculateBranchesToFetch(r)));
}
case NONE -> {
LOG.debug("Remote check disabled");
}
}
finally {
GitOperationsCollector.logRemoteInfoRequest(myProject, remoteCheckStrategy, success.get());
}
}
@ApiStatus.Internal
@@ -354,20 +364,22 @@ public final class GitBranchIncomingOutgoingManager implements GitRepositoryChan
scheduleUpdate();
}
private @NotNull Map<GitLocalBranch, Hash> calculateBranchesToFetch(@NotNull GitRepository repository) {
private @NotNull Map<GitLocalBranch, Hash> calculateBranchesToFetch(@NotNull GitRepository repository,
@NotNull Runnable onError) {
Map<GitLocalBranch, Hash> result = new HashMap<>();
groupTrackInfoByRemotes(repository).entrySet()
.forEach(entry -> result.putAll(calcBranchesToFetchForRemote(repository, entry.getKey(), entry.getValue())));
.forEach(entry -> result.putAll(calcBranchesToFetchForRemote(repository, entry.getKey(), entry.getValue(), onError)));
return result;
}
private @NotNull Map<GitLocalBranch, Hash> calcBranchesToFetchForRemote(@NotNull GitRepository repository,
@NotNull GitRemote gitRemote,
@NotNull Collection<? extends GitBranchTrackInfo> trackInfoList) {
@NotNull Collection<? extends GitBranchTrackInfo> trackInfoList,
@NotNull Runnable onError) {
Map<GitLocalBranch, Hash> result = new HashMap<>();
GitBranchesCollection branchesCollection = repository.getBranches();
final Map<String, Hash> remoteNameWithHash =
lsRemote(repository, gitRemote, ContainerUtil.map(trackInfoList, info -> info.getRemoteBranch().getNameForRemoteOperations()));
lsRemote(repository, gitRemote, ContainerUtil.map(trackInfoList, info -> info.getRemoteBranch().getNameForRemoteOperations()), onError);
for (Map.Entry<String, Hash> hashEntry : remoteNameWithHash.entrySet()) {
String remoteBranchName = hashEntry.getKey();
@@ -435,7 +447,8 @@ public final class GitBranchIncomingOutgoingManager implements GitRepositoryChan
private @NotNull Map<String, Hash> lsRemote(@NotNull GitRepository repository,
@NotNull GitRemote remote,
@NotNull List<String> branchRefNames) {
@NotNull List<String> branchRefNames,
@NotNull Runnable onError) {
Map<String, Hash> result = new HashMap<>();
if (!supportsIncomingOutgoing()) return result;
@@ -452,6 +465,9 @@ public final class GitBranchIncomingOutgoingManager implements GitRepositoryChan
result.putAll(getResolvedHashes(hashWithNameMap));
myAuthSuccessMap.putValue(repository, remote);
}
else {
onError.run();
}
});
return result;
}
@@ -17,4 +17,6 @@ interface GitFetchResult {
fun showNotificationIfFailed(title: @NlsContexts.NotificationTitle String): Boolean
fun throwExceptionIfFailed()
fun isSuccessful(): Boolean
}
@@ -323,6 +323,8 @@ internal class GitFetchSupportImpl(private val project: Project) : GitFetchSuppo
private val isFailed = results.values.any { !it.totallySuccessful() }
override fun isSuccessful(): Boolean = !isFailed
override fun showNotification() {
doShowNotification()
}