Merge branch 'stable-5.3' into stable-5.4

* stable-5.3:
  Prepare 5.3.3-SNAPSHOT builds
  JGit v5.3.2.201906051522-r
  Prepare 5.1.9-SNAPSHOT builds
  JGit v5.1.8.201906050907-r
  Test detecting modified packfiles
  Enhance fsTick() to use filesystem timer resolution
  Add debug trace to measure time needed to open pack index
  Extend FileSnapshot for packfiles to also use checksum to detect changes
  Wait opening new packfile until it can't be racy anymore
  Avoid null PackConfig in GC
  Add FileSnapshot test testing recognition of file size changes
  Capture reason for result of FileSnapshot#isModified
  Skip FileSnapshotTest#testSimulatePackfileReplacement on Windows
  Tune max heap size for tests
  Fix FileSnapshotTest.testNewFileNoWait() to match its javadoc
  ObjectDirectory: fix closing of obsolete packs
  Update API filters for methods added to fix bugs
  Bazel: Increase severity of most error-prone checks to ERROR
  Enable error-prone checks by default
  Add bazel options to align with gerrit's
  Include filekey file attribute when comparing FileSnapshots
  Measure file timestamp resolution used in FileSnapshot
  Fix FileSnapshot's consideration of file size
  Fix API problem filters
  Fix API problem filters

Change-Id: Ie1c7e4752661aec9e1754660934921224e2408eb
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/.gitignore b/.gitignore
index 3679a33..553ecac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
 /.project
 /target
-.DS_Store
 infer-out
 bazel-*
-*~
+
+# Do not add editor- and OS-specific files like *~ (Emacs) and .DS_Store
+# (macOS). Instead, add them to $XDG_CONFIG_HOME/git/ignore
+# (~/.config/git/ignore if $XDG_CONFIG_HOME is not set) or
+# $GIT_DIR/info/exclude. See "git help gitignore" for details.
diff --git a/WORKSPACE b/WORKSPACE
index c9ffb73..7ddd554 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -30,8 +30,8 @@
 
 maven_jar(
     name = "jsch",
-    artifact = "com.jcraft:jsch:0.1.54",
-    sha1 = "da3584329a263616e277e15462b387addd1b208d",
+    artifact = "com.jcraft:jsch:0.1.55",
+    sha1 = "bbd40e5aa7aa3cfad5db34965456cee738a42a50",
 )
 
 maven_jar(
@@ -59,15 +59,15 @@
 )
 
 maven_jar(
-    name = "sshd-core",
-    artifact = "org.apache.sshd:sshd-core:2.0.0",
-    sha1 = "f4275079a2463cfd2bf1548a80e1683288a8e86b",
+    name = "sshd-osgi",
+    artifact = "org.apache.sshd:sshd-osgi:2.2.0",
+    sha1 = "a45d48cb53678e699816e8e054e55fa33f5a4558",
 )
 
 maven_jar(
     name = "sshd-sftp",
-    artifact = "org.apache.sshd:sshd-sftp:2.0.0",
-    sha1 = "a12d64dc2d5d23271a4dc58075e55f9c64a68494",
+    artifact = "org.apache.sshd:sshd-sftp:2.2.0",
+    sha1 = "3d011e00adf38e49bb8711a9dd762fe908a2170c",
 )
 
 maven_jar(
@@ -215,21 +215,21 @@
 BOUNCYCASTLE_VER = "1.60"
 
 maven_jar(
-    name = "bcpg-jdk15on",
+    name = "bcpg",
     artifact = "org.bouncycastle:bcpg-jdk15on:" + BOUNCYCASTLE_VER,
     sha1 = "13c7a199c484127daad298996e95818478431a2c",
     src_sha1 = "edcd9e86d95e39b4da39bb295efd93bc4f56266e",
 )
 
 maven_jar(
-    name = "bcprov-jdk15on",
+    name = "bcprov",
     artifact = "org.bouncycastle:bcprov-jdk15on:" + BOUNCYCASTLE_VER,
     sha1 = "bd47ad3bd14b8e82595c7adaa143501e60842a84",
     src_sha1 = "7c57a4d13fe53d9abb967bba600dd0b293dafd6a",
 )
 
 maven_jar(
-    name = "bcpkix-jdk15on",
+    name = "bcpkix",
     artifact = "org.bouncycastle:bcpkix-jdk15on:" + BOUNCYCASTLE_VER,
     sha1 = "d0c46320fbc07be3a24eb13a56cee4e3d38e0c75",
     src_sha1 = "a25f041293f401af08efba63ff4bbdce98134a03",
diff --git a/lib/BUILD b/lib/BUILD
index d89ebab..f41ad16 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -68,14 +68,14 @@
 )
 
 java_library(
-    name = "sshd-core",
+    name = "sshd-osgi",
     visibility = [
         "//org.eclipse.jgit.junit.ssh:__pkg__",
         "//org.eclipse.jgit.ssh.apache:__pkg__",
         "//org.eclipse.jgit.ssh.apache.test:__pkg__",
         "//org.eclipse.jgit.test:__pkg__",
     ],
-    exports = ["@sshd-core//jar"],
+    exports = ["@sshd-osgi//jar"],
 )
 
 java_library(
@@ -153,7 +153,7 @@
         "//org.eclipse.jgit:__pkg__",
         "//org.eclipse.jgit.test:__pkg__",
     ],
-    exports = ["@bcpg-jdk15on//jar"],
+    exports = ["@bcpg//jar"],
 )
 
 java_library(
@@ -162,7 +162,7 @@
         "//org.eclipse.jgit:__pkg__",
         "//org.eclipse.jgit.test:__pkg__",
     ],
-    exports = ["@bcprov-jdk15on//jar"],
+    exports = ["@bcprov//jar"],
 )
 
 java_library(
@@ -171,7 +171,7 @@
         "//org.eclipse.jgit:__pkg__",
         "//org.eclipse.jgit.test:__pkg__",
     ],
-    exports = ["@bcpkix-jdk15on//jar"],
+    exports = ["@bcpkix//jar"],
 )
 
 java_library(
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 708fad8..3ef2c4d 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -4,13 +4,13 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.ant.test
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.ant.tasks;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index b9f5fb6..0ba52d4 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
@@ -105,7 +105,7 @@
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
-          <argLine>@{argLine} -Xmx256m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
+          <argLine>@{argLine} -Xmx512m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
         </configuration>
       </plugin>
     </plugins>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index 804d90c..6a9e317 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,11 +3,11 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ant
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)"
+  org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="5.3.3";
+Export-Package: org.eclipse.jgit.ant.tasks;version="5.4.0";
  uses:="org.apache.tools.ant.types,org.apache.tools.ant"
diff --git a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..0326191
--- /dev/null
+++ b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.ant - Sources
+Bundle-SymbolicName: org.eclipse.jgit.ant.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index ffbe8db..f43f1af 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -45,62 +45,103 @@
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 
-	<parent>
-		<groupId>org.eclipse.jgit</groupId>
-		<artifactId>org.eclipse.jgit-parent</artifactId>
-		<version>5.3.3-SNAPSHOT</version>
-	</parent>
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>5.4.0-SNAPSHOT</version>
+  </parent>
 
-	<artifactId>org.eclipse.jgit.ant</artifactId>
-	<name>JGit - Ant Tasks</name>
+  <artifactId>org.eclipse.jgit.ant</artifactId>
+  <name>JGit - Ant Tasks</name>
 
-	<description>
-    Ant based user interface for Git
-    </description>
+  <description>Ant based user interface for Git</description>
 
-	<properties>
-		<translate-qualifier />
-	</properties>
+  <properties>
+    <translate-qualifier />
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
+  </properties>
 
-	<dependencies>
-		<dependency>
-			<groupId>org.eclipse.jgit</groupId>
-			<artifactId>org.eclipse.jgit</artifactId>
-			<version>${project.version}</version>
-		</dependency>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
 
-		<dependency>
-			<groupId>org.apache.ant</groupId>
-			<artifactId>ant</artifactId>
-			<version>1.10.5</version>
-		</dependency>
-	</dependencies>
+    <dependency>
+      <groupId>org.apache.ant</groupId>
+      <artifactId>ant</artifactId>
+      <version>1.10.5</version>
+    </dependency>
+  </dependencies>
 
-	<build>
-		<sourceDirectory>src/</sourceDirectory>
+  <build>
+    <sourceDirectory>src/</sourceDirectory>
 
-		<resources>
-			<resource>
-				<directory>.</directory>
-				<includes>
-					<include>plugin.properties</include>
-                    <include>about.html</include>
-                </includes>
-			</resource>
-			<resource>
-				<directory>resources/</directory>
-			</resource>
-		</resources>
+	<resources>
+      <resource>
+        <directory>.</directory>
+          <includes>
+            <include>plugin.properties</include>
+            <include>about.html</include>
+          </includes>
+      </resource>
+      <resource>
+        <directory>resources/</directory>
+      </resource>
+    </resources>
 
-		<plugins>
-			<plugin>
-				<artifactId>maven-jar-plugin</artifactId>
-				<configuration>
-					<archive>
-						<manifestFile>${bundle-manifest}</manifestFile>
-					</archive>
-				</configuration>
-			</plugin>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${bundle-manifest}</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
 
       <plugin>
           <groupId>com.github.siom79.japicmp</groupId>
@@ -143,10 +184,10 @@
           </execution>
         </executions>
       </plugin>
-		</plugins>
-	</build>
+    </plugins>
+  </build>
 
-	<reporting>
+  <reporting>
     <plugins>
       <plugin>
           <groupId>com.github.siom79.japicmp</groupId>
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index a172543..4d04f29 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.archive
 Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -13,15 +13,15 @@
  org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)",
  org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)",
  org.apache.commons.compress.compressors.xz;version="[1.4,2.0)",
- org.eclipse.jgit.api;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.api;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.osgi.framework;version="[1.3.0,2.0.0)"
 Bundle-ActivationPolicy: lazy
 Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="5.3.3";
+Export-Package: org.eclipse.jgit.archive;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.api,
    org.apache.commons.compress.archivers,
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index b118374..87e3931 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.archive - Sources
 Bundle-SymbolicName: org.eclipse.jgit.archive.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.3.3.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.3.3.qualifier";roots="."
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 95e1e44..1207d19 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
@@ -64,6 +64,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -99,6 +100,48 @@
 
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <configuration>
           <archive>
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index 0819e8e..6e199e9 100644
--- a/org.eclipse.jgit.coverage/pom.xml
+++ b/org.eclipse.jgit.coverage/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
@@ -18,88 +18,88 @@
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.archive</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.apache</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.server</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ui</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.test</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant.test</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.test</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm.test</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.test</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
-      <version>5.3.3-SNAPSHOT</version>
+      <version>5.4.0-SNAPSHOT</version>
     </dependency>
   </dependencies>
 
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index 6905939..5a8ae0b 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.http.apache
 Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
@@ -23,11 +23,11 @@
  org.apache.http.impl.client;version="[4.3.0,5.0.0)",
  org.apache.http.impl.conn;version="[4.3.0,5.0.0)",
  org.apache.http.params;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="5.3.3";
+ org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="5.4.0";
   uses:="org.apache.http.client,
    org.eclipse.jgit.transport.http,
    org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..bffe9f1
--- /dev/null
+++ b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.http.apache - Sources
+Bundle-SymbolicName: org.eclipse.jgit.http.apache.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index c2ea27d..5d1b386 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -45,64 +45,107 @@
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 
-	<parent>
-		<groupId>org.eclipse.jgit</groupId>
-		<artifactId>org.eclipse.jgit-parent</artifactId>
-		<version>5.3.3-SNAPSHOT</version>
-	</parent>
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>5.4.0-SNAPSHOT</version>
+  </parent>
 
-	<artifactId>org.eclipse.jgit.http.apache</artifactId>
-	<name>JGit - Apache httpclient based HTTP support</name>
+  <artifactId>org.eclipse.jgit.http.apache</artifactId>
+  <name>JGit - Apache httpclient based HTTP support</name>
 
-	<description>
+  <description>
     Apache httpclient based HTTP support
-    </description>
+  </description>
 
-	<properties>
-		<translate-qualifier />
-	</properties>
+  <properties>
+    <translate-qualifier />
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
+  </properties>
 
-	<dependencies>
-		<dependency>
-			<groupId>org.eclipse.jgit</groupId>
-			<artifactId>org.eclipse.jgit</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-        <dependency>
-          <groupId>org.apache.httpcomponents</groupId>
-          <artifactId>httpclient</artifactId>
-        </dependency>
-        <dependency>
-          <groupId>org.apache.httpcomponents</groupId>
-          <artifactId>httpcore</artifactId>
-        </dependency>
-	</dependencies>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+    </dependency>
+  </dependencies>
 
-	<build>
-		<sourceDirectory>src/</sourceDirectory>
+  <build>
+    <sourceDirectory>src/</sourceDirectory>
 
-		<resources>
-			<resource>
-				<directory>.</directory>
-				<includes>
-					<include>plugin.properties</include>
-                    <include>about.html</include>
-                </includes>
-			</resource>
-			<resource>
-				<directory>resources/</directory>
-			</resource>
-		</resources>
+    <resources>
+      <resource>
+        <directory>.</directory>
+        <includes>
+          <include>plugin.properties</include>
+          <include>about.html</include>
+        </includes>
+      </resource>
+      <resource>
+        <directory>resources/</directory>
+      </resource>
+    </resources>
 
-		<plugins>
-			<plugin>
-				<artifactId>maven-jar-plugin</artifactId>
-				<configuration>
-					<archive>
-						<manifestFile>${bundle-manifest}</manifestFile>
-					</archive>
-				</configuration>
-			</plugin>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${bundle-manifest}</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
 
       <plugin>
           <groupId>com.github.siom79.japicmp</groupId>
@@ -145,10 +188,10 @@
           </execution>
         </executions>
       </plugin>
-		</plugins>
-	</build>
+    </plugins>
+  </build>
 
-	<reporting>
+  <reporting>
     <plugins>
       <plugin>
           <groupId>com.github.siom79.japicmp</groupId>
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 1891061..0c885a6 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.http.server
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="5.3.3",
- org.eclipse.jgit.http.server.glue;version="5.3.3";
+Export-Package: org.eclipse.jgit.http.server;version="5.4.0",
+ org.eclipse.jgit.http.server.glue;version="5.4.0";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="5.3.3";
+ org.eclipse.jgit.http.server.resolver;version="5.4.0";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -18,13 +18,13 @@
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
  javax.servlet.http;version="[2.5.0,3.2.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.transport.parser;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.resolver;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)"
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)"
diff --git a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..46451bb
--- /dev/null
+++ b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.http.server - Sources
+Bundle-SymbolicName: org.eclipse.jgit.http.server.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 6aff14f..b160bfa 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
@@ -64,6 +64,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -99,6 +100,28 @@
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
         <inherited>true</inherited>
         <executions>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
index 2ebe1b7..51de8ab 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
@@ -202,7 +202,7 @@ public void init(FilterConfig filterConfig) throws ServletException {
 		if (resolver == null) {
 			File root = getFile(filterConfig, "base-path");
 			boolean exportAll = getBoolean(filterConfig, "export-all");
-			setRepositoryResolver(new FileResolver<HttpServletRequest>(root, exportAll));
+			setRepositoryResolver(new FileResolver<>(root, exportAll));
 		}
 
 		initialized = true;
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
index d8fa712..14b6506 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
@@ -47,7 +47,6 @@
 
 import java.io.IOException;
 
-import javax.servlet.FilterChain;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
@@ -140,14 +139,10 @@ public void destroy() {
 	@Override
 	protected void service(HttpServletRequest req, HttpServletResponse res)
 			throws ServletException, IOException {
-		filter.doFilter(req, res, new FilterChain() {
-			@Override
-			public void doFilter(ServletRequest request,
-					ServletResponse response) throws IOException,
-					ServletException {
-				((HttpServletResponse) response).sendError(SC_NOT_FOUND);
-			}
-		});
+		filter.doFilter(req, res,
+				(ServletRequest request, ServletResponse response) -> {
+					((HttpServletResponse) response).sendError(SC_NOT_FOUND);
+				});
 	}
 
 	/**
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 40e7c29..cd161be 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.http.test
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -28,25 +28,25 @@
  org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.http.server;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.http.server.glue;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.http.server.resolver;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit.http;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.resolver;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.http.server;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.http.server.glue;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.http.server.resolver;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit.http;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.hamcrest;version="[1.1.0,2.0.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index fbd583e..df8dc27 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -51,7 +51,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
@@ -139,7 +139,7 @@
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
-          <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory}  -Xmx300m</argLine>
+          <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory}  -Xmx512m</argLine>
           <includes>
             <include>**/*Test.java</include>
             <include>**/*Tests.java</include>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
index 5a46967..ec9ced0 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
@@ -69,7 +69,6 @@
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.Transport;
 import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.transport.resolver.RepositoryResolver;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.junit.Before;
@@ -90,18 +89,13 @@ public void setUp() throws Exception {
 
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
-		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
-			@Override
-			public Repository open(HttpServletRequest req, String name)
-					throws RepositoryNotFoundException,
-					ServiceNotEnabledException {
-				if (!name.equals(srcName))
-					throw new RepositoryNotFoundException(name);
-
-				final Repository db = src.getRepository();
-				db.incrementOpen();
-				return db;
+		gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
+			if (!name.equals(srcName)) {
+				throw new RepositoryNotFoundException(name);
 			}
+			final Repository db = src.getRepository();
+			db.incrementOpen();
+			return db;
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
 			@Override
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
index 8dce98b..eb19365 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
@@ -75,7 +75,6 @@
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.Transport;
 import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.transport.resolver.RepositoryResolver;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.junit.Before;
@@ -117,18 +116,13 @@ public void setUp() throws Exception {
 
 		ServletContextHandler app = server.addContext("/git");
 		gs = new GitServlet();
-		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
-			@Override
-			public Repository open(HttpServletRequest req, String name)
-					throws RepositoryNotFoundException,
-					ServiceNotEnabledException {
-				if (!name.equals(repoName))
-					throw new RepositoryNotFoundException(name);
-
-				final Repository db = srv.getRepository();
-				db.incrementOpen();
-				return db;
+		gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
+			if (!name.equals(repoName)) {
+				throw new RepositoryNotFoundException(name);
 			}
+			final Repository db = srv.getRepository();
+			db.incrementOpen();
+			return db;
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
 			@Override
@@ -179,12 +173,8 @@ public void testRuntimeExceptionInPreReceiveHook() throws Exception {
 
 		maxPackSize = 0;
 		postHook = null;
-		preHook = new PreReceiveHook() {
-			@Override
-			public void onPreReceive(ReceivePack rp,
-					Collection<ReceiveCommand> commands) {
-				throw new IllegalStateException();
-			}
+		preHook = (ReceivePack rp, Collection<ReceiveCommand> commands) -> {
+			throw new IllegalStateException();
 		};
 
 		try (Transport t = Transport.open(clientRepo, srvURI)) {
@@ -263,15 +253,11 @@ public void testUnpackErrorWithSubsequentExceptionInPostReceiveHook()
 		maxPackSize = 100;
 		// this PostReceiveHook when called after an unsuccesfull unpack will
 		// lead to an IllegalStateException
-		postHook = new PostReceiveHook() {
-			@Override
-			public void onPostReceive(ReceivePack rp,
-					Collection<ReceiveCommand> commands) {
-				// the maxPackSize setting caused that the packfile couldn't be
-				// saved to disk. Calling getPackSize() now will lead to a
-				// IllegalStateException.
-				rp.getPackSize();
-			}
+		postHook = (ReceivePack rp, Collection<ReceiveCommand> commands) -> {
+			// the maxPackSize setting caused that the packfile couldn't be
+			// saved to disk. Calling getPackSize() now will lead to a
+			// IllegalStateException.
+			rp.getPackSize();
 		};
 
 		try (Transport t = Transport.open(clientRepo, srvURI)) {
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index 5a5ff1a..49ff51a 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -70,14 +70,12 @@
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.PreReceiveHook;
 import org.eclipse.jgit.transport.PushResult;
 import org.eclipse.jgit.transport.ReceiveCommand;
 import org.eclipse.jgit.transport.ReceivePack;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.Transport;
 import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.transport.resolver.RepositoryResolver;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.junit.Before;
@@ -98,18 +96,13 @@ public void setUp() throws Exception {
 
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
-		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
-			@Override
-			public Repository open(HttpServletRequest req, String name)
-					throws RepositoryNotFoundException,
-					ServiceNotEnabledException {
-				if (!name.equals(srcName))
-					throw new RepositoryNotFoundException(name);
-
-				final Repository db = src.getRepository();
-				db.incrementOpen();
-				return db;
+		gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
+			if (!name.equals(srcName)) {
+				throw new RepositoryNotFoundException(name);
 			}
+			final Repository db = src.getRepository();
+			db.incrementOpen();
+			return db;
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
 			@Override
@@ -117,14 +110,11 @@ public ReceivePack create(HttpServletRequest req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
 				ReceivePack recv = super.create(req, db);
-				recv.setPreReceiveHook(new PreReceiveHook() {
-					@Override
-					public void onPreReceive(ReceivePack rp,
-							Collection<ReceiveCommand> commands) {
-						rp.sendMessage("message line 1");
-						rp.sendError("no soup for you!");
-						rp.sendMessage("come back next year!");
-					}
+				recv.setPreReceiveHook((ReceivePack rp,
+						Collection<ReceiveCommand> commands) -> {
+					rp.sendMessage("message line 1");
+					rp.sendError("no soup for you!");
+					rp.sendMessage("come back next year!");
 				});
 				return recv;
 			}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 53626b1..1660f14 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -87,8 +87,6 @@
 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
 import org.eclipse.jgit.transport.http.HttpConnection;
 import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory;
-import org.eclipse.jgit.transport.resolver.RepositoryResolver;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -141,18 +139,13 @@ private ServletContextHandler dumb(String path) {
 
 	private ServletContextHandler smart(String path) {
 		GitServlet gs = new GitServlet();
-		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
-			@Override
-			public Repository open(HttpServletRequest req, String name)
-					throws RepositoryNotFoundException,
-					ServiceNotEnabledException {
-				final Repository db = remoteRepository.getRepository();
-				if (!name.equals(nameOf(db)))
-					throw new RepositoryNotFoundException(name);
-
-				db.incrementOpen();
-				return db;
+		gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
+			final Repository db = remoteRepository.getRepository();
+			if (!name.equals(nameOf(db))) {
+				throw new RepositoryNotFoundException(name);
 			}
+			db.incrementOpen();
+			return db;
 		});
 
 		ServletContextHandler ctx = server.addContext(path);
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
index 0415bcb..79df5a2 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
@@ -63,14 +63,12 @@
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.PostReceiveHook;
 import org.eclipse.jgit.transport.PushResult;
 import org.eclipse.jgit.transport.ReceiveCommand;
 import org.eclipse.jgit.transport.ReceivePack;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.Transport;
 import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.transport.resolver.RepositoryResolver;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.junit.Before;
@@ -93,18 +91,13 @@ public void setUp() throws Exception {
 
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
-		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
-			@Override
-			public Repository open(HttpServletRequest req, String name)
-					throws RepositoryNotFoundException,
-					ServiceNotEnabledException {
-				if (!name.equals(srcName))
-					throw new RepositoryNotFoundException(name);
-
-				final Repository db = src.getRepository();
-				db.incrementOpen();
-				return db;
+		gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
+			if (!name.equals(srcName)) {
+				throw new RepositoryNotFoundException(name);
 			}
+			final Repository db = src.getRepository();
+			db.incrementOpen();
+			return db;
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
 			@Override
@@ -112,13 +105,9 @@ public ReceivePack create(HttpServletRequest req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
 				ReceivePack recv = super.create(req, db);
-				recv.setPostReceiveHook(new PostReceiveHook() {
-
-					@Override
-					public void onPostReceive(ReceivePack rp,
-							Collection<ReceiveCommand> commands) {
-						packSize = rp.getPackSize();
-					}
+				recv.setPostReceiveHook((ReceivePack rp,
+						Collection<ReceiveCommand> commands) -> {
+					packSize = rp.getPackSize();
 				});
 				return recv;
 			}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
index a1baae3..f0fac5a 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
@@ -71,8 +71,6 @@
 import org.eclipse.jgit.transport.PacketLineIn;
 import org.eclipse.jgit.transport.PacketLineOut;
 import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.transport.resolver.RepositoryResolver;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.eclipse.jgit.util.NB;
 import org.junit.Before;
 import org.junit.Test;
@@ -94,18 +92,13 @@ public void setUp() throws Exception {
 
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
-		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
-			@Override
-			public Repository open(HttpServletRequest req, String name)
-					throws RepositoryNotFoundException,
-					ServiceNotEnabledException {
-				if (!name.equals(srcName))
-					throw new RepositoryNotFoundException(name);
-
-				final Repository db = src.getRepository();
-				db.incrementOpen();
-				return db;
+		gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
+			if (!name.equals(srcName)) {
+				throw new RepositoryNotFoundException(name);
 			}
+			final Repository db = src.getRepository();
+			db.incrementOpen();
+			return db;
 		});
 		app.addServlet(new ServletHolder(gs), "/*");
 
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index aad029c..54c1c03 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -124,9 +124,6 @@
 import org.eclipse.jgit.transport.http.HttpConnectionFactory;
 import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory;
 import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
-import org.eclipse.jgit.transport.resolver.UploadPackFactory;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.HttpSupport;
 import org.eclipse.jgit.util.SystemReader;
@@ -192,18 +189,13 @@ public void setUp() throws Exception {
 						ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
 
 		GitServlet gs = new GitServlet();
-		gs.setUploadPackFactory(new UploadPackFactory<HttpServletRequest>() {
-			@Override
-			public UploadPack create(HttpServletRequest req, Repository db)
-					throws ServiceNotEnabledException,
-					ServiceNotAuthorizedException {
-				DefaultUploadPackFactory f = new DefaultUploadPackFactory();
-				UploadPack up = f.create(req, db);
-				if (advertiseRefsHook != null) {
-					up.setAdvertiseRefsHook(advertiseRefsHook);
-				}
-				return up;
+		gs.setUploadPackFactory((HttpServletRequest req, Repository db) -> {
+			DefaultUploadPackFactory f = new DefaultUploadPackFactory();
+			UploadPack up = f.create(req, db);
+			if (advertiseRefsHook != null) {
+				up.setAdvertiseRefsHook(advertiseRefsHook);
 			}
+			return up;
 		});
 
 		ServletContextHandler app = addNormalContext(gs, src, srcName);
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index c8b48dc..c9b29ed 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.junit.http
 Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
@@ -22,16 +22,16 @@
  org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.ssl;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.http.server;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.resolver;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.http.server;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.4.0,5.5.0)",
  org.junit;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="5.3.3";
+Export-Package: org.eclipse.jgit.junit.http;version="5.4.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.junit,
    javax.servlet.http,
diff --git a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..5f88db1
--- /dev/null
+++ b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.junit.http - Sources
+Bundle-SymbolicName: org.eclipse.jgit.junit.http.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 9c984c0..7441d9f 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
@@ -62,6 +62,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -109,6 +110,48 @@
 
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <configuration>
           <archive>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
index 245b510..7c78330 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
@@ -54,8 +54,6 @@
 import org.eclipse.jgit.http.server.GitServlet;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.transport.resolver.RepositoryResolver;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 
 /**
  * Simple http server for testing http access to Git repositories.
@@ -136,17 +134,12 @@ public URIish getSecureUri() {
 
 	private ServletContextHandler smart(String path) {
 		GitServlet gs = new GitServlet();
-		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
-			@Override
-			public Repository open(HttpServletRequest req, String name)
-					throws RepositoryNotFoundException,
-					ServiceNotEnabledException {
-				if (!name.equals(nameOf(db)))
-					throw new RepositoryNotFoundException(name);
-
-				db.incrementOpen();
-				return db;
+		gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
+			if (!name.equals(nameOf(db))) {
+				throw new RepositoryNotFoundException(name);
 			}
+			db.incrementOpen();
+			return db;
 		});
 
 		ServletContextHandler ctx = server.addContext(path);
diff --git a/org.eclipse.jgit.junit.ssh/BUILD b/org.eclipse.jgit.junit.ssh/BUILD
index e9a04c7..8c17cb7 100644
--- a/org.eclipse.jgit.junit.ssh/BUILD
+++ b/org.eclipse.jgit.junit.ssh/BUILD
@@ -7,7 +7,7 @@
     resource_strip_prefix = "org.eclipse.jgit.junit.ssh/resources",
     resources = glob(["resources/**"]),
     deps = [
-        "//lib:sshd-core",
+        "//lib:sshd-osgi",
         "//lib:sshd-sftp",
         # We want these deps to be provided_deps
         "//org.eclipse.jgit:jgit",
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index ed11de9..0d66ecc 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -3,34 +3,35 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.junit.ssh
 Bundle-SymbolicName: org.eclipse.jgit.junit.ssh
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.apache.sshd.common;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.config.keys;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.file.virtualfs;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.helpers;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.io;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.kex;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.keyprovider;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.session;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.buffer;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.logging;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.security;version="[2.0.0,2.1.0)",
- org.apache.sshd.server;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.auth;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.auth.gss;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.auth.keyboard;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.auth.password;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.command;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.session;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.shell;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.subsystem;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.subsystem.sftp;version="[2.0.0,2.1.0)",
- org.eclipse.jgit.annotations;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
+Import-Package: org.apache.sshd.common;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.config.keys;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.file.virtualfs;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.helpers;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.io;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.kex;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.keyprovider;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.session;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.buffer;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.logging;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.security;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.threads;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.auth;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.auth.gss;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.auth.keyboard;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.auth.password;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.command;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.session;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.shell;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.subsystem;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.subsystem.sftp;version="[2.2.0,2.3.0)",
+ org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.junit.ssh;version="5.3.3"
+Export-Package: org.eclipse.jgit.junit.ssh;version="5.4.0"
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..ad94c41
--- /dev/null
+++ b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.junit.ssh - Sources
+Bundle-SymbolicName: org.eclipse.jgit.junit.ssh.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index 0194faa..1c2830e 100644
--- a/org.eclipse.jgit.junit.ssh/pom.xml
+++ b/org.eclipse.jgit.junit.ssh/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
@@ -62,6 +62,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -73,7 +74,7 @@
 
     <dependency>
       <groupId>org.apache.sshd</groupId>
-      <artifactId>sshd-core</artifactId>
+      <artifactId>sshd-osgi</artifactId>
       <version>${apache-sshd-version}</version>
     </dependency>
 
@@ -104,6 +105,48 @@
 
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <configuration>
           <archive>
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
index f5af2e5..25d952f 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java
@@ -55,10 +55,9 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
 import org.apache.sshd.common.config.keys.KeyUtils;
@@ -67,6 +66,8 @@
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.threads.CloseableExecutorService;
+import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.server.auth.UserAuth;
@@ -110,8 +111,8 @@ public class SshTestGitServer {
 	@NonNull
 	protected PublicKey testKey;
 
-	private final ExecutorService executorService = Executors
-			.newFixedThreadPool(2);
+	private final CloseableExecutorService executorService = ThreadUtils
+			.newFixedThreadPool("SshTestGitServerPool", 2);
 
 	/**
 	 * Creates a ssh git <em>test</em> server. It serves one single repository,
@@ -138,11 +139,12 @@ public SshTestGitServer(@NonNull String testUser, @NonNull Path testKey,
 		server = SshServer.setUpDefaultServer();
 		// Set host key
 		try (ByteArrayInputStream in = new ByteArrayInputStream(hostKey)) {
-			hostKeys.add(SecurityUtils.loadKeyPairIdentity("", in, null));
+			SecurityUtils.loadKeyPairIdentities(null, null, in, null)
+					.forEach((k) -> hostKeys.add(k));
 		} catch (IOException | GeneralSecurityException e) {
 			// Ignore.
 		}
-		server.setKeyPairProvider(() -> hostKeys);
+		server.setKeyPairProvider((session) -> hostKeys);
 
 		configureAuthentication();
 
@@ -276,8 +278,10 @@ protected void configureShell() {
 	public void addHostKey(@NonNull Path key, boolean inFront)
 			throws IOException, GeneralSecurityException {
 		try (InputStream in = Files.newInputStream(key)) {
-			KeyPair pair = SecurityUtils.loadKeyPairIdentity(key.toString(), in,
-					null);
+			KeyPair pair = SecurityUtils
+					.loadKeyPairIdentities(null,
+							NamedResource.ofName(key.toString()), in, null)
+					.iterator().next();
 			if (inFront) {
 				hostKeys.add(0, pair);
 			} else {
@@ -335,14 +339,14 @@ public void stop() throws IOException {
 	public void setTestUserPublicKey(Path key)
 			throws IOException, GeneralSecurityException {
 		this.testKey = AuthorizedKeyEntry.readAuthorizedKeys(key).get(0)
-				.resolvePublicKey(PublicKeyEntryResolver.IGNORING);
+				.resolvePublicKey(null, PublicKeyEntryResolver.IGNORING);
 	}
 
 	private class GitUploadPackCommand extends AbstractCommandSupport {
 
 		protected GitUploadPackCommand(String command,
-				ExecutorService executorService) {
-			super(command, executorService, false);
+				CloseableExecutorService executorService) {
+			super(command, ThreadUtils.noClose(executorService));
 		}
 
 		@Override
@@ -370,8 +374,8 @@ public void run() {
 	private class GitReceivePackCommand extends AbstractCommandSupport {
 
 		protected GitReceivePackCommand(String command,
-				ExecutorService executorService) {
-			super(command, executorService, false);
+				CloseableExecutorService executorService) {
+			super(command, ThreadUtils.noClose(executorService));
 		}
 
 		@Override
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index a104bc1..308feb7 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,34 +3,34 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.junit
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.annotations;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.api;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.api.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.dircache;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.merge;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="5.3.3",
- org.eclipse.jgit.treewalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util.io;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util.time;version="[5.3.3,5.4.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.api;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.api.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.dircache;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.merge;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="5.4.0",
+ org.eclipse.jgit.treewalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util.io;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util.time;version="[5.4.0,5.5.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
  org.junit.runner;version="[4.12,5.0.0)",
  org.junit.runners.model;version="[4.12,5.0.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="5.3.3";
+Export-Package: org.eclipse.jgit.junit;version="5.4.0";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -43,4 +43,4 @@
    org.junit.runners.model,
    org.junit.runner,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.junit.time;version="5.3.3";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="5.4.0";uses:="org.eclipse.jgit.util.time"
diff --git a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..ad3ad5b
--- /dev/null
+++ b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.junit - Sources
+Bundle-SymbolicName: org.eclipse.jgit.junit.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index e67a9cd..b6f5b35 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
@@ -64,6 +64,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -94,6 +95,48 @@
 
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <configuration>
           <archive>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 4b2eadf..f93424c 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -235,17 +235,18 @@ private static boolean recursiveDelete(final File dir,
 		if (!dir.exists())
 			return silent;
 		final File[] ls = dir.listFiles();
-		if (ls != null)
-			for (int k = 0; k < ls.length; k++) {
-				final File e = ls[k];
-				if (e.isDirectory())
-					silent = recursiveDelete(e, silent, failOnError);
-				else if (!e.delete()) {
-					if (!silent)
-						reportDeleteFailure(failOnError, e);
+		if (ls != null) {
+			for (File f : ls) {
+				if (f.isDirectory()) {
+					silent = recursiveDelete(f, silent, failOnError);
+				} else if (!f.delete()) {
+					if (!silent) {
+						reportDeleteFailure(failOnError, f);
+					}
 					silent = !failOnError;
 				}
 			}
+		}
 		if (!dir.delete()) {
 			if (!silent)
 				reportDeleteFailure(failOnError, dir);
@@ -366,7 +367,9 @@ public static String indexState(Repository repo, int includedOptions)
 	/**
 	 * Creates a new empty bare repository.
 	 *
-	 * @return the newly created repository, opened for access
+	 * @return the newly created bare repository, opened for access. The
+	 *         repository will not be closed in {@link #tearDown()}; the caller
+	 *         is responsible for closing it.
 	 * @throws IOException
 	 *             the repository could not be created in the temporary area
 	 */
@@ -377,7 +380,9 @@ protected FileRepository createBareRepository() throws IOException {
 	/**
 	 * Creates a new empty repository within a new empty working directory.
 	 *
-	 * @return the newly created repository, opened for access
+	 * @return the newly created repository, opened for access. The repository
+	 *         will not be closed in {@link #tearDown()}; the caller is
+	 *         responsible for closing it.
 	 * @throws IOException
 	 *             the repository could not be created in the temporary area
 	 */
@@ -391,7 +396,9 @@ protected FileRepository createWorkRepository() throws IOException {
 	 * @param bare
 	 *            true to create a bare repository; false to make a repository
 	 *            within its working directory
-	 * @return the newly created repository, opened for access
+	 * @return the newly created repository, opened for access. The repository
+	 *         will not be closed in {@link #tearDown()}; the caller is
+	 *         responsible for closing it.
 	 * @throws IOException
 	 *             the repository could not be created in the temporary area
 	 * @since 5.3
@@ -548,13 +555,7 @@ protected File write(String body) throws IOException {
 		try {
 			write(f, body);
 			return f;
-		} catch (Error e) {
-			f.delete();
-			throw e;
-		} catch (RuntimeException e) {
-			f.delete();
-			throw e;
-		} catch (IOException e) {
+		} catch (Error | RuntimeException | IOException e) {
 			f.delete();
 			throw e;
 		}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index 8de386e..89f2530 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -171,22 +171,20 @@ public long getCurrentTime() {
 	/** {@inheritDoc} */
 	@Override
 	public MonotonicClock getClock() {
-		return new MonotonicClock() {
-			@Override
-			public ProposedTimestamp propose() {
-				long t = getCurrentTime();
-				return new ProposedTimestamp() {
-					@Override
-					public long read(TimeUnit unit) {
-						return unit.convert(t, TimeUnit.MILLISECONDS);
-					}
+		return () -> {
+			long t = getCurrentTime();
+			return new ProposedTimestamp() {
 
-					@Override
-					public void blockUntil(Duration maxWait) {
-						// Do not wait.
-					}
-				};
-			}
+				@Override
+				public long read(TimeUnit unit) {
+					return unit.convert(t, TimeUnit.MILLISECONDS);
+				}
+
+				@Override
+				public void blockUntil(Duration maxWait) {
+					// Do not wait.
+				}
+			};
 		};
 	}
 
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
index 987f923..a5270ed 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
@@ -492,9 +492,7 @@ else if (empty)
 				git.branchCreate().setName(branch).setStartPoint(commit).call();
 
 			return commit;
-		} catch (IOException e) {
-			throw new RuntimeException(e);
-		} catch (GitAPIException e) {
+		} catch (IOException | GitAPIException e) {
 			throw new RuntimeException(e);
 		}
 	}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index 55a7766..356d9d8 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -1235,7 +1235,7 @@ private void insertChangeId(org.eclipse.jgit.lib.CommitBuilder c) {
 				firstParentId = parents.get(0);
 
 			ObjectId cid;
-			if (changeId.equals(""))
+			if (changeId.isEmpty())
 				cid = ChangeIdUtil.computeChangeId(c.getTreeId(), firstParentId,
 						c.getAuthor(), c.getCommitter(), message);
 			else
diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
index 4bef1de..bf3c1c7 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs.server.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -28,24 +28,24 @@
  org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.api;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.api.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit.http;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.server;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.test;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.api;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.api.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit.http;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.server;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.test;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index 44f166b..ce1e280 100644
--- a/org.eclipse.jgit.lfs.server.test/pom.xml
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
@@ -137,7 +137,7 @@
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
-          <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory}  -Xmx300m</argLine>
+          <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory}  -Xmx512m</argLine>
         </configuration>
       </plugin>
     </plugins>
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java
index 09f8d0a..c6f9535 100644
--- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java
@@ -53,7 +53,6 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Callable;
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -123,13 +122,10 @@ public void testParallelUploads() throws Exception {
 		ExecutorService e = Executors.newFixedThreadPool(count);
 		try {
 			for (Path p : paths) {
-				e.submit(new Callable<Void>() {
-					@Override
-					public Void call() throws Exception {
-						barrier.await();
-						putContent(p);
-						return null;
-					}
+				e.submit(() -> {
+					barrier.await();
+					putContent(p);
+					return null;
 				});
 			}
 		} finally {
diff --git a/org.eclipse.jgit.lfs.server/.settings/.api_filters b/org.eclipse.jgit.lfs.server/.settings/.api_filters
index 2a47dd3..6609c3d 100644
--- a/org.eclipse.jgit.lfs.server/.settings/.api_filters
+++ b/org.eclipse.jgit.lfs.server/.settings/.api_filters
@@ -1,13 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <component id="org.eclipse.jgit.lfs.server" version="2">
-    <resource path="META-INF/MANIFEST.MF">
-        <filter id="924844039">
-            <message_arguments>
-                <message_argument value="5.3.2"/>
-                <message_argument value="5.3.0"/>
-            </message_arguments>
-        </filter>
-    </resource>
     <resource path="src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java" type="org.eclipse.jgit.lfs.server.fs.ObjectUploadListener">
         <filter id="1142947843">
             <message_arguments>
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index 0df2eea..9fca010 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -3,19 +3,19 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs.server
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs.server;version="5.3.3";
+Export-Package: org.eclipse.jgit.lfs.server;version="5.4.0";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="5.3.3";
+ org.eclipse.jgit.lfs.server.fs;version="5.4.0";
   uses:="javax.servlet,
    javax.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="5.3.3";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="5.3.3";
+ org.eclipse.jgit.lfs.server.internal;version="5.4.0";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="5.4.0";
   uses:="org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -25,15 +25,15 @@
  javax.servlet.http;version="[3.1.0,4.0.0)",
  org.apache.http;version="[4.3.0,5.0.0)",
  org.apache.http.client;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.internal;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.internal;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..370c729
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.lfs.server - Sources
+Bundle-SymbolicName: org.eclipse.jgit.lfs.server.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index 729665c..3fa460d 100644
--- a/org.eclipse.jgit.lfs.server/pom.xml
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server</artifactId>
@@ -62,6 +62,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -114,6 +115,28 @@
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
         <inherited>true</inherited>
         <executions>
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index 64d7c05..7be4bd1 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,23 +3,22 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.runner;version="[4.12,5.0.0)",
  org.junit.runners;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.lfs.test;version="5.3.3";x-friends:="org.eclipse.jgit.lfs.server.test"
-
+Export-Package: org.eclipse.jgit.lfs.test;version="5.4.0";x-friends:="org.eclipse.jgit.lfs.server.test"
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index 7138969..ef97708 100644
--- a/org.eclipse.jgit.lfs.test/pom.xml
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.test</artifactId>
@@ -111,7 +111,7 @@
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
-          <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory}  -Xmx300m</argLine>
+          <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory}  -Xmx512m</argLine>
         </configuration>
       </plugin>
     </plugins>
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index a13f990..27792c6 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,33 +3,33 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs
 Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs;version="5.3.3",
- org.eclipse.jgit.lfs.errors;version="5.3.3",
- org.eclipse.jgit.lfs.internal;version="5.3.3";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
- org.eclipse.jgit.lfs.lib;version="5.3.3"
+Export-Package: org.eclipse.jgit.lfs;version="5.4.0",
+ org.eclipse.jgit.lfs.errors;version="5.4.0",
+ org.eclipse.jgit.lfs.internal;version="5.4.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="5.4.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
  com.google.gson.stream;version="[2.8.2,3.0.0)",
  org.apache.http.impl.client;version="[4.2.6,5.0.0)",
  org.apache.http.impl.conn;version="[4.2.6,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.3.3,5.4.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.attributes;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.diff;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.hooks;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.pack;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util.io;version="[5.3.3,5.4.0)"
+ org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.attributes;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.diff;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.hooks;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.pack;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util.io;version="[5.4.0,5.5.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..7586720
--- /dev/null
+++ b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.lfs - Sources
+Bundle-SymbolicName: org.eclipse.jgit.lfs.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index b7d739f..3b6fe5c 100644
--- a/org.eclipse.jgit.lfs/pom.xml
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs</artifactId>
@@ -62,6 +62,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -94,6 +95,28 @@
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
         <inherited>true</inherited>
         <executions>
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
index 217e46f..80da802 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
@@ -78,14 +78,7 @@ public class CleanFilter extends FilterCommand {
 	 * The factory is responsible for creating instances of
 	 * {@link org.eclipse.jgit.lfs.CleanFilter}
 	 */
-	public final static FilterCommandFactory FACTORY = new FilterCommandFactory() {
-
-		@Override
-		public FilterCommand create(Repository db, InputStream in,
-				OutputStream out) throws IOException {
-			return new CleanFilter(db, in, out);
-		}
-	};
+	public final static FilterCommandFactory FACTORY = CleanFilter::new;
 
 	/**
 	 * Registers this filter by calling
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
index 7bacf49..6b30da3 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
@@ -92,13 +92,7 @@ public class SmudgeFilter extends FilterCommand {
 	 * The factory is responsible for creating instances of
 	 * {@link org.eclipse.jgit.lfs.SmudgeFilter}
 	 */
-	public final static FilterCommandFactory FACTORY = new FilterCommandFactory() {
-		@Override
-		public FilterCommand create(Repository db, InputStream in,
-				OutputStream out) throws IOException {
-			return new SmudgeFilter(db, in, out);
-		}
-	};
+	public final static FilterCommandFactory FACTORY = SmudgeFilter::new;
 
 	/**
 	 * Register this filter in JGit
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index 2551f34..1c65b27 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit"
       label="%featureName"
-      version="5.3.3.qualifier"
+      version="5.4.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index 68cb1cd..43b5c87 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index e85a302..ccf68c4 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.http.apache"
       label="%featureName"
-      version="5.3.3.qualifier"
+      version="5.4.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit"/>
+      <import plugin="org.eclipse.jgit" version="5.4.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index d9a7bb4..40a99e1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 3b42f10..34f0ac9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.junit"
       label="%featureName"
-      version="5.3.3.qualifier"
+      version="5.4.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -24,6 +24,7 @@
 
    <requires>
       <import plugin="com.jcraft.jsch"/>
+      <import plugin="org.eclipse.jgit" version="5.4.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -62,7 +63,7 @@
          unpack="false"/>
 
    <plugin
-         id="org.apache.sshd.core"
+         id="org.apache.sshd.osgi"
          download-size="0"
          install-size="0"
          version="0.0.0"
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index 817e3b6..52b7ce1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index db81687..62be3c8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.lfs"
       label="%featureName"
-      version="5.3.3.qualifier"
+      version="5.4.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit"/>
+      <import feature="org.eclipse.jgit" version="5.4.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 5e9fddb..29f010b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index 692af1e..92f0a24 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.pgm"
       label="%featureName"
-      version="5.3.3.qualifier"
+      version="5.4.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -30,10 +30,14 @@
          id="org.eclipse.jgit.lfs"
          version="0.0.0"/>
 
+   <includes
+         id="org.eclipse.jgit.ssh.apache"
+         version="0.0.0"/>
+
    <requires>
-      <import feature="org.eclipse.jgit" version="5.3.3" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="5.3.3" match="equivalent"/>
-      <import feature="org.eclipse.jgit.ssh.apache" version="5.3.3" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.4.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="5.4.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.ssh.apache" version="5.4.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index bffe350..fc1c951 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.gitignore
deleted file mode 100644
index eb5a316..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.project
deleted file mode 100644
index 5f57483..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>org.eclipse.jgit.pgm.source.feature</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.pde.FeatureBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.pde.FeatureNature</nature>
-	</natures>
-</projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 6f96ce8..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Fri Jun 18 23:33:45 CEST 2010

-eclipse.preferences.version=1

-encoding/<project>=UTF-8

diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 1a32dba..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Fri Jun 18 23:33:45 CEST 2010

-eclipse.preferences.version=1

-line.separator=\n

diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
deleted file mode 100644
index 823c0f5..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-eclipse.preferences.version=1
-project.repository.kind=bugzilla
-project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
deleted file mode 100644
index 2fca432..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description}\n\nBug\: ${task.key}
-eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/build.properties
deleted file mode 100644
index b4a8dde..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/build.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-bin.includes = feature.xml,\
-               edl-v10.html,\
-               feature.properties,\
-               license.html
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/edl-v10.html
deleted file mode 100644
index 8caddac..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/edl-v10.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
-<title>Eclipse Distribution License - Version 1.0</title>
-<style type="text/css">
-	body {
-		size: 8.5in 11.0in;
-		margin: 0.25in 0.5in 0.25in 0.5in;
-		tab-interval: 0.5in;
-	}
-	p {
-		margin-left: auto;
-		margin-top:  0.5em;
-		margin-bottom: 0.5em;
-	}
-	p.list {
-		margin-left: 0.5in;
-		margin-top:  0.05em;
-		margin-bottom: 0.05em;
-	}
-</style>
-
-</head>
-
-<body lang="EN-US">
-
-<p><b>Eclipse Distribution License - v 1.0</b></p>
-
-<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
-
-<p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification,
-	are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice,
-	this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice,
-	this list of conditions and the following disclaimer in the documentation
-	and/or other materials provided with the distribution. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
-	contributors may be used to endorse or promote products derived from
-	this software without specific prior written permission. </li></ul>
-</p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.</p>
-
-</body>
-
-</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties
deleted file mode 100644
index 73701b2..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties
+++ /dev/null
@@ -1,178 +0,0 @@
-###############################################################################
-# Copyright (c) 2000, 2010 IBM Corporation and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Eclipse Public License v1.0
-# which accompanies this distribution, and is available at
-# http://www.eclipse.org/legal/epl-v10.html
-#
-###############################################################################
-
-featureName=Java implementation of Git - Command Line Interface - Source Code
-providerName=Eclipse JGit
-
-updateSiteName=Eclipse JGit Update Site
-
-# description property - text of the "Feature Description"
-description=\
-Do not install in your IDE: this feature is meant to provision Target Platforms.\n\
-Source code for the support for PDE's JUnit runner for a Target Platform\n
-################ end of description property ##################################
-
-# "copyright" property - text of the "Feature Update Copyright"
-copyright=\
-Copyright (c) 2005, 2012 Shawn Pearce, Robin Rosenberg, et.al.\n\
-All rights reserved. This program and the accompanying materials\n\
-are made available under the terms of the Eclipse Distribution License v1.0\n\
-which accompanies this distribution, and is available at\n\
-http://www.eclipse.org/org/documents/edl-v10.html\n
-################ end of copyright property ####################################
-
-# "licenseURL" property - URL of the "Feature License"
-# do not translate value - just change to point to a locale-specific HTML page
-licenseURL=license.html
-
-# "license" property - text of the "Feature Update License"
-# should be plain text version of license agreement pointed to be "licenseURL"
-license=\
-Eclipse Foundation Software User Agreement\n\
-\n\
-November 22, 2017\n\
-\n\
-Usage Of Content\n\
-\n\
-THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION\n\
-AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT"). USE OF\n\
-THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE\n\
-TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
-BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED\n\
-BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE\n\
-AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
-TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS OF ANY\n\
-APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU\n\
-MAY NOT USE THE CONTENT.\n\
-\n\
-Applicable Licenses\n\
-\n\
-Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\
-is provided to you under the terms and conditions of the Eclipse Public License\n\
-Version 2.0 ("EPL"). A copy of the EPL is provided with this Content and is also\n\
-available at http://www.eclipse.org/legal/epl-2.0. For purposes of the EPL,\n\
-"Program" will mean the Content.\n\
-\n\
-Content includes, but is not limited to, source code, object code, documentation\n\
-and other files maintained in the Eclipse Foundation source code repository\n\
-("Repository") in software modules ("Modules") and made available as\n\
-downloadable archives ("Downloads").\n\
-\n\
--   Content may be structured and packaged into modules to facilitate\n\
-    delivering, extending, and upgrading the Content. Typical modules may\n\
-    include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and\n\
-    features ("Features").\n\
--   Each Plug-in or Fragment may be packaged as a sub-directory or JAR\n\
-    (Java™ ARchive) in a directory named "plugins".\n\
--   A Feature is a bundle of one or more Plug-ins and/or Fragments and\n\
-    associated material. Each Feature may be packaged as a sub-directory in a\n\
-    directory named "features". Within a Feature, files named "feature.xml" may\n\
-    contain a list of the names and version numbers of the Plug-ins and/or\n\
-    Fragments associated with that Feature.\n\
--   Features may also include other Features ("Included Features"). Within a\n\
-    Feature, files named "feature.xml" may contain a list of the names and\n\
-    version numbers of Included Features.\n\
-\n\
-The terms and conditions governing Plug-ins and Fragments should be contained in\n\
-files named "about.html" ("Abouts"). The terms and conditions governing Features\n\
-and Included Features should be contained in files named "license.html"\n\
-("Feature Licenses"). Abouts and Feature Licenses may be located in any\n\
-directory of a Download or Module including, but not limited to the following\n\
-locations:\n\
-\n\
--   The top-level (root) directory\n\
--   Plug-in and Fragment directories\n\
--   Inside Plug-ins and Fragments packaged as JARs\n\
--   Sub-directories of the directory named "src" of certain Plug-ins\n\
--   Feature directories\n\
-\n\
-Note: if a Feature made available by the Eclipse Foundation is installed using\n\
-the Provisioning Technology (as defined below), you must agree to a license\n\
-("Feature Update License") during the installation process. If the Feature\n\
-contains Included Features, the Feature Update License should either provide you\n\
-with the terms and conditions governing the Included Features or inform you\n\
-where you can locate them. Feature Update Licenses may be found in the "license"\n\
-property of files named "feature.properties" found within a Feature. Such\n\
-Abouts, Feature Licenses, and Feature Update Licenses contain the terms and\n\
-conditions (or references to such terms and conditions) that govern your use of\n\
-the associated Content in that directory.\n\
-\n\
-THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL\n\
-OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE\n\
-OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
-\n\
--   Eclipse Public License Version 1.0 (available at\n\
-    http://www.eclipse.org/legal/epl-v10.html)\n\
--   Eclipse Distribution License Version 1.0 (available at\n\
-    http://www.eclipse.org/licenses/edl-v1.0.html)\n\
--   Common Public License Version 1.0 (available at\n\
-    http://www.eclipse.org/legal/cpl-v10.html)\n\
--   Apache Software License 1.1 (available at\n\
-    http://www.apache.org/licenses/LICENSE)\n\
--   Apache Software License 2.0 (available at\n\
-    http://www.apache.org/licenses/LICENSE-2.0)\n\
--   Mozilla Public License Version 1.1 (available at\n\
-    http://www.mozilla.org/MPL/MPL-1.1.html)\n\
-\n\
-IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO\n\
-USE OF THE CONTENT. If no About, Feature License, or Feature Update License is\n\
-provided, please contact the Eclipse Foundation to determine what terms and\n\
-conditions govern that particular Content.\n\
-\n\
-Use of Provisioning Technology\n\
-\n\
-The Eclipse Foundation makes available provisioning software, examples of which\n\
-include, but are not limited to, p2 and the Eclipse Update Manager\n\
-("Provisioning Technology") for the purpose of allowing users to install\n\
-software, documentation, information and/or other materials (collectively\n\
-"Installable Software"). This capability is provided with the intent of allowing\n\
-such users to install, extend and update Eclipse-based products. Information\n\
-about packaging Installable Software is available at\n\
-http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
-\n\
-You may use Provisioning Technology to allow other parties to install\n\
-Installable Software. You shall be responsible for enabling the applicable\n\
-license agreements relating to the Installable Software to be presented to, and\n\
-accepted by, the users of the Provisioning Technology in accordance with the\n\
-Specification. By using Provisioning Technology in such a manner and making it\n\
-available in accordance with the Specification, you further acknowledge your\n\
-agreement to, and the acquisition of all necessary rights to permit the\n\
-following:\n\
-\n\
-1.  A series of actions may occur ("Provisioning Process") in which a user may\n\
-    execute the Provisioning Technology on a machine ("Target Machine") with the\n\
-    intent of installing, extending or updating the functionality of an\n\
-    Eclipse-based product.\n\
-2.  During the Provisioning Process, the Provisioning Technology may cause third\n\
-    party Installable Software or a portion thereof to be accessed and copied to\n\
-    the Target Machine.\n\
-3.  Pursuant to the Specification, you will provide to the user the terms and\n\
-    conditions that govern the use of the Installable Software ("Installable\n\
-    Software Agreement") and such Installable Software Agreement shall be\n\
-    accessed from the Target Machine in accordance with the Specification. Such\n\
-    Installable Software Agreement must inform the user of the terms and\n\
-    conditions that govern the Installable Software and must solicit acceptance\n\
-    by the end user in the manner prescribed in such Installable\n\
-    Software Agreement. Upon such indication of agreement by the user, the\n\
-    provisioning Technology will complete installation of the\n\
-    Installable Software.\n\
-\n\
-Cryptography\n\
-\n\
-Content may contain encryption software. The country in which you are currently\n\
-may have restrictions on the import, possession, and use, and/or re-export to\n\
-another country, of encryption software. BEFORE using any encryption software,\n\
-please check the country's laws, regulations and policies concerning the import,\n\
-possession, or use, and re-export of encryption software, to see if this is\n\
-permitted.\n\
-\n\
-Java and all Java-based trademarks are trademarks of Oracle Corporation in the\n\
-United States, other countries, or both.\n
-########### end of license property ##########################################
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
deleted file mode 100644
index 075aea1..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<feature
-      id="org.eclipse.jgit.pgm.source"
-      label="%featureName"
-      version="5.3.3.qualifier"
-      provider-name="%providerName">
-
-   <description url="http://www.eclipse.org/jgit/">
-     %description
-   </description>
-
-   <copyright>
-     %copyright
-   </copyright>
-
-   <license url="%licenseURL">
-     %license
-   </license>
-
-   <url>
-      <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
-      <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
-   </url>
-
-   <plugin
-         id="org.eclipse.jgit.pgm.source"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/license.html
deleted file mode 100644
index 008b801..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/license.html
+++ /dev/null
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
-<title>Eclipse Foundation Software User Agreement</title>
-</head>
-
-<body lang="EN-US">
-	<h2>Eclipse Foundation Software User Agreement</h2>
-	<p>November 22, 2017</p>
-
-	<h3>Usage Of Content</h3>
-
-	<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION,
-		INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
-		(COLLECTIVELY &quot;CONTENT&quot;). USE OF THE CONTENT IS GOVERNED BY
-		THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
-		CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
-		BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS
-		GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY
-		APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
-		BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS
-		AGREEMENT AND THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE
-		AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT
-		USE THE CONTENT.</p>
-
-	<h3>Applicable Licenses</h3>
-
-	<p>
-		Unless otherwise indicated, all Content made available by the Eclipse
-		Foundation is provided to you under the terms and conditions of the
-		Eclipse Public License Version 2.0 (&quot;EPL&quot;). A copy of the
-		EPL is provided with this Content and is also available at <a
-			href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
-		For purposes of the EPL, &quot;Program&quot; will mean the Content.
-	</p>
-
-	<p>Content includes, but is not limited to, source code, object
-		code, documentation and other files maintained in the Eclipse
-		Foundation source code repository (&quot;Repository&quot;) in software
-		modules (&quot;Modules&quot;) and made available as downloadable
-		archives (&quot;Downloads&quot;).</p>
-
-	<ul>
-		<li>Content may be structured and packaged into modules to
-			facilitate delivering, extending, and upgrading the Content. Typical
-			modules may include plug-ins (&quot;Plug-ins&quot;), plug-in
-			fragments (&quot;Fragments&quot;), and features
-			(&quot;Features&quot;).</li>
-		<li>Each Plug-in or Fragment may be packaged as a sub-directory
-			or JAR (Java&trade; ARchive) in a directory named
-			&quot;plugins&quot;.</li>
-		<li>A Feature is a bundle of one or more Plug-ins and/or
-			Fragments and associated material. Each Feature may be packaged as a
-			sub-directory in a directory named &quot;features&quot;. Within a
-			Feature, files named &quot;feature.xml&quot; may contain a list of
-			the names and version numbers of the Plug-ins and/or Fragments
-			associated with that Feature.</li>
-		<li>Features may also include other Features (&quot;Included
-			Features&quot;). Within a Feature, files named
-			&quot;feature.xml&quot; may contain a list of the names and version
-			numbers of Included Features.</li>
-	</ul>
-
-	<p>The terms and conditions governing Plug-ins and Fragments should
-		be contained in files named &quot;about.html&quot;
-		(&quot;Abouts&quot;). The terms and conditions governing Features and
-		Included Features should be contained in files named
-		&quot;license.html&quot; (&quot;Feature Licenses&quot;). Abouts and
-		Feature Licenses may be located in any directory of a Download or
-		Module including, but not limited to the following locations:</p>
-
-	<ul>
-		<li>The top-level (root) directory</li>
-		<li>Plug-in and Fragment directories</li>
-		<li>Inside Plug-ins and Fragments packaged as JARs</li>
-		<li>Sub-directories of the directory named &quot;src&quot; of
-			certain Plug-ins</li>
-		<li>Feature directories</li>
-	</ul>
-
-	<p>Note: if a Feature made available by the Eclipse Foundation is
-		installed using the Provisioning Technology (as defined below), you
-		must agree to a license (&quot;Feature Update License&quot;) during
-		the installation process. If the Feature contains Included Features,
-		the Feature Update License should either provide you with the terms
-		and conditions governing the Included Features or inform you where you
-		can locate them. Feature Update Licenses may be found in the
-		&quot;license&quot; property of files named
-		&quot;feature.properties&quot; found within a Feature. Such Abouts,
-		Feature Licenses, and Feature Update Licenses contain the terms and
-		conditions (or references to such terms and conditions) that govern
-		your use of the associated Content in that directory.</p>
-
-	<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY
-		REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND
-		CONDITIONS. SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT
-		ARE NOT LIMITED TO):</p>
-
-	<ul>
-		<li>Eclipse Public License Version 1.0 (available at <a
-			href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>)
-		</li>
-		<li>Eclipse Distribution License Version 1.0 (available at <a
-			href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)
-		</li>
-		<li>Common Public License Version 1.0 (available at <a
-			href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)
-		</li>
-		<li>Apache Software License 1.1 (available at <a
-			href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)
-		</li>
-		<li>Apache Software License 2.0 (available at <a
-			href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)
-		</li>
-		<li>Mozilla Public License Version 1.1 (available at <a
-			href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)
-		</li>
-	</ul>
-
-	<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND
-		CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License,
-		or Feature Update License is provided, please contact the Eclipse
-		Foundation to determine what terms and conditions govern that
-		particular Content.</p>
-
-
-	<h3>Use of Provisioning Technology</h3>
-
-	<p>
-		The Eclipse Foundation makes available provisioning software, examples
-		of which include, but are not limited to, p2 and the Eclipse Update
-		Manager (&quot;Provisioning Technology&quot;) for the purpose of
-		allowing users to install software, documentation, information and/or
-		other materials (collectively &quot;Installable Software&quot;). This
-		capability is provided with the intent of allowing such users to
-		install, extend and update Eclipse-based products. Information about
-		packaging Installable Software is available at <a
-			href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
-		(&quot;Specification&quot;).
-	</p>
-
-	<p>You may use Provisioning Technology to allow other parties to
-		install Installable Software. You shall be responsible for enabling
-		the applicable license agreements relating to the Installable Software
-		to be presented to, and accepted by, the users of the Provisioning
-		Technology in accordance with the Specification. By using Provisioning
-		Technology in such a manner and making it available in accordance with
-		the Specification, you further acknowledge your agreement to, and the
-		acquisition of all necessary rights to permit the following:</p>
-
-	<ol>
-		<li>A series of actions may occur (&quot;Provisioning
-			Process&quot;) in which a user may execute the Provisioning
-			Technology on a machine (&quot;Target Machine&quot;) with the intent
-			of installing, extending or updating the functionality of an
-			Eclipse-based product.</li>
-		<li>During the Provisioning Process, the Provisioning Technology
-			may cause third party Installable Software or a portion thereof to be
-			accessed and copied to the Target Machine.</li>
-		<li>Pursuant to the Specification, you will provide to the user
-			the terms and conditions that govern the use of the Installable
-			Software (&quot;Installable Software Agreement&quot;) and such
-			Installable Software Agreement shall be accessed from the Target
-			Machine in accordance with the Specification. Such Installable
-			Software Agreement must inform the user of the terms and conditions
-			that govern the Installable Software and must solicit acceptance by
-			the end user in the manner prescribed in such Installable Software
-			Agreement. Upon such indication of agreement by the user, the
-			provisioning Technology will complete installation of the Installable
-			Software.</li>
-	</ol>
-
-	<h3>Cryptography</h3>
-
-	<p>Content may contain encryption software. The country in which
-		you are currently may have restrictions on the import, possession, and
-		use, and/or re-export to another country, of encryption software.
-		BEFORE using any encryption software, please check the country's laws,
-		regulations and policies concerning the import, possession, or use,
-		and re-export of encryption software, to see if this is permitted.</p>
-
-	<p>
-		<small>Java and all Java-based trademarks are trademarks of
-			Oracle Corporation in the United States, other countries, or both.</small>
-	</p>
-</body>
-</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
deleted file mode 100644
index 605a23a..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   Copyright (C) 2012, Matthias Sohn <matthias.sohn@sap.com>
-   and other copyright owners as documented in the project's IP log.
-
-   This program and the accompanying materials are made available
-   under the terms of the Eclipse Distribution License v1.0 which
-   accompanies this distribution, is reproduced below, and is
-   available at http://www.eclipse.org/org/documents/edl-v10.php
-
-   All rights reserved.
-
-   Redistribution and use in source and binary forms, with or
-   without modification, are permitted provided that the following
-   conditions are met:
-
-   - Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-
-   - Redistributions in binary form must reproduce the above
-     copyright notice, this list of conditions and the following
-     disclaimer in the documentation and/or other materials provided
-     with the distribution.
-
-   - Neither the name of the Eclipse Foundation, Inc. nor the
-     names of its contributors may be used to endorse or promote
-     products derived from this software without specific prior
-     written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.eclipse.jgit</groupId>
-    <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
-  </parent>
-
-  <groupId>org.eclipse.jgit.feature</groupId>
-  <artifactId>org.eclipse.jgit.pgm.source</artifactId>
-  <packaging>eclipse-feature</packaging>
-
-  <name>JGit Command Line Interface Source Feature</name>
-
-</project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index 995bd21..e2aeee3 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -15,24 +15,32 @@
    <feature url="features/org.eclipse.jgit.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.source" version="0.0.0" patch="true">
       <category name="JGit"/>
    </feature>
-   <feature url="features/org.eclipse.jgit.pgm.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.pgm.source" version="0.0.0" patch="true">
-      <category name="JGit"/>
-   </feature>
-   <feature url="features/org.eclipse.jgit.ssh.apache.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.ssh.apache.source" version="0.0.0" patch="true">
-      <category name="JGit"/>
-   </feature>
    <feature url="features/org.eclipse.jgit.junit_0.0.0.qualifier.jar" id="org.eclipse.jgit.junit" version="0.0.0" patch="true">
       <category name="JGit"/>
    </feature>
    <feature url="features/org.eclipse.jgit.http.apache_0.0.0.qualifier.jar" id="org.eclipse.jgit.http.apache" version="0.0.0" patch="true">
       <category name="JGit"/>
    </feature>
-   <feature url="features/org.eclipse.jgit.lfs_0.0.0.qualifier.jar" id="org.eclipse.jgit.lfs" version="0.0.0">
+   <feature url="features/org.eclipse.jgit.lfs_0.0.0.qualifier.jar" id="org.eclipse.jgit.lfs" version="0.0.0" patch="true">
       <category name="JGit"/>
    </feature>
+   <bundle id="org.eclipse.jgit.ant" version="0.0.0">
+      <category name="JGit-additional-bundles"/>
+   </bundle>
+   <bundle id="org.eclipse.jgit.archive" version="0.0.0">
+      <category name="JGit-additional-bundles"/>
+   </bundle>
+   <bundle id="org.eclipse.jgit.ui" version="0.0.0">
+      <category name="JGit-additional-bundles"/>
+   </bundle>
    <category-def name="JGit" label="Java implementation of Git">
       <description>
          Java implementation of Git
       </description>
    </category-def>
+   <category-def name="JGit-additional-bundles" label="Java implementation of Git - additional bundles">
+      <description>
+         Java implementation of Git - additional bundles
+      </description>
+   </category-def>
 </site>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index dd9dba7..a2736d4 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
@@ -64,24 +64,24 @@
       <artifactId>org.eclipse.jgit</artifactId>
       <version>${project.version}</version>
     </dependency>
-	<dependency>
-	  <groupId>org.eclipse.jgit</groupId>
-	  <artifactId>org.eclipse.jgit.http.apache</artifactId>
-	  <version>${project.version}</version>
-	</dependency>
-    <dependency>
-	  <groupId>org.eclipse.jgit</groupId>
-	  <artifactId>org.eclipse.jgit.lfs</artifactId>
-	  <version>${project.version}</version>
-	</dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
-      <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+      <artifactId>org.eclipse.jgit.ant</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
-      <artifactId>org.eclipse.jgit.pgm</artifactId>
+      <artifactId>org.eclipse.jgit.archive</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.http.apache</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.http.server</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
@@ -101,7 +101,17 @@
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
-      <artifactId>org.eclipse.jgit.http.server</artifactId>
+      <artifactId>org.eclipse.jgit.lfs</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.pgm</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
@@ -109,5 +119,10 @@
       <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.ui</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index 68c5efa..2a6deb2 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -2,19 +2,19 @@
 <feature
       id="org.eclipse.jgit.source"
       label="%featureName"
-      version="5.3.3.qualifier"
+      version="5.4.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
-     %description
+      %description
    </description>
 
    <copyright>
-     %copyright
+      %copyright
    </copyright>
 
    <license url="%licenseURL">
-     %license
+      %license
    </license>
 
    <url>
@@ -22,10 +22,99 @@
       <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
    </url>
 
+   <requires>
+      <import feature="org.eclipse.jgit" version="5.4.0" match="equivalent"/>
+   </requires>
+
    <plugin
          id="org.eclipse.jgit.source"
          download-size="0"
          install-size="0"
          version="0.0.0"
          unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.ant.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.archive.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.http.apache.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.http.server.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.junit.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.junit.http.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.junit.ssh.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.lfs.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.lfs.server.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.pgm.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.ssh.apache.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.ui.source"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index d260f83..b156487 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
@@ -59,4 +59,12 @@
 
   <name>JGit Source Feature</name>
 
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jgit.feature</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>5.4.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+
 </project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
index 7a957df..a84d590 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.ssh.apache"
       label="%featureName"
-      version="5.3.3.qualifier"
+      version="5.4.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit"/>
+      <import feature="org.eclipse.jgit" version="5.4.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -34,7 +34,7 @@
          unpack="false"/>
 
    <plugin
-         id="org.apache.sshd.core"
+         id="org.apache.sshd.osgi"
          download-size="0"
          install-size="0"
          version="0.0.0"
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
index fb549a2..fcc2eb6 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore
deleted file mode 100644
index eb5a316..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project
deleted file mode 100644
index 5562084..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>org.eclipse.jgit.ssh.apache.source.feature</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.pde.FeatureBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.pde.FeatureNature</nature>
-	</natures>
-</projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 6f96ce8..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Fri Jun 18 23:33:45 CEST 2010

-eclipse.preferences.version=1

-encoding/<project>=UTF-8

diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 1a32dba..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Fri Jun 18 23:33:45 CEST 2010

-eclipse.preferences.version=1

-line.separator=\n

diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
deleted file mode 100644
index 823c0f5..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-eclipse.preferences.version=1
-project.repository.kind=bugzilla
-project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
deleted file mode 100644
index 2fca432..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description}\n\nBug\: ${task.key}
-eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties
deleted file mode 100644
index b4a8dde..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-bin.includes = feature.xml,\
-               edl-v10.html,\
-               feature.properties,\
-               license.html
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html
deleted file mode 100644
index 8caddac..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
-<title>Eclipse Distribution License - Version 1.0</title>
-<style type="text/css">
-	body {
-		size: 8.5in 11.0in;
-		margin: 0.25in 0.5in 0.25in 0.5in;
-		tab-interval: 0.5in;
-	}
-	p {
-		margin-left: auto;
-		margin-top:  0.5em;
-		margin-bottom: 0.5em;
-	}
-	p.list {
-		margin-left: 0.5in;
-		margin-top:  0.05em;
-		margin-bottom: 0.05em;
-	}
-</style>
-
-</head>
-
-<body lang="EN-US">
-
-<p><b>Eclipse Distribution License - v 1.0</b></p>
-
-<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
-
-<p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification,
-	are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice,
-	this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice,
-	this list of conditions and the following disclaimer in the documentation
-	and/or other materials provided with the distribution. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
-	contributors may be used to endorse or promote products derived from
-	this software without specific prior written permission. </li></ul>
-</p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.</p>
-
-</body>
-
-</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties
deleted file mode 100644
index 9935431..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties
+++ /dev/null
@@ -1,178 +0,0 @@
-###############################################################################
-# Copyright (c) 2000, 2010 IBM Corporation and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Eclipse Public License v1.0
-# which accompanies this distribution, and is available at
-# http://www.eclipse.org/legal/epl-v10.html
-#
-###############################################################################
-
-featureName=Java implementation of Git - ssh support using Apache MINA sshd - Source Code
-providerName=Eclipse JGit
-
-updateSiteName=Eclipse JGit Update Site
-
-# description property - text of the "Feature Description"
-description=\
-Do not install in your IDE: this feature is meant to provision Target Platforms.\n\
-Source code for the JGit ssh support using Apache MINA sshd.\n
-################ end of description property ##################################
-
-# "copyright" property - text of the "Feature Update Copyright"
-copyright=\
-Copyright (c) 2018 Thomas Wolf and others\n\
-All rights reserved. This program and the accompanying materials\n\
-are made available under the terms of the Eclipse Distribution License v1.0\n\
-which accompanies this distribution, and is available at\n\
-http://www.eclipse.org/org/documents/edl-v10.html\n
-################ end of copyright property ####################################
-
-# "licenseURL" property - URL of the "Feature License"
-# do not translate value - just change to point to a locale-specific HTML page
-licenseURL=license.html
-
-# "license" property - text of the "Feature Update License"
-# should be plain text version of license agreement pointed to be "licenseURL"
-license=\
-Eclipse Foundation Software User Agreement\n\
-\n\
-November 22, 2017\n\
-\n\
-Usage Of Content\n\
-\n\
-THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION\n\
-AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT"). USE OF\n\
-THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE\n\
-TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
-BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED\n\
-BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE\n\
-AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
-TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS OF ANY\n\
-APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU\n\
-MAY NOT USE THE CONTENT.\n\
-\n\
-Applicable Licenses\n\
-\n\
-Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\
-is provided to you under the terms and conditions of the Eclipse Public License\n\
-Version 2.0 ("EPL"). A copy of the EPL is provided with this Content and is also\n\
-available at http://www.eclipse.org/legal/epl-2.0. For purposes of the EPL,\n\
-"Program" will mean the Content.\n\
-\n\
-Content includes, but is not limited to, source code, object code, documentation\n\
-and other files maintained in the Eclipse Foundation source code repository\n\
-("Repository") in software modules ("Modules") and made available as\n\
-downloadable archives ("Downloads").\n\
-\n\
--   Content may be structured and packaged into modules to facilitate\n\
-    delivering, extending, and upgrading the Content. Typical modules may\n\
-    include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and\n\
-    features ("Features").\n\
--   Each Plug-in or Fragment may be packaged as a sub-directory or JAR\n\
-    (Java™ ARchive) in a directory named "plugins".\n\
--   A Feature is a bundle of one or more Plug-ins and/or Fragments and\n\
-    associated material. Each Feature may be packaged as a sub-directory in a\n\
-    directory named "features". Within a Feature, files named "feature.xml" may\n\
-    contain a list of the names and version numbers of the Plug-ins and/or\n\
-    Fragments associated with that Feature.\n\
--   Features may also include other Features ("Included Features"). Within a\n\
-    Feature, files named "feature.xml" may contain a list of the names and\n\
-    version numbers of Included Features.\n\
-\n\
-The terms and conditions governing Plug-ins and Fragments should be contained in\n\
-files named "about.html" ("Abouts"). The terms and conditions governing Features\n\
-and Included Features should be contained in files named "license.html"\n\
-("Feature Licenses"). Abouts and Feature Licenses may be located in any\n\
-directory of a Download or Module including, but not limited to the following\n\
-locations:\n\
-\n\
--   The top-level (root) directory\n\
--   Plug-in and Fragment directories\n\
--   Inside Plug-ins and Fragments packaged as JARs\n\
--   Sub-directories of the directory named "src" of certain Plug-ins\n\
--   Feature directories\n\
-\n\
-Note: if a Feature made available by the Eclipse Foundation is installed using\n\
-the Provisioning Technology (as defined below), you must agree to a license\n\
-("Feature Update License") during the installation process. If the Feature\n\
-contains Included Features, the Feature Update License should either provide you\n\
-with the terms and conditions governing the Included Features or inform you\n\
-where you can locate them. Feature Update Licenses may be found in the "license"\n\
-property of files named "feature.properties" found within a Feature. Such\n\
-Abouts, Feature Licenses, and Feature Update Licenses contain the terms and\n\
-conditions (or references to such terms and conditions) that govern your use of\n\
-the associated Content in that directory.\n\
-\n\
-THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL\n\
-OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE\n\
-OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
-\n\
--   Eclipse Public License Version 1.0 (available at\n\
-    http://www.eclipse.org/legal/epl-v10.html)\n\
--   Eclipse Distribution License Version 1.0 (available at\n\
-    http://www.eclipse.org/licenses/edl-v1.0.html)\n\
--   Common Public License Version 1.0 (available at\n\
-    http://www.eclipse.org/legal/cpl-v10.html)\n\
--   Apache Software License 1.1 (available at\n\
-    http://www.apache.org/licenses/LICENSE)\n\
--   Apache Software License 2.0 (available at\n\
-    http://www.apache.org/licenses/LICENSE-2.0)\n\
--   Mozilla Public License Version 1.1 (available at\n\
-    http://www.mozilla.org/MPL/MPL-1.1.html)\n\
-\n\
-IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO\n\
-USE OF THE CONTENT. If no About, Feature License, or Feature Update License is\n\
-provided, please contact the Eclipse Foundation to determine what terms and\n\
-conditions govern that particular Content.\n\
-\n\
-Use of Provisioning Technology\n\
-\n\
-The Eclipse Foundation makes available provisioning software, examples of which\n\
-include, but are not limited to, p2 and the Eclipse Update Manager\n\
-("Provisioning Technology") for the purpose of allowing users to install\n\
-software, documentation, information and/or other materials (collectively\n\
-"Installable Software"). This capability is provided with the intent of allowing\n\
-such users to install, extend and update Eclipse-based products. Information\n\
-about packaging Installable Software is available at\n\
-http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
-\n\
-You may use Provisioning Technology to allow other parties to install\n\
-Installable Software. You shall be responsible for enabling the applicable\n\
-license agreements relating to the Installable Software to be presented to, and\n\
-accepted by, the users of the Provisioning Technology in accordance with the\n\
-Specification. By using Provisioning Technology in such a manner and making it\n\
-available in accordance with the Specification, you further acknowledge your\n\
-agreement to, and the acquisition of all necessary rights to permit the\n\
-following:\n\
-\n\
-1.  A series of actions may occur ("Provisioning Process") in which a user may\n\
-    execute the Provisioning Technology on a machine ("Target Machine") with the\n\
-    intent of installing, extending or updating the functionality of an\n\
-    Eclipse-based product.\n\
-2.  During the Provisioning Process, the Provisioning Technology may cause third\n\
-    party Installable Software or a portion thereof to be accessed and copied to\n\
-    the Target Machine.\n\
-3.  Pursuant to the Specification, you will provide to the user the terms and\n\
-    conditions that govern the use of the Installable Software ("Installable\n\
-    Software Agreement") and such Installable Software Agreement shall be\n\
-    accessed from the Target Machine in accordance with the Specification. Such\n\
-    Installable Software Agreement must inform the user of the terms and\n\
-    conditions that govern the Installable Software and must solicit acceptance\n\
-    by the end user in the manner prescribed in such Installable\n\
-    Software Agreement. Upon such indication of agreement by the user, the\n\
-    provisioning Technology will complete installation of the\n\
-    Installable Software.\n\
-\n\
-Cryptography\n\
-\n\
-Content may contain encryption software. The country in which you are currently\n\
-may have restrictions on the import, possession, and use, and/or re-export to\n\
-another country, of encryption software. BEFORE using any encryption software,\n\
-please check the country's laws, regulations and policies concerning the import,\n\
-possession, or use, and re-export of encryption software, to see if this is\n\
-permitted.\n\
-\n\
-Java and all Java-based trademarks are trademarks of Oracle Corporation in the\n\
-United States, other countries, or both.\n
-########### end of license property ##########################################
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml
deleted file mode 100644
index e0ef793..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<feature
-      id="org.eclipse.jgit.ssh.apache.source"
-      label="%featureName"
-      version="5.3.3.qualifier"
-      provider-name="%providerName">
-
-   <description url="http://www.eclipse.org/jgit/">
-     %description
-   </description>
-
-   <copyright>
-     %copyright
-   </copyright>
-
-   <license url="%licenseURL">
-     %license
-   </license>
-
-   <url>
-      <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
-      <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
-   </url>
-
-   <plugin
-         id="org.eclipse.jgit.ssh.apache.source"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html
deleted file mode 100644
index 008b801..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html
+++ /dev/null
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
-<title>Eclipse Foundation Software User Agreement</title>
-</head>
-
-<body lang="EN-US">
-	<h2>Eclipse Foundation Software User Agreement</h2>
-	<p>November 22, 2017</p>
-
-	<h3>Usage Of Content</h3>
-
-	<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION,
-		INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
-		(COLLECTIVELY &quot;CONTENT&quot;). USE OF THE CONTENT IS GOVERNED BY
-		THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
-		CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
-		BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS
-		GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY
-		APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED
-		BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS
-		AGREEMENT AND THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE
-		AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT
-		USE THE CONTENT.</p>
-
-	<h3>Applicable Licenses</h3>
-
-	<p>
-		Unless otherwise indicated, all Content made available by the Eclipse
-		Foundation is provided to you under the terms and conditions of the
-		Eclipse Public License Version 2.0 (&quot;EPL&quot;). A copy of the
-		EPL is provided with this Content and is also available at <a
-			href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
-		For purposes of the EPL, &quot;Program&quot; will mean the Content.
-	</p>
-
-	<p>Content includes, but is not limited to, source code, object
-		code, documentation and other files maintained in the Eclipse
-		Foundation source code repository (&quot;Repository&quot;) in software
-		modules (&quot;Modules&quot;) and made available as downloadable
-		archives (&quot;Downloads&quot;).</p>
-
-	<ul>
-		<li>Content may be structured and packaged into modules to
-			facilitate delivering, extending, and upgrading the Content. Typical
-			modules may include plug-ins (&quot;Plug-ins&quot;), plug-in
-			fragments (&quot;Fragments&quot;), and features
-			(&quot;Features&quot;).</li>
-		<li>Each Plug-in or Fragment may be packaged as a sub-directory
-			or JAR (Java&trade; ARchive) in a directory named
-			&quot;plugins&quot;.</li>
-		<li>A Feature is a bundle of one or more Plug-ins and/or
-			Fragments and associated material. Each Feature may be packaged as a
-			sub-directory in a directory named &quot;features&quot;. Within a
-			Feature, files named &quot;feature.xml&quot; may contain a list of
-			the names and version numbers of the Plug-ins and/or Fragments
-			associated with that Feature.</li>
-		<li>Features may also include other Features (&quot;Included
-			Features&quot;). Within a Feature, files named
-			&quot;feature.xml&quot; may contain a list of the names and version
-			numbers of Included Features.</li>
-	</ul>
-
-	<p>The terms and conditions governing Plug-ins and Fragments should
-		be contained in files named &quot;about.html&quot;
-		(&quot;Abouts&quot;). The terms and conditions governing Features and
-		Included Features should be contained in files named
-		&quot;license.html&quot; (&quot;Feature Licenses&quot;). Abouts and
-		Feature Licenses may be located in any directory of a Download or
-		Module including, but not limited to the following locations:</p>
-
-	<ul>
-		<li>The top-level (root) directory</li>
-		<li>Plug-in and Fragment directories</li>
-		<li>Inside Plug-ins and Fragments packaged as JARs</li>
-		<li>Sub-directories of the directory named &quot;src&quot; of
-			certain Plug-ins</li>
-		<li>Feature directories</li>
-	</ul>
-
-	<p>Note: if a Feature made available by the Eclipse Foundation is
-		installed using the Provisioning Technology (as defined below), you
-		must agree to a license (&quot;Feature Update License&quot;) during
-		the installation process. If the Feature contains Included Features,
-		the Feature Update License should either provide you with the terms
-		and conditions governing the Included Features or inform you where you
-		can locate them. Feature Update Licenses may be found in the
-		&quot;license&quot; property of files named
-		&quot;feature.properties&quot; found within a Feature. Such Abouts,
-		Feature Licenses, and Feature Update Licenses contain the terms and
-		conditions (or references to such terms and conditions) that govern
-		your use of the associated Content in that directory.</p>
-
-	<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY
-		REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND
-		CONDITIONS. SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT
-		ARE NOT LIMITED TO):</p>
-
-	<ul>
-		<li>Eclipse Public License Version 1.0 (available at <a
-			href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>)
-		</li>
-		<li>Eclipse Distribution License Version 1.0 (available at <a
-			href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)
-		</li>
-		<li>Common Public License Version 1.0 (available at <a
-			href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)
-		</li>
-		<li>Apache Software License 1.1 (available at <a
-			href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)
-		</li>
-		<li>Apache Software License 2.0 (available at <a
-			href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)
-		</li>
-		<li>Mozilla Public License Version 1.1 (available at <a
-			href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)
-		</li>
-	</ul>
-
-	<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND
-		CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License,
-		or Feature Update License is provided, please contact the Eclipse
-		Foundation to determine what terms and conditions govern that
-		particular Content.</p>
-
-
-	<h3>Use of Provisioning Technology</h3>
-
-	<p>
-		The Eclipse Foundation makes available provisioning software, examples
-		of which include, but are not limited to, p2 and the Eclipse Update
-		Manager (&quot;Provisioning Technology&quot;) for the purpose of
-		allowing users to install software, documentation, information and/or
-		other materials (collectively &quot;Installable Software&quot;). This
-		capability is provided with the intent of allowing such users to
-		install, extend and update Eclipse-based products. Information about
-		packaging Installable Software is available at <a
-			href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
-		(&quot;Specification&quot;).
-	</p>
-
-	<p>You may use Provisioning Technology to allow other parties to
-		install Installable Software. You shall be responsible for enabling
-		the applicable license agreements relating to the Installable Software
-		to be presented to, and accepted by, the users of the Provisioning
-		Technology in accordance with the Specification. By using Provisioning
-		Technology in such a manner and making it available in accordance with
-		the Specification, you further acknowledge your agreement to, and the
-		acquisition of all necessary rights to permit the following:</p>
-
-	<ol>
-		<li>A series of actions may occur (&quot;Provisioning
-			Process&quot;) in which a user may execute the Provisioning
-			Technology on a machine (&quot;Target Machine&quot;) with the intent
-			of installing, extending or updating the functionality of an
-			Eclipse-based product.</li>
-		<li>During the Provisioning Process, the Provisioning Technology
-			may cause third party Installable Software or a portion thereof to be
-			accessed and copied to the Target Machine.</li>
-		<li>Pursuant to the Specification, you will provide to the user
-			the terms and conditions that govern the use of the Installable
-			Software (&quot;Installable Software Agreement&quot;) and such
-			Installable Software Agreement shall be accessed from the Target
-			Machine in accordance with the Specification. Such Installable
-			Software Agreement must inform the user of the terms and conditions
-			that govern the Installable Software and must solicit acceptance by
-			the end user in the manner prescribed in such Installable Software
-			Agreement. Upon such indication of agreement by the user, the
-			provisioning Technology will complete installation of the Installable
-			Software.</li>
-	</ol>
-
-	<h3>Cryptography</h3>
-
-	<p>Content may contain encryption software. The country in which
-		you are currently may have restrictions on the import, possession, and
-		use, and/or re-export to another country, of encryption software.
-		BEFORE using any encryption software, please check the country's laws,
-		regulations and policies concerning the import, possession, or use,
-		and re-export of encryption software, to see if this is permitted.</p>
-
-	<p>
-		<small>Java and all Java-based trademarks are trademarks of
-			Oracle Corporation in the United States, other countries, or both.</small>
-	</p>
-</body>
-</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml
deleted file mode 100644
index 1ac70b0..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   Copyright (C) 2018 Thomas Wolf <thomas.wolf@paranor.ch>
-   and other copyright owners as documented in the project's IP log.
-
-   This program and the accompanying materials are made available
-   under the terms of the Eclipse Distribution License v1.0 which
-   accompanies this distribution, is reproduced below, and is
-   available at http://www.eclipse.org/org/documents/edl-v10.php
-
-   All rights reserved.
-
-   Redistribution and use in source and binary forms, with or
-   without modification, are permitted provided that the following
-   conditions are met:
-
-   - Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-
-   - Redistributions in binary form must reproduce the above
-     copyright notice, this list of conditions and the following
-     disclaimer in the documentation and/or other materials provided
-     with the distribution.
-
-   - Neither the name of the Eclipse Foundation, Inc. nor the
-     names of its contributors may be used to endorse or promote
-     products derived from this software without specific prior
-     written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.eclipse.jgit</groupId>
-    <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
-  </parent>
-
-  <groupId>org.eclipse.jgit.feature</groupId>
-  <artifactId>org.eclipse.jgit.ssh.apache.source</artifactId>
-  <packaging>eclipse-feature</packaging>
-
-  <name>JGit Apache MINA ssh Source Feature</name>
-
-</project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.classpath b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.classpath
deleted file mode 100644
index eca7bdb..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.classpath
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.project
index 48c2bd1..ed148da 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.project
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.project
@@ -10,14 +10,8 @@
 			<arguments>
 			</arguments>
 		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.pde.ManifestBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
 	</buildSpec>
 	<natures>
-		<nature>org.eclipse.pde.PluginNature</nature>
 		<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
 	</natures>
 </projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
deleted file mode 100644
index f28abfa..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,5 +0,0 @@
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Bundle-Name: JGit Target Platform Bundle
-Bundle-SymbolicName: org.eclipse.jgit.target
-Bundle-Version: 5.3.3.qualifier
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/build.properties
deleted file mode 100644
index 34d2e4d..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/build.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
-               .
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
index 1a6a0fc..440092a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
@@ -1,6 +1,9 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><?pde?><!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --><target name="jgit-4.10" sequenceNumber="1551829107">
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.10" sequenceNumber="1558537043">
   <locations>
-    <location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="slicer" includeSource="true" type="InstallableUnit">
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.14.v20181114"/>
       <unit id="org.eclipse.jetty.client.source" version="9.4.14.v20181114"/>
       <unit id="org.eclipse.jetty.continuation" version="9.4.14.v20181114"/>
@@ -19,17 +22,17 @@
       <unit id="org.eclipse.jetty.util.source" version="9.4.14.v20181114"/>
       <repository id="jetty-9.4.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.14.v20181114"/>
     </location>
-    <location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="slicer" includeSource="true" type="InstallableUnit">
-      <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.apache.ant" version="1.10.6.v20190516-0412"/>
+      <unit id="org.apache.ant.source" version="1.10.6.v20190516-0412"/>
       <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
       <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190213-1430"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190213-1430"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190503-0009"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190503-0009"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
@@ -59,8 +62,8 @@
       <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
       <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
-      <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
@@ -75,15 +78,15 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
       <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20190226160451/repository"/>
+      <unit id="org.apache.sshd.osgi" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.2.0.v20190425-2127"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository"/>
     </location>
-    <location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="slicer" includeSource="true" type="InstallableUnit">
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
       <repository location="http://download.eclipse.org/releases/2018-12/"/>
     </location>
   </locations>
-</target>
\ No newline at end of file
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd
index 31981f2..5bfd814 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.10" with source configurePhase
 
 include "projects/jetty-9.4.14.tpd"
-include "orbit/R20190226160451-2019-03.tpd"
+include "orbit/S20190521195709.tpd"
 
 location "http://download.eclipse.org/releases/2018-12/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.tpd
deleted file mode 100644
index 05002f2..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.11-staging" with source configurePhase
-
-include "projects/jetty-9.4.14.tpd"
-include "orbit/R20190226160451-2019-03.tpd"
-
-location "http://download.eclipse.org/staging/2019-03/" {
-	org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
similarity index 87%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.target
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
index 14cd0d7..a3ad331 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.11-staging" sequenceNumber="1551829209">
+<target name="jgit-4.11" sequenceNumber="1558539017">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.14.v20181114"/>
@@ -23,16 +23,16 @@
       <repository id="jetty-9.4.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.14.v20181114"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+      <unit id="org.apache.ant" version="1.10.6.v20190516-0412"/>
+      <unit id="org.apache.ant.source" version="1.10.6.v20190516-0412"/>
       <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
       <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190213-1430"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190213-1430"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190503-0009"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190503-0009"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
@@ -62,8 +62,8 @@
       <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
       <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
-      <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
@@ -78,15 +78,15 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
       <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20190226160451/repository"/>
+      <unit id="org.apache.sshd.osgi" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.2.0.v20190425-2127"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
-      <repository location="http://download.eclipse.org/staging/2019-03/"/>
+      <repository location="http://download.eclipse.org/releases/2019-03/"/>
     </location>
   </locations>
 </target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd
new file mode 100644
index 0000000..8b3b509
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.11" with source configurePhase
+
+include "projects/jetty-9.4.14.tpd"
+include "orbit/S20190521195709.tpd"
+
+location "http://download.eclipse.org/releases/2019-03/" {
+	org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.target
similarity index 88%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.target
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.target
index 14cd0d7..2ea7b79 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.11-staging" sequenceNumber="1551829209">
+<target name="jgit-4.12-staging" sequenceNumber="1558539043">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.14.v20181114"/>
@@ -23,16 +23,16 @@
       <repository id="jetty-9.4.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.14.v20181114"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+      <unit id="org.apache.ant" version="1.10.6.v20190516-0412"/>
+      <unit id="org.apache.ant.source" version="1.10.6.v20190516-0412"/>
       <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
       <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190213-1430"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190213-1430"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190503-0009"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190503-0009"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
@@ -62,8 +62,8 @@
       <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
       <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
-      <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
@@ -78,15 +78,15 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
       <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20190226160451/repository"/>
+      <unit id="org.apache.sshd.osgi" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.2.0.v20190425-2127"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
-      <repository location="http://download.eclipse.org/staging/2019-03/"/>
+      <repository location="http://download.eclipse.org/staging/2019-06/"/>
     </location>
   </locations>
 </target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.tpd
new file mode 100644
index 0000000..f0cd304
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12-staging.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.12-staging" with source configurePhase
+
+include "projects/jetty-9.4.14.tpd"
+include "orbit/S20190521195709.tpd"
+
+location "http://download.eclipse.org/staging/2019-06/" {
+	org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index f8641ca..64cf3e7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.6" sequenceNumber="1551829238">
+<target name="jgit-4.6" sequenceNumber="1558539043">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.14.v20181114"/>
@@ -23,16 +23,16 @@
       <repository id="jetty-9.4.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.14.v20181114"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+      <unit id="org.apache.ant" version="1.10.6.v20190516-0412"/>
+      <unit id="org.apache.ant.source" version="1.10.6.v20190516-0412"/>
       <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
       <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190213-1430"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190213-1430"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190503-0009"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190503-0009"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
@@ -62,8 +62,8 @@
       <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
       <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
-      <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
@@ -78,11 +78,11 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
       <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20190226160451/repository"/>
+      <unit id="org.apache.sshd.osgi" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.2.0.v20190425-2127"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
index 81eb219..8ae721f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.6" with source configurePhase
 
 include "projects/jetty-9.4.14.tpd"
-include "orbit/R20190226160451-2019-03.tpd"
+include "orbit/S20190521195709.tpd"
 
 location "http://download.eclipse.org/releases/neon/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index 427f9dc..eb8c8e9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.7" sequenceNumber="1551829255">
+<target name="jgit-4.7" sequenceNumber="1558539038">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.14.v20181114"/>
@@ -23,16 +23,16 @@
       <repository id="jetty-9.4.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.14.v20181114"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+      <unit id="org.apache.ant" version="1.10.6.v20190516-0412"/>
+      <unit id="org.apache.ant.source" version="1.10.6.v20190516-0412"/>
       <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
       <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190213-1430"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190213-1430"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190503-0009"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190503-0009"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
@@ -62,8 +62,8 @@
       <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
       <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
-      <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
@@ -78,11 +78,11 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
       <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20190226160451/repository"/>
+      <unit id="org.apache.sshd.osgi" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.2.0.v20190425-2127"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
index 10a4eb7..430506a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.7" with source configurePhase
 
 include "projects/jetty-9.4.14.tpd"
-include "orbit/R20190226160451-2019-03.tpd"
+include "orbit/S20190521195709.tpd"
 
 location "http://download.eclipse.org/releases/oxygen/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
index fe641ef..9764a61 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.8" sequenceNumber="1551829263">
+<target name="jgit-4.8" sequenceNumber="1558539037">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.14.v20181114"/>
@@ -23,16 +23,16 @@
       <repository id="jetty-9.4.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.14.v20181114"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+      <unit id="org.apache.ant" version="1.10.6.v20190516-0412"/>
+      <unit id="org.apache.ant.source" version="1.10.6.v20190516-0412"/>
       <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
       <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190213-1430"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190213-1430"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190503-0009"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190503-0009"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
@@ -62,8 +62,8 @@
       <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
       <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
-      <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
@@ -78,11 +78,11 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
       <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20190226160451/repository"/>
+      <unit id="org.apache.sshd.osgi" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.2.0.v20190425-2127"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
index daa24dd..e18fc72 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.8" with source configurePhase
 
 include "projects/jetty-9.4.14.tpd"
-include "orbit/R20190226160451-2019-03.tpd"
+include "orbit/S20190521195709.tpd"
 
 location "http://download.eclipse.org/releases/photon/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
index 4f63f66..331d141 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.9" sequenceNumber="1551829271">
+<target name="jgit-4.9" sequenceNumber="1558539042">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.14.v20181114"/>
@@ -23,16 +23,16 @@
       <repository id="jetty-9.4.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.14.v20181114"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/>
+      <unit id="org.apache.ant" version="1.10.6.v20190516-0412"/>
+      <unit id="org.apache.ant.source" version="1.10.6.v20190516-0412"/>
       <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/>
       <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/>
       <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/>
       <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190213-1430"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190213-1430"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.6.v20190503-0009"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.6.v20190503-0009"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.10.v20190123-2214"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
@@ -62,8 +62,8 @@
       <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/>
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
       <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
-      <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
-      <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
+      <unit id="com.jcraft.jsch" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
@@ -78,11 +78,11 @@
       <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/>
       <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/>
-      <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20190226160451/repository"/>
+      <unit id="org.apache.sshd.osgi" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.osgi.source" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp" version="2.2.0.v20190425-2127"/>
+      <unit id="org.apache.sshd.sftp.source" version="2.2.0.v20190425-2127"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
index d024dca..6b71dff 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.9" with source configurePhase
 
 include "projects/jetty-9.4.14.tpd"
-include "orbit/R20190226160451-2019-03.tpd"
+include "orbit/S20190521195709.tpd"
 
 location "http://download.eclipse.org/releases/2018-09/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20190521195709.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20190521195709.tpd
new file mode 100644
index 0000000..d526ab4
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20190521195709.tpd
@@ -0,0 +1,64 @@
+target "S20190521195709" with source configurePhase
+// see http://download.eclipse.org/tools/orbit/downloads/
+
+location "http://download.eclipse.org/tools/orbit/downloads/drops/S20190521195709/repository" {
+	org.apache.ant [1.10.6.v20190516-0412,1.10.6.v20190516-0412]
+	org.apache.ant.source [1.10.6.v20190516-0412,1.10.6.v20190516-0412]
+	org.apache.commons.codec [1.10.0.v20180409-1845,1.10.0.v20180409-1845]
+	org.apache.commons.codec.source [1.10.0.v20180409-1845,1.10.0.v20180409-1845]
+	org.apache.commons.compress [1.18.0.v20181121-2221,1.18.0.v20181121-2221]
+	org.apache.commons.compress.source [1.18.0.v20181121-2221,1.18.0.v20181121-2221]
+	org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+	org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+	org.apache.httpcomponents.httpclient [4.5.6.v20190503-0009,4.5.6.v20190503-0009]
+	org.apache.httpcomponents.httpclient.source [4.5.6.v20190503-0009,4.5.6.v20190503-0009]
+	org.apache.httpcomponents.httpcore [4.4.10.v20190123-2214,4.4.10.v20190123-2214]
+	org.apache.httpcomponents.httpcore.source [4.4.10.v20190123-2214,4.4.10.v20190123-2214]
+	org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
+	org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
+	org.bouncycastle.bcpg [1.60.0.v20181210-2057,1.60.0.v20181210-2057]
+	org.bouncycastle.bcpg.source [1.60.0.v20181210-2057,1.60.0.v20181210-2057]
+	org.bouncycastle.bcpkix [1.60.0.v20181210-2057,1.60.0.v20181210-2057]
+	org.bouncycastle.bcpkix.source [1.60.0.v20181210-2057,1.60.0.v20181210-2057]
+	org.bouncycastle.bcprov [1.60.0.v20181210-2057,1.60.0.v20181210-2057]
+	org.bouncycastle.bcprov.source [1.60.0.v20181210-2057,1.60.0.v20181210-2057]
+	org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+	org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+	org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000]
+	org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+	org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+	org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+	org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+	javaewah [1.1.6.v20160919-1400,1.1.6.v20160919-1400]
+	javaewah.source [1.1.6.v20160919-1400,1.1.6.v20160919-1400]
+	org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+	org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+	org.mockito [2.13.0.v20180426-1843,2.13.0.v20180426-1843]
+	org.mockito.source [2.13.0.v20180426-1843,2.13.0.v20180426-1843]
+	net.bytebuddy.byte-buddy [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+	net.bytebuddy.byte-buddy.source [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+	net.bytebuddy.byte-buddy-agent [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+	net.bytebuddy.byte-buddy-agent.source [1.7.9.v20180420-1519,1.7.9.v20180420-1519]
+	com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
+	com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
+	com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
+	com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
+	org.junit [4.12.0.v201504281640,4.12.0.v201504281640]
+	org.junit.source [4.12.0.v201504281640,4.12.0.v201504281640]
+	javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
+	javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
+	org.tukaani.xz [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
+	org.tukaani.xz.source [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
+	org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
+	org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
+	org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
+	org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
+	com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
+	com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
+	net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
+	net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
+	org.apache.sshd.osgi [2.2.0.v20190425-2127,2.2.0.v20190425-2127]
+	org.apache.sshd.osgi.source [2.2.0.v20190425-2127,2.2.0.v20190425-2127]
+	org.apache.sshd.sftp [2.2.0.v20190425-2127,2.2.0.v20190425-2127]
+	org.apache.sshd.sftp.source [2.2.0.v20190425-2127,2.2.0.v20190425-2127]
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
index 532100b..16ff484 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
@@ -49,7 +49,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index f4d8c4f..325e287 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -53,13 +53,13 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>5.3.3-SNAPSHOT</version>
+  <version>5.4.0-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
 
   <properties>
-    <tycho-version>1.3.0</tycho-version>
+    <tycho-version>1.4.0</tycho-version>
     <tycho-extras-version>${tycho-version}</tycho-extras-version>
     <target-platform>jgit-4.6</target-platform>
   </properties>
@@ -83,8 +83,6 @@
     <module>org.eclipse.jgit.lfs.feature</module>
     <module>org.eclipse.jgit.pgm.feature</module>
     <module>org.eclipse.jgit.source.feature</module>
-    <module>org.eclipse.jgit.pgm.source.feature</module>
-    <module>org.eclipse.jgit.ssh.apache.source.feature</module>
     <module>org.eclipse.jgit.junit.feature</module>
     <module>org.eclipse.jgit.repository</module>
   </modules>
@@ -113,6 +111,60 @@
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.ant</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.archive</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.http.apache</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.http.server</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit.http</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
       <version>${project.version}</version>
       <classifier>sources</classifier>
@@ -123,6 +175,12 @@
       <version>${project.version}</version>
       <classifier>sources</classifier>
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.ui</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index b0e6506..c6003a0 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -3,28 +3,28 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.pgm.test
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.api.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.diff;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.dircache;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="5.3.3",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.merge;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.pgm;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.pgm.internal;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.pgm.opt;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util.io;version="[5.3.3,5.4.0)",
+Import-Package: org.eclipse.jgit.api;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.api.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.diff;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.dircache;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="5.4.0",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.merge;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.pgm;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.pgm.opt;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util.io;version="[5.4.0,5.5.0)",
  org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 601f0e0..fcdebdc 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
@@ -109,7 +109,7 @@
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
-          <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory}</argLine>
+          <argLine>@{argLine} -Xmx512m -Djava.io.tmpdir=${project.build.directory}</argLine>
         </configuration>
       </plugin>
     </plugins>
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
index 0d1894b..a830ff2 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
@@ -214,7 +214,7 @@ protected String escapeJava(String line) {
 	protected void assertStringArrayEquals(String expected, String[] actual) {
 		// if there is more than one line, ignore last one if empty
 		assertEquals(1,
-				actual.length > 1 && actual[actual.length - 1].equals("")
+				actual.length > 1 && actual[actual.length - 1].isEmpty()
 						? actual.length - 1 : actual.length);
 		assertEquals(expected, actual[0]);
 	}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
index 47eb156..e07fdd5 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
@@ -60,7 +60,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -703,15 +702,12 @@ private void writeRaw(String filename, byte[] data)
 	private static Future<Object> writeAsync(OutputStream stream, byte[] data) {
 		ExecutorService executor = Executors.newSingleThreadExecutor();
 
-		return executor.submit(new Callable<Object>() {
-			@Override
-			public Object call() throws IOException {
-				try {
-					stream.write(data);
-					return null;
-				} finally {
-					stream.close();
-				}
+		return executor.submit(() -> {
+			try {
+				stream.write(data);
+				return null;
+			} finally {
+				stream.close();
 			}
 		});
 	}
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 4551c1c..9c98ac4 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.pgm
 Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-Localization: plugin
@@ -28,50 +28,50 @@
  org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.api;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.api.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.archive;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.awtui;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.blame;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.diff;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.dircache;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.gitrepo;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.ketch;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.server;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs.server.s3;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.merge;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.notes;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revplot;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.pack;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.resolver;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.sshd;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util.io;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.api;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.api.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.archive;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.awtui;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.blame;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.diff;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.dircache;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.gitrepo;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.ketch;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.server;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.merge;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.notes;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revplot;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.pack;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util.io;version="[5.4.0,5.5.0)",
  org.kohsuke.args4j;version="[2.33.0,3.0.0)",
  org.kohsuke.args4j.spi;version="[2.33.0,3.0.0)"
-Export-Package: org.eclipse.jgit.console;version="5.3.3";
+Export-Package: org.eclipse.jgit.console;version="5.4.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="5.3.3";
+ org.eclipse.jgit.pgm;version="5.4.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.pgm.opt,
@@ -82,11 +82,11 @@
    org.eclipse.jgit.treewalk,
    javax.swing,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="5.3.3";
+ org.eclipse.jgit.pgm.debug;version="5.4.0";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="5.3.3";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="5.3.3";
+ org.eclipse.jgit.pgm.internal;version="5.4.0";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.kohsuke.args4j.spi,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index c225dab..4cbd46e 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.pgm - Sources
 Bundle-SymbolicName: org.eclipse.jgit.pgm.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.3.3.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.3.3.qualifier";roots="."
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 272cd1f..cf878f4 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index a482ce3..b9982fe 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -24,7 +24,6 @@
 branchNotFound=branch ''{0}'' not found.
 cacheTreePathInfo="{0}": {1} entries, {2} children
 cannotBeRenamed={0} cannot be renamed
-cannotChekoutNoHeadsAdvertisedByRemote=cannot checkout; no HEAD advertised by remote
 cannotCombineSquashWithNoff=You cannot combine --squash with --no-ff.
 cannotCreateCommand=Cannot create command {0}
 cannotCreateOutputStream=cannot create output stream
@@ -315,7 +314,7 @@
 usage_beMoreVerbose=be more verbose
 usage_beVerbose=be verbose
 usage_cached=compare against index
-usage_checkout=Checkout a branch to the working tree
+usage_checkout=Check out a branch to the working tree
 usage_cloneRepositoryIntoNewDir=Clone a repository into a new directory
 usage_configFile=configuration file
 usage_configGlobal=use global configuration in ~/.gitconfig
@@ -323,7 +322,7 @@
 usage_configLocal=use local configuration in .git/config
 usage_configSystem=use system-wide configuration in $(prefix)/etc/gitconfig
 usage_configureTheServiceInDaemonServicename=configure the service in daemon.servicename
-usage_createBranchAndCheckout=create branch and checkout
+usage_createBranchAndCheckout=create branch and check out
 usage_deleteBranchEvenIfNotMerged=delete branch (even if not merged)
 usage_deleteFullyMergedBranch=delete fully merged branch
 usage_date=date format, one of default, rfc, local, iso, short, raw (as defined by git-log(1) ), locale or localelocal (jgit extensions)
@@ -416,7 +415,7 @@
 usage_updateRef=reference to update
 usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
 usage_useNameInsteadOfOriginToTrackUpstream=use <name> instead of 'origin' to track upstream
-usage_checkoutBranchAfterClone=checkout named branch instead of remote's HEAD
+usage_checkoutBranchAfterClone=check out named branch instead of remote's HEAD
 usage_viewCommitHistory=View commit history
 usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from other branches and commits.
 usernameFor=Username for {0}:
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
index 3858b3d..8794ca6 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
@@ -295,14 +295,14 @@ private void parseLineRangeOption() {
 			}
 		}
 
-		if (beginStr.equals("")) //$NON-NLS-1$
+		if (beginStr.isEmpty())
 			begin = 0;
 		else if (beginStr.startsWith("/")) //$NON-NLS-1$
 			begin = findLine(0, beginStr);
 		else
 			begin = Math.max(0, Integer.parseInt(beginStr) - 1);
 
-		if (endStr.equals("")) //$NON-NLS-1$
+		if (endStr.isEmpty())
 			end = blame.getResultContents().size();
 		else if (endStr.startsWith("/")) //$NON-NLS-1$
 			end = findLine(begin, endStr);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
index dbdccc1..dab52f6 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
@@ -52,7 +52,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
@@ -115,12 +114,8 @@ public static CommandRef get(String name) {
 
 	private static CommandRef[] toSortedArray(Collection<CommandRef> c) {
 		final CommandRef[] r = c.toArray(new CommandRef[0]);
-		Arrays.sort(r, new Comparator<CommandRef>() {
-			@Override
-			public int compare(CommandRef o1, CommandRef o2) {
-				return o1.getName().compareTo(o2.getName());
-			}
-		});
+		Arrays.sort(r, (CommandRef o1, CommandRef o2) -> o1.getName()
+				.compareTo(o2.getName()));
 		return r;
 	}
 
@@ -163,13 +158,9 @@ private void load(String cn) {
 		final Class<? extends TextBuiltin> clazz;
 		try {
 			clazz = Class.forName(cn, false, ldr).asSubclass(TextBuiltin.class);
-		} catch (ClassNotFoundException notBuiltin) {
+		} catch (ClassNotFoundException | ClassCastException notBuiltin) {
 			// Doesn't exist, even though the service entry is present.
-			//
-			return;
-		} catch (ClassCastException notBuiltin) {
 			// Isn't really a builtin, even though its listed as such.
-			//
 			return;
 		}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java
index 1773de5..85ace3a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java
@@ -156,24 +156,19 @@ public TextBuiltin create() {
 		final Constructor<? extends TextBuiltin> c;
 		try {
 			c = impl.getDeclaredConstructor();
-		} catch (SecurityException e) {
-			throw new RuntimeException(MessageFormat.format(CLIText.get().cannotCreateCommand, getName(), e));
-		} catch (NoSuchMethodException e) {
-			throw new RuntimeException(MessageFormat.format(CLIText.get().cannotCreateCommand, getName(), e));
+		} catch (SecurityException | NoSuchMethodException e) {
+			throw new RuntimeException(MessageFormat
+					.format(CLIText.get().cannotCreateCommand, getName(), e));
 		}
 		c.setAccessible(true);
 
 		final TextBuiltin r;
 		try {
 			r = c.newInstance();
-		} catch (InstantiationException e) {
-			throw new RuntimeException(MessageFormat.format(CLIText.get().cannotCreateCommand, getName(), e));
-		} catch (IllegalAccessException e) {
-			throw new RuntimeException(MessageFormat.format(CLIText.get().cannotCreateCommand, getName(), e));
-		} catch (IllegalArgumentException e) {
-			throw new RuntimeException(MessageFormat.format(CLIText.get().cannotCreateCommand, getName(), e));
-		} catch (InvocationTargetException e) {
-			throw new RuntimeException(MessageFormat.format(CLIText.get().cannotCreateCommand, getName(), e));
+		} catch (InstantiationException | IllegalAccessException
+				| IllegalArgumentException | InvocationTargetException e) {
+			throw new RuntimeException(MessageFormat
+					.format(CLIText.get().cannotCreateCommand, getName(), e));
 		}
 		r.setCommandName(getName());
 		return r;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
index 319b5e3..4382663 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
@@ -66,7 +66,6 @@
 import org.eclipse.jgit.transport.ReceivePack;
 import org.eclipse.jgit.transport.resolver.FileResolver;
 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.eclipse.jgit.util.FS;
 import org.kohsuke.args4j.Argument;
@@ -188,22 +187,17 @@ private void startKetchLeader(org.eclipse.jgit.transport.Daemon daemon) {
 		final ReceivePackFactory<DaemonClient> factory;
 
 		factory = daemon.getReceivePackFactory();
-		daemon.setReceivePackFactory(new ReceivePackFactory<DaemonClient>() {
-			@Override
-			public ReceivePack create(DaemonClient req, Repository repo)
-					throws ServiceNotEnabledException,
-					ServiceNotAuthorizedException {
-				ReceivePack rp = factory.create(req, repo);
-				KetchLeader leader;
-				try {
-					leader = leaders.get(repo);
-				} catch (URISyntaxException err) {
-					throw new ServiceNotEnabledException(
-							KetchText.get().invalidFollowerUri, err);
-				}
-				rp.setPreReceiveHook(new KetchPreReceive(leader));
-				return rp;
+		daemon.setReceivePackFactory((DaemonClient req, Repository repo) -> {
+			ReceivePack rp = factory.create(req, repo);
+			KetchLeader leader;
+			try {
+				leader = leaders.get(repo);
+			} catch (URISyntaxException err) {
+				throw new ServiceNotEnabledException(
+						KetchText.get().invalidFollowerUri, err);
 			}
+			rp.setPreReceiveHook(new KetchPreReceive(leader));
+			return rp;
 		});
 	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
index 7747dc7..3d6ebfd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
@@ -87,7 +87,7 @@ class Diff extends TextBuiltin {
 	@Argument(index = 1, metaVar = "metaVar_treeish")
 	private AbstractTreeIterator newTree;
 
-	@Option(name = "--cached", usage = "usage_cached")
+	@Option(name = "--cached", aliases = { "--staged" }, usage = "usage_cached")
 	private boolean cached;
 
 	@Option(name = "--", metaVar = "metaVar_paths", handler = PathTreeFilterHandler.class)
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
index 2b5af5d..00bbfb7 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
@@ -47,7 +47,6 @@
 import java.awt.BorderLayout;
 import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.File;
@@ -87,11 +86,8 @@ public void windowClosing(WindowEvent e) {
 		final JPanel buttons = new JPanel(new FlowLayout());
 		final JButton repaint = new JButton();
 		repaint.setText(CLIText.get().repaint);
-		repaint.addActionListener(new ActionListener() {
-			@Override
-			public void actionPerformed(ActionEvent e) {
-				graphPane.repaint();
-			}
+		repaint.addActionListener((ActionEvent e) -> {
+			graphPane.repaint();
 		});
 		buttons.add(repaint);
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
index 1c2564d..708fcde 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
@@ -46,7 +46,6 @@
 package org.eclipse.jgit.pgm;
 
 import java.io.IOException;
-import java.util.Comparator;
 import java.util.TreeSet;
 
 import org.eclipse.jgit.api.Git;
@@ -76,13 +75,8 @@ class LsRemote extends TextBuiltin {
 	protected void run() {
 		LsRemoteCommand command = Git.lsRemoteRepository().setRemote(remote)
 				.setTimeout(timeout).setHeads(heads).setTags(tags);
-		TreeSet<Ref> refs = new TreeSet<>(new Comparator<Ref>() {
-
-			@Override
-			public int compare(Ref r1, Ref r2) {
-				return r1.getName().compareTo(r2.getName());
-			}
-		});
+		TreeSet<Ref> refs = new TreeSet<>(
+				(Ref r1, Ref r2) -> r1.getName().compareTo(r2.getName()));
 		try {
 			refs.addAll(command.call());
 			for (Ref r : refs) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
index ad10ec9..9952f5c 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
@@ -330,22 +330,12 @@ private static boolean installConsole() {
 			install("org.eclipse.jgit.console.ConsoleAuthenticator"); //$NON-NLS-1$
 			install("org.eclipse.jgit.console.ConsoleCredentialsProvider"); //$NON-NLS-1$
 			return true;
-		} catch (ClassNotFoundException e) {
+		} catch (ClassNotFoundException | NoClassDefFoundError
+				| UnsupportedClassVersionError e) {
 			return false;
-		} catch (NoClassDefFoundError e) {
-			return false;
-		} catch (UnsupportedClassVersionError e) {
-			return false;
-
-		} catch (IllegalArgumentException e) {
-			throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
-		} catch (SecurityException e) {
-			throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
-		} catch (IllegalAccessException e) {
-			throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
-		} catch (InvocationTargetException e) {
-			throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
-		} catch (NoSuchMethodException e) {
+		} catch (IllegalArgumentException | SecurityException
+				| IllegalAccessException | InvocationTargetException
+				| NoSuchMethodException e) {
 			throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
 		}
 	}
@@ -390,12 +380,12 @@ static void configureHttpProxy() throws MalformedURLException {
 			if (s == null && protocol.equals("https")) { //$NON-NLS-1$
 				s = System.getenv("HTTPS_PROXY"); //$NON-NLS-1$
 			}
-			if (s == null || s.equals("")) { //$NON-NLS-1$
+			if (s == null || s.isEmpty()) {
 				continue;
 			}
 
 			final URL u = new URL(
-					(s.indexOf("://") == -1) ? protocol + "://" + s : s); //$NON-NLS-1$ //$NON-NLS-2$
+					(!s.contains("://")) ? protocol + "://" + s : s); //$NON-NLS-1$ //$NON-NLS-2$
 			if (!u.getProtocol().startsWith("http")) //$NON-NLS-1$
 				throw new MalformedURLException(MessageFormat.format(
 						CLIText.get().invalidHttpProxyOnlyHttpSupported, s));
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
index dfc8a94..8b18758 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
@@ -282,7 +282,7 @@ protected void printSectionHeader(String pattern, Object... arguments)
 		if (!porcelain) {
 			outw.println(CLIText.formatLine(MessageFormat.format(pattern,
 					arguments)));
-			if (!pattern.equals("")) //$NON-NLS-1$
+			if (!pattern.isEmpty())
 				outw.println(CLIText.formatLine("")); //$NON-NLS-1$
 			outw.flush();
 		}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
index 0e1b398..85a7444 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
@@ -52,7 +52,6 @@
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
 import org.eclipse.jgit.diff.DiffAlgorithm;
@@ -235,15 +234,12 @@ private void run(Repository repo) throws Exception {
 			}
 		}
 
-		Collections.sort(all, new Comparator<Test>() {
-			@Override
-			public int compare(Test a, Test b) {
-				int result = Long.signum(a.runningTimeNanos - b.runningTimeNanos);
-				if (result == 0) {
-					result = a.algorithm.name.compareTo(b.algorithm.name);
-				}
-				return result;
+		Collections.sort(all, (Test a, Test b) -> {
+			int result = Long.signum(a.runningTimeNanos - b.runningTimeNanos);
+			if (result == 0) {
+				result = a.algorithm.name.compareTo(b.algorithm.name);
 			}
+			return result;
 		});
 
 		File directory = repo.getDirectory();
@@ -338,12 +334,9 @@ private List<Test> init() {
 					}
 				}
 			}
-		} catch (IllegalArgumentException e) {
-			throw die("Cannot determine names", e); //$NON-NLS-1$
-		} catch (IllegalAccessException e) {
+		} catch (IllegalArgumentException | IllegalAccessException e) {
 			throw die("Cannot determine names", e); //$NON-NLS-1$
 		}
-
 		return all;
 	}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
index 300a01d..a2ea8c2 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
@@ -403,9 +403,7 @@ private List<Function> init() {
 					folds.add(fold);
 				}
 			}
-		} catch (IllegalArgumentException e) {
-			throw new RuntimeException("Cannot determine names", e); //$NON-NLS-1$
-		} catch (IllegalAccessException e) {
+		} catch (IllegalArgumentException | IllegalAccessException e) {
 			throw new RuntimeException("Cannot determine names", e); //$NON-NLS-1$
 		}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index d0288a8..7b9401f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -138,7 +138,6 @@ public static String fatalError(String message) {
 	/***/ public String cacheTreePathInfo;
 	/***/ public String configFileNotFound;
 	/***/ public String cannotBeRenamed;
-	/***/ public String cannotChekoutNoHeadsAdvertisedByRemote;
 	/***/ public String cannotCombineSquashWithNoff;
 	/***/ public String cannotCreateCommand;
 	/***/ public String cannotCreateOutputStream;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
index a14f651..213d987 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
@@ -133,10 +133,7 @@ public int parseArguments(Parameters params) throws CmdLineException {
 		final CanonicalTreeParser p = new CanonicalTreeParser();
 		try (ObjectReader curs = clp.getRepository().newObjectReader()) {
 			p.reset(curs, clp.getRevWalk().parseTree(id));
-		} catch (MissingObjectException e) {
-			throw new CmdLineException(clp,
-					CLIText.format(CLIText.get().notATree), name);
-		} catch (IncorrectObjectTypeException e) {
+		} catch (MissingObjectException | IncorrectObjectTypeException e) {
 			throw new CmdLineException(clp,
 					CLIText.format(CLIText.get().notATree), name);
 		} catch (IOException e) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
index d99f88e..4a50708 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
@@ -96,7 +96,7 @@ public int parseArguments(Parameters params) throws CmdLineException {
 			filters.add(PathFilter.create(path));
 		}
 
-		if (filters.size() == 0)
+		if (filters.isEmpty())
 			return 0;
 		if (filters.size() == 1) {
 			setter.addValue(filters.get(0));
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
index b925e31..d4effa3 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
@@ -130,10 +130,7 @@ private void addOne(String name, boolean interesting)
 		final RevCommit c;
 		try {
 			c = clp.getRevWalk().parseCommit(id);
-		} catch (MissingObjectException e) {
-			throw new CmdLineException(clp,
-					CLIText.format(CLIText.get().notACommit), name);
-		} catch (IncorrectObjectTypeException e) {
+		} catch (MissingObjectException | IncorrectObjectTypeException e) {
 			throw new CmdLineException(clp,
 					CLIText.format(CLIText.get().notACommit), name);
 		} catch (IOException e) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
index 85922a2..19841f6 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
@@ -102,10 +102,7 @@ public int parseArguments(Parameters params) throws CmdLineException {
 		final RevTree c;
 		try {
 			c = clp.getRevWalk().parseTree(id);
-		} catch (MissingObjectException e) {
-			throw new CmdLineException(clp,
-					CLIText.format(CLIText.get().notATree), name);
-		} catch (IncorrectObjectTypeException e) {
+		} catch (MissingObjectException | IncorrectObjectTypeException e) {
 			throw new CmdLineException(clp,
 					CLIText.format(CLIText.get().notATree), name);
 		} catch (IOException e) {
diff --git a/org.eclipse.jgit.ssh.apache.test/BUILD b/org.eclipse.jgit.ssh.apache.test/BUILD
index a13cf0b..18a48dc 100644
--- a/org.eclipse.jgit.ssh.apache.test/BUILD
+++ b/org.eclipse.jgit.ssh.apache.test/BUILD
@@ -10,7 +10,7 @@
     deps = [
         "//lib:eddsa",
         "//lib:junit",
-        "//lib:sshd-core",
+        "//lib:sshd-osgi",
         "//lib:sshd-sftp",
         "//org.eclipse.jgit:jgit",
         "//org.eclipse.jgit.ssh.apache:ssh-apache",
diff --git a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
index 35b27fe..c1a300e 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -3,17 +3,17 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %Provider-Name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit.ssh;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.ssh;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.sshd;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+Import-Package: org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.ssh;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.experimental.theories;version="[4.12,5.0.0)",
  org.junit.runner;version="[4.12,5.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml
index 40bdbbb..1fabd6e 100644
--- a/org.eclipse.jgit.ssh.apache.test/pom.xml
+++ b/org.eclipse.jgit.ssh.apache.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache/BUILD b/org.eclipse.jgit.ssh.apache/BUILD
index a1a6c8e..372cfc2 100644
--- a/org.eclipse.jgit.ssh.apache/BUILD
+++ b/org.eclipse.jgit.ssh.apache/BUILD
@@ -12,7 +12,7 @@
     deps = [
         "//lib:eddsa",
         "//lib:slf4j-api",
-        "//lib:sshd-core",
+        "//lib:sshd-osgi",
         "//lib:sshd-sftp",
         "//org.eclipse.jgit:jgit",
     ],
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 105b0a1..5d344f4 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -5,9 +5,9 @@
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache
 Bundle-Vendor: %Provider-Name
 Bundle-ActivationPolicy: lazy
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.3.3";x-internal:=true;
+Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.4.0";x-internal:=true;
   uses:="org.apache.sshd.client,
    org.apache.sshd.client.auth,
    org.apache.sshd.client.auth.keyboard,
@@ -22,9 +22,9 @@
    org.apache.sshd.common.signature,
    org.apache.sshd.common.util.buffer,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.internal.transport.sshd.auth;version="5.3.3";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="5.3.3";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="5.3.3";
+ org.eclipse.jgit.internal.transport.sshd.auth;version="5.4.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="5.4.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.transport.sshd;version="5.4.0";
   uses:="org.eclipse.jgit.transport,
    org.apache.sshd.client.config.hosts,
    org.apache.sshd.common.keyprovider,
@@ -32,52 +32,54 @@
    org.apache.sshd.client.session,
    org.apache.sshd.client.keyverifier"
 Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
- org.apache.sshd.agent;version="[2.0.0,2.1.0)",
- org.apache.sshd.client;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.auth;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.auth.keyboard;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.auth.password;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.auth.pubkey;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.channel;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.config.hosts;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.config.keys;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.future;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.keyverifier;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.session;version="[2.0.0,2.1.0)",
- org.apache.sshd.client.subsystem.sftp;version="[2.0.0,2.1.0)",
- org.apache.sshd.common;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.auth;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.channel;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.compression;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.config.keys;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.config.keys.loader;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.digest;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.forward;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.future;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.helpers;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.io;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.kex;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.keyprovider;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.mac;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.random;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.session;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.session.helpers;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.signature;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.subsystem.sftp;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.buffer;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.closeable;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.io;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.logging;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.net;version="[2.0.0,2.1.0)",
- org.apache.sshd.common.util.security;version="[2.0.0,2.1.0)",
- org.apache.sshd.server.auth;version="[2.0.0,2.1.0)",
- org.eclipse.jgit.annotations;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.fnmatch;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
+ org.apache.sshd.agent;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.auth;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.auth.keyboard;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.auth.password;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.auth.pubkey;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.channel;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.config.hosts;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.config.keys;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.future;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.keyverifier;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.session;version="[2.2.0,2.3.0)",
+ org.apache.sshd.client.subsystem.sftp;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.auth;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.channel;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.compression;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.config.keys;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.config.keys.loader;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.digest;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.forward;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.future;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.helpers;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.io;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.kex;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.keyprovider;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.mac;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.random;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.session;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.session.helpers;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.signature;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.subsystem.sftp;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.buffer;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.closeable;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.io;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.io.resource;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.logging;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.net;version="[2.2.0,2.3.0)",
+ org.apache.sshd.common.util.security;version="[2.2.0,2.3.0)",
+ org.apache.sshd.server.auth;version="[2.2.0,2.3.0)",
+ org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.fnmatch;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
index c76ba5a..cfd81da4 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.ssh.apache - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.3.3.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.3.3.qualifier";roots="."
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index 99100cd..31bf6d7 100644
--- a/org.eclipse.jgit.ssh.apache/pom.xml
+++ b/org.eclipse.jgit.ssh.apache/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
@@ -75,7 +75,7 @@
 
     <dependency>
       <groupId>org.apache.sshd</groupId>
-      <artifactId>sshd-core</artifactId>
+      <artifactId>sshd-osgi</artifactId>
       <version>${apache-sshd-version}</version>
     </dependency>
 
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java
index 1072f32..a1ec318 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java
@@ -45,10 +45,13 @@
 import static java.text.MessageFormat.format;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
 import java.security.KeyPair;
+import java.security.PrivateKey;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -57,13 +60,20 @@
 import java.util.NoSuchElementException;
 import java.util.concurrent.CancellationException;
 
+import javax.security.auth.DestroyFailedException;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.io.resource.IoResource;
+import org.apache.sshd.common.util.security.SecurityUtils;
 import org.eclipse.jgit.transport.sshd.KeyCache;
 
 /**
- * A {@link EncryptedFileKeyPairProvider} that uses an external
- * {@link KeyCache}.
+ * A {@link FileKeyPairProvider} that uses an external {@link KeyCache}.
  */
-public class CachingKeyPairProvider extends EncryptedFileKeyPairProvider
+public class CachingKeyPairProvider extends FileKeyPairProvider
 		implements Iterable<KeyPair> {
 
 	private final KeyCache cache;
@@ -71,7 +81,7 @@ public class CachingKeyPairProvider extends EncryptedFileKeyPairProvider
 	/**
 	 * Creates a new {@link CachingKeyPairProvider} using the given
 	 * {@link KeyCache}. If the cache is {@code null}, this is a simple
-	 * {@link EncryptedFileKeyPairProvider}.
+	 * {@link FileKeyPairProvider}.
 	 *
 	 * @param paths
 	 *            to load keys from
@@ -85,36 +95,36 @@ public CachingKeyPairProvider(List<Path> paths, KeyCache cache) {
 
 	@Override
 	public Iterator<KeyPair> iterator() {
+		return iterator(null);
+	}
+
+	private Iterator<KeyPair> iterator(SessionContext session) {
 		Collection<? extends Path> resources = getPaths();
 		if (resources.isEmpty()) {
 			return Collections.emptyListIterator();
 		}
-		return new CancellingKeyPairIterator(resources);
+		return new CancellingKeyPairIterator(session, resources);
 	}
 
 	@Override
-	public Iterable<KeyPair> loadKeys() {
-		return this;
+	public Iterable<KeyPair> loadKeys(SessionContext session) {
+		return () -> iterator(session);
 	}
 
-	@Override
-	protected KeyPair doLoadKey(Path resource)
+	private KeyPair loadKey(SessionContext session, Path path)
 			throws IOException, GeneralSecurityException {
-		if (!Files.exists(resource)) {
-			log.warn(format(SshdText.get().identityFileNotFound, resource));
+		if (!Files.exists(path)) {
+			log.warn(format(SshdText.get().identityFileNotFound, path));
 			return null;
 		}
-		// By calling doLoadKey(String, Path, FilePasswordProvider) instead of
-		// super.doLoadKey(Path) we can bypass the key caching in
-		// AbstractResourceKeyPairProvider, over which we have no real control.
-		String resourceId = resource.toString();
+		IoResource<Path> resource = getIoResource(session, path);
 		if (cache == null) {
-			return doLoadKey(resourceId, resource, getPasswordFinder());
+			return loadKey(session, resource, path, getPasswordFinder());
 		}
 		Throwable t[] = { null };
-		KeyPair key = cache.get(resource, p -> {
+		KeyPair key = cache.get(path, p -> {
 			try {
-				return doLoadKey(resourceId, p, getPasswordFinder());
+				return loadKey(session, resource, p, getPasswordFinder());
 			} catch (IOException | GeneralSecurityException e) {
 				t[0] = e;
 				return null;
@@ -130,18 +140,55 @@ protected KeyPair doLoadKey(Path resource)
 		return key;
 	}
 
+	private KeyPair loadKey(SessionContext session, NamedResource resource,
+			Path path, FilePasswordProvider passwordProvider)
+			throws IOException, GeneralSecurityException {
+		try (InputStream stream = Files.newInputStream(path)) {
+			Iterable<KeyPair> ids = SecurityUtils.loadKeyPairIdentities(session,
+					resource, stream, passwordProvider);
+			if (ids == null) {
+				throw new InvalidKeyException(
+						format(SshdText.get().identityFileNoKey, path));
+			}
+			Iterator<KeyPair> keys = ids.iterator();
+			if (!keys.hasNext()) {
+				throw new InvalidKeyException(format(
+						SshdText.get().identityFileUnsupportedFormat, path));
+			}
+			KeyPair result = keys.next();
+			if (keys.hasNext()) {
+				log.warn(format(SshdText.get().identityFileMultipleKeys, path));
+				keys.forEachRemaining(k -> {
+					PrivateKey pk = k.getPrivate();
+					if (pk != null) {
+						try {
+							pk.destroy();
+						} catch (DestroyFailedException e) {
+							// Ignore
+						}
+					}
+				});
+			}
+			return result;
+		}
+	}
+
 	private class CancellingKeyPairIterator implements Iterator<KeyPair> {
 
+		private final SessionContext context;
+
 		private final Iterator<Path> paths;
 
 		private KeyPair nextItem;
 
 		private boolean nextSet;
 
-		public CancellingKeyPairIterator(Collection<? extends Path> resources) {
+		public CancellingKeyPairIterator(SessionContext session,
+				Collection<? extends Path> resources) {
 			List<Path> copy = new ArrayList<>(resources.size());
 			copy.addAll(resources);
 			paths = copy.iterator();
+			context = session;
 		}
 
 		@Override
@@ -152,7 +199,7 @@ public boolean hasNext() {
 			nextSet = true;
 			while (nextItem == null && paths.hasNext()) {
 				try {
-					nextItem = doLoadKey(paths.next());
+					nextItem = loadKey(context, paths.next());
 				} catch (CancellationException cancelled) {
 					throw cancelled;
 				} catch (Exception other) {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java
deleted file mode 100644
index ef8e611..0000000
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.internal.transport.sshd;
-
-import static java.text.MessageFormat.format;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyPair;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.security.auth.DestroyFailedException;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.eclipse.jgit.internal.transport.sshd.RepeatingFilePasswordProvider.ResourceDecodeResult;
-
-/**
- * A {@link FileKeyPairProvider} that asks repeatedly for a passphrase for an
- * encrypted private key if the {@link FilePasswordProvider} is a
- * {@link RepeatingFilePasswordProvider}.
- */
-public abstract class EncryptedFileKeyPairProvider extends FileKeyPairProvider {
-
-	// TODO: remove this class once we're based on sshd > 2.1.0. See upstream
-	// issue SSHD-850 https://issues.apache.org/jira/browse/SSHD-850 and commit
-	// https://github.com/apache/mina-sshd/commit/f19bd2e34
-
-	/**
-	 * Creates a new {@link EncryptedFileKeyPairProvider} for the given
-	 * {@link Path}s.
-	 *
-	 * @param paths
-	 *            to read keys from
-	 */
-	public EncryptedFileKeyPairProvider(List<Path> paths) {
-		super(paths);
-	}
-
-	@Override
-	protected KeyPair doLoadKey(String resourceKey, InputStream inputStream,
-			FilePasswordProvider provider)
-			throws IOException, GeneralSecurityException {
-		if (!(provider instanceof RepeatingFilePasswordProvider)) {
-			return super.doLoadKey(resourceKey, inputStream, provider);
-		}
-		KeyPairResourceParser parser = SecurityUtils.getKeyPairResourceParser();
-		if (parser == null) {
-			// This is an internal configuration error, thus no translation.
-			throw new NoSuchProviderException(
-					"No registered key-pair resource parser"); //$NON-NLS-1$
-		}
-		RepeatingFilePasswordProvider realProvider = (RepeatingFilePasswordProvider) provider;
-		// Read the stream now so that we can process the content several
-		// times.
-		List<String> lines = IoUtils.readAllLines(inputStream);
-		Collection<KeyPair> ids = null;
-		while (ids == null) {
-			try {
-				ids = parser.loadKeyPairs(resourceKey, realProvider, lines);
-				realProvider.handleDecodeAttemptResult(resourceKey, "", null); //$NON-NLS-1$
-				// No exception; success. Exit the loop even if ids is still
-				// null!
-				break;
-			} catch (IOException | GeneralSecurityException
-					| RuntimeException e) {
-				ResourceDecodeResult loadResult = realProvider
-						.handleDecodeAttemptResult(resourceKey, "", e); //$NON-NLS-1$
-				if (loadResult == null
-						|| loadResult == ResourceDecodeResult.TERMINATE) {
-					throw e;
-				} else if (loadResult == ResourceDecodeResult.RETRY) {
-					continue;
-				}
-				// IGNORE doesn't make any sense here, but OK, let's ignore it.
-				// ids == null, so we'll throw an exception below.
-				break;
-			}
-		}
-		if (ids == null) {
-			// The javadoc on loadKeyPairs says it might return null if no
-			// key pair found. Bad API.
-			throw new InvalidKeyException(
-					format(SshdText.get().identityFileNoKey, resourceKey));
-		}
-		Iterator<KeyPair> keys = ids.iterator();
-		if (!keys.hasNext()) {
-			throw new InvalidKeyException(format(
-					SshdText.get().identityFileUnsupportedFormat, resourceKey));
-		}
-		KeyPair result = keys.next();
-		if (keys.hasNext()) {
-			log.warn(format(SshdText.get().identityFileMultipleKeys,
-					resourceKey));
-			keys.forEachRemaining(k -> {
-				PrivateKey pk = k.getPrivate();
-				if (pk != null) {
-					try {
-						pk.destroy();
-					} catch (DestroyFailedException e) {
-						// Ignore
-					}
-				}
-			});
-		}
-		return result;
-	}
-}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
index dcf330a..56f8ade 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java
@@ -46,6 +46,7 @@
 
 import java.io.IOException;
 import java.net.SocketAddress;
+import java.security.GeneralSecurityException;
 import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -173,7 +174,8 @@ protected IoWriteFuture sendIdentification(String ident)
 	}
 
 	@Override
-	protected byte[] sendKexInit() throws IOException {
+	protected byte[] sendKexInit()
+			throws IOException, GeneralSecurityException {
 		StatefulProxyConnector proxy = proxyHandler;
 		if (proxy != null) {
 			try {
@@ -187,7 +189,7 @@ protected IoWriteFuture sendIdentification(String ident)
 				// This is called only from the ClientSessionImpl
 				// constructor, where the return value is ignored.
 				return null;
-			} catch (IOException e) {
+			} catch (IOException | GeneralSecurityException e) {
 				throw e;
 			} catch (Exception other) {
 				throw new IOException(other.getLocalizedMessage(), other);
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java
index 7b22b88..21e8bea 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java
@@ -53,28 +53,11 @@
  * A {@link HostConfigEntry} that provides access to the multi-valued keys as
  * lists of strings. The super class treats them as single strings containing
  * comma-separated lists.
- *
  */
 public class JGitHostConfigEntry extends HostConfigEntry {
 
 	private Map<String, List<String>> multiValuedOptions;
 
-	@Override
-	public String getProperty(String name, String defaultValue) {
-		// Upstream bug fix (SSHD-867): if there are _no_ properties at all, the
-		// super implementation returns always null even if a default value is
-		// given.
-		//
-		// See https://issues.apache.org/jira/projects/SSHD/issues/SSHD-867
-		//
-		// TODO: remove this override once we're based on sshd > 2.1.0
-		Map<String, String> properties = getProperties();
-		if (properties == null || properties.isEmpty()) {
-			return defaultValue;
-		}
-		return super.getProperty(name, defaultValue);
-	}
-
 	/**
 	 * Sets the multi-valued options.
 	 *
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java
deleted file mode 100644
index 0b3de4a..0000000
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.internal.transport.sshd;
-
-import java.util.List;
-
-import org.apache.sshd.client.auth.AbstractUserAuthFactory;
-import org.apache.sshd.client.auth.UserAuth;
-import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.signature.Signature;
-import org.apache.sshd.common.signature.SignatureFactoriesManager;
-
-/**
- * A customized authentication factory for public key user authentication. The
- * default implementation {@link UserAuthPublicKeyFactory} ends up doing some
- * crazy stream "magic" that loads too many keys too early.
- */
-public class JGitPublicKeyAuthFactory extends AbstractUserAuthFactory
-		implements SignatureFactoriesManager {
-
-	/** The singleton {@link JGitPublicKeyAuthFactory}. */
-	public static final JGitPublicKeyAuthFactory INSTANCE = new JGitPublicKeyAuthFactory();
-
-	private JGitPublicKeyAuthFactory() {
-		super(UserAuthPublicKeyFactory.NAME);
-	}
-
-	@Override
-	public UserAuth create() {
-		return new JGitPublicKeyAuthentication(getSignatureFactories());
-	}
-
-	@Override
-	public List<NamedFactory<Signature>> getSignatureFactories() {
-		return null;
-	}
-
-	@Override
-	public void setSignatureFactories(List<NamedFactory<Signature>> factories) {
-		throw new UnsupportedOperationException();
-	}
-}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
deleted file mode 100644
index 63b3990..0000000
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.internal.transport.sshd;
-
-import java.util.List;
-
-import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.signature.Signature;
-
-/**
- * A specialized public key authentication handler that uses our own
- * {@link JGitPublicKeyIterator}. The super class creates in
- * {@link #init(ClientSession, String)} a
- * {@link org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}, which
- * in its constructor does some strange {@link java.util.stream.Stream} "magic"
- * that ends up loading keys prematurely.
- */
-public class JGitPublicKeyAuthentication extends UserAuthPublicKey {
-
-	private ClientSession clientSession;
-
-	private String serviceName;
-
-	/**
-	 * Creates a new {@link JGitPublicKeyAuthentication}.
-	 *
-	 * @param factories
-	 *            signature factories to use
-	 */
-	public JGitPublicKeyAuthentication(
-			List<NamedFactory<Signature>> factories) {
-		super(factories);
-	}
-
-	@Override
-	public void init(ClientSession session, String service) throws Exception {
-		// Do *not* call super.init(); it'll create a UserAuthPublicKeyIterator
-		// and that's where things then go wrong. Instead, do the whole
-		// initialization directly here.
-		clientSession = session;
-		serviceName = service;
-		releaseKeys();
-		// Use our own iterator!
-		keys = new JGitPublicKeyIterator(session, this);
-	}
-
-	@Override
-	public ClientSession getClientSession() {
-		return clientSession;
-	}
-
-	@Override
-	public ClientSession getSession() {
-		return clientSession;
-	}
-
-	@Override
-	public String getService() {
-		return serviceName;
-	}
-}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java
deleted file mode 100644
index cda1262..0000000
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.internal.transport.sshd;
-
-import java.io.IOException;
-import java.nio.channels.Channel;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.sshd.agent.SshAgent;
-import org.apache.sshd.agent.SshAgentFactory;
-import org.apache.sshd.client.auth.pubkey.AbstractKeyPairIterator;
-import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity;
-import org.apache.sshd.client.auth.pubkey.KeyPairIdentity;
-import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
-import org.apache.sshd.client.config.hosts.HostConfigEntry;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.signature.SignatureFactoriesManager;
-
-/**
- * A new iterator over key pairs that we use instead of the default
- * {@link org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}, which
- * in its constructor does some strange {@link java.util.stream.Stream} "magic"
- * that ends up loading keys prematurely. This class uses plain
- * {@link Iterator}s instead to avoid that problem. Used in
- * {@link JGitPublicKeyAuthentication}.
- *
- * @see <a href=
- *      "https://issues.apache.org/jira/projects/SSHD/issues/SSHD-860">Upstream
- *      issue SSHD-860</a>
- */
-public class JGitPublicKeyIterator
-		extends AbstractKeyPairIterator<PublicKeyIdentity> implements Channel {
-
-	// Re: the cause for the problem mentioned above has not been determined.
-	// It looks as if either the Apache code inadvertently calls
-	// GenericUtils.isEmpty() on all streams (which would load the first key
-	// of each stream), or the Java stream implementation does some prefetching.
-	// It's not entirely clear. Using Iterators we have more control over
-	// what happens when.
-
-	private final AtomicBoolean open = new AtomicBoolean(true);
-
-	private SshAgent agent;
-
-	private final List<Iterator<PublicKeyIdentity>> keys = new ArrayList<>(3);
-
-	private final Iterator<Iterator<PublicKeyIdentity>> keyIter;
-
-	private Iterator<PublicKeyIdentity> current;
-
-	private Boolean hasElement;
-
-	/**
-	 * Creates a new {@link JGitPublicKeyIterator}.
-	 *
-	 * @param session
-	 *            we're trying to authenticate
-	 * @param signatureFactories
-	 *            to use
-	 * @throws Exception
-	 *             if an {@link SshAgentFactory} is configured and getting
-	 *             identities from the agent fails
-	 */
-	public JGitPublicKeyIterator(ClientSession session,
-			SignatureFactoriesManager signatureFactories) throws Exception {
-		super(session);
-		boolean useAgent = true;
-		if (session instanceof JGitClientSession) {
-			HostConfigEntry config = ((JGitClientSession) session)
-					.getHostConfigEntry();
-			useAgent = !config.isIdentitiesOnly();
-		}
-		if (useAgent) {
-			FactoryManager manager = session.getFactoryManager();
-			SshAgentFactory factory = manager == null ? null
-					: manager.getAgentFactory();
-			if (factory != null) {
-				try {
-					agent = factory.createClient(manager);
-					keys.add(new AgentIdentityIterator(agent));
-				} catch (IOException e) {
-					try {
-						closeAgent();
-					} catch (IOException err) {
-						e.addSuppressed(err);
-					}
-					throw e;
-				}
-			}
-		}
-		keys.add(
-				new KeyPairIdentityIterator(session.getRegisteredIdentities(),
-						session, signatureFactories));
-		keys.add(new KeyPairIdentityIterator(session.getKeyPairProvider(),
-				session, signatureFactories));
-		keyIter = keys.iterator();
-	}
-
-	@Override
-	public boolean isOpen() {
-		return open.get();
-	}
-
-	@Override
-	public void close() throws IOException {
-		if (open.getAndSet(false)) {
-			closeAgent();
-		}
-	}
-
-	@Override
-	public boolean hasNext() {
-		if (!isOpen()) {
-			return false;
-		}
-		if (hasElement != null) {
-			return hasElement.booleanValue();
-		}
-		while (current == null || !current.hasNext()) {
-			if (keyIter.hasNext()) {
-				current = keyIter.next();
-			} else {
-				current = null;
-				hasElement = Boolean.FALSE;
-				return false;
-			}
-		}
-		hasElement = Boolean.TRUE;
-		return true;
-	}
-
-	@Override
-	public PublicKeyIdentity next() {
-		if (!isOpen() || hasElement == null && !hasNext()
-				|| !hasElement.booleanValue()) {
-			throw new NoSuchElementException();
-		}
-		hasElement = null;
-		PublicKeyIdentity result;
-		try {
-			result = current.next();
-		} catch (NoSuchElementException e) {
-			result = null;
-		}
-		return result;
-	}
-
-	private void closeAgent() throws IOException {
-		if (agent == null) {
-			return;
-		}
-		try {
-			agent.close();
-		} finally {
-			agent = null;
-		}
-	}
-
-	/**
-	 * An {@link Iterator} that maps the data obtained from an agent to
-	 * {@link PublicKeyIdentity}.
-	 */
-	private static class AgentIdentityIterator
-			implements Iterator<PublicKeyIdentity> {
-
-		private final SshAgent agent;
-
-		private final Iterator<? extends Map.Entry<PublicKey, String>> iter;
-
-		public AgentIdentityIterator(SshAgent agent) throws IOException {
-			this.agent = agent;
-			iter = agent == null ? null : agent.getIdentities().iterator();
-		}
-
-		@Override
-		public boolean hasNext() {
-			return iter != null && iter.hasNext();
-		}
-
-		@Override
-		public PublicKeyIdentity next() {
-			if (iter == null) {
-				throw new NoSuchElementException();
-			}
-			Map.Entry<PublicKey, String> entry = iter.next();
-			return new KeyAgentIdentity(agent, entry.getKey(),
-					entry.getValue());
-		}
-	}
-
-	/**
-	 * An {@link Iterator} that maps {@link KeyPair} to
-	 * {@link PublicKeyIdentity}.
-	 */
-	private static class KeyPairIdentityIterator
-			implements Iterator<PublicKeyIdentity> {
-
-		private final Iterator<KeyPair> keyPairs;
-
-		private final ClientSession session;
-
-		private final SignatureFactoriesManager signatureFactories;
-
-		public KeyPairIdentityIterator(KeyIdentityProvider provider,
-				ClientSession session,
-				SignatureFactoriesManager signatureFactories) {
-			this.session = session;
-			this.signatureFactories = signatureFactories;
-			keyPairs = provider == null ? null : provider.loadKeys().iterator();
-		}
-
-		@Override
-		public boolean hasNext() {
-			return keyPairs != null && keyPairs.hasNext();
-		}
-
-		@Override
-		public PublicKeyIdentity next() {
-			if (keyPairs == null) {
-				throw new NoSuchElementException();
-			}
-			KeyPair key = keyPairs.next();
-			return new KeyPairIdentity(signatureFactories, session, key);
-		}
-	}
-}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
index b9ff5e5..98e71df 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
@@ -48,10 +48,12 @@
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.Proxy;
+import java.net.SocketAddress;
 import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.Iterator;
@@ -66,12 +68,14 @@
 import org.apache.sshd.client.future.DefaultConnectFuture;
 import org.apache.sshd.client.session.ClientSessionImpl;
 import org.apache.sshd.client.session.SessionFactory;
+import org.apache.sshd.common.AttributeRepository;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoConnectFuture;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.eclipse.jgit.internal.transport.sshd.proxy.HttpClientConnector;
@@ -117,7 +121,8 @@ protected SessionFactory createSessionFactory() {
 	}
 
 	@Override
-	public ConnectFuture connect(HostConfigEntry hostConfig)
+	public ConnectFuture connect(HostConfigEntry hostConfig,
+			AttributeRepository context, SocketAddress localAddress)
 			throws IOException {
 		if (connector == null) {
 			throw new IllegalStateException("SshClient not started."); //$NON-NLS-1$
@@ -149,7 +154,7 @@ public ConnectFuture connect(HostConfigEntry hostConfig)
 			address = configureProxy(proxy, address);
 			proxy.clearPassword();
 		}
-		connector.connect(address).addListener(listener);
+		connector.connect(address, this, localAddress).addListener(listener);
 		return connectFuture;
 	}
 
@@ -263,16 +268,16 @@ private JGitClientSession createSession(IoSession ioSession,
 				identities, keyCache);
 		ourConfiguredKeysProvider.setPasswordFinder(passwordProvider);
 		if (hostConfig.isIdentitiesOnly()) {
-			session.setKeyPairProvider(ourConfiguredKeysProvider);
+			session.setKeyIdentityProvider(ourConfiguredKeysProvider);
 		} else {
-			KeyPairProvider defaultKeysProvider = getKeyPairProvider();
+			KeyIdentityProvider defaultKeysProvider = getKeyIdentityProvider();
 			if (defaultKeysProvider instanceof AbstractResourceKeyPairProvider<?>) {
 				((AbstractResourceKeyPairProvider<?>) defaultKeysProvider)
 						.setPasswordFinder(passwordProvider);
 			}
-			KeyPairProvider combinedProvider = new CombinedKeyPairProvider(
+			KeyIdentityProvider combinedProvider = new CombinedKeyIdentityProvider(
 					ourConfiguredKeysProvider, defaultKeysProvider);
-			session.setKeyPairProvider(combinedProvider);
+			session.setKeyIdentityProvider(combinedProvider);
 		}
 		return session;
 	}
@@ -363,39 +368,30 @@ protected ClientSessionImpl doCreateSession(IoSession ioSession)
 	}
 
 	/**
-	 * A {@link KeyPairProvider} that iterates over the {@link Iterable}s
-	 * returned by other {@link KeyPairProvider}s.
+	 * A {@link KeyIdentityProvider} that iterates over the {@link Iterable}s
+	 * returned by other {@link KeyIdentityProvider}s.
 	 */
-	private static class CombinedKeyPairProvider implements KeyPairProvider {
+	private static class CombinedKeyIdentityProvider
+			implements KeyIdentityProvider {
 
-		private final List<KeyPairProvider> providers;
+		private final List<KeyIdentityProvider> providers;
 
-		public CombinedKeyPairProvider(KeyPairProvider... providers) {
+		public CombinedKeyIdentityProvider(KeyIdentityProvider... providers) {
 			this(Arrays.stream(providers).filter(Objects::nonNull)
 					.collect(Collectors.toList()));
 		}
 
-		public CombinedKeyPairProvider(List<KeyPairProvider> providers) {
+		public CombinedKeyIdentityProvider(
+				List<KeyIdentityProvider> providers) {
 			this.providers = providers;
 		}
 
 		@Override
-		public Iterable<String> getKeyTypes() {
-			throw new UnsupportedOperationException(
-					"Should not have been called in a ssh client"); //$NON-NLS-1$
-		}
-
-		@Override
-		public KeyPair loadKey(String type) {
-			throw new UnsupportedOperationException(
-					"Should not have been called in a ssh client"); //$NON-NLS-1$
-		}
-
-		@Override
-		public Iterable<KeyPair> loadKeys() {
+		public Iterable<KeyPair> loadKeys(SessionContext context) {
 			return () -> new Iterator<KeyPair>() {
 
-				private Iterator<KeyPairProvider> factories = providers.iterator();
+				private Iterator<KeyIdentityProvider> factories = providers
+						.iterator();
 				private Iterator<KeyPair> current;
 
 				private Boolean hasElement;
@@ -407,7 +403,12 @@ public boolean hasNext() {
 					}
 					while (current == null || !current.hasNext()) {
 						if (factories.hasNext()) {
-							current = factories.next().loadKeys().iterator();
+							try {
+								current = factories.next().loadKeys(context)
+										.iterator();
+							} catch (IOException | GeneralSecurityException e) {
+								throw new RuntimeException(e);
+							}
 						} else {
 							current = null;
 							hasElement = Boolean.FALSE;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
index 9846439..6468b3e 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java
@@ -47,11 +47,13 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.net.SocketAddress;
 import java.util.Map;
 import java.util.TreeMap;
 
 import org.apache.sshd.client.config.hosts.HostConfigEntry;
 import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.common.AttributeRepository;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
@@ -101,7 +103,8 @@ public JGitSshConfig(@NonNull File home, @NonNull File config,
 
 	@Override
 	public HostConfigEntry resolveEffectiveHost(String host, int port,
-			String username) throws IOException {
+			SocketAddress localAddress, String username,
+			AttributeRepository attributes) throws IOException {
 		HostEntry entry = configFile.lookup(host, port, username);
 		JGitHostConfigEntry config = new JGitHostConfigEntry();
 		// Apache MINA conflates all keys, even multi-valued ones, in one map
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java
index 7d8f3fd..381f7cf 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java
@@ -60,6 +60,7 @@
 import java.security.GeneralSecurityException;
 import java.security.PublicKey;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
@@ -586,9 +587,7 @@ public ModifiedKeyHandling acceptModifiedServerKey(
 					KeyUtils.getFingerPrint(BuiltinDigests.sha256, expected),
 					KeyUtils.getFingerPrint(BuiltinDigests.md5, actual),
 					KeyUtils.getFingerPrint(BuiltinDigests.sha256, actual));
-			for (String line : warning.split("\n")) { //$NON-NLS-1$
-				messages.add(line);
-			}
+			messages.addAll(Arrays.asList(warning.split("\n"))); //$NON-NLS-1$
 
 			CredentialsProvider provider = getCredentialsProvider(
 					clientSession);
@@ -673,7 +672,7 @@ private List<HostEntryPair> reload(Path path) throws IOException {
 						continue;
 					}
 					try {
-						PublicKey serverKey = keyPart.resolvePublicKey(
+						PublicKey serverKey = keyPart.resolvePublicKey(null,
 								PublicKeyEntryResolver.IGNORING);
 						if (serverKey == null) {
 							LOG.warn(format(
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
index 93bd102..bec65f1 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
@@ -50,6 +50,8 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.session.SessionContext;
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.transport.CredentialsProvider;
 import org.eclipse.jgit.transport.URIish;
@@ -83,10 +85,12 @@ public int getAttempts() {
 	}
 
 	@Override
-	public String getPassword(String resourceKey) throws IOException {
+	public String getPassword(SessionContext session, NamedResource resource,
+			int attemptIndex) throws IOException {
+		String key = resource.getName();
 		int attempt = counts
-				.computeIfAbsent(resourceKey, k -> new AtomicInteger()).get();
-		char[] passphrase = delegate.getPassphrase(toUri(resourceKey), attempt);
+				.computeIfAbsent(key, k -> new AtomicInteger()).get();
+		char[] passphrase = delegate.getPassphrase(toUri(key), attempt);
 		if (passphrase == null) {
 			return null;
 		}
@@ -98,21 +102,23 @@ public String getPassword(String resourceKey) throws IOException {
 	}
 
 	@Override
-	public ResourceDecodeResult handleDecodeAttemptResult(String resourceKey,
+	public ResourceDecodeResult handleDecodeAttemptResult(
+			SessionContext session, NamedResource resource, int retryIndex,
 			String password, Exception err)
 			throws IOException, GeneralSecurityException {
-		AtomicInteger count = counts.get(resourceKey);
+		String key = resource.getName();
+		AtomicInteger count = counts.get(key);
 		int numberOfAttempts = count == null ? 0 : count.incrementAndGet();
 		ResourceDecodeResult result = null;
 		try {
-			if (delegate.keyLoaded(toUri(resourceKey), numberOfAttempts, err)) {
+			if (delegate.keyLoaded(toUri(key), numberOfAttempts, err)) {
 				result = ResourceDecodeResult.RETRY;
 			} else {
 				result = ResourceDecodeResult.TERMINATE;
 			}
 		} finally {
 			if (result != ResourceDecodeResult.RETRY) {
-				counts.remove(resourceKey);
+				counts.remove(key);
 			}
 		}
 		return result;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java
index e491cae..977f1a2 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java
@@ -42,9 +42,6 @@
  */
 package org.eclipse.jgit.internal.transport.sshd;
 
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 
 /**
@@ -74,42 +71,4 @@ default int getAttempts() {
 		return 1;
 	}
 
-	// The following part of this interface is from the upstream resolution of
-	// SSHD-850. See https://github.com/apache/mina-sshd/commit/f19bd2e34 .
-	// TODO: remove this once we move to sshd > 2.1.0
-
-	/**
-	 * Result value of
-	 * {@link RepeatingFilePasswordProvider#handleDecodeAttemptResult(String, String, Exception)}.
-	 */
-	public enum ResourceDecodeResult {
-		/** Re-throw the decoding exception. */
-		TERMINATE,
-		/** Retry the decoding process - including password prompt. */
-		RETRY,
-		/** Skip attempt and see if we can proceed without the key. */
-		IGNORE;
-	}
-
-	/**
-	 * Invoked to inform the password provider about the decoding result.
-	 * <b>Note:</b> any exception thrown from this method (including if called
-	 * to inform about success) will be propagated instead of the original (if
-	 * any was reported)
-	 *
-	 * @param resourceKey
-	 *            The resource key representing the <U>private</U> file
-	 * @param password
-	 *            The password that was attempted
-	 * @param err
-	 *            The attempt result - {@code null} for success
-	 * @return How to proceed in case of error - <u>ignored</u> if invoked in
-	 *         order to report success. <b>Note:</b> {@code null} is same as
-	 *         {@link ResourceDecodeResult#TERMINATE}.
-	 * @throws IOException
-	 * @throws GeneralSecurityException
-	 */
-	ResourceDecodeResult handleDecodeAttemptResult(String resourceKey,
-			String password, Exception err)
-			throws IOException, GeneralSecurityException;
 }
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
index a257a5e..6fa528d 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
@@ -145,18 +145,13 @@ public void process() throws Exception {
 	 */
 	protected void askCredentials() {
 		clearPassword();
-		PasswordAuthentication auth = AccessController
-				.doPrivileged(new PrivilegedAction<PasswordAuthentication>() {
-
-					@Override
-					public PasswordAuthentication run() {
-						return Authenticator.requestPasswordAuthentication(
-								proxy.getHostString(), proxy.getAddress(),
-								proxy.getPort(), SshConstants.SSH_SCHEME,
+		PasswordAuthentication auth = AccessController.doPrivileged(
+				(PrivilegedAction<PasswordAuthentication>) () -> Authenticator
+						.requestPasswordAuthentication(proxy.getHostString(),
+								proxy.getAddress(), proxy.getPort(),
+								SshConstants.SSH_SCHEME,
 								SshdText.get().proxyPasswordPrompt, "Basic", //$NON-NLS-1$
-								null, RequestorType.PROXY);
-					}
-				});
+								null, RequestorType.PROXY));
 		if (auth == null) {
 			user = ""; //$NON-NLS-1$
 			throw new CancellationException(
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
index 7d0e686..1e8d7d1 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
@@ -177,10 +177,7 @@ public Process exec(String commandName, int timeout) throws IOException {
 				timeoutMillis -= TimeUnit.NANOSECONDS
 						.toMillis(System.nanoTime() - start);
 			}
-		} catch (IOException e) {
-			exec.close(true);
-			throw e;
-		} catch (RuntimeException e) {
+		} catch (IOException | RuntimeException e) {
 			exec.close(true);
 			throw e;
 		}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index cdd47bf..90dc8ca 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -64,18 +64,19 @@
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.auth.UserAuth;
 import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
+import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
 import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCryptKdfOptions;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider;
 import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory;
 import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory;
-import org.eclipse.jgit.internal.transport.sshd.JGitPublicKeyAuthFactory;
 import org.eclipse.jgit.internal.transport.sshd.JGitSshClient;
 import org.eclipse.jgit.internal.transport.sshd.JGitSshConfig;
 import org.eclipse.jgit.internal.transport.sshd.JGitUserInteraction;
@@ -157,6 +158,11 @@ public SshdSessionFactory(KeyCache keyCache, ProxyDataFactory proxies) {
 		super();
 		this.keyCache = keyCache;
 		this.proxies = proxies;
+		// sshd limits the number of BCrypt KDF rounds to 255 by default.
+		// Decrypting such a key takes about two seconds on my machine.
+		// I consider this limit too low. The time increases linearly with the
+		// number of rounds.
+		BCryptKdfOptions.setMaxAllowedRounds(16384);
 	}
 
 	/** A simple general map key. */
@@ -211,7 +217,7 @@ public SshdSession getSession(URIish uri,
 				}
 				HostConfigEntryResolver configFile = getHostConfigEntryResolver(
 						home, sshDir);
-				KeyPairProvider defaultKeysProvider = toKeyPairProvider(
+				KeyIdentityProvider defaultKeysProvider = toKeyIdentityProvider(
 						getDefaultKeys(sshDir));
 				KeyPasswordProvider passphrases = createKeyPasswordProvider(
 						credentialsProvider);
@@ -227,7 +233,7 @@ public SshdSession getSession(URIish uri,
 				client.setUserInteraction(
 						new JGitUserInteraction(credentialsProvider));
 				client.setUserAuthFactories(getUserAuthFactories());
-				client.setKeyPairProvider(defaultKeysProvider);
+				client.setKeyIdentityProvider(defaultKeysProvider);
 				// JGit-specific things:
 				JGitSshClient jgitClient = (JGitSshClient) client;
 				jgitClient.setKeyCache(getKeyCache());
@@ -438,17 +444,18 @@ protected Iterable<KeyPair> getDefaultKeys(@NonNull File sshDir) {
 
 	/**
 	 * Converts an {@link Iterable} of {link KeyPair}s into a
-	 * {@link KeyPairProvider}.
+	 * {@link KeyIdentityProvider}.
 	 *
 	 * @param keys
-	 *            to provide via the returned {@link KeyPairProvider}
-	 * @return a {@link KeyPairProvider} that provides the given {@code keys}
+	 *            to provide via the returned {@link KeyIdentityProvider}
+	 * @return a {@link KeyIdentityProvider} that provides the given
+	 *         {@code keys}
 	 */
-	private KeyPairProvider toKeyPairProvider(Iterable<KeyPair> keys) {
-		if (keys instanceof KeyPairProvider) {
-			return (KeyPairProvider) keys;
+	private KeyIdentityProvider toKeyIdentityProvider(Iterable<KeyPair> keys) {
+		if (keys instanceof KeyIdentityProvider) {
+			return (KeyIdentityProvider) keys;
 		}
-		return () -> keys;
+		return (session) -> keys;
 	}
 
 	/**
@@ -522,7 +529,7 @@ private List<NamedFactory<UserAuth>> getUserAuthFactories() {
 		// Password auth doesn't have this problem.
 		return Collections.unmodifiableList(
 				Arrays.asList(GssApiWithMicAuthFactory.INSTANCE,
-						JGitPublicKeyAuthFactory.INSTANCE,
+						UserAuthPublicKeyFactory.INSTANCE,
 						JGitPasswordAuthFactory.INSTANCE,
 						UserAuthKeyboardInteractiveFactory.INSTANCE));
 	}
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index 95fb79b..be4fdbd 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -19,6 +19,7 @@
     "nls/MissingPropertyBundle.java",
     "nls/NoPropertiesBundle.java",
     "nls/NonTranslatedBundle.java",
+    "revwalk/ReachabilityCheckerTestCase.java",
     "revwalk/RevQueueTestCase.java",
     "revwalk/RevWalkTestCase.java",
     "transport/ObjectIdMatcher.java",
@@ -68,7 +69,7 @@
     deps = [
         "//lib:jsch",
         "//lib:junit",
-        "//lib:sshd-core",
+        "//lib:sshd-osgi",
         "//lib:sshd-sftp",
         "//org.eclipse.jgit:jgit",
         "//org.eclipse.jgit.junit:junit",
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 14eef13..73c8de8 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.test
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
@@ -12,54 +12,54 @@
  com.jcraft.jsch;version="[0.1.54,0.2.0)",
  net.bytebuddy.dynamic.loading;version="[1.7.0,2.0.0)",
  org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)",
- org.eclipse.jgit.annotations;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.api;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.api.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.attributes;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.awtui;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.blame;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.diff;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.dircache;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.events;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.fnmatch;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.gitrepo;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.hooks;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.ignore;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.ignore.internal;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.fsck;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.internal.transport.parser;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.junit.ssh;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lfs;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.merge;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.notes;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.patch;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.pgm;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.pgm.internal;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revplot;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.file;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.storage.pack;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.submodule;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.http;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport.resolver;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util.io;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util.sha1;version="[5.3.3,5.4.0)",
+ org.eclipse.jgit.annotations;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.api;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.api.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.attributes;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.awtui;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.blame;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.diff;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.dircache;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.events;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.fnmatch;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.gitrepo;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.hooks;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.ignore;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.ignore.internal;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.fsck;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lfs;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.merge;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.notes;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.patch;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.pgm;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revplot;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.file;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.storage.pack;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.submodule;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.http;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util.io;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util.sha1;version="[5.4.0,5.5.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.experimental.theories;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
@@ -73,4 +73,4 @@
  org.slf4j;version="[1.7.0,2.0.0)"
 Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.hamcrest.library;bundle-version="[1.1.0,2.0.0)"
-Export-Package: org.eclipse.jgit.transport.ssh;version="5.3.3";x-friends:="org.eclipse.jgit.ssh.apache.test"
+Export-Package: org.eclipse.jgit.transport.ssh;version="5.4.0";x-friends:="org.eclipse.jgit.ssh.apache.test"
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 21c0642..6248756 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
@@ -190,7 +190,7 @@
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
-          <argLine>@{argLine} -Xmx1024m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
+          <argLine>@{argLine} -Xmx768m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
           <includes>
             <include>**/*Test.java</include>
             <include>**/*Tests.java</include>
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_expensive_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_expensive_testpass
new file mode 100644
index 0000000..904cf30
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_expensive_testpass
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAsFN8vig
+Nw4/Ow6xbb7MAZAAABAAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEZXZRjuttLufaP8
+wFD/i4lYPnKk01z46Jwv/9U4mPioAAAAkHLErPaXeC179rzXMaSwClstzsKvJ/Gqh2cY8d
+cWzymXtKZcivWMKesRHbC+1qRx53ofx15IzT5Fmg6NuNk4sm2s+lH8x8HN3CPWBfjGIelP
+iQUR6M6Y91mPigpRC2HUJmJIaFNdrRqFF84a5+qyK//tdy1fv4gNMLi5yPdXiL/Ttw05FS
+LkFikjfvSGZSO/MA==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_expensive_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_expensive_testpass.pub
new file mode 100644
index 0000000..65038b5
--- /dev/null
+++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_expensive_testpass.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEZXZRjuttLufaP8wFD/i4lYPnKk01z46Jwv/9U4mPio test
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
index 5a01eae..e6100e2 100644
--- a/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
@@ -43,6 +43,7 @@
 
 package org.eclipse.jgit.lib;
 
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -50,8 +51,7 @@ public class Sets {
 	@SafeVarargs
 	public static <T> Set<T> of(T... elements) {
 		Set<T> ret = new HashSet<>();
-		for (T element : elements)
-			ret.add(element);
+		ret.addAll(Arrays.asList(elements));
 		return ret;
 	}
 }
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java
index 2f367ba..b8c90b2 100644
--- a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java
@@ -89,7 +89,9 @@ public abstract class SshTestBase extends SshTestHarness {
 			"id_rsa_4096_testpass", //
 			"id_ecdsa_256_testpass", //
 			"id_ecdsa_384_testpass", //
-			"id_ecdsa_521_testpass" };
+			"id_ecdsa_521_testpass", //
+			"id_ed25519_testpass", //
+			"id_ed25519_expensive_testpass" };
 
 	protected File defaultCloneDir;
 
diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl
index dc21029..6b5c7a1 100644
--- a/org.eclipse.jgit.test/tests.bzl
+++ b/org.eclipse.jgit.test/tests.bzl
@@ -45,7 +45,7 @@
             additional_deps = [
                 "//lib:jsch",
                 "//lib:jzlib",
-                "//lib:sshd-core",
+                "//lib:sshd-osgi",
                 "//lib:sshd-sftp",
                 ":sshd-helpers",
             ]
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
index 139f199..4eeaf4d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
@@ -128,7 +128,7 @@ public void testCleanDirs() throws NoWorkTreeException, GitAPIException {
 		status = git.status().call();
 		files = status.getUntracked();
 
-		assertTrue(files.size() == 0);
+		assertTrue(files.isEmpty());
 		assertTrue(cleanedFiles.contains("File2.txt"));
 		assertTrue(cleanedFiles.contains("File3.txt"));
 		assertTrue(!cleanedFiles.contains("sub-noclean/File1.txt"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index cd96f41..4bfac15 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -485,7 +485,7 @@ public void testReflogs() throws Exception {
 	}
 
 	private static String reflogComments(List<ReflogEntry> entries) {
-		StringBuffer b = new StringBuffer();
+		StringBuilder b = new StringBuilder();
 		for (ReflogEntry e : entries) {
 			b.append(e.getComment()).append(";");
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
index 807079e..bc3ac7a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
@@ -203,7 +203,6 @@ public void testDescribeMultiMatch() throws Exception {
 			assertNotNull(describe(c1, "v1.1*", "v1.0*"));
 			assertNotNull(describe(c2, "v1.0*", "v1.1*"));
 			assertNotNull(describe(c2, "v1.1*", "v1.0*"));
-
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index 9461c42..2fd3788 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -346,14 +346,11 @@ private enum TestPullMode {
 	@Test
 	/** global rebase config should be respected */
 	public void testPullWithRebasePreserve1Config() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("pull", null, "rebase", "preserve");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("pull", null, "rebase", "preserve");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
 	}
@@ -361,15 +358,12 @@ public PullResult call() throws Exception {
 	@Test
 	/** the branch-local config should win over the global config */
 	public void testPullWithRebasePreserveConfig2() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("pull", null, "rebase", "false");
-				config.setString("branch", "master", "rebase", "preserve");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("pull", null, "rebase", "false");
+			config.setString("branch", "master", "rebase", "preserve");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
 	}
@@ -377,14 +371,11 @@ public PullResult call() throws Exception {
 	@Test
 	/** the branch-local config should be respected */
 	public void testPullWithRebasePreserveConfig3() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("branch", "master", "rebase", "preserve");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("branch", "master", "rebase", "preserve");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
 	}
@@ -392,14 +383,11 @@ public PullResult call() throws Exception {
 	@Test
 	/** global rebase config should be respected */
 	public void testPullWithRebaseConfig1() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("pull", null, "rebase", "true");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("pull", null, "rebase", "true");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.REBASE);
 	}
@@ -407,15 +395,12 @@ public PullResult call() throws Exception {
 	@Test
 	/** the branch-local config should win over the global config */
 	public void testPullWithRebaseConfig2() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("pull", null, "rebase", "preserve");
-				config.setString("branch", "master", "rebase", "true");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("pull", null, "rebase", "preserve");
+			config.setString("branch", "master", "rebase", "true");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.REBASE);
 	}
@@ -423,14 +408,11 @@ public PullResult call() throws Exception {
 	@Test
 	/** the branch-local config should be respected */
 	public void testPullWithRebaseConfig3() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("branch", "master", "rebase", "true");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("branch", "master", "rebase", "true");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.REBASE);
 	}
@@ -438,27 +420,19 @@ public PullResult call() throws Exception {
 	@Test
 	/** without config it should merge */
 	public void testPullWithoutConfig() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				return target.pull().call();
-			}
-		};
+		Callable<PullResult> setup = target.pull()::call;
 		doTestPullWithRebase(setup, TestPullMode.MERGE);
 	}
 
 	@Test
 	/** the branch local config should win over the global config */
 	public void testPullWithMergeConfig() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("pull", null, "rebase", "true");
-				config.setString("branch", "master", "rebase", "false");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("pull", null, "rebase", "true");
+			config.setString("branch", "master", "rebase", "false");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.MERGE);
 	}
@@ -466,14 +440,11 @@ public PullResult call() throws Exception {
 	@Test
 	/** the branch local config should win over the global config */
 	public void testPullWithMergeConfig2() throws Exception {
-		Callable<PullResult> setup = new Callable<PullResult>() {
-			@Override
-			public PullResult call() throws Exception {
-				StoredConfig config = dbTarget.getConfig();
-				config.setString("pull", null, "rebase", "false");
-				config.save();
-				return target.pull().call();
-			}
+		Callable<PullResult> setup = () -> {
+			StoredConfig config = dbTarget.getConfig();
+			config.setString("pull", null, "rebase", "false");
+			config.save();
+			return target.pull().call();
 		};
 		doTestPullWithRebase(setup, TestPullMode.MERGE);
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
index 1d9cd78..d6aead4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
@@ -49,6 +49,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -854,8 +855,7 @@ private File writeGlobalAttributeFile(String fileName, String... attributes)
 
 	static Set<Attribute> asSet(Attribute... attrs) {
 		HashSet<Attribute> result = new HashSet<>();
-		for (Attribute attr : attrs)
-			result.add(attr);
+		result.addAll(Arrays.asList(attrs));
 		return result;
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
index 014406e..972dc60 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
@@ -205,8 +205,9 @@ public void testBuildThenClear() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 		assertFalse(dc.hasUnmergedPaths());
 
@@ -229,8 +230,9 @@ public void testDetectUnmergedPaths() throws Exception {
 		ents[2].setFileMode(FileMode.REGULAR_FILE);
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 		assertTrue(dc.hasUnmergedPaths());
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java
index 3598f3a..6020a74 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java
@@ -71,8 +71,9 @@ public void testPathFilterGroup_DoesNotSkipTail() throws Exception {
 		}
 		{
 			final DirCacheBuilder b = dc.builder();
-			for (int i = 0; i < ents.length; i++)
-				b.add(ents[i]);
+			for (DirCacheEntry ent : ents) {
+				b.add(ent);
+			}
 			b.finish();
 		}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
index b6291bf..0c1131b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
@@ -210,12 +210,8 @@ final class ReceivedEventMarkerException extends RuntimeException {
 		boolean receivedEvent = false;
 
 		DirCache dc = db.lockDirCache();
-		IndexChangedListener listener = new IndexChangedListener() {
-
-			@Override
-			public void onIndexChanged(IndexChangedEvent event) {
-				throw new ReceivedEventMarkerException();
-			}
+		IndexChangedListener listener = (IndexChangedEvent event) -> {
+			throw new ReceivedEventMarkerException();
 		};
 
 		ListenerList l = db.getListenerList();
@@ -238,12 +234,8 @@ public void onIndexChanged(IndexChangedEvent event) {
 		// do the same again, as this doesn't change index compared to first
 		// round we should get no event this time
 		dc = db.lockDirCache();
-		listener = new IndexChangedListener() {
-
-			@Override
-			public void onIndexChanged(IndexChangedEvent event) {
-				throw new ReceivedEventMarkerException();
-			}
+		listener = (IndexChangedEvent event) -> {
+			throw new ReceivedEventMarkerException();
 		};
 
 		l = db.getListenerList();
@@ -301,8 +293,9 @@ public void testAdd_InGitSortOrder() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		assertEquals(paths.length, dc.getEntryCount());
@@ -351,8 +344,9 @@ public void testBuilderClear() throws Exception {
 		}
 		{
 			final DirCacheBuilder b = dc.builder();
-			for (int i = 0; i < ents.length; i++)
-				b.add(ents[i]);
+			for (DirCacheEntry ent : ents) {
+				b.add(ent);
+			}
 			b.finish();
 		}
 		assertEquals(paths.length, dc.getEntryCount());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java
index 3b8c6ee..7fc2801 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java
@@ -66,8 +66,9 @@ public void testEntriesWithin() throws Exception {
 		final int aLast = 3;
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		assertEquals(paths.length, dc.getEntryCount());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java
index 82565fc..bc99aee 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java
@@ -94,8 +94,9 @@ public void testNoSubtree_NoTreeWalk() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		final DirCacheIterator i = new DirCacheIterator(dc);
@@ -121,8 +122,9 @@ public void testNoSubtree_WithTreeWalk() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		final DirCacheIterator i = new DirCacheIterator(dc);
@@ -154,8 +156,9 @@ public void testSingleSubtree_NoRecursion() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		final String[] expPaths = { "a-", "a", "a0b" };
@@ -200,8 +203,9 @@ public void testSingleSubtree_Recursive() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		final DirCacheIterator i = new DirCacheIterator(dc);
@@ -236,8 +240,9 @@ public void testTwoLevelSubtree_Recursive() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		try (TreeWalk tw = new TreeWalk(db)) {
@@ -271,8 +276,9 @@ public void testReset() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		DirCacheIterator dci = new DirCacheIterator(dc);
@@ -365,8 +371,9 @@ public void testBackBug396127() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		DirCacheIterator dci = new DirCacheIterator(dc);
@@ -398,8 +405,9 @@ public void testTwoLevelSubtree_FilterPath() throws Exception {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		try (TreeWalk tw = new TreeWalk(db)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java
index f662e26..513db75 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java
@@ -102,8 +102,9 @@ public void testSingleSubtree() throws Exception {
 		final int aLast = 3;
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		assertNull(dc.getCacheTree(false));
@@ -142,8 +143,9 @@ public void testTwoLevelSubtree() throws Exception {
 		final int acLast = 3;
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 		b.finish();
 
 		assertNull(dc.getCacheTree(false));
@@ -198,8 +200,9 @@ public void testWriteReadTree() throws CorruptObjectException, IOException {
 		}
 
 		final DirCacheBuilder b = dc.builder();
-		for (int i = 0; i < ents.length; i++)
-			b.add(ents[i]);
+		for (DirCacheEntry ent : ents) {
+			b.add(ent);
+		}
 
 		b.commit();
 		DirCache read = db.readDirCache();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java
index 3624100..b6f312d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java
@@ -54,12 +54,9 @@ public class ConfigChangeEventTest extends RepositoryTestCase {
 	@Test
 	public void testFileRepository_ChangeEventsOnlyOnSave() throws Exception {
 		final ConfigChangedEvent[] events = new ConfigChangedEvent[1];
-		db.getListenerList().addConfigChangedListener(
-				new ConfigChangedListener() {
-					@Override
-					public void onConfigChanged(ConfigChangedEvent event) {
-						events[0] = event;
-					}
+		db.getListenerList()
+				.addConfigChangedListener((ConfigChangedEvent event) -> {
+					events[0] = event;
 				});
 		FileBasedConfig config = db.getConfig();
 		assertNull(events[0]);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/InMemoryRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/InMemoryRepositoryTest.java
new file mode 100644
index 0000000..bab6110
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/InMemoryRepositoryTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.junit.Test;
+
+public class InMemoryRepositoryTest {
+
+	@Test
+	public void keepUpdateIndexPeelingTag() throws Exception {
+		InMemoryRepository repo = new InMemoryRepository(
+				new DfsRepositoryDescription());
+		try (TestRepository<InMemoryRepository> git = new TestRepository<>(
+				repo)) {
+			RevCommit commit = git.branch("master").commit()
+					.message("first commit").create();
+			RevTag tag = git.tag("v0.1", commit);
+			git.update("refs/tags/v0.1", tag);
+
+			Ref unpeeledTag = new ObjectIdRef.Unpeeled(Storage.LOOSE,
+					"refs/tags/v0.1", tag.getId(), 1000);
+
+			Ref peeledTag = repo.getRefDatabase().peel(unpeeledTag);
+			assertTrue(peeledTag instanceof ObjectIdRef.PeeledTag);
+			assertEquals(1000, peeledTag.getUpdateIndex());
+		}
+	}
+
+	@Test
+	public void keepUpdateIndexPeelingNonTag() throws Exception {
+		InMemoryRepository repo = new InMemoryRepository(
+				new DfsRepositoryDescription());
+		try (TestRepository<InMemoryRepository> git = new TestRepository<>(
+				repo)) {
+			RevCommit commit = git.branch("master").commit()
+					.message("first commit").create();
+
+			Ref unpeeledRef = new ObjectIdRef.Unpeeled(Storage.LOOSE,
+					"refs/heads/master", commit.getId(), 1000);
+			Ref peeledRef = repo.getRefDatabase().peel(unpeeledRef);
+			assertTrue(peeledRef instanceof ObjectIdRef.PeeledNonTag);
+			assertEquals(1000, peeledRef.getUpdateIndex());
+		}
+	}
+
+	@Test
+	public void sha1ToTip_ref() throws Exception {
+		InMemoryRepository repo = new InMemoryRepository(
+				new DfsRepositoryDescription());
+		try (TestRepository<InMemoryRepository> git = new TestRepository<>(
+				repo)) {
+			RevCommit commit = git.branch("master").commit()
+					.message("first commit").create();
+
+			Set<Ref> tipsWithSha1 = repo.getRefDatabase()
+					.getTipsWithSha1(commit.getId());
+			assertEquals(1, tipsWithSha1.size());
+			Ref ref = tipsWithSha1.iterator().next();
+			assertEquals(ref.getName(), "refs/heads/master");
+			assertEquals(commit.getId(), ref.getObjectId());
+		}
+	}
+
+	@Test
+	public void sha1ToTip_annotatedTag() throws Exception {
+		InMemoryRepository repo = new InMemoryRepository(
+				new DfsRepositoryDescription());
+		try (TestRepository<InMemoryRepository> git = new TestRepository<>(
+				repo)) {
+			RevCommit commit = git.commit()
+					.message("first commit").create();
+			RevTag tagObj = git.tag("v0.1", commit);
+			git.update("refs/tags/v0.1", tagObj);
+			Set<Ref> tipsWithSha1 = repo.getRefDatabase()
+					.getTipsWithSha1(commit.getId());
+			assertEquals(1, tipsWithSha1.size());
+			Ref ref = tipsWithSha1.iterator().next();
+			assertEquals(ref.getName(), "refs/tags/v0.1");
+			assertEquals(commit.getId(), ref.getPeeledObjectId());
+		}
+	}
+
+	@Test
+	public void sha1ToTip_tag() throws Exception {
+		InMemoryRepository repo = new InMemoryRepository(
+				new DfsRepositoryDescription());
+		try (TestRepository<InMemoryRepository> git = new TestRepository<>(
+				repo)) {
+			RevCommit commit = git.commit().message("first commit").create();
+			git.update("refs/tags/v0.2", commit);
+			Set<Ref> tipsWithSha1 = repo.getRefDatabase()
+					.getTipsWithSha1(commit.getId());
+			assertEquals(1, tipsWithSha1.size());
+			Ref ref = tipsWithSha1.iterator().next();
+			assertEquals(ref.getName(), "refs/tags/v0.2");
+			assertEquals(commit.getId(), ref.getObjectId());
+		}
+	}
+}
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 05f7c65..9e7d41a 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
@@ -239,20 +239,15 @@ public void testInterruptGc() throws Exception {
 		SampleDataRepositoryTestCase.copyCGitTestPacks(repo);
 		ExecutorService executor = Executors.newSingleThreadExecutor();
 		final CountDownLatch latch = new CountDownLatch(1);
-		Future<Collection<PackFile>> result = executor
-				.submit(new Callable<Collection<PackFile>>() {
-
-					@Override
-					public Collection<PackFile> call() throws Exception {
-						long start = System.currentTimeMillis();
-						System.out.println("starting gc");
-						latch.countDown();
-						Collection<PackFile> r = gc.gc();
-						System.out.println("gc took "
-								+ (System.currentTimeMillis() - start) + " ms");
-						return r;
-					}
-				});
+		Future<Collection<PackFile>> result = executor.submit(() -> {
+			long start = System.currentTimeMillis();
+			System.out.println("starting gc");
+			latch.countDown();
+			Collection<PackFile> r = gc.gc();
+			System.out.println(
+					"gc took " + (System.currentTimeMillis() - start) + " ms");
+			return r;
+		});
 		try {
 			latch.await();
 			Thread.sleep(5);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
index c43bdbd..1fa5aa0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
@@ -155,40 +155,32 @@ public void whileRefUpdatedRefUpdateSucceeds()
 		final CyclicBarrier packRefsDone = new CyclicBarrier(2);
 		ExecutorService pool = Executors.newFixedThreadPool(2);
 		try {
-			Future<Result> result = pool.submit(new Callable<Result>() {
-
-				@Override
-				public Result call() throws Exception {
-					RefUpdate update = new RefDirectoryUpdate(
-							(RefDirectory) repo.getRefDatabase(),
-							repo.exactRef("refs/tags/t")) {
-						@Override
-						public boolean isForceUpdate() {
-							try {
-								refUpdateLockedRef.await();
-								packRefsDone.await();
-							} catch (InterruptedException e) {
-								Thread.currentThread().interrupt();
-							} catch (BrokenBarrierException e) {
-								Thread.currentThread().interrupt();
-							}
-							return super.isForceUpdate();
+			Future<Result> result = pool.submit(() -> {
+				RefUpdate update = new RefDirectoryUpdate(
+						(RefDirectory) repo.getRefDatabase(),
+						repo.exactRef("refs/tags/t")) {
+					@Override
+					public boolean isForceUpdate() {
+						try {
+							refUpdateLockedRef.await();
+							packRefsDone.await();
+						} catch (InterruptedException
+								| BrokenBarrierException e) {
+							Thread.currentThread().interrupt();
 						}
-					};
-					update.setForceUpdate(true);
-					update.setNewObjectId(b);
-					return update.update();
-				}
+						return super.isForceUpdate();
+					}
+				};
+				update.setForceUpdate(true);
+				update.setNewObjectId(b);
+				return update.update();
 			});
 
-			pool.submit(new Callable<Void>() {
-				@Override
-				public Void call() throws Exception {
-					refUpdateLockedRef.await();
-					gc.packRefs();
-					packRefsDone.await();
-					return null;
-				}
+			pool.submit(() -> {
+				refUpdateLockedRef.await();
+				gc.packRefs();
+				packRefsDone.await();
+				return null;
 			});
 
 			assertSame(result.get(), Result.FORCED);
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 a3a302d..a9d0dc2 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
@@ -47,7 +47,6 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.text.MessageFormat;
@@ -167,12 +166,8 @@ public void testScanningForPackfiles() throws Exception {
 			// Bug. To show the bug we sleep for more than 2500ms
 			Thread.sleep(2600);
 
-			File[] ret = packsFolder.listFiles(new FilenameFilter() {
-				@Override
-				public boolean accept(File dir, String name) {
-					return name.endsWith(".pack");
-				}
-			});
+			File[] ret = packsFolder.listFiles(
+					(File dir, String name) -> name.endsWith(".pack"));
 			assertTrue(ret != null && ret.length == 1);
 			Assume.assumeTrue(tmpFile.lastModified() == ret[0].lastModified());
 
@@ -220,12 +215,8 @@ public void testShallowFileCorrupt()
 
 	private Collection<Callable<ObjectId>> blobInsertersForTheSameFanOutDir(
 			final ObjectDirectory dir) {
-		Callable<ObjectId> callable = new Callable<ObjectId>() {
-			@Override
-			public ObjectId call() throws Exception {
-				return dir.newInserter().insert(Constants.OBJ_BLOB, new byte[0]);
-			}
-		};
+		Callable<ObjectId> callable = () -> dir.newInserter()
+				.insert(Constants.OBJ_BLOB, new byte[0]);
 		return Collections.nCopies(4, callable);
 	}
 
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 ca44862..5d0a7e2 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
@@ -60,7 +60,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -881,12 +880,8 @@ private void verifyObjectsOrder(ObjectId objectsOrder[]) {
 		for (MutableEntry me : pack) {
 			entries.add(me.cloneEntry());
 		}
-		Collections.sort(entries, new Comparator<PackIndex.MutableEntry>() {
-			@Override
-			public int compare(MutableEntry o1, MutableEntry o2) {
-				return Long.signum(o1.getOffset() - o2.getOffset());
-			}
-		});
+		Collections.sort(entries, (MutableEntry o1, MutableEntry o2) -> Long
+				.signum(o1.getOffset() - o2.getOffset()));
 
 		int i = 0;
 		for (MutableEntry me : entries) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
index 7b3684c..bd9572b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
@@ -70,7 +70,6 @@
 import org.eclipse.jgit.errors.LockFailedException;
 import org.eclipse.jgit.events.ListenerHandle;
 import org.eclipse.jgit.events.RefsChangedEvent;
-import org.eclipse.jgit.events.RefsChangedListener;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.AnyObjectId;
@@ -570,12 +569,8 @@ public void testGetRefs_LooseSorting_Bug_348834() throws IOException {
 		final int[] count = new int[1];
 
 		ListenerHandle listener = Repository.getGlobalListenerList()
-				.addRefsChangedListener(new RefsChangedListener() {
-
-					@Override
-					public void onRefsChanged(RefsChangedEvent event) {
-						count[0]++;
-					}
+				.addRefsChangedListener((RefsChangedEvent event) -> {
+					count[0]++;
 				});
 
 		refs = refdir.getRefs(RefDatabase.ALL);
@@ -1316,19 +1311,15 @@ public void testRefsChangedStackOverflow() throws Exception {
 		final AtomicReference<StackOverflowError> error = new AtomicReference<>();
 		final AtomicReference<IOException> exception = new AtomicReference<>();
 		final AtomicInteger changeCount = new AtomicInteger();
-		newRepo.getListenerList().addRefsChangedListener(
-				new RefsChangedListener() {
-
-					@Override
-					public void onRefsChanged(RefsChangedEvent event) {
-						try {
-							refDb.getRefsByPrefix("ref");
-							changeCount.incrementAndGet();
-						} catch (StackOverflowError soe) {
-							error.set(soe);
-						} catch (IOException ioe) {
-							exception.set(ioe);
-						}
+		newRepo.getListenerList()
+				.addRefsChangedListener((RefsChangedEvent event) -> {
+					try {
+						refDb.getRefsByPrefix("ref");
+						changeCount.incrementAndGet();
+					} catch (StackOverflowError soe) {
+						error.set(soe);
+					} catch (IOException ioe) {
+						exception.set(ioe);
 					}
 				});
 		refDb.getRefsByPrefix("ref");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
index 8ef21e6..c3f5baa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
@@ -633,35 +633,23 @@ private static ReceiveCommand command(AnyObjectId a, AnyObjectId b,
 
 	private void symref(String name, String dst)
 			throws IOException {
-		commit(new Function() {
-			@Override
-			public boolean apply(ObjectReader reader, RefTree tree)
-					throws IOException {
-				Ref old = tree.exactRef(reader, name);
-				Command n = new Command(
-					old,
-					new SymbolicRef(
-						name,
-						new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null)));
-				return tree.apply(Collections.singleton(n));
-			}
+		commit((ObjectReader reader, RefTree tree) -> {
+			Ref old = tree.exactRef(reader, name);
+			Command n = new Command(old, new SymbolicRef(name,
+					new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null)));
+			return tree.apply(Collections.singleton(n));
 		});
 	}
 
 	private void update(String name, ObjectId id)
 			throws IOException {
-		commit(new Function() {
-			@Override
-			public boolean apply(ObjectReader reader, RefTree tree)
-					throws IOException {
-				Ref old = tree.exactRef(reader, name);
-				Command n;
-				try (RevWalk rw = new RevWalk(repo)) {
-					n = new Command(old,
-							Command.toRef(rw, id, null, name, true));
-				}
-				return tree.apply(Collections.singleton(n));
+		commit((ObjectReader reader, RefTree tree) -> {
+			Ref old = tree.exactRef(reader, name);
+			Command n;
+			try (RevWalk rw = new RevWalk(repo)) {
+				n = new Command(old, Command.toRef(rw, id, null, name, true));
 			}
+			return tree.apply(Collections.singleton(n));
 		});
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index 21d8d66..5a4bd88 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -1450,6 +1450,13 @@ public void testDropBackslashFromInvalidEscapeSequenceInSubsectionName()
 		assertEquals("xt", parseEscapedSubsection("\"x\\t\""));
 	}
 
+	@Test
+	public void testInvalidGroupHeader() throws ConfigInvalidException {
+		expectedEx.expect(ConfigInvalidException.class);
+		expectedEx.expectMessage(JGitText.get().badGroupHeader);
+		parse("[foo \"bar\" ]\nfoo=bar\n");
+	}
+
 	private static void assertValueRoundTrip(String value)
 			throws ConfigInvalidException {
 		assertValueRoundTrip(value, value);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java
index 531c918..e7a5b28 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java
@@ -1678,7 +1678,6 @@ public void testRejectInvalidWindowsCharacters() {
 		rejectName('>');
 		rejectName(':');
 		rejectName('"');
-		rejectName('/');
 		rejectName('\\');
 		rejectName('|');
 		rejectName('?');
@@ -1693,7 +1692,8 @@ private void rejectName(char c) {
 			checkOneName("te" + c + "st");
 			fail("incorrectly accepted with " + c);
 		} catch (CorruptObjectException e) {
-			assertEquals("name contains '" + c + "'", e.getMessage());
+
+			assertEquals("char '" + c + "' not allowed in Windows filename", e.getMessage());
 		}
 	}
 
@@ -1703,7 +1703,19 @@ private void rejectName(byte c) {
 			checkOneName("te" + ((char) c) + "st");
 			fail("incorrectly accepted with 0x" + h);
 		} catch (CorruptObjectException e) {
-			assertEquals("name contains byte 0x" + h, e.getMessage());
+			assertEquals("byte 0x" + h + " not allowed in Windows filename", e.getMessage());
+		}
+	}
+
+
+	@Test
+	public void testRejectInvalidCharacter() {
+		try {
+			checkOneName("te/st");
+			fail("incorrectly accepted with /");
+		} catch (CorruptObjectException e) {
+
+			assertEquals("name contains '/'", e.getMessage());
 		}
 	}
 
@@ -1763,16 +1775,13 @@ private void assertSkipListRejects(String msg, int type, byte[] data) {
 	}
 
 	private static ObjectIdSet set(ObjectId... ids) {
-		return new ObjectIdSet() {
-			@Override
-			public boolean contains(AnyObjectId objectId) {
-				for (ObjectId id : ids) {
-					if (id.equals(objectId)) {
-						return true;
-					}
+		return (AnyObjectId objectId) -> {
+			for (ObjectId id : ids) {
+				if (id.equals(objectId)) {
+					return true;
 				}
-				return false;
 			}
+			return false;
 		};
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index 7d2c4a2..b53d5b9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -58,8 +58,10 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.TreeSet;
 
 import org.eclipse.jgit.lib.Ref.Storage;
@@ -313,7 +315,7 @@ public void testResolvedSymRef() throws IOException {
 		assertEquals(dst.isPeeled(), ref.isPeeled());
 	}
 
-	private static void checkContainsRef(List<Ref> haystack, Ref needle) {
+	private static void checkContainsRef(Collection<Ref> haystack, Ref needle) {
 		for (Ref ref : haystack) {
 			if (ref.getName().equals(needle.getName()) &&
 					ref.getObjectId().equals(needle.getObjectId())) {
@@ -347,4 +349,17 @@ public void testGetRefsByPrefixes() throws IOException {
 		checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
 		checkContainsRef(refs, db.exactRef("refs/tags/A"));
 	}
+
+	@Test
+	public void testResolveTipSha1() throws IOException {
+		ObjectId masterId = db.resolve("refs/heads/master");
+		Set<Ref> resolved = db.getRefDatabase().getTipsWithSha1(masterId);
+
+		assertEquals(2, resolved.size());
+		checkContainsRef(resolved, db.exactRef("refs/heads/master"));
+		checkContainsRef(resolved, db.exactRef("HEAD"));
+
+		assertEquals(db.getRefDatabase()
+				.getTipsWithSha1(ObjectId.zeroId()).size(), 0);
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
index df1a52d..f2f277c6e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
@@ -45,7 +45,6 @@
 
 package org.eclipse.jgit.lib;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -63,7 +62,8 @@ public void testlogAllRefUpdates() throws Exception {
 
 		// check that there are no entries in the reflog and turn off writing
 		// reflogs
-		assertEquals(0, db.getReflogReader(Constants.HEAD).getReverseEntries().size());
+		assertTrue(db.getReflogReader(Constants.HEAD).getReverseEntries()
+				.isEmpty());
 		final FileBasedConfig cfg = db.getConfig();
 		cfg.setBoolean("core", null, "logallrefupdates", false);
 		cfg.save();
@@ -72,9 +72,8 @@ public void testlogAllRefUpdates() throws Exception {
 		// written
 		commit("A Commit\n", commitTime, tz);
 		commitTime += 60 * 1000;
-		assertTrue(
-				"Reflog for HEAD still contain no entry",
-				db.getReflogReader(Constants.HEAD).getReverseEntries().size() == 0);
+		assertTrue("Reflog for HEAD still contain no entry", db
+				.getReflogReader(Constants.HEAD).getReverseEntries().isEmpty());
 
 		// set the logAllRefUpdates parameter to true and check it
 		cfg.setBoolean("core", null, "logallrefupdates", true);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
index 545a188..6829822 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
@@ -61,29 +61,26 @@ public void testFailsMethodsOnBackgroundThread()
 		final MockProgressMonitor mock = new MockProgressMonitor();
 		final ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(mock);
 
-		runOnThread(new Runnable() {
-			@Override
-			public void run() {
-				try {
-					pm.start(1);
-					fail("start did not fail on background thread");
-				} catch (IllegalStateException notMainThread) {
-					// Expected result
-				}
+		runOnThread(() -> {
+			try {
+				pm.start(1);
+				fail("start did not fail on background thread");
+			} catch (IllegalStateException notMainThread) {
+				// Expected result
+			}
 
-				try {
-					pm.beginTask("title", 1);
-					fail("beginTask did not fail on background thread");
-				} catch (IllegalStateException notMainThread) {
-					// Expected result
-				}
+			try {
+				pm.beginTask("title", 1);
+				fail("beginTask did not fail on background thread");
+			} catch (IllegalStateException notMainThread) {
+				// Expected result
+			}
 
-				try {
-					pm.endTask();
-					fail("endTask did not fail on background thread");
-				} catch (IllegalStateException notMainThread) {
-					// Expected result
-				}
+			try {
+				pm.endTask();
+				fail("endTask did not fail on background thread");
+			} catch (IllegalStateException notMainThread) {
+				// Expected result
 			}
 		});
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java
new file mode 100644
index 0000000..3a78e1e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java
@@ -0,0 +1,139 @@
+package org.eclipse.jgit.revwalk;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.GC;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BitmapCalculatorTest extends LocalDiskRepositoryTestCase {
+	TestRepository<FileRepository> repo;
+
+	/** {@inheritDoc} */
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		FileRepository db = createWorkRepository();
+		repo = new TestRepository<>(db);
+	}
+
+	@Test
+	public void addOnlyCommits() throws Exception {
+		RevBlob abBlob = repo.blob("a_b_content");
+		RevCommit root = repo.commit().add("a/b", abBlob).create();
+		repo.update("refs/heads/master", root);
+
+		// GC creates bitmap index with ALL objects
+		GC gc = new GC(repo.getRepository());
+		gc.setAuto(false);
+		gc.gc();
+
+		// These objects are not in the bitmap index.
+		RevBlob acBlob = repo.blob("a_c_content");
+		RevCommit head = repo.commit().parent(root).add("a/c", acBlob).create();
+		repo.update("refs/heads/master", head);
+
+		BitmapCalculator bitmapWalker = new BitmapCalculator(repo.getRevWalk());
+		BitmapBuilder bitmap = bitmapWalker
+				.getBitmap(head, NullProgressMonitor.INSTANCE);
+
+		assertTrue(bitmap.contains(root.getId()));
+		assertTrue(bitmap.contains(root.getTree().getId()));
+		assertTrue(bitmap.contains(abBlob.getId()));
+
+		// BitmapCalculator added only the commit, no other objects.
+		assertTrue(bitmap.contains(head.getId()));
+		assertFalse(bitmap.contains(head.getTree().getId()));
+		assertFalse(bitmap.contains(acBlob.getId()));
+	}
+
+	@Test
+	public void walkUntilBitmap() throws Exception {
+		RevCommit root = repo.commit().create();
+		repo.update("refs/heads/master", root);
+
+		// GC creates bitmap index with ALL objects
+		GC gc = new GC(repo.getRepository());
+		gc.setAuto(false);
+		gc.gc();
+
+		// These objects are not in the bitmap index.
+		RevCommit commit1 = repo.commit(root);
+		RevCommit commit2 = repo.commit(commit1);
+		repo.update("refs/heads/master", commit2);
+
+		CounterProgressMonitor monitor = new CounterProgressMonitor();
+		BitmapCalculator bitmapWalker = new BitmapCalculator(repo.getRevWalk());
+		BitmapBuilder bitmap = bitmapWalker.getBitmap(commit2, monitor);
+
+		assertTrue(bitmap.contains(root));
+		assertTrue(bitmap.contains(commit1));
+		assertTrue(bitmap.contains(commit2));
+		assertEquals(2, monitor.getUpdates());
+	}
+
+	@Test
+	public void noNeedToWalk() throws Exception {
+		RevCommit root = repo.commit().create();
+		RevCommit commit1 = repo.commit(root);
+		RevCommit commit2 = repo.commit(commit1);
+		repo.update("refs/heads/master", commit2);
+
+		// GC creates bitmap index with ALL objects
+		GC gc = new GC(repo.getRepository());
+		gc.setAuto(false);
+		gc.gc();
+
+		CounterProgressMonitor monitor = new CounterProgressMonitor();
+		BitmapCalculator bitmapWalker = new BitmapCalculator(repo.getRevWalk());
+		BitmapBuilder bitmap = bitmapWalker.getBitmap(commit2, monitor);
+
+		assertTrue(bitmap.contains(root));
+		assertTrue(bitmap.contains(commit1));
+		assertTrue(bitmap.contains(commit2));
+		assertEquals(0, monitor.getUpdates());
+	}
+
+	private static class CounterProgressMonitor implements ProgressMonitor {
+
+		private int counter;
+
+		@Override
+		public void start(int totalTasks) {
+			// Nothing to do in tests
+		}
+
+		@Override
+		public void beginTask(String title, int totalWork) {
+			// Nothing to to in tests
+		}
+
+		@Override
+		public void update(int completed) {
+			counter += 1;
+		}
+
+		@Override
+		public void endTask() {
+			// Nothing to do in tests
+		}
+
+		@Override
+		public boolean isCancelled() {
+			return false;
+		}
+
+		int getUpdates() {
+			return counter;
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedReachabilityCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedReachabilityCheckerTest.java
new file mode 100644
index 0000000..5d3adf5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmappedReachabilityCheckerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019, Google LLC
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.revwalk;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.GC;
+import org.eclipse.jgit.junit.TestRepository;
+
+public class BitmappedReachabilityCheckerTest
+		extends ReachabilityCheckerTestCase {
+
+	@Override
+	protected ReachabilityChecker getChecker(
+			TestRepository<FileRepository> repository) throws Exception {
+		// GC generates the bitmaps
+		GC gc = new GC(repo.getRepository());
+		gc.setAuto(false);
+		gc.gc();
+
+		// This is null when the test didn't create any branch
+		assertNotNull("Probably the test didn't define any ref",
+				repo.getRevWalk().getObjectReader().getBitmapIndex());
+
+		return new BitmappedReachabilityChecker(repository.getRevWalk());
+	}
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianReachabilityCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianReachabilityCheckerTest.java
new file mode 100644
index 0000000..8d3e78c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/PedestrianReachabilityCheckerTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.revwalk;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.TestRepository;
+
+public class PedestrianReachabilityCheckerTest
+		extends ReachabilityCheckerTestCase {
+
+	@Override
+	protected ReachabilityChecker getChecker(
+			TestRepository<FileRepository> repository) {
+		return new PedestrianReachabilityChecker(true, repository.getRevWalk());
+	}
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java
new file mode 100644
index 0000000..dd73e35
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.revwalk;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class ReachabilityCheckerTestCase
+		extends LocalDiskRepositoryTestCase {
+
+	protected abstract ReachabilityChecker getChecker(
+			TestRepository<FileRepository> repository) throws Exception;
+
+	TestRepository<FileRepository> repo;
+
+	/** {@inheritDoc} */
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		FileRepository db = createWorkRepository();
+		repo = new TestRepository<>(db);
+	}
+
+	@Test
+	public void reachable() throws Exception {
+		RevCommit a = repo.commit().create();
+		RevCommit b1 = repo.commit(a);
+		RevCommit b2 = repo.commit(b1);
+		RevCommit c1 = repo.commit(a);
+		RevCommit c2 = repo.commit(c1);
+		repo.update("refs/heads/checker", b2);
+
+		ReachabilityChecker checker = getChecker(repo);
+
+		assertReachable("reachable from one tip",
+				checker.areAllReachable(Arrays.asList(a), Arrays.asList(c2)));
+		assertReachable("reachable from another tip",
+				checker.areAllReachable(Arrays.asList(a), Arrays.asList(b2)));
+		assertReachable("reachable from itself",
+				checker.areAllReachable(Arrays.asList(a), Arrays.asList(b2)));
+	}
+
+	@Test
+	public void reachable_merge() throws Exception {
+		RevCommit a = repo.commit().create();
+		RevCommit b1 = repo.commit(a);
+		RevCommit b2 = repo.commit(b1);
+		RevCommit c1 = repo.commit(a);
+		RevCommit c2 = repo.commit(c1);
+		RevCommit merge = repo.commit(c2, b2);
+		repo.update("refs/heads/checker", merge);
+
+		ReachabilityChecker checker = getChecker(repo);
+
+		assertReachable("reachable through one branch",
+				checker.areAllReachable(Arrays.asList(b1),
+						Arrays.asList(merge)));
+		assertReachable("reachable through another branch",
+				checker.areAllReachable(Arrays.asList(c1),
+						Arrays.asList(merge)));
+		assertReachable("reachable, before the branching",
+				checker.areAllReachable(Arrays.asList(a),
+						Arrays.asList(merge)));
+	}
+
+	@Test
+	public void unreachable_isLaterCommit() throws Exception {
+		RevCommit a = repo.commit().create();
+		RevCommit b1 = repo.commit(a);
+		RevCommit b2 = repo.commit(b1);
+		repo.update("refs/heads/checker", b2);
+
+		ReachabilityChecker checker = getChecker(repo);
+
+		assertUnreachable("unreachable from the future",
+				checker.areAllReachable(Arrays.asList(b2), Arrays.asList(b1)));
+	}
+
+	@Test
+	public void unreachable_differentBranch() throws Exception {
+		RevCommit a = repo.commit().create();
+		RevCommit b1 = repo.commit(a);
+		RevCommit b2 = repo.commit(b1);
+		RevCommit c1 = repo.commit(a);
+		repo.update("refs/heads/checker", b2);
+
+		ReachabilityChecker checker = getChecker(repo);
+
+		assertUnreachable("unreachable from different branch",
+				checker.areAllReachable(Arrays.asList(c1), Arrays.asList(b2)));
+	}
+
+	@Test
+	public void reachable_longChain() throws Exception {
+		RevCommit root = repo.commit().create();
+		RevCommit head = root;
+		for (int i = 0; i < 10000; i++) {
+			head = repo.commit(head);
+		}
+		repo.update("refs/heads/master", head);
+
+		ReachabilityChecker checker = getChecker(repo);
+
+		assertReachable("reachable with long chain in the middle", checker
+				.areAllReachable(Arrays.asList(root), Arrays.asList(head)));
+	}
+
+	private static void assertReachable(String msg,
+			Optional<RevCommit> result) {
+		assertFalse(msg, result.isPresent());
+	}
+
+	private static void assertUnreachable(String msg,
+			Optional<RevCommit> result) {
+		assertTrue(msg, result.isPresent());
+	}
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
index 2c98c84..28a9c03 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
@@ -193,7 +193,8 @@ public void testRecvWantsFilter()
 		assertThat(request.getWantIds(),
 				hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
 						"f900c8326a43303685c46b279b9f70411bff1a4b"));
-		assertEquals(13000, request.getFilterBlobLimit());
+		assertEquals(13000, request.getFilterSpec().getBlobLimit());
+		assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
 	}
 
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
index dafa81e..740d92e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
@@ -232,7 +232,8 @@ public void testFetchWithNoneFilter() throws IOException {
 		ProtocolV2Parser parser = new ProtocolV2Parser(
 				ConfigBuilder.start().allowFilter().done());
 		FetchV2Request request = parser.parseFetchRequest(pckIn);
-		assertEquals(0, request.getFilterBlobLimit());
+		assertEquals(0, request.getFilterSpec().getBlobLimit());
+		assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
 	}
 
 	@Test
@@ -243,7 +244,20 @@ public void testFetchWithBlobSizeFilter() throws IOException {
 		ProtocolV2Parser parser = new ProtocolV2Parser(
 				ConfigBuilder.start().allowFilter().done());
 		FetchV2Request request = parser.parseFetchRequest(pckIn);
-		assertEquals(15, request.getFilterBlobLimit());
+		assertEquals(15, request.getFilterSpec().getBlobLimit());
+		assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
+	}
+
+	@Test
+	public void testFetchWithTreeDepthFilter() throws IOException {
+		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM,
+				"filter tree:3",
+				PacketLineIn.END);
+		ProtocolV2Parser parser = new ProtocolV2Parser(
+				ConfigBuilder.start().allowFilter().done());
+		FetchV2Request request = parser.parseFetchRequest(pckIn);
+		assertEquals(-1, request.getFilterSpec().getBlobLimit());
+		assertEquals(3, request.getFilterSpec().getTreeDepthLimit());
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
index cea432e..89d02d7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
@@ -67,9 +67,6 @@
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -91,27 +88,16 @@ public void setUp() throws Exception {
 		server = newRepo("server");
 		client = newRepo("client");
 		processedRefs = new ArrayList<>();
-		testProtocol = new TestProtocol<>(
-				null,
-				new ReceivePackFactory<Object>() {
-					@Override
-					public ReceivePack create(Object req, Repository db)
-							throws ServiceNotEnabledException,
-							ServiceNotAuthorizedException {
-						ReceivePack rp = new ReceivePack(db);
-						rp.setPreReceiveHook(
-								new PreReceiveHook() {
-									@Override
-									public void onPreReceive(ReceivePack receivePack,
-											Collection<ReceiveCommand> cmds) {
-										for (ReceiveCommand cmd : cmds) {
-											processedRefs.add(cmd.getRefName());
-										}
-									}
-								});
-						return rp;
-					}
-				});
+		testProtocol = new TestProtocol<>(null, (Object req, Repository db) -> {
+			ReceivePack rp = new ReceivePack(db);
+			rp.setPreReceiveHook((ReceivePack receivePack,
+					Collection<ReceiveCommand> cmds) -> {
+				for (ReceiveCommand cmd : cmds) {
+					processedRefs.add(cmd.getRefName());
+				}
+			});
+			return rp;
+		});
 		uri = testProtocol.register(ctx, server);
 
 		try (ObjectInserter ins = server.newObjectInserter()) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java
index f26201d..fd1c3bf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java
@@ -69,9 +69,6 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -95,16 +92,11 @@ public void setUp() throws Exception {
 		client = newRepo("client");
 
 		testProtocol = new TestProtocol<>(null,
-				new ReceivePackFactory<Object>() {
-					@Override
-					public ReceivePack create(Object req, Repository git)
-							throws ServiceNotEnabledException,
-							ServiceNotAuthorizedException {
-						receivePack = new ReceivePack(git);
-						receivePack.setAllowPushOptions(true);
-						receivePack.setAtomic(true);
-						return receivePack;
-					}
+				(Object req, Repository git) -> {
+					receivePack = new ReceivePack(git);
+					receivePack.setAllowPushOptions(true);
+					receivePack.setAtomic(true);
+					return receivePack;
 				});
 
 		uri = testProtocol.register(ctx, server);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
index 1c4d0cf..f4c55aa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
@@ -92,11 +92,8 @@ private static class DefaultUpload implements UploadPackFactory<User> {
 		@Override
 		public UploadPack create(User req, Repository db) {
 			UploadPack up = new UploadPack(db);
-			up.setPostUploadHook(new PostUploadHook() {
-				@Override
-				public void onPostUpload(PackStatistics stats) {
-					havesCount = stats.getHaves();
-				}
+			up.setPostUploadHook((PackStatistics stats) -> {
+				havesCount = stats.getHaves();
 			});
 			return up;
 		}
@@ -217,16 +214,12 @@ public void testUploadPackFactory() throws Exception {
 		ObjectId master = remote.branch("master").commit().create();
 
 		final AtomicInteger rejected = new AtomicInteger();
-		TestProtocol<User> proto = registerProto(new UploadPackFactory<User>() {
-			@Override
-			public UploadPack create(User req, Repository db)
-					throws ServiceNotAuthorizedException {
-				if (!"user2".equals(req.name)) {
-					rejected.incrementAndGet();
-					throw new ServiceNotAuthorizedException();
-				}
-				return new UploadPack(db);
+		TestProtocol<User> proto = registerProto((User req, Repository db) -> {
+			if (!"user2".equals(req.name)) {
+				rejected.incrementAndGet();
+				throw new ServiceNotAuthorizedException();
 			}
+			return new UploadPack(db);
 		}, new DefaultReceive());
 
 		// Same repository, different users.
@@ -262,16 +255,12 @@ public void testReceivePackFactory() throws Exception {
 
 		final AtomicInteger rejected = new AtomicInteger();
 		TestProtocol<User> proto = registerProto(new DefaultUpload(),
-				new ReceivePackFactory<User>() {
-					@Override
-					public ReceivePack create(User req, Repository db)
-							throws ServiceNotAuthorizedException {
-						if (!"user2".equals(req.name)) {
-							rejected.incrementAndGet();
-							throw new ServiceNotAuthorizedException();
-						}
-						return new ReceivePack(db);
+				(User req, Repository db) -> {
+					if (!"user2".equals(req.name)) {
+						rejected.incrementAndGet();
+						throw new ServiceNotAuthorizedException();
 					}
+					return new ReceivePack(db);
 				});
 
 		// Same repository, different users.
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index 1cbe8a4..9ffcbcc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -16,11 +16,16 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector;
@@ -28,6 +33,8 @@
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
@@ -38,10 +45,8 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.storage.pack.PackStatistics;
 import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
-import org.eclipse.jgit.transport.resolver.UploadPackFactory;
 import org.eclipse.jgit.util.io.NullOutputStream;
 import org.hamcrest.Matchers;
 import org.junit.After;
@@ -69,6 +74,8 @@ public class UploadPackTest {
 
 	private TestRepository<InMemoryRepository> remote;
 
+	private PackStatistics stats;
+
 	@Before
 	public void setUp() throws Exception {
 		server = newRepo("server");
@@ -92,17 +99,11 @@ private void generateBitmaps(InMemoryRepository repo) throws Exception {
 	}
 
 	private static TestProtocol<Object> generateReachableCommitUploadPackProtocol() {
-		return new TestProtocol<>(
-				new UploadPackFactory<Object>() {
-					@Override
-					public UploadPack create(Object req, Repository db)
-							throws ServiceNotEnabledException,
-							ServiceNotAuthorizedException {
-						UploadPack up = new UploadPack(db);
-						up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
-						return up;
-					}
-				}, null);
+		return new TestProtocol<>((Object req, Repository db) -> {
+			UploadPack up = new UploadPack(db);
+			up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
+			return up;
+		}, null);
 	}
 
 	@Test
@@ -112,20 +113,14 @@ public void testFetchParentOfShallowCommit() throws Exception {
 		RevCommit tip = remote.commit().message("2").parent(commit1).create();
 		remote.update("master", tip);
 
-		testProtocol = new TestProtocol<>(
-				new UploadPackFactory<Object>() {
-					@Override
-					public UploadPack create(Object req, Repository db)
-							throws ServiceNotEnabledException,
-							ServiceNotAuthorizedException {
-						UploadPack up = new UploadPack(db);
-						up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
-						// assume client has a shallow commit
-						up.getRevWalk().assumeShallow(
-								Collections.singleton(commit1.getId()));
-						return up;
-					}
-				}, null);
+		testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+			UploadPack up = new UploadPack(db);
+			up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
+			// assume client has a shallow commit
+			up.getRevWalk()
+					.assumeShallow(Collections.singleton(commit1.getId()));
+			return up;
+		}, null);
 		uri = testProtocol.register(ctx, server);
 
 		assertFalse(client.getObjectDatabase().has(commit0.toObjectId()));
@@ -212,19 +207,14 @@ public void testFetchWithBlobNoneFilter() throws Exception {
 			server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
 					true);
 
-			testProtocol = new TestProtocol<>(new UploadPackFactory<Object>() {
-				@Override
-				public UploadPack create(Object req, Repository db)
-						throws ServiceNotEnabledException,
-						ServiceNotAuthorizedException {
-					UploadPack up = new UploadPack(db);
-					return up;
-				}
+			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+				UploadPack up = new UploadPack(db);
+				return up;
 			}, null);
 			uri = testProtocol.register(ctx, server2);
 
 			try (Transport tn = testProtocol.open(uri, client, "server2")) {
-				tn.setFilterBlobLimit(0);
+				tn.setFilterSpec(FilterSpec.withBlobLimit(0));
 				tn.fetch(NullProgressMonitor.INSTANCE,
 						Collections.singletonList(new RefSpec(commit.name())));
 				assertTrue(client.getObjectDatabase().has(tree.toObjectId()));
@@ -250,19 +240,14 @@ public void testFetchExplicitBlobWithFilter() throws Exception {
 			server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
 					true);
 
-			testProtocol = new TestProtocol<>(new UploadPackFactory<Object>() {
-				@Override
-				public UploadPack create(Object req, Repository db)
-						throws ServiceNotEnabledException,
-						ServiceNotAuthorizedException {
-					UploadPack up = new UploadPack(db);
-					return up;
-				}
+			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+				UploadPack up = new UploadPack(db);
+				return up;
 			}, null);
 			uri = testProtocol.register(ctx, server2);
 
 			try (Transport tn = testProtocol.open(uri, client, "server2")) {
-				tn.setFilterBlobLimit(0);
+				tn.setFilterSpec(FilterSpec.withBlobLimit(0));
 				tn.fetch(NullProgressMonitor.INSTANCE, Arrays.asList(
 						new RefSpec(commit.name()), new RefSpec(blob1.name())));
 				assertTrue(client.getObjectDatabase().has(tree.toObjectId()));
@@ -287,19 +272,14 @@ public void testFetchWithBlobLimitFilter() throws Exception {
 			server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
 					true);
 
-			testProtocol = new TestProtocol<>(new UploadPackFactory<Object>() {
-				@Override
-				public UploadPack create(Object req, Repository db)
-						throws ServiceNotEnabledException,
-						ServiceNotAuthorizedException {
-					UploadPack up = new UploadPack(db);
-					return up;
-				}
+			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+				UploadPack up = new UploadPack(db);
+				return up;
 			}, null);
 			uri = testProtocol.register(ctx, server2);
 
 			try (Transport tn = testProtocol.open(uri, client, "server2")) {
-				tn.setFilterBlobLimit(5);
+				tn.setFilterSpec(FilterSpec.withBlobLimit(5));
 				tn.fetch(NullProgressMonitor.INSTANCE,
 						Collections.singletonList(new RefSpec(commit.name())));
 				assertFalse(
@@ -330,19 +310,14 @@ public void testFetchExplicitBlobWithFilterAndBitmaps() throws Exception {
 			new DfsGarbageCollector(server2).pack(null);
 			server2.scanForRepoChanges();
 
-			testProtocol = new TestProtocol<>(new UploadPackFactory<Object>() {
-				@Override
-				public UploadPack create(Object req, Repository db)
-						throws ServiceNotEnabledException,
-						ServiceNotAuthorizedException {
-					UploadPack up = new UploadPack(db);
-					return up;
-				}
+			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+				UploadPack up = new UploadPack(db);
+				return up;
 			}, null);
 			uri = testProtocol.register(ctx, server2);
 
 			try (Transport tn = testProtocol.open(uri, client, "server2")) {
-				tn.setFilterBlobLimit(0);
+				tn.setFilterSpec(FilterSpec.withBlobLimit(0));
 				tn.fetch(NullProgressMonitor.INSTANCE, Arrays.asList(
 						new RefSpec(commit.name()), new RefSpec(blob1.name())));
 				assertTrue(client.getObjectDatabase().has(blob1.toObjectId()));
@@ -370,19 +345,14 @@ public void testFetchWithBlobLimitFilterAndBitmaps() throws Exception {
 			new DfsGarbageCollector(server2).pack(null);
 			server2.scanForRepoChanges();
 
-			testProtocol = new TestProtocol<>(new UploadPackFactory<Object>() {
-				@Override
-				public UploadPack create(Object req, Repository db)
-						throws ServiceNotEnabledException,
-						ServiceNotAuthorizedException {
-					UploadPack up = new UploadPack(db);
-					return up;
-				}
+			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+				UploadPack up = new UploadPack(db);
+				return up;
 			}, null);
 			uri = testProtocol.register(ctx, server2);
 
 			try (Transport tn = testProtocol.open(uri, client, "server2")) {
-				tn.setFilterBlobLimit(5);
+				tn.setFilterSpec(FilterSpec.withBlobLimit(5));
 				tn.fetch(NullProgressMonitor.INSTANCE,
 						Collections.singletonList(new RefSpec(commit.name())));
 				assertFalse(
@@ -406,19 +376,14 @@ public void testFetchWithNonSupportingServer() throws Exception {
 			server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
 					false);
 
-			testProtocol = new TestProtocol<>(new UploadPackFactory<Object>() {
-				@Override
-				public UploadPack create(Object req, Repository db)
-						throws ServiceNotEnabledException,
-						ServiceNotAuthorizedException {
-					UploadPack up = new UploadPack(db);
-					return up;
-				}
+			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+				UploadPack up = new UploadPack(db);
+				return up;
 			}, null);
 			uri = testProtocol.register(ctx, server2);
 
 			try (Transport tn = testProtocol.open(uri, client, "server2")) {
-				tn.setFilterBlobLimit(0);
+				tn.setFilterSpec(FilterSpec.withBlobLimit(0));
 
 				thrown.expect(TransportException.class);
 				thrown.expectMessage(
@@ -453,6 +418,7 @@ private ByteArrayInputStream uploadPackV2Setup(RequestPolicy requestPolicy,
 
 		ByteArrayOutputStream recv = new ByteArrayOutputStream();
 		up.upload(send, recv, null);
+		stats = up.getStatistics();
 
 		return new ByteArrayInputStream(recv.toByteArray());
 	}
@@ -1551,6 +1517,324 @@ public void testV2FetchFilter() throws Exception {
 		assertTrue(client.getObjectDatabase().has(small.toObjectId()));
 	}
 
+	abstract class TreeBuilder {
+		abstract void addElements(DirCacheBuilder dcBuilder) throws Exception;
+
+		RevTree build() throws Exception {
+			DirCache dc = DirCache.newInCore();
+			DirCacheBuilder dcBuilder = dc.builder();
+			addElements(dcBuilder);
+			dcBuilder.finish();
+			ObjectId id;
+			try (ObjectInserter ins =
+					remote.getRepository().newObjectInserter()) {
+				id = dc.writeTree(ins);
+				ins.flush();
+			}
+			return remote.getRevWalk().parseTree(id);
+		}
+	}
+
+	class DeepTreePreparator {
+		RevBlob blobLowDepth = remote.blob("lo");
+		RevBlob blobHighDepth = remote.blob("hi");
+
+		RevTree subtree = remote.tree(remote.file("1", blobHighDepth));
+		RevTree rootTree = (new TreeBuilder() {
+				@Override
+				void addElements(DirCacheBuilder dcBuilder) throws Exception {
+					dcBuilder.add(remote.file("1", blobLowDepth));
+					dcBuilder.addTree(new byte[] {'2'}, DirCacheEntry.STAGE_0,
+							remote.getRevWalk().getObjectReader(), subtree);
+				}
+			}).build();
+		RevCommit commit = remote.commit(rootTree);
+
+		DeepTreePreparator() throws Exception {}
+	}
+
+	private void uploadV2WithTreeDepthFilter(
+			long depth, ObjectId... wants) throws Exception {
+		server.getConfig().setBoolean("uploadpack", null, "allowfilter", true);
+
+		List<String> input = new ArrayList();
+		input.add("command=fetch\n");
+		input.add(PacketLineIn.DELIM);
+		for (ObjectId want : wants) {
+			input.add("want " + want.getName() + "\n");
+		}
+		input.add("filter tree:" + depth + "\n");
+		input.add("done\n");
+		input.add(PacketLineIn.END);
+		ByteArrayInputStream recvStream =
+				uploadPackV2(RequestPolicy.ANY, /*refFilter=*/null,
+							 /*hook=*/null, input.toArray(new String[0]));
+		PacketLineIn pckIn = new PacketLineIn(recvStream);
+		assertThat(pckIn.readString(), is("packfile"));
+		parsePack(recvStream);
+	}
+
+	@Test
+	public void testV2FetchFilterTreeDepth0() throws Exception {
+		DeepTreePreparator preparator = new DeepTreePreparator();
+		remote.update("master", preparator.commit);
+
+		uploadV2WithTreeDepthFilter(0, preparator.commit.toObjectId());
+
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.rootTree.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.subtree.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.blobLowDepth.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.blobHighDepth.toObjectId()));
+		assertEquals(1, stats.getTreesTraversed());
+	}
+
+	@Test
+	public void testV2FetchFilterTreeDepth1_serverHasBitmap() throws Exception {
+		DeepTreePreparator preparator = new DeepTreePreparator();
+		remote.update("master", preparator.commit);
+
+		// The bitmap should be ignored since we need to track the depth while
+		// traversing the trees.
+		generateBitmaps(server);
+
+		uploadV2WithTreeDepthFilter(1, preparator.commit.toObjectId());
+
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.rootTree.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.subtree.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.blobLowDepth.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.blobHighDepth.toObjectId()));
+		assertEquals(1, stats.getTreesTraversed());
+	}
+
+	@Test
+	public void testV2FetchFilterTreeDepth2() throws Exception {
+		DeepTreePreparator preparator = new DeepTreePreparator();
+		remote.update("master", preparator.commit);
+
+		uploadV2WithTreeDepthFilter(2, preparator.commit.toObjectId());
+
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.rootTree.toObjectId()));
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.subtree.toObjectId()));
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.blobLowDepth.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.blobHighDepth.toObjectId()));
+		assertEquals(2, stats.getTreesTraversed());
+	}
+
+	/**
+	 * Creates a commit with the following files:
+	 * <pre>
+	 * a/x/b/foo
+	 * x/b/foo
+	 * </pre>
+	 * which has an identical tree in two locations: once at / and once at /a
+	 */
+	class RepeatedSubtreePreparator {
+		RevBlob foo = remote.blob("foo");
+		RevTree subtree3 = remote.tree(remote.file("foo", foo));
+		RevTree subtree2 = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree3);
+			}
+		}).build();
+		RevTree subtree1 = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree2);
+			}
+		}).build();
+		RevTree rootTree = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree1);
+				dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree2);
+			}
+		}).build();
+		RevCommit commit = remote.commit(rootTree);
+
+		RepeatedSubtreePreparator() throws Exception {}
+	}
+
+	@Test
+	public void testV2FetchFilterTreeDepth_iterateOverTreeAtTwoLevels()
+			throws Exception {
+		// Test tree:<depth> where a tree is iterated to twice - once where a
+		// subentry is too deep to be included, and again where the blob inside
+		// it is shallow enough to be included.
+		RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
+		remote.update("master", preparator.commit);
+
+		uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
+
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.foo.toObjectId()));
+	}
+
+	/**
+	 * Creates a commit with the following files:
+	 * <pre>
+	 * a/x/b/foo
+	 * b/u/c/baz
+	 * y/x/b/foo
+	 * z/v/c/baz
+	 * </pre>
+	 * which has two pairs of identical trees:
+	 * <ul>
+	 * <li>one at /a and /y
+	 * <li>one at /b/u and /z/v
+	 * </ul>
+	 * Note that this class defines unique 8 trees (rootTree and subtree1-7)
+	 * which means PackStatistics should report having visited 8 trees.
+	 */
+	class RepeatedSubtreeAtSameLevelPreparator {
+		RevBlob foo = remote.blob("foo");
+
+		/** foo */
+		RevTree subtree1 = remote.tree(remote.file("foo", foo));
+
+		/** b/foo */
+		RevTree subtree2 = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree1);
+			}
+		}).build();
+
+		/** x/b/foo */
+		RevTree subtree3 = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree2);
+			}
+		}).build();
+
+		RevBlob baz = remote.blob("baz");
+
+		/** baz */
+		RevTree subtree4 = remote.tree(remote.file("baz", baz));
+
+		/** c/baz */
+		RevTree subtree5 = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'c'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree4);
+			}
+		}).build();
+
+		/** u/c/baz */
+		RevTree subtree6 = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'u'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree5);
+			}
+		}).build();
+
+		/** v/c/baz */
+		RevTree subtree7 = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'v'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree5);
+			}
+		}).build();
+
+		RevTree rootTree = (new TreeBuilder() {
+			@Override
+			void addElements(DirCacheBuilder dcBuilder) throws Exception {
+				dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree3);
+				dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree6);
+				dcBuilder.addTree(new byte[] {'y'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree3);
+				dcBuilder.addTree(new byte[] {'z'}, DirCacheEntry.STAGE_0,
+						remote.getRevWalk().getObjectReader(), subtree7);
+			}
+		}).build();
+		RevCommit commit = remote.commit(rootTree);
+
+		RepeatedSubtreeAtSameLevelPreparator() throws Exception {}
+	}
+
+	@Test
+	public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelIncludeFile()
+			throws Exception {
+		RepeatedSubtreeAtSameLevelPreparator preparator =
+				new RepeatedSubtreeAtSameLevelPreparator();
+		remote.update("master", preparator.commit);
+
+		uploadV2WithTreeDepthFilter(5, preparator.commit.toObjectId());
+
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.foo.toObjectId()));
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.baz.toObjectId()));
+		assertEquals(8, stats.getTreesTraversed());
+	}
+
+	@Test
+	public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelExcludeFile()
+			throws Exception {
+		RepeatedSubtreeAtSameLevelPreparator preparator =
+				new RepeatedSubtreeAtSameLevelPreparator();
+		remote.update("master", preparator.commit);
+
+		uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
+
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.foo.toObjectId()));
+		assertFalse(client.getObjectDatabase()
+				.has(preparator.baz.toObjectId()));
+		assertEquals(8, stats.getTreesTraversed());
+	}
+
+	@Test
+	public void testWantFilteredObject() throws Exception {
+		RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
+		remote.update("master", preparator.commit);
+
+		// Specify wanted blob objects that are deep enough to be filtered. We
+		// should still upload them.
+		uploadV2WithTreeDepthFilter(
+				3,
+				preparator.commit.toObjectId(),
+				preparator.foo.toObjectId());
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.foo.toObjectId()));
+
+		client = newRepo("client");
+		// Specify a wanted tree object that is deep enough to be filtered. We
+		// should still upload it.
+		uploadV2WithTreeDepthFilter(
+				2,
+				preparator.commit.toObjectId(),
+				preparator.subtree3.toObjectId());
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.foo.toObjectId()));
+		assertTrue(client.getObjectDatabase()
+				.has(preparator.subtree3.toObjectId()));
+	}
+
 	@Test
 	public void testV2FetchFilterWhenNotAllowed() throws Exception {
 		RevCommit commit = remote.commit().message("0").create();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index 6f61912..0303ea2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -582,26 +582,23 @@ public void idOffset() throws Exception {
 		}
 	}
 
-	private final FileTreeIterator.FileModeStrategy NO_GITLINKS_STRATEGY =
-			new FileTreeIterator.FileModeStrategy() {
-				@Override
-				public FileMode getMode(File f, FS.Attributes attributes) {
-					if (attributes.isSymbolicLink()) {
-						return FileMode.SYMLINK;
-					} else if (attributes.isDirectory()) {
-						// NOTE: in the production DefaultFileModeStrategy, there is
-						// a check here for a subdirectory called '.git', and if it
-						// exists, we create a GITLINK instead of recursing into the
-						// tree.  In this custom strategy, we ignore nested git dirs
-						// and treat all directories the same.
-						return FileMode.TREE;
-					} else if (attributes.isExecutable()) {
-						return FileMode.EXECUTABLE_FILE;
-					} else {
-						return FileMode.REGULAR_FILE;
-					}
-				}
-			};
+	private final FileTreeIterator.FileModeStrategy NO_GITLINKS_STRATEGY = (
+			File f, FS.Attributes attributes) -> {
+		if (attributes.isSymbolicLink()) {
+			return FileMode.SYMLINK;
+		} else if (attributes.isDirectory()) {
+			// NOTE: in the production DefaultFileModeStrategy, there is
+			// a check here for a subdirectory called '.git', and if it
+			// exists, we create a GITLINK instead of recursing into the
+			// tree. In this custom strategy, we ignore nested git dirs
+			// and treat all directories the same.
+			return FileMode.TREE;
+		} else if (attributes.isExecutable()) {
+			return FileMode.EXECUTABLE_FILE;
+		} else {
+			return FileMode.REGULAR_FILE;
+		}
+	};
 
 	private Repository createNestedRepo() throws IOException {
 		File gitdir = createUniqueTestGitDir(false);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
index d124d73..9981bd6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
@@ -128,6 +128,41 @@ public void testBuilder_AddThenSort() {
 	}
 
 	@Test
+	public void testBuilder_AddThenDedupe() {
+		RefList.Builder<Ref> builder = new RefList.Builder<>(1);
+		builder.add(REF_B);
+		builder.add(REF_A);
+		builder.add(REF_A);
+		builder.add(REF_B);
+		builder.add(REF_c);
+
+		builder.sort();
+		builder.dedupe((a, b) -> b);
+		RefList<Ref> list = builder.toRefList();
+
+		assertEquals(3, list.size());
+		assertSame(REF_A, list.get(0));
+		assertSame(REF_B, list.get(1));
+		assertSame(REF_c, list.get(2));
+	}
+
+	@Test
+	public void testBuilder_AddThenDedupe_Border() {
+		RefList.Builder<Ref> builder = new RefList.Builder<>(1);
+		builder.sort();
+		builder.dedupe((a, b) -> b);
+		RefList<Ref> list = builder.toRefList();
+		assertTrue(list.isEmpty());
+
+		builder = new RefList.Builder<>(1);
+		builder.add(REF_A);
+		builder.sort();
+		builder.dedupe((a, b) -> b);
+		list = builder.toRefList();
+		assertEquals(1, list.size());
+	}
+
+	@Test
 	public void testBuilder_AddAll() {
 		RefList.Builder<Ref> builder = new RefList.Builder<>(1);
 		Ref[] src = { REF_A, REF_B, REF_c, REF_A };
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index d5e51b6..7027838 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -4,14 +4,14 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.ui
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="5.3.3"
-Import-Package: org.eclipse.jgit.errors;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.lib;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.nls;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revplot;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.revwalk;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.transport;version="[5.3.3,5.4.0)",
- org.eclipse.jgit.util;version="[5.3.3,5.4.0)"
+Export-Package: org.eclipse.jgit.awtui;version="5.4.0"
+Import-Package: org.eclipse.jgit.errors;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.lib;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.nls;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revplot;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.revwalk;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.transport;version="[5.4.0,5.5.0)",
+ org.eclipse.jgit.util;version="[5.4.0,5.5.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..82ded94
--- /dev/null
+++ b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.ui - Sources
+Bundle-SymbolicName: org.eclipse.jgit.ui.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 792ba43..0177f25 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
@@ -64,6 +64,7 @@
 
   <properties>
     <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
   </properties>
 
   <dependencies>
@@ -92,6 +93,48 @@
 
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>translate-source-qualifier</id>
+            <phase>generate-resources</phase>
+            <configuration>
+              <target>
+                <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
+                <replace file="${source-bundle-manifest}">
+                  <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
+                </replace>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <configuration>
           <archive>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index eb93d3e..6719570 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,13 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <component id="org.eclipse.jgit" version="2">
-    <resource path="META-INF/MANIFEST.MF">
-        <filter id="924844039">
-            <message_arguments>
-                <message_argument value="5.3.2"/>
-                <message_argument value="5.3.0"/>
-            </message_arguments>
-        </filter>
-    </resource>
     <resource path="src/org/eclipse/jgit/errors/PackInvalidException.java" type="org.eclipse.jgit.errors.PackInvalidException">
         <filter id="1142947843">
             <message_arguments>
@@ -22,6 +14,22 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/lib/Ref.java" type="org.eclipse.jgit.lib.Ref">
+        <filter id="403767336">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.lib.Ref"/>
+                <message_argument value="UNDEFINED_UPDATE_INDEX"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/revwalk/ObjectWalk.java" type="org.eclipse.jgit.revwalk.ObjectWalk">
+        <filter comment="ignore the risk subclasses could define the same field and cause a name clash" id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.revwalk.ObjectWalk"/>
+                <message_argument value="SIMPLE_VISITATION_POLICY"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig">
         <filter id="336658481">
             <message_arguments>
@@ -78,6 +86,36 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/storage/pack/PackStatistics.java" type="org.eclipse.jgit.storage.pack.PackStatistics$Accumulator">
+        <filter comment="ignore the risk subclasses could define the same field and cause a name clash" id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/>
+                <message_argument value="treesTraversed"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/transport/Transport.java" type="org.eclipse.jgit.transport.Transport">
+        <filter comment="Marked as final since overriding a deprecated stub is likely a mistake" id="421654647">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.Transport"/>
+                <message_argument value="getFilterBlobLimit()"/>
+            </message_arguments>
+        </filter>
+        <filter comment="Marked as final since overriding a deprecated stub is likely a mistake" id="421654647">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.Transport"/>
+                <message_argument value="setFilterBlobLimit(long)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/transport/UploadPack.java" type="org.eclipse.jgit.transport.UploadPack">
+        <filter id="421654647">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.UploadPack"/>
+                <message_argument value="getFilterBlobLimit()"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS">
         <filter id="1142947843">
             <message_arguments>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index d422692..95594f2 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,12 +3,12 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 5.3.3.qualifier
+Bundle-Version: 5.4.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="5.3.3",
- org.eclipse.jgit.api;version="5.3.3";
+Export-Package: org.eclipse.jgit.annotations;version="5.4.0",
+ org.eclipse.jgit.api;version="5.4.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
@@ -22,53 +22,53 @@
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="5.3.3",
- org.eclipse.jgit.blame;version="5.3.3";
+ org.eclipse.jgit.api.errors;version="5.4.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="5.4.0",
+ org.eclipse.jgit.blame;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="5.3.3";
+ org.eclipse.jgit.diff;version="5.4.0";
   uses:="org.eclipse.jgit.patch,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="5.3.3";
+ org.eclipse.jgit.dircache;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util,
    org.eclipse.jgit.events,
    org.eclipse.jgit.attributes",
- org.eclipse.jgit.errors;version="5.3.3";
+ org.eclipse.jgit.errors;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.internal.storage.pack,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="5.3.3";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="5.3.3",
- org.eclipse.jgit.gitrepo;version="5.3.3";
+ org.eclipse.jgit.events;version="5.4.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="5.4.0",
+ org.eclipse.jgit.gitrepo;version="5.4.0";
   uses:="org.eclipse.jgit.api,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.xml.sax.helpers,
    org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="5.3.3";x-internal:=true,
- org.eclipse.jgit.hooks;version="5.3.3";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="5.3.3",
- org.eclipse.jgit.ignore.internal;version="5.3.3";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="5.3.3";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="5.3.3";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.revwalk;version="5.3.3";x-internal:=true,
- org.eclipse.jgit.internal.storage.dfs;version="5.3.3";
+ org.eclipse.jgit.gitrepo.internal;version="5.4.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="5.4.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="5.4.0",
+ org.eclipse.jgit.ignore.internal;version="5.4.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="5.4.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.fsck;version="5.4.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.ketch;version="5.4.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.revwalk;version="5.4.0";x-internal:=true,
+ org.eclipse.jgit.internal.storage.dfs;version="5.4.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.server,
    org.eclipse.jgit.http.test,
    org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="5.3.3";
+ org.eclipse.jgit.internal.storage.file;version="5.4.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -77,18 +77,18 @@
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="5.3.3";
+ org.eclipse.jgit.internal.storage.io;version="5.4.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.pack;version="5.4.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftable;version="5.4.0";
   x-friends:="org.eclipse.jgit.http.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="5.3.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="5.3.3";x-internal:=true,
- org.eclipse.jgit.internal.transport.parser;version="5.3.3";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="5.3.3";x-friends:="org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.lib;version="5.3.3";
+ org.eclipse.jgit.internal.storage.reftree;version="5.4.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.submodule;version="5.4.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.parser;version="5.4.0";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.ssh;version="5.4.0";x-friends:="org.eclipse.jgit.ssh.apache",
+ org.eclipse.jgit.lib;version="5.4.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
@@ -98,33 +98,33 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.submodule",
- org.eclipse.jgit.lib.internal;version="5.3.3";x-internal:=true,
- org.eclipse.jgit.merge;version="5.3.3";
+ org.eclipse.jgit.lib.internal;version="5.4.0";x-internal:=true,
+ org.eclipse.jgit.merge;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.dircache,
    org.eclipse.jgit.api",
- org.eclipse.jgit.nls;version="5.3.3",
- org.eclipse.jgit.notes;version="5.3.3";
+ org.eclipse.jgit.nls;version="5.4.0",
+ org.eclipse.jgit.notes;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="5.3.3";
+ org.eclipse.jgit.patch;version="5.4.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="5.4.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="5.3.3";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="5.3.3";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="5.3.3";
+ org.eclipse.jgit.revwalk.filter;version="5.4.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="5.4.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="5.4.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="5.4.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="5.4.0";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.internal.storage.pack,
@@ -137,24 +137,24 @@
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.errors,
    org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="5.3.3";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="5.3.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="5.3.3";
+ org.eclipse.jgit.transport.http;version="5.4.0";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="5.4.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
    org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="5.3.3";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="5.3.3";
+ org.eclipse.jgit.treewalk.filter;version="5.4.0";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="5.4.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.storage.file,
    org.ietf.jgss",
- org.eclipse.jgit.util.io;version="5.3.3",
- org.eclipse.jgit.util.sha1;version="5.3.3",
- org.eclipse.jgit.util.time;version="5.3.3"
+ org.eclipse.jgit.util.io;version="5.4.0",
+ org.eclipse.jgit.util.sha1;version="5.4.0",
+ org.eclipse.jgit.util.time;version="5.4.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
  com.jcraft.jsch;version="[0.1.37,0.2.0)",
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index e3fd47d..ad348ee 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit - Sources
 Bundle-SymbolicName: org.eclipse.jgit.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.3.3.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="5.3.3.qualifier";roots="."
+Bundle-Version: 5.4.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="5.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index d22ff1f..3abdae8 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -53,7 +53,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.3.3-SNAPSHOT</version>
+    <version>5.4.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
@@ -115,7 +115,7 @@
         <includes>
           <include>plugin.properties</include>
           <include>about.html</include>
-		  <include>META-INF/eclipse.inf</include>
+          <include>META-INF/eclipse.inf</include>
         </includes>
       </resource>
       <resource>
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 51910f8..fc2a26f 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -54,7 +54,7 @@
 cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included.
 cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}.
 cannotChangeToComment=Cannot change a non-comment line to a comment line.
-cannotCheckoutFromUnbornBranch=Cannot checkout from unborn branch
+cannotCheckoutFromUnbornBranch=Cannot check out from unborn branch
 cannotCheckoutOursSwitchBranch=Checking out ours/theirs is only possible when checking out index, not when switching branches.
 cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff.
 cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}.
@@ -175,8 +175,8 @@
 corruptObjectInvalidType2=invalid type {0}
 corruptObjectMalformedHeader=malformed header: {0}
 corruptObjectMissingEmail=missing email
-corruptObjectNameContainsByte=name contains byte 0x%x
-corruptObjectNameContainsChar=name contains '%c'
+corruptObjectNameContainsByte=byte 0x%x not allowed in Windows filename
+corruptObjectNameContainsChar=char '%c' not allowed in Windows filename
 corruptObjectNameContainsNullByte=name contains byte 0x00
 corruptObjectNameContainsSlash=name contains '/'
 corruptObjectNameDot=invalid name '.'
@@ -330,6 +330,7 @@
 gpgNoKeyInLegacySecring=no matching secret key found in legacy secring.gpg for key or user id: {0}
 gpgNoPublicKeyFound=Unable to find a public-key with key or user id: {0}
 gpgNoSecretKeyForPublicKey=unable to find associated secret key for public key: {0}
+gpgNotASigningKey=Secret key ({0}) is not suitable for signing
 gpgKeyInfo=GPG Key (fingerprint {0})
 gpgSigningCancelled=Signing was cancelled
 headRequiredToStash=HEAD required to stash local changes
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 9f071c4..8671800 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -282,7 +282,7 @@ public RevCommit call() throws GitAPIException, NoHeadException,
 					ru.setRefLogMessage(reflogComment, false);
 				} else {
 					String prefix = amend ? "commit (amend): " //$NON-NLS-1$
-							: parents.size() == 0 ? "commit (initial): " //$NON-NLS-1$
+							: parents.isEmpty() ? "commit (initial): " //$NON-NLS-1$
 									: "commit: "; //$NON-NLS-1$
 					ru.setRefLogMessage(prefix + revCommit.getShortMessage(),
 							false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
index 31e7281..c3feaef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
@@ -46,6 +46,7 @@
 import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -188,8 +189,7 @@ public List<String> call() throws GitAPIException,
 	public DeleteBranchCommand setBranchNames(String... branchnames) {
 		checkCallable();
 		this.branchNames.clear();
-		for (String branch : branchnames)
-			this.branchNames.add(branch);
+		this.branchNames.addAll(Arrays.asList(branchnames));
 		return this;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java
index 63a090c..1970240 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java
@@ -45,6 +45,7 @@
 import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -135,8 +136,7 @@ public List<String> call() throws GitAPIException {
 	public DeleteTagCommand setTags(String... tags) {
 		checkCallable();
 		this.tags.clear();
-		for (String tagName : tags)
-			this.tags.add(tagName);
+		this.tags.addAll(Arrays.asList(tags));
 		return this;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index a484742..9ebcf9f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -102,12 +102,12 @@ public class DescribeCommand extends GitCommand<String> {
 	private boolean longDesc;
 
 	/**
-	 * Pattern matchers to be applied to tags under consideration
+	 * Pattern matchers to be applied to tags under consideration.
 	 */
 	private List<IMatcher> matchers = new ArrayList<>();
 
 	/**
-	 * Whether to use all tags (incl. lightweight) or not
+	 * Whether to use all tags (incl. lightweight) or not.
 	 */
 	private boolean useTags = false;
 
@@ -246,9 +246,9 @@ private Date tagDate(Ref tag) throws IOException {
 	};
 
 	private Optional<Ref> getBestMatch(List<Ref> tags) {
-		if (tags == null || tags.size() == 0) {
+		if (tags == null || tags.isEmpty()) {
 			return Optional.empty();
-		} else if (matchers.size() == 0) {
+		} else if (matchers.isEmpty()) {
 			Collections.sort(tags, TAG_TIE_BREAKER);
 			return Optional.of(tags.get(0));
 		} else {
@@ -402,12 +402,8 @@ String describe(ObjectId tip) throws IOException {
 			if (candidates.isEmpty())
 				return null;
 
-			Candidate best = Collections.min(candidates, new Comparator<Candidate>() {
-				@Override
-				public int compare(Candidate o1, Candidate o2) {
-					return o1.depth - o2.depth;
-				}
-			});
+			Candidate best = Collections.min(candidates,
+					(Candidate o1, Candidate o2) -> o1.depth - o2.depth);
 
 			return best.describe(target);
 		} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 400a7df..835e7b5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -43,6 +43,8 @@
  */
 package org.eclipse.jgit.api;
 
+import static java.util.Objects.requireNonNull;
+
 import java.io.File;
 import java.io.IOException;
 
@@ -220,9 +222,7 @@ public Git(Repository repo) {
 	}
 
 	Git(Repository repo, boolean closeRepo) {
-		if (repo == null)
-			throw new NullPointerException();
-		this.repo = repo;
+		this.repo = requireNonNull(repo);
 		this.closeRepo = closeRepo;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
index 29a51a0..e0eafc7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
@@ -53,7 +53,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
@@ -134,12 +133,8 @@ public List<Ref> call() throws GitAPIException {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 
-		Collections.sort(resultRefs, new Comparator<Ref>() {
-			@Override
-			public int compare(Ref o1, Ref o2) {
-				return o1.getName().compareTo(o2.getName());
-			}
-		});
+		Collections.sort(resultRefs,
+				(Ref o1, Ref o2) -> o1.getName().compareTo(o2.getName()));
 		setCallable(false);
 		return resultRefs;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
index 01c1991..c894d05 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
@@ -45,7 +45,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
@@ -87,12 +86,8 @@ public List<Ref> call() throws GitAPIException {
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
-		Collections.sort(tags, new Comparator<Ref>() {
-			@Override
-			public int compare(Ref o1, Ref o2) {
-				return o1.getName().compareTo(o2.getName());
-			}
-		});
+		Collections.sort(tags,
+				(Ref o1, Ref o2) -> o1.getName().compareTo(o2.getName()));
 		setCallable(false);
 		return tags;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
index cf3d35f..9b8016c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -282,13 +282,11 @@ public LogCommand all() throws IOException {
 			RevCommit commit = null;
 			try {
 				commit = walk.parseCommit(objectId);
-			} catch (MissingObjectException e) {
-				// ignore: the ref points to an object that does not exist;
-				// it should be ignored as traversal starting point.
-			} catch (IncorrectObjectTypeException e) {
-				// ignore: the ref points to an object that is not a commit
-				// (e.g. a tree or a blob);
-				// it should be ignored as traversal starting point.
+			} catch (MissingObjectException | IncorrectObjectTypeException e) {
+				// ignore as traversal starting point:
+				// - the ref points to an object that does not exist
+				// - the ref points to an object that is not a commit (e.g. a
+				// tree or a blob)
 			}
 			if (commit != null)
 				add(commit);
@@ -348,9 +346,7 @@ private LogCommand add(boolean include, AnyObjectId start)
 			} else
 				walk.markUninteresting(walk.lookupCommit(start));
 			return this;
-		} catch (MissingObjectException e) {
-			throw e;
-		} catch (IncorrectObjectTypeException e) {
+		} catch (MissingObjectException | IncorrectObjectTypeException e) {
 			throw e;
 		} catch (IOException e) {
 			throw new JGitInternalException(MessageFormat.format(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 0e3d000..cdd1b80 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -362,7 +362,7 @@ public RebaseResult call() throws GitAPIException, NoHeadException,
 
 			List<RebaseTodoLine> steps = repo.readRebaseTodo(
 					rebaseState.getPath(GIT_REBASE_TODO), false);
-			if (steps.size() == 0) {
+			if (steps.isEmpty()) {
 				return finishRebase(walk.parseCommit(repo.resolve(Constants.HEAD)), false);
 			}
 			if (isInteractive()) {
@@ -1295,13 +1295,8 @@ private RevCommit tryFastForward(String headName, RevCommit oldCommit,
 				}
 			}
 			return newCommit;
-		} catch (RefAlreadyExistsException e) {
-			throw new JGitInternalException(e.getMessage(), e);
-		} catch (RefNotFoundException e) {
-			throw new JGitInternalException(e.getMessage(), e);
-		} catch (InvalidRefNameException e) {
-			throw new JGitInternalException(e.getMessage(), e);
-		} catch (CheckoutConflictException e) {
+		} catch (RefAlreadyExistsException | RefNotFoundException
+				| InvalidRefNameException | CheckoutConflictException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
index f92455a..8908277 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -238,9 +238,7 @@ public Repository call() throws GitAPIException {
 			modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
 					name, ConfigConstants.CONFIG_KEY_URL, uri);
 			modulesConfig.save();
-		} catch (IOException e) {
-			throw new JGitInternalException(e.getMessage(), e);
-		} catch (ConfigInvalidException e) {
+		} catch (IOException | ConfigInvalidException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java
index 5a0528b..ebcea4b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java
@@ -173,8 +173,8 @@ private void deinit(String path) throws IOException {
 		}
 		final File[] ls = dir.listFiles();
 		if (ls != null) {
-			for (int i = 0; i < ls.length; i++) {
-				FileUtils.delete(ls[i], RECURSIVE);
+			for (File f : ls) {
+				FileUtils.delete(f, RECURSIVE);
 			}
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
index 2db12b8..6fd9405 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
@@ -128,9 +128,7 @@ public Collection<String> call() throws GitAPIException {
 			if (!initialized.isEmpty())
 				config.save();
 			return initialized;
-		} catch (IOException e) {
-			throw new JGitInternalException(e.getMessage(), e);
-		} catch (ConfigInvalidException e) {
+		} catch (IOException | ConfigInvalidException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
index 0606c5b..58e5959 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
@@ -108,9 +108,7 @@ public Map<String, SubmoduleStatus> call() throws GitAPIException {
 				statuses.put(status.getPath(), status);
 			}
 			return statuses;
-		} catch (IOException e) {
-			throw new JGitInternalException(e.getMessage(), e);
-		} catch (ConfigInvalidException e) {
+		} catch (IOException | ConfigInvalidException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
index 7cf4b73..5239369 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
@@ -162,9 +162,7 @@ public Map<String, String> call() throws GitAPIException {
 			if (!synced.isEmpty())
 				config.save();
 			return synced;
-		} catch (IOException e) {
-			throw new JGitInternalException(e.getMessage(), e);
-		} catch (ConfigInvalidException e) {
+		} catch (IOException | ConfigInvalidException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index e7ad0bc..1cecff6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -1063,7 +1063,7 @@ private RawText open(DiffEntry.Side side, DiffEntry entry)
 					entry.newId = id;
 					break;
 				}
-			} else if (ids.size() == 0)
+			} else if (ids.isEmpty())
 				throw new MissingObjectException(id, Constants.OBJ_BLOB);
 			else
 				throw new AmbiguousObjectException(id, ids);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 14653fe..eeab03d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -111,14 +111,12 @@ public class DirCache {
 
 	private static final byte[] NO_CHECKSUM = {};
 
-	static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
-		@Override
-		public int compare(DirCacheEntry o1, DirCacheEntry o2) {
-			final int cr = cmp(o1, o2);
-			if (cr != 0)
-				return cr;
-			return o1.getStage() - o2.getStage();
-		}
+	static final Comparator<DirCacheEntry> ENT_CMP = (DirCacheEntry o1,
+			DirCacheEntry o2) -> {
+		final int cr = cmp(o1, o2);
+		if (cr != 0)
+			return cr;
+		return o1.getStage() - o2.getStage();
 	};
 
 	static int cmp(DirCacheEntry a, DirCacheEntry b) {
@@ -253,13 +251,7 @@ public static DirCache lock(File indexLocation, FS fs)
 
 		try {
 			c.read();
-		} catch (IOException e) {
-			c.unlock();
-			throw e;
-		} catch (RuntimeException e) {
-			c.unlock();
-			throw e;
-		} catch (Error e) {
+		} catch (IOException | RuntimeException | Error e) {
 			c.unlock();
 			throw e;
 		}
@@ -636,13 +628,7 @@ public void write() throws IOException {
 		try (OutputStream o = tmp.getOutputStream();
 				OutputStream bo = new BufferedOutputStream(o)) {
 			writeTo(liveFile.getParentFile(), bo);
-		} catch (IOException err) {
-			tmp.unlock();
-			throw err;
-		} catch (RuntimeException err) {
-			tmp.unlock();
-			throw err;
-		} catch (Error err) {
+		} catch (IOException | RuntimeException | Error err) {
 			tmp.unlock();
 			throw err;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 307fd3f..a3dbc50 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -632,7 +632,7 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
 			if (!builder.commit())
 				throw new IndexWriteException();
 		}
-		return toBeDeleted.size() == 0;
+		return toBeDeleted.isEmpty();
 	}
 
 	private void checkoutGitlink(String path, DirCacheEntry entry)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
index 74ba97f..5b8e11f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
@@ -75,13 +75,11 @@
  * @see DirCacheBuilder
  */
 public class DirCacheEditor extends BaseDirCacheEditor {
-	private static final Comparator<PathEdit> EDIT_CMP = new Comparator<PathEdit>() {
-		@Override
-		public int compare(PathEdit o1, PathEdit o2) {
-			final byte[] a = o1.path;
-			final byte[] b = o2.path;
-			return cmp(a, a.length, b, b.length);
-		}
+	private static final Comparator<PathEdit> EDIT_CMP = (PathEdit o1,
+			PathEdit o2) -> {
+		final byte[] a = o1.path;
+		final byte[] b = o2.path;
+		return cmp(a, a.length, b, b.length);
 	};
 
 	private final List<PathEdit> edits;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
index 11a3474..80e1084 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
@@ -81,25 +81,26 @@ public class DirCacheTree {
 
 	private static final DirCacheTree[] NO_CHILDREN = {};
 
-	private static final Comparator<DirCacheTree> TREE_CMP = new Comparator<DirCacheTree>() {
-		@Override
-		public int compare(DirCacheTree o1, DirCacheTree o2) {
-			final byte[] a = o1.encodedName;
-			final byte[] b = o2.encodedName;
-			final int aLen = a.length;
-			final int bLen = b.length;
-			int cPos;
-			for (cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
-				final int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff);
-				if (cmp != 0)
-					return cmp;
+	private static final Comparator<DirCacheTree> TREE_CMP = (DirCacheTree o1,
+			DirCacheTree o2) -> {
+		final byte[] a = o1.encodedName;
+		final byte[] b = o2.encodedName;
+		final int aLen = a.length;
+		final int bLen = b.length;
+		int cPos;
+		for (cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
+			final int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff);
+			if (cmp != 0) {
+				return cmp;
 			}
-			if (aLen == bLen)
-				return 0;
-			if (aLen < bLen)
-				return '/' - (b[cPos] & 0xff);
-			return (a[cPos] & 0xff) - '/';
 		}
+		if (aLen == bLen) {
+			return 0;
+		}
+		if (aLen < bLen) {
+			return '/' - (b[cPos] & 0xff);
+		}
+		return (a[cPos] & 0xff) - '/';
 	};
 
 	/** Tree this tree resides in; null if we are the root. */
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 018b643..ca0024d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -391,6 +391,7 @@ public static JGitText get() {
 	/***/ public String gpgNoKeyInLegacySecring;
 	/***/ public String gpgNoPublicKeyFound;
 	/***/ public String gpgNoSecretKeyForPublicKey;
+	/***/ public String gpgNotASigningKey;
 	/***/ public String gpgKeyInfo;
 	/***/ public String gpgSigningCancelled;
 	/***/ public String headRequiredToStash;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java
index fa32b26..c0364ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java
@@ -385,12 +385,7 @@ private void initialize() throws IOException {
 
 	private void scheduleLeader() {
 		idle = false;
-		system.getExecutor().execute(new Runnable() {
-			@Override
-			public void run() {
-				runLeader();
-			}
-		});
+		system.getExecutor().execute(this::runLeader);
 	}
 
 	private void runLeader() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java
index 6f1f5c5..cd0ded5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java
@@ -123,21 +123,18 @@ void initialize(Repository repo) throws IOException {
 	/** {@inheritDoc} */
 	@Override
 	protected void startPush(ReplicaPushRequest req) {
-		getSystem().getExecutor().execute(new Runnable() {
-			@Override
-			public void run() {
-				MonotonicClock clk = getSystem().getClock();
-				try (Repository git = getLeader().openRepository();
-						ProposedTimestamp ts = clk.propose()) {
-					try {
-						update(git, req, ts);
-						req.done(git);
-					} catch (Throwable err) {
-						req.setException(git, err);
-					}
-				} catch (IOException err) {
-					req.setException(null, err);
+		getSystem().getExecutor().execute(() -> {
+			MonotonicClock clk = getSystem().getClock();
+			try (Repository git = getLeader().openRepository();
+					ProposedTimestamp ts = clk.propose()) {
+				try {
+					update(git, req, ts);
+					req.done(git);
+				} catch (Throwable err) {
+					req.setException(git, err);
 				}
+			} catch (IOException err) {
+				req.setException(null, err);
 			}
 		});
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
index b61274e..3c9b187 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
@@ -139,19 +139,16 @@ protected String describeForLog() {
 	/** {@inheritDoc} */
 	@Override
 	protected void startPush(ReplicaPushRequest req) {
-		getSystem().getExecutor().execute(new Runnable() {
-			@Override
-			public void run() {
-				try (Repository git = getLeader().openRepository()) {
-					try {
-						push(git, req);
-						req.done(git);
-					} catch (Throwable err) {
-						req.setException(git, err);
-					}
-				} catch (IOException err) {
-					req.setException(null, err);
+		getSystem().getExecutor().execute(() -> {
+			try (Repository git = getLeader().openRepository()) {
+				try {
+					push(git, req);
+					req.done(git);
+				} catch (Throwable err) {
+					req.setException(git, err);
 				}
+			} catch (IOException err) {
+				req.setException(null, err);
 			}
 		});
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
index bd4b4d2..3b92dee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
@@ -180,8 +180,8 @@ int getMemoryUsedByLruChainForTest() {
 
 	int getMemoryUsedByTableForTest() {
 		int r = 0;
-		for (int i = 0; i < table.length; i++) {
-			for (Entry e = table[i]; e != null; e = e.tableNext) {
+		for (Entry t : table) {
+			for (Entry e = t; e != null; e = e.tableNext) {
 				r += e.data.length;
 			}
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 127ee6b..6f3f2bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -469,12 +469,8 @@ private List<ObjectIdWithOffset> toInclude(DfsPackFile src, DfsReader ctx)
 					continue SCAN;
 			want.add(new ObjectIdWithOffset(id, ent.getOffset()));
 		}
-		Collections.sort(want, new Comparator<ObjectIdWithOffset>() {
-			@Override
-			public int compare(ObjectIdWithOffset a, ObjectIdWithOffset b) {
-				return Long.signum(a.offset - b.offset);
-			}
-		});
+		Collections.sort(want, (ObjectIdWithOffset a,
+				ObjectIdWithOffset b) -> Long.signum(a.offset - b.offset));
 		return want;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index d04709f..c75b88f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -287,14 +287,12 @@ public Set<ObjectId> getShallowCommits() {
 		return Collections.emptySet();
 	}
 
-	private static final Comparator<FoundObject<?>> FOUND_OBJECT_SORT = new Comparator<FoundObject<?>>() {
-		@Override
-		public int compare(FoundObject<?> a, FoundObject<?> b) {
-			int cmp = a.packIndex - b.packIndex;
-			if (cmp == 0)
-				cmp = Long.signum(a.offset - b.offset);
-			return cmp;
-		}
+	private static final Comparator<FoundObject<?>> FOUND_OBJECT_SORT = (
+			FoundObject<?> a, FoundObject<?> b) -> {
+		int cmp = a.packIndex - b.packIndex;
+		if (cmp == 0)
+			cmp = Long.signum(a.offset - b.offset);
+		return cmp;
 	};
 
 	private static class FoundObject<T extends ObjectId> {
@@ -565,12 +563,9 @@ public DfsObjectToPack newObjectToPack(AnyObjectId objectId, int type) {
 		return new DfsObjectToPack(objectId, type);
 	}
 
-	private static final Comparator<DfsObjectToPack> OFFSET_SORT = new Comparator<DfsObjectToPack>() {
-		@Override
-		public int compare(DfsObjectToPack a, DfsObjectToPack b) {
-			return Long.signum(a.getOffset() - b.getOffset());
-		}
-	};
+	private static final Comparator<DfsObjectToPack> OFFSET_SORT = (
+			DfsObjectToPack a,
+			DfsObjectToPack b) -> Long.signum(a.getOffset() - b.getOffset());
 
 	@Override
 	public void selectObjectRepresentation(PackWriter packer,
@@ -757,6 +752,7 @@ int copy(BlockBasedFile file, long position, byte[] dstbuf, int dstoff,
 	 */
 	int inflate(DfsPackFile pack, long position, byte[] dstbuf,
 			boolean headerOnly) throws IOException, DataFormatException {
+		long start = System.nanoTime();
 		prepareInflater();
 		pin(pack, position);
 		position += block.setInput(position, inf);
@@ -765,6 +761,7 @@ int inflate(DfsPackFile pack, long position, byte[] dstbuf,
 			dstoff += n;
 			if (inf.finished() || (headerOnly && dstoff == dstbuf.length)) {
 				stats.inflatedBytes += dstoff;
+				stats.inflationMicros += BlockBasedFile.elapsedMicros(start);
 				return dstoff;
 			} else if (inf.needsInput()) {
 				pin(pack, position);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
index c35801f..d6401a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
@@ -85,6 +85,9 @@ public static class Accumulator {
 		/** Total number of bytes decompressed. */
 		long inflatedBytes;
 
+		/** Total microseconds spent inflating compressed bytes. */
+		long inflationMicros;
+
 		Accumulator() {
 		}
 	}
@@ -186,4 +189,13 @@ public long getReadBlocksMicros() {
 	public long getInflatedBytes() {
 		return stats.inflatedBytes;
 	}
+
+	/**
+	 * Get total microseconds spent inflating compressed bytes.
+	 *
+	 * @return total microseconds inflating compressed bytes.
+	 */
+	public long getInflationMicros() {
+		return stats.inflationMicros;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
index 8b2a03d..732cd4d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
@@ -43,6 +43,7 @@
 
 package org.eclipse.jgit.internal.storage.dfs;
 
+import static org.eclipse.jgit.lib.Ref.UNDEFINED_UPDATE_INDEX;
 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
 
 import java.io.IOException;
@@ -175,7 +176,7 @@ public Ref peel(Ref ref) throws IOException {
 			cachePeeledState(oldLeaf, newLeaf);
 		}
 
-		return recreate(ref, newLeaf);
+		return recreate(ref, newLeaf, hasVersioning());
 	}
 
 	Ref doPeel(Ref leaf) throws MissingObjectException,
@@ -187,20 +188,26 @@ Ref doPeel(Ref leaf) throws MissingObjectException,
 						leaf.getStorage(),
 						leaf.getName(),
 						leaf.getObjectId(),
-						rw.peel(obj).copy());
+						rw.peel(obj).copy(),
+						hasVersioning() ? leaf.getUpdateIndex()
+								: UNDEFINED_UPDATE_INDEX);
 			} else {
 				return new ObjectIdRef.PeeledNonTag(
 						leaf.getStorage(),
 						leaf.getName(),
-						leaf.getObjectId());
+						leaf.getObjectId(),
+						hasVersioning() ? leaf.getUpdateIndex()
+								: UNDEFINED_UPDATE_INDEX);
 			}
 		}
 	}
 
-	static Ref recreate(Ref old, Ref leaf) {
+	static Ref recreate(Ref old, Ref leaf, boolean hasVersioning) {
 		if (old.isSymbolic()) {
-			Ref dst = recreate(old.getTarget(), leaf);
-			return new SymbolicRef(old.getName(), dst);
+			Ref dst = recreate(old.getTarget(), leaf, hasVersioning);
+			return new SymbolicRef(old.getName(), dst,
+					hasVersioning ? old.getUpdateIndex()
+							: UNDEFINED_UPDATE_INDEX);
 		}
 		return leaf;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
index 83394bb..6050c15 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
@@ -47,8 +47,10 @@
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.eclipse.jgit.annotations.Nullable;
@@ -277,12 +279,31 @@ public List<Ref> getRefsByPrefix(String prefix) throws IOException {
 
 	/** {@inheritDoc} */
 	@Override
+	public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
+		if (!getReftableConfig().isIndexObjects()) {
+			return super.getTipsWithSha1(id);
+		}
+		lock.lock();
+		try {
+			RefCursor cursor = reader().byObjectId(id);
+			Set<Ref> refs = new HashSet<>();
+			while (cursor.next()) {
+				refs.add(cursor.getRef());
+			}
+			return refs;
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
 	public Ref peel(Ref ref) throws IOException {
 		Ref oldLeaf = ref.getLeaf();
 		if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null) {
 			return ref;
 		}
-		return recreate(ref, doPeel(oldLeaf));
+		return recreate(ref, doPeel(oldLeaf), hasVersioning());
 	}
 
 	@Override
@@ -315,6 +336,7 @@ protected boolean compareAndPut(Ref oldRef, @Nullable Ref newRef)
 			throws IOException {
 		ReceiveCommand cmd = toCommand(oldRef, newRef);
 		try (RevWalk rw = new RevWalk(getRepository())) {
+			rw.setRetainBody(false);
 			newBatchUpdate().setAllowNonFastForwards(true).addCommand(cmd)
 					.execute(rw, NullProgressMonitor.INSTANCE);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index 5ced686..d82d29e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -62,8 +62,6 @@
 import org.eclipse.jgit.attributes.AttributesNode;
 import org.eclipse.jgit.attributes.AttributesNodeProvider;
 import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.events.ConfigChangedEvent;
-import org.eclipse.jgit.events.ConfigChangedListener;
 import org.eclipse.jgit.events.IndexChangedEvent;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
@@ -206,12 +204,7 @@ public boolean isOutdated() {
 		loadUserConfig();
 		loadRepoConfig();
 
-		repoConfig.addChangeListener(new ConfigChangedListener() {
-			@Override
-			public void onConfigChanged(ConfigChangedEvent event) {
-				fireEvent(event);
-			}
-		});
+		repoConfig.addChangeListener(this::fireEvent);
 
 		final long repositoryFormatVersion = getConfig().getLong(
 				ConfigConstants.CONFIG_CORE_SECTION, null,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index b80c58c..a4fc1a2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -116,12 +116,8 @@ static File getLockFile(File file) {
 	}
 
 	/** Filter to skip over active lock files when listing a directory. */
-	static final FilenameFilter FILTER = new FilenameFilter() {
-		@Override
-		public boolean accept(File dir, String name) {
-			return !name.endsWith(LOCK_SUFFIX);
-		}
-	};
+	static final FilenameFilter FILTER = (File dir,
+			String name) -> !name.endsWith(LOCK_SUFFIX);
 
 	private final File ref;
 
@@ -239,13 +235,7 @@ public void copyCurrentContent() throws IOException {
 			// Don't worry about a file that doesn't exist yet, it
 			// conceptually has no current content to copy.
 			//
-		} catch (IOException ioe) {
-			unlock();
-			throw ioe;
-		} catch (RuntimeException ioe) {
-			unlock();
-			throw ioe;
-		} catch (Error ioe) {
+		} catch (IOException | RuntimeException | Error ioe) {
 			unlock();
 			throw ioe;
 		}
@@ -299,13 +289,7 @@ public void write(byte[] content) throws IOException {
 			}
 			os.close();
 			os = null;
-		} catch (IOException ioe) {
-			unlock();
-			throw ioe;
-		} catch (RuntimeException ioe) {
-			unlock();
-			throw ioe;
-		} catch (Error ioe) {
+		} catch (IOException | RuntimeException | Error ioe) {
 			unlock();
 			throw ioe;
 		}
@@ -353,13 +337,7 @@ public void close() throws IOException {
 						os.getChannel().force(true);
 					out.close();
 					os = null;
-				} catch (IOException ioe) {
-					unlock();
-					throw ioe;
-				} catch (RuntimeException ioe) {
-					unlock();
-					throw ioe;
-				} catch (Error ioe) {
+				} catch (IOException | RuntimeException | Error ioe) {
 					unlock();
 					throw ioe;
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
index eff7958..9941ff3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
@@ -45,7 +45,6 @@
 
 import java.text.MessageFormat;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
@@ -133,12 +132,8 @@ private static void sortByOffsetAndIndex(BlockList<PositionEntry> byOffset,
 		for (int i = 0; i < entries.size(); i++) {
 			positionEntries.add(new PositionEntry(entries.get(i), i));
 		}
-		Collections.sort(entries, new Comparator<ObjectToPack>() {
-			@Override
-			public int compare(ObjectToPack a, ObjectToPack b) {
-				return Long.signum(a.getOffset() - b.getOffset());
-			}
-		});
+		Collections.sort(entries, (ObjectToPack a, ObjectToPack b) -> Long
+				.signum(a.getOffset() - b.getOffset()));
 		for (int i = 0; i < entries.size(); i++) {
 			PositionEntry e = positionEntries.get(entries.get(i));
 			e.offsetPosition = i;
@@ -310,57 +305,55 @@ public int getObjectCount() {
 	public Iterable<StoredEntry> getCompressedBitmaps() {
 		// Add order is from oldest to newest. The reverse add order is the
 		// output order.
-		return new Iterable<StoredEntry>() {
+		return () -> new Iterator<StoredEntry>() {
+
+			private int index = byAddOrder.size() - 1;
+
 			@Override
-			public Iterator<StoredEntry> iterator() {
-				return new Iterator<StoredEntry>() {
-					private int index = byAddOrder.size() - 1;
+			public boolean hasNext() {
+				return index >= 0;
+			}
 
-					@Override
-					public boolean hasNext() {
-						return index >= 0;
+			@Override
+			public StoredEntry next() {
+				if (!hasNext()) {
+					throw new NoSuchElementException();
+				}
+				StoredBitmap item = byAddOrder.get(index);
+				int bestXorOffset = 0;
+				EWAHCompressedBitmap bestBitmap = item.getBitmap();
+
+				// Attempt to compress the bitmap with an XOR of the
+				// previously written entries.
+				for (int i = 1; i <= MAX_XOR_OFFSET_SEARCH; i++) {
+					int curr = i + index;
+					if (curr >= byAddOrder.size()) {
+						break;
 					}
 
-					@Override
-					public StoredEntry next() {
-						if (!hasNext())
-							throw new NoSuchElementException();
-						StoredBitmap item = byAddOrder.get(index);
-						int bestXorOffset = 0;
-						EWAHCompressedBitmap bestBitmap = item.getBitmap();
+					StoredBitmap other = byAddOrder.get(curr);
+					EWAHCompressedBitmap bitmap = other.getBitmap()
+							.xor(item.getBitmap());
 
-						// Attempt to compress the bitmap with an XOR of the
-						// previously written entries.
-						for (int i = 1; i <= MAX_XOR_OFFSET_SEARCH; i++) {
-							int curr = i + index;
-							if (curr >= byAddOrder.size())
-								break;
-
-							StoredBitmap other = byAddOrder.get(curr);
-							EWAHCompressedBitmap bitmap = other.getBitmap()
-									.xor(item.getBitmap());
-
-							if (bitmap.sizeInBytes()
-									< bestBitmap.sizeInBytes()) {
-								bestBitmap = bitmap;
-								bestXorOffset = i;
-							}
-						}
-						index--;
-
-						PositionEntry entry = positionEntries.get(item);
-						if (entry == null)
-							throw new IllegalStateException();
-						bestBitmap.trim();
-						return new StoredEntry(entry.namePosition, bestBitmap,
-								bestXorOffset, item.getFlags());
+					if (bitmap.sizeInBytes() < bestBitmap.sizeInBytes()) {
+						bestBitmap = bitmap;
+						bestXorOffset = i;
 					}
+				}
+				index--;
 
-					@Override
-					public void remove() {
-						throw new UnsupportedOperationException();
-					}
-				};
+				PositionEntry entry = positionEntries.get(item);
+				if (entry == null) {
+					throw new IllegalStateException();
+				}
+				bestBitmap.trim();
+				return new StoredEntry(entry.namePosition, bestBitmap,
+						bestXorOffset, item.getFlags());
+			}
+
+			@Override
+			public void remove() {
+				throw new UnsupportedOperationException();
 			}
 		};
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
index 73ad38c..a89e2ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
@@ -104,12 +104,8 @@
 public class PackFile implements Iterable<PackIndex.MutableEntry> {
 	private final static Logger LOG = LoggerFactory.getLogger(PackFile.class);
 	/** Sorts PackFiles to be most recently created to least recently created. */
-	public static final Comparator<PackFile> SORT = new Comparator<PackFile>() {
-		@Override
-		public int compare(PackFile a, PackFile b) {
-			return b.packLastModified - a.packLastModified;
-		}
-	};
+	public static final Comparator<PackFile> SORT = (PackFile a,
+			PackFile b) -> b.packLastModified - a.packLastModified;
 
 	private final File packFile;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index a4729bb..e5cea6c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -745,7 +745,7 @@ private PackedRefList pack(Collection<String> refs,
 		for (LockFile ol : heldLocks.values()) {
 			ol.requireLock();
 		}
-		if (refs.size() == 0) {
+		if (refs.isEmpty()) {
 			return null;
 		}
 		FS fs = parent.getFS();
@@ -1128,9 +1128,11 @@ private Ref readRef(String name, RefList<Ref> packed) throws IOException {
 
 		// check whether the found new ref is the an additional ref. These refs
 		// should not go into looseRefs
-		for (int i = 0; i < additionalRefsNames.length; i++)
-			if (name.equals(additionalRefsNames[i]))
+		for (String additionalRefsName : additionalRefsNames) {
+			if (name.equals(additionalRefsName)) {
 				return n;
+			}
+		}
 
 		if (looseRefs.compareAndSet(curList, curList.add(idx, n)))
 			modCnt.incrementAndGet();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index e8fac51..da7250d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -298,13 +298,7 @@ private ByteWindow load(PackFile pack, long offset)
 			if (mmap)
 				return pack.mmap(offset, windowSize);
 			return pack.read(offset, windowSize);
-		} catch (IOException e) {
-			close(pack);
-			throw e;
-		} catch (RuntimeException e) {
-			close(pack);
-			throw e;
-		} catch (Error e) {
+		} catch (IOException | RuntimeException | Error e) {
 			close(pack);
 			throw e;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
index 0347644..a211d16 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
@@ -46,7 +46,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -212,12 +211,8 @@ private ArrayList<WeightedPath> computeTopPaths() {
 			}
 
 			// Sort by starting index to identify gaps later.
-			Collections.sort(topPaths, new Comparator<WeightedPath>() {
-				@Override
-				public int compare(WeightedPath a, WeightedPath b) {
-					return a.slice.beginIndex - b.slice.beginIndex;
-				}
-			});
+			Collections.sort(topPaths, (WeightedPath a,
+					WeightedPath b) -> a.slice.beginIndex - b.slice.beginIndex);
 
 			bytesPerUnit = 1;
 			while (MAX_METER <= (totalWeight / bytesPerUnit)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
index a047534..d152a39 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
@@ -365,9 +365,7 @@ private void cacheDelta(ObjectToPack srcObj, ObjectToPack resObj) {
 
 				resObj.setCachedDelta(deltaCache.cache(zbuf, len, deltaLen));
 				resObj.setCachedSize(deltaLen);
-			} catch (IOException err) {
-				deltaCache.credit(deltaLen);
-			} catch (OutOfMemoryError err) {
+			} catch (IOException | OutOfMemoryError err) {
 				deltaCache.credit(deltaLen);
 			}
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 1e3d74a..2f7e1a6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -44,6 +44,7 @@
 
 package org.eclipse.jgit.internal.storage.pack;
 
+import static java.util.Objects.requireNonNull;
 import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_DELTA;
 import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_WHOLE;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
@@ -61,7 +62,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -119,6 +120,7 @@
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.storage.pack.PackConfig;
 import org.eclipse.jgit.storage.pack.PackStatistics;
+import org.eclipse.jgit.transport.FilterSpec;
 import org.eclipse.jgit.transport.ObjectCountCallback;
 import org.eclipse.jgit.transport.WriteAbortedException;
 import org.eclipse.jgit.util.BlockList;
@@ -303,7 +305,7 @@ public static Iterable<PackWriter> getInstances() {
 
 	private ObjectCountCallback callback;
 
-	private long filterBlobLimit = -1;
+	private FilterSpec filterSpec = FilterSpec.NO_FILTER;
 
 	/**
 	 * Create writer for specified repository.
@@ -641,10 +643,11 @@ public void setShallowPack(int depth,
 	}
 
 	/**
-	 * @param bytes exclude blobs of size greater than this
+	 * @param filter the filter which indicates what and what not this writer
+	 *            should include
 	 */
-	public void setFilterBlobLimit(long bytes) {
-		filterBlobLimit = bytes;
+	public void setFilterSpec(@NonNull FilterSpec filter) {
+		filterSpec = requireNonNull(filter);
 	}
 
 	/**
@@ -870,6 +873,37 @@ private ObjectWalk getObjectWalk() {
 	}
 
 	/**
+	 * A visitation policy which uses the depth at which the object is seen to
+	 * decide if re-traversal is necessary. In particular, if the object has
+	 * already been visited at this depth or shallower, it is not necessary to
+	 * re-visit at this depth.
+	 */
+	private class DepthAwareVisitationPolicy
+			implements ObjectWalk.VisitationPolicy {
+		private final Map<ObjectId, Integer> lowestDepthVisited = new HashMap<>();
+
+		private final ObjectWalk walk;
+
+		DepthAwareVisitationPolicy(ObjectWalk walk) {
+			this.walk = requireNonNull(walk);
+		}
+
+		@Override
+		public boolean shouldVisit(RevObject o) {
+			Integer lastDepth = lowestDepthVisited.get(o);
+			if (lastDepth == null) {
+				return true;
+			}
+			return walk.getTreeDepth() < lastDepth.intValue();
+		}
+
+		@Override
+		public void visited(RevObject o) {
+			lowestDepthVisited.put(o, Integer.valueOf(walk.getTreeDepth()));
+		}
+	}
+
+	/**
 	 * Prepare the list of objects to be written to the pack stream.
 	 * <p>
 	 * Basing on these 2 sets, another set of objects to put in a pack file is
@@ -910,6 +944,9 @@ public void preparePack(ProgressMonitor countingMonitor,
 		if (shallowPack && !(walk instanceof DepthWalk.ObjectWalk))
 			throw new IllegalArgumentException(
 					JGitText.get().shallowPacksRequireDepthWalk);
+		if (filterSpec.getTreeDepthLimit() >= 0) {
+			walk.setVisitationPolicy(new DepthAwareVisitationPolicy(walk));
+		}
 		findObjectsToPack(countingMonitor, walk, interestingObjects,
 				uninterestingObjects, noBitmaps);
 	}
@@ -1380,32 +1417,33 @@ private void searchForDeltas(ProgressMonitor monitor)
 		// applies "Linus' Law" which states that newer files tend to be the
 		// bigger ones, because source files grow and hardly ever shrink.
 		//
-		Arrays.sort(list, 0, cnt, new Comparator<ObjectToPack>() {
-			@Override
-			public int compare(ObjectToPack a, ObjectToPack b) {
-				int cmp = (a.isDoNotDelta() ? 1 : 0)
-						- (b.isDoNotDelta() ? 1 : 0);
-				if (cmp != 0)
-					return cmp;
-
-				cmp = a.getType() - b.getType();
-				if (cmp != 0)
-					return cmp;
-
-				cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1);
-				if (cmp != 0)
-					return cmp;
-
-				cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1);
-				if (cmp != 0)
-					return cmp;
-
-				cmp = (a.isEdge() ? 0 : 1) - (b.isEdge() ? 0 : 1);
-				if (cmp != 0)
-					return cmp;
-
-				return b.getWeight() - a.getWeight();
+		Arrays.sort(list, 0, cnt, (ObjectToPack a, ObjectToPack b) -> {
+			int cmp = (a.isDoNotDelta() ? 1 : 0) - (b.isDoNotDelta() ? 1 : 0);
+			if (cmp != 0) {
+				return cmp;
 			}
+
+			cmp = a.getType() - b.getType();
+			if (cmp != 0) {
+				return cmp;
+			}
+
+			cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1);
+			if (cmp != 0) {
+				return cmp;
+			}
+
+			cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1);
+			if (cmp != 0) {
+				return cmp;
+			}
+
+			cmp = (a.isEdge() ? 0 : 1) - (b.isEdge() ? 0 : 1);
+			if (cmp != 0) {
+				return cmp;
+			}
+
+			return b.getWeight() - a.getWeight();
 		});
 
 		// Above we stored the objects we cannot delta onto the end.
@@ -1499,7 +1537,7 @@ private void parallelDeltaSearch(ProgressMonitor monitor,
 
 		Executor executor = config.getExecutor();
 		final List<Throwable> errors =
-				Collections.synchronizedList(new ArrayList<Throwable>(threads));
+				Collections.synchronizedList(new ArrayList<>(threads));
 		if (executor instanceof ExecutorService) {
 			// Caller supplied us a service, use it directly.
 			runTasks((ExecutorService) executor, pm, taskBlock, errors);
@@ -1526,14 +1564,11 @@ private void parallelDeltaSearch(ProgressMonitor monitor,
 			// asynchronous execution.  Wrap everything and hope it
 			// can schedule these for us.
 			for (DeltaTask task : taskBlock.tasks) {
-				executor.execute(new Runnable() {
-					@Override
-					public void run() {
-						try {
-							task.call();
-						} catch (Throwable failure) {
-							errors.add(failure);
-						}
+				executor.execute(() -> {
+					try {
+						task.call();
+					} catch (Throwable failure) {
+						errors.add(failure);
 					}
 				});
 			}
@@ -1969,7 +2004,9 @@ private void findObjectsToPack(@NonNull ProgressMonitor countingMonitor,
 				byte[] pathBuf = walker.getPathBuffer();
 				int pathLen = walker.getPathLength();
 				bases.addBase(o.getType(), pathBuf, pathLen, pathHash);
-				filterAndAddObject(o, o.getType(), pathHash, want);
+				if (!depthSkip(o, walker)) {
+					filterAndAddObject(o, o.getType(), pathHash, want);
+				}
 				countingMonitor.update(1);
 			}
 		} else {
@@ -1979,7 +2016,10 @@ private void findObjectsToPack(@NonNull ProgressMonitor countingMonitor,
 					continue;
 				if (exclude(o))
 					continue;
-				filterAndAddObject(o, o.getType(), walker.getPathHashCode(), want);
+				if (!depthSkip(o, walker)) {
+					filterAndAddObject(o, o.getType(), walker.getPathHashCode(),
+									   want);
+				}
 				countingMonitor.update(1);
 			}
 		}
@@ -2071,6 +2111,44 @@ private void addObject(
 		objectsMap.add(otp);
 	}
 
+	/**
+	 * Determines if the object should be omitted from the pack as a result of
+	 * its depth (probably because of the tree:<depth> filter).
+	 * <p>
+	 * Causes {@code walker} to skip traversing the current tree, which ought to
+	 * have just started traversal, assuming this method is called as soon as a
+	 * new depth is reached.
+	 * <p>
+	 * This method increments the {@code treesTraversed} statistic.
+	 *
+	 * @param obj
+	 *            the object to check whether it should be omitted.
+	 * @param walker
+	 *            the walker being used for traveresal.
+	 * @return whether the given object should be skipped.
+	 */
+	private boolean depthSkip(@NonNull RevObject obj, ObjectWalk walker) {
+		long treeDepth = walker.getTreeDepth();
+
+		// Check if this object needs to be rejected because it is a tree or
+		// blob that is too deep from the root tree.
+
+		// A blob is considered one level deeper than the tree that contains it.
+		if (obj.getType() == OBJ_BLOB) {
+			treeDepth++;
+		} else {
+			stats.treesTraversed++;
+		}
+
+		if (filterSpec.getTreeDepthLimit() < 0 ||
+			treeDepth <= filterSpec.getTreeDepthLimit()) {
+			return false;
+		}
+
+		walker.skipTree();
+		return true;
+	}
+
 	// Adds the given object as an object to be packed, first performing
 	// filtering on blobs at or exceeding a given size.
 	private void filterAndAddObject(@NonNull AnyObjectId src, int type,
@@ -2079,10 +2157,10 @@ private void filterAndAddObject(@NonNull AnyObjectId src, int type,
 
 		// Check if this object needs to be rejected, doing the cheaper
 		// checks first.
-		boolean reject = filterBlobLimit >= 0 &&
+		boolean reject = filterSpec.getBlobLimit() >= 0 &&
 			type == OBJ_BLOB &&
 			!want.contains(src) &&
-			reader.getObjectSize(src, OBJ_BLOB) > filterBlobLimit;
+			reader.getObjectSize(src, OBJ_BLOB) > filterSpec.getBlobLimit();
 		if (!reject) {
 			addObject(src, type, pathHashCode);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
index 99db749..d3b5e12 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -91,12 +91,9 @@ class PackWriterBitmapPreparer {
 
 	private static final int DAY_IN_SECONDS = 24 * 60 * 60;
 
-	private static final Comparator<RevCommit> ORDER_BY_REVERSE_TIMESTAMP = new Comparator<RevCommit>() {
-		@Override
-		public int compare(RevCommit a, RevCommit b) {
-			return Integer.signum(b.getCommitTime() - a.getCommitTime());
-		}
-	};
+	private static final Comparator<RevCommit> ORDER_BY_REVERSE_TIMESTAMP = (
+			RevCommit a, RevCommit b) -> Integer
+					.signum(b.getCommitTime() - a.getCommitTime());
 
 	private final ObjectReader reader;
 	private final ProgressMonitor pm;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 9763fb7..54f6bbc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -365,50 +365,50 @@ public final class ConfigConstants {
 	public static final String CONFIG_KEY_CHECKSTAT = "checkstat";
 
 	/**
-	 * The "renamelimit" key in the "diff section"
+	 * The "renamelimit" key in the "diff" section
 	 * @since 3.0
 	 */
 	public static final String CONFIG_KEY_RENAMELIMIT = "renamelimit";
 
 	/**
-	 * The "trustfolderstat" key in the "core section"
+	 * The "trustfolderstat" key in the "core" section
 	 * @since 3.6
 	 */
 	public static final String CONFIG_KEY_TRUSTFOLDERSTAT = "trustfolderstat";
 
 	/**
-	 * The "supportsAtomicFileCreation" key in the "core section"
+	 * The "supportsAtomicFileCreation" key in the "core" section
 	 *
 	 * @since 4.5
 	 */
 	public static final String CONFIG_KEY_SUPPORTSATOMICFILECREATION = "supportsatomicfilecreation";
 
 	/**
-	 * The "noprefix" key in the "diff section"
+	 * The "noprefix" key in the "diff" section
 	 * @since 3.0
 	 */
 	public static final String CONFIG_KEY_NOPREFIX = "noprefix";
 
 	/**
-	 * A "renamelimit" value in the "diff section"
+	 * A "renamelimit" value in the "diff" section
 	 * @since 3.0
 	 */
 	public static final String CONFIG_RENAMELIMIT_COPY = "copy";
 
 	/**
-	 * A "renamelimit" value in the "diff section"
+	 * A "renamelimit" value in the "diff" section
 	 * @since 3.0
 	 */
 	public static final String CONFIG_RENAMELIMIT_COPIES = "copies";
 
 	/**
-	 * The "renames" key in the "diff section"
+	 * The "renames" key in the "diff" section
 	 * @since 3.0
 	 */
 	public static final String CONFIG_KEY_RENAMES = "renames";
 
 	/**
-	 * The "inCoreLimit" key in the "merge section". It's a size limit (bytes) used to
+	 * The "inCoreLimit" key in the "merge" section. It's a size limit (bytes) used to
 	 * control a file to be stored in {@code Heap} or {@code LocalFile} during the merge.
 	 * @since 4.9
 	 */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 4c55196..8f4468e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -58,7 +58,7 @@
 import org.eclipse.jgit.util.MutableInteger;
 
 /**
- * Misc. constants used throughout JGit.
+ * Misc. constants and helpers used throughout JGit.
  */
 @SuppressWarnings("nls")
 public final class Constants {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index f37c310..42d1330 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -352,12 +352,7 @@ public interface WorkingTreeIteratorFactory {
 		public WorkingTreeIterator getWorkingTreeIterator(Repository repo);
 	}
 
-	private WorkingTreeIteratorFactory wTreeIt = new WorkingTreeIteratorFactory() {
-		@Override
-		public WorkingTreeIterator getWorkingTreeIterator(Repository repo) {
-			return new FileTreeIterator(repo);
-		}
-	};
+	private WorkingTreeIteratorFactory wTreeIt = FileTreeIterator::new;
 
 	/**
 	 * Allows higher layers to set the factory for WorkingTreeIterators.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
index b791c64..d65c1bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
@@ -67,7 +67,7 @@ public static class Unpeeled extends ObjectIdRef {
 		 */
 		public Unpeeled(@NonNull Storage st, @NonNull String name,
 				@Nullable ObjectId id) {
-			super(st, name, id, -1);
+			super(st, name, id, UNDEFINED_UPDATE_INDEX);
 		}
 
 		/**
@@ -119,7 +119,7 @@ public static class PeeledTag extends ObjectIdRef {
 		 */
 		public PeeledTag(@NonNull Storage st, @NonNull String name,
 				@Nullable ObjectId id, @NonNull ObjectId p) {
-			super(st, name, id, -1);
+			super(st, name, id, UNDEFINED_UPDATE_INDEX);
 			peeledObjectId = p;
 		}
 
@@ -172,7 +172,7 @@ public static class PeeledNonTag extends ObjectIdRef {
 		 */
 		public PeeledNonTag(@NonNull Storage st, @NonNull String name,
 				@Nullable ObjectId id) {
-			super(st, name, id, -1);
+			super(st, name, id, UNDEFINED_UPDATE_INDEX);
 		}
 
 		/**
@@ -284,7 +284,7 @@ public Storage getStorage() {
 	 */
 	@Override
 	public long getUpdateIndex() {
-		if (updateIndex == -1) {
+		if (updateIndex == UNDEFINED_UPDATE_INDEX) {
 			throw new UnsupportedOperationException();
 		}
 		return updateIndex;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
index 32c8b06..4082d21 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
@@ -126,6 +126,13 @@ public boolean isPacked() {
 	}
 
 	/**
+	 * Update index value when a reference doesn't have one
+	 *
+	 * @since 5.4
+	 */
+	long UNDEFINED_UPDATE_INDEX = -1L;
+
+	/**
 	 * What this ref is called within the repository.
 	 *
 	 * @return name of this ref.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index 8777920..4d9450e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -44,6 +44,7 @@
 package org.eclipse.jgit.lib;
 
 import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -52,7 +53,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
+import java.util.Set;
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
 
@@ -470,6 +471,31 @@ public List<Ref> getRefsByPrefix(String... prefixes) throws IOException {
 		return Collections.unmodifiableList(result);
 	}
 
+
+	/**
+	 * Returns all refs that resolve directly to the given {@link ObjectId}.
+	 * Includes peeled {@linkObjectId}s. This is the inverse lookup of
+	 * {@link #exactRef(String...)}.
+	 *
+	 * <p>
+	 * The default implementation uses a linear scan. Implementors of
+	 * {@link RefDatabase} should override this method directly if a better
+	 * implementation is possible.
+	 *
+	 * @param id
+	 *            {@link ObjectId} to resolve
+	 * @return a {@link Set} of {@link Ref}s whose tips point to the provided
+	 *         id.
+	 * @throws java.io.IOException
+	 *             the reference space cannot be accessed.
+	 * @since 5.4
+	 */
+	@NonNull
+	public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
+		return getRefs().stream().filter(r -> id.equals(r.getObjectId())
+				|| id.equals(r.getPeeledObjectId())).collect(toSet());
+	}
+
 	/**
 	 * Check if any refs exist in the ref database.
 	 * <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index 1ce1528..7841627 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -599,6 +599,7 @@ public Result forceUpdate() throws IOException {
 	 */
 	public Result update() throws IOException {
 		try (RevWalk rw = new RevWalk(getRepository())) {
+			rw.setRetainBody(false);
 			return update(rw);
 		}
 	}
@@ -646,6 +647,7 @@ Result execute(Result status) throws IOException {
 	 */
 	public Result delete() throws IOException {
 		try (RevWalk rw = new RevWalk(getRepository())) {
+			rw.setRetainBody(false);
 			return delete(rw);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index a61897a..aac63e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -489,6 +489,7 @@ public ObjectId resolve(String revstr)
 			throws AmbiguousObjectException, IncorrectObjectTypeException,
 			RevisionSyntaxException, IOException {
 		try (RevWalk rw = new RevWalk(this)) {
+			rw.setRetainBody(false);
 			Object resolved = resolve(rw, revstr);
 			if (resolved instanceof String) {
 				final Ref ref = findRef((String) resolved);
@@ -515,6 +516,7 @@ public ObjectId resolve(String revstr)
 	public String simplify(String revstr)
 			throws AmbiguousObjectException, IOException {
 		try (RevWalk rw = new RevWalk(this)) {
+			rw.setRetainBody(true);
 			Object resolved = resolve(rw, revstr);
 			if (resolved != null)
 				if (resolved instanceof String)
@@ -606,7 +608,7 @@ private Object resolve(RevWalk rw, String revstr)
 								if (!(rev instanceof RevBlob))
 									throw new IncorrectObjectTypeException(rev,
 											Constants.TYPE_BLOB);
-							} else if (item.equals("")) { //$NON-NLS-1$
+							} else if (item.isEmpty()) {
 								rev = rw.peel(rev);
 							} else
 								throw new RevisionSyntaxException(revstr);
@@ -707,7 +709,7 @@ private Object resolve(RevWalk rw, String revstr)
 					if (time.equals("upstream")) { //$NON-NLS-1$
 						if (name == null)
 							name = new String(revChars, done, i);
-						if (name.equals("")) //$NON-NLS-1$
+						if (name.isEmpty())
 							// Currently checked out branch, HEAD if
 							// detached
 							name = Constants.HEAD;
@@ -762,7 +764,7 @@ private Object resolve(RevWalk rw, String revstr)
 					} else {
 						if (name == null)
 							name = new String(revChars, done, i);
-						if (name.equals("")) //$NON-NLS-1$
+						if (name.isEmpty())
 							name = Constants.HEAD;
 						if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
 							throw new RevisionSyntaxException(MessageFormat
@@ -788,7 +790,7 @@ private Object resolve(RevWalk rw, String revstr)
 				if (rev == null) {
 					if (name == null)
 						name = new String(revChars, done, i);
-					if (name.equals("")) //$NON-NLS-1$
+					if (name.isEmpty())
 						name = Constants.HEAD;
 					rev = parseSimple(rw, name);
 					name = null;
@@ -921,7 +923,7 @@ private ObjectId resolveAbbreviation(String revstr) throws IOException,
 		AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
 		try (ObjectReader reader = newObjectReader()) {
 			Collection<ObjectId> matches = reader.resolve(id);
-			if (matches.size() == 0)
+			if (matches.isEmpty())
 				return null;
 			else if (matches.size() == 1)
 				return matches.iterator().next();
@@ -1275,11 +1277,8 @@ public DirCache lockDirCache() throws NoWorkTreeException,
 			CorruptObjectException, IOException {
 		// we want DirCache to inform us so that we can inform registered
 		// listeners about index changes
-		IndexChangedListener l = new IndexChangedListener() {
-			@Override
-			public void onIndexChanged(IndexChangedEvent event) {
-				notifyIndexChanged(true);
-			}
+		IndexChangedListener l = (IndexChangedEvent event) -> {
+			notifyIndexChanged(true);
 		};
 		return DirCache.lock(this, l);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index 400342b1..27befba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -255,14 +255,11 @@ private void configureEviction(
 			if (delay == RepositoryCacheConfig.NO_CLEANUP) {
 				return;
 			}
-			cleanupTask = scheduler.scheduleWithFixedDelay(new Runnable() {
-				@Override
-				public void run() {
-					try {
-						cache.clearAllExpired();
-					} catch (Throwable e) {
-						LOG.error(e.getMessage(), e);
-					}
+			cleanupTask = scheduler.scheduleWithFixedDelay(() -> {
+				try {
+					cache.clearAllExpired();
+				} catch (Throwable e) {
+					LOG.error(e.getMessage(), e);
 				}
 			}, delay, delay, TimeUnit.MILLISECONDS);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
index 00fcf52..9f0568f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
@@ -71,7 +71,7 @@ public class SymbolicRef implements Ref {
 	public SymbolicRef(@NonNull String refName, @NonNull Ref target) {
 		this.name = refName;
 		this.target = target;
-		this.updateIndex = -1;
+		this.updateIndex = UNDEFINED_UPDATE_INDEX;
 	}
 
 	/**
@@ -155,7 +155,7 @@ public boolean isPeeled() {
 	 */
 	@Override
 	public long getUpdateIndex() {
-		if (updateIndex == -1) {
+		if (updateIndex == UNDEFINED_UPDATE_INDEX) {
 			throw new UnsupportedOperationException();
 		}
 		return updateIndex;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
index 091667d..df9615f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
@@ -85,6 +85,8 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.SystemReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Locates GPG keys from either <code>~/.gnupg/private-keys-v1.d</code> or
@@ -92,6 +94,9 @@
  */
 class BouncyCastleGpgKeyLocator {
 
+	private static final Logger log = LoggerFactory
+			.getLogger(BouncyCastleGpgKeyLocator.class);
+
 	private static final Path GPG_DIRECTORY = findGpgDirectory();
 
 	private static final Path USER_KEYBOX_PATH = GPG_DIRECTORY
@@ -157,11 +162,14 @@ public BouncyCastleGpgKeyLocator(String signingKey,
 	private PGPSecretKey attemptParseSecretKey(Path keyFile,
 			PGPDigestCalculatorProvider calculatorProvider,
 			PBEProtectionRemoverFactory passphraseProvider,
-			PGPPublicKey publicKey) throws IOException {
+			PGPPublicKey publicKey) {
 		try (InputStream in = newInputStream(keyFile)) {
 			return new SExprParser(calculatorProvider).parseSecretKey(
 					new BufferedInputStream(in), passphraseProvider, publicKey);
-		} catch (PGPException | ClassCastException e) {
+		} catch (IOException | PGPException | ClassCastException e) {
+			if (log.isDebugEnabled())
+				log.debug("Ignoring unreadable file '{}': {}", keyFile, //$NON-NLS-1$
+						e.getMessage(), e);
 			return null;
 		}
 	}
@@ -173,10 +181,11 @@ private boolean containsSigningKey(String userId) {
 
 	private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob)
 			throws IOException {
+		String keyId = signingKey.toLowerCase(Locale.ROOT);
 		for (KeyInformation keyInfo : keyBlob.getKeyInformation()) {
-			if (signingKey.toLowerCase(Locale.ROOT)
-					.equals(Hex.toHexString(keyInfo.getKeyID())
-							.toLowerCase(Locale.ROOT))) {
+			String fingerprint = Hex.toHexString(keyInfo.getFingerprint())
+					.toLowerCase(Locale.ROOT);
+			if (fingerprint.endsWith(keyId)) {
 				return getFirstPublicKey(keyBlob);
 			}
 		}
@@ -252,6 +261,10 @@ public BouncyCastleGpgKey findSecretKey()
 					USER_PGP_LEGACY_SECRING_FILE);
 
 			if (secretKey != null) {
+				if (!secretKey.isSigningKey()) {
+					throw new PGPException(MessageFormat.format(
+							JGitText.get().gpgNotASigningKey, signingKey));
+				}
 				return new BouncyCastleGpgKey(secretKey, USER_PGP_LEGACY_SECRING_FILE);
 			}
 
@@ -285,6 +298,10 @@ private BouncyCastleGpgKey findSecretKeyForKeyBoxPublicKey(
 				PGPSecretKey secretKey = attemptParseSecretKey(keyFile,
 						calculatorProvider, passphraseProvider, publicKey);
 				if (secretKey != null) {
+					if (!secretKey.isSigningKey()) {
+						throw new PGPException(MessageFormat.format(
+								JGitText.get().gpgNotASigningKey, signingKey));
+					}
 					return new BouncyCastleGpgKey(secretKey, userKeyboxPath);
 				}
 			}
@@ -326,6 +343,7 @@ private PGPSecretKey findSecretKeyInLegacySecring(String signingkey,
 					PGPUtil.getDecoderStream(new BufferedInputStream(in)),
 					new JcaKeyFingerprintCalculator());
 
+			String keyId = signingkey.toLowerCase(Locale.ROOT);
 			Iterator<PGPSecretKeyRing> keyrings = pgpSec.getKeyRings();
 			while (keyrings.hasNext()) {
 				PGPSecretKeyRing keyRing = keyrings.next();
@@ -336,8 +354,7 @@ private PGPSecretKey findSecretKeyInLegacySecring(String signingkey,
 					String fingerprint = Hex
 							.toHexString(key.getPublicKey().getFingerprint())
 							.toLowerCase(Locale.ROOT);
-					if (fingerprint
-							.endsWith(signingkey.toLowerCase(Locale.ROOT))) {
+					if (fingerprint.endsWith(keyId)) {
 						return key;
 					}
 					// try user id
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 9ea1868..b69327a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -1179,7 +1179,7 @@ public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
 	 *         fail.
 	 */
 	public Map<String, MergeFailureReason> getFailingPaths() {
-		return (failingPaths.size() == 0) ? null : failingPaths;
+		return failingPaths.isEmpty() ? null : failingPaths;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
index fd425ab..84bf214 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
@@ -97,9 +97,7 @@ static synchronized <T extends TranslationBundle> T lookupBundle(Locale locale,
 				bundles.put(type, bundle);
 			}
 			return (T) bundle;
-		} catch (InstantiationException e) {
-			throw new Error(e);
-		} catch (IllegalAccessException e) {
+		} catch (InstantiationException | IllegalAccessException e) {
 			throw new Error(e);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java
index cdd7be2..c41fb41 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/TranslationBundle.java
@@ -182,9 +182,7 @@ void load(Locale locale)
 					field.set(this, translatedText);
 				} catch (MissingResourceException e) {
 					throw new TranslationStringMissingException(bundleClass, locale, field.getName(), e);
-				} catch (IllegalArgumentException e) {
-					throw new Error(e);
-				} catch (IllegalAccessException e) {
+				} catch (IllegalArgumentException | IllegalAccessException e) {
 					throw new Error(e);
 				}
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
index d278132..74eec81 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
@@ -109,12 +109,13 @@ void parseHeader() {
 		final MutableInteger ptr = new MutableInteger();
 		ptr.value = nextLF(buf, startOffset, ' ');
 
-		for (int n = 0; n < old.length; n++) {
-			old[n].startLine = -parseBase10(buf, ptr.value, ptr);
-			if (buf[ptr.value] == ',')
-				old[n].lineCount = parseBase10(buf, ptr.value + 1, ptr);
-			else
-				old[n].lineCount = 1;
+		for (CombinedOldImage o : old) {
+			o.startLine = -parseBase10(buf, ptr.value, ptr);
+			if (buf[ptr.value] == ',') {
+				o.lineCount = parseBase10(buf, ptr.value + 1, ptr);
+			} else {
+				o.lineCount = 1;
+			}
 		}
 
 		newStartLine = parseBase10(buf, ptr.value + 1, ptr);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java
index 58e2106..b2f8d11 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/AbstractPlotRenderer.java
@@ -114,39 +114,39 @@ protected void paintCommit(PlotCommit<TLane> commit, int h) {
 			drawLine(myColor, myLaneX, h, myLaneX, (h + dotSize) / 2,
 					LINE_WIDTH);
 
-			for (int i = 0; i < commit.mergingLanes.length; i++) {
-				final TLane pLane = (TLane) commit.mergingLanes[i];
+			for (PlotLane mergingLane : commit.mergingLanes) {
+				final TLane pLane = (TLane) mergingLane;
 				final TColor pColor = laneColor(pLane);
 				final int cx = laneC(pLane);
-
 				if (Math.abs(myLaneX - cx) > LANE_WIDTH) {
 					final int ix;
-					if (myLaneX < cx)
+					if (myLaneX < cx) {
 						ix = cx - LANE_WIDTH / 2;
-					else
+					} else {
 						ix = cx + LANE_WIDTH / 2;
+					}
 
 					drawLine(pColor, myLaneX, h / 2, ix, h / 2, LINE_WIDTH);
 					drawLine(pColor, ix, h / 2, cx, h, LINE_WIDTH);
 				} else
 					drawLine(pColor, myLaneX, h / 2, cx, h, LINE_WIDTH);
-
 				maxCenter = Math.max(maxCenter, cx);
 			}
 		}
 
 
 		if (commit.getChildCount() > 0) {
-			for (int i = 0; i < commit.forkingOffLanes.length; i++) {
-				final TLane childLane = (TLane) commit.forkingOffLanes[i];
+			for (PlotLane forkingOffLane : commit.forkingOffLanes) {
+				final TLane childLane = (TLane) forkingOffLane;
 				final TColor cColor = laneColor(childLane);
 				final int cx = laneC(childLane);
 				if (Math.abs(myLaneX - cx) > LANE_WIDTH) {
 					final int ix;
-					if (myLaneX < cx)
+					if (myLaneX < cx) {
 						ix = cx - LANE_WIDTH / 2;
-					else
+					} else {
 						ix = cx + LANE_WIDTH / 2;
+					}
 
 					drawLine(cColor, myLaneX, h / 2, ix, h / 2, LINE_WIDTH);
 					drawLine(cColor, ix, h / 2, cx, 0, LINE_WIDTH);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java
new file mode 100644
index 0000000..e1d5d4a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java
@@ -0,0 +1,93 @@
+package org.eclipse.jgit.revwalk;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.revwalk.AddToBitmapFilter;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.ProgressMonitor;
+
+/**
+ * Calculate the bitmap indicating what other commits are reachable from certain
+ * commit.
+ * <p>
+ * This bitmap refers only to commits. For a bitmap with ALL objects reachable
+ * from certain object, see {@code BitmapWalker}.
+ */
+class BitmapCalculator {
+
+	private final RevWalk walk;
+	private final BitmapIndex bitmapIndex;
+
+	BitmapCalculator(RevWalk walk) throws IOException {
+		this.walk = walk;
+		this.bitmapIndex = requireNonNull(
+				walk.getObjectReader().getBitmapIndex());
+	}
+
+	/**
+	 * Get the reachability bitmap from certain commit to other commits.
+	 * <p>
+	 * This will return a precalculated bitmap if available or walk building one
+	 * until finding a precalculated bitmap (and returning the union).
+	 * <p>
+	 * Beware that the returned bitmap it is guaranteed to include ONLY the
+	 * commits reachable from the initial commit. It COULD include other objects
+	 * (because precalculated bitmaps have them) but caller shouldn't count on
+	 * that. See {@link BitmapWalker} for a full reachability bitmap.
+	 *
+	 * @param start
+	 *            the commit. Use {@code walk.parseCommit(objectId)} to get this
+	 *            object from the id.
+	 * @param pm
+	 *            progress monitor. Updated by one per commit browsed in the
+	 *            graph
+	 * @return the bitmap of reachable commits (and maybe some extra objects)
+	 *         for the commit
+	 * @throws MissingObjectException
+	 *             the supplied id doesn't exist
+	 * @throws IncorrectObjectTypeException
+	 *             the supplied id doens't refer to a commit or a tag
+	 * @throws IOException
+	 */
+	BitmapBuilder getBitmap(RevCommit start, ProgressMonitor pm)
+			throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		Bitmap precalculatedBitmap = bitmapIndex.getBitmap(start);
+		if (precalculatedBitmap != null) {
+			return asBitmapBuilder(precalculatedBitmap);
+		}
+
+		walk.reset();
+		walk.sort(RevSort.TOPO);
+		walk.markStart(start);
+		// Unbounded walk. If the repo has bitmaps, it should bump into one at
+		// some point.
+
+		BitmapBuilder bitmapResult = bitmapIndex.newBitmapBuilder();
+		walk.setRevFilter(new AddToBitmapFilter(bitmapResult));
+		while (walk.next() != null) {
+			// Iterate through all of the commits. The BitmapRevFilter does
+			// the work.
+			//
+			// filter.include returns true for commits that do not have
+			// a bitmap in bitmapIndex and are not reachable from a
+			// bitmap in bitmapIndex encountered earlier in the walk.
+			// Thus the number of commits returned by next() measures how
+			// much history was traversed without being able to make use
+			// of bitmaps.
+			pm.update(1);
+		}
+
+		return bitmapResult;
+	}
+
+	private BitmapBuilder asBitmapBuilder(Bitmap bitmap) {
+		return bitmapIndex.newBitmapBuilder().or(bitmap);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
new file mode 100644
index 0000000..ab45343
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019, Google LLC
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.revwalk;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+
+/**
+ * Checks the reachability using bitmaps.
+ *
+ * @since 5.4
+ */
+public class BitmappedReachabilityChecker implements ReachabilityChecker {
+
+	private final RevWalk walk;
+
+	/**
+	 * @param walk
+	 *            walk on the repository to get or create the bitmaps for the
+	 *            commits. It must have bitmaps.
+	 * @throws AssertionError
+	 *             runtime exception if walk is over a repository without
+	 *             bitmaps
+	 * @throws IOException
+	 *             if the index or the object reader cannot be opened.
+	 */
+	public BitmappedReachabilityChecker(RevWalk walk)
+			throws IOException {
+		this.walk = walk;
+		if (walk.getObjectReader().getBitmapIndex() == null) {
+			throw new AssertionError(
+					"Trying to use bitmapped reachability check " //$NON-NLS-1$
+							+ "on a repository without bitmaps"); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Check all targets are reachable from the starters.
+	 * <p>
+	 * In this implementation, it is recommended to put the most popular
+	 * starters (e.g. refs/heads tips) at the beginning of the collection
+	 */
+	@Override
+	public Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
+			Collection<RevCommit> starters) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		BitmapCalculator calculator = new BitmapCalculator(walk);
+
+		/**
+		 * Iterate over starters bitmaps and remove targets as they become
+		 * reachable.
+		 *
+		 * Building the total starters bitmap has the same cost (iterating over
+		 * all starters adding the bitmaps) and this gives us the chance to
+		 * shorcut the loop.
+		 *
+		 * This is based on the assuption that most of the starters will have
+		 * the reachability bitmap precalculated. If many require a walk, the
+		 * walk.reset() could start to take too much time.
+		 */
+		List<RevCommit> remainingTargets = new ArrayList<>(targets);
+		for (RevCommit starter : starters) {
+			BitmapBuilder starterBitmap = calculator.getBitmap(starter,
+					NullProgressMonitor.INSTANCE);
+			remainingTargets.removeIf(starterBitmap::contains);
+			if (remainingTargets.isEmpty()) {
+				return Optional.empty();
+			}
+		}
+
+		return Optional.of(remainingTargets.get(0));
+	}
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
index 5154920..5199a29 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
@@ -108,12 +108,25 @@ class DepthGenerator extends Generator {
 
 		// Begin by sucking out all of the source's commits, and
 		// adding them to the pending queue
+		FIFORevQueue unshallowCommits = new FIFORevQueue();
 		for (;;) {
 			RevCommit c = s.next();
 			if (c == null)
 				break;
-			if (((DepthWalk.Commit) c).getDepth() == 0)
+			if (c.has(UNSHALLOW)) {
+				unshallowCommits.add(c);
+			} else if (((DepthWalk.Commit) c).getDepth() == 0) {
 				pending.add(c);
+			}
+		}
+		// Move unshallow commits to the front so that the REINTERESTING flag
+		// carry over code is executed first.
+		for (;;) {
+			RevCommit c = unshallowCommits.next();
+			if (c == null) {
+				break;
+			}
+			pending.unpop(c);
 		}
 
 		// Mark DEEPEN_NOT on all deepen-not commits and their ancestors.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
index fd578da..af6040f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -43,6 +43,7 @@
 
 package org.eclipse.jgit.revwalk;
 
+import static java.util.Objects.requireNonNull;
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
 import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
@@ -97,6 +98,55 @@ public class ObjectWalk extends RevWalk {
 	 */
 	private static final int IN_PENDING = RevWalk.REWRITE;
 
+	/**
+	 * When walking over a tree and blob graph, objects are usually marked as
+	 * seen as they are visited and this "seen" status is checked upon the next
+	 * visit. If they are already "seen" then they are not processed (returned
+	 * by {@link ObjectWalk#nextObject()}) again. However, this behavior can be
+	 * overridden by supplying a different implementation of this class.
+	 *
+	 * @since 5.4
+	 */
+	public interface VisitationPolicy {
+		/**
+		 * Whenever the rev or object walk reaches a Git object, if that object
+		 * already exists as a RevObject, this method is called to determine if
+		 * that object should be visited.
+		 *
+		 * @param o
+		 *            the object to check if it should be visited
+		 * @return true if the object should be visited
+		 */
+		boolean shouldVisit(RevObject o);
+
+		/**
+		 * Called when an object is visited.
+		 *
+		 * @param o
+		 *            the object that was visited
+		 */
+		void visited(RevObject o);
+	}
+
+	/**
+	 * The default visitation policy: causes all objects to be visited exactly
+	 * once.
+	 *
+	 * @since 5.4
+	 */
+	public static final VisitationPolicy SIMPLE_VISITATION_POLICY =
+			new VisitationPolicy() {
+		@Override
+		public boolean shouldVisit(RevObject o) {
+			return (o.flags & SEEN) == 0;
+		}
+
+		@Override
+		public void visited(RevObject o) {
+			o.flags |= SEEN;
+		}
+	};
+
 	private List<RevObject> rootObjects;
 
 	private BlockObjQueue pendingObjects;
@@ -113,6 +163,8 @@ public class ObjectWalk extends RevWalk {
 
 	private boolean boundary;
 
+	private VisitationPolicy visitationPolicy = SIMPLE_VISITATION_POLICY;
+
 	/**
 	 * Create a new revision and object walker for a given repository.
 	 *
@@ -299,6 +351,18 @@ public void setObjectFilter(ObjectFilter newFilter) {
 		objectFilter = newFilter != null ? newFilter : ObjectFilter.ALL;
 	}
 
+	/**
+	 * Sets the visitation policy to use during this walk.
+	 *
+	 * @param policy
+	 *            the {@code VisitationPolicy} to use
+	 * @since 5.4
+	 */
+	public void setVisitationPolicy(VisitationPolicy policy) {
+		assertNotStarted();
+		visitationPolicy = requireNonNull(policy);
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public RevCommit next() throws MissingObjectException,
@@ -326,6 +390,17 @@ public RevCommit next() throws MissingObjectException,
 	}
 
 	/**
+	 * Skips the current tree such that {@link #nextObject()} does not return
+	 * any objects inside it. This should be called right after
+	 * {@link #nextObject()} returns the tree.
+	 *
+	 * @since 5.4
+	 */
+	public void skipTree() {
+		currVisit.ptr = currVisit.buf.length;
+	}
+
+	/**
 	 * Pop the next most recent object.
 	 *
 	 * @return next most recent object; null if traversal is over.
@@ -357,24 +432,23 @@ public RevObject nextObject() throws MissingObjectException,
 				}
 
 				RevObject obj = objects.get(idBuffer);
-				if (obj != null && (obj.flags & SEEN) != 0)
+				if (obj != null && !visitationPolicy.shouldVisit(obj))
 					continue;
 
 				int mode = parseMode(buf, startPtr, ptr, tv);
-				int flags;
 				switch (mode >>> TYPE_SHIFT) {
 				case TYPE_FILE:
 				case TYPE_SYMLINK:
 					if (obj == null) {
 						obj = new RevBlob(idBuffer);
-						obj.flags = SEEN;
+						visitationPolicy.visited(obj);
 						objects.add(obj);
 						return obj;
 					}
 					if (!(obj instanceof RevBlob))
 						throw new IncorrectObjectTypeException(obj, OBJ_BLOB);
-					obj.flags = flags = obj.flags | SEEN;
-					if ((flags & UNINTERESTING) == 0)
+					visitationPolicy.visited(obj);
+					if ((obj.flags & UNINTERESTING) == 0)
 						return obj;
 					if (boundary)
 						return obj;
@@ -383,17 +457,17 @@ public RevObject nextObject() throws MissingObjectException,
 				case TYPE_TREE:
 					if (obj == null) {
 						obj = new RevTree(idBuffer);
-						obj.flags = SEEN;
+						visitationPolicy.visited(obj);
 						objects.add(obj);
-						return enterTree(obj);
+						return pushTree(obj);
 					}
 					if (!(obj instanceof RevTree))
 						throw new IncorrectObjectTypeException(obj, OBJ_TREE);
-					obj.flags = flags = obj.flags | SEEN;
-					if ((flags & UNINTERESTING) == 0)
-						return enterTree(obj);
+					visitationPolicy.visited(obj);
+					if ((obj.flags & UNINTERESTING) == 0)
+						return pushTree(obj);
 					if (boundary)
-						return enterTree(obj);
+						return pushTree(obj);
 					continue;
 
 				case TYPE_GITLINK:
@@ -419,30 +493,23 @@ public RevObject nextObject() throws MissingObjectException,
 			if (o == null) {
 				return null;
 			}
-			int flags = o.flags;
-			if ((flags & SEEN) != 0)
+			if (!visitationPolicy.shouldVisit(o)) {
 				continue;
-			flags |= SEEN;
-			o.flags = flags;
-			if ((flags & UNINTERESTING) == 0 | boundary) {
+			}
+			visitationPolicy.visited(o);
+			if ((o.flags & UNINTERESTING) == 0 | boundary) {
 				if (o instanceof RevTree) {
-					tv = newTreeVisit(o);
-					tv.parent = null;
-					currVisit = tv;
+					// The previous while loop should have exhausted the stack
+					// of trees.
+					assert currVisit == null;
+
+					pushTree(o);
 				}
 				return o;
 			}
 		}
 	}
 
-	private RevObject enterTree(RevObject obj) throws MissingObjectException,
-			IncorrectObjectTypeException, IOException {
-		TreeVisit tv = newTreeVisit(obj);
-		tv.parent = currVisit;
-		currVisit = tv;
-		return obj;
-	}
-
 	private static int findObjectId(byte[] buf, int ptr) {
 		// Skip over the mode and name until the NUL before the ObjectId
 		// can be located. Skip the NUL as the function returns.
@@ -582,6 +649,17 @@ public String getPathString() {
 	}
 
 	/**
+	 * @return the current traversal depth from the root tree object
+	 * @since 5.4
+	 */
+	public int getTreeDepth() {
+		if (currVisit == null) {
+			return 0;
+		}
+		return currVisit.depth;
+	}
+
+	/**
 	 * Get the current object's path hash code.
 	 * <p>
 	 * This method computes a hash code on the fly for this path, the hash is
@@ -768,7 +846,7 @@ private void markTreeUninteresting(RevTree tree)
 		}
 	}
 
-	private TreeVisit newTreeVisit(RevObject obj) throws LargeObjectException,
+	private RevObject pushTree(RevObject obj) throws LargeObjectException,
 			MissingObjectException, IncorrectObjectTypeException, IOException {
 		TreeVisit tv = freeVisit;
 		if (tv != null) {
@@ -782,7 +860,15 @@ private TreeVisit newTreeVisit(RevObject obj) throws LargeObjectException,
 		}
 		tv.obj = obj;
 		tv.buf = reader.open(obj, OBJ_TREE).getCachedBytes();
-		return tv;
+		tv.parent = currVisit;
+		currVisit = tv;
+		if (tv.parent == null) {
+			tv.depth = 1;
+		} else {
+			tv.depth = tv.parent.depth + 1;
+		}
+
+		return obj;
 	}
 
 	private void releaseTreeVisit(TreeVisit tv) {
@@ -812,5 +898,8 @@ private static class TreeVisit {
 
 		/** Number of bytes in the path leading up to this tree. */
 		int pathLen;
+
+		/** Number of levels deep from the root tree. 0 for root tree. */
+		int depth;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
new file mode 100644
index 0000000..4012a45
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.revwalk;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Optional;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+
+/**
+ * Checks the reachability walking the graph from the starters towards the
+ * target.
+ *
+ * @since 5.4
+ */
+public class PedestrianReachabilityChecker implements ReachabilityChecker {
+
+	private final boolean topoSort;
+
+	private final RevWalk walk;
+
+	/**
+	 * New instance of the reachability checker using a existing walk.
+	 *
+	 * @param topoSort
+	 *            walk commits in topological order
+	 * @param walk
+	 *            RevWalk instance to reuse. Caller retains ownership.
+	 */
+	public PedestrianReachabilityChecker(boolean topoSort,
+			RevWalk walk) {
+		this.topoSort = topoSort;
+		this.walk = walk;
+	}
+
+	@Override
+	public Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
+			Collection<RevCommit> starters)
+					throws MissingObjectException, IncorrectObjectTypeException,
+					IOException {
+		walk.reset();
+		if (topoSort) {
+			walk.sort(RevSort.TOPO);
+		}
+
+		for (RevCommit target: targets) {
+			walk.markStart(target);
+		}
+
+		for (RevCommit starter : starters) {
+			walk.markUninteresting(starter);
+		}
+
+		return Optional.ofNullable(walk.next());
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
new file mode 100644
index 0000000..2ed06d1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.revwalk;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Optional;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+
+/**
+ * Check if a commit is reachable from a collection of starting commits.
+ * <p>
+ * Note that this checks the reachability of commits (and tags). Trees, blobs or
+ * any other object will cause IncorrectObjectTypeException exceptions.
+ *
+ * @since 5.4
+ */
+public interface ReachabilityChecker {
+
+	/**
+	 * Check if all targets are reachable from the {@code starter} commits.
+	 * <p>
+	 * Caller should parse the objectIds (preferably with
+	 * {@code walk.parseCommit()} and handle missing/incorrect type objects
+	 * before calling this method.
+	 *
+	 * @param targets
+	 *            commits to reach.
+	 * @param starters
+	 *            known starting points.
+	 * @return An unreachable target if at least one of the targets is
+	 *         unreachable. An empty optional if all targets are reachable from
+	 *         the starters.
+	 *
+	 * @throws MissingObjectException
+	 *             if any of the incoming objects doesn't exist in the
+	 *             repository.
+	 * @throws IncorrectObjectTypeException
+	 *             if any of the incoming objects is not a commit or a tag.
+	 * @throws IOException
+	 *             if any of the underlying indexes or readers can not be
+	 *             opened.
+	 */
+	Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
+			Collection<RevCommit> starters)
+			throws MissingObjectException, IncorrectObjectTypeException,
+			IOException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 0a43e8f..f12eb2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -506,7 +506,7 @@ public void sort(RevSort s, boolean use) {
 
 		if (sorting.size() > 1)
 			sorting.remove(RevSort.NONE);
-		else if (sorting.size() == 0)
+		else if (sorting.isEmpty())
 			sorting.add(RevSort.NONE);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
index f1252a4..fabf707 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
@@ -130,7 +130,7 @@ public static List<RevCommit> find(final RevWalk walk,
 
 	/**
 	 * Find the list of branches a given commit is reachable from when following
-	 * parent.s
+	 * parents.
 	 * <p>
 	 * Note that this method calls
 	 * {@link org.eclipse.jgit.revwalk.RevWalk#reset()} at the beginning.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java
index c67c44b..c3dc3de 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFlagFilter.java
@@ -44,6 +44,7 @@
 package org.eclipse.jgit.revwalk.filter;
 
 import java.io.IOException;
+import java.util.Arrays;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -78,8 +79,7 @@ public static RevFilter has(RevFlag a) {
 	 */
 	public static RevFilter hasAll(RevFlag... a) {
 		final RevFlagSet set = new RevFlagSet();
-		for (RevFlag flag : a)
-			set.add(flag);
+		set.addAll(Arrays.asList(a));
 		return new HasAll(set);
 	}
 
@@ -103,8 +103,7 @@ public static RevFilter hasAll(RevFlagSet a) {
 	 */
 	public static RevFilter hasAny(RevFlag... a) {
 		final RevFlagSet set = new RevFlagSet();
-		for (RevFlag flag : a)
-			set.add(flag);
+		set.addAll(Arrays.asList(a));
 		return new HasAny(set);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
index 68878e5..e6e3d4f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
@@ -266,6 +266,10 @@ public static class Accumulator {
 		/** Time in ms spent writing the pack. */
 		public long timeWriting;
 
+		/** Number of trees traversed in the walk when writing the pack.
+		 * @since 5.4*/
+		public long treesTraversed;
+
 		/**
 		 * Statistics about each object type in the pack (commits, tags, trees
 		 * and blobs.)
@@ -586,6 +590,14 @@ public long getTimeWriting() {
 	}
 
 	/**
+	 * @return number of trees traversed in the walk when writing the pack.
+	 * @since 5.4
+	 */
+	public long getTreesTraversed() {
+		return statistics.treesTraversed;
+	}
+
+	/**
 	 * Get total time spent processing this pack.
 	 *
 	 * @return total time spent processing this pack.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
index ce8995a..e5559de 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -144,7 +144,8 @@ public static SubmoduleWalk forIndex(Repository repository)
 	 *            path and .gitmodules at the root.
 	 * @param path
 	 *            a {@link java.lang.String} object.
-	 * @return generator at given path, null if no submodule at given path
+	 * @return generator at given path. The caller is responsible for calling
+	 *         {@link #close()}. Null if no submodule at given path.
 	 * @throws java.io.IOException
 	 */
 	public static SubmoduleWalk forPath(Repository repository,
@@ -177,7 +178,8 @@ public static SubmoduleWalk forPath(Repository repository,
 	 *            path and .gitmodules at the root.
 	 * @param path
 	 *            a {@link java.lang.String} object.
-	 * @return generator at given path, null if no submodule at given path
+	 * @return generator at given path. The caller is responsible for calling
+	 *         {@link #close()}. Null if no submodule at given path.
 	 * @throws java.io.IOException
 	 */
 	public static SubmoduleWalk forPath(Repository repository,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
index fcf78ac..20a5d9d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -53,6 +53,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.text.MessageFormat;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Set;
@@ -182,10 +183,7 @@ protected void readAdvertisedRefs() throws TransportException {
 		} catch (TransportException err) {
 			close();
 			throw err;
-		} catch (IOException err) {
-			close();
-			throw new TransportException(err.getMessage(), err);
-		} catch (RuntimeException err) {
+		} catch (IOException | RuntimeException err) {
 			close();
 			throw new TransportException(err.getMessage(), err);
 		}
@@ -217,8 +215,8 @@ private void readAdvertisedRefsImpl() throws IOException {
 				if (nul >= 0) {
 					// The first line (if any) may contain "hidden"
 					// capability values after a NUL byte.
-					for (String c : line.substring(nul + 1).split(" ")) //$NON-NLS-1$
-						remoteCapablities.add(c);
+					remoteCapablities.addAll(
+							Arrays.asList(line.substring(nul + 1).split(" "))); //$NON-NLS-1$
 					line = line.substring(0, nul);
 				}
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index ed7465c..4dd9cc5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -245,8 +245,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection
 
 	private PacketLineOut pckState;
 
-	/** If not -1, the maximum blob size to be sent to the server. */
-	private final long filterBlobLimit;
+	/**
+	 * Either FilterSpec.NO_FILTER for a filter that doesn't filter
+	 * anything, or a filter that indicates what and what not to send to the
+	 * server.
+	 */
+	private final FilterSpec filterSpec;
 
 	/**
 	 * Create a new connection to fetch using the native git transport.
@@ -268,10 +272,11 @@ public BasePackFetchConnection(PackTransport packTransport) {
 
 		includeTags = transport.getTagOpt() != TagOpt.NO_TAGS;
 		thinPack = transport.isFetchThin();
-		filterBlobLimit = transport.getFilterBlobLimit();
+		filterSpec = transport.getFilterSpec();
 
 		if (local != null) {
 			walk = new RevWalk(local);
+			walk.setRetainBody(false);
 			reachableCommits = new RevCommitList<>();
 			REACHABLE = walk.newFlag("REACHABLE"); //$NON-NLS-1$
 			COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
@@ -395,10 +400,7 @@ protected void doFetch(final ProgressMonitor monitor,
 		} catch (CancelledException ce) {
 			close();
 			return; // Caller should test (or just know) this themselves.
-		} catch (IOException err) {
-			close();
-			throw new TransportException(err.getMessage(), err);
-		} catch (RuntimeException err) {
+		} catch (IOException | RuntimeException err) {
 			close();
 			throw new TransportException(err.getMessage(), err);
 		}
@@ -520,10 +522,8 @@ private boolean sendWants(Collection<Ref> want) throws IOException {
 		if (first) {
 			return false;
 		}
-		if (filterBlobLimit == 0) {
-			p.writeString(OPTION_FILTER + " blob:none"); //$NON-NLS-1$
-		} else if (filterBlobLimit > 0) {
-			p.writeString(OPTION_FILTER + " blob:limit=" + filterBlobLimit); //$NON-NLS-1$
+		if (!filterSpec.isNoOp()) {
+			p.writeString(filterSpec.filterLine());
 		}
 		p.end();
 		outNeedsEnd = false;
@@ -565,7 +565,7 @@ else if (wantCapability(line, OPTION_SIDE_BAND))
 					OPTION_MULTI_ACK_DETAILED));
 		}
 
-		if (filterBlobLimit >= 0 && !wantCapability(line, OPTION_FILTER)) {
+		if (!filterSpec.isNoOp() && !wantCapability(line, OPTION_FILTER)) {
 			throw new PackProtocolException(uri,
 					JGitText.get().filterRequiresCapability);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index 6f17620..1741db9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -314,6 +314,7 @@ public Set<String> getCapabilities() {
 	protected BaseReceivePack(Repository into) {
 		db = into;
 		walk = new RevWalk(db);
+		walk.setRetainBody(false);
 
 		TransferConfig tc = db.getConfig().get(TransferConfig.KEY);
 		objectChecker = tc.newReceiveObjectChecker();
@@ -1240,7 +1241,7 @@ public void sendAdvertisedRefs(RefAdvertiser adv)
 			adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS);
 		}
 		adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
-		adv.send(getAdvertisedOrDefaultRefs());
+		adv.send(getAdvertisedOrDefaultRefs().values());
 		for (ObjectId obj : advertisedHaves)
 			adv.advertiseHave(obj);
 		if (adv.isEmpty())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
index 84a0972..6cf7503 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
@@ -112,10 +112,7 @@ class BundleFetchConnection extends BaseFetchConnection {
 		} catch (TransportException err) {
 			close();
 			throw err;
-		} catch (IOException err) {
-			close();
-			throw new TransportException(transport.uri, err.getMessage(), err);
-		} catch (RuntimeException err) {
+		} catch (IOException | RuntimeException err) {
 			close();
 			throw new TransportException(transport.uri, err.getMessage(), err);
 		}
@@ -205,10 +202,7 @@ protected void doFetch(final ProgressMonitor monitor,
 				packLock = parser.parse(NullProgressMonitor.INSTANCE);
 				ins.flush();
 			}
-		} catch (IOException err) {
-			close();
-			throw new TransportException(transport.uri, err.getMessage(), err);
-		} catch (RuntimeException err) {
+		} catch (IOException | RuntimeException err) {
 			close();
 			throw new TransportException(transport.uri, err.getMessage(), err);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index 56aaede..a09b1ff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -229,8 +229,9 @@ public void writeBundle(ProgressMonitor monitor, OutputStream os)
 			packWriter.setDeltaBaseAsOffset(true);
 			packWriter.setThin(exc.size() > 0);
 			packWriter.setReuseValidatingObjects(false);
-			if (exc.size() == 0)
+			if (exc.isEmpty()) {
 				packWriter.setTagTargets(tagTargets);
+			}
 			packWriter.preparePack(monitor, inc, exc);
 
 			final Writer w = new OutputStreamWriter(os, UTF_8);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
index 7289ce7..2b27df2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
@@ -115,36 +115,26 @@ public Daemon(InetSocketAddress addr) {
 
 		repositoryResolver = (RepositoryResolver<DaemonClient>) RepositoryResolver.NONE;
 
-		uploadPackFactory = new UploadPackFactory<DaemonClient>() {
-			@Override
-			public UploadPack create(DaemonClient req, Repository db)
-					throws ServiceNotEnabledException,
-					ServiceNotAuthorizedException {
-				UploadPack up = new UploadPack(db);
-				up.setTimeout(getTimeout());
-				up.setPackConfig(getPackConfig());
-				return up;
-			}
+		uploadPackFactory = (DaemonClient req, Repository db) -> {
+			UploadPack up = new UploadPack(db);
+			up.setTimeout(getTimeout());
+			up.setPackConfig(getPackConfig());
+			return up;
 		};
 
-		receivePackFactory = new ReceivePackFactory<DaemonClient>() {
-			@Override
-			public ReceivePack create(DaemonClient req, Repository db)
-					throws ServiceNotEnabledException,
-					ServiceNotAuthorizedException {
-				ReceivePack rp = new ReceivePack(db);
+		receivePackFactory = (DaemonClient req, Repository db) -> {
+			ReceivePack rp = new ReceivePack(db);
 
-				InetAddress peer = req.getRemoteAddress();
-				String host = peer.getCanonicalHostName();
-				if (host == null)
-					host = peer.getHostAddress();
-				String name = "anonymous"; //$NON-NLS-1$
-				String email = name + "@" + host; //$NON-NLS-1$
-				rp.setRefLogIdent(new PersonIdent(name, email));
-				rp.setTimeout(getTimeout());
+			InetAddress peer = req.getRemoteAddress();
+			String host = peer.getCanonicalHostName();
+			if (host == null)
+				host = peer.getHostAddress();
+			String name = "anonymous"; //$NON-NLS-1$
+			String email = name + "@" + host; //$NON-NLS-1$
+			rp.setRefLogIdent(new PersonIdent(name, email));
+			rp.setTimeout(getTimeout());
 
-				return rp;
-			}
+			return rp;
 		};
 
 		services = new DaemonService[] {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index 681ae12..e584440 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -200,6 +200,7 @@ else if (tagopt == TagOpt.FETCH_TAGS)
 				.setAllowNonFastForwards(true)
 				.setRefLogMessage("fetch", true); //$NON-NLS-1$
 		try (RevWalk walk = new RevWalk(transport.local)) {
+			walk.setRetainBody(false);
 			if (monitor instanceof BatchingProgressMonitor) {
 				((BatchingProgressMonitor) monitor).setDelayStart(
 						250, TimeUnit.MILLISECONDS);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
index 40ba3a3..4dd7d6e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
@@ -62,7 +62,7 @@ abstract class FetchRequest {
 
 	final Set<ObjectId> clientShallowCommits;
 
-	final long filterBlobLimit;
+	final FilterSpec filterSpec;
 
 	final Set<String> clientCapabilities;
 
@@ -82,8 +82,8 @@ abstract class FetchRequest {
 	 *            how deep to go in the tree
 	 * @param clientShallowCommits
 	 *            commits the client has without history
-	 * @param filterBlobLimit
-	 *            to exclude blobs on certain conditions
+	 * @param filterSpec
+	 *            the filter spec
 	 * @param clientCapabilities
 	 *            capabilities sent in the request
 	 * @param deepenNotRefs
@@ -96,13 +96,14 @@ abstract class FetchRequest {
 	 *            agent as reported by the client in the request body
 	 */
 	FetchRequest(@NonNull Set<ObjectId> wantIds, int depth,
-			@NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit,
+			@NonNull Set<ObjectId> clientShallowCommits,
+			@NonNull FilterSpec filterSpec,
 			@NonNull Set<String> clientCapabilities, int deepenSince,
 			@NonNull List<String> deepenNotRefs, @Nullable String agent) {
 		this.wantIds = requireNonNull(wantIds);
 		this.depth = depth;
 		this.clientShallowCommits = requireNonNull(clientShallowCommits);
-		this.filterBlobLimit = filterBlobLimit;
+		this.filterSpec = requireNonNull(filterSpec);
 		this.clientCapabilities = requireNonNull(clientCapabilities);
 		this.deepenSince = deepenSince;
 		this.deepenNotRefs = requireNonNull(deepenNotRefs);
@@ -137,10 +138,11 @@ Set<ObjectId> getClientShallowCommits() {
 	}
 
 	/**
-	 * @return the blob limit set in a "filter" line (-1 if not set)
+	 * @return the filter spec given in a "filter" line
 	 */
-	long getFilterBlobLimit() {
-		return filterBlobLimit;
+	@NonNull
+	FilterSpec getFilterSpec() {
+		return filterSpec;
 	}
 
 	/**
@@ -191,4 +193,4 @@ List<String> getDeepenNotRefs() {
 	String getAgent() {
 		return agent;
 	}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
index 05f4a81..231ab9f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
@@ -42,6 +42,8 @@
  */
 package org.eclipse.jgit.transport;
 
+import static java.util.Objects.requireNonNull;
+
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -57,10 +59,11 @@
 final class FetchV0Request extends FetchRequest {
 
 	FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth,
-			@NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit,
+			@NonNull Set<ObjectId> clientShallowCommits,
+			@NonNull FilterSpec filterSpec,
 			@NonNull Set<String> clientCapabilities, @Nullable String agent) {
-		super(wantIds, depth, clientShallowCommits, filterBlobLimit,
-				clientCapabilities, 0, Collections.emptyList(), agent);
+		super(wantIds, depth, clientShallowCommits, filterSpec,
+				clientCapabilities, 0, Collections.emptyList(),	agent);
 	}
 
 	static final class Builder {
@@ -71,7 +74,7 @@ static final class Builder {
 
 		final Set<ObjectId> clientShallowCommits = new HashSet<>();
 
-		long filterBlobLimit = -1;
+		FilterSpec filterSpec = FilterSpec.NO_FILTER;
 
 		final Set<String> clientCaps = new HashSet<>();
 
@@ -129,18 +132,18 @@ Builder setAgent(String clientAgent) {
 		}
 
 		/**
-		 * @param filterBlobLim
-		 *            blob limit set in a "filter" line
+		 * @param filter
+		 *            the filter set in a filter line
 		 * @return this builder
 		 */
-		Builder setFilterBlobLimit(long filterBlobLim) {
-			filterBlobLimit = filterBlobLim;
+		Builder setFilterSpec(@NonNull FilterSpec filter) {
+			filterSpec = requireNonNull(filter);
 			return this;
 		}
 
 		FetchV0Request build() {
 			return new FetchV0Request(wantIds, depth, clientShallowCommits,
-					filterBlobLimit, clientCaps, agent);
+					filterSpec, clientCaps, agent);
 		}
 
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
index ac6361c..6c24269 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
@@ -77,11 +77,12 @@ public final class FetchV2Request extends FetchRequest {
 			@NonNull Set<ObjectId> wantIds,
 			@NonNull Set<ObjectId> clientShallowCommits, int deepenSince,
 			@NonNull List<String> deepenNotRefs, int depth,
-			long filterBlobLimit,
+			@NonNull FilterSpec filterSpec,
 			boolean doneReceived, @NonNull Set<String> clientCapabilities,
 			@Nullable String agent, @NonNull List<String> serverOptions) {
-		super(wantIds, depth, clientShallowCommits, filterBlobLimit,
-				clientCapabilities, deepenSince, deepenNotRefs, agent);
+		super(wantIds, depth, clientShallowCommits, filterSpec,
+				clientCapabilities, deepenSince,
+				deepenNotRefs, agent);
 		this.peerHas = requireNonNull(peerHas);
 		this.wantedRefs = requireNonNull(wantedRefs);
 		this.doneReceived = doneReceived;
@@ -98,9 +99,11 @@ List<ObjectId> getPeerHas() {
 
 	/**
 	 * @return list of references received in "want-ref" lines
+	 *
+	 * @since 5.4
 	 */
 	@NonNull
-	List<String> getWantedRefs() {
+	public List<String> getWantedRefs() {
 		return wantedRefs;
 	}
 
@@ -147,7 +150,7 @@ static final class Builder {
 
 		int deepenSince;
 
-		long filterBlobLimit = -1;
+		FilterSpec filterSpec = FilterSpec.NO_FILTER;
 
 		boolean doneReceived;
 
@@ -266,12 +269,12 @@ int getDeepenSince() {
 		}
 
 		/**
-		 * @param filterBlobLim
-		 *            set in a "filter" line
+		 * @param filter
+		 *            spec set in a "filter" line
 		 * @return this builder
 		 */
-		Builder setFilterBlobLimit(long filterBlobLim) {
-			filterBlobLimit = filterBlobLim;
+		Builder setFilterSpec(@NonNull FilterSpec filter) {
+			filterSpec = requireNonNull(filter);
 			return this;
 		}
 
@@ -320,7 +323,7 @@ Builder addServerOption(@NonNull String value) {
 		FetchV2Request build() {
 			return new FetchV2Request(peerHas, wantedRefs, wantIds,
 					clientShallowCommits, deepenSince, deepenNotRefs,
-					depth, filterBlobLimit, doneReceived, clientCapabilities,
+					depth, filterSpec, doneReceived, clientCapabilities,
 					agent, Collections.unmodifiableList(serverOptions));
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java
new file mode 100644
index 0000000..a663c9b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2019, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Represents either a filter specified in a protocol "filter" line, or a
+ * placeholder to indicate no filtering.
+ *
+ * @since 5.4
+ */
+public final class FilterSpec {
+
+	private final long blobLimit;
+
+	private final long treeDepthLimit;
+
+	private FilterSpec(long blobLimit, long treeDepthLimit) {
+		this.blobLimit = blobLimit;
+		this.treeDepthLimit = treeDepthLimit;
+	}
+
+	/**
+	 * Process the content of "filter" line from the protocol. It has a shape
+	 * like:
+	 *
+	 * <ul>
+	 *   <li>"blob:none"
+	 *   <li>"blob:limit=N", with N &gt;= 0
+	 *   <li>"tree:DEPTH", with DEPTH &gt;= 0
+	 * </ul>
+	 *
+	 * @param filterLine
+	 *            the content of the "filter" line in the protocol
+	 * @return a FilterSpec representing the given filter
+	 * @throws PackProtocolException
+	 *             invalid filter because due to unrecognized format or
+	 *             negative/non-numeric filter.
+	 */
+	public static FilterSpec fromFilterLine(String filterLine)
+			throws PackProtocolException {
+		if (filterLine.equals("blob:none")) { //$NON-NLS-1$
+			return FilterSpec.withBlobLimit(0);
+		} else if (filterLine.startsWith("blob:limit=")) { //$NON-NLS-1$
+			long blobLimit = -1;
+			try {
+				blobLimit = Long
+						.parseLong(filterLine.substring("blob:limit=".length())); //$NON-NLS-1$
+			} catch (NumberFormatException e) {
+				// Do not change blobLimit so that we throw a
+				// PackProtocolException later.
+			}
+			if (blobLimit >= 0) {
+				return FilterSpec.withBlobLimit(blobLimit);
+			}
+		} else if (filterLine.startsWith("tree:")) { //$NON-NLS-1$
+			long treeDepthLimit = -1;
+			try {
+				treeDepthLimit = Long
+						.parseLong(filterLine.substring("tree:".length())); //$NON-NLS-1$
+			} catch (NumberFormatException e) {
+				// Do not change blobLimit so that we throw a
+				// PackProtocolException later.
+			}
+			if (treeDepthLimit >= 0) {
+				return FilterSpec.withTreeDepthLimit(treeDepthLimit);
+			}
+		}
+
+		// Did not match any known filter format.
+		throw new PackProtocolException(
+				MessageFormat.format(JGitText.get().invalidFilter, filterLine));
+	}
+
+	/**
+	 * @param blobLimit
+	 *            the blob limit in a "blob:[limit]" or "blob:none" filter line
+	 * @return a filter spec which filters blobs above a certain size
+	 */
+	static FilterSpec withBlobLimit(long blobLimit) {
+		if (blobLimit < 0) {
+			throw new IllegalArgumentException(
+					"blobLimit cannot be negative: " + blobLimit); //$NON-NLS-1$
+		}
+		return new FilterSpec(blobLimit, -1);
+	}
+
+	/**
+	 * @param treeDepthLimit
+	 *            the tree depth limit in a "tree:[depth]" filter line
+	 * @return a filter spec which filters blobs and trees beyond a certain tree
+	 *         depth
+	 */
+	static FilterSpec withTreeDepthLimit(long treeDepthLimit) {
+		if (treeDepthLimit < 0) {
+			throw new IllegalArgumentException(
+					"treeDepthLimit cannot be negative: " + treeDepthLimit); //$NON-NLS-1$
+		}
+		return new FilterSpec(-1, treeDepthLimit);
+	}
+
+	/**
+	 * A placeholder that indicates no filtering.
+	 */
+	public static final FilterSpec NO_FILTER = new FilterSpec(-1, -1);
+
+	/**
+	 * @return -1 if this filter does not filter blobs based on size, or a
+	 *         non-negative integer representing the max size of blobs to allow
+	 */
+	public long getBlobLimit() {
+		return blobLimit;
+	}
+
+	/**
+	 * @return -1 if this filter does not filter blobs and trees based on depth,
+	 *         or a non-negative integer representing the max tree depth of
+	 *         blobs and trees to fetch
+	 */
+	public long getTreeDepthLimit() {
+		return treeDepthLimit;
+	}
+
+	/**
+	 * @return true if this filter doesn't filter out anything
+	 */
+	public boolean isNoOp() {
+		return blobLimit == -1 && treeDepthLimit == -1;
+	}
+
+	/**
+	 * @return the filter line which describes this spec, e.g. "filter blob:limit=42"
+	 */
+	@Nullable
+	public String filterLine() {
+		if (blobLimit == 0) {
+			return GitProtocolConstants.OPTION_FILTER + " blob:none"; //$NON-NLS-1$
+		}
+
+		if (blobLimit > 0) {
+			return GitProtocolConstants.OPTION_FILTER + " blob:limit=" + blobLimit; //$NON-NLS-1$
+		}
+
+		return null;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
index 6c26b70..53eaa6a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
@@ -78,9 +78,7 @@ public HMACSHA1NonceGenerator(String seed) throws IllegalStateException {
 			SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1"); //$NON-NLS-1$
 			mac = Mac.getInstance("HmacSHA1"); //$NON-NLS-1$
 			mac.init(signingKey);
-		} catch (InvalidKeyException e) {
-			throw new IllegalStateException(e);
-		} catch (NoSuchAlgorithmException e) {
+		} catch (InvalidKeyException | NoSuchAlgorithmException e) {
 			throw new IllegalStateException(e);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
index 9fb9062..dc3dcbc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
@@ -107,16 +107,12 @@ public void run() {
 				try {
 					final UploadPack rp = uploadPackFactory.create(req, remote);
 					rp.upload(out_r, in_w, null);
-				} catch (ServiceNotEnabledException e) {
+				} catch (ServiceNotEnabledException
+						| ServiceNotAuthorizedException e) {
 					// Ignored. Client cannot use this repository.
-				} catch (ServiceNotAuthorizedException e) {
-					// Ignored. Client cannot use this repository.
-				} catch (IOException err) {
+				} catch (IOException | RuntimeException err) {
 					// Client side of the pipes should report the problem.
 					err.printStackTrace();
-				} catch (RuntimeException err) {
-					// Client side will notice we went away, and report.
-					err.printStackTrace();
 				} finally {
 					try {
 						out_r.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
index f05e0b8..9663de0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
@@ -100,16 +100,14 @@ public void run() {
 				try {
 					final ReceivePack rp = receivePackFactory.create(req, remote);
 					rp.receive(out_r, in_w, System.err);
-				} catch (ServiceNotEnabledException e) {
-					// Ignored. Client cannot use this repository.
-				} catch (ServiceNotAuthorizedException e) {
+				} catch (ServiceNotEnabledException
+						| ServiceNotAuthorizedException e) {
 					// Ignored. Client cannot use this repository.
 				} catch (IOException e) {
-					// Since the InternalPushConnection
-					// is used in tests, we want to avoid hiding exceptions
-					// because they can point to programming errors on the server
-					// side. By rethrowing, the default handler will dump it
-					// to stderr.
+					// Since the InternalPushConnection is used in tests, we
+					// want to avoid hiding exceptions because they can point to
+					// programming errors on the server side. By rethrowing, the
+					// default handler will dump it to stderr.
 					throw new UncheckedIOException(e);
 				} finally {
 					try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
index ba5d2f3..28a146d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
@@ -63,12 +63,9 @@
  */
 public interface PostReceiveHook {
 	/** A simple no-op hook. */
-	PostReceiveHook NULL = new PostReceiveHook() {
-		@Override
-		public void onPostReceive(final ReceivePack rp,
-				final Collection<ReceiveCommand> commands) {
-			// Do nothing.
-		}
+	PostReceiveHook NULL = (final ReceivePack rp,
+			final Collection<ReceiveCommand> commands) -> {
+		// Do nothing.
 	};
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
index 3aa3b12..251bfe2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
@@ -57,11 +57,8 @@
  */
 public interface PostUploadHook {
 	/** A simple no-op hook. */
-	PostUploadHook NULL = new PostUploadHook() {
-		@Override
-		public void onPostUpload(PackStatistics stats) {
-			// Do nothing.
-		}
+	PostUploadHook NULL = (PackStatistics stats) -> {
+		// Do nothing.
 	};
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
index 30845d3..b91756b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
@@ -79,12 +79,9 @@
  */
 public interface PreReceiveHook {
 	/** A simple no-op hook. */
-	PreReceiveHook NULL = new PreReceiveHook() {
-		@Override
-		public void onPreReceive(final ReceivePack rp,
-				final Collection<ReceiveCommand> commands) {
-			// Do nothing.
-		}
+	PreReceiveHook NULL = (final ReceivePack rp,
+			final Collection<ReceiveCommand> commands) -> {
+		// Do nothing.
 	};
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
index 21498d6..396327a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
@@ -130,7 +130,7 @@ FetchV0Request recvWants(PacketLineIn pckIn)
 				}
 				filterReceived = true;
 
-				reqBuilder.setFilterBlobLimit(ProtocolV2Parser.filterLine(arg));
+				reqBuilder.setFilterSpec(FilterSpec.fromFilterLine(arg));
 				continue;
 			}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
index 8f4b86e..cb04ff6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -207,7 +207,7 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn)
 							JGitText.get().tooManyFilters);
 				}
 				filterReceived = true;
-				reqBuilder.setFilterBlobLimit(filterLine(
+				reqBuilder.setFilterSpec(FilterSpec.fromFilterLine(
 						line.substring(OPTION_FILTER.length() + 1)));
 			} else {
 				throw new PackProtocolException(MessageFormat
@@ -269,42 +269,4 @@ LsRefsV2Request parseLsRefsRequest(PacketLineIn pckIn)
 		return builder.setRefPrefixes(prefixes).build();
 	}
 
-	/*
-	 * Process the content of "filter" line from the protocol. It has a shape
-	 * like "blob:none" or "blob:limit=N", with limit a positive number.
-	 *
-	 * @param blobLine
-	 *            the content of the "filter" line in the protocol
-	 * @return N, the limit, defaulting to 0 if "none"
-	 * @throws PackProtocolException
-	 *             invalid filter because due to unrecognized format or
-	 *             negative/non-numeric filter.
-	 */
-	static long filterLine(String blobLine) throws PackProtocolException {
-		long blobLimit = -1;
-
-		if (blobLine.equals("blob:none")) { //$NON-NLS-1$
-			blobLimit = 0;
-		} else if (blobLine.startsWith("blob:limit=")) { //$NON-NLS-1$
-			try {
-				blobLimit = Long
-						.parseLong(blobLine.substring("blob:limit=".length())); //$NON-NLS-1$
-			} catch (NumberFormatException e) {
-				throw new PackProtocolException(MessageFormat
-						.format(JGitText.get().invalidFilter, blobLine));
-			}
-		}
-		/*
-		 * We must have (1) either "blob:none" or "blob:limit=" set (because we
-		 * only support blob size limits for now), and (2) if the latter, then
-		 * it must be nonnegative. Throw if (1) or (2) is not met.
-		 */
-		if (blobLimit < 0) {
-			throw new PackProtocolException(
-					MessageFormat.format(JGitText.get().invalidFilter, blobLine));
-		}
-
-		return blobLimit;
-	}
-
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
index aeca635..14afc44 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -57,7 +57,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -193,81 +192,78 @@ public PushCertificate get(String refName) throws IOException {
 	 *         close resources.
 	 */
 	public Iterable<PushCertificate> getAll(String refName) {
-		return new Iterable<PushCertificate>() {
-			@Override
-			public Iterator<PushCertificate> iterator() {
-				return new Iterator<PushCertificate>() {
-					private final String path = pathName(refName);
-					private PushCertificate next;
+		return () -> new Iterator<PushCertificate>() {
+			private final String path = pathName(refName);
 
-					private RevWalk rw;
-					{
+			private PushCertificate next;
+
+			private RevWalk rw;
+			{
+				try {
+					if (reader == null) {
+						load();
+					}
+					if (commit != null) {
+						rw = new RevWalk(reader);
+						rw.setTreeFilter(AndTreeFilter.create(
+								PathFilterGroup.create(Collections
+										.singleton(PathFilter.create(path))),
+								TreeFilter.ANY_DIFF));
+						rw.setRewriteParents(false);
+						rw.markStart(rw.parseCommit(commit));
+					} else {
+						rw = null;
+					}
+				} catch (IOException e) {
+					throw new RuntimeException(e);
+				}
+			}
+
+			@Override
+			public boolean hasNext() {
+				try {
+					if (next == null) {
+						if (rw == null) {
+							return false;
+						}
 						try {
-							if (reader == null) {
-								load();
-							}
-							if (commit != null) {
-								rw = new RevWalk(reader);
-								rw.setTreeFilter(AndTreeFilter.create(
-										PathFilterGroup.create(
-											Collections.singleton(PathFilter.create(path))),
-										TreeFilter.ANY_DIFF));
-								rw.setRewriteParents(false);
-								rw.markStart(rw.parseCommit(commit));
+							RevCommit c = rw.next();
+							if (c != null) {
+								try (TreeWalk tw = TreeWalk.forPath(
+										rw.getObjectReader(), path,
+										c.getTree())) {
+									next = read(tw);
+								}
 							} else {
-								rw = null;
+								next = null;
 							}
 						} catch (IOException e) {
 							throw new RuntimeException(e);
 						}
 					}
-
-					@Override
-					public boolean hasNext() {
-						try {
-							if (next == null) {
-								if (rw == null) {
-									return false;
-								}
-								try {
-									RevCommit c = rw.next();
-									if (c != null) {
-										try (TreeWalk tw = TreeWalk.forPath(
-												rw.getObjectReader(), path, c.getTree())) {
-											next = read(tw);
-										}
-									} else {
-										next = null;
-									}
-								} catch (IOException e) {
-									throw new RuntimeException(e);
-								}
-							}
-							return next != null;
-						} finally {
-							if (next == null && rw != null) {
-								rw.close();
-								rw = null;
-							}
-						}
+					return next != null;
+				} finally {
+					if (next == null && rw != null) {
+						rw.close();
+						rw = null;
 					}
+				}
+			}
 
-					@Override
-					public PushCertificate next() {
-						hasNext();
-						PushCertificate n = next;
-						if (n == null) {
-							throw new NoSuchElementException();
-						}
-						next = null;
-						return n;
-					}
+			@Override
+			public PushCertificate next() {
+				hasNext();
+				PushCertificate n = next;
+				if (n == null) {
+					throw new NoSuchElementException();
+				}
+				next = null;
+				return n;
+			}
 
-					@Override
-					public void remove() {
-						throw new UnsupportedOperationException();
-					}
-				};
+			@Override
+			public void remove() {
+				throw new UnsupportedOperationException();
 			}
 		};
 	}
@@ -442,13 +438,8 @@ private ObjectId write() throws IOException {
 	}
 
 	private static void sortPending(List<PendingCert> pending) {
-		Collections.sort(pending, new Comparator<PendingCert>() {
-			@Override
-			public int compare(PendingCert a, PendingCert b) {
-				return Long.signum(
-						a.ident.getWhen().getTime() - b.ident.getWhen().getTime());
-			}
-		});
+		Collections.sort(pending, (PendingCert a, PendingCert b) -> Long.signum(
+				a.ident.getWhen().getTime() - b.ident.getWhen().getTime()));
 	}
 
 	private DirCache newDirCache() throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
index d6d6198..d19c652 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
@@ -61,12 +61,7 @@ public interface RefFilter {
 	/**
 	 * The default filter, allows all refs to be shown.
 	 */
-	RefFilter DEFAULT = new RefFilter() {
-		@Override
-		public Map<String, Ref> filter (Map<String, Ref> refs) {
-			return refs;
-		}
-	};
+	RefFilter DEFAULT = (Map<String, Ref> refs) -> refs;
 
 	/**
 	 * Filters a {@code Map} of refs before it is advertised to the client.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index 005a0c2..fdb19df 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -100,13 +100,9 @@ public static void setInstance(SshSessionFactory newFactory) {
 	 * @since 5.2
 	 */
 	public static String getLocalUserName() {
-		return AccessController.doPrivileged(new PrivilegedAction<String>() {
-			@Override
-			public String run() {
-				return SystemReader.getInstance()
-						.getProperty(Constants.OS_USER_NAME_KEY);
-			}
-		});
+		return AccessController
+				.doPrivileged((PrivilegedAction<String>) () -> SystemReader
+						.getInstance().getProperty(Constants.OS_USER_NAME_KEY));
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index 621c2ea..0b79070 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -47,6 +47,7 @@
 package org.eclipse.jgit.transport;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -70,6 +71,7 @@
 import java.util.Vector;
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.api.errors.AbortedByHookException;
 import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.TransportException;
@@ -178,10 +180,7 @@ private static void load(ClassLoader ldr, String cn) {
 				TransportProtocol proto;
 				try {
 					proto = (TransportProtocol) f.get(null);
-				} catch (IllegalArgumentException e) {
-					// If we cannot access the field, don't.
-					continue;
-				} catch (IllegalAccessException e) {
+				} catch (IllegalArgumentException | IllegalAccessException e) {
 					// If we cannot access the field, don't.
 					continue;
 				}
@@ -790,7 +789,7 @@ private static String findTrackingRefName(final String remoteName,
 	/** Should refs no longer on the source be pruned from the destination? */
 	private boolean removeDeletedRefs;
 
-	private long filterBlobLimit = -1;
+	private FilterSpec filterSpec = FilterSpec.NO_FILTER;
 
 	/** Timeout in seconds to wait before aborting an IO read or write. */
 	private int timeout;
@@ -1067,20 +1066,42 @@ public void setRemoveDeletedRefs(boolean remove) {
 	}
 
 	/**
-	 * @return the last value passed to {@link #setFilterBlobLimit}, or -1 if
-	 *         it was never invoked.
+	 * @return the blob limit value set with {@link #setFilterBlobLimit} or
+	 *         {@link #setFilterSpec(FilterSpec)}, or -1 if no blob limit value
+	 *         was set
 	 * @since 5.0
+	 * @deprecated Use {@link #getFilterSpec()} instead
 	 */
-	public long getFilterBlobLimit() {
-		return filterBlobLimit;
+	@Deprecated
+	public final long getFilterBlobLimit() {
+		return filterSpec.getBlobLimit();
 	}
 
 	/**
 	 * @param bytes exclude blobs of size greater than this
 	 * @since 5.0
+	 * @deprecated Use {@link #setFilterSpec(FilterSpec)} instead
 	 */
-	public void setFilterBlobLimit(long bytes) {
-		filterBlobLimit = bytes;
+	@Deprecated
+	public final void setFilterBlobLimit(long bytes) {
+		setFilterSpec(FilterSpec.withBlobLimit(bytes));
+	}
+
+	/**
+	 * @return the last filter spec set with {@link #setFilterSpec(FilterSpec)},
+	 *         or {@link FilterSpec#NO_FILTER} if it was never invoked.
+	 * @since 5.4
+	 */
+	public final FilterSpec getFilterSpec() {
+		return filterSpec;
+	}
+
+	/**
+	 * @param filter a new filter to use for this transport
+	 * @since 5.4
+	 */
+	public final void setFilterSpec(@NonNull FilterSpec filter) {
+		filterSpec = requireNonNull(filter);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 8b41ab0..b752a65 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -363,9 +363,7 @@ public FetchConnection openFetch() throws TransportException,
 			try (InputStream in = openInputStream(c)) {
 				return getConnection(c, in, service);
 			}
-		} catch (NotSupportedException err) {
-			throw err;
-		} catch (TransportException err) {
+		} catch (NotSupportedException | TransportException err) {
 			throw err;
 		} catch (IOException err) {
 			throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err);
@@ -449,9 +447,7 @@ public PushConnection openPush() throws NotSupportedException,
 					throw new NotSupportedException(msg);
 				}
 			}
-		} catch (NotSupportedException err) {
-			throw err;
-		} catch (TransportException err) {
+		} catch (NotSupportedException | TransportException err) {
 			throw err;
 		} catch (IOException err) {
 			throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err);
@@ -575,9 +571,7 @@ private HttpConnection connect(String service)
 					String err = status + " " + conn.getResponseMessage(); //$NON-NLS-1$
 					throw new TransportException(uri, err);
 				}
-			} catch (NotSupportedException e) {
-				throw e;
-			} catch (TransportException e) {
+			} catch (NotSupportedException | TransportException e) {
 				throw e;
 			} catch (SSLHandshakeException e) {
 				handleSslFailure(e);
@@ -779,7 +773,7 @@ private boolean isValidRedirect(URL current, String next, String checkFor) {
 		}
 		// git allows only rewriting the root, i.e., everything before INFO_REFS
 		// or the service name
-		if (next.indexOf(checkFor) < 0) {
+		if (!next.contains(checkFor)) {
 			return false;
 		}
 		// Basically we should test here that whatever follows INFO_REFS is
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index fbb2c44..f2a2b0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -188,12 +188,8 @@ public FetchConnection openFetch() throws TransportException {
 				&& !"git upload-pack".equals(up)) //$NON-NLS-1$
 			return new ForkLocalFetchConnection();
 
-		UploadPackFactory<Void> upf = new UploadPackFactory<Void>() {
-			@Override
-			public UploadPack create(Void req, Repository db) {
-				return createUploadPack(db);
-			}
-		};
+		UploadPackFactory<Void> upf = (Void req,
+				Repository db) -> createUploadPack(db);
 		return new InternalFetchConnection<>(this, upf, null, openRepo());
 	}
 
@@ -205,12 +201,8 @@ public PushConnection openPush() throws TransportException {
 				&& !"git receive-pack".equals(rp)) //$NON-NLS-1$
 			return new ForkLocalPushConnection();
 
-		ReceivePackFactory<Void> rpf = new ReceivePackFactory<Void>() {
-			@Override
-			public ReceivePack create(Void req, Repository db) {
-				return createReceivePack(db);
-			}
-		};
+		ReceivePackFactory<Void> rpf = (Void req,
+				Repository db) -> createReceivePack(db);
 		return new InternalPushConnection<>(this, rpf, null, openRepo());
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 1d0f836..3ed9886 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -44,13 +44,11 @@
 package org.eclipse.jgit.transport;
 
 import static java.util.Collections.unmodifiableMap;
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toMap;
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION;
 import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
 import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
@@ -66,6 +64,7 @@
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
+import static org.eclipse.jgit.util.RefMap.toRefMap;
 
 import java.io.ByteArrayOutputStream;
 import java.io.EOFException;
@@ -81,8 +80,10 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.stream.Collectors;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
@@ -105,8 +106,11 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
 import org.eclipse.jgit.revwalk.BitmapWalker;
+import org.eclipse.jgit.revwalk.BitmappedReachabilityChecker;
 import org.eclipse.jgit.revwalk.DepthWalk;
 import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.PedestrianReachabilityChecker;
+import org.eclipse.jgit.revwalk.ReachabilityChecker;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevFlagSet;
@@ -817,7 +821,7 @@ private Map<String, Ref> getAdvertisedOrDefaultRefs() throws IOException {
 			// Fall back to all refs.
 			setAdvertisedRefs(
 					db.getRefDatabase().getRefs().stream()
-						.collect(toMap(Ref::getName, identity())));
+							.collect(toRefMap((a, b) -> b)));
 		}
 		return refs;
 	}
@@ -836,7 +840,7 @@ private Map<String, Ref> getFilteredRefs(Collection<String> refPrefixes)
 			String[] prefixes = refPrefixes.toArray(new String[0]);
 			Map<String, Ref> rs =
 					db.getRefDatabase().getRefsByPrefix(prefixes).stream()
-						.collect(toMap(Ref::getName, identity(), (a, b) -> b));
+							.collect(toRefMap((a, b) -> b));
 			if (refFilter != RefFilter.DEFAULT) {
 				return refFilter.filter(rs);
 			}
@@ -848,7 +852,7 @@ private Map<String, Ref> getFilteredRefs(Collection<String> refPrefixes)
 		return refs.values().stream()
 				.filter(ref -> refPrefixes.stream()
 						.anyMatch(ref.getName()::startsWith))
-				.collect(toMap(Ref::getName, identity()));
+				.collect(toRefMap((a, b) -> b));
 	}
 
 	/**
@@ -871,7 +875,7 @@ private static Map<String, Ref> mapRefs(
 				names.stream()
 					.map(refs::get)
 					.filter(Objects::nonNull)
-					.collect(toMap(Ref::getName, identity(), (a, b) -> b)));
+						.collect(toRefMap((a, b) -> b)));
 	}
 
 	/**
@@ -1066,7 +1070,7 @@ private void lsRefsV2() throws IOException {
 			findSymrefs(adv, refsToSend);
 		}
 
-		adv.send(refsToSend);
+		adv.send(refsToSend.values());
 		adv.end();
 	}
 
@@ -1477,7 +1481,8 @@ public void sendAdvertisedRefs(RefAdvertiser adv,
 		}
 		adv.setDerefTags(true);
 		findSymrefs(adv, advertisedOrDefaultRefs);
-		advertised = adv.send(advertisedOrDefaultRefs);
+		advertised = adv.send(advertisedOrDefaultRefs.values());
+
 		if (adv.isEmpty())
 			adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
 		adv.end();
@@ -1526,17 +1531,30 @@ public int getDepth() {
 	}
 
 	/**
-	 * Returns the filter blob limit for the current request. Valid only after
-	 * calling recvWants(). A limit -1 means no limit.
+	 * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}.
 	 *
 	 * @return filter blob limit requested by the client, or -1 if no limit
 	 * @since 5.3
+	 * @deprecated Use {@link #getFilterSpec()} instead
 	 */
-	public long getFilterBlobLimit() {
+	@Deprecated
+	public final long getFilterBlobLimit() {
+		return getFilterSpec().getBlobLimit();
+	}
+
+	/**
+	 * Returns the filter spec for the current request. Valid only after
+	 * calling recvWants(). This may be a no-op filter spec, but it won't be
+	 * null.
+	 *
+	 * @return filter requested by the client
+	 * @since 5.4
+	 */
+	public final FilterSpec getFilterSpec() {
 		if (currentRequest == null) {
 			throw new RequestNotYetReadException();
 		}
-		return currentRequest.getFilterBlobLimit();
+		return currentRequest.getFilterSpec();
 	}
 
 	/**
@@ -1854,58 +1872,89 @@ private static void checkNotAdvertisedWantsUsingBitmap(ObjectReader reader,
 
 	private static void checkNotAdvertisedWants(UploadPack up,
 			List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
-			throws MissingObjectException, IncorrectObjectTypeException, IOException {
-		// Walk the requested commits back to the provided set of commits. If any
-		// commit exists, a branch was deleted or rewound and the repository owner
-		// no longer exports that requested item. If the requested commit is merged
-		// into an advertised branch it will be marked UNINTERESTING and no commits
-		// return.
+			throws IOException {
 
 		ObjectReader reader = up.getRevWalk().getObjectReader();
 		try (RevWalk walk = new RevWalk(reader)) {
-			AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true);
-			try {
-				RevObject obj;
-				while ((obj = q.next()) != null) {
-					if (!(obj instanceof RevCommit)) {
-						// If unadvertized non-commits are requested, use
-						// bitmaps. If there are no bitmaps, instead of
-						// incurring the expense of a manual walk, reject
-						// the request.
-						BitmapIndex bitmapIndex = reader.getBitmapIndex();
-						if (bitmapIndex != null) {
-							checkNotAdvertisedWantsUsingBitmap(
-									reader,
-									bitmapIndex,
-									notAdvertisedWants,
-									reachableFrom);
-							return;
-						}
-						throw new WantNotValidException(obj);
-					}
-					walk.markStart((RevCommit) obj);
+			walk.setRetainBody(false);
+			// Missing "wants" throw exception here
+			List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk,
+					notAdvertisedWants);
+			List<RevCommit> wantsAsCommits = wantsAsObjs.stream()
+					.filter(obj -> obj instanceof RevCommit)
+					.map(obj -> (RevCommit) obj)
+					.collect(Collectors.toList());
+			boolean allWantsAreCommits = wantsAsObjs.size() == wantsAsCommits
+					.size();
+			boolean repoHasBitmaps = reader.getBitmapIndex() != null;
+
+			if (!allWantsAreCommits) {
+				if (!repoHasBitmaps) {
+					// If unadvertized non-commits are requested, use
+					// bitmaps. If there are no bitmaps, instead of
+					// incurring the expense of a manual walk, reject
+					// the request.
+					RevObject nonCommit = wantsAsObjs
+							.stream()
+							.filter(obj -> !(obj instanceof RevCommit))
+							.limit(1)
+							.collect(Collectors.toList()).get(0);
+					throw new WantNotValidException(nonCommit);
+
 				}
-			} catch (MissingObjectException notFound) {
-				throw new WantNotValidException(notFound.getObjectId(),
-						notFound);
-			} finally {
-				q.release();
-			}
-			for (ObjectId id : reachableFrom) {
-				try {
-					walk.markUninteresting(walk.parseCommit(id));
-				} catch (IncorrectObjectTypeException notCommit) {
-					continue;
-				}
+				checkNotAdvertisedWantsUsingBitmap(reader,
+						reader.getBitmapIndex(), notAdvertisedWants,
+						reachableFrom);
+				return;
 			}
 
-			RevCommit bad = walk.next();
-			if (bad != null) {
-				throw new WantNotValidException(bad);
+			// All wants are commits, we can use ReachabilityChecker
+			ReachabilityChecker reachabilityChecker = repoHasBitmaps
+					? new BitmappedReachabilityChecker(walk)
+					: new PedestrianReachabilityChecker(true, walk);
+
+			List<RevCommit> starters = objectIdsToRevCommits(walk,
+					reachableFrom);
+			Optional<RevCommit> unreachable = reachabilityChecker
+					.areAllReachable(wantsAsCommits, starters);
+			if (unreachable.isPresent()) {
+				throw new WantNotValidException(unreachable.get());
 			}
+
+		} catch (MissingObjectException notFound) {
+			throw new WantNotValidException(notFound.getObjectId(), notFound);
 		}
 	}
 
+	// Resolve the ObjectIds into RevObjects. Any missing object raises an
+	// exception
+	private static List<RevObject> objectIdsToRevObjects(RevWalk walk,
+			Iterable<ObjectId> objectIds)
+			throws MissingObjectException, IOException {
+		List<RevObject> result = new ArrayList<>();
+		for (ObjectId objectId : objectIds) {
+			result.add(walk.parseAny(objectId));
+		}
+		return result;
+	}
+
+	// Get commits from object ids. If the id is not a commit, ignore it. If the
+	// id doesn't exist, report the missing object in a exception.
+	private static List<RevCommit> objectIdsToRevCommits(RevWalk walk,
+			Iterable<ObjectId> objectIds)
+			throws MissingObjectException, IOException {
+		List<RevCommit> result = new ArrayList<>();
+		for (ObjectId objectId : objectIds) {
+			try {
+				result.add(walk.parseCommit(objectId));
+			} catch (IncorrectObjectTypeException e) {
+				continue;
+			}
+		}
+		return result;
+	}
+
+
 	private void addCommonBase(RevObject o) {
 		if (!o.has(COMMON)) {
 			o.add(COMMON);
@@ -1988,21 +2037,12 @@ private void sendPack(PackStatistics.Accumulator accumulator,
 			} catch (ServiceMayNotContinueException noPack) {
 				// This was already reported on (below).
 				throw noPack;
-			} catch (IOException err) {
-				if (reportInternalServerErrorOverSideband())
+			} catch (IOException | RuntimeException | Error err) {
+				if (reportInternalServerErrorOverSideband()) {
 					throw new UploadPackInternalServerErrorException(err);
-				else
+				} else {
 					throw err;
-			} catch (RuntimeException err) {
-				if (reportInternalServerErrorOverSideband())
-					throw new UploadPackInternalServerErrorException(err);
-				else
-					throw err;
-			} catch (Error err) {
-				if (reportInternalServerErrorOverSideband())
-					throw new UploadPackInternalServerErrorException(err);
-				else
-					throw err;
+				}
 			}
 		} else {
 			sendPack(false, req, accumulator, allTags, unshallowCommits, deepenNots);
@@ -2097,15 +2137,16 @@ private void sendPack(final boolean sideband,
 				accumulator);
 		try {
 			pw.setIndexDisabled(true);
-			if (req.getFilterBlobLimit() >= 0) {
-				pw.setFilterBlobLimit(req.getFilterBlobLimit());
-				pw.setUseCachedPacks(false);
-			} else {
+			if (req.getFilterSpec().isNoOp()) {
 				pw.setUseCachedPacks(true);
+			} else {
+				pw.setFilterSpec(req.getFilterSpec());
+				pw.setUseCachedPacks(false);
 			}
 			pw.setUseBitmaps(
 					req.getDepth() == 0
-							&& req.getClientShallowCommits().isEmpty());
+							&& req.getClientShallowCommits().isEmpty()
+							&& req.getFilterSpec().getTreeDepthLimit() == -1);
 			pw.setClientShallowCommits(req.getClientShallowCommits());
 			pw.setReuseDeltaCommits(true);
 			pw.setDeltaBaseAsOffset(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java
index 60acd2f..b4a7af0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java
@@ -127,11 +127,7 @@ public Repository open(C req, String name)
 				} else
 					throw new ServiceNotEnabledException();
 
-			} catch (RuntimeException e) {
-				db.close();
-				throw new RepositoryNotFoundException(name, e);
-
-			} catch (IOException e) {
+			} catch (RuntimeException | IOException e) {
 				db.close();
 				throw new RepositoryNotFoundException(name, e);
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
index b850d1e..5d36e58 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
@@ -57,12 +57,8 @@ public interface ReceivePackFactory<C> {
 	/**
 	 * A factory disabling the ReceivePack service for all repositories
 	 */
-	ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() {
-		@Override
-		public ReceivePack create(Object req, Repository db)
-				throws ServiceNotEnabledException {
-			throw new ServiceNotEnabledException();
-		}
+	ReceivePackFactory<?> DISABLED = (Object req, Repository db) -> {
+		throw new ServiceNotEnabledException();
 	};
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
index 4816f21..dd24b7a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
@@ -57,12 +57,8 @@ public interface RepositoryResolver<C> {
 	/**
 	 * Resolver configured to open nothing.
 	 */
-	RepositoryResolver<?> NONE = new RepositoryResolver<Object>() {
-		@Override
-		public Repository open(Object req, String name)
-				throws RepositoryNotFoundException {
-			throw new RepositoryNotFoundException(name);
-		}
+	RepositoryResolver<?> NONE = (Object req, String name) -> {
+		throw new RepositoryNotFoundException(name);
 	};
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
index bb43b13..b8673f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
@@ -57,12 +57,8 @@ public interface UploadPackFactory<C> {
 	/**
 	 * A factory disabling the UploadPack service for all repositories.
 	 */
-	UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() {
-		@Override
-		public UploadPack create(Object req, Repository db)
-				throws ServiceNotEnabledException {
-			throw new ServiceNotEnabledException();
-		}
+	UploadPackFactory<?> DISABLED = (Object req, Repository db) -> {
+		throw new ServiceNotEnabledException();
 	};
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index 61b130f..2f8af78 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -323,8 +323,7 @@ private AbstractTreeIterator combineDF(AbstractTreeIterator minRef)
 	@Override
 	void popEntriesEqual() throws CorruptObjectException {
 		final AbstractTreeIterator ch = currentHead;
-		for (int i = 0; i < trees.length; i++) {
-			final AbstractTreeIterator t = trees[i];
+		for (AbstractTreeIterator t : trees) {
 			if (t.matches == ch) {
 				if (t.matchShift == 0)
 					t.next(1);
@@ -343,8 +342,7 @@ void popEntriesEqual() throws CorruptObjectException {
 	@Override
 	void skipEntriesEqual() throws CorruptObjectException {
 		final AbstractTreeIterator ch = currentHead;
-		for (int i = 0; i < trees.length; i++) {
-			final AbstractTreeIterator t = trees[i];
+		for (AbstractTreeIterator t : trees) {
 			if (t.matches == ch) {
 				if (t.matchShift == 0)
 					t.skip();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index 69303d6..65d8512 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -1338,8 +1338,7 @@ AbstractTreeIterator min() throws CorruptObjectException {
 
 	void popEntriesEqual() throws CorruptObjectException {
 		final AbstractTreeIterator ch = currentHead;
-		for (int i = 0; i < trees.length; i++) {
-			final AbstractTreeIterator t = trees[i];
+		for (AbstractTreeIterator t : trees) {
 			if (t.matches == ch) {
 				t.next(1);
 				t.matches = null;
@@ -1349,8 +1348,7 @@ void popEntriesEqual() throws CorruptObjectException {
 
 	void skipEntriesEqual() throws CorruptObjectException {
 		final AbstractTreeIterator ch = currentHead;
-		for (int i = 0; i < trees.length; i++) {
-			final AbstractTreeIterator t = trees[i];
+		for (AbstractTreeIterator t : trees) {
 			if (t.matches == ch) {
 				t.skip();
 				t.matches = null;
@@ -1398,10 +1396,8 @@ static String pathOf(byte[] buf, int pos, int end) {
 	 * @param <T>
 	 *            a tree type.
 	 */
-	public <T extends AbstractTreeIterator> T getTree(
-			Class<T> type) {
-		for (int i = 0; i < trees.length; i++) {
-			AbstractTreeIterator tree = trees[i];
+	public <T extends AbstractTreeIterator> T getTree(Class<T> type) {
+		for (AbstractTreeIterator tree : trees) {
 			if (type.isInstance(tree)) {
 				return type.cast(tree);
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index b768acd..c0c2487 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -774,14 +774,10 @@ public AttributesNode getEntryAttributesNode() throws IOException {
 		return attributesNode;
 	}
 
-	private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
-		@Override
-		public int compare(Entry a, Entry b) {
-			return Paths.compare(
-					a.encodedName, 0, a.encodedNameLen, a.getMode().getBits(),
-					b.encodedName, 0, b.encodedNameLen, b.getMode().getBits());
-		}
-	};
+	private static final Comparator<Entry> ENTRY_CMP = (Entry a,
+			Entry b) -> Paths.compare(a.encodedName, 0, a.encodedNameLen,
+					a.getMode().getBits(), b.encodedName, 0, b.encodedNameLen,
+					b.getMode().getBits());
 
 	/**
 	 * Constructor helper.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index fb95887..bde750b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -563,13 +563,9 @@ public BasicFileAttributes fileAttributes(File file) throws IOException {
 	 * @return the user's home directory; null if the user does not have one.
 	 */
 	protected File userHomeImpl() {
-		final String home = AccessController
-				.doPrivileged(new PrivilegedAction<String>() {
-					@Override
-					public String run() {
-						return System.getProperty("user.home"); //$NON-NLS-1$
-					}
-				});
+		final String home = AccessController.doPrivileged(
+				(PrivilegedAction<String>) () -> System.getProperty("user.home") //$NON-NLS-1$
+		);
 		if (home == null || home.length() == 0)
 			return null;
 		return new File(home).getAbsoluteFile();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index e5c8d9d..9a163e8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -80,12 +80,9 @@ public class FS_Win32_Cygwin extends FS_Win32 {
 	 */
 	public static boolean isCygwin() {
 		final String path = AccessController
-				.doPrivileged(new PrivilegedAction<String>() {
-					@Override
-					public String run() {
-						return System.getProperty("java.library.path"); //$NON-NLS-1$
-					}
-				});
+				.doPrivileged((PrivilegedAction<String>) () -> System
+						.getProperty("java.library.path") //$NON-NLS-1$
+				);
 		if (path == null)
 			return false;
 		File found = FS.searchPath(path, "cygpath.exe"); //$NON-NLS-1$
@@ -141,13 +138,9 @@ public File resolve(File dir, String pn) {
 	/** {@inheritDoc} */
 	@Override
 	protected File userHomeImpl() {
-		final String home = AccessController
-				.doPrivileged(new PrivilegedAction<String>() {
-					@Override
-					public String run() {
-						return System.getenv("HOME"); //$NON-NLS-1$
-					}
-				});
+		final String home = AccessController.doPrivileged(
+				(PrivilegedAction<String>) () -> System.getenv("HOME") //$NON-NLS-1$
+		);
 		if (home == null || home.length() == 0)
 			return super.userHomeImpl();
 		return resolve(new File("."), home); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index 9bba6ca..0e8732d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -48,6 +48,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.file.AtomicMoveNotSupportedException;
@@ -194,31 +195,50 @@ public static void delete(File f, int options) throws IOException {
 		if ((options & EMPTY_DIRECTORIES_ONLY) != 0) {
 			if (f.isDirectory()) {
 				delete = true;
-			} else {
-				if ((options & IGNORE_ERRORS) == 0)
-					throw new IOException(MessageFormat.format(
-							JGitText.get().deleteFileFailed,
-							f.getAbsolutePath()));
+			} else if ((options & IGNORE_ERRORS) == 0) {
+				throw new IOException(MessageFormat.format(
+						JGitText.get().deleteFileFailed, f.getAbsolutePath()));
 			}
 		} else {
 			delete = true;
 		}
 
-		if (delete && !f.delete()) {
-			if ((options & RETRY) != 0 && fs.exists(f)) {
+		if (delete) {
+			Throwable t = null;
+			Path p = f.toPath();
+			try {
+				Files.delete(p);
+				return;
+			} catch (FileNotFoundException e) {
+				if ((options & (SKIP_MISSING | IGNORE_ERRORS)) == 0) {
+					throw new IOException(MessageFormat.format(
+							JGitText.get().deleteFileFailed,
+							f.getAbsolutePath()), e);
+				}
+				return;
+			} catch (IOException e) {
+				t = e;
+			}
+			if ((options & RETRY) != 0) {
 				for (int i = 1; i < 10; i++) {
 					try {
 						Thread.sleep(100);
-					} catch (InterruptedException e) {
+					} catch (InterruptedException ex) {
 						// ignore
 					}
-					if (f.delete())
+					try {
+						Files.deleteIfExists(p);
 						return;
+					} catch (IOException e) {
+						t = e;
+					}
 				}
 			}
-			if ((options & IGNORE_ERRORS) == 0)
+			if ((options & IGNORE_ERRORS) == 0) {
 				throw new IOException(MessageFormat.format(
-						JGitText.get().deleteFileFailed, f.getAbsolutePath()));
+						JGitText.get().deleteFileFailed, f.getAbsolutePath()),
+						t);
+			}
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java
index da57999..5927b33 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GSSManagerFactory.java
@@ -145,13 +145,8 @@ public GSSManager newInstance(URL url) {
 
 				return (GSSManager) GSS_MANAGER_IMPL_CONSTRUCTOR
 						.newInstance(httpCaller);
-			} catch (InstantiationException e) {
-				throw new Error(e);
-			} catch (IllegalAccessException e) {
-				throw new Error(e);
-			} catch (IllegalArgumentException e) {
-				throw new Error(e);
-			} catch (InvocationTargetException e) {
+			} catch (InstantiationException | IllegalAccessException
+					| IllegalArgumentException | InvocationTargetException e) {
 				throw new Error(e);
 			}
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 9190a59..54e4ee0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -303,9 +303,7 @@ public static void disableSslVerify(HttpConnection conn)
 		try {
 			conn.configure(null, trustAllCerts, null);
 			conn.setHostnameVerifier(new DummyHostnameVerifier());
-		} catch (KeyManagementException e) {
-			throw new IOException(e.getMessage());
-		} catch (NoSuchAlgorithmException e) {
+		} catch (KeyManagementException | NoSuchAlgorithmException e) {
 			throw new IOException(e.getMessage());
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
index 639c353..60dead5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
@@ -48,7 +48,10 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.function.BinaryOperator;
+import java.util.stream.Collector;
 
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefComparator;
 
@@ -333,6 +336,32 @@ public String toString() {
 	}
 
 	/**
+	 * Create a {@link Collector} for {@link Ref}.
+	 *
+	 * @param mergeFunction
+	 *            if specified the result will be sorted and deduped.
+	 * @return {@link Collector} for {@link Ref}
+	 * @since 5.4
+	 */
+	public static <T extends Ref> Collector<T, ?, RefList<T>> toRefList(
+			@Nullable BinaryOperator<T> mergeFunction) {
+		return Collector.of(
+				() -> new Builder<>(),
+				Builder<T>::add, (b1, b2) -> {
+					Builder<T> b = new Builder<>();
+					b.addAll(b1);
+					b.addAll(b2);
+					return b;
+				}, (b) -> {
+					if (mergeFunction != null) {
+						b.sort();
+						b.dedupe(mergeFunction);
+					}
+					return b.toRefList();
+				});
+	}
+
+	/**
 	 * Builder to facilitate fast construction of an immutable RefList.
 	 *
 	 * @param <T>
@@ -405,6 +434,16 @@ public void add(T ref) {
 		}
 
 		/**
+		 * Add all items from another builder.
+		 *
+		 * @param other
+		 * @since 5.4
+		 */
+		public void addAll(Builder other) {
+			addAll(other.list, 0, other.size);
+		}
+
+		/**
 		 * Add all items from a source array.
 		 * <p>
 		 * References must be added in sort order, or the array must be sorted
@@ -444,6 +483,31 @@ public void sort() {
 			Arrays.sort(list, 0, size, RefComparator.INSTANCE);
 		}
 
+		/**
+		 * Dedupe the refs in place. Must be called after {@link #sort}.
+		 *
+		 * @param mergeFunction
+		 */
+		@SuppressWarnings("unchecked")
+		void dedupe(BinaryOperator<T> mergeFunction) {
+			if (size == 0) {
+				return;
+			}
+			int lastElement = 0;
+			for (int i = 1; i < size; i++) {
+				if (RefComparator.INSTANCE.compare(list[lastElement],
+						list[i]) == 0) {
+					list[lastElement] = mergeFunction
+							.apply((T) list[lastElement], (T) list[i]);
+				} else {
+					list[lastElement + 1] = list[i];
+					lastElement++;
+				}
+			}
+			size = lastElement + 1;
+			Arrays.fill(list, size, list.length, null);
+		}
+
 		/** @return an unmodifiable list using this collection's backing array. */
 		public RefList<T> toRefList() {
 			return new RefList<>(list, size);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
index a3f9730..d7a4c25 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
@@ -49,6 +49,9 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.function.BinaryOperator;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
 
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.ObjectId;
@@ -285,6 +288,21 @@ public String toString() {
 		return r.toString();
 	}
 
+	/**
+	 * Create a {@link Collector} for {@link Ref}.
+	 *
+	 * @param mergeFunction
+	 * @return {@link Collector} for {@link Ref}
+	 * @since 5.4
+	 */
+	public static Collector<Ref, ?, RefMap> toRefMap(
+			BinaryOperator<Ref> mergeFunction) {
+		return Collectors.collectingAndThen(RefList.toRefList(mergeFunction),
+				(refs) -> new RefMap("", //$NON-NLS-1$
+							refs, RefList.emptyList(),
+						RefList.emptyList()));
+	}
+
 	private String toRefName(String name) {
 		if (0 < prefix.length())
 			name = prefix + name;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 7497750..d04f087 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -368,12 +368,9 @@ public boolean isMacOS() {
 	}
 
 	private String getOsName() {
-		return AccessController.doPrivileged(new PrivilegedAction<String>() {
-			@Override
-			public String run() {
-				return getProperty("os.name"); //$NON-NLS-1$
-			}
-		});
+		return AccessController.doPrivileged(
+				(PrivilegedAction<String>) () -> getProperty("os.name") //$NON-NLS-1$
+		);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java
index 65470d4..18de705 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/IsolatedOutputStream.java
@@ -88,7 +88,7 @@ public class IsolatedOutputStream extends OutputStream {
 	public IsolatedOutputStream(OutputStream out) {
 		dst = out;
 		copier = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS,
-				new ArrayBlockingQueue<Runnable>(1), new NamedThreadFactory());
+				new ArrayBlockingQueue<>(1), new NamedThreadFactory());
 	}
 
 	/** {@inheritDoc} */
@@ -102,12 +102,9 @@ public void write(int ch) throws IOException {
 	public void write(byte[] buf, int pos, int cnt)
 			throws IOException {
 		checkClosed();
-		execute(new Callable<Void>() {
-			@Override
-			public Void call() throws IOException {
-				dst.write(buf, pos, cnt);
-				return null;
-			}
+		execute(() -> {
+			dst.write(buf, pos, cnt);
+			return null;
 		});
 	}
 
@@ -115,12 +112,9 @@ public Void call() throws IOException {
 	@Override
 	public void flush() throws IOException {
 		checkClosed();
-		execute(new Callable<Void>() {
-			@Override
-			public Void call() throws IOException {
-				dst.flush();
-				return null;
-			}
+		execute(() -> {
+			dst.flush();
+			return null;
 		});
 	}
 
@@ -159,12 +153,9 @@ private boolean tryCleanClose() {
 	}
 
 	private void cleanClose() throws IOException {
-		execute(new Callable<Void>() {
-			@Override
-			public Void call() throws IOException {
-				dst.close();
-				return null;
-			}
+		execute(() -> {
+			dst.close();
+			return null;
 		});
 	}
 
@@ -178,12 +169,9 @@ private void dirtyClose() throws IOException {
 
 		Future<Void> close;
 		try {
-			close = copier.submit(new Callable<Void>() {
-				@Override
-				public Void call() throws IOException {
-					dst.close();
-					return null;
-				}
+			close = copier.submit(() -> {
+				dst.close();
+				return null;
 			});
 		} catch (RejectedExecutionException e) {
 			throw new IOException(e);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
index caabcef..9431aaf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
@@ -68,12 +68,10 @@ public class ThrowingPrintWriter extends Writer {
 	 */
 	public ThrowingPrintWriter(Writer out) {
 		this.out = out;
-		LF = AccessController.doPrivileged(new PrivilegedAction<String>() {
-			@Override
-			public String run() {
-				return SystemReader.getInstance().getProperty("line.separator"); //$NON-NLS-1$
-			}
-		});
+		LF = AccessController
+				.doPrivileged((PrivilegedAction<String>) () -> SystemReader
+						.getInstance().getProperty("line.separator") //$NON-NLS-1$
+				);
 	}
 
 	/** {@inheritDoc} */
diff --git a/pom.xml b/pom.xml
index 48abd18..5377b9e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,7 @@
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>org.eclipse.jgit-parent</artifactId>
   <packaging>pom</packaging>
-  <version>5.3.3-SNAPSHOT</version>
+  <version>5.4.0-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -182,9 +182,9 @@
     <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
-    <jgit-last-release-version>5.2.0.201812061821-r</jgit-last-release-version>
-    <apache-sshd-version>2.0.0</apache-sshd-version>
-    <jsch-version>0.1.54</jsch-version>
+    <jgit-last-release-version>5.3.0.201903130848-r</jgit-last-release-version>
+    <apache-sshd-version>2.2.0</apache-sshd-version>
+    <jsch-version>0.1.55</jsch-version>
     <jzlib-version>1.1.1</jzlib-version>
     <javaewah-version>1.1.6</javaewah-version>
     <junit-version>4.12</junit-version>
@@ -204,8 +204,8 @@
     <gson-version>2.8.2</gson-version>
     <bouncycastle-version>1.60</bouncycastle-version>
     <spotbugs-maven-plugin-version>3.1.11</spotbugs-maven-plugin-version>
-    <maven-surefire-version>2.22.1</maven-surefire-version>
-    <maven-compiler-plugin-version>3.8.0</maven-compiler-plugin-version>
+    <maven-surefire-version>2.22.2</maven-surefire-version>
+    <maven-compiler-plugin-version>3.8.1</maven-compiler-plugin-version>
     <maven-project-info-reports-plugin-version>3.0.0</maven-project-info-reports-plugin-version>
     <maven-jxr-plugin-version>3.0.0</maven-jxr-plugin-version>
 
@@ -328,7 +328,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-pmd-plugin</artifactId>
-          <version>3.11.0</version>
+          <version>3.12.0</version>
           <configuration>
             <sourceEncoding>utf-8</sourceEncoding>
             <minimumTokens>100</minimumTokens>
@@ -906,7 +906,7 @@
               <dependency>
                 <groupId>org.eclipse.jdt</groupId>
                 <artifactId>ecj</artifactId>
-                <version>3.16.0</version>
+                <version>3.17.0</version>
               </dependency>
             </dependencies>
           </plugin>
diff --git a/tools/version.sh b/tools/version.sh
index 8b8095d..80693cd 100755
--- a/tools/version.sh
+++ b/tools/version.sh
@@ -131,7 +131,8 @@
 		$seen_version = 1 if (!/<\?xml/ &&
 		s/(version=")[^"]*(")/${1}'"$OSGI_V"'${2}/);
 	}
-	s/(import feature="org\.eclipse\.jgit.*" version=")[^"]*(")/${1}'"$API_V"'${2}/;
+	s/(import feature="org\.eclipse\.jgit[^"]*" version=")[^"]*(")/${1}'"$API_V"'${2}/;
+	s/(import plugin="org\.eclipse\.jgit[^"]*" version=")[^"]*(")/${1}'"$API_V"'${2}/;
 	' org.eclipse.jgit.packaging/org.*.feature/feature.xml
 
 perl -pi~ -e '
@@ -139,22 +140,11 @@
 		$seen_version = 0;
 		$old_argv = $ARGV;
 	}
-	if (!$seen_version) {
-		$seen_version = 1 if
+	if ($seen_version < 2) {
+		$seen_version++ if
 		s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
 	}
-	' org.eclipse.jgit.packaging/org.*.feature/pom.xml
-
-perl -pi~ -e '
-	if ($ARGV ne $old_argv) {
-		$seen_version = 0;
-		$old_argv = $ARGV;
-	}
-	if (!$seen_version) {
-		$seen_version = 1 if
-		s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
-	}
-	' org.eclipse.jgit.packaging/pom.xml
+	' org.eclipse.jgit.packaging/org.*.source.feature/pom.xml
 
 perl -pi~ -e '
 	if ($ARGV ne $old_argv) {
@@ -162,7 +152,7 @@
 		$old_argv = $ARGV;
 	}
 	if ($seen_version < 18) {
-			$seen_version++ if
+		$seen_version++ if
 		s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
 	}
 	' org.eclipse.jgit.coverage/pom.xml