Merge branch 'stable-5.9' into stable-5.10

* stable-5.9:
  Remove unused imports
  Silence API warnings
  Remove erraneously merged source features
  Prepare 5.3.9-SNAPSHOT builds
  JGit v5.3.8.202011260953-r
  Prepare 5.1.15-SNAPSHOT builds
  JGit v5.1.14.202011251942-r
  GC#deleteOrphans: log warning for deleted orphaned files
  GC#deleteOrphans: handle failure to list files in pack directory
  Ensure that GC#deleteOrphans respects pack lock
  PacketLineIn: ensure that END != DELIM
  Update API warning filters
  Remove unused imports

Change-Id: Icf415ac5bab2f69f71189c942424ee69f8a64d4e
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
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 84d364b..c5c316d 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
@@ -24,10 +24,14 @@
 
 	private static final String BITMAP_File_1 = PACK + "-1.bitmap";
 
+	private static final String BITMAP_File_2 = PACK + "-2.bitmap";
+
 	private static final String IDX_File_2 = PACK + "-2.idx";
 
 	private static final String IDX_File_malformed = PACK + "-1234idx";
 
+	private static final String KEEP_File_2 = PACK + "-2.keep";
+
 	private static final String PACK_File_2 = PACK + "-2.pack";
 
 	private static final String PACK_File_3 = PACK + "-3.pack";
@@ -72,6 +76,22 @@
 		assertTrue(new File(packDir, IDX_File_malformed).exists());
 	}
 
+	@Test
+	public void keepPreventsDeletionOfIndexFilesForMissingPackFile()
+			throws Exception {
+		createFileInPackFolder(BITMAP_File_1);
+		createFileInPackFolder(IDX_File_2);
+		createFileInPackFolder(BITMAP_File_2);
+		createFileInPackFolder(KEEP_File_2);
+		createFileInPackFolder(PACK_File_3);
+		gc.gc();
+		assertFalse(new File(packDir, BITMAP_File_1).exists());
+		assertTrue(new File(packDir, BITMAP_File_2).exists());
+		assertTrue(new File(packDir, IDX_File_2).exists());
+		assertTrue(new File(packDir, KEEP_File_2).exists());
+		assertTrue(new File(packDir, PACK_File_3).exists());
+	}
+
 	private void createFileInPackFolder(String fileName) throws IOException {
 		if (!packDir.exists() || !packDir.isDirectory()) {
 			assertTrue(packDir.mkdirs());
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index c9e48a5..12902b9 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -213,6 +213,7 @@
 deepenSinceWithDeepen=Cannot combine deepen with deepen-since
 deleteBranchUnexpectedResult=Delete branch returned unexpected result {0}
 deleteFileFailed=Could not delete file {0}
+deletedOrphanInPackDir=Deleted orphaned file {}
 deleteRequiresZeroNewId=Delete requires new ID to be zero
 deleteTagUnexpectedResult=Delete tag returned unexpected result {0}
 deletingNotSupported=Deleting {0} not supported.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index b761210..892657d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -241,6 +241,7 @@
 	/***/ public String deepenSinceWithDeepen;
 	/***/ public String deleteBranchUnexpectedResult;
 	/***/ public String deleteFileFailed;
+	/***/ public String deletedOrphanInPackDir;
 	/***/ public String deleteRequiresZeroNewId;
 	/***/ public String deleteTagUnexpectedResult;
 	/***/ public String deletingNotSupported;
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 741d86e..1f2fe10 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
@@ -115,6 +115,8 @@
 
 	private static final String INDEX_EXT = "." + PackExt.INDEX.getExtension(); //$NON-NLS-1$
 
+	private static final String KEEP_EXT = "." + PackExt.KEEP.getExtension(); //$NON-NLS-1$
+
 	private static final int DEFAULT_AUTOPACKLIMIT = 50;
 
 	private static final int DEFAULT_AUTOLIMIT = 6700;
@@ -961,11 +963,15 @@
 			fileNames = files.map(path -> path.getFileName().toString())
 					.filter(name -> (name.endsWith(PACK_EXT)
 							|| name.endsWith(BITMAP_EXT)
-							|| name.endsWith(INDEX_EXT)))
+							|| name.endsWith(INDEX_EXT)
+							|| name.endsWith(KEEP_EXT)))
+					// sort files with same base name in the order:
+					// .pack, .keep, .index, .bitmap to avoid look ahead
 					.sorted(Collections.reverseOrder())
 					.collect(Collectors.toList());
-		} catch (IOException e1) {
-			// ignore
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+			return;
 		}
 		if (fileNames == null) {
 			return;
@@ -973,13 +979,15 @@
 
 		String base = null;
 		for (String n : fileNames) {
-			if (n.endsWith(PACK_EXT)) {
+			if (n.endsWith(PACK_EXT) || n.endsWith(KEEP_EXT)) {
 				base = n.substring(0, n.lastIndexOf('.'));
 			} else {
 				if (base == null || !n.startsWith(base)) {
 					try {
-						FileUtils.delete(packDir.resolve(n).toFile(),
+						Path delete = packDir.resolve(n);
+						FileUtils.delete(delete.toFile(),
 								FileUtils.RETRY | FileUtils.SKIP_MISSING);
+						LOG.warn(JGitText.get().deletedOrphanInPackDir, delete);
 					} catch (IOException e) {
 						LOG.error(e.getMessage(), e);
 					}