mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
IDEA-320968 git: do not use .sh scripts for callbacks on Windows if custom ssh executable is configured
GitOrigin-RevId: c42638ba3a1516560611dcbf75032d054620d0c0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7a5f784b55
commit
aa70e4b6f4
@@ -118,4 +118,8 @@ public interface Repository extends Disposable {
|
||||
@NonNls
|
||||
@NotNull
|
||||
String toLogString();
|
||||
|
||||
default boolean isDisposed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ public abstract class RepositoryImpl implements Repository {
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final VirtualFile myRootDir;
|
||||
|
||||
private boolean myDisposed;
|
||||
|
||||
protected RepositoryImpl(@NotNull Project project,
|
||||
@NotNull VirtualFile dir,
|
||||
@NotNull Disposable parentDisposable) {
|
||||
@@ -62,8 +64,14 @@ public abstract class RepositoryImpl implements Repository {
|
||||
return getCurrentRevision() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisposed() {
|
||||
return myDisposed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
myDisposed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -561,7 +561,7 @@
|
||||
description="Use workaround for Native SSH support in Git. Batch file cannot properly handle arguments with newlines when used as SSH_ASKPASS."/>
|
||||
<registryKey key="git.use.setsid.for.native.ssh" defaultValue="true"
|
||||
description="Wrap 'git' process with 'setsid' for remote operations.
|
||||
This fixes issue with SSH_ASKPASS if IDE is executed from terminal or under DM/WM that pass system TTY from Xorg to GUI applications."/>
|
||||
This fixes issue with SSH_ASKPASS if IDE is executed from terminal or under DM/WM that pass system TTY from Xorg to GUI applications. IDEA-201054"/>
|
||||
<registryKey key="git.use.setsid.wait.for.wsl.ssh" defaultValue="true"
|
||||
description="Wrap 'git' process with 'setsid -w' for remote operations with WSL executable."/>
|
||||
<registryKey key="git.wsl.exe.executable.no.shell" defaultValue="false"
|
||||
|
||||
@@ -76,6 +76,8 @@ public final class GitCommand {
|
||||
public static final @NonNls String GIT_SSH_ASK_PASS_ENV = "SSH_ASKPASS";
|
||||
public static final @NonNls String SSH_ASKPASS_REQUIRE_ENV = "SSH_ASKPASS_REQUIRE";
|
||||
public static final @NonNls String DISPLAY_ENV = "DISPLAY";
|
||||
public static final @NonNls String GIT_SSH_ENV = "GIT_SSH";
|
||||
public static final @NonNls String GIT_SSH_COMMAND_ENV = "GIT_SSH_COMMAND";
|
||||
/**
|
||||
* Marker-ENV, that lets git hooks to detect us if needed.
|
||||
*/
|
||||
|
||||
@@ -57,6 +57,7 @@ public abstract class GitHandler {
|
||||
private final GitCommand myCommand;
|
||||
|
||||
private boolean myPreValidateExecutable = true;
|
||||
private boolean myEnableInteractiveCallbacks = true;
|
||||
|
||||
protected final GeneralCommandLine myCommandLine;
|
||||
private final Map<String, String> myCustomEnv = new HashMap<>();
|
||||
@@ -397,6 +398,20 @@ public abstract class GitHandler {
|
||||
return myPreValidateExecutable;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link GitImplBase#run(Computable, Computable)}
|
||||
*/
|
||||
public boolean isEnableInteractiveCallbacks() {
|
||||
return myEnableInteractiveCallbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link GitImplBase#run(Computable, Computable)}
|
||||
*/
|
||||
public void setEnableInteractiveCallbacks(boolean enableInteractiveCallbacks) {
|
||||
myEnableInteractiveCallbacks = enableInteractiveCallbacks;
|
||||
}
|
||||
|
||||
void runInCurrentThread() throws IOException {
|
||||
try {
|
||||
start();
|
||||
|
||||
@@ -12,13 +12,16 @@ import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.EnvironmentUtil;
|
||||
import externalApp.nativessh.NativeSshAskPassAppHandler;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.config.GitExecutable;
|
||||
import git4idea.config.GitVcsApplicationSettings;
|
||||
import git4idea.config.GitVersion;
|
||||
import git4idea.config.GitVersionSpecialty;
|
||||
import git4idea.config.*;
|
||||
import git4idea.http.GitAskPassAppHandler;
|
||||
import git4idea.repo.GitProjectConfigurationCache;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -177,12 +180,53 @@ public final class GitHandlerAuthenticationManager implements AutoCloseable {
|
||||
private void addHandlerPathToEnvironment(@NotNull String env,
|
||||
@NotNull ExternalProcessHandlerService<?> service) throws IOException {
|
||||
GitExecutable executable = myHandler.getExecutable();
|
||||
boolean useBatchFile = SystemInfo.isWindows &&
|
||||
executable.isLocal() &&
|
||||
(!Registry.is("git.use.shell.script.on.windows") ||
|
||||
!GitVersionSpecialty.CAN_USE_SHELL_HELPER_SCRIPT_ON_WINDOWS.existsIn(myVersion));
|
||||
File scriptFile = service.getCallbackScriptPath(executable.getId(), new GitScriptGenerator(executable), useBatchFile);
|
||||
File scriptFile = service.getCallbackScriptPath(executable.getId(),
|
||||
new GitScriptGenerator(executable),
|
||||
shouldUseBatchScript(executable));
|
||||
String scriptPath = executable.convertFilePath(scriptFile);
|
||||
myHandler.addCustomEnvironmentVariable(env, scriptPath);
|
||||
}
|
||||
|
||||
private boolean shouldUseBatchScript(@NotNull GitExecutable executable) {
|
||||
if (!SystemInfo.isWindows) return false;
|
||||
if (!executable.isLocal()) return false;
|
||||
if (Registry.is("git.use.shell.script.on.windows") &&
|
||||
GitVersionSpecialty.CAN_USE_SHELL_HELPER_SCRIPT_ON_WINDOWS.existsIn(myVersion)) {
|
||||
return isCustomSshExecutableConfigured();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isCustomSshExecutableConfigured() {
|
||||
String sshCommand = readSshCommand();
|
||||
String command = StringUtil.trim(StringUtil.unquoteString(StringUtil.notNullize(sshCommand)));
|
||||
// do not treat 'ssh -vvv' as custom executable
|
||||
return !command.isEmpty() && !command.startsWith("ssh ");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String readSshCommand() {
|
||||
String sshCommand = EnvironmentUtil.getValue(GitCommand.GIT_SSH_COMMAND_ENV);
|
||||
if (sshCommand != null) return sshCommand;
|
||||
|
||||
sshCommand = EnvironmentUtil.getValue(GitCommand.GIT_SSH_ENV);
|
||||
if (sshCommand != null) return sshCommand;
|
||||
|
||||
VirtualFile root = myHandler.getExecutableContext().getRoot();
|
||||
if (root == null) return null;
|
||||
|
||||
GitRepository repo = GitRepositoryManager.getInstance(myProject).getRepositoryForRoot(root);
|
||||
if (repo != null) {
|
||||
return GitProjectConfigurationCache.getInstance(myProject).readRepositoryConfig(repo, GitConfigUtil.CORE_SSH_COMMAND);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
return GitConfigUtil.getValue(myProject, root, GitConfigUtil.CORE_SSH_COMMAND);
|
||||
}
|
||||
catch (VcsException e) {
|
||||
LOG.warn(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public abstract class GitImplBase implements Git {
|
||||
throw new ProcessCanceledException();
|
||||
}
|
||||
|
||||
if (project != null) {
|
||||
if (project != null && handler.isEnableInteractiveCallbacks()) {
|
||||
try (GitHandlerAuthenticationManager authenticationManager = GitHandlerAuthenticationManager.prepare(project, handler, version)) {
|
||||
try (GitHandlerRebaseEditorManager ignored = prepareGeneralPurposeEditor(project, handler)) {
|
||||
GitCommandResult result = doRun(handler, version, outputCollector);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package git4idea.config;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -28,6 +28,7 @@ public final class GitConfigUtil {
|
||||
public static final @NlsSafe String USER_EMAIL = "user.email";
|
||||
public static final @NlsSafe String CORE_AUTOCRLF = "core.autocrlf";
|
||||
public static final @NlsSafe String CREDENTIAL_HELPER = "credential.helper";
|
||||
public static final @NlsSafe String CORE_SSH_COMMAND = "core.sshCommand";
|
||||
public static final @NlsSafe String LOG_OUTPUT_ENCODING = "i18n.logoutputencoding";
|
||||
public static final @NlsSafe String COMMIT_ENCODING = "i18n.commitencoding";
|
||||
public static final @NlsSafe String COMMIT_TEMPLATE = "commit.template";
|
||||
@@ -43,6 +44,7 @@ public final class GitConfigUtil {
|
||||
@Nullable @NonNls String keyMask,
|
||||
@NotNull Map<String, String> result) throws VcsException {
|
||||
GitLineHandler h = new GitLineHandler(project, root, GitCommand.CONFIG);
|
||||
h.setEnableInteractiveCallbacks(false);
|
||||
h.setSilent(true);
|
||||
h.addParameters("--null");
|
||||
if (keyMask != null) {
|
||||
@@ -66,12 +68,15 @@ public final class GitConfigUtil {
|
||||
}
|
||||
|
||||
|
||||
public static @Nullable String getValue(@NotNull Project project, @NotNull VirtualFile root, @NotNull @NonNls String key) throws VcsException {
|
||||
@Nullable
|
||||
public static String getValue(@NotNull Project project, @NotNull VirtualFile root, @NotNull @NonNls String key) throws VcsException {
|
||||
GitLineHandler h = new GitLineHandler(project, root, GitCommand.CONFIG);
|
||||
return getValue(h, key);
|
||||
}
|
||||
|
||||
private static @Nullable String getValue(@NotNull GitLineHandler h, @NotNull @NonNls String key) throws VcsException {
|
||||
@Nullable
|
||||
private static String getValue(@NotNull GitLineHandler h, @NotNull @NonNls String key) throws VcsException {
|
||||
h.setEnableInteractiveCallbacks(false);
|
||||
h.setSilent(true);
|
||||
h.addParameters("--null", "--get", key);
|
||||
GitCommandResult result = Git.getInstance().runCommand(h);
|
||||
@@ -87,7 +92,8 @@ public final class GitConfigUtil {
|
||||
* Converts the git config boolean value (which can be specified in various ways) to Java Boolean.
|
||||
* @return true if the value represents "true", false if the value represents "false", null if the value doesn't look like a boolean value.
|
||||
*/
|
||||
public static @Nullable Boolean getBooleanValue(@Nullable @NonNls String value) {
|
||||
@Nullable
|
||||
public static Boolean getBooleanValue(@Nullable @NonNls String value) {
|
||||
if (value == null) return null;
|
||||
value = StringUtil.toLowerCase(value);
|
||||
if (ContainerUtil.newHashSet("true", "yes", "on", "1").contains(value)) return true;
|
||||
@@ -98,7 +104,8 @@ public final class GitConfigUtil {
|
||||
/**
|
||||
* Get commit encoding for the specified root, or UTF-8 if the encoding is note explicitly specified
|
||||
*/
|
||||
public static @NotNull String getCommitEncoding(@NotNull Project project, @NotNull VirtualFile root) {
|
||||
@NotNull
|
||||
public static String getCommitEncoding(@NotNull Project project, @NotNull VirtualFile root) {
|
||||
String encoding = null;
|
||||
try {
|
||||
encoding = getValue(project, root, COMMIT_ENCODING);
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.vcs.VcsLocaleHelper
|
||||
import git4idea.commands.GitHandler
|
||||
import git4idea.i18n.GitBundle
|
||||
import git4idea.repo.GitConfigKey
|
||||
import git4idea.repo.GitConfigurationCache
|
||||
import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.annotations.NonNls
|
||||
@@ -195,7 +196,7 @@ sealed class GitExecutable {
|
||||
|
||||
private data class WslSupportedLocaleKey(
|
||||
val distribution: WSLDistribution
|
||||
) : GitConfigurationCache.ConfigKey<Map<String, String>?>
|
||||
) : GitConfigKey<Map<String, String>?>
|
||||
|
||||
private fun computeWslSupportedLocaleKey(distribution: WSLDistribution): Map<String, String>? {
|
||||
val knownLocales = listOf(VcsLocaleHelper.EN_UTF_LOCALE, VcsLocaleHelper.C_UTF_LOCALE)
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
package git4idea.repo
|
||||
|
||||
import com.intellij.concurrency.ConcurrentCollectionFactory
|
||||
import com.intellij.dvcs.repo.VcsRepositoryManager
|
||||
import com.intellij.dvcs.repo.VcsRepositoryMappingListener
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.VcsException
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.intellij.util.messages.MessageBusConnection
|
||||
import git4idea.config.GitConfigUtil
|
||||
import git4idea.config.GitExecutableListener
|
||||
import git4idea.config.GitExecutableManager
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -19,21 +25,60 @@ import java.util.concurrent.ExecutionException
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@Service(Service.Level.APP)
|
||||
class GitConfigurationCache : Disposable {
|
||||
class GitConfigurationCache : GitConfigurationCacheBase() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(): GitConfigurationCache = service()
|
||||
}
|
||||
}
|
||||
|
||||
private val cache: MutableMap<ConfigKey<*>, CompletableFuture<*>> = ConcurrentCollectionFactory.createConcurrentMap()
|
||||
@ApiStatus.Experimental
|
||||
@Service(Service.Level.PROJECT)
|
||||
class GitProjectConfigurationCache(val project: Project) : GitConfigurationCacheBase() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): GitProjectConfigurationCache = project.service()
|
||||
}
|
||||
|
||||
init {
|
||||
val connection: MessageBusConnection = ApplicationManager.getApplication().getMessageBus().connect(this)
|
||||
val connection: MessageBusConnection = project.messageBus.connect(this)
|
||||
connection.subscribe<VcsRepositoryMappingListener>(VcsRepositoryManager.VCS_REPOSITORY_MAPPING_UPDATED, VcsRepositoryMappingListener {
|
||||
clearInvalidKeys()
|
||||
})
|
||||
}
|
||||
|
||||
@RequiresBackgroundThread
|
||||
fun <T> readRepositoryConfig(repository: GitRepository, key: String): String? {
|
||||
return computeCachedValue(RepoConfigKey(repository, key)) {
|
||||
try {
|
||||
GitConfigUtil.getValue(repository.getProject(), repository.getRoot(), key)
|
||||
}
|
||||
catch (e: VcsException) {
|
||||
logger<GitProjectConfigurationCache>().warn(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearInvalidKeys() {
|
||||
cache.keys.removeIf {
|
||||
it is GitRepositoryConfigKey && it.repository.isDisposed()
|
||||
}
|
||||
}
|
||||
|
||||
data class RepoConfigKey(override val repository: GitRepository, val key: String) : GitRepositoryConfigKey<String?>
|
||||
}
|
||||
|
||||
abstract class GitConfigurationCacheBase : Disposable {
|
||||
protected val cache: MutableMap<GitConfigKey<*>, CompletableFuture<*>> = ConcurrentCollectionFactory.createConcurrentMap()
|
||||
|
||||
init {
|
||||
val connection: MessageBusConnection = ApplicationManager.getApplication().messageBus.connect(this)
|
||||
connection.subscribe<GitExecutableListener>(GitExecutableManager.TOPIC, GitExecutableListener { clearCache() })
|
||||
}
|
||||
|
||||
@RequiresBackgroundThread
|
||||
fun <T> computeCachedValue(configKey: ConfigKey<T>, computeValue: () -> T): T {
|
||||
fun <T> computeCachedValue(configKey: GitConfigKey<T>, computeValue: () -> T): T {
|
||||
val future = CompletableFuture<T>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -79,6 +124,9 @@ class GitConfigurationCache : Disposable {
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
interface ConfigKey<T>
|
||||
}
|
||||
|
||||
interface GitConfigKey<T>
|
||||
interface GitRepositoryConfigKey<T> : GitConfigKey<T> {
|
||||
val repository: GitRepository
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user