Don't block in GC#gc until garbage collection finished

Let GC#gc return collection of newly created packs as CompletableFuture
to enable using gc() asynchronously.

Change-Id: I3627014fd458c738cfe54225e631d6f7d9cfb1a7
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityTest.java
index f2f7405..d574428 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedObjectReachabilityTest.java
@@ -23,7 +23,7 @@ ObjectReachabilityChecker getChecker(
 		// GC generates the bitmaps
 		GC gc = new GC(repository.getRepository());
 		gc.setAuto(false);
-		gc.gc();
+		gc.gc().get();
 
 		return new BitmappedObjectReachabilityChecker(
 				repository.getRevWalk().toObjectWalkWithSameObjects());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityCheckerTest.java
index 5833c7a..253246e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityCheckerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/revwalk/BitmappedReachabilityCheckerTest.java
@@ -25,7 +25,7 @@ protected ReachabilityChecker getChecker(
 		// GC generates the bitmaps
 		GC gc = new GC(repo.getRepository());
 		gc.setAuto(false);
-		gc.gc();
+		gc.gc().get();
 
 		// This is null when the test didn't create any branch
 		assertNotNull("Probably the test didn't define any ref",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index 8dc1ddb..6cad8b6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -54,7 +54,7 @@ public void testPackRepoWithNoRefs(boolean aggressive) throws Exception {
 		assertEquals(4, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
 		configureGc(gc, aggressive);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(4, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
@@ -72,7 +72,7 @@ public void testPack2Commits(boolean aggressive) throws Exception {
 		assertEquals(8, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
 		configureGc(gc, aggressive);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(8, stats.numberOfPackedObjects);
@@ -93,7 +93,7 @@ public void testPack2Commits_noPackFolder(boolean aggressive) throws Exception {
 		assertEquals(8, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
 		configureGc(gc, aggressive);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(8, stats.numberOfPackedObjects);
@@ -112,7 +112,7 @@ public void testPackAllObjectsInOnePack(boolean aggressive)
 		assertEquals(4, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
 		configureGc(gc, aggressive);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(4, stats.numberOfPackedObjects);
@@ -120,7 +120,7 @@ public void testPackAllObjectsInOnePack(boolean aggressive)
 		assertEquals(1, stats.numberOfBitmaps);
 
 		// Do the gc again and check that it hasn't changed anything
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(4, stats.numberOfPackedObjects);
@@ -140,7 +140,7 @@ public void testPackCommitsAndLooseOne(boolean aggressive)
 		assertEquals(8, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
 		configureGc(gc, aggressive);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(8, stats.numberOfPackedObjects);
@@ -168,7 +168,7 @@ public void testNotPackTwice(boolean aggressive) throws Exception {
 		gc.setExpireAgeMillis(0);
 		fsTick();
 		configureGc(gc, aggressive);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 
@@ -187,7 +187,7 @@ public void testDonePruneTooYoungPacks() throws Exception {
 		bb2.commit().message("M").add("M", "M").create();
 
 		gc.setExpireAgeMillis(0);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(4, stats.numberOfPackedObjects);
@@ -207,7 +207,7 @@ public void testDonePruneTooYoungPacks() throws Exception {
 		// The old packfile is too young to be deleted. We should end up with
 		// two pack files
 		gc.setExpire(new Date(oldPackfile.lastModified() - 1));
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		// if objects exist in multiple packFiles then they are counted multiple
@@ -218,7 +218,7 @@ public void testDonePruneTooYoungPacks() throws Exception {
 		// repack again but now without a grace period for loose objects. Since
 		// we don't have loose objects anymore this shouldn't change anything
 		gc.setExpireAgeMillis(0);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		// if objects exist in multiple packFiles then they are counted multiple
@@ -233,7 +233,7 @@ public void testDonePruneTooYoungPacks() throws Exception {
 		// we want to keep newly-loosened objects though
 		gc.setExpireAgeMillis(-1);
 
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(1, stats.numberOfLooseObjects);
 		// if objects exist in multiple packFiles then they are counted multiple
@@ -252,7 +252,7 @@ public void testImmediatePruning() throws Exception {
 		bb2.commit().message("M").add("M", "M").create();
 
 		gc.setExpireAgeMillis(0);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 
 		fsTick();
@@ -273,7 +273,7 @@ public void testImmediatePruning() throws Exception {
 		//And we don't want to keep packs full of dead objects
 		gc.setPackExpireAgeMillis(0);
 
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(6, stats.numberOfPackedObjects);
@@ -284,7 +284,7 @@ public void testImmediatePruning() throws Exception {
 	public void testPreserveAndPruneOldPacks() throws Exception {
 		testPreserveOldPacks();
 		configureGc(gc, false).setPrunePreserved(true);
-		gc.gc();
+		gc.gc().get();
 
 		assertFalse(repo.getObjectDatabase().getPreservedDirectory().exists());
 	}
@@ -295,7 +295,7 @@ private void testPreserveOldPacks() throws Exception {
 
 		// pack loose object into packfile
 		gc.setExpireAgeMillis(0);
-		gc.gc();
+		gc.gc().get();
 		PackFile oldPackfile = tr.getRepository().getObjectDatabase().getPacks()
 				.iterator().next().getPackFile();
 		assertTrue(oldPackfile.exists());
@@ -308,7 +308,7 @@ private void testPreserveOldPacks() throws Exception {
 		// preserved directory
 		gc.setPackExpireAgeMillis(0);
 		configureGc(gc, false).setPreserveOldPacks(true);
-		gc.gc();
+		gc.gc().get();
 
 		File preservedPackFile = oldPackfile.createPreservedForDirectory(
 				repo.getObjectDatabase().getPreservedDirectory());
@@ -330,7 +330,7 @@ public void testPruneAndRestoreOldPacks() throws Exception {
 		configureGc(gc, false);
 		gc.setExpireAgeMillis(0);
 		gc.setPackExpireAgeMillis(0);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(4, stats.numberOfPackedObjects);
@@ -347,7 +347,7 @@ public void testPruneAndRestoreOldPacks() throws Exception {
 
 		// Repack with only orphaned commit, so packfile will be pruned
 		configureGc(gc, false).setPreserveOldPacks(true);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
index 5cac1e3..2c5f1a8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
@@ -110,7 +110,7 @@ public void repackAndGetStats() throws Exception {
 		test.commit().add("a", "a").create();
 		GC gc1 = new GC(tr.getRepository());
 		gc1.setPackExpireAgeMillis(0);
-		gc1.gc();
+		gc1.gc().get();
 		test.commit().add("b", "b").create();
 
 		// Create a new Repository instance and trigger a gc
@@ -120,7 +120,7 @@ public void repackAndGetStats() throws Exception {
 				tr.getRepository().getDirectory());
 		GC gc2 = new GC(r2);
 		gc2.setPackExpireAgeMillis(0);
-		gc2.gc();
+		gc2.gc().get();
 
 		new GC(tr.getRepository()).getStatistics();
 	}
@@ -133,7 +133,7 @@ public void repackAndUploadPack() throws Exception {
 
 		GC gc1 = new GC(tr.getRepository());
 		gc1.setPackExpireAgeMillis(0);
-		gc1.gc();
+		gc1.gc().get();
 
 		RevCommit b = test.commit().add("b", "b").create();
 
@@ -141,7 +141,7 @@ public void repackAndUploadPack() throws Exception {
 				tr.getRepository().getDirectory());
 		GC gc2 = new GC(r2);
 		gc2.setPackExpireAgeMillis(0);
-		gc2.gc();
+		gc2.gc().get();
 
 		// Simulate parts of an UploadPack. This is the situation on
 		// server side (e.g. gerrit) when clients are
@@ -172,7 +172,7 @@ public void repackAndCheckBitmapUsage() throws Exception {
 		FileRepository repository = tr.getRepository();
 		GC gc1 = new GC(repository);
 		gc1.setPackExpireAgeMillis(0);
-		gc1.gc();
+		gc1.gc().get();
 		String oldPackName = getSinglePack(repository).getPackName();
 		RevCommit b = test.commit().add("b", "b").create();
 
@@ -180,7 +180,7 @@ public void repackAndCheckBitmapUsage() throws Exception {
 		FileRepository repository2 = new FileRepository(repository.getDirectory());
 		GC gc2 = new GC(repository2);
 		gc2.setPackExpireAgeMillis(0);
-		gc2.gc();
+		gc2.gc().get();
 		String newPackName = getSinglePack(repository2).getPackName();
 		// make sure gc() has caused creation of a new packfile
 		assertNotEquals(oldPackName, newPackName);
@@ -210,7 +210,7 @@ public void testInterruptGc() throws Exception {
 			long start = System.currentTimeMillis();
 			System.out.println("starting gc");
 			latch.countDown();
-			Collection<Pack> r = gc.gc();
+			Collection<Pack> r = gc.gc().get();
 			System.out.println(
 					"gc took " + (System.currentTimeMillis() - start) + " ms");
 			return r;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java
index 564f8ab..39aafc8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java
@@ -48,7 +48,7 @@ public void emptyRefFoldersAreDeleted() throws Exception {
 		setLastModifiedTime(fileTime, heads, REF_FOLDER_02);
 		assertTrue(refDir01.toFile().exists());
 		assertTrue(refDir02.toFile().exists());
-		gc.gc();
+		gc.gc().get();
 
 		assertFalse(refDir01.toFile().exists());
 		assertFalse(refDir01.getParent().toFile().exists());
@@ -68,7 +68,7 @@ public void emptyRefFoldersSkipFiles() throws Exception {
 		setLastModifiedTime(fileTime, heads, REF_FOLDER_02);
 		assertTrue(refDir01.toFile().exists());
 		assertTrue(refDir02.toFile().exists());
-		gc.gc();
+		gc.gc().get();
 		assertTrue(Files.exists(refFile));
 	}
 
@@ -88,7 +88,7 @@ public void emptyRefFoldersAreKeptIfTheyAreTooRecent()
 		Path refDir02 = Files.createDirectories(heads.resolve(REF_FOLDER_02));
 		assertTrue(refDir01.toFile().exists());
 		assertTrue(refDir02.toFile().exists());
-		gc.gc();
+		gc.gc().get();
 
 		assertTrue(refDir01.toFile().exists());
 		assertTrue(refDir02.toFile().exists());
@@ -104,7 +104,7 @@ public void nonEmptyRefsFoldersAreKept() throws Exception {
 		assertTrue(refDir02.toFile().exists());
 		assertTrue(ref01.toFile().exists());
 		assertTrue(ref02.toFile().exists());
-		gc.gc();
+		gc.gc().get();
 		assertTrue(refDir01.toFile().exists());
 		assertTrue(refDir02.toFile().exists());
 		assertTrue(ref01.toFile().exists());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java
index a4dff26..a51b0c2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java
@@ -25,7 +25,7 @@ public void testDirCacheSavesObjects() throws Exception {
 		stats = gc.getStatistics();
 		assertEquals(9, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(1, stats.numberOfLooseObjects);
 		assertEquals(8, stats.numberOfPackedObjects);
@@ -43,7 +43,7 @@ public void testDirCacheSavesObjectsWithPruneNow() throws Exception {
 		assertEquals(0, stats.numberOfPackedObjects);
 		gc.setExpireAgeMillis(0);
 		fsTick();
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(8, stats.numberOfPackedObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
index 5fcdd37..840c098 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
@@ -30,7 +30,7 @@ public void testKeepFiles() throws Exception {
 		assertEquals(4, stats.numberOfLooseObjects);
 		assertEquals(0, stats.numberOfPackedObjects);
 		assertEquals(0, stats.numberOfPackFiles);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(4, stats.numberOfPackedObjects);
@@ -48,7 +48,7 @@ public void testKeepFiles() throws Exception {
 		assertEquals(4, stats.numberOfLooseObjects);
 		assertEquals(4, stats.numberOfPackedObjects);
 		assertEquals(1, stats.numberOfPackFiles);
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(8, stats.numberOfPackedObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
index c5c316d..620aedf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
@@ -50,7 +50,7 @@ public void bitmapAndIdxDeletedButPackNot() throws Exception {
 		createFileInPackFolder(BITMAP_File_1);
 		createFileInPackFolder(IDX_File_2);
 		createFileInPackFolder(PACK_File_3);
-		gc.gc();
+		gc.gc().get();
 		assertFalse(new File(packDir, BITMAP_File_1).exists());
 		assertFalse(new File(packDir, IDX_File_2).exists());
 		assertTrue(new File(packDir, PACK_File_3).exists());
@@ -62,7 +62,7 @@ public void bitmapDeletedButIdxAndPackNot() throws Exception {
 		createFileInPackFolder(IDX_File_2);
 		createFileInPackFolder(PACK_File_2);
 		createFileInPackFolder(PACK_File_3);
-		gc.gc();
+		gc.gc().get();
 		assertFalse(new File(packDir, BITMAP_File_1).exists());
 		assertTrue(new File(packDir, IDX_File_2).exists());
 		assertTrue(new File(packDir, PACK_File_2).exists());
@@ -72,7 +72,7 @@ public void bitmapDeletedButIdxAndPackNot() throws Exception {
 	@Test
 	public void malformedIdxNotDeleted() throws Exception {
 		createFileInPackFolder(IDX_File_malformed);
-		gc.gc();
+		gc.gc().get();
 		assertTrue(new File(packDir, IDX_File_malformed).exists());
 	}
 
@@ -84,7 +84,7 @@ public void keepPreventsDeletionOfIndexFilesForMissingPackFile()
 		createFileInPackFolder(BITMAP_File_2);
 		createFileInPackFolder(KEEP_File_2);
 		createFileInPackFolder(PACK_File_3);
-		gc.gc();
+		gc.gc().get();
 		assertFalse(new File(packDir, BITMAP_File_1).exists());
 		assertTrue(new File(packDir, BITMAP_File_2).exists());
 		assertTrue(new File(packDir, IDX_File_2).exists());
@@ -102,6 +102,6 @@ private void createFileInPackFolder(String fileName) throws IOException {
 	@Test
 	public void noSuchPackFolder() throws Exception {
 		assertTrue(packDir.delete());
-		gc.gc();
+		gc.gc().get();
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
index 7386621..ca0f684 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
@@ -80,7 +80,7 @@ public void testPackCommitsAndLooseOneWithPruneNow() throws Exception {
 		assertEquals(0, stats.numberOfPackedObjects);
 		gc.setExpireAgeMillis(0);
 		fsTick();
-		gc.gc();
+		gc.gc().get();
 		stats = gc.getStatistics();
 		assertNoEmptyFanoutDirectories();
 		assertEquals(0, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
index 0901d86..e6c1ee5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
@@ -59,7 +59,7 @@ public void testPackRepoWithCorruptReflog() throws Exception {
 				.create();
 		// make sure HEAD exists
 		Git.wrap(repo).checkout().setName("refs/heads/master").call();
-		gc.gc();
+		gc.gc().get();
 	}
 
 	@Test
@@ -78,7 +78,7 @@ public void testPackCommitsAndLooseOneNoReflog() throws Exception {
 		FileUtils.delete(
 				new File(repo.getDirectory(), "logs/refs/heads/master"),
 				FileUtils.RETRY | FileUtils.SKIP_MISSING);
-		gc.gc();
+		gc.gc().get();
 
 		stats = gc.getStatistics();
 		assertEquals(4, stats.numberOfLooseObjects);
@@ -104,7 +104,7 @@ public void testPackCommitsAndLooseOneWithPruneNowNoReflog()
 				new File(repo.getDirectory(), "logs/refs/heads/master"),
 				FileUtils.RETRY | FileUtils.SKIP_MISSING);
 		gc.setExpireAgeMillis(0);
-		gc.gc();
+		gc.gc().get();
 
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTemporaryFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTemporaryFilesTest.java
index 16bde19..1a7dd5e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTemporaryFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTemporaryFilesTest.java
@@ -50,7 +50,7 @@ public void oldTempPacksAndIdxAreDeleted() throws Exception {
 				- 24 * 60 * 62 * 1000;
 		tempIndex.setLastModified(_24HoursBefore);
 		tempPack.setLastModified(_24HoursBefore);
-		gc.gc();
+		gc.gc().get();
 		assertFalse(tempIndex.exists());
 		assertFalse(tempPack.exists());
 	}
@@ -66,7 +66,7 @@ public void recentTempPacksAndIdxAreNotDeleted() throws Exception {
 		assertTrue(tempIndex.createNewFile());
 		assertTrue(tempIndex.exists());
 		assertTrue(tempPack.exists());
-		gc.gc();
+		gc.gc().get();
 		assertTrue(tempIndex.exists());
 		assertTrue(tempPack.exists());
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
index 316e336..1a3b378 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
@@ -126,7 +126,7 @@ public void testScanningForPackfiles() throws Exception {
 			// setup a repo which has at least one pack file and trigger
 			// scanning of the packs directory
 			ObjectId id = commitFile("file.txt", "test", "master").getId();
-			gc.gc();
+			gc.gc().get();
 			assertFalse(receivingDB.getObjectDatabase().has(unknownID));
 			assertTrue(receivingDB.getObjectDatabase().hasPackedObject(id));
 
@@ -155,7 +155,7 @@ public void testScanningForPackfiles() throws Exception {
 
 			// trigger a gc. This will create packfiles which have likely the
 			// same mtime than the packfolder
-			gc.gc();
+			gc.gc().get();
 
 			// To deal with racy-git situations JGit's Filesnapshot class will
 			// report a file/folder potentially dirty if
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
index 7c32ce7..3de015b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
@@ -25,8 +25,6 @@
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
-//import java.nio.file.attribute.BasicFileAttributes;
-import java.text.ParseException;
 import java.time.Instant;
 import java.util.Collection;
 import java.util.Iterator;
@@ -281,8 +279,7 @@ private Path copyPack(Path base, String srcSuffix, String dstSuffix)
 	}
 
 	private Pack repackAndCheck(int compressionLevel, String oldName,
-			Long oldLength, AnyObjectId oldChkSum)
-			throws IOException, ParseException {
+			Long oldLength, AnyObjectId oldChkSum) throws Exception {
 		Pack p = getSinglePack(gc(compressionLevel));
 		File pf = p.getPackFile();
 		// The following two assumptions should not cause the test to fail. If
@@ -305,8 +302,7 @@ private Pack getSinglePack(Collection<Pack> packs) {
 		return p;
 	}
 
-	private Collection<Pack> gc(int compressionLevel)
-			throws IOException, ParseException {
+	private Collection<Pack> gc(int compressionLevel) throws Exception {
 		GC gc = new GC(db);
 		PackConfig pc = new PackConfig(db.getConfig());
 		pc.setCompressionLevel(compressionLevel);
@@ -322,7 +318,7 @@ private Collection<Pack> gc(int compressionLevel)
 		gc.setPackConfig(pc);
 		gc.setExpireAgeMillis(0);
 		gc.setPackExpireAgeMillis(0);
-		return gc.gc();
+		return gc.gc().get();
 	}
 
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index 71aca9d..1ff2264 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -28,7 +28,6 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.text.ParseException;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -232,15 +231,13 @@ public void testIgnoreNonExistingObjects() throws IOException {
 	 * Use a repo with bitmap indexes because then PackWriter will use
 	 * PackWriterBitmapWalker which had problems with this situation.
 	 *
-	 * @throws IOException
-	 * @throws ParseException
+	 * @throws Exception
 	 */
 	@Test
-	public void testIgnoreNonExistingObjectsWithBitmaps() throws IOException,
-			ParseException {
+	public void testIgnoreNonExistingObjectsWithBitmaps() throws Exception {
 		final ObjectId nonExisting = ObjectId
 				.fromString("0000000000000000000000000000000000000001");
-		new GC(db).gc();
+		new GC(db).gc().get();
 		createVerifyOpenPack(NONE, haves(nonExisting), false, true, true);
 		// shouldn't throw anything
 	}
@@ -732,11 +729,11 @@ private FileRepository setUpRepoWithMultiplePackfiles() throws Exception {
 			gc.setPackExpireAgeMillis(Long.MAX_VALUE);
 			gc.setExpireAgeMillis(Long.MAX_VALUE);
 			// Creates packfile P1 (containing C1, T1)
-			gc.gc();
+			gc.gc().get();
 			// Creates 1 object (C2 commit)
 			git.commit().setMessage("Second commit").call();
 			// Creates packfile P2 (containing C1, T1, C2)
-			gc.gc();
+			gc.gc().get();
 			// Create 1 object (C3 commit)
 			git.commit().setMessage("Third commit").call();
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
index cc826c3..bb56c84 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
@@ -74,7 +74,7 @@ private void testBitmapSpansNoMerges(boolean withTags) throws Exception {
 
 			gc.setPackExpireAgeMillis(0); // immediately delete old packs
 			gc.setExpireAgeMillis(0);
-			gc.gc();
+			gc.gc().get();
 			assertEquals(currentCommits * 3, // commit/tree/object
 					gc.getStatistics().numberOfPackedObjects);
 			assertEquals(currentCommits + " commits: ", expectedBitmapCount,
@@ -138,7 +138,7 @@ public void testBitmapSpansWithMerges() throws Exception {
 
 			gc.setPackExpireAgeMillis(0); // immediately delete old packs
 			gc.setExpireAgeMillis(0);
-			gc.gc();
+			gc.gc().get();
 			assertEquals(currentCommits + " commits: ", expectedBitmapCount,
 					gc.getStatistics().numberOfBitmaps);
 		}
@@ -179,7 +179,7 @@ public void testBitmapsForExcessiveBranches() throws Exception {
 		// Excessive branch history pruning, one old branch.
 		gc.setPackExpireAgeMillis(0); // immediately delete old packs
 		gc.setExpireAgeMillis(0);
-		gc.gc();
+		gc.gc().get();
 		assertEquals(
 				commitsForSparseBranch + commitsForFullBranch
 						+ commitsForShallowBranches,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
index b5fff7d..584d2bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
@@ -14,6 +14,7 @@
 import java.text.ParseException;
 import java.util.Date;
 import java.util.Properties;
+import java.util.concurrent.ExecutionException;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -176,9 +177,10 @@ public Properties call() throws GitAPIException {
 					gc.setExpire(expire);
 
 				try {
-					gc.gc();
+					gc.gc().get();
 					return toProperties(gc.getStatistics());
-				} catch (ParseException e) {
+				} catch (ParseException | InterruptedException
+						| ExecutionException e) {
 					throw new JGitInternalException(JGitText.get().gcFailed, e);
 				}
 			} else if (repo instanceof DfsRepository) {
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 93c6201..ca4f7a2 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
@@ -12,8 +12,8 @@
 
 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
-import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -46,8 +46,9 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Supplier;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -214,19 +215,18 @@ public GC(FileRepository repo) {
 	 *             If the configuration parameter "gc.pruneexpire" couldn't be
 	 *             parsed
 	 */
-	// TODO(ms): change signature and return Future<Collection<Pack>>
-	@SuppressWarnings("FutureReturnValueIgnored")
-	public Collection<Pack> gc() throws IOException, ParseException {
+	public CompletableFuture<Collection<Pack>> gc()
+			throws IOException, ParseException {
 		if (!background) {
-			return doGc();
+			return CompletableFuture.completedFuture(doGc());
 		}
 		final GcLog gcLog = new GcLog(repo);
 		if (!gcLog.lock()) {
 			// there is already a background gc running
-			return Collections.emptyList();
+			return CompletableFuture.completedFuture(Collections.emptyList());
 		}
 
-		Callable<Collection<Pack>> gcTask = () -> {
+		Supplier<Collection<Pack>> gcTask = () -> {
 			try {
 				Collection<Pack> newPacks = doGc();
 				if (automatic && tooManyLooseObjects()) {
@@ -251,9 +251,7 @@ public Collection<Pack> gc() throws IOException, ParseException {
 			}
 			return Collections.emptyList();
 		};
-		// TODO(ms): change signature and return the Future
-		executor().submit(gcTask);
-		return Collections.emptyList();
+		return CompletableFuture.supplyAsync(gcTask, executor());
 	}
 
 	private ExecutorService executor() {