diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2019-10-07 17:58:56 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2019-11-09 18:01:19 +0100 |
commit | 838b5a84b5093c335b95a644b8888006d9e95493 (patch) | |
tree | 3443d1fd7052bee0e128237eca54a7d7ad5fb5ac | |
parent | ffe74210d64550d5e731d1179567b4fdc746fd7a (diff) | |
download | jgit-838b5a84b5093c335b95a644b8888006d9e95493.tar.gz jgit-838b5a84b5093c335b95a644b8888006d9e95493.zip |
Store filesystem timestamp resolution in extra jgit config
This avoids polluting hand-crafted user level config with
auto-configured options which might disturb in environments where
the user level config is replicated between different machines.
Add a jgit config as parent of the system level config. Persist
measured timestamp resolutions always in this jgit config and read it
via the user global config. This has the effect that auto-configured
timestamp resolution will be used by default and can be overridden in
either the system level or user level config.
Store the jgit config under the XDG_CONFIG_HOME directory following the
XDG base directory specification [1] in order to ensure that we have
write permissions to persist the file. This has the effect that each OS
user will use its jgit config since they typically use different
XDG_CONFIG_HOME directories.
If the environment variable XDG_CONFIG_HOME is defined the jgit config
file is located at $XDG_CONFIG_HOME/jgit/config otherwise the default is
~/.config/jgit/config.
If you want to avoid redundant measurement for different OS users
manually copy the values measured and auto-configured for one OS user to
the system level git config.
[1] https://wiki.archlinux.org/index.php/XDG_Base_Directory
Bug: 551850
Change-Id: I0022bd40ae62f82e5b964c2ea25822eb55d94687
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
9 files changed, 181 insertions, 23 deletions
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java index 5c2cd6ac65..eca8179ece 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java @@ -125,8 +125,9 @@ public abstract class LocalDiskRepositoryTestCase { public void setUp() throws Exception { tmp = File.createTempFile("jgit_test_", "_tmp"); CleanupThread.deleteOnShutdown(tmp); - if (!tmp.delete() || !tmp.mkdir()) + if (!tmp.delete() || !tmp.mkdir()) { throw new IOException("Cannot create " + tmp); + } mockSystemReader = new MockSystemReader(); SystemReader.setInstance(mockSystemReader); @@ -137,7 +138,11 @@ public abstract class LocalDiskRepositoryTestCase { // the same one here FS.getFileStoreAttributes(tmp.toPath().getParent()); - FileBasedConfig userConfig = new FileBasedConfig( + FileBasedConfig jgitConfig = new FileBasedConfig( + new File(tmp, "jgitconfig"), FS.DETECTED); + FileBasedConfig systemConfig = new FileBasedConfig(jgitConfig, + new File(tmp, "systemgitconfig"), FS.DETECTED); + FileBasedConfig userConfig = new FileBasedConfig(systemConfig, new File(tmp, "usergitconfig"), FS.DETECTED); // We have to set autoDetach to false for tests, because tests expect to be able // to clean up by recursively removing the repository, and background GC might be @@ -145,7 +150,10 @@ public abstract class LocalDiskRepositoryTestCase { userConfig.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null, ConfigConstants.CONFIG_KEY_AUTODETACH, false); userConfig.save(); + mockSystemReader.setJGitConfig(jgitConfig); + mockSystemReader.setSystemGitConfig(systemConfig); mockSystemReader.setUserGitConfig(userConfig); + ceilTestDirectories(getCeilings()); author = new PersonIdent("J. Author", "jauthor@example.com"); 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 13c2932282..630c8a4799 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 @@ -103,6 +103,8 @@ public class MockSystemReader extends SystemReader { private FileBasedConfig userGitConfig; + private FileBasedConfig jgitConfig; + FileBasedConfig systemGitConfig; /** @@ -119,6 +121,16 @@ public class MockSystemReader extends SystemReader { } /** + * Set the jgit config stored at $XDG_CONFIG_HOME/jgit/config + * + * @param jgitConfig + * set the jgit configuration + */ + public void setJGitConfig(FileBasedConfig jgitConfig) { + this.jgitConfig = jgitConfig; + } + + /** * Set the system-level git config * * @param systemGitConfig @@ -142,6 +154,7 @@ public class MockSystemReader extends SystemReader { init(Constants.GIT_COMMITTER_EMAIL_KEY); setProperty(Constants.OS_USER_DIR, "."); userGitConfig = new MockConfig(null, null); + jgitConfig = new MockConfig(null, null); systemGitConfig = new MockConfig(null, null); setCurrentPlatform(); } @@ -200,6 +213,11 @@ public class MockSystemReader extends SystemReader { } @Override + public FileBasedConfig getJGitConfig() { + return jgitConfig; + } + + @Override public StoredConfig getSystemConfig() throws IOException, ConfigInvalidException { return systemGitConfig; @@ -333,4 +351,9 @@ public class MockSystemReader extends SystemReader { return "MockSystemReader"; } + @Override + public FileBasedConfig openJGitConfig(Config parent, FS fs) { + return jgitConfig; + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FS_POSIXTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FS_POSIXTest.java index 87349a25a4..d7a7d86775 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FS_POSIXTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FS_POSIXTest.java @@ -59,7 +59,7 @@ import org.junit.Before; import org.junit.Test; public class FS_POSIXTest { - private SystemReader originalSystemReaderInstance; + private FileBasedConfig jgitConfig; private FileBasedConfig systemConfig; @@ -70,6 +70,7 @@ public class FS_POSIXTest { @Before public void setUp() throws Exception { tmp = Files.createTempDirectory("jgit_test_"); + MockSystemReader mockSystemReader = new MockSystemReader(); SystemReader.setInstance(mockSystemReader); @@ -78,7 +79,10 @@ public class FS_POSIXTest { // The MockSystemReader must be configured first since we need to use // the same one here FS.getFileStoreAttributes(tmp.getParent()); - systemConfig = new FileBasedConfig( + + jgitConfig = new FileBasedConfig(new File(tmp.toFile(), "jgitconfig"), + FS.DETECTED); + systemConfig = new FileBasedConfig(jgitConfig, new File(tmp.toFile(), "systemgitconfig"), FS.DETECTED); userConfig = new FileBasedConfig(systemConfig, new File(tmp.toFile(), "usergitconfig"), FS.DETECTED); @@ -89,16 +93,14 @@ public class FS_POSIXTest { userConfig.setBoolean(ConfigConstants.CONFIG_GC_SECTION, null, ConfigConstants.CONFIG_KEY_AUTODETACH, false); userConfig.save(); + mockSystemReader.setJGitConfig(jgitConfig); mockSystemReader.setSystemGitConfig(systemConfig); mockSystemReader.setUserGitConfig(userConfig); - - originalSystemReaderInstance = SystemReader.getInstance(); - SystemReader.setInstance(mockSystemReader); } @After public void tearDown() throws IOException { - SystemReader.setInstance(originalSystemReaderInstance); + SystemReader.setInstance(null); FileUtils.delete(tmp.toFile(), FileUtils.RECURSIVE | FileUtils.RETRY); } diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 5717512964..2d52081760 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -1,5 +1,13 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <component id="org.eclipse.jgit" version="2"> + <resource path="META-INF/MANIFEST.MF"> + <filter id="924844039"> + <message_arguments> + <message_argument value="5.5.2"/> + <message_argument value="5.5.0"/> + </message_arguments> + </filter> + </resource> <resource path="src/org/eclipse/jgit/dircache/DirCacheEntry.java" type="org.eclipse.jgit.dircache.DirCacheEntry"> <filter id="1142947843"> <message_arguments> @@ -57,6 +65,14 @@ </message_arguments> </filter> </resource> + <resource path="src/org/eclipse/jgit/lib/Constants.java" type="org.eclipse.jgit.lib.Constants"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.5.2"/> + <message_argument value="XDG_CONFIG_HOME"/> + </message_arguments> + </filter> + </resource> <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator"> <filter id="1142947843"> <message_arguments> @@ -159,6 +175,12 @@ </filter> </resource> <resource path="src/org/eclipse/jgit/util/SystemReader.java" type="org.eclipse.jgit.util.SystemReader"> + <filter id="336695337"> + <message_arguments> + <message_argument value="org.eclipse.jgit.util.SystemReader"/> + <message_argument value="openJGitConfig(Config, FS)"/> + </message_arguments> + </filter> <filter id="1142947843"> <message_arguments> <message_argument value="5.1.9"/> @@ -171,5 +193,17 @@ <message_argument value="getUserConfig()"/> </message_arguments> </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.5.2"/> + <message_argument value="getJGitConfig()"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="5.5.2"/> + <message_argument value="openJGitConfig(Config, FS)"/> + </message_arguments> + </filter> </resource> </component> diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 6b09faf0a1..b82512f71b 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -201,8 +201,10 @@ countingObjects=Counting objects corruptPack=Pack file {0} is corrupt, removing it from pack list createBranchFailedUnknownReason=Create branch failed for unknown reason createBranchUnexpectedResult=Create branch returned unexpected result {0} +createJGitConfigFailed=Creating JGit config directory {} failed createNewFileFailed=Could not create new file {0} createRequiresZeroOldId=Create requires old ID to be zero +createXDGConfigHomeFailed=Creating XDG_CONFIG_HOME directory {} failed credentialPassword=Password credentialPassphrase=Passphrase credentialUsername=Username diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 6a7d22df9d..61b145657d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -262,8 +262,10 @@ public class JGitText extends TranslationBundle { /***/ public String countingObjects; /***/ public String createBranchFailedUnknownReason; /***/ public String createBranchUnexpectedResult; + /***/ public String createJGitConfigFailed; /***/ public String createNewFileFailed; /***/ public String createRequiresZeroOldId; + /***/ public String createXDGConfigHomeFailed; /***/ public String credentialPassword; /***/ public String credentialPassphrase; /***/ public String credentialUsername; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index a084c82871..9274fc6777 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -343,6 +343,15 @@ public final class Constants { public static final String GIT_CONFIG_NOSYSTEM_KEY = "GIT_CONFIG_NOSYSTEM"; /** + * The key of the XDG_CONFIG_HOME directory defined in the XDG base + * directory specification, see + * {@link "https://wiki.archlinux.org/index.php/XDG_Base_Directory"} + * + * @since 5.5.2 + */ + public static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME"; + + /** * The environment variable that limits how close to the root of the file * systems JGit will traverse when looking for a repository root. */ 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 29519298c4..b3d01d3b48 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -545,9 +545,9 @@ public abstract class FS { private static void saveToConfig(FileStore s, FileStoreAttributes c) { - StoredConfig userConfig; + StoredConfig jgitConfig; try { - userConfig = SystemReader.getInstance().getUserConfig(); + jgitConfig = SystemReader.getInstance().getJGitConfig(); } catch (IOException | ConfigInvalidException e) { LOG.error(JGitText.get().saveFileStoreAttributesFailed, e); return; @@ -568,20 +568,19 @@ public abstract class FS { String key = getConfigKey(s); while (!succeeded && retries < max_retries) { try { - userConfig.load(); - userConfig.setString( + jgitConfig.setString( ConfigConstants.CONFIG_FILESYSTEM_SECTION, key, ConfigConstants.CONFIG_KEY_TIMESTAMP_RESOLUTION, String.format("%d %s", //$NON-NLS-1$ Long.valueOf(resolutionValue), resolutionUnit.name().toLowerCase())); - userConfig.setString( + jgitConfig.setString( ConfigConstants.CONFIG_FILESYSTEM_SECTION, key, ConfigConstants.CONFIG_KEY_MIN_RACY_THRESHOLD, String.format("%d %s", //$NON-NLS-1$ Long.valueOf(minRacyThresholdValue), minRacyThresholdUnit.name().toLowerCase())); - userConfig.save(); + jgitConfig.save(); succeeded = true; } catch (LockFailedException e) { // race with another thread, wait a bit and try again @@ -590,11 +589,11 @@ public abstract class FS { if (retries < max_retries) { Thread.sleep(100); LOG.debug("locking {} failed, retries {}/{}", //$NON-NLS-1$ - userConfig, Integer.valueOf(retries), + jgitConfig, Integer.valueOf(retries), Integer.valueOf(max_retries)); } else { LOG.warn(MessageFormat.format( - JGitText.get().lockFailedRetry, userConfig, + JGitText.get().lockFailedRetry, jgitConfig, Integer.valueOf(retries))); } } catch (InterruptedException e1) { @@ -603,12 +602,7 @@ public abstract class FS { } } catch (IOException e) { LOG.error(MessageFormat.format( - JGitText.get().cannotSaveConfig, userConfig), e); - break; - } catch (ConfigInvalidException e) { - LOG.error(MessageFormat.format( - JGitText.get().repositoryConfigFileInvalid, - userConfig, e.getMessage())); + JGitText.get().cannotSaveConfig, jgitConfig), e); break; } } 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 a2253bc118..59184ffda2 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,10 @@ import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedAction; import java.text.DateFormat; @@ -60,6 +64,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectChecker; @@ -137,6 +142,42 @@ public abstract class SystemReader { fs); } + private Path getXDGConfigHome(FS fs) { + String configHomePath = getenv(Constants.XDG_CONFIG_HOME); + if (StringUtils.isEmptyOrNull(configHomePath)) { + configHomePath = new File(fs.userHome(), ".config") //$NON-NLS-1$ + .getAbsolutePath(); + } + try { + Path xdgHomePath = Paths.get(configHomePath); + Files.createDirectories(xdgHomePath); + return xdgHomePath; + } catch (IOException | InvalidPathException e) { + LOG.error(JGitText.get().createXDGConfigHomeFailed, + configHomePath, e); + } + return null; + } + + @Override + public FileBasedConfig openJGitConfig(Config parent, FS fs) { + Path xdgPath = getXDGConfigHome(fs); + if (xdgPath != null) { + Path configPath = null; + try { + configPath = xdgPath.resolve("jgit"); //$NON-NLS-1$ + Files.createDirectories(configPath); + configPath = configPath.resolve(Constants.CONFIG); + return new FileBasedConfig(parent, configPath.toFile(), fs); + } catch (IOException e) { + LOG.error(JGitText.get().createJGitConfigFailed, configPath, + e); + } + } + return new FileBasedConfig(parent, + new File(fs.userHome(), ".jgitconfig"), fs); //$NON-NLS-1$ + } + @Override public String getHostname() { if (hostname == null) { @@ -198,6 +239,8 @@ public abstract class SystemReader { private AtomicReference<FileBasedConfig> userConfig = new AtomicReference<>(); + private AtomicReference<FileBasedConfig> jgitConfig = new AtomicReference<>(); + private void init() { // Creating ObjectChecker must be deferred. Unit tests change // behavior of is{Windows,MacOS} in constructor of subclass. @@ -275,6 +318,22 @@ public abstract class SystemReader { public abstract FileBasedConfig openSystemConfig(Config parent, FS fs); /** + * Open the jgit configuration located at $XDG_CONFIG_HOME/jgit/config. Use + * {@link #getJGitConfig()} to get the current jgit configuration in the + * user home since it manages automatic reloading when the jgit config file + * was modified and avoids unnecessary reloads. + * + * @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. + * @return the jgit configuration located at $XDG_CONFIG_HOME/jgit/config + * @since 5.5.2 + */ + public abstract FileBasedConfig openJGitConfig(Config parent, FS fs); + + /** * Get the git configuration found in the user home. The configuration will * be reloaded automatically if the configuration file was modified. Also * reloads the system config if the system config file was modified. If the @@ -302,6 +361,31 @@ public abstract class SystemReader { } /** + * Get the jgit configuration located at $XDG_CONFIG_HOME/jgit/config. The + * configuration will be reloaded automatically if the configuration file + * was modified. If the configuration file wasn't modified returns the + * cached configuration. + * + * @return the jgit configuration located at $XDG_CONFIG_HOME/jgit/config + * @throws ConfigInvalidException + * if configuration is invalid + * @throws IOException + * if something went wrong when reading files + * @since 5.5.2 + */ + public StoredConfig getJGitConfig() + throws ConfigInvalidException, IOException { + FileBasedConfig c = jgitConfig.get(); + if (c == null) { + jgitConfig.compareAndSet(null, + openJGitConfig(null, FS.DETECTED)); + c = jgitConfig.get(); + } + updateAll(c); + return c; + } + + /** * Get the gitconfig configuration found in the system-wide "etc" directory. * The configuration will be reloaded automatically if the configuration * file was modified otherwise returns the cached system level config. @@ -319,7 +403,7 @@ public abstract class SystemReader { FileBasedConfig c = systemConfig.get(); if (c == null) { systemConfig.compareAndSet(null, - openSystemConfig(null, FS.DETECTED)); + openSystemConfig(getJGitConfig(), FS.DETECTED)); c = systemConfig.get(); } updateAll(c); |