From 797ebba30707259f7a4bc06baa40360ab79d2ff8 Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Tue, 28 Dec 2010 17:15:18 +0100 Subject: [PATCH] Add support for getting the system wide configuration These settings are stored in /etc/gitconfig. The C Git binary is installed in /bin, so we look for the C Git executable to find this location, first by looking at the PATH environment variable and then by attemting to launch bash as a login shell to find out. Bug: 333216 Change-Id: I1bbee9fb123a81714a34a9cc242b92beacfbb4a8 Signed-off-by: Shawn O. Pearce Signed-off-by: Robin Rosenberg --- .../eclipse/jgit/junit/MockSystemReader.java | 43 +++++++--- .../tst/org/eclipse/jgit/lib/ConfigTest.java | 3 +- .../org/eclipse/jgit/JGitText.properties | 1 + .../src/org/eclipse/jgit/JGitText.java | 1 + .../jgit/storage/file/FileRepository.java | 26 +++++- .../src/org/eclipse/jgit/util/FS.java | 3 + .../src/org/eclipse/jgit/util/FS_POSIX.java | 81 +++++++++++++++++++ .../org/eclipse/jgit/util/FS_POSIX_Java5.java | 2 +- .../org/eclipse/jgit/util/FS_POSIX_Java6.java | 2 +- .../src/org/eclipse/jgit/util/FS_Win32.java | 20 +++++ .../org/eclipse/jgit/util/SystemReader.java | 44 ++++++++-- 11 files changed, 205 insertions(+), 21 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java index 5c2e77f67..b53dce216 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java @@ -45,39 +45,50 @@ package org.eclipse.jgit.junit; +import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; public class MockSystemReader extends SystemReader { + private final class MockConfig extends FileBasedConfig { + private MockConfig(File cfgLocation, FS fs) { + super(cfgLocation, fs); + } + + @Override + public void load() throws IOException, ConfigInvalidException { + // Do nothing + } + + @Override + public boolean isOutdated() { + return false; + } + } + final Map values = new HashMap(); FileBasedConfig userGitConfig; + FileBasedConfig systemGitConfig; + public MockSystemReader() { init(Constants.OS_USER_NAME_KEY); init(Constants.GIT_AUTHOR_NAME_KEY); init(Constants.GIT_AUTHOR_EMAIL_KEY); init(Constants.GIT_COMMITTER_NAME_KEY); init(Constants.GIT_COMMITTER_EMAIL_KEY); - userGitConfig = new FileBasedConfig(null, null) { - @Override - public void load() throws IOException, ConfigInvalidException { - // Do nothing - } - - @Override - public boolean isOutdated() { - return false; - } - }; + userGitConfig = new MockConfig(null, null); + systemGitConfig = new MockConfig(null, null); } private void init(final String n) { @@ -103,10 +114,17 @@ public String getProperty(String key) { } @Override - public FileBasedConfig openUserConfig(FS fs) { + public FileBasedConfig openUserConfig(Config parent, FS fs) { + assert parent == null || parent == systemGitConfig; return userGitConfig; } + @Override + public FileBasedConfig openSystemConfig(Config parent, FS fs) { + assert parent == null; + return systemGitConfig; + } + @Override public String getHostname() { return "fake.host.example.com"; @@ -121,4 +139,5 @@ public long getCurrentTime() { public int getTimezone(long when) { return TimeZone.getTimeZone("GMT-03:30").getOffset(when) / (60 * 1000); } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index eb9f03c55..65d1b6e24 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -120,7 +120,8 @@ public void test007_readUserConfig() { final MockSystemReader mockSystemReader = new MockSystemReader(); SystemReader.setInstance(mockSystemReader); final String hostname = mockSystemReader.getHostname(); - final Config userGitConfig = mockSystemReader.openUserConfig(FS.DETECTED); + final Config userGitConfig = mockSystemReader.openUserConfig(null, + FS.DETECTED); final Config localConfig = new Config(userGitConfig); mockSystemReader.clearProperties(); diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties index 05a429ddf..9e47d618f 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties @@ -393,6 +393,7 @@ startingReadStageWithoutWrittenRequestDataPendingIsNotSupported=Starting read st statelessRPCRequiresOptionToBeEnabled=stateless RPC requires {0} to be enabled submodulesNotSupported=Submodules are not supported symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java. +systemConfigFileInvalid=Systen wide config file {0} is invalid {1} tagNameInvalid=tag name {0} is invalid tagOnRepoWithoutHEADCurrentlyNotSupported=Tag on repository without HEAD currently not supported tSizeMustBeGreaterOrEqual1=tSize must be >= 1 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java index 2e7503a13..ce518f73d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java @@ -453,6 +453,7 @@ public static JGitText get() { /***/ public String statelessRPCRequiresOptionToBeEnabled; /***/ public String submodulesNotSupported; /***/ public String symlinkCannotBeWrittenAsTheLinkTarget; + /***/ public String systemConfigFileInvalid; /***/ public String tagNameInvalid; /***/ public String tagOnRepoWithoutHEADCurrentlyNotSupported; /***/ public String tSizeMustBeGreaterOrEqual1; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java index 417fa4b53..28b83b0f9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java @@ -95,6 +95,8 @@ * */ public class FileRepository extends Repository { + private final FileBasedConfig systemConfig; + private final FileBasedConfig userConfig; private final FileBasedConfig repoConfig; @@ -152,11 +154,14 @@ public FileRepository(final String gitDir) throws IOException { public FileRepository(final BaseRepositoryBuilder options) throws IOException { super(options); - userConfig = SystemReader.getInstance().openUserConfig(getFS()); + systemConfig = SystemReader.getInstance().openSystemConfig(null, getFS()); + userConfig = SystemReader.getInstance().openUserConfig(systemConfig, + getFS()); repoConfig = new FileBasedConfig(userConfig, // getFS().resolve(getDirectory(), "config"), // getFS()); + loadSystemConfig(); loadUserConfig(); loadRepoConfig(); @@ -184,6 +189,18 @@ public void onConfigChanged(ConfigChangedEvent event) { } } + private void loadSystemConfig() throws IOException { + try { + systemConfig.load(); + } catch (ConfigInvalidException e1) { + IOException e2 = new IOException(MessageFormat.format(JGitText + .get().systemConfigFileInvalid, systemConfig.getFile() + .getAbsolutePath(), e1)); + e2.initCause(e1); + throw e2; + } + } + private void loadUserConfig() throws IOException { try { userConfig.load(); @@ -285,6 +302,13 @@ public RefDatabase getRefDatabase() { * @return the configuration of this repository */ public FileBasedConfig getConfig() { + if (systemConfig.isOutdated()) { + try { + loadSystemConfig(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } if (userConfig.isOutdated()) { try { loadUserConfig(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index 0a3fad001..87d5395bf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -252,4 +252,7 @@ protected static String readPipe(File dir, String[] command, String encoding) { } return null; } + + /** @return the $prefix directory C Git would use. */ + public abstract File gitPrefix(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java new file mode 100644 index 000000000..0ebf2e307 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010, Robin Rosenberg + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.util; + +import java.io.File; +import java.nio.charset.Charset; +import java.security.AccessController; +import java.security.PrivilegedAction; + +abstract class FS_POSIX extends FS { + @Override + public File gitPrefix() { + String path = SystemReader.getInstance().getenv("PATH"); + File gitExe = searchPath(path, "git"); + if (gitExe != null) + return gitExe.getParentFile().getParentFile(); + + if (isMacOS()) { + // On MacOSX, PATH is shorter when Eclipse is launched from the + // Finder than from a terminal. Therefore try to launch bash as a + // login shell and search using that. + // + String w = readPipe(userHome(), // + new String[] { "bash", "--login", "-c", "which git" }, // + Charset.defaultCharset().name()); + return new File(w).getParentFile().getParentFile(); + } + + return null; + } + + private static boolean isMacOS() { + final String osDotName = AccessController + .doPrivileged(new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + return "Mac OS X".equals(osDotName); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java index 950d49189..ec8175ab5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java @@ -45,7 +45,7 @@ import java.io.File; -class FS_POSIX_Java5 extends FS { +class FS_POSIX_Java5 extends FS_POSIX { public boolean supportsExecute() { return false; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java index 192d9bbbb..dfc03c451 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java @@ -49,7 +49,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -class FS_POSIX_Java6 extends FS { +class FS_POSIX_Java6 extends FS_POSIX { private static final Method canExecute; private static final Method setExecute; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java index 4e35e6ee4..2cf8bf955 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.util; import java.io.File; +import java.nio.charset.Charset; import java.security.AccessController; import java.security.PrivilegedAction; @@ -76,4 +77,23 @@ public boolean setExecute(final File f, final boolean canExec) { public boolean retryFailedLockFileCommit() { return true; } + + @Override + public File gitPrefix() { + String path = SystemReader.getInstance().getenv("PATH"); + File gitExe = searchPath(path, "git.exe", "git.cmd"); + if (gitExe != null) + return gitExe.getParentFile().getParentFile(); + + // This isn't likely to work, if bash is in $PATH, git should + // also be in $PATH. But its worth trying. + // + String w = readPipe(userHome(), // + new String[] { "bash", "--login", "-c", "which git" }, // + Charset.defaultCharset().name()); + if (w != null) + return new File(w).getParentFile().getParentFile(); + + return null; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java index 475c871c3..ced1d90b8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java @@ -50,6 +50,7 @@ import java.net.UnknownHostException; import java.util.TimeZone; +import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.storage.file.FileBasedConfig; /** @@ -72,9 +73,28 @@ public String getProperty(String key) { return System.getProperty(key); } - public FileBasedConfig openUserConfig(FS fs) { + public FileBasedConfig openSystemConfig(Config parent, FS fs) { + File prefix = fs.gitPrefix(); + if (prefix == null) { + return new FileBasedConfig(null, fs) { + public void load() { + // empty, do not load + } + + public boolean isOutdated() { + // regular class would bomb here + return false; + } + }; + } + File etc = fs.resolve(prefix, "etc"); + File config = fs.resolve(etc, "gitconfig"); + return new FileBasedConfig(parent, config, fs); + } + + public FileBasedConfig openUserConfig(Config parent, FS fs) { final File home = fs.userHome(); - return new FileBasedConfig(new File(home, ".gitconfig"), fs); + return new FileBasedConfig(parent, new File(home, ".gitconfig"), fs); } public String getHostname() { @@ -136,12 +156,26 @@ public static void setInstance(SystemReader newReader) { public abstract String getProperty(String key); /** + * @param parent + * a config with values not found directly in the returned config * @param fs - * the file system abstraction which will be necessary to - * perform certain file system operations. + * the file system abstraction which will be necessary to perform + * certain file system operations. * @return the git configuration found in the user home */ - public abstract FileBasedConfig openUserConfig(FS fs); + public abstract FileBasedConfig openUserConfig(Config parent, FS fs); + + /** + * @param parent + * a config with values not found directly in the returned + * config. Null is a reasonable value here. + * @param fs + * the file system abstraction which will be necessary to perform + * certain file system operations. + * @return the gitonfig configuration found in the system-wide "etc" + * directory + */ + public abstract FileBasedConfig openSystemConfig(Config parent, FS fs); /** * @return the current system time