mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[git] Better detect Git executable on Windows, refactor, test
* Introduce GitExecutableDetector to detect executable path. - Collect all potential msysgit distributives under Program Files and Program Files (x86). - Select the best candidate: this is either "Git" without version, or the one with the highest possible version (if we could parse it out from the directory name). - If nothing found, try cygwin. - If nothing found, return the sole executable "git.exe" or "git.cmd". The latter is preferable, but doesn't exist in 1.8.0.2 (and maybe won't exist in the future), so we need to check if git.cmd can be started. - git.exe is returned as the default. It still can be not functional if it is not in the PATH, but we can't do more to find it. * GitVcs#activate: if the path to Git is not set, detect it. If it is set to git.exe/cmd (sole executable), and it is not valid, try to detect something better. Then pass to the ExecutableValidator. * Write tests for the Windows detector.
This commit is contained in:
@@ -85,6 +85,7 @@ import git4idea.vfs.GitVFSListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -339,11 +340,7 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
|
||||
if (myExecutableValidator.checkExecutableAndNotifyIfNeeded()) {
|
||||
checkVersion();
|
||||
}
|
||||
}
|
||||
checkExecutableAndVersion();
|
||||
|
||||
if (myVFSListener == null) {
|
||||
myVFSListener = new GitVFSListener(myProject, this, myGit);
|
||||
@@ -364,6 +361,27 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
|
||||
((GitCommitsSequentialIndex) ServiceManager.getService(GitCommitsSequentially.class)).activate();
|
||||
}
|
||||
|
||||
private void checkExecutableAndVersion() {
|
||||
boolean executableIsAlreadyCheckedAndFine = false;
|
||||
String pathToGit = myAppSettings.getPathToGitAtStartup();
|
||||
if (pathToGit == null) {
|
||||
myAppSettings.setPathToGit(new GitExecutableDetector().detect());
|
||||
}
|
||||
else if (!pathToGit.contains(File.separator)) { // no path, just sole executable, with a hope that it is in path
|
||||
// subject to redetect the path if executable validator fails
|
||||
if (!myExecutableValidator.isExecutableValid()) {
|
||||
myAppSettings.setPathToGit(new GitExecutableDetector().detect());
|
||||
}
|
||||
else {
|
||||
executableIsAlreadyCheckedAndFine = true; // not to check it twice
|
||||
}
|
||||
}
|
||||
|
||||
if (executableIsAlreadyCheckedAndFine || myExecutableValidator.checkExecutableAndNotifyIfNeeded()) {
|
||||
checkVersion();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
if (myVFSListener != null) {
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package git4idea.config;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.CapturingProcessHandler;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Tries to detect the path to Git executable.
|
||||
*
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GitExecutableDetector {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(GitExecutableDetector.class);
|
||||
private static final String[] UNIX_PATHS = { "/usr/local/bin",
|
||||
"/usr/bin",
|
||||
"/opt/local/bin",
|
||||
"/opt/bin",
|
||||
"/usr/local/git/bin"};
|
||||
private static final String UNIX_EXECUTABLE = "git";
|
||||
|
||||
private static final File WIN_ROOT = new File("C:"); // the constant is extracted to be able to create files in "Program Files" in tests
|
||||
private static final String GIT_CMD = "git.cmd";
|
||||
private static final String GIT_EXE = "git.exe";
|
||||
|
||||
@NotNull
|
||||
public String detect() {
|
||||
if (SystemInfo.isWindows) {
|
||||
return detectForWindows();
|
||||
}
|
||||
return detectForUnix();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String detectForUnix() {
|
||||
for (String p : UNIX_PATHS) {
|
||||
File f = new File(p, UNIX_EXECUTABLE);
|
||||
if (f.exists()) {
|
||||
return f.getPath();
|
||||
}
|
||||
}
|
||||
return UNIX_EXECUTABLE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String detectForWindows() {
|
||||
String exec = checkMsys();
|
||||
if (exec != null) {
|
||||
return exec;
|
||||
}
|
||||
|
||||
exec = checkCygwin();
|
||||
if (exec != null) {
|
||||
return exec;
|
||||
}
|
||||
|
||||
return checkSoleExecutable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String checkMsys() {
|
||||
final String[] PROGRAM_FILES = { "Program Files", "Program Files (x86)" };
|
||||
|
||||
// collecting all potential msys distributives
|
||||
List<File> distrs = new ArrayList<File>();
|
||||
for (String programFiles : PROGRAM_FILES) {
|
||||
File pf = new File(WIN_ROOT, programFiles);
|
||||
File[] children = pf.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.isDirectory() && pathname.getName().toLowerCase().startsWith("git");
|
||||
}
|
||||
});
|
||||
if (!pf.exists() || children == null) {
|
||||
continue;
|
||||
}
|
||||
distrs.addAll(Arrays.asList(children));
|
||||
}
|
||||
|
||||
// greater is better => sorting in the descending order to match the best version first, when iterating
|
||||
Collections.sort(distrs, Collections.reverseOrder(new VersionDirsComparator()));
|
||||
|
||||
for (File distr : distrs) {
|
||||
String exec = checkDistributive(distr);
|
||||
if (exec != null) {
|
||||
return exec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String checkCygwin() {
|
||||
final String[] OTHER_WINDOWS_PATHS = { FileUtil.toSystemDependentName("cygwin/bin/git.exe") };
|
||||
for (String otherPath : OTHER_WINDOWS_PATHS) {
|
||||
File file = new File(WIN_ROOT, otherPath);
|
||||
if (file.exists()) {
|
||||
return file.getPath();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String checkSoleExecutable() {
|
||||
if (runs(GIT_CMD)) {
|
||||
return GIT_CMD;
|
||||
}
|
||||
return GIT_EXE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String checkDistributive(@Nullable File gitDir) {
|
||||
if (gitDir == null || !gitDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String[] binDirs = { "cmd", "bin" };
|
||||
for (String binDir : binDirs) {
|
||||
String exec = checkBinDir(new File(gitDir, binDir));
|
||||
if (exec != null) {
|
||||
return exec;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String checkBinDir(@NotNull File binDir) {
|
||||
if (!binDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (String exec : new String[]{ GIT_CMD, GIT_EXE }) {
|
||||
File fe = new File(binDir, exec);
|
||||
if (fe.exists()) {
|
||||
return fe.getPath();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it is possible to run the specified program.
|
||||
* Made protected for tests not to start a process there.
|
||||
*/
|
||||
protected boolean runs(@NotNull String exec) {
|
||||
GeneralCommandLine commandLine = new GeneralCommandLine();
|
||||
commandLine.setExePath(exec);
|
||||
try {
|
||||
CapturingProcessHandler handler = new CapturingProcessHandler(commandLine.createProcess(), CharsetToolkit.getDefaultSystemCharset());
|
||||
ProcessOutput result = handler.runProcess((int)TimeUnit.SECONDS.toMillis(5));
|
||||
return !result.isTimeout();
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare strategy: greater is better (if v1 > v2, then v1 is a better candidate for the Git executable)
|
||||
private static class VersionDirsComparator implements Comparator<File> {
|
||||
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
String name1 = f1.getName().toLowerCase();
|
||||
String name2 = f2.getName().toLowerCase();
|
||||
|
||||
// C:\Program Files\Git is better candidate for _default_ than C:\Program Files\Git_1.8.0
|
||||
if (name1.equals("git")) {
|
||||
return name2.equals("git") ? fallback(f1, f2) : 1;
|
||||
}
|
||||
else if (name2.equals("git")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final Pattern GIT_WITH_VERSION = Pattern.compile("^git[ _]*([\\d\\.]*).*$");
|
||||
Matcher m1 = GIT_WITH_VERSION.matcher(name1);
|
||||
Matcher m2 = GIT_WITH_VERSION.matcher(name2);
|
||||
if (m1.matches() && m2.matches()) {
|
||||
GitVersion v1 = parseGitVersion(m1.group(1));
|
||||
GitVersion v2 = parseGitVersion(m2.group(1));
|
||||
if (v1 == null || v2 == null) {
|
||||
return fallback(f1, f2);
|
||||
}
|
||||
int compareVersions = v1.compareTo(v2);
|
||||
return compareVersions == 0 ? fallback(f1, f2) : compareVersions;
|
||||
}
|
||||
return fallback(f1, f2);
|
||||
}
|
||||
|
||||
private static int fallback(@NotNull File f1, @NotNull File f2) {
|
||||
// "Program Files" is preferable over "Program Files (x86)"
|
||||
int compareParents = f1.getParentFile().getName().compareTo(f2.getParentFile().getName());
|
||||
if (compareParents != 0) {
|
||||
return -compareParents; // greater is better => reversing
|
||||
}
|
||||
|
||||
// probably some unrecognized format of Git directory naming => just compare lexicographically
|
||||
String name1 = f1.getName().toLowerCase();
|
||||
String name2 = f2.getName().toLowerCase();
|
||||
return name1.compareTo(name2);
|
||||
}
|
||||
|
||||
// not using GitVersion#parse(), because it requires at least 3 items in the version (1.7.3),
|
||||
// and parses the `git version` command output, not just the version string.
|
||||
@Nullable
|
||||
private static GitVersion parseGitVersion(@Nullable String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
final Pattern VERSION = Pattern.compile("(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:\\.(\\d+))?.*");
|
||||
Matcher m = VERSION.matcher(name);
|
||||
if (!m.matches()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
int major = Integer.parseInt(m.group(1));
|
||||
return new GitVersion(major, parseOrNull(m.group(2)), parseOrNull(m.group(3)), parseOrNull(m.group(4)));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
LOG.info("Unexpected NFE when parsing [" + name + "]", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseOrNull(String group) {
|
||||
return group == null ? 0 : Integer.parseInt(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,9 @@
|
||||
package git4idea.config;
|
||||
|
||||
import com.intellij.openapi.components.*;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* The application wide settings for the git
|
||||
*/
|
||||
@@ -31,17 +27,7 @@ import java.io.File;
|
||||
storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/vcs.xml")})
|
||||
public class GitVcsApplicationSettings implements PersistentStateComponent<GitVcsApplicationSettings.State> {
|
||||
|
||||
@NonNls private static final String[] DEFAULT_WINDOWS_PATHS = { "C:\\Program Files\\Git\\bin",
|
||||
"C:\\Program Files (x86)\\Git\\bin",
|
||||
"C:\\cygwin\\bin" };
|
||||
@NonNls private static final String[] DEFAULT_UNIX_PATHS = { "/usr/local/bin",
|
||||
"/usr/bin",
|
||||
"/opt/local/bin",
|
||||
"/opt/bin",
|
||||
"/usr/local/git/bin" };
|
||||
@NonNls private static final String[] DEFAULT_WINDOWS_GITS = { "git.cmd", "git.exe" };
|
||||
@NonNls private static final String DEFAULT_UNIX_GIT = "git";
|
||||
|
||||
|
||||
private State myState = new State();
|
||||
|
||||
/**
|
||||
@@ -70,42 +56,22 @@ public class GitVcsApplicationSettings implements PersistentStateComponent<GitVc
|
||||
myState = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the default executable name depending on the platform
|
||||
*/
|
||||
@NotNull
|
||||
public String defaultGit() {
|
||||
if (myState.myPathToGit == null) {
|
||||
String[] paths;
|
||||
String[] programVariants;
|
||||
if (SystemInfo.isWindows) {
|
||||
programVariants = DEFAULT_WINDOWS_GITS;
|
||||
paths = DEFAULT_WINDOWS_PATHS;
|
||||
}
|
||||
else {
|
||||
programVariants = new String[] { DEFAULT_UNIX_GIT };
|
||||
paths = DEFAULT_UNIX_PATHS;
|
||||
}
|
||||
|
||||
for (String p : paths) {
|
||||
for (String program : programVariants) {
|
||||
File f = new File(p, program);
|
||||
if (f.exists()) {
|
||||
myState.myPathToGit = f.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (myState.myPathToGit == null) { // otherwise, take the first variant and hope it's in $PATH
|
||||
myState.myPathToGit = programVariants[0];
|
||||
}
|
||||
}
|
||||
public String getPathToGit() {
|
||||
return myState.myPathToGit;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getPathToGit() {
|
||||
return myState.myPathToGit == null ? defaultGit() : myState.myPathToGit;
|
||||
/**
|
||||
* <p>This method differs from {@link #getPathToGit()} only in the @Nullable annotation: initially the path can be null,
|
||||
* but after VCS is initialized for the first time, correct path is set in {@link git4idea.GitVcs} and is never null,
|
||||
* so we want the @NotNull annotation there.</p>
|
||||
* <p>This method should be called only from {@link git4idea.GitVcs#activate()}.</p>
|
||||
*
|
||||
* @return Path to Git or null if the path was not written to the config yet.
|
||||
*/
|
||||
@Nullable
|
||||
public String getPathToGitAtStartup() {
|
||||
return myState.myPathToGit;
|
||||
}
|
||||
|
||||
public void setPathToGit(String pathToGit) {
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package git4idea.config
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import git4idea.test.GitTestUtil
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
import static junit.framework.Assert.assertEquals
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
class GitExecutableDetectorWindowsTest {
|
||||
|
||||
private File testRoot
|
||||
private def canRun = []
|
||||
|
||||
@Before
|
||||
void setUp() {
|
||||
GitTestUtil.setWindows(true);
|
||||
testRoot = FileUtil.createTempDirectory("", "")
|
||||
setWindowsRoot(testRoot);
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Simple case"() {
|
||||
fs "/Program Files/Git/bin/git.exe"
|
||||
assertExecutable "C:/Program Files/Git/bin/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Prefer default Git without version to versioned ones"() {
|
||||
fs "/Program Files/Git/bin/git.exe", "/Program Files/Git 1.8/bin/git.exe", "/Program Files/Git_1.7.1/bin/git.exe"
|
||||
assertExecutable "C:/Program Files/Git/bin/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Prefer the latest version"() {
|
||||
fs "/Program Files (x86)/Git 1.8/cmd/git.exe",
|
||||
"/Program Files/Git_1.7.1/bin/git.exe",
|
||||
"/Program Files/Git_1.7.5/cmd/git.cmd",
|
||||
"/Program Files (x86)/Git_1.7.0.2/bin/git.exe"
|
||||
assertExecutable "C:/Program Files (x86)/Git 1.8/cmd/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Prefer Program Files over x86"() {
|
||||
fs "/Program Files (x86)/Git 1.8/bin/git.exe", "/Program Files/Git 1.8/bin/git.exe"
|
||||
assertExecutable "C:/Program Files/Git 1.8/bin/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Prefer git.cmd over git.exe"() {
|
||||
fs "/Program Files (x86)/Git 1.7.4/bin/git.exe", "/Program Files/Git 1.7.4/cmd/git.cmd"
|
||||
assertExecutable "C:/Program Files/Git 1.7.4/cmd/git.cmd"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Prefer cmd over bin in newer versions of Git"() {
|
||||
fs "/Program Files (x86)/Git 1.8/bin/git.exe", "/Program Files/Git 1.8/cmd/git.exe"
|
||||
assertExecutable "C:/Program Files/Git 1.8/cmd/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "1.8.0 Prefer cmd/git.cmd over cmd/git.exe and bin/git.exe"() {
|
||||
fs "/Program Files (x86)/Git_1.8/bin/git.exe",
|
||||
"/Program Files (x86)/Git_1.8/cmd/git.cmd",
|
||||
"/Program Files (x86)/Git_1.8/cmd/git.exe"
|
||||
assertExecutable "C:/Program Files (x86)/Git_1.8/cmd/git.cmd"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Prefer msys over cygwin"() {
|
||||
fs "/Program Files (x86)/Git_1.8.0.2/cmd/git.exe",
|
||||
"/Program Files (x86)/Git_1.8.0.2/bin/git.exe",
|
||||
"/cygwin/bin/git.exe"
|
||||
assertExecutable "C:/Program Files (x86)/Git_1.8.0.2/cmd/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Only cygwin"() {
|
||||
fs "/cygwin/bin/git.exe"
|
||||
assertExecutable "C:/cygwin/bin/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Many different versions, real case"() {
|
||||
fs "/Program Files (x86)/Git_1.7.0.2/bin/git.exe",
|
||||
"/Program Files (x86)/Git_1.7.0.2/cmd/git.cmd",
|
||||
"/Program Files (x86)/Git_1.7.8/bin/git.exe",
|
||||
"/Program Files (x86)/Git_1.7.8/cmd/git.cmd",
|
||||
"/Program Files (x86)/Git_1.8/bin/git.exe",
|
||||
"/Program Files (x86)/Git_1.8/cmd/git.cmd",
|
||||
"/Program Files (x86)/Git_1.8/cmd/git.exe",
|
||||
"/Program Files (x86)/Git_1.8.0.2/cmd/git.exe",
|
||||
"/Program Files (x86)/Git_1.8.0.2/bin/git.exe",
|
||||
"/cygwin/bin/git.exe"
|
||||
assertExecutable "C:/Program Files (x86)/Git_1.8.0.2/cmd/git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "Program not found, try git.exe"() {
|
||||
canRun = [ "git.exe" ]
|
||||
assertExecutable "git.exe"
|
||||
}
|
||||
|
||||
@Test
|
||||
void "For both git.exe and git.cmd prefer git.cmd"() {
|
||||
canRun = [ "git.exe", "git.cmd" ]
|
||||
assertExecutable "git.cmd"
|
||||
}
|
||||
|
||||
def assertExecutable(String expected) {
|
||||
expected = FileUtil.toSystemDependentName(expected).replace("C:", testRoot.path)
|
||||
assertEquals "Incorrect executable detected", expected, detect()
|
||||
}
|
||||
|
||||
def fs(String... paths) {
|
||||
paths.each {
|
||||
mkPath(it)
|
||||
}
|
||||
}
|
||||
|
||||
def mkPath(String path) {
|
||||
def file = new File(testRoot.getPath() + FileUtil.toSystemDependentName(path))
|
||||
file.getParentFile().mkdirs()
|
||||
file.createNewFile()
|
||||
}
|
||||
|
||||
private String detect() {
|
||||
new GitExecutableDetector() {
|
||||
@Override
|
||||
protected boolean runs(@NotNull String exec) {
|
||||
return canRun.contains(exec);
|
||||
}
|
||||
}.detect();
|
||||
}
|
||||
|
||||
static def setWindowsRoot(File file) {
|
||||
Field field = GitExecutableDetector.class.getDeclaredField("WIN_ROOT");
|
||||
field.setAccessible(true);
|
||||
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
|
||||
field.set(null, file);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user