diff options
Diffstat (limited to 'org.eclipse.jgit')
8 files changed, 105 insertions, 27 deletions
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 42b1d235bf..2e534d580f 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 @@ -52,10 +52,12 @@ import org.eclipse.jgit.internal.storage.pack.PackOutputStream; import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.util.LongList; /** @@ -1283,11 +1285,16 @@ public final class DfsPackFile extends BlockBasedFile { DfsStreamKey cgkey) throws IOException { ctx.stats.readCommitGraph++; long start = System.nanoTime(); + StoredConfig repoConfig = ctx.db.getRepository().getConfig(); + boolean readChangedPathFilters = repoConfig.getBoolean( + ConfigConstants.CONFIG_COMMIT_GRAPH_SECTION, + ConfigConstants.CONFIG_KEY_READ_CHANGED_PATHS, false); try (ReadableChannel rc = ctx.db.openFile(desc, COMMIT_GRAPH)) { long size; CommitGraph cg; try { - cg = CommitGraphLoader.read(alignTo8kBlocks(rc)); + cg = CommitGraphLoader.read(alignTo8kBlocks(rc), + readChangedPathFilters); } finally { size = rc.position(); ctx.stats.readCommitGraphBytes += size; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectory.java index 46607f60d9..1dc5776e06 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SnapshottingRefDirectory.java @@ -16,15 +16,21 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.revwalk.RevWalk; +import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * Snapshotting write-through cache of a {@link RefDirectory}. * <p> * This is intended to be short-term write-through snapshot based cache used in - * a request scope to avoid re-reading packed-refs on each read. A future - * improvement could also snapshot loose refs. + * a request scope to avoid re-reading packed-refs on each read and to avoid + * refreshing paths to a loose ref that has already been refreshed. * <p> * Only use this class when concurrent writes from other requests (not using the * same instance of SnapshottingRefDirectory) generally need not be visible to @@ -34,6 +40,7 @@ import java.util.List; */ class SnapshottingRefDirectory extends RefDirectory { final RefDirectory refDb; + private final Set<File> refreshedLooseRefDirs = ConcurrentHashMap.newKeySet(); private volatile boolean isValid; @@ -67,6 +74,22 @@ class SnapshottingRefDirectory extends RefDirectory { } @Override + void refreshPathToLooseRef(Path refPath) { + for (int i = 1; i < refPath.getNameCount(); i++) { + File dir = fileFor(refPath.subpath(0, i).toString()); + if (!refreshedLooseRefDirs.contains(dir)) { + try (InputStream stream = Files.newInputStream(dir.toPath())) { + // open the dir to refresh attributes (on some NFS clients) + } catch (IOException e) { + break; // loose ref may not exist + } finally { + refreshedLooseRefDirs.add(dir); + } + } + } + } + + @Override void delete(RefDirectoryUpdate update) throws IOException { refreshSnapshot(); super.delete(update); @@ -107,6 +130,7 @@ class SnapshottingRefDirectory extends RefDirectory { } synchronized void invalidateSnapshot() { + refreshedLooseRefDirs.clear(); isValid = false; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/CleanupService.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/CleanupService.java index 76e09307ab..29ed7564d3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/CleanupService.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/CleanupService.java @@ -9,6 +9,10 @@ */ package org.eclipse.jgit.internal.util; +import org.eclipse.jgit.internal.JGitText; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A class that is registered as an OSGi service via the manifest. If JGit runs * in OSGi, OSGi will instantiate a singleton as soon as the bundle is activated @@ -23,12 +27,17 @@ package org.eclipse.jgit.internal.util; */ public final class CleanupService { + private static final Logger LOG = LoggerFactory + .getLogger(CleanupService.class); + private static final Object LOCK = new Object(); private static CleanupService INSTANCE; private final boolean isOsgi; + private JGitText jgitText; + private Runnable cleanup; /** @@ -74,8 +83,24 @@ public final class CleanupService { if (isOsgi) { cleanup = cleanUp; } else { + // Ensure the JGitText class is loaded. Depending on the framework + // JGit runs in, it may not be possible anymore to load classes when + // the hook runs. For instance when run in a maven plug-in: the + // Plexus class world that loaded JGit may already have been + // disposed by the time the JVM shutdown hook runs when the whole + // maven build terminates. + jgitText = JGitText.get(); + assert jgitText != null; try { - Runtime.getRuntime().addShutdownHook(new Thread(cleanUp)); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + cleanUp.run(); + // Don't catch exceptions; let the JVM do the problem + // reporting. + } finally { + jgitText = null; + } + })); } catch (IllegalStateException e) { // Ignore -- the JVM is already shutting down. } @@ -86,7 +111,11 @@ public final class CleanupService { if (isOsgi && cleanup != null) { Runnable r = cleanup; cleanup = null; - r.run(); + try { + r.run(); + } catch (RuntimeException e) { + LOG.error(JGitText.get().shutdownCleanupFailed, e); + } } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/ShutdownHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/ShutdownHook.java index 5ba33dbbff..f6b4723489 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/ShutdownHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/ShutdownHook.java @@ -15,9 +15,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jgit.internal.JGitText; import org.slf4j.Logger; @@ -66,25 +66,27 @@ public enum ShutdownHook { private final Set<Listener> listeners = ConcurrentHashMap.newKeySet(); - private volatile boolean shutdownInProgress; + private final AtomicBoolean shutdownInProgress = new AtomicBoolean(); private ShutdownHook() { CleanupService.getInstance().register(this::cleanup); } private void cleanup() { - shutdownInProgress = true; - ExecutorService runner = Executors.newWorkStealingPool(); - try { - runner.submit(() -> { - this.doCleanup(); - return null; - }).get(30L, TimeUnit.SECONDS); - } catch (RejectedExecutionException | InterruptedException - | ExecutionException | TimeoutException e) { - LOG.error(JGitText.get().shutdownCleanupFailed, e); + if (!shutdownInProgress.getAndSet(true)) { + ExecutorService runner = Executors.newWorkStealingPool(); + try { + runner.submit(() -> { + this.doCleanup(); + return null; + }).get(30L, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException + | TimeoutException e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + runner.shutdownNow(); + } } - runner.shutdownNow(); } private void doCleanup() { @@ -112,7 +114,7 @@ public enum ShutdownHook { * @return {@code true} if this object has been registered */ public boolean register(Listener l) { - if (shutdownInProgress) { + if (shutdownInProgress.get()) { return listeners.contains(l); } LOG.debug("register {} with shutdown hook", l); //$NON-NLS-1$ @@ -131,7 +133,7 @@ public enum ShutdownHook { * @return {@code true} if this object is no longer registered */ public boolean unregister(Listener l) { - if (shutdownInProgress) { + if (shutdownInProgress.get()) { return !listeners.contains(l); } LOG.debug("unregister {} from shutdown hook", l); //$NON-NLS-1$ @@ -145,6 +147,6 @@ public enum ShutdownHook { * @return {@code true} if a JGit shutdown is in progress */ public boolean isShutdownInProgress() { - return shutdownInProgress; + return shutdownInProgress.get(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java index 43571a6868..99943b78e6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java @@ -139,11 +139,8 @@ public class TreeRevFilter extends RevFilter { .getPathsBestEffort(); if (paths.isPresent()) { changedPathFilterUsed = true; - for (byte[] path : paths.get()) { - if (!cpf.maybeContains(path)) { - mustCalculateChgs = false; - break; - } + if (paths.get().stream().noneMatch(cpf::maybeContains)) { + mustCalculateChgs = false; } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java index c94160144e..bcf79a285d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java @@ -15,6 +15,10 @@ package org.eclipse.jgit.treewalk.filter; import org.eclipse.jgit.util.RawParseUtils; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + /** * Specialized set for byte arrays, interpreted as strings for use in * {@link PathFilterGroup.Group}. Most methods assume the hash is already know @@ -291,4 +295,8 @@ class ByteArraySet { return ret; } + Set<byte[]> toSet() { + return Arrays.stream(toArray()).collect(Collectors.toSet()); + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java index 59855572f2..4c0604ad56 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java @@ -12,6 +12,8 @@ package org.eclipse.jgit.treewalk.filter; import java.util.Collection; +import java.util.Optional; +import java.util.Set; import org.eclipse.jgit.errors.StopWalkException; import org.eclipse.jgit.internal.JGitText; @@ -232,6 +234,15 @@ public class PathFilterGroup { } @Override + public Optional<Set<byte[]>> getPathsBestEffort() { + Set<byte[]> result = fullpaths.toSet(); + if (result.isEmpty()) { + return Optional.empty(); + } + return Optional.of(result); + } + + @Override public TreeFilter clone() { return this; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java index 22d430bc27..a9066dc8f8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java @@ -210,7 +210,7 @@ public abstract class TreeFilter { public abstract boolean shouldBeRecursive(); /** - * If this filter checks that a specific set of paths have all been + * If this filter checks that at least one of the paths in a set has been * modified, returns that set of paths to be checked against a changed path * filter. Otherwise, returns empty. * |