Merge branch 'stable-6.4'
* stable-6.4: Cache trustFolderStat/trustPackedRefsStat value per-instance Refresh 'objects' dir and retry if a loose object is not found Change-Id: Iea8038dfde29ab988501469f86ee829e578a2fe8
This commit is contained in:
commit
a1901305b2
|
@ -45,7 +45,7 @@ For details on native git options see also the official [git config documentatio
|
||||||
| `core.streamFileThreshold` | `50 MiB` | ⃞ | The size threshold beyond which objects must be streamed. |
|
| `core.streamFileThreshold` | `50 MiB` | ⃞ | The size threshold beyond which objects must be streamed. |
|
||||||
| `core.supportsAtomicFileCreation` | `true` | ⃞ | Whether the filesystem supports atomic file creation. |
|
| `core.supportsAtomicFileCreation` | `true` | ⃞ | Whether the filesystem supports atomic file creation. |
|
||||||
| `core.symlinks` | Auto detect if filesystem supports symlinks| ✅ | If false, symbolic links are checked out as small plain files that contain the link text. |
|
| `core.symlinks` | Auto detect if filesystem supports symlinks| ✅ | If false, symbolic links are checked out as small plain files that contain the link text. |
|
||||||
| `core.trustFolderStat` | `true` | ⃞ | Whether to trust the pack folder's and packed-refs file's file attributes (Java equivalent of stat command on *nix). When looking for pack files, if `false` JGit will always scan the `.git/objects/pack` folder and if set to `true` it assumes that pack files are unchanged if the file attributes of the pack folder are unchanged. When getting the list of packed refs, if `false` JGit will always read the packed-refs file and if set to `true` it uses the file attributes of the packed-refs file and will only read it if a file attribute has changed. Setting this option to `false` can help to workaround caching issues on NFS, but reduces performance.|
|
| `core.trustFolderStat` | `true` | ⃞ | Whether to trust the pack folder's, packed-refs file's and loose-objects folder's file attributes (Java equivalent of stat command on *nix). When looking for pack files, if `false` JGit will always scan the `.git/objects/pack` folder and if set to `true` it assumes that pack files are unchanged if the file attributes of the pack folder are unchanged. When getting the list of packed refs, if `false` JGit will always read the packed-refs file and if set to `true` it uses the file attributes of the packed-refs file and will only read it if a file attribute has changed. When looking for loose objects, if `false` and if a loose object is not found, JGit will open and close a stream to `.git/objects` folder (which can refresh its directory listing, at least on some NFS clients) and retry looking for that loose object. Setting this option to `false` can help to workaround caching issues on NFS, but reduces performance. |
|
||||||
| `core.trustPackedRefsStat` | `unset` | ⃞ | Whether to trust the file attributes (Java equivalent of stat command on *nix) of the packed-refs file. If `never` JGit will ignore the file attributes of the packed-refs file and always read it. If `always` JGit will trust the file attributes of the packed-refs file and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, except that the packed-refs file is opened and closed before its file attributes are considered. An open/close of the packed-refs file is known to refresh its file attributes, at least on some NFS clients. If `unset`, JGit will use the behavior described in `trustFolderStat`. |
|
| `core.trustPackedRefsStat` | `unset` | ⃞ | Whether to trust the file attributes (Java equivalent of stat command on *nix) of the packed-refs file. If `never` JGit will ignore the file attributes of the packed-refs file and always read it. If `always` JGit will trust the file attributes of the packed-refs file and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, except that the packed-refs file is opened and closed before its file attributes are considered. An open/close of the packed-refs file is known to refresh its file attributes, at least on some NFS clients. If `unset`, JGit will use the behavior described in `trustFolderStat`. |
|
||||||
| `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | ✅ | The path to the root of the working tree. |
|
| `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | ✅ | The path to the root of the working tree. |
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
@ -24,6 +25,8 @@
|
||||||
import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult;
|
import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult;
|
||||||
import org.eclipse.jgit.lib.AbbreviatedObjectId;
|
import org.eclipse.jgit.lib.AbbreviatedObjectId;
|
||||||
import org.eclipse.jgit.lib.AnyObjectId;
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.ConfigConstants;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectLoader;
|
import org.eclipse.jgit.lib.ObjectLoader;
|
||||||
|
@ -52,15 +55,22 @@ class LooseObjects {
|
||||||
|
|
||||||
private final UnpackedObjectCache unpackedObjectCache;
|
private final UnpackedObjectCache unpackedObjectCache;
|
||||||
|
|
||||||
|
private final boolean trustFolderStat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a reference to an on-disk object directory.
|
* Initialize a reference to an on-disk object directory.
|
||||||
*
|
*
|
||||||
|
* @param config
|
||||||
|
* configuration for the loose objects handler.
|
||||||
* @param dir
|
* @param dir
|
||||||
* the location of the <code>objects</code> directory.
|
* the location of the <code>objects</code> directory.
|
||||||
*/
|
*/
|
||||||
LooseObjects(File dir) {
|
LooseObjects(Config config, File dir) {
|
||||||
directory = dir;
|
directory = dir;
|
||||||
unpackedObjectCache = new UnpackedObjectCache();
|
unpackedObjectCache = new UnpackedObjectCache();
|
||||||
|
trustFolderStat = config.getBoolean(
|
||||||
|
ConfigConstants.CONFIG_CORE_SECTION,
|
||||||
|
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,6 +108,19 @@ boolean hasCached(AnyObjectId id) {
|
||||||
* @return {@code true} if the specified object is stored as a loose object.
|
* @return {@code true} if the specified object is stored as a loose object.
|
||||||
*/
|
*/
|
||||||
boolean has(AnyObjectId objectId) {
|
boolean has(AnyObjectId objectId) {
|
||||||
|
boolean exists = hasWithoutRefresh(objectId);
|
||||||
|
if (trustFolderStat || exists) {
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
try (InputStream stream = Files.newInputStream(directory.toPath())) {
|
||||||
|
// refresh directory to work around NFS caching issue
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hasWithoutRefresh(objectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasWithoutRefresh(AnyObjectId objectId) {
|
||||||
return fileFor(objectId).exists();
|
return fileFor(objectId).exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +206,22 @@ ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException {
|
||||||
*/
|
*/
|
||||||
ObjectLoader getObjectLoader(WindowCursor curs, File path, AnyObjectId id)
|
ObjectLoader getObjectLoader(WindowCursor curs, File path, AnyObjectId id)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
try {
|
||||||
|
return getObjectLoaderWithoutRefresh(curs, path, id);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
if (trustFolderStat) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
try (InputStream stream = Files
|
||||||
|
.newInputStream(directory.toPath())) {
|
||||||
|
// refresh directory to work around NFS caching issues
|
||||||
|
}
|
||||||
|
return getObjectLoaderWithoutRefresh(curs, path, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectLoader getObjectLoaderWithoutRefresh(WindowCursor curs,
|
||||||
|
File path, AnyObjectId id) throws IOException {
|
||||||
try (FileInputStream in = new FileInputStream(path)) {
|
try (FileInputStream in = new FileInputStream(path)) {
|
||||||
unpackedObjectCache().add(id);
|
unpackedObjectCache().add(id);
|
||||||
return UnpackedObject.open(in, path, id, curs);
|
return UnpackedObject.open(in, path, id, curs);
|
||||||
|
@ -203,18 +242,36 @@ UnpackedObjectCache unpackedObjectCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
long getSize(WindowCursor curs, AnyObjectId id) throws IOException {
|
long getSize(WindowCursor curs, AnyObjectId id) throws IOException {
|
||||||
File f = fileFor(id);
|
try {
|
||||||
try (FileInputStream in = new FileInputStream(f)) {
|
return getSizeWithoutRefresh(curs, id);
|
||||||
unpackedObjectCache().add(id);
|
|
||||||
return UnpackedObject.getSize(in, id, curs);
|
|
||||||
} catch (FileNotFoundException noFile) {
|
} catch (FileNotFoundException noFile) {
|
||||||
if (f.exists()) {
|
try {
|
||||||
|
if (trustFolderStat) {
|
||||||
|
throw noFile;
|
||||||
|
}
|
||||||
|
try (InputStream stream = Files
|
||||||
|
.newInputStream(directory.toPath())) {
|
||||||
|
// refresh directory to work around NFS caching issue
|
||||||
|
}
|
||||||
|
return getSizeWithoutRefresh(curs, id);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
if (fileFor(id).exists()) {
|
||||||
throw noFile;
|
throw noFile;
|
||||||
}
|
}
|
||||||
unpackedObjectCache().remove(id);
|
unpackedObjectCache().remove(id);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getSizeWithoutRefresh(WindowCursor curs, AnyObjectId id)
|
||||||
|
throws IOException {
|
||||||
|
File f = fileFor(id);
|
||||||
|
try (FileInputStream in = new FileInputStream(f)) {
|
||||||
|
unpackedObjectCache().add(id);
|
||||||
|
return UnpackedObject.getSize(in, id, curs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InsertLooseObjectResult insert(File tmp, ObjectId id) throws IOException {
|
InsertLooseObjectResult insert(File tmp, ObjectId id) throws IOException {
|
||||||
final File dst = fileFor(id);
|
final File dst = fileFor(id);
|
||||||
|
|
|
@ -126,7 +126,7 @@ public ObjectDirectory(final Config cfg, final File dir,
|
||||||
File packDirectory = new File(objects, "pack"); //$NON-NLS-1$
|
File packDirectory = new File(objects, "pack"); //$NON-NLS-1$
|
||||||
File preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
|
File preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
|
||||||
alternatesFile = new File(objects, Constants.INFO_ALTERNATES);
|
alternatesFile = new File(objects, Constants.INFO_ALTERNATES);
|
||||||
loose = new LooseObjects(objects);
|
loose = new LooseObjects(config, objects);
|
||||||
packed = new PackDirectory(config, packDirectory);
|
packed = new PackDirectory(config, packDirectory);
|
||||||
preserved = new PackDirectory(config, preservedDirectory);
|
preserved = new PackDirectory(config, preservedDirectory);
|
||||||
fileCommitGraph = new FileCommitGraph(objects);
|
fileCommitGraph = new FileCommitGraph(objects);
|
||||||
|
|
|
@ -63,12 +63,12 @@ class PackDirectory {
|
||||||
private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY,
|
private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY,
|
||||||
new Pack[0]);
|
new Pack[0]);
|
||||||
|
|
||||||
private final Config config;
|
|
||||||
|
|
||||||
private final File directory;
|
private final File directory;
|
||||||
|
|
||||||
private final AtomicReference<PackList> packList;
|
private final AtomicReference<PackList> packList;
|
||||||
|
|
||||||
|
private final boolean trustFolderStat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a reference to an on-disk 'pack' directory.
|
* Initialize a reference to an on-disk 'pack' directory.
|
||||||
*
|
*
|
||||||
|
@ -78,9 +78,16 @@ class PackDirectory {
|
||||||
* the location of the {@code pack} directory.
|
* the location of the {@code pack} directory.
|
||||||
*/
|
*/
|
||||||
PackDirectory(Config config, File directory) {
|
PackDirectory(Config config, File directory) {
|
||||||
this.config = config;
|
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
packList = new AtomicReference<>(NO_PACKS);
|
packList = new AtomicReference<>(NO_PACKS);
|
||||||
|
|
||||||
|
// Whether to trust the pack folder's modification time. If set to false
|
||||||
|
// we will always scan the .git/objects/pack folder to check for new
|
||||||
|
// pack files. If set to true (default) we use the folder's size,
|
||||||
|
// modification time, and key (inode) and assume that no new pack files
|
||||||
|
// can be in this folder if these attributes have not changed.
|
||||||
|
trustFolderStat = config.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
|
||||||
|
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -331,16 +338,6 @@ private boolean doLogExponentialBackoff(int n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean searchPacksAgain(PackList old) {
|
boolean searchPacksAgain(PackList old) {
|
||||||
// Whether to trust the pack folder's modification time. If set
|
|
||||||
// to false we will always scan the .git/objects/pack folder to
|
|
||||||
// check for new pack files. If set to true (default) we use the
|
|
||||||
// lastmodified attribute of the folder and assume that no new
|
|
||||||
// pack files can be in this folder if his modification time has
|
|
||||||
// not changed.
|
|
||||||
boolean trustFolderStat = config.getBoolean(
|
|
||||||
ConfigConstants.CONFIG_CORE_SECTION,
|
|
||||||
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
|
|
||||||
|
|
||||||
return ((!trustFolderStat) || old.snapshot.isModified(directory))
|
return ((!trustFolderStat) || old.snapshot.isModified(directory))
|
||||||
&& old != scanPacks(old);
|
&& old != scanPacks(old);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,6 +174,10 @@ public class RefDirectory extends RefDatabase {
|
||||||
|
|
||||||
private List<Integer> retrySleepMs = RETRY_SLEEP_MS;
|
private List<Integer> retrySleepMs = RETRY_SLEEP_MS;
|
||||||
|
|
||||||
|
private final boolean trustFolderStat;
|
||||||
|
|
||||||
|
private final TrustPackedRefsStat trustPackedRefsStat;
|
||||||
|
|
||||||
RefDirectory(FileRepository db) {
|
RefDirectory(FileRepository db) {
|
||||||
final FS fs = db.getFS();
|
final FS fs = db.getFS();
|
||||||
parent = db;
|
parent = db;
|
||||||
|
@ -185,6 +189,13 @@ public class RefDirectory extends RefDatabase {
|
||||||
|
|
||||||
looseRefs.set(RefList.<LooseRef> emptyList());
|
looseRefs.set(RefList.<LooseRef> emptyList());
|
||||||
packedRefs.set(NO_PACKED_REFS);
|
packedRefs.set(NO_PACKED_REFS);
|
||||||
|
trustFolderStat = db.getConfig()
|
||||||
|
.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
|
||||||
|
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
|
||||||
|
trustPackedRefsStat = db.getConfig()
|
||||||
|
.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
|
||||||
|
ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT,
|
||||||
|
TrustPackedRefsStat.UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
Repository getRepository() {
|
Repository getRepository() {
|
||||||
|
@ -886,16 +897,6 @@ else if (0 <= (idx = packed.find(dst.getName())))
|
||||||
}
|
}
|
||||||
|
|
||||||
PackedRefList getPackedRefs() throws IOException {
|
PackedRefList getPackedRefs() throws IOException {
|
||||||
boolean trustFolderStat = getRepository().getConfig().getBoolean(
|
|
||||||
ConfigConstants.CONFIG_CORE_SECTION,
|
|
||||||
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
|
|
||||||
TrustPackedRefsStat trustPackedRefsStat =
|
|
||||||
getRepository().getConfig().getEnum(
|
|
||||||
ConfigConstants.CONFIG_CORE_SECTION,
|
|
||||||
null,
|
|
||||||
ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT,
|
|
||||||
TrustPackedRefsStat.UNSET);
|
|
||||||
|
|
||||||
final PackedRefList curList = packedRefs.get();
|
final PackedRefList curList = packedRefs.get();
|
||||||
|
|
||||||
switch (trustPackedRefsStat) {
|
switch (trustPackedRefsStat) {
|
||||||
|
|
Loading…
Reference in New Issue