diff options
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit')
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; + } +} |