diff options
author | Hector Caballero <hector.caballero@ericsson.com> | 2018-02-05 08:58:46 -0500 |
---|---|---|
committer | Hector Oswaldo Caballero <hector.caballero@ericsson.com> | 2018-02-07 18:37:12 -0500 |
commit | 93654f75a25d9af193731a97d2db675f57cc86bb (patch) | |
tree | e2d60400b80320552a4391f5507d2251f6db549d /org.eclipse.jgit | |
parent | ab1b97234ded461407b163b178a592cdabe66930 (diff) | |
download | jgit-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.java | 45 |
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> |