mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-172253 git: support xmlrpc callbacks with WSL executable
GitOrigin-RevId: 60d678ae57d007d045b7203c10fca3368c7f2ffa
This commit is contained in:
committed by
intellij-monorepo-bot
parent
3cf2ecbd13
commit
c41b29aba3
@@ -8,7 +8,6 @@ import org.jetbrains.git4idea.GitExternalApp;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
@@ -30,10 +29,6 @@ public class GitRebaseEditorMain implements GitExternalApp {
|
||||
* Rebase editor handler name
|
||||
*/
|
||||
@NonNls static final String HANDLER_NAME = "Git4ideaRebaseEditorHandler";
|
||||
/**
|
||||
* The prefix for cygwin files
|
||||
*/
|
||||
private static final String CYGDRIVE_PREFIX = "/cygdrive/";
|
||||
|
||||
/**
|
||||
* A private constructor for static class
|
||||
@@ -68,16 +63,14 @@ public class GitRebaseEditorMain implements GitExternalApp {
|
||||
System.exit(ERROR_EXIT_CODE);
|
||||
}
|
||||
|
||||
String file = args[1];
|
||||
try {
|
||||
XmlRpcClientLite client = new XmlRpcClientLite("127.0.0.1", port);
|
||||
Vector<Object> params = new Vector<>();
|
||||
params.add(handlerId);
|
||||
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows") && file.startsWith(CYGDRIVE_PREFIX)) {
|
||||
int p = CYGDRIVE_PREFIX.length();
|
||||
file = file.charAt(p) + ":" + file.substring(p + 1);
|
||||
}
|
||||
params.add(new File(file).getAbsolutePath());
|
||||
|
||||
params.add(args[1]);
|
||||
params.add(new File("").getAbsolutePath());
|
||||
|
||||
Integer exitCode = (Integer)client.execute(HANDLER_NAME + ".editCommits", params);
|
||||
if (exitCode == null) {
|
||||
exitCode = ERROR_EXIT_CODE;
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.config.GitExecutable;
|
||||
import git4idea.config.GitVcsApplicationSettings;
|
||||
import git4idea.config.GitVersion;
|
||||
import git4idea.config.GitVersionSpecialty;
|
||||
@@ -170,10 +171,12 @@ public class GitHandlerAuthenticationManager implements AutoCloseable {
|
||||
|
||||
private void addHandlerPathToEnvironment(@NotNull String env,
|
||||
@NotNull GitXmlRpcHandlerService 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.getScriptPath(useBatchFile);
|
||||
File scriptFile = service.getScriptPath(executable, useBatchFile);
|
||||
myHandler.addCustomEnvironmentVariable(env, scriptFile);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,18 @@ package git4idea.config
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.util.ExecUtil
|
||||
import com.intellij.execution.wsl.WSLDistribution
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import git4idea.commands.GitHandler
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.io.File
|
||||
|
||||
sealed class GitExecutable {
|
||||
companion object {
|
||||
@NonNls
|
||||
private const val CYGDRIVE_PREFIX = "/cygdrive/"
|
||||
}
|
||||
|
||||
abstract val id: String
|
||||
abstract val exePath: String
|
||||
abstract val isLocal: Boolean
|
||||
|
||||
@@ -15,15 +23,30 @@ sealed class GitExecutable {
|
||||
* Convert absolute file path into a form, that can be passed into executable arguments.
|
||||
*/
|
||||
abstract fun convertFilePath(file: File): String
|
||||
|
||||
/**
|
||||
* Convert file path, returned by git, to be used by IDE.
|
||||
*/
|
||||
abstract fun convertFilePathBack(path: String, workingDir: File): File
|
||||
abstract fun patchCommandLine(handler: GitHandler, commandLine: GeneralCommandLine, withLowPriority: Boolean, withNoTty: Boolean)
|
||||
|
||||
data class Local(override val exePath: String)
|
||||
: GitExecutable() {
|
||||
override val id: String = "local"
|
||||
override val isLocal: Boolean = true
|
||||
override fun toString(): String = exePath
|
||||
|
||||
override fun convertFilePath(file: File): String = file.absolutePath
|
||||
|
||||
override fun convertFilePathBack(path: String, workingDir: File): File {
|
||||
if (SystemInfo.isWindows && path.startsWith(CYGDRIVE_PREFIX)) {
|
||||
val prefixSize = CYGDRIVE_PREFIX.length
|
||||
val localPath = path.substring(prefixSize, prefixSize + 1) + ":" + path.substring(prefixSize + 1)
|
||||
return File(localPath)
|
||||
}
|
||||
return workingDir.resolve(path)
|
||||
}
|
||||
|
||||
override fun patchCommandLine(handler: GitHandler, commandLine: GeneralCommandLine, withLowPriority: Boolean, withNoTty: Boolean) {
|
||||
if (withLowPriority) ExecUtil.setupLowPriorityExecution(commandLine)
|
||||
if (withNoTty) ExecUtil.setupNoTtyExecution(commandLine)
|
||||
@@ -33,6 +56,7 @@ sealed class GitExecutable {
|
||||
data class Wsl(override val exePath: String,
|
||||
val distribution: WSLDistribution)
|
||||
: GitExecutable() {
|
||||
override val id: String = "wsl-${distribution.id}"
|
||||
override val isLocal: Boolean = false
|
||||
override fun toString(): String = "${distribution.presentableName}: $exePath"
|
||||
|
||||
@@ -41,6 +65,11 @@ sealed class GitExecutable {
|
||||
return distribution.getWslPath(path) ?: path
|
||||
}
|
||||
|
||||
override fun convertFilePathBack(path: String, workingDir: File): File {
|
||||
val localPath = distribution.getWindowsPath(path) ?: path
|
||||
return File(localPath)
|
||||
}
|
||||
|
||||
override fun patchCommandLine(handler: GitHandler, commandLine: GeneralCommandLine, withLowPriority: Boolean, withNoTty: Boolean) {
|
||||
// Handling 'withNoTty' is not needed, as tty can't leak into WSL executable
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package git4idea.rebase;
|
||||
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.commands.GitCommand;
|
||||
import git4idea.commands.GitHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -38,8 +37,8 @@ public class GitHandlerRebaseEditorManager implements AutoCloseable {
|
||||
|
||||
private void prepareEditor() {
|
||||
if (myHandler.containsCustomEnvironmentVariable(GIT_EDITOR_ENV)) return;
|
||||
myHandlerId = myService.registerHandler(myEditorHandler);
|
||||
myHandler.addCustomEnvironmentVariable(GitCommand.GIT_EDITOR_ENV, myService.getEditorCommand());
|
||||
myHandlerId = myService.registerHandler(myHandler, myEditorHandler);
|
||||
myHandler.addCustomEnvironmentVariable(GIT_EDITOR_ENV, myService.getEditorCommand(myHandler.getExecutable()));
|
||||
myHandler.addCustomEnvironmentVariable(GitRebaseEditorMain.IDEA_REBASE_HANDER_NO, myHandlerId.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import com.intellij.ide.XmlRpcServer;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import git4idea.commands.GitHandler;
|
||||
import git4idea.config.GitExecutable;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.git4idea.util.ScriptGenerator;
|
||||
@@ -20,12 +22,6 @@ import java.util.UUID;
|
||||
* The service that generates editor script for
|
||||
*/
|
||||
public class GitRebaseEditorService implements Disposable {
|
||||
private static final String CYGDRIVE_PREFIX = "/cygdrive/";
|
||||
|
||||
/**
|
||||
* The editor command that is set to env variable
|
||||
*/
|
||||
private String myEditorCommand;
|
||||
/**
|
||||
* The lock object
|
||||
*/
|
||||
@@ -33,7 +29,7 @@ public class GitRebaseEditorService implements Disposable {
|
||||
/**
|
||||
* The handlers to use
|
||||
*/
|
||||
private final Map<UUID, GitRebaseEditorHandler> myHandlers = new HashMap<>();
|
||||
private final Map<UUID, Pair<GitRebaseEditorHandler, GitExecutable>> myHandlers = new HashMap<>();
|
||||
/**
|
||||
* The lock for the handlers
|
||||
*/
|
||||
@@ -76,14 +72,11 @@ public class GitRebaseEditorService implements Disposable {
|
||||
* @return the editor command
|
||||
*/
|
||||
@NotNull
|
||||
public synchronized String getEditorCommand() {
|
||||
public synchronized String getEditorCommand(@NotNull GitExecutable executable) {
|
||||
synchronized (myScriptLock) {
|
||||
if (myEditorCommand == null) {
|
||||
ScriptGenerator generator = new ScriptGenerator(GIT_REBASE_EDITOR_PREFIX, GitRebaseEditorMain.class);
|
||||
generator.addInternal(Integer.toString(BuiltInServerManager.getInstance().waitForStart().getPort()));
|
||||
myEditorCommand = generator.commandLine();
|
||||
}
|
||||
return myEditorCommand;
|
||||
ScriptGenerator generator = new ScriptGenerator(GIT_REBASE_EDITOR_PREFIX, GitRebaseEditorMain.class);
|
||||
generator.addInternal(Integer.toString(BuiltInServerManager.getInstance().waitForStart().getPort()));
|
||||
return generator.commandLine(executable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,11 +87,11 @@ public class GitRebaseEditorService implements Disposable {
|
||||
* @return the handler identifier
|
||||
*/
|
||||
@NotNull
|
||||
public UUID registerHandler(@NotNull GitRebaseEditorHandler handler) {
|
||||
public UUID registerHandler(@NotNull GitHandler handler, @NotNull GitRebaseEditorHandler editorHandler) {
|
||||
addInternalHandler();
|
||||
synchronized (myHandlersLock) {
|
||||
UUID key = UUID.randomUUID();
|
||||
myHandlers.put(key, handler);
|
||||
myHandlers.put(key, Pair.create(editorHandler, handler.getExecutable()));
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@@ -122,13 +115,13 @@ public class GitRebaseEditorService implements Disposable {
|
||||
* @param handlerNo the handler number.
|
||||
*/
|
||||
@NotNull
|
||||
GitRebaseEditorHandler getHandler(@NotNull UUID handlerNo) {
|
||||
Pair<GitRebaseEditorHandler, GitExecutable> getHandler(@NotNull UUID handlerNo) {
|
||||
synchronized (myHandlersLock) {
|
||||
GitRebaseEditorHandler h = myHandlers.get(handlerNo);
|
||||
if (h == null) {
|
||||
Pair<GitRebaseEditorHandler, GitExecutable> pair = myHandlers.get(handlerNo);
|
||||
if (pair == null) {
|
||||
throw new IllegalStateException("The handler " + handlerNo + " is not registered");
|
||||
}
|
||||
return h;
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,22 +137,14 @@ public class GitRebaseEditorService implements Disposable {
|
||||
* @return exit code
|
||||
*/
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
public int editCommits(@NotNull String handlerNo, String path) {
|
||||
GitRebaseEditorHandler editor = getHandler(UUID.fromString(handlerNo));
|
||||
public int editCommits(@NotNull String handlerNo, String path, String workingDir) {
|
||||
Pair<GitRebaseEditorHandler, GitExecutable> pair = getHandler(UUID.fromString(handlerNo));
|
||||
GitExecutable executable = pair.second;
|
||||
GitRebaseEditorHandler editorHandler = pair.first;
|
||||
|
||||
String localPath = adjustFilePath(path);
|
||||
File file = new File(localPath);
|
||||
File file = executable.convertFilePathBack(path, new File(workingDir));
|
||||
|
||||
return editor.editCommits(file);
|
||||
return editorHandler.editCommits(file);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String adjustFilePath(@NotNull String file) {
|
||||
if (SystemInfo.isWindows && file.startsWith(CYGDRIVE_PREFIX)) {
|
||||
final int prefixSize = CYGDRIVE_PREFIX.length();
|
||||
return file.charAt(prefixSize) + ":" + file.substring(prefixSize + 1);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,17 @@ import com.intellij.ide.XmlRpcServer;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import git4idea.config.GitExecutable;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.git4idea.GitExternalApp;
|
||||
import org.jetbrains.git4idea.util.ScriptGenerator;
|
||||
import org.jetbrains.ide.BuiltInServerManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.intellij.openapi.diagnostic.Logger.getInstance;
|
||||
@@ -42,8 +44,7 @@ public abstract class GitXmlRpcHandlerService<T> implements Disposable {
|
||||
@NotNull private final String myHandlerName;
|
||||
@NotNull private final Class<? extends GitExternalApp> myScriptMainClass;
|
||||
|
||||
@Nullable private File myBatchScriptPath;
|
||||
@Nullable private File myShellScriptPath;
|
||||
@NotNull private final Map<String, File> myScriptPaths = new HashMap<>();
|
||||
@NotNull private final Object SCRIPT_FILE_LOCK = new Object();
|
||||
|
||||
@NotNull private final THashMap<UUID, T> handlers = new THashMap<>();
|
||||
@@ -74,22 +75,16 @@ public abstract class GitXmlRpcHandlerService<T> implements Disposable {
|
||||
* @throws IOException if script cannot be generated
|
||||
*/
|
||||
@NotNull
|
||||
public File getScriptPath(boolean useBatchFile) throws IOException {
|
||||
ScriptGenerator generator = new ScriptGenerator(myScriptTempFilePrefix, myScriptMainClass);
|
||||
|
||||
public File getScriptPath(@NotNull GitExecutable executable, boolean useBatchFile) throws IOException {
|
||||
synchronized (SCRIPT_FILE_LOCK) {
|
||||
if (useBatchFile) {
|
||||
if (myBatchScriptPath == null || !myBatchScriptPath.exists()) {
|
||||
myBatchScriptPath = generator.generate(useBatchFile);
|
||||
}
|
||||
return myBatchScriptPath;
|
||||
}
|
||||
else {
|
||||
if (myShellScriptPath == null || !myShellScriptPath.exists()) {
|
||||
myShellScriptPath = generator.generate(useBatchFile);
|
||||
}
|
||||
return myShellScriptPath;
|
||||
String id = executable.getId() + (useBatchFile ? "-bat" : "");
|
||||
File scriptPath = myScriptPaths.get(id);
|
||||
if (scriptPath == null || !scriptPath.exists()) {
|
||||
ScriptGenerator generator = new ScriptGenerator(myScriptTempFilePrefix + "-" + executable.getId(), myScriptMainClass);
|
||||
scriptPath = generator.generate(executable, useBatchFile);
|
||||
myScriptPaths.put(id, scriptPath);
|
||||
}
|
||||
return scriptPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,22 @@ package org.jetbrains.git4idea.util;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.util.text.StringUtilRt;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import git4idea.config.GitExecutable;
|
||||
import git4idea.rebase.GitRebaseEditorMain;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.xmlrpc.XmlRpcClientLite;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.git4idea.http.GitAskPassXmlRpcHandler;
|
||||
import org.jetbrains.git4idea.nativessh.GitNativeSshAskPassXmlRpcHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Script generator utility class. It uses to generate a temporary scripts that
|
||||
@@ -31,7 +37,7 @@ public class ScriptGenerator {
|
||||
/**
|
||||
* The class paths for the script
|
||||
*/
|
||||
private final ArrayList<String> myPaths = new ArrayList<>();
|
||||
private final ArrayList<File> myPaths = new ArrayList<>();
|
||||
/**
|
||||
* The internal parameters for the script
|
||||
*/
|
||||
@@ -57,7 +63,7 @@ public class ScriptGenerator {
|
||||
*/
|
||||
private void addClasses(final Class... classes) {
|
||||
for (Class<?> c : classes) {
|
||||
String classPath = PathUtil.getJarPathForClass(c);
|
||||
File classPath = new File(PathUtil.getJarPathForClass(c));
|
||||
if (!myPaths.contains(classPath)) {
|
||||
// the size of path is expected to be quite small, so no optimization is done here
|
||||
myPaths.add(classPath);
|
||||
@@ -104,8 +110,8 @@ public class ScriptGenerator {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public File generate(boolean useBatchFile) throws IOException {
|
||||
String commandLine = commandLine();
|
||||
public File generate(@NotNull GitExecutable executable, boolean useBatchFile) throws IOException {
|
||||
String commandLine = commandLine(executable);
|
||||
return useBatchFile ? generateBatch(myPrefix, commandLine)
|
||||
: generateShell(myPrefix, commandLine);
|
||||
}
|
||||
@@ -113,26 +119,45 @@ public class ScriptGenerator {
|
||||
/**
|
||||
* @return a command line for the the executable program
|
||||
*/
|
||||
public String commandLine() {
|
||||
public String commandLine(@NotNull GitExecutable executable) {
|
||||
StringBuilder cmd = new StringBuilder();
|
||||
cmd.append('\"').append(System.getProperty("java.home")).append(File.separatorChar).append("bin").append(File.separatorChar)
|
||||
.append("java\" -cp \"");
|
||||
boolean first = true;
|
||||
for (String p : myPaths) {
|
||||
if (!first) {
|
||||
cmd.append(File.pathSeparatorChar);
|
||||
}
|
||||
else {
|
||||
first = false;
|
||||
}
|
||||
cmd.append(p);
|
||||
|
||||
if (executable instanceof GitExecutable.Wsl) {
|
||||
List<String> envs = ContainerUtil.newArrayList(
|
||||
GitNativeSshAskPassXmlRpcHandler.IJ_HANDLER_ENV,
|
||||
GitNativeSshAskPassXmlRpcHandler.IJ_PORT_ENV,
|
||||
GitAskPassXmlRpcHandler.GIT_ASK_PASS_HANDLER_ENV,
|
||||
GitAskPassXmlRpcHandler.GIT_ASK_PASS_PORT_ENV,
|
||||
GitRebaseEditorMain.IDEA_REBASE_HANDER_NO);
|
||||
cmd.append("export WSLENV=");
|
||||
cmd.append(StringUtil.join(envs, it -> it + "/w", ":"));
|
||||
cmd.append("\n");
|
||||
|
||||
cmd.append('"');
|
||||
File javaExecutable = new File(String.format("%s\\bin\\java.exe", System.getProperty("java.home")));
|
||||
cmd.append(executable.convertFilePath(javaExecutable));
|
||||
cmd.append('"');
|
||||
}
|
||||
cmd.append("\" ");
|
||||
else {
|
||||
cmd.append('"');
|
||||
cmd.append(String.format("%s/bin/java", System.getProperty("java.home")));
|
||||
cmd.append('"');
|
||||
}
|
||||
|
||||
cmd.append(" -cp ");
|
||||
cmd.append('"');
|
||||
String classpathSeparator = String.valueOf(File.pathSeparatorChar);
|
||||
cmd.append(StringUtil.join(myPaths, file -> file.getPath(), classpathSeparator));
|
||||
cmd.append('"');
|
||||
|
||||
cmd.append(' ');
|
||||
cmd.append(myMainClass.getName());
|
||||
|
||||
for (String p : myInternalParameters) {
|
||||
cmd.append(' ');
|
||||
cmd.append(p);
|
||||
}
|
||||
|
||||
String line = cmd.toString();
|
||||
if (SystemInfo.isWindows) {
|
||||
line = line.replace('\\', '/');
|
||||
|
||||
Reference in New Issue
Block a user