postConstructionSetup, String... inputLines)
+ throws Exception {
+
+ ByteArrayInputStream send = linesAsInputStream(inputLines);
+
+ server.getConfig().setString("protocol", null, "version", "2");
+ UploadPack up = new UploadPack(server);
+ if (postConstructionSetup != null) {
+ postConstructionSetup.accept(up);
+ }
+ up.setExtraParameters(Sets.of("version=2"));
+
+ ByteArrayOutputStream recv = new ByteArrayOutputStream();
+ up.upload(send, recv, null);
+
+ return new ByteArrayInputStream(recv.toByteArray());
+ }
+
+ private static ByteArrayInputStream linesAsInputStream(String... inputLines)
+ throws IOException {
+ try (ByteArrayOutputStream send = new ByteArrayOutputStream()) {
+ PacketLineOut pckOut = new PacketLineOut(send);
+ for (String line : inputLines) {
+ Objects.requireNonNull(line);
+ if (PacketLineIn.isEnd(line)) {
+ pckOut.end();
+ } else if (PacketLineIn.isDelimiter(line)) {
+ pckOut.writeDelim();
+ } else {
+ pckOut.writeString(line);
+ }
+ }
+ return new ByteArrayInputStream(send.toByteArray());
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
index 8de7ba6c1..171d80c3d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
@@ -19,7 +19,6 @@
import java.io.File;
import java.io.IOException;
-import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
@@ -182,7 +181,7 @@ public void testReadPipePosixCommandFailure()
FS.readPipe(fs.userHome(),
new String[] { "/bin/sh", "-c", "exit 1" },
- Charset.defaultCharset().name());
+ SystemReader.getInstance().getDefaultCharset().name());
}
@Test(expected = CommandFailedException.class)
@@ -192,7 +191,7 @@ public void testReadPipeCommandStartFailure()
FS.readPipe(fs.userHome(),
new String[] { "this-command-does-not-exist" },
- Charset.defaultCharset().name());
+ SystemReader.getInstance().getDefaultCharset().name());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
index 85ce5381f..db2f6da31 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
@@ -34,7 +34,7 @@ public void test() throws IOException {
assertNoCrLf("\r\n\r", "\n\r");
assertNoCrLf("\r\n\r\r", "\r\n\r\r");
assertNoCrLf("\r\n\r\n", "\r\n\r\n");
- assertNoCrLf("\r\n\r\n\r", "\n\r\n\r");
+ assertNoCrLf("\n\r\n\r", "\n\r\n\r");
assertNoCrLf("\0\n", "\0\n");
}
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 3acceab09..74762a902 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -440,6 +440,7 @@ lockOnNotHeld=Lock on {0} not held.
lockStreamClosed=Output to lock on {0} already closed
lockStreamMultiple=Output to lock on {0} already opened
logInconsistentFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: {} > {}, but diff = {}. Aborting measurement at resolution {}.
+logInvalidDefaultCharset=System property "native.encoding" specifies unknown character set: {}
logLargerFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: diff = {} > {} (last good value). Aborting measurement.
logSmallerFiletime={}: got smaller file timestamp on {}, {}: {} < {}. Aborting measurement at resolution {}.
logXDGConfigHomeInvalid=Environment variable XDG_CONFIG_HOME contains an invalid path {}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
index a2fbd411f..b5fff7d6d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
@@ -221,6 +221,7 @@ public Properties getStatistics() throws GitAPIException {
@SuppressWarnings("boxing")
private static Properties toProperties(RepoStatistics stats) {
Properties p = new Properties();
+ p.put("numberOfBitmaps", stats.numberOfBitmaps); //$NON-NLS-1$
p.put("numberOfLooseObjects", stats.numberOfLooseObjects); //$NON-NLS-1$
p.put("numberOfLooseRefs", stats.numberOfLooseRefs); //$NON-NLS-1$
p.put("numberOfPackedObjects", stats.numberOfPackedObjects); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index ce3ad2239..dbfd41594 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -12,13 +12,13 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.nio.charset.Charset;
import java.util.concurrent.Callable;
import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.ProcessResult;
+import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.io.TeeOutputStream;
/**
@@ -171,7 +171,8 @@ protected void doRun() throws AbortedByHookException, IOException {
getStdinArgs());
if (result.isExecutedWithError()) {
handleError(new String(errorByteArray.toByteArray(),
- Charset.defaultCharset().name()), result);
+ SystemReader.getInstance().getDefaultCharset().name()),
+ result);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 76340dabb..3d5d0607e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -468,6 +468,7 @@ public static JGitText get() {
/***/ public String lockStreamClosed;
/***/ public String lockStreamMultiple;
/***/ public String logInconsistentFiletimeDiff;
+ /***/ public String logInvalidDefaultCharset;
/***/ public String logLargerFiletimeDiff;
/***/ public String logSmallerFiletime;
/***/ public String logXDGConfigHomeInvalid;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index 092d035b3..e87bfe24e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -39,9 +39,10 @@
* Its too expensive during object access to be accurate with a least recently
* used (LRU) algorithm. Strictly ordering every read is a lot of overhead that
* typically doesn't yield a corresponding benefit to the application. This
- * cache implements a clock replacement algorithm, giving each block one chance
- * to have been accessed during a sweep of the cache to save itself from
- * eviction.
+ * cache implements a clock replacement algorithm, giving each block at least
+ * one chance to have been accessed during a sweep of the cache to save itself
+ * from eviction. The number of swipe chances is configurable per pack
+ * extension.
*
* Entities created by the cache are held under hard references, preventing the
* Java VM from clearing anything. Blocks are discarded by the replacement
@@ -161,6 +162,9 @@ public static DfsBlockCache getInstance() {
/** Current position of the clock. */
private Ref clockHand;
+ /** Limits of cache hot count per pack file extension. */
+ private final int[] cacheHotLimits = new int[PackExt.values().length];
+
@SuppressWarnings("unchecked")
private DfsBlockCache(DfsBlockCacheConfig cfg) {
tableSize = tableSize(cfg);
@@ -196,6 +200,15 @@ private DfsBlockCache(DfsBlockCacheConfig cfg) {
liveBytes = new AtomicReference<>(newCounters());
refLockWaitTime = cfg.getRefLockWaitTimeConsumer();
+
+ for (int i = 0; i < PackExt.values().length; ++i) {
+ Integer limit = cfg.getCacheHotMap().get(PackExt.values()[i]);
+ if (limit != null && limit.intValue() > 0) {
+ cacheHotLimits[i] = limit.intValue();
+ } else {
+ cacheHotLimits[i] = DfsBlockCacheConfig.DEFAULT_CACHE_HOT_MAX;
+ }
+ }
}
boolean shouldCopyThroughCache(long length) {
@@ -394,7 +407,7 @@ DfsBlock getOrLoad(BlockBasedFile file, long position, DfsReader ctx,
}
Ref ref = new Ref<>(key, position, v.size(), v);
- ref.hot = true;
+ ref.markHotter();
for (;;) {
HashEntry n = new HashEntry(clean(e2), ref);
if (table.compareAndSet(slot, e2, n)) {
@@ -424,10 +437,10 @@ private void reserveSpace(long reserve, DfsStreamKey key) {
Ref prev = clockHand;
Ref hand = clockHand.next;
do {
- if (hand.hot) {
- // Value was recently touched. Clear
- // hot and give it another chance.
- hand.hot = false;
+ if (hand.isHot()) {
+ // Value was recently touched. Cache is still hot so
+ // give it another chance, but cool it down a bit.
+ hand.markColder();
prev = hand;
hand = hand.next;
continue;
@@ -525,7 +538,7 @@ Ref getOrLoadRef(
}
getStat(statMiss, key).incrementAndGet();
ref = loader.load();
- ref.hot = true;
+ ref.markHotter();
// Reserve after loading to get the size of the object
reserveSpace(ref.size, key);
for (;;) {
@@ -568,7 +581,7 @@ Ref put(DfsStreamKey key, long pos, long size, T v) {
}
ref = new Ref<>(key, pos, size, v);
- ref.hot = true;
+ ref.markHotter();
for (;;) {
HashEntry n = new HashEntry(clean(e2), ref);
if (table.compareAndSet(slot, e2, n)) {
@@ -692,7 +705,8 @@ static final class Ref {
final long size;
volatile T value;
Ref next;
- volatile boolean hot;
+
+ private volatile int hotCount;
Ref(DfsStreamKey key, long position, long size, T v) {
this.key = key;
@@ -704,7 +718,7 @@ static final class Ref {
T get() {
T v = value;
if (v != null) {
- hot = true;
+ markHotter();
}
return v;
}
@@ -712,6 +726,20 @@ T get() {
boolean has() {
return value != null;
}
+
+ void markHotter() {
+ int cap = DfsBlockCache
+ .getInstance().cacheHotLimits[key.packExtPos];
+ hotCount = Math.min(cap, hotCount + 1);
+ }
+
+ void markColder() {
+ hotCount = Math.max(0, hotCount - 1);
+ }
+
+ boolean isHot() {
+ return hotCount > 0;
+ }
}
@FunctionalInterface
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index 6e7ad3e61..2716f79a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -18,9 +18,12 @@
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
import java.text.MessageFormat;
+import java.util.Collections;
+import java.util.Map;
import java.util.function.Consumer;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.lib.Config;
/**
@@ -34,6 +37,9 @@ public class DfsBlockCacheConfig {
/** 1024 {@link #KB} (number of bytes in one mebibyte/megabyte) */
public static final int MB = 1024 * KB;
+ /** Default number of max cache hits. */
+ public static final int DEFAULT_CACHE_HOT_MAX = 1;
+
private long blockLimit;
private int blockSize;
private double streamRatio;
@@ -41,6 +47,8 @@ public class DfsBlockCacheConfig {
private Consumer refLock;
+ private Map cacheHotMap;
+
/**
* Create a default configuration.
*/
@@ -49,6 +57,7 @@ public DfsBlockCacheConfig() {
setBlockSize(64 * KB);
setStreamRatio(0.30);
setConcurrencyLevel(32);
+ cacheHotMap = Collections.emptyMap();
}
/**
@@ -184,6 +193,28 @@ public DfsBlockCacheConfig setRefLockWaitTimeConsumer(Consumer c) {
return this;
}
+ /**
+ * Get the map of hot count per pack extension for {@code DfsBlockCache}.
+ *
+ * @return map of hot count per pack extension for {@code DfsBlockCache}.
+ */
+ public Map getCacheHotMap() {
+ return cacheHotMap;
+ }
+
+ /**
+ * Set the map of hot count per pack extension for {@code DfsBlockCache}.
+ *
+ * @param cacheHotMap
+ * map of hot count per pack extension for {@code DfsBlockCache}.
+ * @return {@code this}
+ */
+ public DfsBlockCacheConfig setCacheHotMap(
+ Map cacheHotMap) {
+ this.cacheHotMap = Collections.unmodifiableMap(cacheHotMap);
+ return this;
+ }
+
/**
* Update properties by setting fields from the configuration.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index 9be3df3b1..b5bf03fcb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -59,13 +59,6 @@ public final class DfsPackFile extends BlockBasedFile {
private static final int REC_SIZE = Constants.OBJECT_ID_LENGTH + 8;
private static final long REF_POSITION = 0;
- /**
- * Lock for initialization of {@link #index} and {@link #corruptObjects}.
- *
- * This lock ensures only one thread can perform the initialization work.
- */
- private final Object initLock = new Object();
-
/** Index mapping {@link ObjectId} to position within the pack stream. */
private volatile PackIndex index;
@@ -84,6 +77,9 @@ public final class DfsPackFile extends BlockBasedFile {
*/
private volatile LongList corruptObjects;
+ /** Lock for {@link #corruptObjects}. */
+ private final Object corruptObjectsLock = new Object();
+
/**
* Construct a reader for an existing, packfile.
*
@@ -155,35 +151,26 @@ private PackIndex idx(DfsReader ctx) throws IOException {
Repository.getGlobalListenerList()
.dispatch(new BeforeDfsPackIndexLoadedEvent(this));
-
- synchronized (initLock) {
- if (index != null) {
- return index;
+ try {
+ DfsStreamKey idxKey = desc.getStreamKey(INDEX);
+ AtomicBoolean cacheHit = new AtomicBoolean(true);
+ DfsBlockCache.Ref idxref = cache.getOrLoadRef(idxKey,
+ REF_POSITION, () -> {
+ cacheHit.set(false);
+ return loadPackIndex(ctx, idxKey);
+ });
+ if (cacheHit.get()) {
+ ctx.stats.idxCacheHit++;
}
-
- try {
- DfsStreamKey idxKey = desc.getStreamKey(INDEX);
- AtomicBoolean cacheHit = new AtomicBoolean(true);
- DfsBlockCache.Ref idxref = cache.getOrLoadRef(
- idxKey,
- REF_POSITION,
- () -> {
- cacheHit.set(false);
- return loadPackIndex(ctx, idxKey);
- });
- if (cacheHit.get()) {
- ctx.stats.idxCacheHit++;
- }
- PackIndex idx = idxref.get();
- if (index == null && idx != null) {
- index = idx;
- }
- return index;
- } catch (IOException e) {
- invalid = true;
- invalidatingCause = e;
- throw e;
+ PackIndex idx = idxref.get();
+ if (index == null && idx != null) {
+ index = idx;
}
+ return index;
+ } catch (IOException e) {
+ invalid = true;
+ invalidatingCause = e;
+ throw e;
}
}
@@ -191,7 +178,17 @@ final boolean isGarbage() {
return desc.getPackSource() == UNREACHABLE_GARBAGE;
}
- PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
+ /**
+ * Get the BitmapIndex for this PackFile.
+ *
+ * @param ctx
+ * reader context to support reading from the backing store if
+ * the index is not already loaded in memory.
+ * @return the BitmapIndex.
+ * @throws java.io.IOException
+ * the bitmap index is not available, or is corrupt.
+ */
+ public PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
if (invalid || isGarbage() || !desc.hasFileExt(BITMAP_INDEX)) {
return null;
}
@@ -200,31 +197,21 @@ PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
return bitmapIndex;
}
- synchronized (initLock) {
- if (bitmapIndex != null) {
- return bitmapIndex;
- }
-
- PackIndex idx = idx(ctx);
- PackReverseIndex revidx = getReverseIdx(ctx);
- DfsStreamKey bitmapKey = desc.getStreamKey(BITMAP_INDEX);
- AtomicBoolean cacheHit = new AtomicBoolean(true);
- DfsBlockCache.Ref idxref = cache.getOrLoadRef(
- bitmapKey,
- REF_POSITION,
- () -> {
- cacheHit.set(false);
- return loadBitmapIndex(ctx, bitmapKey, idx, revidx);
- });
- if (cacheHit.get()) {
- ctx.stats.bitmapCacheHit++;
- }
- PackBitmapIndex bmidx = idxref.get();
- if (bitmapIndex == null && bmidx != null) {
- bitmapIndex = bmidx;
- }
- return bitmapIndex;
+ DfsStreamKey bitmapKey = desc.getStreamKey(BITMAP_INDEX);
+ AtomicBoolean cacheHit = new AtomicBoolean(true);
+ DfsBlockCache.Ref idxref = cache
+ .getOrLoadRef(bitmapKey, REF_POSITION, () -> {
+ cacheHit.set(false);
+ return loadBitmapIndex(ctx, bitmapKey);
+ });
+ if (cacheHit.get()) {
+ ctx.stats.bitmapCacheHit++;
}
+ PackBitmapIndex bmidx = idxref.get();
+ if (bitmapIndex == null && bmidx != null) {
+ bitmapIndex = bmidx;
+ }
+ return bitmapIndex;
}
PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
@@ -232,31 +219,23 @@ PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
return reverseIndex;
}
- synchronized (initLock) {
- if (reverseIndex != null) {
- return reverseIndex;
- }
-
- PackIndex idx = idx(ctx);
- DfsStreamKey revKey = new DfsStreamKey.ForReverseIndex(
- desc.getStreamKey(INDEX));
- AtomicBoolean cacheHit = new AtomicBoolean(true);
- DfsBlockCache.Ref revref = cache.getOrLoadRef(
- revKey,
- REF_POSITION,
- () -> {
- cacheHit.set(false);
- return loadReverseIdx(ctx, revKey, idx);
- });
- if (cacheHit.get()) {
- ctx.stats.ridxCacheHit++;
- }
- PackReverseIndex revidx = revref.get();
- if (reverseIndex == null && revidx != null) {
- reverseIndex = revidx;
- }
- return reverseIndex;
+ PackIndex idx = idx(ctx);
+ DfsStreamKey revKey = new DfsStreamKey.ForReverseIndex(
+ desc.getStreamKey(INDEX));
+ AtomicBoolean cacheHit = new AtomicBoolean(true);
+ DfsBlockCache.Ref revref = cache.getOrLoadRef(revKey,
+ REF_POSITION, () -> {
+ cacheHit.set(false);
+ return loadReverseIdx(ctx, revKey, idx);
+ });
+ if (cacheHit.get()) {
+ ctx.stats.ridxCacheHit++;
}
+ PackReverseIndex revidx = revref.get();
+ if (reverseIndex == null && revidx != null) {
+ reverseIndex = revidx;
+ }
+ return reverseIndex;
}
/**
@@ -1003,7 +982,7 @@ boolean isCorrupt(long offset) {
private void setCorrupt(long offset) {
LongList list = corruptObjects;
if (list == null) {
- synchronized (initLock) {
+ synchronized (corruptObjectsLock) {
list = corruptObjects;
if (list == null) {
list = new LongList();
@@ -1066,11 +1045,8 @@ private DfsBlockCache.Ref loadReverseIdx(
revidx);
}
- private DfsBlockCache.Ref loadBitmapIndex(
- DfsReader ctx,
- DfsStreamKey bitmapKey,
- PackIndex idx,
- PackReverseIndex revidx) throws IOException {
+ private DfsBlockCache.Ref loadBitmapIndex(DfsReader ctx,
+ DfsStreamKey bitmapKey) throws IOException {
ctx.stats.readBitmap++;
long start = System.nanoTime();
try (ReadableChannel rc = ctx.db.openFile(desc, BITMAP_INDEX)) {
@@ -1086,7 +1062,8 @@ private DfsBlockCache.Ref loadBitmapIndex(
bs = wantSize;
}
in = new BufferedInputStream(in, bs);
- bmidx = PackBitmapIndex.read(in, idx, revidx);
+ bmidx = PackBitmapIndex.read(in, () -> idx(ctx),
+ () -> getReverseIdx(ctx));
} finally {
size = rc.position();
ctx.stats.readBitmapIdxBytes += size;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
index a80fa837b..e9e17c0ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
@@ -28,6 +28,7 @@
import java.util.stream.Collectors;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
@@ -592,15 +593,20 @@ private static Ref refForWrite(RevWalk rw, Ref r) throws IOException {
r.getTarget().getName(), null));
}
ObjectId newId = r.getObjectId();
- RevObject obj = rw.parseAny(newId);
RevObject peel = null;
- if (obj instanceof RevTag) {
- peel = rw.peel(obj);
+ try {
+ RevObject obj = rw.parseAny(newId);
+ if (obj instanceof RevTag) {
+ peel = rw.peel(obj);
+ }
+ } catch (MissingObjectException e) {
+ /* ignore this error and copy the dangling object ID into reftable too. */
}
if (peel != null) {
- return new ObjectIdRef.PeeledTag(PACKED, r.getName(), newId,
- peel.copy());
- }
+ return new ObjectIdRef.PeeledTag(PACKED, r.getName(), newId,
+ peel.copy());
+ }
+
return new ObjectIdRef.PeeledNonTag(PACKED, r.getName(), newId);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
index beb51dc2e..8401f0718 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
@@ -57,11 +57,10 @@ public abstract class PackBitmapIndex {
* @throws CorruptObjectException
* the stream does not contain a valid pack bitmap index.
*/
- public static PackBitmapIndex open(
- File idxFile, PackIndex packIndex, PackReverseIndex reverseIndex)
+ public static PackBitmapIndex open(File idxFile, PackIndex packIndex,
+ PackReverseIndex reverseIndex)
throws IOException {
- try (SilentFileInputStream fd = new SilentFileInputStream(
- idxFile)) {
+ try (SilentFileInputStream fd = new SilentFileInputStream(idxFile)) {
try {
return read(fd, packIndex, reverseIndex);
} catch (IOException ioe) {
@@ -94,10 +93,39 @@ public static PackBitmapIndex open(
* @throws CorruptObjectException
* the stream does not contain a valid pack bitmap index.
*/
- public static PackBitmapIndex read(
- InputStream fd, PackIndex packIndex, PackReverseIndex reverseIndex)
+ public static PackBitmapIndex read(InputStream fd, PackIndex packIndex,
+ PackReverseIndex reverseIndex) throws IOException {
+ return new PackBitmapIndexV1(fd, () -> packIndex, () -> reverseIndex);
+ }
+
+ /**
+ * Read an existing pack bitmap index file from a buffered stream.
+ *
+ * The format of the file will be automatically detected and a proper access
+ * implementation for that format will be constructed and returned to the
+ * caller. The file may or may not be held open by the returned instance.
+ *
+ * @param fd
+ * stream to read the bitmap index file from. The stream must be
+ * buffered as some small IOs are performed against the stream.
+ * The caller is responsible for closing the stream.
+ * @param packIndexSupplier
+ * the supplier for pack index for the corresponding pack file.
+ * @param reverseIndexSupplier
+ * the supplier for pack reverse index for the corresponding pack
+ * file.
+ * @return a copy of the index in-memory.
+ * @throws java.io.IOException
+ * the stream cannot be read.
+ * @throws CorruptObjectException
+ * the stream does not contain a valid pack bitmap index.
+ */
+ public static PackBitmapIndex read(InputStream fd,
+ SupplierWithIOException packIndexSupplier,
+ SupplierWithIOException reverseIndexSupplier)
throws IOException {
- return new PackBitmapIndexV1(fd, packIndex, reverseIndex);
+ return new PackBitmapIndexV1(fd, packIndexSupplier,
+ reverseIndexSupplier);
}
/** Footer checksum applied on the bottom of the pack file. */
@@ -121,7 +149,8 @@ public static PackBitmapIndex read(
* @throws java.lang.IllegalArgumentException
* when the item is not found.
*/
- public abstract ObjectId getObject(int position) throws IllegalArgumentException;
+ public abstract ObjectId getObject(int position)
+ throws IllegalArgumentException;
/**
* Returns a bitmap containing positions for objects that have the given Git
@@ -161,4 +190,19 @@ public abstract EWAHCompressedBitmap ofObjectType(
* @return the number of bitmaps in this bitmap index.
*/
public abstract int getBitmapCount();
+
+ /**
+ * Supplier that propagates IOException.
+ *
+ * @param
+ * the return type which is expected from {@link #get()}
+ */
+ @FunctionalInterface
+ public interface SupplierWithIOException {
+ /**
+ * @return result
+ * @throws IOException
+ */
+ T get() throws IOException;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
index b7d241f3f..6846e3bca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
@@ -14,8 +14,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@@ -46,11 +49,13 @@ class PackBitmapIndexV1 extends BasePackBitmapIndex {
private final ObjectIdOwnerMap bitmaps;
- PackBitmapIndexV1(final InputStream fd, PackIndex packIndex,
- PackReverseIndex reverseIndex) throws IOException {
+ PackBitmapIndexV1(final InputStream fd,
+ SupplierWithIOException packIndexSupplier,
+ SupplierWithIOException reverseIndexSupplier)
+ throws IOException {
+ // An entry is object id, xor offset, flag byte, and a length encoded
+ // bitmap. The object id is an int32 of the nth position sorted by name.
super(new ObjectIdOwnerMap());
- this.packIndex = packIndex;
- this.reverseIndex = reverseIndex;
this.bitmaps = getBitmaps();
final byte[] scratch = new byte[32];
@@ -97,10 +102,10 @@ class PackBitmapIndexV1 extends BasePackBitmapIndex {
this.blobs = readBitmap(dataInput);
this.tags = readBitmap(dataInput);
- // An entry is object id, xor offset, flag byte, and a length encoded
- // bitmap. The object id is an int32 of the nth position sorted by name.
+ // Read full bitmap from storage first.
+ List idxPositionBitmapList = new ArrayList<>();
// The xor offset is a single byte offset back in the list of entries.
- StoredBitmap[] recentBitmaps = new StoredBitmap[MAX_XOR_OFFSET];
+ IdxPositionBitmap[] recentBitmaps = new IdxPositionBitmap[MAX_XOR_OFFSET];
for (int i = 0; i < (int) numEntries; i++) {
IO.readFully(fd, scratch, 0, 6);
int nthObjectId = NB.decodeInt32(scratch, 0);
@@ -108,38 +113,58 @@ class PackBitmapIndexV1 extends BasePackBitmapIndex {
int flags = scratch[5];
EWAHCompressedBitmap bitmap = readBitmap(dataInput);
- if (nthObjectId < 0)
+ if (nthObjectId < 0) {
throw new IOException(MessageFormat.format(
JGitText.get().invalidId, String.valueOf(nthObjectId)));
- if (xorOffset < 0)
+ }
+ if (xorOffset < 0) {
throw new IOException(MessageFormat.format(
JGitText.get().invalidId, String.valueOf(xorOffset)));
- if (xorOffset > MAX_XOR_OFFSET)
+ }
+ if (xorOffset > MAX_XOR_OFFSET) {
throw new IOException(MessageFormat.format(
JGitText.get().expectedLessThanGot,
String.valueOf(MAX_XOR_OFFSET),
String.valueOf(xorOffset)));
- if (xorOffset > i)
+ }
+ if (xorOffset > i) {
throw new IOException(MessageFormat.format(
JGitText.get().expectedLessThanGot, String.valueOf(i),
String.valueOf(xorOffset)));
-
- ObjectId objectId = packIndex.getObjectId(nthObjectId);
- StoredBitmap xorBitmap = null;
+ }
+ IdxPositionBitmap xorIdxPositionBitmap = null;
if (xorOffset > 0) {
int index = (i - xorOffset);
- xorBitmap = recentBitmaps[index % recentBitmaps.length];
- if (xorBitmap == null)
+ xorIdxPositionBitmap = recentBitmaps[index
+ % recentBitmaps.length];
+ if (xorIdxPositionBitmap == null) {
throw new IOException(MessageFormat.format(
JGitText.get().invalidId,
String.valueOf(xorOffset)));
+ }
}
-
- StoredBitmap sb = new StoredBitmap(
- objectId, bitmap, xorBitmap, flags);
- bitmaps.add(sb);
- recentBitmaps[i % recentBitmaps.length] = sb;
+ IdxPositionBitmap idxPositionBitmap = new IdxPositionBitmap(
+ nthObjectId, xorIdxPositionBitmap, bitmap, flags);
+ idxPositionBitmapList.add(idxPositionBitmap);
+ recentBitmaps[i % recentBitmaps.length] = idxPositionBitmap;
}
+
+ this.packIndex = packIndexSupplier.get();
+ for (int i = 0; i < idxPositionBitmapList.size(); ++i) {
+ IdxPositionBitmap idxPositionBitmap = idxPositionBitmapList.get(i);
+ ObjectId objectId = packIndex
+ .getObjectId(idxPositionBitmap.nthObjectId);
+ StoredBitmap sb = new StoredBitmap(objectId,
+ idxPositionBitmap.bitmap,
+ idxPositionBitmap.getXorStoredBitmap(),
+ idxPositionBitmap.flags);
+ // Save the StoredBitmap for a possible future XorStoredBitmap
+ // reference.
+ idxPositionBitmap.sb = sb;
+ bitmaps.add(sb);
+ }
+
+ this.reverseIndex = reverseIndexSupplier.get();
}
/** {@inheritDoc} */
@@ -214,4 +239,34 @@ private static EWAHCompressedBitmap readBitmap(DataInput dataInput)
bitmap.deserialize(dataInput);
return bitmap;
}
+
+ /**
+ * Temporary holder of object position in pack index and other metadata for
+ * {@code StoredBitmap}.
+ */
+ private static final class IdxPositionBitmap {
+ int nthObjectId;
+
+ IdxPositionBitmap xorIdxPositionBitmap;
+
+ EWAHCompressedBitmap bitmap;
+
+ int flags;
+
+ StoredBitmap sb;
+
+ IdxPositionBitmap(int nthObjectId,
+ @Nullable IdxPositionBitmap xorIdxPositionBitmap,
+ EWAHCompressedBitmap bitmap, int flags) {
+ this.nthObjectId = nthObjectId;
+ this.xorIdxPositionBitmap = xorIdxPositionBitmap;
+ this.bitmap = bitmap;
+ this.flags = flags;
+ }
+
+ StoredBitmap getXorStoredBitmap() {
+ return xorIdxPositionBitmap == null ? null
+ : xorIdxPositionBitmap.sb;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
index 009631289..d07713db8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
@@ -59,7 +59,6 @@
class BlockReader {
private byte blockType;
private long endPosition;
- private boolean truncated;
private byte[] buf;
private int bufLen;
@@ -79,10 +78,6 @@ byte type() {
return blockType;
}
- boolean truncated() {
- return truncated;
- }
-
long endPosition() {
return endPosition;
}
@@ -298,16 +293,8 @@ private void parseBlockStart(BlockSource src, long pos, int fileBlockSize)
// Log blocks must be inflated after the header.
long deflatedSize = inflateBuf(src, pos, blockLen, fileBlockSize);
endPosition = pos + 4 + deflatedSize;
- }
- if (bufLen < blockLen) {
- if (blockType != INDEX_BLOCK_TYPE) {
- throw invalidBlock();
- }
- // Its OK during sequential scan for an index block to have been
- // partially read and be truncated in-memory. This happens when
- // the index block is larger than the file's blockSize. Caller
- // will break out of its scan loop once it sees the blockType.
- truncated = true;
+ } else if (bufLen < blockLen) {
+ readBlockIntoBuf(src, pos, blockLen);
} else if (bufLen > blockLen) {
bufLen = blockLen;
}
@@ -372,7 +359,7 @@ private void setupEmptyFileBlock() {
}
void verifyIndex() throws IOException {
- if (blockType != INDEX_BLOCK_TYPE || truncated) {
+ if (blockType != INDEX_BLOCK_TYPE) {
throw invalidBlock();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
index 9e2ae9160..cabb2e184 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
@@ -435,7 +435,7 @@ private BlockReader readBlock(long pos, long end) throws IOException {
BlockReader b = new BlockReader();
b.readBlock(src, pos, sz);
- if (b.type() == INDEX_BLOCK_TYPE && !b.truncated()) {
+ if (b.type() == INDEX_BLOCK_TYPE) {
if (indexCache == null) {
indexCache = new LongMap<>();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index 228c25f0a..a7a143328 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -210,7 +210,7 @@ private List parse(BufferedReader reader)
// The man page doesn't say so, but the openssh parser (readconf.c)
// starts out in active mode and thus always applies any lines that
// occur before the first host block. We gather those options in a
- // HostEntry for DEFAULT_NAME.
+ // HostEntry.
HostEntry defaults = new HostEntry();
HostEntry current = defaults;
entries.add(defaults);
@@ -309,8 +309,7 @@ private List parseList(String argument) {
* @return the validated and possibly sanitized value
*/
protected String validate(String key, String value) {
- if (String.CASE_INSENSITIVE_ORDER.compare(key,
- SshConstants.PREFERRED_AUTHENTICATIONS) == 0) {
+ if (SshConstants.PREFERRED_AUTHENTICATIONS.equalsIgnoreCase(key)) {
return stripWhitespace(value);
}
return value;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index c06ca0bc1..8d571f5b1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -528,6 +528,7 @@ private List[ getMergedInto(RevCommit needle, Collection][ haystacks,
Enum returnStrategy, ProgressMonitor monitor) throws IOException {
List][ result = new ArrayList<>();
List uninteresting = new ArrayList<>();
+ List marked = new ArrayList<>();
RevFilter oldRF = filter;
TreeFilter oldTF = treeFilter;
try {
@@ -540,22 +541,25 @@ private List][ getMergedInto(RevCommit needle, Collection][ haystacks,
return result;
}
monitor.update(1);
- RevObject o = parseAny(r.getObjectId());
+ RevObject o = peel(parseAny(r.getObjectId()));
if (!(o instanceof RevCommit)) {
continue;
}
RevCommit c = (RevCommit) o;
- resetRetain(RevFlag.UNINTERESTING);
+ reset(UNINTERESTING | TEMP_MARK);
markStart(c);
boolean commitFound = false;
RevCommit next;
while ((next = next()) != null) {
- if (References.isSameObject(next, needle)) {
+ if (References.isSameObject(next, needle)
+ || (next.flags & TEMP_MARK) != 0) {
result.add(r);
if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND) {
return result;
}
commitFound = true;
+ c.flags |= TEMP_MARK;
+ marked.add(c);
break;
}
}
@@ -571,6 +575,9 @@ private List][ getMergedInto(RevCommit needle, Collection][ haystacks,
roots.addAll(uninteresting);
filter = oldRF;
treeFilter = oldTF;
+ for (RevCommit c : marked) {
+ c.flags &= ~TEMP_MARK;
+ }
}
return result;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 258271dc6..6da6c1334 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -57,6 +57,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -406,14 +407,16 @@ public final Map getAdvertisedRefs() {
* were advertised.
*/
public void setAdvertisedRefs(@Nullable Map allRefs) {
- if (allRefs != null)
+ if (allRefs != null) {
refs = allRefs;
- else
- refs = db.getAllRefs();
- if (refFilter == RefFilter.DEFAULT)
+ } else {
+ refs = getAllRefs();
+ }
+ if (refFilter == RefFilter.DEFAULT) {
refs = transferConfig.getRefFilter().filter(refs);
- else
+ } else {
refs = refFilter.filter(refs);
+ }
}
/**
@@ -864,6 +867,20 @@ public PackStatistics getStatistics() {
return statistics;
}
+ /**
+ * Extract the full list of refs from the ref-db.
+ *
+ * @return Map of all refname/ref
+ */
+ private Map getAllRefs() {
+ try {
+ return db.getRefDatabase().getRefs().stream().collect(
+ Collectors.toMap(Ref::getName, Function.identity()));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private Map getAdvertisedOrDefaultRefs() throws IOException {
if (refs != null) {
return refs;
@@ -1092,6 +1109,7 @@ private void lsRefsV2(PacketLineOut pckOut) throws IOException {
rawOut.stopBuffering();
PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
+ adv.init(db);
adv.setUseProtocolV2(true);
if (req.getPeel()) {
adv.setDerefTags(true);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index 31a05933c..dd656e5f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -23,7 +23,6 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
-import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileStore;
import java.nio.file.Files;
@@ -1507,7 +1506,7 @@ protected File discoverGitSystemConfig() {
try {
v = readPipe(gitExe.getParentFile(),
new String[] { gitExe.getPath(), "--version" }, //$NON-NLS-1$
- Charset.defaultCharset().name());
+ SystemReader.getInstance().getDefaultCharset().name());
} catch (CommandFailedException e) {
LOG.warn(e.getMessage());
return null;
@@ -1527,7 +1526,7 @@ protected File discoverGitSystemConfig() {
w = readPipe(gitExe.getParentFile(),
new String[] { gitExe.getPath(), "config", "--system", //$NON-NLS-1$ //$NON-NLS-2$
"--edit" }, //$NON-NLS-1$
- Charset.defaultCharset().name(), env);
+ SystemReader.getInstance().getDefaultCharset().name(), env);
} catch (CommandFailedException e) {
LOG.warn(e.getMessage());
return null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index 946d81c73..1c113617f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -17,7 +17,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.nio.charset.Charset;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileStore;
import java.nio.file.FileSystemException;
@@ -119,8 +118,8 @@ private static int readUmask() {
new String[] { "sh", "-c", "umask" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
null, null);
try (BufferedReader lineRead = new BufferedReader(
- new InputStreamReader(p.getInputStream(), Charset
- .defaultCharset().name()))) {
+ new InputStreamReader(p.getInputStream(), SystemReader
+ .getInstance().getDefaultCharset().name()))) {
if (p.waitFor() == 0) {
String s = lineRead.readLine();
if (s != null && s.matches("0?\\d{3}")) { //$NON-NLS-1$
@@ -150,7 +149,8 @@ protected File discoverGitExe() {
try {
String w = readPipe(userHome(),
new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
- Charset.defaultCharset().name());
+ SystemReader.getInstance().getDefaultCharset()
+ .name());
if (!StringUtils.isEmptyOrNull(w)) {
gitExe = new File(w);
}
@@ -168,7 +168,8 @@ protected File discoverGitExe() {
try {
String w = readPipe(userHome(),
new String[] { "xcode-select", "-p" }, //$NON-NLS-1$ //$NON-NLS-2$
- Charset.defaultCharset().name());
+ SystemReader.getInstance().getDefaultCharset()
+ .name());
if (StringUtils.isEmptyOrNull(w)) {
gitExe = null;
} else {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index d44dc32d1..ff094f697 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -13,7 +13,6 @@
import java.io.File;
import java.io.IOException;
-import java.nio.charset.Charset;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
@@ -150,8 +149,10 @@ protected File discoverGitExe() {
String w;
try {
w = readPipe(userHome(),
- new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
- Charset.defaultCharset().name());
+ new String[] { "bash", "--login", "-c", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "which git" }, // //$NON-NLS-1$
+ SystemReader.getInstance().getDefaultCharset()
+ .name());
} catch (CommandFailedException e) {
LOG.warn(e.getMessage());
return null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
index df9c6c78f..93bf84719 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
@@ -1160,7 +1160,7 @@ public static String decodeNoFallback(final Charset cs,
// Try the default character set. A small group of people
// might actually use the same (or very similar) locale.
- Charset defcs = Charset.defaultCharset();
+ Charset defcs = SystemReader.getInstance().getDefaultCharset();
if (!defcs.equals(cs) && !defcs.equals(UTF_8)) {
try {
return decode(b, defcs);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 54fd539f6..16e257791 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -17,6 +17,9 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -198,6 +201,8 @@ public static void setInstance(SystemReader newReader) {
private AtomicReference jgitConfig = new AtomicReference<>();
+ private volatile Charset defaultCharset;
+
private void init() {
// Creating ObjectChecker must be deferred. Unit tests change
// behavior of is{Windows,MacOS} in constructor of subclass.
@@ -438,6 +443,35 @@ public Locale getLocale() {
return Locale.getDefault();
}
+ /**
+ * Retrieves the default {@link Charset} depending on the system locale.
+ *
+ * @return the {@link Charset}
+ * @since 6.0
+ * @see JEP 400
+ */
+ public Charset getDefaultCharset() {
+ Charset result = defaultCharset;
+ if (result == null) {
+ // JEP 400: Java 18 populates this system property.
+ String encoding = getProperty("native.encoding"); //$NON-NLS-1$
+ try {
+ if (!StringUtils.isEmptyOrNull(encoding)) {
+ result = Charset.forName(encoding);
+ }
+ } catch (IllegalCharsetNameException
+ | UnsupportedCharsetException e) {
+ LOG.error(JGitText.get().logInvalidDefaultCharset, encoding);
+ }
+ if (result == null) {
+ // This is always UTF-8 on Java >= 18.
+ result = Charset.defaultCharset();
+ }
+ defaultCharset = result;
+ }
+ return result;
+ }
+
/**
* Returns a simple date format instance as specified by the given pattern.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
index ea6b6588c..97fe01e5d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
@@ -137,6 +137,9 @@ private int buffer(byte[] b, int off, int len) throws IOException {
private void decideMode() throws IOException {
if (detectBinary) {
isBinary = RawText.isBinary(binbuf, binbufcnt);
+ if (!isBinary) {
+ isBinary = RawText.isCrLfText(binbuf, binbufcnt);
+ }
detectBinary = false;
}
int cachedLen = binbufcnt;
diff --git a/pom.xml b/pom.xml
index 6d98b7cdb..0833c7150 100644
--- a/pom.xml
+++ b/pom.xml
@@ -827,8 +827,8 @@
- com.google.errorprone
- error_prone_core
+ com.google.errorprone
+ error_prone_core
2.9.0
]