* stable-4.8: Silence boxing warning Prepare 4.5.5-SNAPSHOT builds JGit v4.5.4.201711221230-r Fix LockFile semantics when running on NFS Honor trustFolderStats also when reading packed-refs Prepare 4.5.4-SNAPSHOT builds JGit v4.5.3.201708160445-r Change-Id: I7cf2e48934195430b3945b6d74b092f93a3ccd36 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>tags/v4.9.1.201712030800-r
@@ -1,35 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<component id="org.eclipse.jgit.junit" version="2"> | |||
<resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase"> | |||
<filter comment="OK to use internal implementation in tests" id="643842064"> | |||
<message_arguments> | |||
<message_argument value="FileRepository"/> | |||
<message_argument value="LocalDiskRepositoryTestCase"/> | |||
<message_argument value="createBareRepository()"/> | |||
</message_arguments> | |||
</filter> | |||
<filter comment="OK to use internal implementation in tests" id="643842064"> | |||
<message_arguments> | |||
<message_argument value="FileRepository"/> | |||
<message_argument value="LocalDiskRepositoryTestCase"/> | |||
<message_argument value="createRepository(boolean, boolean)"/> | |||
</message_arguments> | |||
</filter> | |||
<filter comment="OK to use internal implementation in tests" id="643842064"> | |||
<message_arguments> | |||
<message_argument value="FileRepository"/> | |||
<message_argument value="LocalDiskRepositoryTestCase"/> | |||
<message_argument value="createWorkRepository()"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/junit/RepositoryTestCase.java" type="org.eclipse.jgit.junit.RepositoryTestCase"> | |||
<filter comment="OK to use internal implementation in tests" id="627060751"> | |||
<message_arguments> | |||
<message_argument value="FileRepository"/> | |||
<message_argument value="RepositoryTestCase"/> | |||
<message_argument value="db"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
</component> |
@@ -1,50 +1,49 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<component id="org.eclipse.jgit" version="2"> | |||
<resource path="src/org/eclipse/jgit/lib/ReflogEntry.java" type="org.eclipse.jgit.lib.ReflogEntry"> | |||
<filter comment="adding enum constant does not break binary compatibility" id="403767336"> | |||
<resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants"> | |||
<filter id="336658481"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.lib.ReflogEntry"/> | |||
<message_argument value="PREFIX_CREATED"/> | |||
<message_argument value="org.eclipse.jgit.lib.ConfigConstants"/> | |||
<message_argument value="CONFIG_KEY_IN_CORE_LIMIT"/> | |||
</message_arguments> | |||
</filter> | |||
<filter comment="adding enum constant does not break binary compatibility" id="403767336"> | |||
<filter id="336658481"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.lib.ReflogEntry"/> | |||
<message_argument value="PREFIX_FAST_FORWARD"/> | |||
<message_argument value="org.eclipse.jgit.lib.ConfigConstants"/> | |||
<message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/> | |||
</message_arguments> | |||
</filter> | |||
<filter comment="adding enum constant does not break binary compatibility" id="403767336"> | |||
<filter id="336658481"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.lib.ReflogEntry"/> | |||
<message_argument value="PREFIX_FORCED_UPDATE"/> | |||
<message_argument value="org.eclipse.jgit.lib.ConfigConstants"/> | |||
<message_argument value="CONFIG_MERGE_SECTION"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/merge/MergeStrategy.java" type="org.eclipse.jgit.merge.MergeStrategy"> | |||
<filter comment="OSGi semantic versioning allows breaking implementors of an API in a minor version" id="336695337"> | |||
<filter id="1141899266"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.merge.MergeStrategy"/> | |||
<message_argument value="newMerger(ObjectInserter, Config)"/> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.9"/> | |||
<message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger"> | |||
<filter comment="OSGi semantic versioning allows breaking implementors of an API in a minor version" id="338792546"> | |||
<resource path="src/org/eclipse/jgit/lib/ReflogEntry.java" type="org.eclipse.jgit.lib.ReflogEntry"> | |||
<filter comment="adding enum constant does not break binary compatibility" id="403767336"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.merge.ResolveMerger"/> | |||
<message_argument value="processEntry(CanonicalTreeParser, CanonicalTreeParser, CanonicalTreeParser, DirCacheBuildIterator, WorkingTreeIterator, boolean)"/> | |||
<message_argument value="org.eclipse.jgit.lib.ReflogEntry"/> | |||
<message_argument value="PREFIX_CREATED"/> | |||
</message_arguments> | |||
</filter> | |||
<filter id="1141899266"> | |||
<filter comment="adding enum constant does not break binary compatibility" id="403767336"> | |||
<message_arguments> | |||
<message_argument value="3.5"/> | |||
<message_argument value="4.9"/> | |||
<message_argument value="processEntry(CanonicalTreeParser, CanonicalTreeParser, CanonicalTreeParser, DirCacheBuildIterator, WorkingTreeIterator, boolean)"/> | |||
<message_argument value="org.eclipse.jgit.lib.ReflogEntry"/> | |||
<message_argument value="PREFIX_FAST_FORWARD"/> | |||
</message_arguments> | |||
</filter> | |||
<filter id="1143996420"> | |||
<filter comment="adding enum constant does not break binary compatibility" id="403767336"> | |||
<message_arguments> | |||
<message_argument value="processEntry(CanonicalTreeParser, CanonicalTreeParser, CanonicalTreeParser, DirCacheBuildIterator, WorkingTreeIterator, boolean)"/> | |||
<message_argument value="org.eclipse.jgit.lib.ReflogEntry"/> | |||
<message_argument value="PREFIX_FORCED_UPDATE"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
@@ -68,4 +67,20 @@ | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS"> | |||
<filter id="1141899266"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.9"/> | |||
<message_argument value="createNewFile(File)"/> | |||
</message_arguments> | |||
</filter> | |||
<filter id="1141899266"> | |||
<message_arguments> | |||
<message_argument value="4.5"/> | |||
<message_argument value="4.9"/> | |||
<message_argument value="supportsAtomicCreateNewFile()"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
</component> |
@@ -168,7 +168,7 @@ public class LockFile { | |||
*/ | |||
public boolean lock() throws IOException { | |||
FileUtils.mkdirs(lck.getParentFile(), true); | |||
if (lck.createNewFile()) { | |||
if (FS.DETECTED.createNewFile(lck)) { | |||
haveLck = true; | |||
try { | |||
os = new FileOutputStream(lck); |
@@ -86,6 +86,7 @@ import org.eclipse.jgit.errors.MissingObjectException; | |||
import org.eclipse.jgit.errors.ObjectWritingException; | |||
import org.eclipse.jgit.events.RefsChangedEvent; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.ConfigConstants; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
import org.eclipse.jgit.lib.ObjectIdRef; | |||
@@ -901,14 +902,20 @@ public class RefDirectory extends RefDatabase { | |||
} | |||
private PackedRefList getPackedRefs() throws IOException { | |||
boolean trustFolderStat = getRepository().getConfig().getBoolean( | |||
ConfigConstants.CONFIG_CORE_SECTION, | |||
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true); | |||
final PackedRefList curList = packedRefs.get(); | |||
if (!curList.snapshot.isModified(packedRefsFile)) | |||
if (trustFolderStat && !curList.snapshot.isModified(packedRefsFile)) { | |||
return curList; | |||
} | |||
final PackedRefList newList = readPackedRefs(); | |||
if (packedRefs.compareAndSet(curList, newList) | |||
&& !curList.id.equals(newList.id)) | |||
&& !curList.id.equals(newList.id)) { | |||
modCnt.incrementAndGet(); | |||
} | |||
return newList; | |||
} | |||
@@ -346,6 +346,13 @@ public class ConfigConstants { | |||
*/ | |||
public static final String CONFIG_KEY_TRUSTFOLDERSTAT = "trustfolderstat"; | |||
/** | |||
* The "supportsAtomicFileCreation" key in the "core section" | |||
* | |||
* @since 4.5 | |||
*/ | |||
public static final String CONFIG_KEY_SUPPORTSATOMICFILECREATION = "supportsatomicfilecreation"; | |||
/** | |||
* The "noprefix" key in the "diff section" | |||
* @since 3.0 |
@@ -235,6 +235,21 @@ public abstract class FS { | |||
*/ | |||
public abstract boolean supportsExecute(); | |||
/** | |||
* Does this file system support atomic file creation via | |||
* java.io.File#createNewFile()? In certain environments (e.g. on NFS) it is | |||
* not guaranteed that when two file system clients run createNewFile() in | |||
* parallel only one will succeed. In such cases both clients may think they | |||
* created a new file. | |||
* | |||
* @return true if this implementation support atomic creation of new | |||
* Files by {@link File#createNewFile()} | |||
* @since 4.5 | |||
*/ | |||
public boolean supportsAtomicCreateNewFile() { | |||
return true; | |||
} | |||
/** | |||
* Does this operating system and JRE supports symbolic links. The | |||
* capability to handle symbolic links is detected at runtime. | |||
@@ -782,6 +797,22 @@ public abstract class FS { | |||
FileUtils.createSymLink(path, target); | |||
} | |||
/** | |||
* Create a new file. See {@link File#createNewFile()}. Subclasses of this | |||
* class may take care to provide a safe implementation for this even if | |||
* {@link #supportsAtomicCreateNewFile()} is <code>false</code> | |||
* | |||
* @param path | |||
* the file to be created | |||
* @return <code>true</code> if the file was created, <code>false</code> if | |||
* the file already existed | |||
* @throws IOException | |||
* @since 4.5 | |||
*/ | |||
public boolean createNewFile(File path) throws IOException { | |||
return path.createNewFile(); | |||
} | |||
/** | |||
* See {@link FileUtils#relativizePath(String, String, String, boolean)}. | |||
* |
@@ -50,6 +50,7 @@ import java.io.PrintStream; | |||
import java.nio.charset.Charset; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.Paths; | |||
import java.nio.file.attribute.PosixFilePermission; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
@@ -58,8 +59,11 @@ import java.util.Set; | |||
import org.eclipse.jgit.api.errors.JGitInternalException; | |||
import org.eclipse.jgit.errors.CommandFailedException; | |||
import org.eclipse.jgit.errors.ConfigInvalidException; | |||
import org.eclipse.jgit.lib.ConfigConstants; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.storage.file.FileBasedConfig; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
@@ -74,6 +78,10 @@ public class FS_POSIX extends FS { | |||
private static final int DEFAULT_UMASK = 0022; | |||
private volatile int umask = -1; | |||
private volatile boolean supportsUnixNLink = true; | |||
private volatile Boolean supportsAtomicCreateNewFile; | |||
/** Default constructor. */ | |||
protected FS_POSIX() { | |||
} | |||
@@ -91,6 +99,34 @@ public class FS_POSIX extends FS { | |||
} | |||
} | |||
@SuppressWarnings("boxing") | |||
private void determineAtomicFileCreationSupport() { | |||
// @TODO: enhance SystemReader to support this without copying code | |||
Boolean ret = getAtomicFileCreationSupportOption( | |||
SystemReader.getInstance().openUserConfig(null, this)); | |||
if (ret == null && StringUtils.isEmptyOrNull(SystemReader.getInstance() | |||
.getenv(Constants.GIT_CONFIG_NOSYSTEM_KEY))) { | |||
ret = getAtomicFileCreationSupportOption( | |||
SystemReader.getInstance().openSystemConfig(null, this)); | |||
} | |||
supportsAtomicCreateNewFile = (ret == null) || ret; | |||
} | |||
private Boolean getAtomicFileCreationSupportOption(FileBasedConfig config) { | |||
try { | |||
config.load(); | |||
String value = config.getString(ConfigConstants.CONFIG_CORE_SECTION, | |||
null, | |||
ConfigConstants.CONFIG_KEY_SUPPORTSATOMICFILECREATION); | |||
if (value == null) { | |||
return null; | |||
} | |||
return Boolean.valueOf(StringUtils.toBoolean(value)); | |||
} catch (IOException | ConfigInvalidException e) { | |||
return Boolean.TRUE; | |||
} | |||
} | |||
@Override | |||
public FS newInstance() { | |||
return new FS_POSIX(this); | |||
@@ -301,4 +337,56 @@ public class FS_POSIX extends FS { | |||
return hookPath.toFile(); | |||
return null; | |||
} | |||
@Override | |||
public boolean supportsAtomicCreateNewFile() { | |||
if (supportsAtomicCreateNewFile == null) { | |||
determineAtomicFileCreationSupport(); | |||
} | |||
return supportsAtomicCreateNewFile.booleanValue(); | |||
} | |||
@Override | |||
@SuppressWarnings("boxing") | |||
/** | |||
* An implementation of the File#createNewFile() semantics which works also | |||
* on NFS. If the config option | |||
* {@code core.supportsAtomicCreateNewFile = true} (which is the default) | |||
* then simply File#createNewFile() is called. | |||
* | |||
* But if {@code core.supportsAtomicCreateNewFile = false} then after | |||
* successful creation of the lock file a hardlink to that lock file is | |||
* created and the attribute nlink of the lock file is checked to be 2. If | |||
* multiple clients manage to create the same lock file nlink would be | |||
* greater than 2 showing the error. | |||
* | |||
* @see https://www.time-travellers.org/shane/papers/NFS_considered_harmful.html | |||
* @since 4.5 | |||
*/ | |||
public boolean createNewFile(File lock) throws IOException { | |||
if (!lock.createNewFile()) { | |||
return false; | |||
} | |||
if (supportsAtomicCreateNewFile() || !supportsUnixNLink) { | |||
return true; | |||
} | |||
Path lockPath = lock.toPath(); | |||
Path link = Files.createLink(Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$ | |||
lockPath); | |||
try { | |||
Integer nlink = (Integer) (Files.getAttribute(lockPath, | |||
"unix:nlink")); //$NON-NLS-1$ | |||
if (nlink != 2) { | |||
LOG.warn("nlink of link to lock file {0} was not 2 but {1}", //$NON-NLS-1$ | |||
lock.getPath(), nlink); | |||
return false; | |||
} | |||
return true; | |||
} catch (UnsupportedOperationException | IllegalArgumentException e) { | |||
supportsUnixNLink = false; | |||
return true; | |||
} finally { | |||
Files.delete(link); | |||
} | |||
} | |||
} |