aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java83
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java124
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WriteConfig.java81
9 files changed, 350 insertions, 140 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
index 66218f6405..dfaf5886e7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -57,6 +57,7 @@ import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.diff.SimilarityIndex.TableFullException;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -356,9 +357,17 @@ public class RenameDetector {
if (pm == null)
pm = NullProgressMonitor.INSTANCE;
+
+ if (0 < breakScore)
breakModifies(reader, pm);
+
+ if (!added.isEmpty() && !deleted.isEmpty())
findExactRenames(pm);
+
+ if (!added.isEmpty() && !deleted.isEmpty())
findContentRenames(reader, pm);
+
+ if (0 < breakScore && !added.isEmpty() && !deleted.isEmpty())
rejoinModifies(pm);
entries.addAll(added);
@@ -382,9 +391,6 @@ public class RenameDetector {
private void breakModifies(ContentSource.Pair reader, ProgressMonitor pm)
throws IOException {
- if (breakScore <= 0)
- return;
-
ArrayList<DiffEntry> newEntries = new ArrayList<DiffEntry>(entries.size());
pm.beginTask(JGitText.get().renamesBreakingModifies, entries.size());
@@ -445,29 +451,36 @@ public class RenameDetector {
private int calculateModifyScore(ContentSource.Pair reader, DiffEntry d)
throws IOException {
- SimilarityIndex src = new SimilarityIndex();
- src.hash(reader.open(OLD, d));
- src.sort();
-
- SimilarityIndex dst = new SimilarityIndex();
- dst.hash(reader.open(NEW, d));
- dst.sort();
- return src.score(dst, 100);
+ try {
+ SimilarityIndex src = new SimilarityIndex();
+ src.hash(reader.open(OLD, d));
+ src.sort();
+
+ SimilarityIndex dst = new SimilarityIndex();
+ dst.hash(reader.open(NEW, d));
+ dst.sort();
+ return src.score(dst, 100);
+ } catch (TableFullException tableFull) {
+ // If either table overflowed while being constructed, don't allow
+ // the pair to be broken. Returning 1 higher than breakScore will
+ // ensure its not similar, but not quite dissimilar enough to break.
+ //
+ overRenameLimit = true;
+ return breakScore + 1;
+ }
}
private void findContentRenames(ContentSource.Pair reader,
ProgressMonitor pm)
throws IOException {
int cnt = Math.max(added.size(), deleted.size());
- if (cnt == 0)
- return;
-
if (getRenameLimit() == 0 || cnt <= getRenameLimit()) {
SimilarityRenameDetector d;
d = new SimilarityRenameDetector(reader, deleted, added);
d.setRenameScore(getRenameScore());
d.compute(pm);
+ overRenameLimit |= d.isTableOverflow();
deleted = d.getLeftOverSources();
added = d.getLeftOverDestinations();
entries.addAll(d.getMatches());
@@ -478,9 +491,6 @@ public class RenameDetector {
@SuppressWarnings("unchecked")
private void findExactRenames(ProgressMonitor pm) {
- if (added.isEmpty() || deleted.isEmpty())
- return;
-
pm.beginTask(JGitText.get().renamesFindingExact, //
added.size() + added.size() + deleted.size()
+ added.size() * deleted.size());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
index 6627268e48..17ccb9726f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
@@ -65,8 +65,8 @@ import org.eclipse.jgit.lib.ObjectStream;
* file are discovered.
*/
class SimilarityIndex {
- /** The {@link #idHash} table stops growing at {@code 1 << MAX_HASH_BITS}. */
- private static final int MAX_HASH_BITS = 17;
+ /** A special {@link TableFullException} used in place of OutOfMemoryError. */
+ private static final TableFullException TABLE_FULL_OUT_OF_MEMORY = new TableFullException();
/**
* Shift to apply before storing a key.
@@ -76,20 +76,26 @@ class SimilarityIndex {
*/
private static final int KEY_SHIFT = 32;
+ /** Maximum value of the count field, also mask to extract the count. */
+ private static final long MAX_COUNT = (1L << KEY_SHIFT) - 1;
+
/** Total size of the file we hashed into the structure. */
private long fileSize;
/** Number of non-zero entries in {@link #idHash}. */
private int idSize;
+ /** {@link #idSize} that triggers {@link #idHash} to double in size. */
+ private int idGrowAt;
+
/**
* Pairings of content keys and counters.
* <p>
* Slots in the table are actually two ints wedged into a single long. The
- * upper {@link #MAX_HASH_BITS} bits stores the content key, and the
- * remaining lower bits stores the number of bytes associated with that key.
- * Empty slots are denoted by 0, which cannot occur because the count cannot
- * be 0. Values can only be positive, which we enforce during key addition.
+ * upper 32 bits stores the content key, and the remaining lower bits stores
+ * the number of bytes associated with that key. Empty slots are denoted by
+ * 0, which cannot occur because the count cannot be 0. Values can only be
+ * positive, which we enforce during key addition.
*/
private long[] idHash;
@@ -99,6 +105,7 @@ class SimilarityIndex {
SimilarityIndex() {
idHashBits = 8;
idHash = new long[1 << idHashBits];
+ idGrowAt = growAt(idHashBits);
}
long getFileSize() {
@@ -109,7 +116,8 @@ class SimilarityIndex {
fileSize = size;
}
- void hash(ObjectLoader obj) throws MissingObjectException, IOException {
+ void hash(ObjectLoader obj) throws MissingObjectException, IOException,
+ TableFullException {
if (obj.isLarge()) {
ObjectStream in = obj.openStream();
try {
@@ -125,7 +133,7 @@ class SimilarityIndex {
}
}
- void hash(byte[] raw, int ptr, final int end) {
+ void hash(byte[] raw, int ptr, final int end) throws TableFullException {
while (ptr < end) {
int hash = 5381;
int start = ptr;
@@ -141,7 +149,8 @@ class SimilarityIndex {
}
}
- void hash(InputStream in, long remaining) throws IOException {
+ void hash(InputStream in, long remaining) throws IOException,
+ TableFullException {
byte[] buf = new byte[4096];
int ptr = 0;
int cnt = 0;
@@ -190,11 +199,11 @@ class SimilarityIndex {
return (int) ((common(dst) * maxScore) / max);
}
- int common(SimilarityIndex dst) {
+ long common(SimilarityIndex dst) {
return common(this, dst);
}
- private static int common(SimilarityIndex src, SimilarityIndex dst) {
+ private static long common(SimilarityIndex src, SimilarityIndex dst) {
int srcIdx = src.packedIndex(0);
int dstIdx = dst.packedIndex(0);
long[] srcHash = src.idHash;
@@ -202,12 +211,12 @@ class SimilarityIndex {
return common(srcHash, srcIdx, dstHash, dstIdx);
}
- private static int common(long[] srcHash, int srcIdx, //
+ private static long common(long[] srcHash, int srcIdx, //
long[] dstHash, int dstIdx) {
if (srcIdx == srcHash.length || dstIdx == dstHash.length)
return 0;
- int common = 0;
+ long common = 0;
int srcKey = keyOf(srcHash[srcIdx]);
int dstKey = keyOf(dstHash[dstIdx]);
@@ -230,8 +239,8 @@ class SimilarityIndex {
break;
srcKey = keyOf(srcHash[srcIdx]);
- } else /* if (srcKey > dstKey) */{
- // Regions of dst which do not appear in dst.
+ } else /* if (dstKey < srcKey) */{
+ // Regions of dst which do not appear in src.
if (++dstIdx == dstHash.length)
break;
dstKey = keyOf(dstHash[dstIdx]);
@@ -268,7 +277,7 @@ class SimilarityIndex {
return (idHash.length - idSize) + idx;
}
- void add(int key, int cnt) {
+ void add(int key, int cnt) throws TableFullException {
key = (key * 0x9e370001) >>> 1; // Mix bits and ensure not negative.
int j = slot(key);
@@ -276,18 +285,20 @@ class SimilarityIndex {
long v = idHash[j];
if (v == 0) {
// Empty slot in the table, store here.
- if (shouldGrow()) {
+ if (idGrowAt <= idSize) {
grow();
j = slot(key);
continue;
}
- idHash[j] = (((long) key) << KEY_SHIFT) | cnt;
+ idHash[j] = pair(key, cnt);
idSize++;
return;
} else if (keyOf(v) == key) {
- // Same key, increment the counter.
- idHash[j] = v + cnt;
+ // Same key, increment the counter. If it overflows, fail
+ // indexing to prevent the key from being impacted.
+ //
+ idHash[j] = pair(key, countOf(v) + cnt);
return;
} else if (++j >= idHash.length) {
@@ -296,6 +307,12 @@ class SimilarityIndex {
}
}
+ private static long pair(int key, long cnt) throws TableFullException {
+ if (MAX_COUNT < cnt)
+ throw new TableFullException();
+ return (((long) key) << KEY_SHIFT) | cnt;
+ }
+
private int slot(int key) {
// We use 31 - idHashBits because the upper bit was already forced
// to be 0 and we want the remaining high bits to be used as the
@@ -304,16 +321,26 @@ class SimilarityIndex {
return key >>> (31 - idHashBits);
}
- private boolean shouldGrow() {
- return idHashBits < MAX_HASH_BITS && idHash.length <= idSize * 2;
+ private static int growAt(int idHashBits) {
+ return (1 << idHashBits) * (idHashBits - 3) / idHashBits;
}
- private void grow() {
+ private void grow() throws TableFullException {
+ if (idHashBits == 30)
+ throw new TableFullException();
+
long[] oldHash = idHash;
int oldSize = idHash.length;
idHashBits++;
- idHash = new long[1 << idHashBits];
+ idGrowAt = growAt(idHashBits);
+
+ try {
+ idHash = new long[1 << idHashBits];
+ } catch (OutOfMemoryError noMemory) {
+ throw TABLE_FULL_OUT_OF_MEMORY;
+ }
+
for (int i = 0; i < oldSize; i++) {
long v = oldHash[i];
if (v != 0) {
@@ -330,7 +357,11 @@ class SimilarityIndex {
return (int) (v >>> KEY_SHIFT);
}
- private static int countOf(long v) {
- return (int) v;
+ private static long countOf(long v) {
+ return v & MAX_COUNT;
+ }
+
+ static class TableFullException extends Exception {
+ private static final long serialVersionUID = 1L;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
index 3075c223a6..3a9847545b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
@@ -49,10 +49,12 @@ import static org.eclipse.jgit.diff.DiffEntry.Side.OLD;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.diff.SimilarityIndex.TableFullException;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ProgressMonitor;
@@ -110,6 +112,9 @@ class SimilarityRenameDetector {
/** Score a pair must exceed to be considered a rename. */
private int renameScore = 60;
+ /** Set if any {@link SimilarityIndex.TableFullException} occurs. */
+ private boolean tableOverflow;
+
private List<DiffEntry> out;
SimilarityRenameDetector(ContentSource.Pair reader, List<DiffEntry> srcs,
@@ -182,6 +187,10 @@ class SimilarityRenameDetector {
return dsts;
}
+ boolean isTableOverflow() {
+ return tableOverflow;
+ }
+
private static List<DiffEntry> compactSrcList(List<DiffEntry> in) {
ArrayList<DiffEntry> r = new ArrayList<DiffEntry>(in.size());
for (DiffEntry e : in) {
@@ -208,25 +217,22 @@ class SimilarityRenameDetector {
long[] srcSizes = new long[srcs.size()];
long[] dstSizes = new long[dsts.size()];
-
- // Init the size arrays to some value that indicates that we haven't
- // calculated the size yet. Since sizes cannot be negative, -1 will work
- Arrays.fill(srcSizes, -1);
- Arrays.fill(dstSizes, -1);
+ BitSet dstTooLarge = null;
// Consider each pair of files, if the score is above the minimum
// threshold we need record that scoring in the matrix so we can
// later find the best matches.
//
int mNext = 0;
- for (int srcIdx = 0; srcIdx < srcs.size(); srcIdx++) {
+ SRC: for (int srcIdx = 0; srcIdx < srcs.size(); srcIdx++) {
DiffEntry srcEnt = srcs.get(srcIdx);
if (!isFile(srcEnt.oldMode)) {
pm.update(dsts.size());
continue;
}
- SimilarityIndex s = hash(OLD, srcEnt);
+ SimilarityIndex s = null;
+
for (int dstIdx = 0; dstIdx < dsts.size(); dstIdx++) {
DiffEntry dstEnt = dsts.get(dstIdx);
@@ -240,15 +246,20 @@ class SimilarityRenameDetector {
continue;
}
+ if (dstTooLarge != null && dstTooLarge.get(dstIdx)) {
+ pm.update(1);
+ continue;
+ }
+
long srcSize = srcSizes[srcIdx];
- if (srcSize < 0) {
- srcSize = size(OLD, srcEnt);
+ if (srcSize == 0) {
+ srcSize = size(OLD, srcEnt) + 1;
srcSizes[srcIdx] = srcSize;
}
long dstSize = dstSizes[dstIdx];
- if (dstSize < 0) {
- dstSize = size(NEW, dstEnt);
+ if (dstSize == 0) {
+ dstSize = size(NEW, dstEnt) + 1;
dstSizes[dstIdx] = dstSize;
}
@@ -260,7 +271,27 @@ class SimilarityRenameDetector {
continue;
}
- SimilarityIndex d = hash(NEW, dstEnt);
+ if (s == null) {
+ try {
+ s = hash(OLD, srcEnt);
+ } catch (TableFullException tableFull) {
+ tableOverflow = true;
+ continue SRC;
+ }
+ }
+
+ SimilarityIndex d;
+ try {
+ d = hash(NEW, dstEnt);
+ } catch (TableFullException tableFull) {
+ if (dstTooLarge == null)
+ dstTooLarge = new BitSet(dsts.size());
+ dstTooLarge.set(dstIdx);
+ tableOverflow = true;
+ pm.update(1);
+ continue;
+ }
+
int contentScore = s.score(d, 10000);
// nameScore returns a value between 0 and 100, but we want it
@@ -336,7 +367,7 @@ class SimilarityRenameDetector {
}
private SimilarityIndex hash(DiffEntry.Side side, DiffEntry ent)
- throws IOException {
+ throws IOException, TableFullException {
SimilarityIndex r = new SimilarityIndex();
r.hash(reader.open(side, ent));
r.sort();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 5b71dc091f..d2629728e6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -144,8 +144,8 @@ public class DirCacheCheckout {
}
/**
- * Constructs a DirCacheCeckout for fast-forwarding from one tree to
- * another, merging it with the index
+ * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
+ * and mergeCommitTree) and the index.
*
* @param repo
* the repository in which we do the checkout
@@ -170,23 +170,63 @@ public class DirCacheCheckout {
}
/**
+ * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
+ * and mergeCommitTree) and the index. As iterator over the working tree
+ * this constructor creates a standard {@link FileTreeIterator}
+ *
+ * @param repo
+ * the repository in which we do the checkout
+ * @param headCommitTree
+ * the id of the tree of the head commit
+ * @param dc
+ * the (already locked) Dircache for this repo
+ * @param mergeCommitTree
+ * the id of the tree of the
+ * @throws IOException
+ */
+ public DirCacheCheckout(Repository repo, ObjectId headCommitTree,
+ DirCache dc, ObjectId mergeCommitTree) throws IOException {
+ this(repo, headCommitTree, dc, mergeCommitTree, new FileTreeIterator(
+ repo.getWorkTree(), repo.getFS(),
+ WorkingTreeOptions.createDefaultInstance()));
+ }
+
+ /**
+ * Constructs a DirCacheCeckout for checking out one tree, merging with the
+ * index.
+ *
+ * @param repo
+ * the repository in which we do the checkout
+ * @param dc
+ * the (already locked) Dircache for this repo
+ * @param mergeCommitTree
+ * the id of the tree we want to fast-forward to
+ * @param workingTree
+ * an iterator over the repositories Working Tree
+ * @throws IOException
+ */
+ public DirCacheCheckout(Repository repo, DirCache dc,
+ ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
+ throws IOException {
+ this(repo, null, dc, mergeCommitTree, workingTree);
+ }
+
+ /**
* Constructs a DirCacheCeckout for checking out one tree, merging with the
* index. As iterator over the working tree this constructor creates a
* standard {@link FileTreeIterator}
*
* @param repo
* the repository in which we do the checkout
- * @param headCommitTree
- * the id of the tree of the head commit
* @param dc
* the (already locked) Dircache for this repo
* @param mergeCommitTree
* the id of the tree of the
* @throws IOException
*/
- public DirCacheCheckout(Repository repo, ObjectId headCommitTree, DirCache dc,
+ public DirCacheCheckout(Repository repo, DirCache dc,
ObjectId mergeCommitTree) throws IOException {
- this(repo, headCommitTree, dc, mergeCommitTree, new FileTreeIterator(
+ this(repo, null, dc, mergeCommitTree, new FileTreeIterator(
repo.getWorkTree(), repo.getFS(),
WorkingTreeOptions.createDefaultInstance()));
}
@@ -274,8 +314,9 @@ public class DirCacheCheckout {
WorkingTreeIterator f) {
if (m != null) {
if (i == null || f == null || !m.idEqual(i)
- || f.isModified(i.getDirCacheEntry(), true,
- config_filemode(), repo.getFS())) {
+ || (i.getDirCacheEntry() != null && f.isModified(i
+ .getDirCacheEntry(), true, config_filemode(), repo
+ .getFS()))) {
update(m.getEntryPathString(), m.getEntryObjectId(),
m.getEntryFileMode());
} else
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
index e8bc3e2cfd..6199f4c450 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
@@ -44,7 +44,6 @@
package org.eclipse.jgit.storage.file;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -52,8 +51,9 @@ import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
-import java.nio.channels.FileLock;
-import java.nio.channels.OverlappingFileLockException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
import java.text.MessageFormat;
import org.eclipse.jgit.JGitText;
@@ -85,14 +85,14 @@ public class LockFile {
private final File lck;
- private FileLock fLck;
-
private boolean haveLck;
private FileOutputStream os;
private boolean needStatInformation;
+ private boolean fsync;
+
private long commitLastModified;
private final FS fs;
@@ -127,23 +127,6 @@ public class LockFile {
haveLck = true;
try {
os = new FileOutputStream(lck);
- try {
- fLck = os.getChannel().tryLock();
- if (fLck == null)
- throw new OverlappingFileLockException();
- } catch (OverlappingFileLockException ofle) {
- // We cannot use unlock() here as this file is not
- // held by us, but we thought we created it. We must
- // not delete it, as it belongs to some other process.
- //
- haveLck = false;
- try {
- os.close();
- } catch (IOException ioe) {
- // Fail by returning haveLck = false.
- }
- os = null;
- }
} catch (IOException ioe) {
unlock();
throw ioe;
@@ -192,10 +175,21 @@ public class LockFile {
try {
final FileInputStream fis = new FileInputStream(ref);
try {
- final byte[] buf = new byte[2048];
- int r;
- while ((r = fis.read(buf)) >= 0)
- os.write(buf, 0, r);
+ if (fsync) {
+ FileChannel in = fis.getChannel();
+ long pos = 0;
+ long cnt = in.size();
+ while (0 < cnt) {
+ long r = os.getChannel().transferFrom(in, pos, cnt);
+ pos += r;
+ cnt -= r;
+ }
+ } else {
+ final byte[] buf = new byte[2048];
+ int r;
+ while ((r = fis.read(buf)) >= 0)
+ os.write(buf, 0, r);
+ }
} finally {
fis.close();
}
@@ -229,26 +223,10 @@ public class LockFile {
* before throwing the underlying exception to the caller.
*/
public void write(final ObjectId id) throws IOException {
- requireLock();
- try {
- final BufferedOutputStream b;
- b = new BufferedOutputStream(os, Constants.OBJECT_ID_STRING_LENGTH + 1);
- id.copyTo(b);
- b.write('\n');
- b.flush();
- fLck.release();
- b.close();
- os = null;
- } catch (IOException ioe) {
- unlock();
- throw ioe;
- } catch (RuntimeException ioe) {
- unlock();
- throw ioe;
- } catch (Error ioe) {
- unlock();
- throw ioe;
- }
+ byte[] buf = new byte[Constants.OBJECT_ID_STRING_LENGTH + 1];
+ id.copyTo(buf, 0);
+ buf[Constants.OBJECT_ID_STRING_LENGTH] = '\n';
+ write(buf);
}
/**
@@ -268,9 +246,15 @@ public class LockFile {
public void write(final byte[] content) throws IOException {
requireLock();
try {
- os.write(content);
- os.flush();
- fLck.release();
+ if (fsync) {
+ FileChannel fc = os.getChannel();
+ ByteBuffer buf = ByteBuffer.wrap(content);
+ while (0 < buf.remaining())
+ fc.write(buf);
+ fc.force(true);
+ } else {
+ os.write(content);
+ }
os.close();
os = null;
} catch (IOException ioe) {
@@ -296,34 +280,36 @@ public class LockFile {
*/
public OutputStream getOutputStream() {
requireLock();
+
+ final OutputStream out;
+ if (fsync)
+ out = Channels.newOutputStream(os.getChannel());
+ else
+ out = os;
+
return new OutputStream() {
@Override
public void write(final byte[] b, final int o, final int n)
throws IOException {
- os.write(b, o, n);
+ out.write(b, o, n);
}
@Override
public void write(final byte[] b) throws IOException {
- os.write(b);
+ out.write(b);
}
@Override
public void write(final int b) throws IOException {
- os.write(b);
- }
-
- @Override
- public void flush() throws IOException {
- os.flush();
+ out.write(b);
}
@Override
public void close() throws IOException {
try {
- os.flush();
- fLck.release();
- os.close();
+ if (fsync)
+ os.getChannel().force(true);
+ out.close();
os = null;
} catch (IOException ioe) {
unlock();
@@ -357,6 +343,16 @@ public class LockFile {
}
/**
+ * Request that {@link #commit()} force dirty data to the drive.
+ *
+ * @param on
+ * true if dirty data should be forced to the drive.
+ */
+ public void setFSync(final boolean on) {
+ fsync = on;
+ }
+
+ /**
* Wait until the lock file information differs from the old file.
* <p>
* This method tests both the length and the last modification date. If both
@@ -447,14 +443,6 @@ public class LockFile {
*/
public void unlock() {
if (os != null) {
- if (fLck != null) {
- try {
- fLck.release();
- } catch (IOException ioe) {
- // Huh?
- }
- fLck = null;
- }
try {
os.close();
} catch (IOException ioe) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
index 16cb8aa35c..d922bebe8f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
@@ -52,6 +52,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.channels.Channels;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.zip.Deflater;
@@ -60,7 +61,6 @@ import java.util.zip.DeflaterOutputStream;
import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -68,13 +68,13 @@ import org.eclipse.jgit.lib.ObjectInserter;
class ObjectDirectoryInserter extends ObjectInserter {
private final FileObjectDatabase db;
- private final Config config;
+ private final WriteConfig config;
private Deflater deflate;
ObjectDirectoryInserter(final FileObjectDatabase dest, final Config cfg) {
db = dest;
- config = cfg;
+ config = cfg.get(WriteConfig.KEY);
}
@Override
@@ -121,9 +121,13 @@ class ObjectDirectoryInserter extends ObjectInserter {
boolean delete = true;
File tmp = newTempFile();
try {
- DigestOutputStream dOut = new DigestOutputStream(
- compress(new FileOutputStream(tmp)), md);
+ FileOutputStream fOut = new FileOutputStream(tmp);
try {
+ OutputStream out = fOut;
+ if (config.getFSyncObjectFiles())
+ out = Channels.newOutputStream(fOut.getChannel());
+ DeflaterOutputStream cOut = compress(out);
+ DigestOutputStream dOut = new DigestOutputStream(cOut, md);
writeHeader(dOut, type, len);
final byte[] buf = buffer();
@@ -134,8 +138,12 @@ class ObjectDirectoryInserter extends ObjectInserter {
dOut.write(buf, 0, n);
len -= n;
}
+ dOut.flush();
+ cOut.finish();
} finally {
- dOut.close();
+ if (config.getFSyncObjectFiles())
+ fOut.getChannel().force(true);
+ fOut.close();
}
delete = false;
@@ -160,7 +168,7 @@ class ObjectDirectoryInserter extends ObjectInserter {
DeflaterOutputStream compress(final OutputStream out) {
if (deflate == null)
- deflate = new Deflater(config.get(CoreConfig.KEY).getCompression());
+ deflate = new Deflater(config.getCompression());
else
deflate.reset();
return new DeflaterOutputStream(out, deflate);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
index 96c8361adb..2af7ca3e6d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
@@ -67,6 +67,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.LinkedList;
@@ -606,6 +608,7 @@ public class RefDirectory extends RefDatabase {
write = false;
if (write) {
+ WriteConfig wc = getRepository().getConfig().get(WriteConfig.KEY);
FileOutputStream out;
try {
out = new FileOutputStream(log, true);
@@ -618,7 +621,15 @@ public class RefDirectory extends RefDatabase {
out = new FileOutputStream(log, true);
}
try {
- out.write(rec);
+ if (wc.getFSyncRefFiles()) {
+ FileChannel fc = out.getChannel();
+ ByteBuffer buf = ByteBuffer.wrap(rec);
+ while (0 < buf.remaining())
+ fc.write(buf);
+ fc.force(true);
+ } else {
+ out.write(rec);
+ }
} finally {
out.close();
}
@@ -757,6 +768,7 @@ public class RefDirectory extends RefDatabase {
@Override
protected void writeFile(String name, byte[] content)
throws IOException {
+ lck.setFSync(true);
lck.setNeedStatInformation(true);
try {
lck.write(content);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
index a9f054837b..109960df2b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
@@ -99,6 +99,10 @@ class RefDirectoryUpdate extends RefUpdate {
@Override
protected Result doUpdate(final Result status) throws IOException {
+ WriteConfig wc = database.getRepository().getConfig()
+ .get(WriteConfig.KEY);
+
+ lock.setFSync(wc.getFSyncRefFiles());
lock.setNeedStatInformation(true);
lock.write(getNewObjectId());
@@ -143,6 +147,10 @@ class RefDirectoryUpdate extends RefUpdate {
@Override
protected Result doLink(final String target) throws IOException {
+ WriteConfig wc = database.getRepository().getConfig()
+ .get(WriteConfig.KEY);
+
+ lock.setFSync(wc.getFSyncRefFiles());
lock.setNeedStatInformation(true);
lock.write(encode(RefDirectory.SYMREF + target + '\n'));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WriteConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WriteConfig.java
new file mode 100644
index 0000000000..fd467a5554
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WriteConfig.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * 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.storage.file;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Config.SectionParser;
+import org.eclipse.jgit.lib.CoreConfig;
+
+class WriteConfig {
+ /** Key for {@link Config#get(SectionParser)}. */
+ static final Config.SectionParser<WriteConfig> KEY = new SectionParser<WriteConfig>() {
+ public WriteConfig parse(final Config cfg) {
+ return new WriteConfig(cfg);
+ }
+ };
+
+ private final int compression;
+
+ private final boolean fsyncObjectFiles;
+
+ private final boolean fsyncRefFiles;
+
+ private WriteConfig(final Config rc) {
+ compression = rc.get(CoreConfig.KEY).getCompression();
+ fsyncObjectFiles = rc.getBoolean("core", "fsyncobjectfiles", false);
+ fsyncRefFiles = rc.getBoolean("core", "fsyncreffiles", false);
+ }
+
+ int getCompression() {
+ return compression;
+ }
+
+ boolean getFSyncObjectFiles() {
+ return fsyncObjectFiles;
+ }
+
+ boolean getFSyncRefFiles() {
+ return fsyncRefFiles;
+ }
+}