summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorHector Caballero <hector.caballero@ericsson.com>2018-02-05 08:58:46 -0500
committerHector Oswaldo Caballero <hector.caballero@ericsson.com>2018-02-07 18:37:12 -0500
commit93654f75a25d9af193731a97d2db675f57cc86bb (patch)
treee2d60400b80320552a4391f5507d2251f6db549d /org.eclipse.jgit
parentab1b97234ded461407b163b178a592cdabe66930 (diff)
downloadjgit-93654f75a25d9af193731a97d2db675f57cc86bb.tar.gz
jgit-93654f75a25d9af193731a97d2db675f57cc86bb.zip
GC: Remove empty references folders
After packaging references, the folders containing these references are not deleted. In a busy repository, this causes operations to slow down as traversing the references tree becomes longer. Delete empty reference folders after the loose references have been packed. To avoid deleting a folder that was just created by another concurrent operation, only delete folders that were not modified in the last 30 seconds. Signed-off-by: Hector Oswaldo Caballero <hector.caballero@ericsson.com> Change-Id: Ie79447d6121271cf5e25171be377ea396c7028e0
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java45
1 files changed, 45 insertions, 0 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 5669d49ae0..56a742d505 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -65,6 +65,7 @@ import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -902,6 +903,7 @@ public class GC {
throw new IOException(e);
}
prunePacked();
+ deleteEmptyRefsFolders();
deleteOrphans();
deleteTempPacksIdx();
@@ -918,6 +920,49 @@ public class GC {
return ref.getName().startsWith(Constants.R_TAGS);
}
+ private void deleteEmptyRefsFolders() throws IOException {
+ Path refs = repo.getDirectory().toPath().resolve("refs"); //$NON-NLS-1$
+ try (Stream<Path> entries = Files.list(refs)) {
+ Iterator<Path> iterator = entries.iterator();
+ while (iterator.hasNext()) {
+ try (Stream<Path> s = Files.list(iterator.next())) {
+ s.forEach(this::deleteDir);
+ }
+ }
+ }
+ }
+
+ private void deleteDir(Path dir) {
+ try (Stream<Path> dirs = Files.walk(dir)) {
+ dirs.filter(this::isDirectory).sorted(Comparator.reverseOrder())
+ .forEach(this::delete);
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+
+ private boolean isDirectory(Path p) {
+ return p.toFile().isDirectory();
+ }
+
+ private boolean delete(Path d) {
+ try {
+ // Avoid deleting a folder that was just created so that concurrent
+ // operations trying to create a reference are not impacted
+ Instant threshold = Instant.now().minus(30, ChronoUnit.SECONDS);
+ Instant lastModified = Files.getLastModifiedTime(d).toInstant();
+ if (lastModified.isBefore(threshold)) {
+ // If the folder is not empty, the delete operation will fail
+ // silently. This is a cheaper alternative to filtering the
+ // stream in the calling method.
+ return d.toFile().delete();
+ }
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return false;
+ }
+
/**
* Deletes orphans
* <p>