Merge branch 'stable-5.11' into stable-5.12

* stable-5.11:
  Add missing since tag for SshBasicTestBase
  Add missing since tag for SshTestHarness#publicKey2
  Silence API errors
  Prevent infinite loop rescanning the pack list on PackMismatchException
  Remove blank in maven.config

Change-Id: I25bb99687b969f9915a7cbda8d1332bec778096a
diff --git a/.mvn/maven.config b/.mvn/maven.config
index ebbe288..3944d88 100644
--- a/.mvn/maven.config
+++ b/.mvn/maven.config
@@ -1 +1 @@
--T 1C
+-T1C
diff --git a/org.eclipse.jgit.junit.ssh/.settings/.api_filters b/org.eclipse.jgit.junit.ssh/.settings/.api_filters
new file mode 100644
index 0000000..44c9dfa
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/.settings/.api_filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jgit.junit.ssh" version="2">
+    <resource path="src/org/eclipse/jgit/junit/ssh/SshTestHarness.java" type="org.eclipse.jgit.junit.ssh.SshTestHarness">
+        <filter id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.junit.ssh.SshTestHarness"/>
+                <message_argument value="publicKey2"/>
+    </resource>
+    <resource path="META-INF/MANIFEST.MF">
+        <filter id="923795461">
+            <message_arguments>
+                <message_argument value="5.11.2"/>
+                <message_argument value="5.10.0"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/junit/ssh/SshTestBase.java" type="org.eclipse.jgit.junit.ssh.SshTestBase">
+        <filter id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.junit.ssh.SshTestBase"/>
+                <message_argument value="testSshWithConfig()"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java
index f9ca0b8..da84b0e 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshBasicTestBase.java
@@ -22,6 +22,8 @@
  * Some minimal cloning and fetching tests. Concrete subclasses can implement
  * the abstract operations from {@link SshTestHarness} to run with different SSH
  * implementations.
+ *
+ * @since 5.11
  */
 public abstract class SshBasicTestBase extends SshTestHarness {
 
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
index 90d981b..a28d5eb 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
@@ -76,6 +76,9 @@
 
 	protected File publicKey1;
 
+	/**
+	 * @since 5.10
+	 */
 	protected File publicKey2;
 
 	protected SshTestGitServer server;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java
index 44b8e01..7a2c70d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackMismatchException.java
@@ -18,6 +18,8 @@
 public class PackMismatchException extends IOException {
 	private static final long serialVersionUID = 1L;
 
+	private boolean permanent;
+
 	/**
 	 * Construct a pack modification error.
 	 *
@@ -27,4 +29,31 @@
 	public PackMismatchException(String why) {
 		super(why);
 	}
+
+	/**
+	 * Set the type of the exception
+	 *
+	 * @param permanent
+	 *            whether the exception is considered permanent
+	 * @since 5.9.1
+	 */
+	public void setPermanent(boolean permanent) {
+		this.permanent = permanent;
+	}
+
+	/**
+	 * Check if this is a permanent problem
+	 *
+	 * @return if this is a permanent problem and repeatedly scanning the
+	 *         packlist couldn't fix it
+	 * @since 5.9.1
+	 */
+	public boolean isPermanent() {
+		return permanent;
+	}
+
+	@Override
+	public String toString() {
+		return super.toString() + ", permanent: " + permanent; //$NON-NLS-1$
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 627facc..a3ce315 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -30,6 +30,7 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.eclipse.jgit.errors.PackMismatchException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -350,7 +351,8 @@
 	}
 
 	private ObjectLoader openPackedFromSelfOrAlternate(WindowCursor curs,
-			AnyObjectId objectId, Set<AlternateHandle.Id> skips) {
+			AnyObjectId objectId, Set<AlternateHandle.Id> skips)
+			throws PackMismatchException {
 		ObjectLoader ldr = openPackedObject(curs, objectId);
 		if (ldr != null) {
 			return ldr;
@@ -386,7 +388,8 @@
 		return null;
 	}
 
-	ObjectLoader openPackedObject(WindowCursor curs, AnyObjectId objectId) {
+	ObjectLoader openPackedObject(WindowCursor curs, AnyObjectId objectId)
+			throws PackMismatchException {
 		return packed.open(curs, objectId);
 	}
 
@@ -421,7 +424,8 @@
 	}
 
 	private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
-			AnyObjectId id, Set<AlternateHandle.Id> skips) {
+			AnyObjectId id, Set<AlternateHandle.Id> skips)
+			throws PackMismatchException {
 		long len = packed.getSize(curs, id);
 		if (0 <= len) {
 			return len;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
index 73745d8..73f6b4f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
@@ -59,6 +59,8 @@
 	private final static Logger LOG = LoggerFactory
 			.getLogger(PackDirectory.class);
 
+	private static final int MAX_PACKLIST_RESCAN_ATTEMPTS = 5;
+
 	private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY,
 			new Pack[0]);
 
@@ -201,9 +203,11 @@
 		return true;
 	}
 
-	ObjectLoader open(WindowCursor curs, AnyObjectId objectId) {
+	ObjectLoader open(WindowCursor curs, AnyObjectId objectId)
+			throws PackMismatchException {
 		PackList pList;
 		do {
+			int retries = 0;
 			SEARCH: for (;;) {
 				pList = packList.get();
 				for (Pack p : pList.packs) {
@@ -215,6 +219,7 @@
 					} catch (PackMismatchException e) {
 						// Pack was modified; refresh the entire pack list.
 						if (searchPacksAgain(pList)) {
+							retries = checkRescanPackThreshold(retries, e);
 							continue SEARCH;
 						}
 					} catch (IOException e) {
@@ -227,9 +232,11 @@
 		return null;
 	}
 
-	long getSize(WindowCursor curs, AnyObjectId id) {
+	long getSize(WindowCursor curs, AnyObjectId id)
+			throws PackMismatchException {
 		PackList pList;
 		do {
+			int retries = 0;
 			SEARCH: for (;;) {
 				pList = packList.get();
 				for (Pack p : pList.packs) {
@@ -242,6 +249,7 @@
 					} catch (PackMismatchException e) {
 						// Pack was modified; refresh the entire pack list.
 						if (searchPacksAgain(pList)) {
+							retries = checkRescanPackThreshold(retries, e);
 							continue SEARCH;
 						}
 					} catch (IOException e) {
@@ -255,8 +263,9 @@
 	}
 
 	void selectRepresentation(PackWriter packer, ObjectToPack otp,
-			WindowCursor curs) {
+			WindowCursor curs) throws PackMismatchException {
 		PackList pList = packList.get();
+		int retries = 0;
 		SEARCH: for (;;) {
 			for (Pack p : pList.packs) {
 				try {
@@ -268,6 +277,7 @@
 				} catch (PackMismatchException e) {
 					// Pack was modified; refresh the entire pack list.
 					//
+					retries = checkRescanPackThreshold(retries, e);
 					pList = scanPacks(pList);
 					continue SEARCH;
 				} catch (IOException e) {
@@ -278,6 +288,15 @@
 		}
 	}
 
+	private int checkRescanPackThreshold(int retries, PackMismatchException e)
+			throws PackMismatchException {
+		if (retries++ > MAX_PACKLIST_RESCAN_ATTEMPTS) {
+			e.setPermanent(true);
+			throw e;
+		}
+		return retries;
+	}
+
 	private void handlePackError(IOException e, Pack p) {
 		String warnTmpl = null;
 		int transientErrorCount = 0;