Merge branch 'stable-5.5'

* stable-5.5:
  BaseReceivePack: Fix the format
  Prepend hostname to subsection used to store file timestamp resolution
  Store filesystem timestamp resolution in extra jgit config
  SystemReader: extract updating config and its parents if outdated

Change-Id: Iecfddce8081303af29badcdcd3d72a0da50c964f
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Matthias Sohn 2019-11-11 11:07:01 -08:00
commit 826317942f
10 changed files with 197 additions and 39 deletions

View File

@ -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 void setUp() throws Exception {
// 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 void setUp() throws Exception {
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");

View File

@ -103,6 +103,8 @@ public String toString() {
private FileBasedConfig userGitConfig;
private FileBasedConfig jgitConfig;
FileBasedConfig systemGitConfig;
/**
@ -118,6 +120,16 @@ public FileBasedConfig setUserGitConfig(FileBasedConfig userGitConfig) {
return old;
}
/**
* 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
*
@ -142,6 +154,7 @@ public MockSystemReader() {
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();
}
@ -199,6 +212,11 @@ public StoredConfig getUserConfig()
return userGitConfig;
}
@Override
public FileBasedConfig getJGitConfig() {
return jgitConfig;
}
@Override
public StoredConfig getSystemConfig()
throws IOException, ConfigInvalidException {
@ -333,4 +351,9 @@ public String toString() {
return "MockSystemReader";
}
@Override
public FileBasedConfig openJGitConfig(Config parent, FS fs) {
return jgitConfig;
}
}

View File

@ -59,7 +59,7 @@
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 void setUp() throws Exception {
// 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 void setUp() throws Exception {
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);
}

View File

@ -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

View File

@ -262,8 +262,10 @@ public static JGitText get() {
/***/ 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;

View File

@ -128,10 +128,22 @@ public Config(Config defaultConfig) {
state = new AtomicReference<>(newState());
}
/**
* Retrieves this config's base config.
*
* @return the base configuration of this config.
*
* @since 5.5.2
*/
public Config getBaseConfig() {
return baseConfig;
}
/**
* Check if a given string is the "missing" value.
*
* @param value string to be checked.
* @param value
* string to be checked.
* @return true if the given string is the "missing" value.
* @since 5.4
*/

View File

@ -342,6 +342,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.

View File

@ -1361,7 +1361,7 @@ private void parseShallow(String idStr) throws PackProtocolException {
}
static ReceiveCommand parseCommand(String line) throws PackProtocolException {
if (line == null || line.length() < 83) {
if (line == null || line.length() < 83) {
throw new PackProtocolException(
JGitText.get().errorInvalidProtocolWantedOldNewRef);
}

View File

@ -246,8 +246,9 @@ private static void setBackground(boolean async) {
background.set(async);
}
private static final String javaVersionPrefix = System
.getProperty("java.vendor") + '|' //$NON-NLS-1$
private static final String javaVersionPrefix = SystemReader
.getInstance().getHostname() + '|'
+ System.getProperty("java.vendor") + '|' //$NON-NLS-1$
+ System.getProperty("java.version") + '|'; //$NON-NLS-1$
private static final Duration FALLBACK_MIN_RACY_INTERVAL = Duration
@ -547,9 +548,9 @@ private static Optional<FileStoreAttributes> readFromConfig(
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;
@ -570,20 +571,19 @@ private static void saveToConfig(FileStore s,
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
@ -592,11 +592,11 @@ private static void saveToConfig(FileStore s,
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) {
@ -605,12 +605,7 @@ private static void saveToConfig(FileStore s,
}
} 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;
}
}

View File

@ -50,6 +50,10 @@
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 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 FileBasedConfig openUserConfig(Config parent, FS fs) {
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 static void setInstance(SystemReader newReader) {
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.
@ -274,6 +317,22 @@ protected final void setPlatformChecker() {
*/
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
@ -288,20 +347,41 @@ protected final void setPlatformChecker() {
* @since 5.1.9
*/
public StoredConfig getUserConfig()
throws IOException, ConfigInvalidException {
throws ConfigInvalidException, IOException {
FileBasedConfig c = userConfig.get();
if (c == null) {
userConfig.compareAndSet(null,
openUserConfig(getSystemConfig(), FS.DETECTED));
c = userConfig.get();
} else {
// Ensure the parent is up to date
getSystemConfig();
}
if (c.isOutdated()) {
LOG.debug("loading user config {}", userConfig); //$NON-NLS-1$
c.load();
// on the very first call this will check a second time if the system
// config is outdated
updateAll(c);
return c;
}
/**
* 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;
}
@ -319,20 +399,45 @@ public StoredConfig getUserConfig()
* @since 5.1.9
*/
public StoredConfig getSystemConfig()
throws IOException, ConfigInvalidException {
throws ConfigInvalidException, IOException {
FileBasedConfig c = systemConfig.get();
if (c == null) {
systemConfig.compareAndSet(null,
openSystemConfig(null, FS.DETECTED));
openSystemConfig(getJGitConfig(), FS.DETECTED));
c = systemConfig.get();
}
if (c.isOutdated()) {
LOG.debug("loading system config {}", systemConfig); //$NON-NLS-1$
c.load();
}
updateAll(c);
return c;
}
/**
* Update config and its parents if they seem modified
*
* @param config
* configuration to reload if outdated
* @throws ConfigInvalidException
* if configuration is invalid
* @throws IOException
* if something went wrong when reading files
*/
private void updateAll(Config config)
throws ConfigInvalidException, IOException {
if (config == null) {
return;
}
updateAll(config.getBaseConfig());
if (config instanceof FileBasedConfig) {
FileBasedConfig cfg = (FileBasedConfig) config;
if (!cfg.getFile().exists()) {
return;
}
if (cfg.isOutdated()) {
LOG.debug("loading config {}", cfg); //$NON-NLS-1$
cfg.load();
}
}
}
/**
* Get the current system time
*