LockFile.commit fails if another thread concurrently reads the base file. The problem is fixed by retrying the rename operation if it fails. Change-Id: I6bb76ea7f2e6e90e3ddc45f9dd4d69bd1b6fa1eb Bug: 308506 Signed-off-by: Jens Baumgart <jens.baumgart@sap.com>tags/v0.9.1
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.storage.file.FileBasedConfig; | import org.eclipse.jgit.storage.file.FileBasedConfig; | ||||
import org.eclipse.jgit.storage.file.LockFile; | import org.eclipse.jgit.storage.file.LockFile; | ||||
import org.eclipse.jgit.util.FS; | |||||
/** | /** | ||||
* Manages the {@code .eclipse_iplog} file in a project. | * Manages the {@code .eclipse_iplog} file in a project. | ||||
* | * | ||||
* @param file | * @param file | ||||
* local file to update with current CQ records. | * local file to update with current CQ records. | ||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
* @param base | * @param base | ||||
* base https:// URL of the IPzilla server. | * base https:// URL of the IPzilla server. | ||||
* @param username | * @param username | ||||
* the local file cannot be read, as it is not a valid | * the local file cannot be read, as it is not a valid | ||||
* configuration file format. | * configuration file format. | ||||
*/ | */ | ||||
public void syncCQs(File file, URL base, String username, String password) | |||||
throws IOException, ConfigInvalidException { | |||||
public void syncCQs(File file, FS fs, URL base, String username, | |||||
String password) throws IOException, ConfigInvalidException { | |||||
if (!file.getParentFile().exists()) | if (!file.getParentFile().exists()) | ||||
file.getParentFile().mkdirs(); | file.getParentFile().mkdirs(); | ||||
LockFile lf = new LockFile(file); | |||||
LockFile lf = new LockFile(file, fs); | |||||
if (!lf.lock()) | if (!lf.lock()) | ||||
throw new IOException(MessageFormat.format(IpLogText.get().cannotLock, file)); | throw new IOException(MessageFormat.format(IpLogText.get().cannotLock, file)); | ||||
try { | try { | ||||
FileBasedConfig cfg = new FileBasedConfig(file); | |||||
FileBasedConfig cfg = new FileBasedConfig(file, fs); | |||||
cfg.load(); | cfg.load(); | ||||
loadFrom(cfg); | loadFrom(cfg); | ||||
import org.eclipse.jgit.storage.file.FileRepository; | import org.eclipse.jgit.storage.file.FileRepository; | ||||
import org.eclipse.jgit.storage.file.WindowCache; | import org.eclipse.jgit.storage.file.WindowCache; | ||||
import org.eclipse.jgit.storage.file.WindowCacheConfig; | import org.eclipse.jgit.storage.file.WindowCacheConfig; | ||||
import org.eclipse.jgit.util.FS; | |||||
import org.eclipse.jgit.util.IO; | import org.eclipse.jgit.util.IO; | ||||
import org.eclipse.jgit.util.SystemReader; | import org.eclipse.jgit.util.SystemReader; | ||||
mockSystemReader = new MockSystemReader(); | mockSystemReader = new MockSystemReader(); | ||||
mockSystemReader.userGitConfig = new FileBasedConfig(new File(trash, | mockSystemReader.userGitConfig = new FileBasedConfig(new File(trash, | ||||
"usergitconfig")); | |||||
"usergitconfig"), FS.DETECTED); | |||||
ceilTestDirectories(getCeilings()); | ceilTestDirectories(getCeilings()); | ||||
SystemReader.setInstance(mockSystemReader); | SystemReader.setInstance(mockSystemReader); | ||||
init(Constants.GIT_AUTHOR_EMAIL_KEY); | init(Constants.GIT_AUTHOR_EMAIL_KEY); | ||||
init(Constants.GIT_COMMITTER_NAME_KEY); | init(Constants.GIT_COMMITTER_NAME_KEY); | ||||
init(Constants.GIT_COMMITTER_EMAIL_KEY); | init(Constants.GIT_COMMITTER_EMAIL_KEY); | ||||
userGitConfig = new FileBasedConfig(null) { | |||||
userGitConfig = new FileBasedConfig(null, null) { | |||||
@Override | @Override | ||||
public void load() throws IOException, ConfigInvalidException { | public void load() throws IOException, ConfigInvalidException { | ||||
// Do nothing | // Do nothing |
private void writeFile(final File p, final byte[] bin) throws IOException, | private void writeFile(final File p, final byte[] bin) throws IOException, | ||||
ObjectWritingException { | ObjectWritingException { | ||||
final LockFile lck = new LockFile(p); | |||||
final LockFile lck = new LockFile(p, db.getFS()); | |||||
if (!lck.lock()) | if (!lck.lock()) | ||||
throw new ObjectWritingException("Can't write " + p); | throw new ObjectWritingException("Can't write " + p); | ||||
try { | try { |
final ObjectId id = db.resolve(Constants.HEAD); | final ObjectId id = db.resolve(Constants.HEAD); | ||||
if (!ObjectId.isId(head) && id != null) { | if (!ObjectId.isId(head) && id != null) { | ||||
final LockFile lf; | final LockFile lf; | ||||
lf = new LockFile(new File(db.getDirectory(), Constants.HEAD)); | |||||
lf = new LockFile(new File(db.getDirectory(), Constants.HEAD), db.getFS()); | |||||
if (!lf.lock()) | if (!lf.lock()) | ||||
throw new IOException(MessageFormat.format(CLIText.get().cannotLock, Constants.HEAD)); | throw new IOException(MessageFormat.format(CLIText.get().cannotLock, Constants.HEAD)); | ||||
lf.write(id); | lf.write(id); | ||||
protected void writeFile(final String name, final byte[] content) | protected void writeFile(final String name, final byte[] content) | ||||
throws IOException { | throws IOException { | ||||
final File file = new File(db.getDirectory(), name); | final File file = new File(db.getDirectory(), name); | ||||
final LockFile lck = new LockFile(file); | |||||
final LockFile lck = new LockFile(file, db.getFS()); | |||||
if (!lck.lock()) | if (!lck.lock()) | ||||
throw new ObjectWritingException(MessageFormat.format(CLIText.get().cantWrite, file)); | throw new ObjectWritingException(MessageFormat.format(CLIText.get().cantWrite, file)); | ||||
try { | try { |
if (output != null) { | if (output != null) { | ||||
if (!output.getParentFile().exists()) | if (!output.getParentFile().exists()) | ||||
output.getParentFile().mkdirs(); | output.getParentFile().mkdirs(); | ||||
LockFile lf = new LockFile(output); | |||||
LockFile lf = new LockFile(output, db.getFS()); | |||||
if (!lf.lock()) | if (!lf.lock()) | ||||
throw die(MessageFormat.format(CLIText.get().cannotLock, output)); | throw die(MessageFormat.format(CLIText.get().cannotLock, output)); | ||||
try { | try { |
output = new File(db.getWorkTree(), IpLogMeta.IPLOG_CONFIG_FILE); | output = new File(db.getWorkTree(), IpLogMeta.IPLOG_CONFIG_FILE); | ||||
IpLogMeta meta = new IpLogMeta(); | IpLogMeta meta = new IpLogMeta(); | ||||
meta.syncCQs(output, ipzilla, username, password); | |||||
meta.syncCQs(output, db.getFS(), ipzilla, username, password); | |||||
} | } | ||||
} | } |
if (new File(name).isFile()) { | if (new File(name).isFile()) { | ||||
final DirCache dirc; | final DirCache dirc; | ||||
try { | try { | ||||
dirc = DirCache.read(new File(name)); | |||||
dirc = DirCache.read(new File(name), FS.DETECTED); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new CmdLineException(MessageFormat.format(CLIText.get().notAnIndexFile, name), e); | throw new CmdLineException(MessageFormat.format(CLIText.get().notAnIndexFile, name), e); | ||||
} | } |
final File idx = new File(db.getDirectory(), "tmp_index"); | final File idx = new File(db.getDirectory(), "tmp_index"); | ||||
assertFalse(idx.exists()); | assertFalse(idx.exists()); | ||||
final DirCache dc = DirCache.read(idx); | |||||
final DirCache dc = DirCache.read(idx, db.getFS()); | |||||
assertNotNull(dc); | assertNotNull(dc); | ||||
assertEquals(0, dc.getEntryCount()); | assertEquals(0, dc.getEntryCount()); | ||||
} | } | ||||
assertFalse(idx.exists()); | assertFalse(idx.exists()); | ||||
assertFalse(lck.exists()); | assertFalse(lck.exists()); | ||||
final DirCache dc = DirCache.lock(idx); | |||||
final DirCache dc = DirCache.lock(idx, db.getFS()); | |||||
assertNotNull(dc); | assertNotNull(dc); | ||||
assertFalse(idx.exists()); | assertFalse(idx.exists()); | ||||
assertTrue(lck.exists()); | assertTrue(lck.exists()); |
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.treewalk.TreeWalk; | import org.eclipse.jgit.treewalk.TreeWalk; | ||||
import org.eclipse.jgit.util.FS; | |||||
import org.eclipse.jgit.util.JGitTestUtil; | import org.eclipse.jgit.util.JGitTestUtil; | ||||
public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase { | public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase { | ||||
public void testReadIndex_LsFiles() throws Exception { | public void testReadIndex_LsFiles() throws Exception { | ||||
final Map<String, CGitIndexRecord> ls = readLsFiles(); | final Map<String, CGitIndexRecord> ls = readLsFiles(); | ||||
final DirCache dc = new DirCache(index); | |||||
final DirCache dc = new DirCache(index, FS.DETECTED); | |||||
assertEquals(0, dc.getEntryCount()); | assertEquals(0, dc.getEntryCount()); | ||||
dc.read(); | dc.read(); | ||||
assertEquals(ls.size(), dc.getEntryCount()); | assertEquals(ls.size(), dc.getEntryCount()); | ||||
public void testTreeWalk_LsFiles() throws Exception { | public void testTreeWalk_LsFiles() throws Exception { | ||||
final Repository db = createBareRepository(); | final Repository db = createBareRepository(); | ||||
final Map<String, CGitIndexRecord> ls = readLsFiles(); | final Map<String, CGitIndexRecord> ls = readLsFiles(); | ||||
final DirCache dc = new DirCache(index); | |||||
final DirCache dc = new DirCache(index, db.getFS()); | |||||
assertEquals(0, dc.getEntryCount()); | assertEquals(0, dc.getEntryCount()); | ||||
dc.read(); | dc.read(); | ||||
assertEquals(ls.size(), dc.getEntryCount()); | assertEquals(ls.size(), dc.getEntryCount()); | ||||
} | } | ||||
public void testUnsupportedOptionalExtension() throws Exception { | public void testUnsupportedOptionalExtension() throws Exception { | ||||
final DirCache dc = new DirCache(pathOf("gitgit.index.ZZZZ")); | |||||
final DirCache dc = new DirCache(pathOf("gitgit.index.ZZZZ"), | |||||
FS.DETECTED); | |||||
dc.read(); | dc.read(); | ||||
assertEquals(1, dc.getEntryCount()); | assertEquals(1, dc.getEntryCount()); | ||||
assertEquals("A", dc.getEntry(0).getPathString()); | assertEquals("A", dc.getEntry(0).getPathString()); | ||||
} | } | ||||
public void testUnsupportedRequiredExtension() throws Exception { | public void testUnsupportedRequiredExtension() throws Exception { | ||||
final DirCache dc = new DirCache(pathOf("gitgit.index.aaaa")); | |||||
final DirCache dc = new DirCache(pathOf("gitgit.index.aaaa"), | |||||
FS.DETECTED); | |||||
try { | try { | ||||
dc.read(); | dc.read(); | ||||
fail("Cache loaded an unsupported extension"); | fail("Cache loaded an unsupported extension"); | ||||
} | } | ||||
public void testCorruptChecksumAtFooter() throws Exception { | public void testCorruptChecksumAtFooter() throws Exception { | ||||
final DirCache dc = new DirCache(pathOf("gitgit.index.badchecksum")); | |||||
final DirCache dc = new DirCache(pathOf("gitgit.index.badchecksum"), | |||||
FS.DETECTED); | |||||
try { | try { | ||||
dc.read(); | dc.read(); | ||||
fail("Cache loaded despite corrupt checksum"); | fail("Cache loaded despite corrupt checksum"); | ||||
public void testReadIndex_DirCacheTree() throws Exception { | public void testReadIndex_DirCacheTree() throws Exception { | ||||
final Map<String, CGitIndexRecord> cList = readLsFiles(); | final Map<String, CGitIndexRecord> cList = readLsFiles(); | ||||
final Map<String, CGitLsTreeRecord> cTree = readLsTree(); | final Map<String, CGitLsTreeRecord> cTree = readLsTree(); | ||||
final DirCache dc = new DirCache(index); | |||||
final DirCache dc = new DirCache(index, FS.DETECTED); | |||||
assertEquals(0, dc.getEntryCount()); | assertEquals(0, dc.getEntryCount()); | ||||
dc.read(); | dc.read(); | ||||
assertEquals(cList.size(), dc.getEntryCount()); | assertEquals(cList.size(), dc.getEntryCount()); |
ObjectId pid = db.resolve("refs/heads/master^"); | ObjectId pid = db.resolve("refs/heads/master^"); | ||||
RefUpdate updateRef = db.updateRef("refs/heads/master"); | RefUpdate updateRef = db.updateRef("refs/heads/master"); | ||||
updateRef.setNewObjectId(pid); | updateRef.setNewObjectId(pid); | ||||
LockFile lockFile1 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); | |||||
LockFile lockFile1 = new LockFile(new File(db.getDirectory(), | |||||
"refs/heads/master"), db.getFS()); | |||||
try { | try { | ||||
assertTrue(lockFile1.lock()); // precondition to test | assertTrue(lockFile1.lock()); // precondition to test | ||||
Result update = updateRef.update(); | Result update = updateRef.update(); | ||||
assertEquals(Result.LOCK_FAILURE, update); | assertEquals(Result.LOCK_FAILURE, update); | ||||
assertEquals(opid, db.resolve("refs/heads/master")); | assertEquals(opid, db.resolve("refs/heads/master")); | ||||
LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); | |||||
LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master"), | |||||
db.getFS()); | |||||
assertFalse(lockFile2.lock()); // was locked, still is | assertFalse(lockFile2.lock()); // was locked, still is | ||||
} finally { | } finally { | ||||
lockFile1.unlock(); | lockFile1.unlock(); | ||||
"logs/" + fromName).exists()); | "logs/" + fromName).exists()); | ||||
// "someone" has branch X locked | // "someone" has branch X locked | ||||
LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock)); | |||||
LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock), | |||||
db.getFS()); | |||||
try { | try { | ||||
assertTrue(lockFile.lock()); | assertTrue(lockFile.lock()); | ||||
import org.eclipse.jgit.lib.ConfigConstants; | import org.eclipse.jgit.lib.ConfigConstants; | ||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.util.FS; | |||||
/** | /** | ||||
* Tests for setting up the working directory when creating a Repository | * Tests for setting up the working directory when creating a Repository | ||||
private FileBasedConfig configFor(File gitDir) throws IOException, | private FileBasedConfig configFor(File gitDir) throws IOException, | ||||
ConfigInvalidException { | ConfigInvalidException { | ||||
FileBasedConfig cfg = new FileBasedConfig(new File(gitDir, "config")); | |||||
File configPath = new File(gitDir, "config"); | |||||
FileBasedConfig cfg = new FileBasedConfig(configPath, FS.DETECTED); | |||||
cfg.load(); | cfg.load(); | ||||
return cfg; | return cfg; | ||||
} | } |
public void test006_ReadUglyConfig() throws IOException, | public void test006_ReadUglyConfig() throws IOException, | ||||
ConfigInvalidException { | ConfigInvalidException { | ||||
final File cfg = new File(db.getDirectory(), "config"); | final File cfg = new File(db.getDirectory(), "config"); | ||||
final FileBasedConfig c = new FileBasedConfig(cfg); | |||||
final FileBasedConfig c = new FileBasedConfig(cfg, db.getFS()); | |||||
final FileWriter pw = new FileWriter(cfg); | final FileWriter pw = new FileWriter(cfg); | ||||
final String configStr = " [core];comment\n\tfilemode = yes\n" | final String configStr = " [core];comment\n\tfilemode = yes\n" | ||||
+ "[user]\n" | + "[user]\n" |
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.lib.ObjectInserter; | import org.eclipse.jgit.lib.ObjectInserter; | ||||
import org.eclipse.jgit.storage.file.LockFile; | import org.eclipse.jgit.storage.file.LockFile; | ||||
import org.eclipse.jgit.util.FS; | |||||
import org.eclipse.jgit.util.IO; | import org.eclipse.jgit.util.IO; | ||||
import org.eclipse.jgit.util.MutableInteger; | import org.eclipse.jgit.util.MutableInteger; | ||||
import org.eclipse.jgit.util.NB; | import org.eclipse.jgit.util.NB; | ||||
* memory). | * memory). | ||||
*/ | */ | ||||
public static DirCache newInCore() { | public static DirCache newInCore() { | ||||
return new DirCache(null); | |||||
return new DirCache(null, null); | |||||
} | } | ||||
/** | /** | ||||
* | * | ||||
* @param indexLocation | * @param indexLocation | ||||
* location of the index file on disk. | * location of the index file on disk. | ||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
* @return a cache representing the contents of the specified index file (if | * @return a cache representing the contents of the specified index file (if | ||||
* it exists) or an empty cache if the file does not exist. | * it exists) or an empty cache if the file does not exist. | ||||
* @throws IOException | * @throws IOException | ||||
* the index file is using a format or extension that this | * the index file is using a format or extension that this | ||||
* library does not support. | * library does not support. | ||||
*/ | */ | ||||
public static DirCache read(final File indexLocation) | |||||
public static DirCache read(final File indexLocation, final FS fs) | |||||
throws CorruptObjectException, IOException { | throws CorruptObjectException, IOException { | ||||
final DirCache c = new DirCache(indexLocation); | |||||
final DirCache c = new DirCache(indexLocation, fs); | |||||
c.read(); | c.read(); | ||||
return c; | return c; | ||||
} | } | ||||
* <p> | * <p> | ||||
* The new index will be locked and then read before it is returned to the | * The new index will be locked and then read before it is returned to the | ||||
* caller. Read failures are reported as exceptions and therefore prevent | * caller. Read failures are reported as exceptions and therefore prevent | ||||
* the method from returning a partially populated index. On read failure, | |||||
* the method from returning a partially populated index. On read failure, | |||||
* the lock is released. | * the lock is released. | ||||
* | * | ||||
* @param indexLocation | * @param indexLocation | ||||
* location of the index file on disk. | * location of the index file on disk. | ||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
* @return a cache representing the contents of the specified index file (if | * @return a cache representing the contents of the specified index file (if | ||||
* it exists) or an empty cache if the file does not exist. | * it exists) or an empty cache if the file does not exist. | ||||
* @throws IOException | * @throws IOException | ||||
* the index file is using a format or extension that this | * the index file is using a format or extension that this | ||||
* library does not support. | * library does not support. | ||||
*/ | */ | ||||
public static DirCache lock(final File indexLocation) | |||||
public static DirCache lock(final File indexLocation, final FS fs) | |||||
throws CorruptObjectException, IOException { | throws CorruptObjectException, IOException { | ||||
final DirCache c = new DirCache(indexLocation); | |||||
final DirCache c = new DirCache(indexLocation, fs); | |||||
if (!c.lock()) | if (!c.lock()) | ||||
throw new IOException(MessageFormat.format(JGitText.get().cannotLock, indexLocation)); | throw new IOException(MessageFormat.format(JGitText.get().cannotLock, indexLocation)); | ||||
/** Our active lock (if we hold it); null if we don't have it locked. */ | /** Our active lock (if we hold it); null if we don't have it locked. */ | ||||
private LockFile myLock; | private LockFile myLock; | ||||
/** file system abstraction **/ | |||||
private final FS fs; | |||||
/** | /** | ||||
* Create a new in-core index representation. | * Create a new in-core index representation. | ||||
* <p> | * <p> | ||||
* | * | ||||
* @param indexLocation | * @param indexLocation | ||||
* location of the index file on disk. | * location of the index file on disk. | ||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
*/ | */ | ||||
public DirCache(final File indexLocation) { | |||||
public DirCache(final File indexLocation, final FS fs) { | |||||
liveFile = indexLocation; | liveFile = indexLocation; | ||||
this.fs = fs; | |||||
clear(); | clear(); | ||||
} | } | ||||
public boolean lock() throws IOException { | public boolean lock() throws IOException { | ||||
if (liveFile == null) | if (liveFile == null) | ||||
throw new IOException(JGitText.get().dirCacheDoesNotHaveABackingFile); | throw new IOException(JGitText.get().dirCacheDoesNotHaveABackingFile); | ||||
final LockFile tmp = new LockFile(liveFile); | |||||
final LockFile tmp = new LockFile(liveFile, fs); | |||||
if (tmp.lock()) { | if (tmp.lock()) { | ||||
tmp.setNeedStatInformation(true); | tmp.setNeedStatInformation(true); | ||||
myLock = tmp; | myLock = tmp; |
// repository and not inherited from other files. | // repository and not inherited from other files. | ||||
// | // | ||||
File path = safeFS().resolve(getGitDir(), "config"); | File path = safeFS().resolve(getGitDir(), "config"); | ||||
FileBasedConfig cfg = new FileBasedConfig(path); | |||||
FileBasedConfig cfg = new FileBasedConfig(path, safeFS()); | |||||
try { | try { | ||||
cfg.load(); | cfg.load(); | ||||
} catch (ConfigInvalidException err) { | } catch (ConfigInvalidException err) { |
*/ | */ | ||||
public DirCache readDirCache() throws NoWorkTreeException, | public DirCache readDirCache() throws NoWorkTreeException, | ||||
CorruptObjectException, IOException { | CorruptObjectException, IOException { | ||||
return DirCache.read(getIndexFile()); | |||||
return DirCache.read(getIndexFile(), getFS()); | |||||
} | } | ||||
/** | /** | ||||
*/ | */ | ||||
public DirCache lockDirCache() throws NoWorkTreeException, | public DirCache lockDirCache() throws NoWorkTreeException, | ||||
CorruptObjectException, IOException { | CorruptObjectException, IOException { | ||||
return DirCache.lock(getIndexFile()); | |||||
return DirCache.lock(getIndexFile(), getFS()); | |||||
} | } | ||||
static byte[] gitInternalSlash(byte[] bytes) { | static byte[] gitInternalSlash(byte[] bytes) { |
import org.eclipse.jgit.lib.Config; | import org.eclipse.jgit.lib.Config; | ||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.StoredConfig; | import org.eclipse.jgit.lib.StoredConfig; | ||||
import org.eclipse.jgit.util.FS; | |||||
import org.eclipse.jgit.util.IO; | import org.eclipse.jgit.util.IO; | ||||
import org.eclipse.jgit.util.RawParseUtils; | import org.eclipse.jgit.util.RawParseUtils; | ||||
public class FileBasedConfig extends StoredConfig { | public class FileBasedConfig extends StoredConfig { | ||||
private final File configFile; | private final File configFile; | ||||
private volatile long lastModified; | private volatile long lastModified; | ||||
private final FS fs; | |||||
/** | /** | ||||
* Create a configuration with no default fallback. | * Create a configuration with no default fallback. | ||||
* | * | ||||
* @param cfgLocation | * @param cfgLocation | ||||
* the location of the configuration file on the file system | * the location of the configuration file on the file system | ||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
*/ | */ | ||||
public FileBasedConfig(File cfgLocation) { | |||||
this(null, cfgLocation); | |||||
public FileBasedConfig(File cfgLocation, FS fs) { | |||||
this(null, cfgLocation, fs); | |||||
} | } | ||||
/** | /** | ||||
* the base configuration file | * the base configuration file | ||||
* @param cfgLocation | * @param cfgLocation | ||||
* the location of the configuration file on the file system | * the location of the configuration file on the file system | ||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
*/ | */ | ||||
public FileBasedConfig(Config base, File cfgLocation) { | |||||
public FileBasedConfig(Config base, File cfgLocation, FS fs) { | |||||
super(base); | super(base); | ||||
configFile = cfgLocation; | configFile = cfgLocation; | ||||
this.fs = fs; | |||||
} | } | ||||
/** @return location of the configuration file on disk */ | /** @return location of the configuration file on disk */ | ||||
*/ | */ | ||||
public void save() throws IOException { | public void save() throws IOException { | ||||
final byte[] out = Constants.encode(toText()); | final byte[] out = Constants.encode(toText()); | ||||
final LockFile lf = new LockFile(getFile()); | |||||
final LockFile lf = new LockFile(getFile(), fs); | |||||
if (!lf.lock()) | if (!lf.lock()) | ||||
throw new IOException(MessageFormat.format(JGitText.get().cannotLockFile, getFile())); | throw new IOException(MessageFormat.format(JGitText.get().cannotLockFile, getFile())); | ||||
try { | try { |
super(options); | super(options); | ||||
userConfig = SystemReader.getInstance().openUserConfig(getFS()); | userConfig = SystemReader.getInstance().openUserConfig(getFS()); | ||||
repoConfig = new FileBasedConfig(userConfig, getFS().resolve(getDirectory(), "config")); | |||||
repoConfig = new FileBasedConfig(userConfig, // | |||||
getFS().resolve(getDirectory(), "config"), // | |||||
getFS()); | |||||
loadUserConfig(); | loadUserConfig(); | ||||
loadRepoConfig(); | loadRepoConfig(); |
import org.eclipse.jgit.JGitText; | import org.eclipse.jgit.JGitText; | ||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.ObjectId; | import org.eclipse.jgit.lib.ObjectId; | ||||
import org.eclipse.jgit.util.FS; | |||||
/** | /** | ||||
* Git style file locking and replacement. | * Git style file locking and replacement. | ||||
private long commitLastModified; | private long commitLastModified; | ||||
private final FS fs; | |||||
/** | /** | ||||
* Create a new lock for any file. | * Create a new lock for any file. | ||||
* | * | ||||
* @param f | * @param f | ||||
* the file that will be locked. | * the file that will be locked. | ||||
* @param fs | |||||
* the file system abstraction which will be necessary to perform | |||||
* certain file system operations. | |||||
*/ | */ | ||||
public LockFile(final File f) { | |||||
public LockFile(final File f, FS fs) { | |||||
ref = f; | ref = f; | ||||
lck = new File(ref.getParentFile(), ref.getName() + SUFFIX); | lck = new File(ref.getParentFile(), ref.getName() + SUFFIX); | ||||
this.fs = fs; | |||||
} | } | ||||
/** | /** | ||||
saveStatInformation(); | saveStatInformation(); | ||||
if (lck.renameTo(ref)) | if (lck.renameTo(ref)) | ||||
return true; | return true; | ||||
if (!ref.exists() || ref.delete()) | |||||
if (!ref.exists() || deleteRef()) | |||||
if (lck.renameTo(ref)) | if (lck.renameTo(ref)) | ||||
return true; | return true; | ||||
unlock(); | unlock(); | ||||
return false; | return false; | ||||
} | } | ||||
private boolean deleteRef() { | |||||
if (!fs.retryFailedLockFileCommit()) | |||||
return ref.delete(); | |||||
// File deletion fails on windows if another thread is | |||||
// concurrently reading the same file. So try a few times. | |||||
// | |||||
for (int attempts = 0; attempts < 10; attempts++) { | |||||
if (ref.delete()) | |||||
return true; | |||||
try { | |||||
Thread.sleep(100); | |||||
} catch (InterruptedException e) { | |||||
return false; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
private void saveStatInformation() { | private void saveStatInformation() { | ||||
if (needStatInformation) | if (needStatInformation) | ||||
commitLastModified = lck.lastModified(); | commitLastModified = lck.lastModified(); |
import java.io.IOException; | import java.io.IOException; | ||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.util.FS; | |||||
/** Keeps track of a {@link PackFile}'s associated <code>.keep</code> file. */ | /** Keeps track of a {@link PackFile}'s associated <code>.keep</code> file. */ | ||||
public class PackLock { | public class PackLock { | ||||
private final File keepFile; | private final File keepFile; | ||||
private final FS fs; | |||||
/** | /** | ||||
* Create a new lock for a pack file. | * Create a new lock for a pack file. | ||||
* | * | ||||
* @param packFile | * @param packFile | ||||
* location of the <code>pack-*.pack</code> file. | * location of the <code>pack-*.pack</code> file. | ||||
* @param fs | |||||
* the filesystem abstraction used by the repository. | |||||
*/ | */ | ||||
public PackLock(final File packFile) { | |||||
public PackLock(final File packFile, final FS fs) { | |||||
final File p = packFile.getParentFile(); | final File p = packFile.getParentFile(); | ||||
final String n = packFile.getName(); | final String n = packFile.getName(); | ||||
keepFile = new File(p, n.substring(0, n.length() - 5) + ".keep"); | keepFile = new File(p, n.substring(0, n.length() - 5) + ".keep"); | ||||
this.fs = fs; | |||||
} | } | ||||
/** | /** | ||||
return false; | return false; | ||||
if (!msg.endsWith("\n")) | if (!msg.endsWith("\n")) | ||||
msg += "\n"; | msg += "\n"; | ||||
final LockFile lf = new LockFile(keepFile); | |||||
final LockFile lf = new LockFile(keepFile, fs); | |||||
if (!lf.lock()) | if (!lf.lock()) | ||||
return false; | return false; | ||||
lf.write(Constants.encode(msg)); | lf.write(Constants.encode(msg)); |
// we don't miss an edit made externally. | // we don't miss an edit made externally. | ||||
final PackedRefList packed = getPackedRefs(); | final PackedRefList packed = getPackedRefs(); | ||||
if (packed.contains(name)) { | if (packed.contains(name)) { | ||||
LockFile lck = new LockFile(packedRefsFile); | |||||
LockFile lck = new LockFile(packedRefsFile, | |||||
update.getRepository().getFS()); | |||||
if (!lck.lock()) | if (!lck.lock()) | ||||
throw new IOException(MessageFormat.format( | throw new IOException(MessageFormat.format( | ||||
JGitText.get().cannotLockFile, packedRefsFile)); | JGitText.get().cannotLockFile, packedRefsFile)); |
if (deref) | if (deref) | ||||
dst = dst.getLeaf(); | dst = dst.getLeaf(); | ||||
String name = dst.getName(); | String name = dst.getName(); | ||||
lock = new LockFile(database.fileFor(name)); | |||||
lock = new LockFile(database.fileFor(name), getRepository().getFS()); | |||||
if (lock.lock()) { | if (lock.lock()) { | ||||
dst = database.getRef(name); | dst = database.getRef(name); | ||||
setOldObjectId(dst != null ? dst.getObjectId() : null); | setOldObjectId(dst != null ? dst.getObjectId() : null); |
File meta = transport.local.getDirectory(); | File meta = transport.local.getDirectory(); | ||||
if (meta == null) | if (meta == null) | ||||
return; | return; | ||||
final LockFile lock = new LockFile(new File(meta, "FETCH_HEAD")); | |||||
final LockFile lock = new LockFile(new File(meta, "FETCH_HEAD"), | |||||
transport.local.getFS()); | |||||
try { | try { | ||||
if (lock.lock()) { | if (lock.lock()) { | ||||
final Writer w = new OutputStreamWriter(lock.getOutputStream()); | final Writer w = new OutputStreamWriter(lock.getOutputStream()); |
final File packDir = new File(repo.getObjectsDirectory(), "pack"); | final File packDir = new File(repo.getObjectsDirectory(), "pack"); | ||||
final File finalPack = new File(packDir, "pack-" + name + ".pack"); | final File finalPack = new File(packDir, "pack-" + name + ".pack"); | ||||
final File finalIdx = new File(packDir, "pack-" + name + ".idx"); | final File finalIdx = new File(packDir, "pack-" + name + ".idx"); | ||||
final PackLock keep = new PackLock(finalPack); | |||||
final PackLock keep = new PackLock(finalPack, repo.getFS()); | |||||
if (!packDir.exists() && !packDir.mkdir() && !packDir.exists()) { | if (!packDir.exists() && !packDir.mkdir() && !packDir.exists()) { | ||||
// The objects/pack directory isn't present, and we are unable | // The objects/pack directory isn't present, and we are unable |
public FileBasedConfig openUserConfig(FS fs) { | public FileBasedConfig openUserConfig(FS fs) { | ||||
final File home = fs.userHome(); | final File home = fs.userHome(); | ||||
return new FileBasedConfig(new File(home, ".gitconfig")); | |||||
return new FileBasedConfig(new File(home, ".gitconfig"), fs); | |||||
} | } | ||||
public String getHostname() { | public String getHostname() { |