packLoadListeners = new ArrayList<>();
/**
* Initialize a new DfsReader
@@ -834,6 +838,100 @@ public DfsReaderIoStats getIoStats() {
return new DfsReaderIoStats(stats);
}
+ /** Announces when data is loaded by reader */
+ protected interface PackLoadListener {
+ /**
+ * Immutable copy of a DFS block metadata
+ */
+ class DfsBlockData {
+ private final int identityHash;
+ private final int size;
+
+ static DfsBlockData of(DfsBlock src) {
+ return new DfsBlockData(src);
+ }
+
+ private DfsBlockData(DfsBlock src) {
+ this.identityHash = System.identityHashCode(src);
+ this.size = src.size();
+ }
+
+ public int getIdentityHash() {
+ return identityHash;
+ }
+
+ public int getSize() {
+ return size;
+ }
+ }
+
+ /**
+ * This is called when an index reference (e.g. primary index, reverse
+ * index, ...) is set in the reader, regarless if loaded from scratch or
+ * copied from cache.
+ *
+ * During the lifetime of the reader, the reference for an index should
+ * be set only once.
+ *
+ * @param packName
+ * Name of the pack
+ * @param src
+ * Source of the pack (e.g. GC, COMPACT, ...)
+ * @param ext
+ * Extension in the pack (e.g. IDX, RIDX, ...)
+ * @param size
+ * Size of the data loaded (usually as bytes in disk)
+ * @param loadedIdx
+ * reference to the loaded index
+ */
+ void onIndexLoad(String packName, PackSource src, PackExt ext, long size,
+ Object loadedIdx);
+
+ /**
+ * This is called when a dfs block is loaded into the reader.
+ *
+ * The reader keeps only one block at a time in memory, so during a
+ * request the same block could be loaded multiple times.
+ *
+ * @param packName
+ * Name of the pack this block belongs to
+ * @param src
+ * Source of the pack (e.g. GC, COMPACT, ...)
+ * @param ext
+ * Extension in the pack (e.g. PACK or REFTABLE)
+ * @param position
+ * Offset in the file requested by caller
+ * @param dfsBlockData
+ * Metadata of the block
+ */
+ void onBlockLoad(String packName, PackSource src, PackExt ext,
+ long position, DfsBlockData dfsBlockData);
+ }
+
+ void emitIndexLoad(DfsPackDescription packDescription, PackExt ext,
+ Object loadedIdx) {
+ packLoadListeners.forEach(
+ listener -> listener.onIndexLoad(packDescription.getFileName(ext),
+ packDescription.getPackSource(), ext,
+ packDescription.getFileSize(ext), loadedIdx));
+ }
+
+ void emitBlockLoad(BlockBasedFile file, long position, DfsBlock dfsBlock) {
+ packLoadListeners
+ .forEach(listener -> listener.onBlockLoad(file.getFileName(),
+ file.desc.getPackSource(), file.ext, position,
+ DfsBlockData.of(dfsBlock)));
+ }
+
+ /**
+ * Add listener to record loads by this reader
+ *
+ * @param listener a listener
+ */
+ protected void addPackLoadListener(PackLoadListener listener) {
+ packLoadListeners.add(listener);
+ }
+
/**
* {@inheritDoc}
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 0ef38db31..579f93179 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -259,7 +259,7 @@ public Pack openPack(File pack) throws IOException {
}
PackFile bitmapIdx = pf.create(BITMAP_INDEX);
- Pack res = new Pack(pack, bitmapIdx.exists() ? bitmapIdx : null);
+ Pack res = new Pack(config, pack, bitmapIdx.exists() ? bitmapIdx : null);
packed.insert(res);
return res;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
index 2b5586a2c..90f981167 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
@@ -15,6 +15,8 @@
import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REVERSE_INDEX;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS;
import java.io.EOFException;
import java.io.File;
@@ -32,6 +34,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.CRC32;
@@ -53,8 +56,10 @@
import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.util.Optionally;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -79,6 +84,8 @@ public class Pack implements Iterable {
public static final Comparator SORT = (a, b) -> b.packLastModified
.compareTo(a.packLastModified);
+ private boolean useStrongRefs;
+
private final PackFile packFile;
private PackFile keepFile;
@@ -111,11 +118,11 @@ public class Pack implements Iterable {
private byte[] packChecksum;
- private volatile PackIndex loadedIdx;
+ private volatile Optionally loadedIdx = Optionally.empty();
- private PackReverseIndex reverseIdx;
+ private Optionally reverseIdx = Optionally.empty();
- private PackBitmapIndex bitmapIdx;
+ private Optionally bitmapIdx = Optionally.empty();
/**
* Objects we have tried to read, and discovered to be corrupt.
@@ -129,12 +136,16 @@ public class Pack implements Iterable {
/**
* Construct a reader for an existing, pre-indexed packfile.
*
+ * @param cfg
+ * configuration this directory consults for write settings.
* @param packFile
* path of the .pack
file holding the data.
* @param bitmapIdxFile
* existing bitmap index file with the same base as the pack
*/
- public Pack(File packFile, @Nullable PackFile bitmapIdxFile) {
+ public Pack(Config cfg, File packFile, @Nullable PackFile bitmapIdxFile) {
+ useStrongRefs = cfg.getBoolean(CONFIG_CORE_SECTION,
+ CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS, WindowCache.getInstance().isPackedIndexGitUseStrongRefs());
this.packFile = new PackFile(packFile);
this.fileSnapshot = PackFileSnapshot.save(packFile);
this.packLastModified = fileSnapshot.lastModifiedInstant();
@@ -148,57 +159,58 @@ public Pack(File packFile, @Nullable PackFile bitmapIdxFile) {
}
private PackIndex idx() throws IOException {
- PackIndex idx = loadedIdx;
- if (idx == null) {
- synchronized (this) {
- idx = loadedIdx;
- if (idx == null) {
- if (invalid) {
- throw new PackInvalidException(packFile,
- invalidatingCause);
- }
- try {
- long start = System.currentTimeMillis();
- PackFile idxFile = packFile.create(INDEX);
- idx = PackIndex.open(idxFile);
- if (LOG.isDebugEnabled()) {
- LOG.debug(String.format(
- "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$
- idxFile.getAbsolutePath(),
- Float.valueOf(idxFile.length()
- / (1024f * 1024)),
- Long.valueOf(System.currentTimeMillis()
- - start)));
- }
-
- if (packChecksum == null) {
- packChecksum = idx.packChecksum;
- fileSnapshot.setChecksum(
- ObjectId.fromRaw(packChecksum));
- } else if (!Arrays.equals(packChecksum,
- idx.packChecksum)) {
- throw new PackMismatchException(MessageFormat
- .format(JGitText.get().packChecksumMismatch,
- packFile.getPath(),
- PackExt.PACK.getExtension(),
- Hex.toHexString(packChecksum),
- PackExt.INDEX.getExtension(),
- Hex.toHexString(idx.packChecksum)));
- }
- loadedIdx = idx;
- } catch (InterruptedIOException e) {
- // don't invalidate the pack, we are interrupted from
- // another thread
- throw e;
- } catch (IOException e) {
- invalid = true;
- invalidatingCause = e;
- throw e;
- }
+ Optional optional = loadedIdx.getOptional();
+ if (optional.isPresent()) {
+ return optional.get();
+ }
+ synchronized (this) {
+ optional = loadedIdx.getOptional();
+ if (optional.isPresent()) {
+ return optional.get();
+ }
+ if (invalid) {
+ throw new PackInvalidException(packFile, invalidatingCause);
+ }
+ try {
+ long start = System.currentTimeMillis();
+ PackFile idxFile = packFile.create(INDEX);
+ PackIndex idx = PackIndex.open(idxFile);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format(
+ "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$
+ idxFile.getAbsolutePath(),
+ Float.valueOf(idxFile.length()
+ / (1024f * 1024)),
+ Long.valueOf(System.currentTimeMillis()
+ - start)));
}
+
+ if (packChecksum == null) {
+ packChecksum = idx.packChecksum;
+ fileSnapshot.setChecksum(
+ ObjectId.fromRaw(packChecksum));
+ } else if (!Arrays.equals(packChecksum,
+ idx.packChecksum)) {
+ throw new PackMismatchException(MessageFormat
+ .format(JGitText.get().packChecksumMismatch,
+ packFile.getPath(),
+ PackExt.PACK.getExtension(),
+ Hex.toHexString(packChecksum),
+ PackExt.INDEX.getExtension(),
+ Hex.toHexString(idx.packChecksum)));
+ }
+ loadedIdx = optionally(idx);
+ return idx;
+ } catch (InterruptedIOException e) {
+ // don't invalidate the pack, we are interrupted from
+ // another thread
+ throw e;
+ } catch (IOException e) {
+ invalid = true;
+ invalidatingCause = e;
+ throw e;
}
}
- return idx;
}
/**
* Get the File object which locates this pack on disk.
@@ -288,8 +300,9 @@ void resolve(Set matches, AbbreviatedObjectId id, int matchLimit)
public void close() {
WindowCache.purge(this);
synchronized (this) {
- loadedIdx = null;
- reverseIdx = null;
+ loadedIdx.clear();
+ reverseIdx.clear();
+ bitmapIdx.clear();
}
}
@@ -1127,40 +1140,41 @@ synchronized PackBitmapIndex getBitmapIndex() throws IOException {
if (invalid || bitmapIdxFile == null) {
return null;
}
- if (bitmapIdx == null) {
- final PackBitmapIndex idx;
- try {
- idx = PackBitmapIndex.open(bitmapIdxFile, idx(),
- getReverseIdx());
- } catch (FileNotFoundException e) {
- // Once upon a time this bitmap file existed. Now it
- // has been removed. Most likely an external gc has
- // removed this packfile and the bitmap
- bitmapIdxFile = null;
- return null;
- }
-
+ Optional optional = bitmapIdx.getOptional();
+ if (optional.isPresent()) {
+ return optional.get();
+ }
+ try {
+ PackBitmapIndex idx = PackBitmapIndex.open(bitmapIdxFile, idx(),
+ getReverseIdx());
// At this point, idx() will have set packChecksum.
if (Arrays.equals(packChecksum, idx.packChecksum)) {
- bitmapIdx = idx;
- } else {
- bitmapIdxFile = null;
+ bitmapIdx = optionally(idx);
+ return idx;
}
+ } catch (FileNotFoundException e) {
+ // Once upon a time this bitmap file existed. Now it
+ // has been removed. Most likely an external gc has
+ // removed this packfile and the bitmap
}
- return bitmapIdx;
+ bitmapIdxFile = null;
+ return null;
}
private synchronized PackReverseIndex getReverseIdx() throws IOException {
if (invalid) {
throw new PackInvalidException(packFile, invalidatingCause);
}
- if (reverseIdx == null) {
- PackFile reverseIndexFile = packFile.create(REVERSE_INDEX);
- reverseIdx = PackReverseIndexFactory.openOrCompute(reverseIndexFile,
- getObjectCount(), () -> getIndex());
- reverseIdx.verifyPackChecksum(getPackFile().getPath());
+ Optional optional = reverseIdx.getOptional();
+ if (optional.isPresent()) {
+ return optional.get();
}
- return reverseIdx;
+ PackFile reverseIndexFile = packFile.create(REVERSE_INDEX);
+ PackReverseIndex revIdx = PackReverseIndexFactory.openOrCompute(reverseIndexFile,
+ getObjectCount(), () -> getIndex());
+ revIdx.verifyPackChecksum(getPackFile().getPath());
+ reverseIdx = optionally(revIdx);
+ return revIdx;
}
private boolean isCorrupt(long offset) {
@@ -1195,4 +1209,8 @@ public String toString() {
+ packFile.length() + ", packChecksum="
+ ObjectId.fromRaw(packChecksum).name() + "]";
}
+
+ private Optionally optionally(T element) {
+ return useStrongRefs ? new Optionally.Hard<>(element) : new Optionally.Soft<>(element);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
index 85b2d34a9..28f6250a2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
@@ -65,6 +65,8 @@ class PackDirectory {
private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY,
new Pack[0]);
+ private final Config config;
+
private final File directory;
private final AtomicReference packList;
@@ -80,6 +82,7 @@ class PackDirectory {
* the location of the {@code pack} directory.
*/
PackDirectory(Config config, File directory) {
+ this.config = config;
this.directory = directory;
packList = new AtomicReference<>(NO_PACKS);
@@ -457,7 +460,7 @@ private PackList scanPacksImpl(PackList old) {
continue;
}
- list.add(new Pack(packFile, packFilesByExt.get(BITMAP_INDEX)));
+ list.add(new Pack(config, packFile, packFilesByExt.get(BITMAP_INDEX)));
foundNew = true;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index 25653b3ce..81537dd46 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -435,7 +435,9 @@ static final void purge(Pack pack) {
private final AtomicBoolean publishMBean = new AtomicBoolean();
- private boolean useStrongRefs;
+ private final boolean useStrongRefs;
+
+ private final boolean useStrongIndexRefs;
private WindowCache(WindowCacheConfig cfg) {
tableSize = tableSize(cfg);
@@ -467,6 +469,7 @@ else if (eb < 4)
windowSizeShift = bits(cfg.getPackedGitWindowSize());
windowSize = 1 << windowSizeShift;
useStrongRefs = cfg.isPackedGitUseStrongRefs();
+ useStrongIndexRefs = cfg.isPackedIndexGitUseStrongRefs();
queue = useStrongRefs ? new StrongCleanupQueue(this)
: new SoftCleanupQueue(this);
@@ -751,6 +754,10 @@ private static Entry clean(Entry top) {
return n == top.next ? top : new Entry(n, top.ref);
}
+ boolean isPackedIndexGitUseStrongRefs() {
+ return useStrongIndexRefs;
+ }
+
private static class Entry {
/** Next entry in the hash table's chain list. */
final Entry next;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
new file mode 100644
index 000000000..987584f64
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, 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 v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.util;
+
+import java.lang.ref.SoftReference;
+import java.util.Optional;
+
+/**
+ * Interface representing a reference to a potentially mutable optional object.
+ *
+ * @param
+ * type of the mutable optional object
+ *
+ * @since 6.7
+ */
+public interface Optionally {
+ /**
+ * A permanently empty Optionally
+ *
+ * @param
+ * type of the mutable optional object
+ *
+ */
+ public class Empty implements Optionally {
+ @Override
+ public void clear() {
+ // empty
+ }
+
+ @Override
+ public Optional getOptional() {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * A permanent(hard) reference to an object
+ *
+ * @param
+ * type of the mutable optional object
+ *
+ */
+ public class Hard implements Optionally {
+ /**
+ * The mutable optional object
+ */
+ protected T element;
+
+ /**
+ * @param element
+ * the mutable optional object
+ */
+ public Hard(T element) {
+ this.element = element;
+ }
+
+ @Override
+ public void clear() {
+ element = null;
+ }
+
+ @Override
+ public Optional getOptional() {
+ return Optional.ofNullable(element);
+ }
+ }
+
+ /**
+ * A SoftReference Optionally
+ *
+ * @param
+ * type of the mutable optional object
+ *
+ */
+ public class Soft extends SoftReference implements Optionally {
+ /**
+ * @param t
+ * the mutable optional object
+ */
+ public Soft(T t) {
+ super(t);
+ }
+
+ @Override
+ public Optional getOptional() {
+ return Optional.ofNullable(get());
+ }
+ }
+
+ /**
+ * The empty Optionally
+ */
+ public static final Optionally> EMPTY = new Empty<>();
+
+ /**
+ * @param
+ * type of the empty Optionally
+ * @return the empty Optionally
+ */
+ @SuppressWarnings("unchecked")
+ public static Optionally empty() {
+ return (Optionally) EMPTY;
+ }
+
+ /**
+ * Clear the object
+ *
+ */
+ void clear();
+
+ /**
+ * Get an Optional representing the current state of the object
+ *
+ * @return the mutable optional object
+ */
+ Optional getOptional();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index 0cccaec49..7e2c5b5ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -739,7 +739,7 @@ protected void fireConfigChangedEvent() {
listeners.dispatch(new ConfigChangedEvent());
}
- String getRawString(final String section, final String subsection,
+ private String getRawString(final String section, final String subsection,
final String name) {
String[] lst = getRawStringList(section, subsection, name);
if (lst != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 7776b0062..d812eacb6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -364,6 +364,12 @@ public final class ConfigConstants {
*/
public static final String CONFIG_KEY_PACKED_GIT_USE_STRONGREFS = "packedgitusestrongrefs";
+ /**
+ * The "packedIndexGitUseStrongRefs" key
+ * @since 6.7
+ */
+ public static final String CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS = "packedindexgitusestrongrefs";
+
/** The "remote" key */
public static final String CONFIG_KEY_REMOTE = "remote";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index 80aceb4e7..a71549c92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -34,7 +34,7 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
@Override
public boolean getBoolean(Config config, String section, String subsection,
String name, boolean defaultValue) {
- String n = config.getRawString(section, subsection, name);
+ String n = config.getString(section, subsection, name);
if (n == null) {
return defaultValue;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 910c5cbd8..7fdcc4d3e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -22,6 +22,8 @@
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.LockFailedException;
@@ -52,6 +54,8 @@ public class FileBasedConfig extends StoredConfig {
private volatile ObjectId hash;
+ private AtomicBoolean exists = new AtomicBoolean();
+
/**
* Create a configuration with no default fallback.
*
@@ -99,6 +103,21 @@ public final File getFile() {
return configFile;
}
+ boolean exists() {
+ return exists.get();
+ }
+
+ @Override
+ public void setStringList(String section, String subsection, String name,
+ List values) {
+ super.setStringList(section, subsection, name, values);
+ }
+
+ @Override
+ public void unsetSection(String section, String subsection) {
+ super.unsetSection(section, subsection);
+ }
+
/**
* {@inheritDoc}
*
@@ -144,6 +163,7 @@ public void load() throws IOException, ConfigInvalidException {
clear();
snapshot = lastSnapshot[0];
}
+ exists.set(wasRead != null);
} catch (IOException e) {
throw e;
} catch (Exception e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UserConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UserConfigFile.java
new file mode 100644
index 000000000..2ad74c23c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UserConfigFile.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2023, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.util.FS;
+
+/**
+ * User (global) git config based on two possible locations,
+ * {@code ~/.gitconfig} and {@code $XDG_CONFIG_HOME/git/config}.
+ *
+ * For reading, both locations are considered, first the XDG file, then the file
+ * in the home directory. All updates occur in the last file read that exists,
+ * or in the home directory file if neither exists. In other words: if only the
+ * XDG file exists, it is updated, otherwise the home directory file is updated.
+ *
+ *
+ * @since 6.7
+ */
+public class UserConfigFile extends FileBasedConfig {
+
+ private final FileBasedConfig parent;
+
+ /**
+ * Creates a new {@link UserConfigFile}.
+ *
+ * @param parent
+ * parent {@link Config}; may be {@code null}
+ * @param config
+ * {@link File} for {@code ~/.gitconfig}
+ * @param xdgConfig
+ * {@link File} for {@code $XDG_CONFIG_HOME/.gitconfig}
+ * @param fileSystem
+ * {@link FS} to use for the two files; normally
+ * {@link FS#DETECTED}
+ */
+ public UserConfigFile(Config parent, @NonNull File config,
+ @NonNull File xdgConfig, @NonNull FS fileSystem) {
+ super(new FileBasedConfig(parent, xdgConfig, fileSystem), config,
+ fileSystem);
+ this.parent = (FileBasedConfig) getBaseConfig();
+ }
+
+ @Override
+ public void setStringList(String section, String subsection, String name,
+ List values) {
+ if (exists() || !parent.exists()) {
+ super.setStringList(section, subsection, name, values);
+ } else {
+ parent.setStringList(section, subsection, name, values);
+ }
+ }
+
+ @Override
+ public void unset(String section, String subsection, String name) {
+ if (exists() || !parent.exists()) {
+ super.unset(section, subsection, name);
+ } else {
+ parent.unset(section, subsection, name);
+ }
+ }
+
+ @Override
+ public void unsetSection(String section, String subsection) {
+ if (exists() || !parent.exists()) {
+ super.unsetSection(section, subsection);
+ } else {
+ parent.unsetSection(section, subsection);
+ }
+ }
+
+ @Override
+ public boolean isOutdated() {
+ return super.isOutdated() || parent.isOutdated();
+ }
+
+ @Override
+ public void load() throws IOException, ConfigInvalidException {
+ if (super.isOutdated()) {
+ super.load();
+ }
+ if (parent.isOutdated()) {
+ parent.load();
+ }
+ }
+
+ @Override
+ public void save() throws IOException {
+ if (exists() || !parent.exists()) {
+ if (exists() || !toText().strip().isEmpty()) {
+ super.save();
+ }
+ } else {
+ parent.save();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
index a12f65259..27795ab96 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -18,6 +18,7 @@
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_GIT_WINDOWSIZE;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_TRESHOLD;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_GIT_USE_STRONGREFS;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS;
import org.eclipse.jgit.internal.storage.file.WindowCache;
import org.eclipse.jgit.lib.Config;
@@ -39,6 +40,8 @@ public class WindowCacheConfig {
private boolean useStrongRefs;
+ private boolean useStrongIndexRefs;
+
private int packedGitWindowSize;
private boolean packedGitMMAP;
@@ -56,6 +59,7 @@ public WindowCacheConfig() {
packedGitOpenFiles = 128;
packedGitLimit = 10 * MB;
useStrongRefs = false;
+ useStrongIndexRefs = true;
packedGitWindowSize = 8 * KB;
packedGitMMAP = false;
deltaBaseCacheLimit = 10 * MB;
@@ -132,6 +136,31 @@ public void setPackedGitUseStrongRefs(boolean useStrongRefs) {
this.useStrongRefs = useStrongRefs;
}
+ /**
+ * Get whether the Pack indices cache should use strong references or
+ * SoftReferences
+ *
+ * @return {@code true} if the cached Pack indices should use strong references,
+ * otherwise it will use {@link java.lang.ref.SoftReference}s
+ * @since 6.7
+ */
+ public boolean isPackedIndexGitUseStrongRefs() {
+ return useStrongIndexRefs;
+ }
+
+ /**
+ * Set if the Pack indices cache should use strong refs or soft refs
+ *
+ * @param useStrongRefs
+ * if @{code true} the Pack strongly references cached indices
+ * otherwise it uses {@link java.lang.ref.SoftReference}s which
+ * can be evicted by the Java gc if heap is almost full
+ * @since 6.7
+ */
+ public void setPackedIndexGitUseStrongRefs(boolean useStrongRefs) {
+ this.useStrongIndexRefs = useStrongRefs;
+ }
+
/**
* Get size in bytes of a single window mapped or read in from the pack
* file.
@@ -270,6 +299,8 @@ public WindowCacheConfig fromConfig(Config rc) {
setPackedGitUseStrongRefs(rc.getBoolean(CONFIG_CORE_SECTION,
CONFIG_KEY_PACKED_GIT_USE_STRONGREFS,
isPackedGitUseStrongRefs()));
+ setPackedIndexGitUseStrongRefs(rc.getBoolean(CONFIG_CORE_SECTION,
+ CONFIG_KEY_PACKED_INDEX_GIT_USE_STRONGREFS, isPackedIndexGitUseStrongRefs()));
setPackedGitOpenFiles(rc.getInt(CONFIG_CORE_SECTION, null,
CONFIG_KEY_PACKED_GIT_OPENFILES, getPackedGitOpenFiles()));
setPackedGitLimit(rc.getLong(CONFIG_CORE_SECTION, null,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
index 80877bbdc..8cc531627 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -66,19 +66,7 @@ public static final byte[] readFully(File path)
public static final byte[] readSome(File path, int limit)
throws FileNotFoundException, IOException {
try (SilentFileInputStream in = new SilentFileInputStream(path)) {
- byte[] buf = new byte[limit];
- int cnt = 0;
- for (;;) {
- int n = in.read(buf, cnt, buf.length - cnt);
- if (n <= 0)
- break;
- cnt += n;
- }
- if (cnt == buf.length)
- return buf;
- byte[] res = new byte[cnt];
- System.arraycopy(buf, 0, res, 0, cnt);
- return res;
+ return in.readNBytes(limit);
}
}
@@ -99,37 +87,10 @@ public static final byte[] readSome(File path, int limit)
public static final byte[] readFully(File path, int max)
throws FileNotFoundException, IOException {
try (SilentFileInputStream in = new SilentFileInputStream(path)) {
- long sz = Math.max(path.length(), 1);
- if (sz > max)
+ byte[] buf = in.readNBytes(max);
+ if (in.read() != -1) {
throw new IOException(MessageFormat.format(
JGitText.get().fileIsTooLarge, path));
-
- byte[] buf = new byte[(int) sz];
- int valid = 0;
- for (;;) {
- if (buf.length == valid) {
- if (buf.length == max) {
- int next = in.read();
- if (next < 0)
- break;
-
- throw new IOException(MessageFormat.format(
- JGitText.get().fileIsTooLarge, path));
- }
-
- byte[] nb = new byte[Math.min(buf.length * 2, max)];
- System.arraycopy(buf, 0, nb, 0, valid);
- buf = nb;
- }
- int n = in.read(buf, valid, buf.length - valid);
- if (n < 0)
- break;
- valid += n;
- }
- if (valid < buf.length) {
- byte[] nb = new byte[valid];
- System.arraycopy(buf, 0, nb, 0, valid);
- buf = nb;
}
return buf;
}
@@ -157,26 +118,7 @@ public static final byte[] readFully(File path, int max)
*/
public static ByteBuffer readWholeStream(InputStream in, int sizeHint)
throws IOException {
- byte[] out = new byte[sizeHint];
- int pos = 0;
- while (pos < out.length) {
- int read = in.read(out, pos, out.length - pos);
- if (read < 0)
- return ByteBuffer.wrap(out, 0, pos);
- pos += read;
- }
-
- int last = in.read();
- if (last < 0)
- return ByteBuffer.wrap(out, 0, pos);
-
- try (TemporaryBuffer.Heap tmp = new TemporaryBuffer.Heap(
- Integer.MAX_VALUE)) {
- tmp.write(out);
- tmp.write(last);
- tmp.copy(in);
- return ByteBuffer.wrap(tmp.toByteArray());
- }
+ return ByteBuffer.wrap(in.readAllBytes());
}
/**
@@ -197,13 +139,9 @@ public static ByteBuffer readWholeStream(InputStream in, int sizeHint)
*/
public static void readFully(final InputStream fd, final byte[] dst,
int off, int len) throws IOException {
- while (len > 0) {
- final int r = fd.read(dst, off, len);
- if (r <= 0)
- throw new EOFException(JGitText.get().shortReadOfBlock);
- off += r;
- len -= r;
- }
+ int read = fd.readNBytes(dst, off, len);
+ if (read != len)
+ throw new EOFException(JGitText.get().shortReadOfBlock);
}
/**
@@ -271,14 +209,7 @@ public static int read(ReadableByteChannel channel, byte[] dst, int off,
*/
public static int readFully(InputStream fd, byte[] dst, int off)
throws IOException {
- int r;
- int len = 0;
- while (off < dst.length
- && (r = fd.read(dst, off, dst.length - off)) >= 0) {
- off += r;
- len += r;
- }
- return len;
+ return fd.readNBytes(dst, off, dst.length - off);
}
/**
@@ -300,6 +231,7 @@ public static int readFully(InputStream fd, byte[] dst, int off)
*/
public static void skipFully(InputStream fd, long toSkip)
throws IOException {
+ // same as fd.skipNBytes(toSkip) of JDK 12;
while (toSkip > 0) {
final long r = fd.skip(toSkip);
if (r <= 0)
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 991de51df..4a4876271 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -39,6 +39,7 @@
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.UserConfigFile;
import org.eclipse.jgit.util.time.MonotonicClock;
import org.eclipse.jgit.util.time.MonotonicSystemClock;
import org.slf4j.Logger;
@@ -124,8 +125,15 @@ public boolean isOutdated() {
@Override
public FileBasedConfig openUserConfig(Config parent, FS fs) {
- return new FileBasedConfig(parent, new File(fs.userHome(), ".gitconfig"), //$NON-NLS-1$
- fs);
+ File homeFile = new File(fs.userHome(), ".gitconfig"); //$NON-NLS-1$
+ Path xdgPath = getXdgConfigDirectory(fs);
+ if (xdgPath != null) {
+ Path configPath = xdgPath.resolve("git") //$NON-NLS-1$
+ .resolve(Constants.CONFIG);
+ return new UserConfigFile(parent, homeFile, configPath.toFile(),
+ fs);
+ }
+ return new FileBasedConfig(parent, homeFile, fs);
}
@Override
diff --git a/pom.xml b/pom.xml
index bd1126007..c26d9f216 100644
--- a/pom.xml
+++ b/pom.xml
@@ -169,7 +169,7 @@
1.7.36
3.5.0
2.10.1
- 1.75
+ 1.76
4.7.3.4
3.4.3
3.3.0
@@ -203,10 +203,6 @@
repo.eclipse.org.cbi-releases
https://repo.eclipse.org/content/repositories/cbi-releases/
-
- repo.eclipse.org.cbi-snapshots
- https://repo.eclipse.org/content/repositories/cbi-snapshots/
-
repo.eclipse.org.dash-releases
https://repo.eclipse.org/content/repositories/dash-licenses-releases/