Merge branch 'stable-5.5' into stable-5.6

* stable-5.5:
  Use FileSnapshot without using configs for FileBasedConfig

Change-Id: If904289feecd1e0d8466c1fb998f160f14d54b61
diff --git a/.bazelversion b/.bazelversion
index 227cea2..fd2a018 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-2.0.0
+3.1.0
diff --git a/Documentation/technical/reftable.md b/Documentation/technical/reftable.md
index 47c61a3..1236a79 100644
--- a/Documentation/technical/reftable.md
+++ b/Documentation/technical/reftable.md
@@ -89,6 +89,10 @@
 
 [ref-fmt]: https://git-scm.com/docs/git-check-ref-format
 
+### Key unicity
+
+Each entry must have a unique key; repeated keys are disallowed.
+
 ### Network byte order
 
 All multi-byte, fixed width fields are in network byte order.
@@ -566,6 +570,10 @@
 
 [update-ref]: https://git-scm.com/docs/git-update-ref#_logging_updates
 
+Contrary to traditional reflog (which is a file), renames are encoded as a
+combination of ref deletion and ref creation.
+
+
 #### Reading the log
 
 Readers accessing the log must first read the footer (below) to
@@ -771,12 +779,12 @@
 A collection of reftable files are stored in the `$GIT_DIR/reftable/`
 directory:
 
-    00000001.log
-    00000001.ref
-    00000002.ref
+    00000001-00000001.log
+    00000002-00000002.ref
+    00000003-00000003.ref
 
 where reftable files are named by a unique name such as produced by
-the function `${update_index}.ref`.
+the function `${min_update_index}-${max_update_index}.ref`.
 
 Log-only files use the `.log` extension, while ref-only and mixed ref
 and log files use `.ref`.  extension.
@@ -786,9 +794,9 @@
 recent):
 
     $ cat .git/refs
-    00000001.log
-    00000001.ref
-    00000002.ref
+    00000001-00000001.log
+    00000002-00000002.ref
+    00000003-00000003.ref
 
 Readers must read `$GIT_DIR/refs` to determine which files are
 relevant right now, and search through the stack in reverse order
@@ -815,8 +823,8 @@
 1. Acquire `refs.lock`.
 2. Read `refs` to determine current reftables.
 3. Select `update_index` to be most recent file's `max_update_index + 1`.
-4. Prepare temp reftable `${update_index}_XXXXXX`, including log entries.
-5. Rename `${update_index}_XXXXXX` to `${update_index}.ref`.
+4. Prepare temp reftable `tmp_XXXXXX`, including log entries.
+5. Rename `tmp_XXXXXX` to `${update_index}-${update_index}.ref`.
 6. Copy `refs` to `refs.lock`, appending file from (5).
 7. Rename `refs.lock` to `refs`.
 
@@ -861,12 +869,13 @@
     Ownership of these locks prevents other processes from trying
     to compact these files.
 3.  Release `refs.lock`.
-4.  Compact `B` and `C` into a temp file `${min_update_index}_XXXXXX`.
+4.  Compact `B` and `C` into a temp file `${min_update_index}-${max_update_index}_XXXXXX`.
 5.  Reacquire lock `refs.lock`.
 6.  Verify that `B` and `C` are still in the stack, in that order. This
     should always be the case, assuming that other processes are adhering
     to the locking protocol.
-7.  Rename `${min_update_index}_XXXXXX` to `${min_update_index}_2.ref`.
+7.  Rename `${min_update_index}-${max_update_index}_XXXXXX` to
+    `${min_update_index}-${max_update_index}.ref`.
 8.  Write the new stack to `refs.lock`, replacing `B` and `C` with the
     file from (4).
 9.  Rename `refs.lock` to `refs`.
@@ -875,6 +884,9 @@
 
 This strategy permits compactions to proceed independently of updates.
 
+Each reftable (compacted or not) is uniquely identified by its name, so open
+reftables can be cached by their name.
+
 ## Alternatives considered
 
 ### bzip packed-refs
diff --git a/WORKSPACE b/WORKSPACE
index e40dd40..93962da 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -9,13 +9,18 @@
     urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.8.0.tar.gz"],
 )
 
-load("@bazel_skylib//lib:versions.bzl", "versions")
+# Check Bazel version when invoked by Bazel directly
+load("//tools:bazelisk_version.bzl", "bazelisk_version")
 
-versions.check(minimum_bazel_version = "0.29.0")
+bazelisk_version(name = "bazelisk_version")
+
+load("@bazelisk_version//:check.bzl", "check_bazel_version")
+
+check_bazel_version()
 
 load("//tools:bazlets.bzl", "load_bazlets")
 
-load_bazlets(commit = "f53f51fb660552d0581aa0ba52c3836ed63d56a3")
+load_bazlets(commit = "f30a992da9fc855dce819875afb59f9dd6f860cd")
 
 load(
     "@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl",
@@ -125,7 +130,7 @@
 )
 
 maven_jar(
-    name = "servlet-api-3_1",
+    name = "servlet-api",
     artifact = "javax.servlet:javax.servlet-api:3.1.0",
     sha1 = "3cd63d075497751784b2fa84be59432f4905bf7c",
 )
@@ -198,69 +203,69 @@
     sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf",
 )
 
-JETTY_VER = "9.4.20.v20190813"
+JETTY_VER = "9.4.22.v20191022"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VER,
-    sha1 = "d5d6610321bd173aead473e994f170989d633b25",
-    src_sha1 = "09aa4fce2579d0905e3bde0b3bc923a032394805",
+    sha1 = "62285df7713347586d55f3f93a96299d1b721714",
+    src_sha1 = "fe2d1f1dc7a82ced141df935a0db5b5cadd76f4a",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
-    sha1 = "300c2e6dd62291c512bdaf3ecfefc1e305e26088",
-    src_sha1 = "ec03ce3b6a8c0c3dd2bd8b32bc51419caa10ad62",
+    sha1 = "f7b3b61f09b34a69fd6df4f267f3907813c9224c",
+    src_sha1 = "5a1b33c5dde638ce9dbc2e07f0cff862e5029195",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
-    sha1 = "d4ee11134bca83db85919a1710fce022c67df3b7",
-    src_sha1 = "0db81f86709a8184c793acd309c27dccdb5d439c",
+    sha1 = "f30b9b2cd6f63b073b63c2ac5e7e7f17b63b0908",
+    src_sha1 = "97fbdf8eade55f05d7e99c16fd90d394b248f717",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
-    sha1 = "edda2fd904a881114aba79e2f881c6f4004e8148",
-    src_sha1 = "3e187c60d578c529f4baa3bbfc727d4b7ca7b93c",
+    sha1 = "f96f87fc73c2b586ff40689cbce6ae62d70f18fa",
+    src_sha1 = "0e51a30d0e3309acfc6ee548b90ff55165fbfa5c",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
-    sha1 = "b246c5e350d0aa1b310c07ec362755c34a1cc8cb",
-    src_sha1 = "d12619b4df0d202847096a8429b0f96f123c7c77",
+    sha1 = "0f08e62908f94d1238be386302236a42204d566a",
+    src_sha1 = "d31c00383f13c95404ef606f57513569868acd6b",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
-    sha1 = "8ee753d673a124ba2880361871ab13f6863b2d40",
-    src_sha1 = "2827925d01d95f42d02adf177d2474fbc8a3d5e0",
+    sha1 = "ffe0bf81a2a4a534b79dd981f971c7346a563095",
+    src_sha1 = "42f8abc84a6c4c9d2441d2dfcc60591e3296dc26",
 )
 
-BOUNCYCASTLE_VER = "1.61"
+BOUNCYCASTLE_VER = "1.64"
 
 maven_jar(
     name = "bcpg",
     artifact = "org.bouncycastle:bcpg-jdk15on:" + BOUNCYCASTLE_VER,
-    sha1 = "422656435514ab8a28752b117d5d2646660a0ace",
-    src_sha1 = "836da34e11114cbce8fa99f54175f8f3278d1cce",
+    sha1 = "56956a8c63ccadf62e7c678571cf86f30bd84441",
+    src_sha1 = "b241337df9516b35637d9be84451e2f03a81d186",
 )
 
 maven_jar(
     name = "bcprov",
     artifact = "org.bouncycastle:bcprov-jdk15on:" + BOUNCYCASTLE_VER,
-    sha1 = "00df4b474e71be02c1349c3292d98886f888d1f7",
-    src_sha1 = "3bf88046a16098ea6cc41576dd50d512854d39e1",
+    sha1 = "1467dac1b787b5ad2a18201c0c281df69882259e",
+    src_sha1 = "2881bfaf2c15e9e64b62c2a143db90db7a0d6035",
 )
 
 maven_jar(
     name = "bcpkix",
     artifact = "org.bouncycastle:bcpkix-jdk15on:" + BOUNCYCASTLE_VER,
-    sha1 = "89bb3aa5b98b48e584eee2a7401b7682a46779b4",
-    src_sha1 = "a0498d09200a18737eccc05aa81bbd05c1be0f8c",
+    sha1 = "3dac163e20110817d850d17e0444852a6d7d0bd7",
+    src_sha1 = "5c87199786c06e1a53adf16b1998386bad52da89",
 )
diff --git a/lib/BUILD b/lib/BUILD
index 93bb731..058899f 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -224,7 +224,7 @@
         "//org.eclipse.jgit.lfs.server.test:__pkg__",
         "//org.eclipse.jgit.pgm:__pkg__",
     ],
-    exports = ["@servlet-api-3_1//jar"],
+    exports = ["@servlet-api//jar"],
 )
 
 java_library(
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
index d4751b5..4c4634a 100644
--- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 74ed271..4f76e40 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -5,13 +5,13 @@
 Automatic-Module-Name: org.eclipse.jgit.ant.test
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
 Bundle-Vendor: %Bundle-Vendor
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.ant.tasks;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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 a5d6304..8bbba4c 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
index 94a1c4f..4335e66 100644
--- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index fac3286..37c40c2 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ant
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)"
+  org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.ant;version="5.5.2",
- org.eclipse.jgit.ant.tasks;version="5.5.2";
+Export-Package: org.eclipse.jgit.ant;version="5.6.2",
+ org.eclipse.jgit.ant.tasks;version="5.6.2";
   uses:="org.apache.tools.ant,
    org.apache.tools.ant.types"
diff --git a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
index a1ba9ce..1c2df65 100644
--- a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.ant - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ant.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ant;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 1c1c494..6066ace 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -48,7 +48,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant</artifactId>
@@ -71,7 +71,7 @@
     <dependency>
       <groupId>org.apache.ant</groupId>
       <artifactId>ant</artifactId>
-      <version>1.10.5</version>
+      <version>1.10.7</version>
     </dependency>
   </dependencies>
 
diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
index ef6f5e7..bc7ba1e 100644
--- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index 14167a6..a67ee1c 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.archive
 Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -13,17 +13,17 @@
  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.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.api;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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.5.2";
+Export-Package: org.eclipse.jgit.archive;version="5.6.2";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.api,
    org.apache.commons.compress.archivers,
    org.osgi.framework",
- org.eclipse.jgit.archive.internal;version="5.5.2";x-internal:=true
+ org.eclipse.jgit.archive.internal;version="5.6.2";x-internal:=true
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index 21facee..bb492b0 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.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 61d25a6..83078af 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml
index 0aade52..a0cd834 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -47,7 +47,7 @@
   <modelVersion>4.0.0</modelVersion>
 
   <groupId>org.eclipse.jgit</groupId>
-  <version>5.5.2-SNAPSHOT</version>
+  <version>5.6.2-SNAPSHOT</version>
   <artifactId>org.eclipse.jgit.benchmarks</artifactId>
   <packaging>jar</packaging>
 
@@ -131,6 +131,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
+        <version>3.2.1</version>
         <executions>
           <execution>
             <phase>package</phase>
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index be76c03..db7c5c4 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-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.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.archive</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.apache</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.server</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ui</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.test</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant.test</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.test</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm.test</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.test</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
   </dependencies>
 
diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
index 94a1c4f..4335e66 100644
--- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index 84da9f8..2f906a5 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.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
@@ -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.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="5.5.2";
+ org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="5.6.2";
   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
index 3666a28..3f52d65 100644
--- a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.http.apache - Sources
 Bundle-SymbolicName: org.eclipse.jgit.http.apache.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index a8f5576..5919dd5 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -48,7 +48,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
index f92c5df..9d9e2f8 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
@@ -92,6 +92,7 @@
 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
 import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.transport.http.HttpConnection;
@@ -165,6 +166,8 @@
 						new BasicHttpClientConnectionManager(registry));
 			}
 			clientBuilder.setDefaultRequestConfig(configBuilder.build());
+			clientBuilder.setDefaultCredentialsProvider(
+					new SystemDefaultCredentialsProvider());
 			client = clientBuilder.build();
 		}
 
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
index 94a1c4f..4335e66 100644
--- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 8d71a4d..f4cde03 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.http.server
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.http.server;version="5.5.2",
- org.eclipse.jgit.http.server.glue;version="5.5.2";
+Export-Package: org.eclipse.jgit.http.server;version="5.6.2",
+ org.eclipse.jgit.http.server.glue;version="5.6.2";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="5.5.2";
+ org.eclipse.jgit.http.server.resolver;version="5.6.2";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -18,13 +18,14 @@
 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.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.transport.parser;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.resolver;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)"
+ org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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
index 39c8877..3298c65 100644
--- a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.http.server - Sources
 Bundle-SymbolicName: org.eclipse.jgit.http.server.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 2d4f758..a6ae774 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
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 1c5e7ec..3396b9b 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
@@ -92,6 +92,8 @@
 
 	private UploadPackFactory<HttpServletRequest> uploadPackFactory = new DefaultUploadPackFactory();
 
+	private UploadPackErrorHandler uploadPackErrorHandler;
+
 	private ReceivePackFactory<HttpServletRequest> receivePackFactory = new DefaultReceivePackFactory();
 
 	private final List<Filter> uploadPackFilters = new LinkedList<>();
@@ -150,6 +152,18 @@
 	}
 
 	/**
+	 * Set a custom error handler for git-upload-pack.
+	 *
+	 * @param h
+	 *            A custom error handler for git-upload-pack.
+	 * @since 5.6
+	 */
+	public void setUploadPackErrorHandler(UploadPackErrorHandler h) {
+		assertNotInitialized();
+		this.uploadPackErrorHandler = h;
+	}
+
+	/**
 	 * Add upload-pack filter
 	 *
 	 * @param filter
@@ -212,7 +226,7 @@
 			b = b.through(new UploadPackServlet.Factory(uploadPackFactory));
 			for (Filter f : uploadPackFilters)
 				b = b.through(f);
-			b.with(new UploadPackServlet());
+			b.with(new UploadPackServlet(uploadPackErrorHandler));
 		}
 
 		if (receivePackFactory != ReceivePackFactory.DISABLED) {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
index 5a27be6..922a98a 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
@@ -83,7 +83,7 @@
 		}
 	}
 
-	private static abstract class PackData extends ObjectFileServlet {
+	private abstract static class PackData extends ObjectFileServlet {
 		private static final long serialVersionUID = 1L;
 
 		PackData(String contentType) {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java
new file mode 100644
index 0000000..03be087
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackErrorHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, Google LLC  and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.http.server;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+import org.eclipse.jgit.transport.UploadPack;
+
+/**
+ * Handle git-upload-pack errors.
+ *
+ * <p>
+ * This is an entry point for customizing an error handler for git-upload-pack.
+ * Right before calling {@link UploadPack#uploadWithExceptionPropagation}, JGit
+ * will call this handler if specified through {@link GitFilter}. The
+ * implementation of this handler is responsible for calling
+ * {@link UploadPackRunnable} and handling exceptions for clients.
+ *
+ * <p>
+ * If a custom handler is not specified, JGit will use the default error
+ * handler.
+ *
+ * @since 5.6
+ */
+public interface UploadPackErrorHandler {
+	/**
+	 * @param req
+	 *            The HTTP request
+	 * @param rsp
+	 *            The HTTP response
+	 * @param r
+	 *            A continuation that handles a git-upload-pack request.
+	 * @throws IOException
+	 */
+	void upload(HttpServletRequest req, HttpServletResponse rsp,
+			UploadPackRunnable r) throws IOException;
+
+	/** Process a git-upload-pack request. */
+	public interface UploadPackRunnable {
+		/**
+		 * See {@link UploadPack#uploadWithExceptionPropagation}.
+		 *
+		 * @throws ServiceMayNotContinueException
+		 * @throws IOException
+		 */
+		void upload() throws ServiceMayNotContinueException, IOException;
+	}
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index 0f40371..54561e0 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
@@ -70,7 +70,8 @@
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.http.server.UploadPackErrorHandler.UploadPackRunnable;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.InternalHttpServerGlue;
 import org.eclipse.jgit.transport.PacketLineOut;
@@ -181,53 +182,71 @@
 		}
 	}
 
+	private final UploadPackErrorHandler handler;
+
+	UploadPackServlet(@Nullable UploadPackErrorHandler handler) {
+		this.handler = handler != null ? handler
+				: this::defaultUploadPackHandler;
+	}
+
 	/** {@inheritDoc} */
 	@Override
-	public void doPost(final HttpServletRequest req,
-			final HttpServletResponse rsp) throws IOException {
+	public void doPost(HttpServletRequest req, HttpServletResponse rsp)
+			throws IOException {
 		if (!UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType())) {
 			rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
 			return;
 		}
 
-		SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
-			@Override
-			public void flush() throws IOException {
-				doFlush();
-			}
-		};
+		UploadPackRunnable r = () -> {
+			UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
+			@SuppressWarnings("resource")
+			SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
+				@Override
+				public void flush() throws IOException {
+					doFlush();
+				}
+			};
 
-		UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
-		try {
 			up.setBiDirectionalPipe(false);
 			rsp.setContentType(UPLOAD_PACK_RESULT_TYPE);
 
-			up.upload(getInputStream(req), out, null);
-			out.close();
-
-		} catch (ServiceMayNotContinueException e) {
-			if (e.isOutput()) {
+			try {
+				up.upload(getInputStream(req), out, null);
+				out.close();
+			} catch (ServiceMayNotContinueException e) {
+				if (e.isOutput()) {
+					consumeRequestBody(req);
+					out.close();
+				}
+				throw e;
+			} catch (UploadPackInternalServerErrorException e) {
+				// Special case exception, error message was sent to client.
+				log(up.getRepository(), e.getCause());
 				consumeRequestBody(req);
 				out.close();
-			} else if (!rsp.isCommitted()) {
+			}
+		};
+
+		handler.upload(req, rsp, r);
+	}
+
+	private void defaultUploadPackHandler(HttpServletRequest req,
+			HttpServletResponse rsp, UploadPackRunnable r) throws IOException {
+		try {
+			r.upload();
+		} catch (ServiceMayNotContinueException e) {
+			if (!e.isOutput() && !rsp.isCommitted()) {
 				rsp.reset();
 				sendError(req, rsp, e.getStatusCode(), e.getMessage());
 			}
-			return;
-
-		} catch (UploadPackInternalServerErrorException e) {
-			// Special case exception, error message was sent to client.
-			log(up.getRepository(), e.getCause());
-			consumeRequestBody(req);
-			out.close();
-
 		} catch (Throwable e) {
+			UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
 			log(up.getRepository(), e);
 			if (!rsp.isCommitted()) {
 				rsp.reset();
 				sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
 			}
-			return;
 		}
 	}
 
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
index 0108105..06b31a5 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
@@ -138,7 +138,7 @@
 				// build a request for them so RegexGroupFilter can pick
 				// a different capture group later. Continue using the
 				// first capture group as the path info.
-				WrappedRequest groups[] = new WrappedRequest[cur.groupCount()];
+				WrappedRequest[] groups = new WrappedRequest[cur.groupCount()];
 				for (int groupId = 1; groupId <= cur.groupCount(); groupId++) {
 					final int s = cur.start(groupId);
 					final String path, info;
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
index f167497..574e94a 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
@@ -86,10 +86,10 @@
 	 * @return the configured servlet, or singleton returning 404 if none.
 	 */
 	protected HttpServlet getServlet() {
-		if (httpServlet != null)
+		if (httpServlet != null) {
 			return httpServlet;
-		else
-			return new ErrorServlet(HttpServletResponse.SC_NOT_FOUND);
+		}
+		return new ErrorServlet(HttpServletResponse.SC_NOT_FOUND);
 	}
 
 	/**
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java
index a69fab0..b9e6882 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java
@@ -83,8 +83,7 @@
 				up.setExtraParameters(Arrays.asList(params));
 			}
 			return up;
-		} else {
-			throw new ServiceNotEnabledException();
 		}
+		throw new ServiceNotEnabledException();
 	}
 }
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
index d4751b5..4c4634a 100644
--- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index cec2a09..0600434 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.http.test
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 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.5.2,5.6.0)",
- org.eclipse.jgit.http.server;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.http.server.glue;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.http.server.resolver;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.resolver;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.http.server;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.http.server.glue;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.http.server.resolver;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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 5ab6a7e..854b3c7 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java b/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java
index 78f909e..6f85979 100644
--- a/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java
+++ b/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/RefsUnreadableInMemoryRepository.java
@@ -43,10 +43,14 @@
 package org.eclipse.jgit.http.test;
 
 import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.internal.storage.reftable.Reftable;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 
 /**
@@ -82,12 +86,43 @@
 	}
 
 	private class RefsUnreadableRefDatabase extends MemRefDatabase {
+
+		/** {@inheritDoc} */
 		@Override
-		protected Reftable reader() throws IOException {
+		public Ref exactRef(String name) throws IOException {
 			if (failing) {
 				throw new IOException("disk failed, no refs found");
 			}
-			return super.reader();
+			return super.exactRef(name);
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public Map<String, Ref> getRefs(String prefix) throws IOException {
+			if (failing) {
+				throw new IOException("disk failed, no refs found");
+			}
+
+			return super.getRefs(prefix);
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public List<Ref> getRefsByPrefix(String prefix) throws IOException {
+			if (failing) {
+				throw new IOException("disk failed, no refs found");
+			}
+
+			return super.getRefsByPrefix(prefix);
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
+			if (failing) {
+				throw new IOException("disk failed, no refs found");
+			}
+			return super.getTipsWithSha1(id);
 		}
 	}
 }
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 3401e26..99aa06b 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
@@ -331,9 +331,8 @@
 					String fragment = u.getRawFragment();
 					if (fragment != null) {
 						return u.getRawPath() + '#' + fragment;
-					} else {
-						return u.getRawPath();
 					}
+					return u.getRawPath();
 				} catch (URISyntaxException e) {
 					return url;
 				}
diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
index 2ca78ff..3dd5840 100644
--- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index a7254a3..26ac174 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.junit.http
 Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 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.5.2,5.6.0)",
- org.eclipse.jgit.http.server;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.resolver;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.http.server;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.6.2,5.7.0)",
  org.junit;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="5.5.2";
+Export-Package: org.eclipse.jgit.junit.http;version="5.6.2";
   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
index 6f5590b..d8c8161 100644
--- a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.junit.http - Sources
 Bundle-SymbolicName: org.eclipse.jgit.junit.http.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index e4b2c6b..988102c 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
index ba0138b..e089c87 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
@@ -205,7 +205,7 @@
 	}
 
 	private SslContextFactory createTestSslContextFactory(String hostName) {
-		SslContextFactory factory = new SslContextFactory(true);
+		SslContextFactory.Client factory = new SslContextFactory.Client(true);
 
 		String dName = "CN=,OU=,O=,ST=,L=,C=";
 
@@ -311,10 +311,10 @@
 
 		@Override
 		protected String[] loadRoleInfo(UserPrincipal user) {
-			if (users.get(user.getName()) == null)
+			if (users.get(user.getName()) == null) {
 				return null;
-			else
-				return new String[] { role };
+			}
+			return new String[] { role };
 		}
 
 		@Override
diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs
index 2ca78ff..3dd5840 100644
--- a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index ff2edd3..d66492b 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.junit.ssh
 Bundle-SymbolicName: org.eclipse.jgit.junit.ssh
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -30,8 +30,8 @@
  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.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.junit.ssh;version="5.5.2"
+Export-Package: org.eclipse.jgit.junit.ssh;version="5.6.2"
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
index c828888..285ba82 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.junit.ssh - Sources
 Bundle-SymbolicName: org.eclipse.jgit.junit.ssh.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index 3b28c00..cee9037 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
diff --git a/org.eclipse.jgit.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters
index 4a2da06..025a79d 100644
--- a/org.eclipse.jgit.junit/.settings/.api_filters
+++ b/org.eclipse.jgit.junit/.settings/.api_filters
@@ -8,6 +8,13 @@
                 <message_argument value="setSystemGitConfig(FileBasedConfig)"/>
             </message_arguments>
         </filter>
+        <filter id="1141899266">
+            <message_arguments>
+                <message_argument value="5.5"/>
+                <message_argument value="5.6"/>
+                <message_argument value="setJGitConfig(FileBasedConfig)"/>
+            </message_arguments>
+        </filter>
         <filter id="1142947843">
             <message_arguments>
                 <message_argument value="5.1.9"/>
diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
index b675029..530f8f6 100644
--- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
@@ -103,7 +103,7 @@
 org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
 org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
 org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 4de3e46..e61df3a 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,35 +3,35 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.junit
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.annotations;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.api;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.api.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.dircache;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.merge;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="5.5.2",
- org.eclipse.jgit.treewalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util.io;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util.time;version="[5.5.2,5.6.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.api;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.api.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.dircache;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.merge;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="5.6.2",
+ org.eclipse.jgit.treewalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util.io;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util.time;version="[5.6.2,5.7.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;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.5.2";
+Export-Package: org.eclipse.jgit.junit;version="5.6.2";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -44,4 +44,4 @@
    org.junit.runners.model,
    org.junit.runner,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.junit.time;version="5.5.2";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="5.6.2";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
index 9568f93..de62cba 100644
--- a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.junit - Sources
 Bundle-SymbolicName: org.eclipse.jgit.junit.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index 1ccc220..691392c 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepeatRule.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepeatRule.java
index 5b58f0b..d003520 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepeatRule.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepeatRule.java
@@ -78,7 +78,7 @@
  */
 public class RepeatRule implements TestRule {
 
-	private static Logger LOG = Logger
+	private static final Logger LOG = Logger
 			.getLogger(RepeatRule.class.getName());
 
 	/**
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 593b960..7b476de 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
@@ -784,9 +784,8 @@
 			}
 			update(Constants.HEAD, result);
 			return pool.parseCommit(result);
-		} else {
-			throw new IOException("Merge conflict");
 		}
+		throw new IOException("Merge conflict");
 	}
 
 	/**
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
index d4751b5..4c4634a 100644
--- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
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 bcd5cf0..5a824cb 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs.server.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 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.5.2,5.6.0)",
- org.eclipse.jgit.api.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.server;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.test;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.api;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.api.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.server;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.test;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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 a116a6d..6b0297f 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
index 525ac67..9fd92b1 100644
--- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index f519f26..93b5964 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs.server
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs.server;version="5.5.2";
+Export-Package: org.eclipse.jgit.lfs.server;version="5.6.2";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="5.5.2";
+ org.eclipse.jgit.lfs.server.fs;version="5.6.2";
   uses:="javax.servlet,
    javax.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="5.5.2";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="5.5.2";
+ org.eclipse.jgit.lfs.server.internal;version="5.6.2";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="5.6.2";
   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.5.2,5.6.0)",
- org.eclipse.jgit.internal;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.internal;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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
index 55cdc0b..4624ac7 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.lfs.server - Sources
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index 01a0b8d..7aaf9ff 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
index c7f55dd..e29783e 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
@@ -93,7 +93,7 @@
  * @since 4.3
  */
 public abstract class LfsProtocolServlet extends HttpServlet {
-	private static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(LfsProtocolServlet.class);
 
 	private static final long serialVersionUID = 1L;
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
index 0a7c37c..ad9ef78 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
@@ -110,9 +110,8 @@
 		Path p = getPath(id);
 		if (Files.exists(p)) {
 			return Files.size(p);
-		} else {
-			return -1;
 		}
+		return -1;
 	}
 
 	/**
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
index 06a7726..e65e603 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
@@ -67,7 +67,7 @@
  */
 public class ObjectDownloadListener implements WriteListener {
 
-	private static Logger LOG = Logger
+	private static final Logger LOG = Logger
 			.getLogger(ObjectDownloadListener.class.getName());
 
 	private final AsyncContext context;
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java
index 3bb2899..332e39f 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java
@@ -71,7 +71,7 @@
  */
 public class ObjectUploadListener implements ReadListener {
 
-	private static Logger LOG = Logger
+	private static final Logger LOG = Logger
 			.getLogger(ObjectUploadListener.class.getName());
 
 	private final AsyncContext context;
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
index 7b76cec..4bcca4a 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
@@ -352,9 +352,8 @@
 		String encodedPath = urlEncode(path, true);
 		if (encodedPath.startsWith("/")) { //$NON-NLS-1$
 			return encodedPath;
-		} else {
-			return "/" + encodedPath; //$NON-NLS-1$
 		}
+		return "/" + encodedPath; //$NON-NLS-1$
 	}
 
 	private static byte[] hash(String s) {
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
index d4751b5..4c4634a 100644
--- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index cf10aa6..d2b9ad7 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,22 +3,22 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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.5.2";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="5.6.2";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 8166a07..2c75ad0 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs/.settings/.api_filters b/org.eclipse.jgit.lfs/.settings/.api_filters
deleted file mode 100644
index 9747df8..0000000
--- a/org.eclipse.jgit.lfs/.settings/.api_filters
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit.lfs" version="2">
-    <resource path="src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java" type="org.eclipse.jgit.lfs.lib.AnyLongObjectId">
-        <filter id="1141899266">
-            <message_arguments>
-                <message_argument value="5.4"/>
-                <message_argument value="5.5"/>
-                <message_argument value="isEqual(AnyLongObjectId, AnyLongObjectId)"/>
-            </message_arguments>
-        </filter>
-    </resource>
-</component>
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
index 525ac67..9fd92b1 100644
--- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index da1fbd7..8b7371f 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.lfs
 Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs;version="5.5.2",
- org.eclipse.jgit.lfs.errors;version="5.5.2",
- org.eclipse.jgit.lfs.internal;version="5.5.2";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.5.2"
+Export-Package: org.eclipse.jgit.lfs;version="5.6.2",
+ org.eclipse.jgit.lfs.errors;version="5.6.2",
+ org.eclipse.jgit.lfs.internal;version="5.6.2";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.6.2"
 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.5.2,5.6.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.attributes;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.diff;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.hooks;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.pack;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util.io;version="[5.5.2,5.6.0)"
+ org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.attributes;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.diff;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.hooks;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.pack;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util.io;version="[5.6.2,5.7.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
index 562747d..e2ff973 100644
--- a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.lfs - Sources
 Bundle-SymbolicName: org.eclipse.jgit.lfs.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 1f2afd9..7c0d30a 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs</artifactId>
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
index 56e3a12..e90d929 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
@@ -83,9 +83,8 @@
 			Attribute attribute) throws IOException {
 		if (isEnabled(db) && (attribute == null || isEnabled(db, attribute))) {
 			return LfsBlobFilter.smudgeLfsBlob(db, loader);
-		} else {
-			return loader;
 		}
+		return loader;
 	}
 
 	@Override
@@ -93,9 +92,8 @@
 			long length, Attribute attribute) throws IOException {
 		if (isEnabled(db, attribute)) {
 			return new LfsInputStream(LfsBlobFilter.cleanLfsBlob(db, input));
-		} else {
-			return new LfsInputStream(input, length);
 		}
+		return new LfsInputStream(input, length);
 	}
 
 	@Override
@@ -108,6 +106,16 @@
 		return null;
 	}
 
+	@Override
+	@Nullable
+	public PrePushHook getPrePushHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		if (isEnabled(repo)) {
+			return new LfsPrePushHook(repo, outputStream, errorStream);
+		}
+		return null;
+	}
+
 	/**
 	 * @param db
 	 *            the repository
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 80da802..27be4f6 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,7 +78,7 @@
 	 * The factory is responsible for creating instances of
 	 * {@link org.eclipse.jgit.lfs.CleanFilter}
 	 */
-	public final static FilterCommandFactory FACTORY = CleanFilter::new;
+	public static final FilterCommandFactory FACTORY = CleanFilter::new;
 
 	/**
 	 * Registers this filter by calling
@@ -138,32 +138,30 @@
 				aOut.write(buf, 0, length);
 				size += length;
 				return length;
-			} else {
-				aOut.close();
-				AnyLongObjectId loid = aOut.getId();
-				aOut = null;
-				Path mediaFile = lfsUtil.getMediaFile(loid);
-				if (Files.isRegularFile(mediaFile)) {
-					long fsSize = Files.size(mediaFile);
-					if (fsSize != size) {
-						throw new CorruptMediaFile(mediaFile, size, fsSize);
-					} else {
-						FileUtils.delete(tmpFile.toFile());
-					}
-				} else {
-					Path parent = mediaFile.getParent();
-					if (parent != null) {
-						FileUtils.mkdirs(parent.toFile(), true);
-					}
-					FileUtils.rename(tmpFile.toFile(), mediaFile.toFile(),
-							StandardCopyOption.ATOMIC_MOVE);
-				}
-				LfsPointer lfsPointer = new LfsPointer(loid, size);
-				lfsPointer.encode(out);
-				in.close();
-				out.close();
-				return -1;
 			}
+			aOut.close();
+			AnyLongObjectId loid = aOut.getId();
+			aOut = null;
+			Path mediaFile = lfsUtil.getMediaFile(loid);
+			if (Files.isRegularFile(mediaFile)) {
+				long fsSize = Files.size(mediaFile);
+				if (fsSize != size) {
+					throw new CorruptMediaFile(mediaFile, size, fsSize);
+				}
+				FileUtils.delete(tmpFile.toFile());
+			} else {
+				Path parent = mediaFile.getParent();
+				if (parent != null) {
+					FileUtils.mkdirs(parent.toFile(), true);
+				}
+				FileUtils.rename(tmpFile.toFile(), mediaFile.toFile(),
+						StandardCopyOption.ATOMIC_MOVE);
+			}
+			LfsPointer lfsPointer = new LfsPointer(loid, size);
+			lfsPointer.encode(out);
+			in.close();
+			out.close();
+			return -1;
 		} catch (IOException e) {
 			if (aOut != null) {
 				aOut.abort();
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java
index 3e6a261..b3e304f 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java
@@ -107,6 +107,20 @@
 		super(repo, outputStream);
 	}
 
+	/**
+	 * @param repo
+	 *            the repository
+	 * @param outputStream
+	 *            not used by this implementation
+	 * @param errorStream
+	 *            not used by this implementation
+	 * @since 5.6
+	 */
+	public LfsPrePushHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		super(repo, outputStream, errorStream);
+	}
+
 	@Override
 	public void setRefs(Collection<RemoteRefUpdate> toRefs) {
 		this.refs = toRefs;
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 6b30da3..3551ee1 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,7 +92,7 @@
 	 * The factory is responsible for creating instances of
 	 * {@link org.eclipse.jgit.lfs.SmudgeFilter}
 	 */
-	public final static FilterCommandFactory FACTORY = SmudgeFilter::new;
+	public static final FilterCommandFactory FACTORY = SmudgeFilter::new;
 
 	/**
 	 * Register this filter in JGit
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
index feb8b4a..184658d 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
@@ -179,9 +179,8 @@
 					remoteUrl, u);
 			additionalHeaders.putAll(action.header);
 			return action.href;
-		} else {
-			return remoteUrl + Protocol.INFO_LFS_ENDPOINT;
 		}
+		return remoteUrl + Protocol.INFO_LFS_ENDPOINT;
 	}
 
 	private static Protocol.ExpiringAction getSshAuthentication(
@@ -262,9 +261,8 @@
 
 		if (path.endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT)) {
 			return path.substring(0, path.length() - 4);
-		} else {
-			return path;
 		}
+		return path;
 	}
 
 	/**
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
index b095d20..a9bd27f 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
@@ -303,10 +303,10 @@
 	/** {@inheritDoc} */
 	@Override
 	public final boolean equals(Object o) {
-		if (o instanceof AnyLongObjectId)
+		if (o instanceof AnyLongObjectId) {
 			return equals((AnyLongObjectId) o);
-		else
-			return false;
+		}
+		return false;
 	}
 
 	/**
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
index 169ef7e..d47cc01 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
@@ -30,7 +30,6 @@
 # "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=\
@@ -69,7 +68,7 @@
     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\
+    (Java\u2122 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\
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 2a1a0b6..b17ec85 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.5.2.qualifier"
+      version="5.6.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/license.html
index 008b801..004b6de 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/license.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/license.html
@@ -5,13 +5,10 @@
 <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
@@ -24,9 +21,7 @@
 		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
@@ -35,13 +30,11 @@
 			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
@@ -62,7 +55,6 @@
 			&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
@@ -70,7 +62,6 @@
 		&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>
@@ -79,7 +70,6 @@
 			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
@@ -92,12 +82,10 @@
 		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>)
@@ -118,16 +106,12 @@
 			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
@@ -140,7 +124,6 @@
 			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
@@ -149,7 +132,6 @@
 		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
@@ -171,19 +153,16 @@
 			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>
+</html>
\ No newline at end of file
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 9f2a30d..b3ae941 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.properties
index 480e9c3..b2fd6ed 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.properties
@@ -30,7 +30,6 @@
 # "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=\
@@ -69,7 +68,7 @@
     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\
+    (Java\u2122 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\
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 4096f8d..7384143 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.5.2.qualifier"
+      version="5.6.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="5.5.2" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="5.6.2" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/license.html
index 008b801..004b6de 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/license.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/license.html
@@ -5,13 +5,10 @@
 <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
@@ -24,9 +21,7 @@
 		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
@@ -35,13 +30,11 @@
 			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
@@ -62,7 +55,6 @@
 			&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
@@ -70,7 +62,6 @@
 		&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>
@@ -79,7 +70,6 @@
 			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
@@ -92,12 +82,10 @@
 		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>)
@@ -118,16 +106,12 @@
 			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
@@ -140,7 +124,6 @@
 			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
@@ -149,7 +132,6 @@
 		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
@@ -171,19 +153,16 @@
 			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>
+</html>
\ No newline at end of file
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 200a1e5..cc1238d 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties
index 291b29e..acf33a9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties
@@ -31,7 +31,6 @@
 # "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=\
@@ -70,7 +69,7 @@
     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\
+    (Java\u2122 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\
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 aa43cef..c8bad5c 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.5.2.qualifier"
+      version="5.6.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -24,7 +24,7 @@
 
    <requires>
       <import plugin="com.jcraft.jsch"/>
-      <import plugin="org.eclipse.jgit" version="5.5.2" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="5.6.2" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/license.html
index 008b801..004b6de 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/license.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/license.html
@@ -5,13 +5,10 @@
 <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
@@ -24,9 +21,7 @@
 		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
@@ -35,13 +30,11 @@
 			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
@@ -62,7 +55,6 @@
 			&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
@@ -70,7 +62,6 @@
 		&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>
@@ -79,7 +70,6 @@
 			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
@@ -92,12 +82,10 @@
 		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>)
@@ -118,16 +106,12 @@
 			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
@@ -140,7 +124,6 @@
 			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
@@ -149,7 +132,6 @@
 		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
@@ -171,19 +153,16 @@
 			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>
+</html>
\ No newline at end of file
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 23d3450..148ce1e 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.properties
index 8387585..5d953fa 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.properties
@@ -30,7 +30,6 @@
 # "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=\
@@ -69,7 +68,7 @@
     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\
+    (Java\u2122 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\
@@ -174,4 +173,4 @@
 \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 ##########################################
+########### end of license property ##########################################
\ No newline at end of file
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 781e94a..1e26021 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.5.2.qualifier"
+      version="5.6.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="5.5.2" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.6.2" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html
index 008b801..004b6de 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html
@@ -5,13 +5,10 @@
 <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
@@ -24,9 +21,7 @@
 		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
@@ -35,13 +30,11 @@
 			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
@@ -62,7 +55,6 @@
 			&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
@@ -70,7 +62,6 @@
 		&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>
@@ -79,7 +70,6 @@
 			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
@@ -92,12 +82,10 @@
 		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>)
@@ -118,16 +106,12 @@
 			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
@@ -140,7 +124,6 @@
 			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
@@ -149,7 +132,6 @@
 		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
@@ -171,19 +153,16 @@
 			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>
+</html>
\ No newline at end of file
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 b5fb886..45821a0 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties
index ef9031e..450cff4 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties
@@ -30,7 +30,6 @@
 # "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=\
@@ -69,7 +68,7 @@
     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\
+    (Java\u2122 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\
@@ -174,4 +173,4 @@
 \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 ##########################################
+########### end of license property ##########################################
\ No newline at end of file
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 8656890..c092b97 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.5.2.qualifier"
+      version="5.6.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -35,9 +35,9 @@
          version="0.0.0"/>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="5.5.2" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="5.5.2" match="equivalent"/>
-      <import feature="org.eclipse.jgit.ssh.apache" version="5.5.2" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.6.2" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="5.6.2" match="equivalent"/>
+      <import feature="org.eclipse.jgit.ssh.apache" version="5.6.2" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/license.html
index 008b801..004b6de 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/license.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/license.html
@@ -5,13 +5,10 @@
 <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
@@ -24,9 +21,7 @@
 		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
@@ -35,13 +30,11 @@
 			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
@@ -62,7 +55,6 @@
 			&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
@@ -70,7 +62,6 @@
 		&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>
@@ -79,7 +70,6 @@
 			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
@@ -92,12 +82,10 @@
 		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>)
@@ -118,16 +106,12 @@
 			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
@@ -140,7 +124,6 @@
 			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
@@ -149,7 +132,6 @@
 		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
@@ -171,19 +153,16 @@
 			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>
+</html>
\ No newline at end of file
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 b4fe972..ff94fb0 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
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 63c76bf..828133a 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties
index c04dc63..5043c32 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties
@@ -31,7 +31,6 @@
 # "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=\
@@ -70,7 +69,7 @@
     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\
+    (Java\u2122 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\
@@ -175,4 +174,4 @@
 \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 ##########################################
+########### end of license property ##########################################
\ No newline at end of file
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 8781c66..2d6e2d8 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,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.source"
       label="%featureName"
-      version="5.5.2.qualifier"
+      version="5.6.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="5.5.2" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.6.2" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/license.html
index 008b801..004b6de 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/license.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/license.html
@@ -5,13 +5,10 @@
 <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
@@ -24,9 +21,7 @@
 		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
@@ -35,13 +30,11 @@
 			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
@@ -62,7 +55,6 @@
 			&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
@@ -70,7 +62,6 @@
 		&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>
@@ -79,7 +70,6 @@
 			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
@@ -92,12 +82,10 @@
 		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>)
@@ -118,16 +106,12 @@
 			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
@@ -140,7 +124,6 @@
 			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
@@ -149,7 +132,6 @@
 		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
@@ -171,19 +153,16 @@
 			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>
+</html>
\ No newline at end of file
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 9ce5ec4..17162b6 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
@@ -63,7 +63,7 @@
     <dependency>
       <groupId>org.eclipse.jgit.feature</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>5.5.2-SNAPSHOT</version>
+      <version>5.6.2-SNAPSHOT</version>
     </dependency>
   </dependencies>
 
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties
index 2b08612..e412fc2 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties
@@ -30,7 +30,6 @@
 # "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=\
@@ -69,7 +68,7 @@
     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\
+    (Java\u2122 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\
@@ -174,4 +173,4 @@
 \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 ##########################################
+########### end of license property ##########################################
\ No newline at end of file
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 a55f941..eb285d6 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.5.2.qualifier"
+      version="5.6.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="5.5.2" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.6.2" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html
index 008b801..004b6de 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html
@@ -5,13 +5,10 @@
 <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
@@ -24,9 +21,7 @@
 		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
@@ -35,13 +30,11 @@
 			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
@@ -62,7 +55,6 @@
 			&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
@@ -70,7 +62,6 @@
 		&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>
@@ -79,7 +70,6 @@
 			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
@@ -92,12 +82,10 @@
 		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>)
@@ -118,16 +106,12 @@
 			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
@@ -140,7 +124,6 @@
 			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
@@ -149,7 +132,6 @@
 		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
@@ -171,19 +153,16 @@
 			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>
+</html>
\ No newline at end of file
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 9ebafca..f4ac351 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
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
index 5954a34..42bc17f 100644
--- 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
@@ -2,4 +2,4 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: JGit Target Platform Bundle
 Bundle-SymbolicName: org.eclipse.jgit.target
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
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 5d9a00a..9fe380e 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,26 +1,26 @@
 <?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="1638608437">
+<target name="jgit-4.10" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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.10.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.tpd
index 8d0df4f..9eda6b0 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.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://download.eclipse.org/releases/2018-12/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
index 76fb1ef..0033f2b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.11" sequenceNumber="1638608437">
+<target name="jgit-4.11" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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.11.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.tpd
index 92eba15..ed0e01b 100644
--- 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
@@ -1,7 +1,7 @@
 target "jgit-4.11" with source configurePhase
 
-include "projects/jetty-9.4.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://download.eclipse.org/releases/2019-03/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
index 8279a30..c39fbe0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.12" sequenceNumber="1638608437">
+<target name="jgit-4.12" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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.12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd
index 36d7110..6232167 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.12" with source configurePhase
 
-include "projects/jetty-9.4.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://download.eclipse.org/releases/2019-06/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
index 5c7c025..f3d955a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.13" sequenceNumber="1638608439">
+<target name="jgit-4.13" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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.13.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd
index 7eb4649..ff8c6d0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.13" with source configurePhase
 
-include "projects/jetty-9.4.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://download.eclipse.org/releases/2019-09/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
new file mode 100644
index 0000000..fa52a85
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.14" sequenceNumber="1638636816">
+  <locations>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <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.55.v20190404-1902"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.55.v20190404-1902"/>
+      <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
+      <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
+      <unit id="javaewah" version="1.1.6.v20160919-1400"/>
+      <unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
+      <unit id="javax.servlet" version="3.1.0.v201410161800"/>
+      <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
+      <unit id="net.bytebuddy.byte-buddy" version="1.9.0.v20181107-1410"/>
+      <unit id="net.bytebuddy.byte-buddy-agent" version="1.9.0.v20181106-1534"/>
+      <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.9.0.v20181106-1534"/>
+      <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
+      <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.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
+      <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.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"/>
+      <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
+      <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"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
+      <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/>
+      <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
+      <unit id="org.junit" version="4.12.0.v201504281640"/>
+      <unit id="org.junit.source" version="4.12.0.v201504281640"/>
+      <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/>
+      <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/>
+      <unit id="org.mockito" version="2.23.0.v20190527-1420"/>
+      <unit id="org.mockito.source" version="2.23.0.v20190527-1420"/>
+      <unit id="org.objenesis" version="2.6.0.v20180420-1519"/>
+      <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/>
+      <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
+      <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/>
+      <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/>
+      <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
+      <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
+      <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/repository"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.osgi" version="0.0.0"/>
+      <repository location="https://download.eclipse.org/releases/2019-12/"/>
+    </location>
+  </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd
new file mode 100644
index 0000000..99fa6e3
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.14" with source configurePhase
+
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
+
+location "https://download.eclipse.org/releases/2019-12/" {
+	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 72a9c97..cd72a5b 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,26 +1,26 @@
 <?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="1638608437">
+<target name="jgit-4.6" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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 a88a927..525ad81 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.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://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 a43101a..d9eba46 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,26 +1,26 @@
 <?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="1638608437">
+<target name="jgit-4.7" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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 38ae2b3..4cac2fd 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.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://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 cabc810..8cd1e24 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,26 +1,26 @@
 <?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="1638608437">
+<target name="jgit-4.8" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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 075525e..258442b 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.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://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 3b2f005..637020d 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,26 +1,26 @@
 <?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="1638608437">
+<target name="jgit-4.9" sequenceNumber="1638636816">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.20.v20190813"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.20.v20190813"/>
-      <repository id="jetty-9.4.20" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.22.v20191022"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.22.v20191022"/>
+      <repository id="jetty-9.4.22" location="https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
@@ -39,8 +39,8 @@
       <unit id="net.bytebuddy.byte-buddy.source" version="1.9.0.v20181107-1410"/>
       <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.ant" version="1.10.5.v20190526-1402"/>
-      <unit id="org.apache.ant.source" version="1.10.5.v20190526-1402"/>
+      <unit id="org.apache.ant" version="1.10.7.v20190926-0324"/>
+      <unit id="org.apache.ant.source" version="1.10.7.v20190926-0324"/>
       <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"/>
@@ -57,12 +57,12 @@
       <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"/>
-      <unit id="org.bouncycastle.bcpg" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpg.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcpkix.source" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov" version="1.61.0.v20190602-1335"/>
-      <unit id="org.bouncycastle.bcprov.source" version="1.61.0.v20190602-1335"/>
+      <unit id="org.bouncycastle.bcpg" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpg.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcpkix.source" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov" version="1.64.0.v20191109-0815"/>
+      <unit id="org.bouncycastle.bcprov.source" version="1.64.0.v20191109-0815"/>
       <unit id="org.hamcrest" version="1.1.0.v20090501071000"/>
       <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
       <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
@@ -82,7 +82,7 @@
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
       <unit id="org.tukaani.xz" version="1.8.0.v20180207-1613"/>
       <unit id="org.tukaani.xz.source" version="1.8.0.v20180207-1613"/>
-      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20190827152740/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/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 36894cf..c4f2b81 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.20.tpd"
-include "orbit/R20190827152740-2019-09.tpd"
+include "projects/jetty-9.4.22.tpd"
+include "orbit/R20191126223242-2019-12.tpd"
 
 location "https://download.eclipse.org/releases/2018-09/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20191126223242-2019-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20191126223242-2019-12.tpd
new file mode 100644
index 0000000..679e919
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20191126223242-2019-12.tpd
@@ -0,0 +1,65 @@
+target "R20191126223242-2019-12" with source configurePhase
+// see http://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/downloads/drops/R20191126223242/repository" {
+	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]
+	com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
+	com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
+	javaewah [1.1.6.v20160919-1400,1.1.6.v20160919-1400]
+	javaewah.source [1.1.6.v20160919-1400,1.1.6.v20160919-1400]
+	javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
+	javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
+	net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
+	net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
+	net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
+	net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
+	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.ant [1.10.7.v20190926-0324,1.10.7.v20190926-0324]
+	org.apache.ant.source [1.10.7.v20190926-0324,1.10.7.v20190926-0324]
+	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.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]
+	org.bouncycastle.bcpg [1.64.0.v20191109-0815,1.64.0.v20191109-0815]
+	org.bouncycastle.bcpg.source [1.64.0.v20191109-0815,1.64.0.v20191109-0815]
+	org.bouncycastle.bcpkix [1.64.0.v20191109-0815,1.64.0.v20191109-0815]
+	org.bouncycastle.bcpkix.source [1.64.0.v20191109-0815,1.64.0.v20191109-0815]
+	org.bouncycastle.bcprov [1.64.0.v20191109-0815,1.64.0.v20191109-0815]
+	org.bouncycastle.bcprov.source [1.64.0.v20191109-0815,1.64.0.v20191109-0815]
+	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]
+	org.junit [4.12.0.v201504281640,4.12.0.v201504281640]
+	org.junit.source [4.12.0.v201504281640,4.12.0.v201504281640]
+	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.mockito [2.23.0.v20190527-1420,2.23.0.v20190527-1420]
+	org.mockito.source [2.23.0.v20190527-1420,2.23.0.v20190527-1420]
+	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.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]
+	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]
+}
+
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 c0cf064..e722adb 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.20.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.20.tpd
deleted file mode 100644
index 0e4d189..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.20.tpd
+++ /dev/null
@@ -1,20 +0,0 @@
-target "jetty-9.4.20" with source configurePhase
-
-location jetty-9.4.20 "https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.20.v20190813/" {
-	org.eclipse.jetty.client [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.client.source [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.continuation [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.continuation.source [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.http [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.http.source [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.io [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.io.source [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.security [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.security.source [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.server [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.server.source [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.servlet [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.servlet.source [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.util [9.4.20.v20190813,9.4.20.v20190813]
-	org.eclipse.jetty.util.source [9.4.20.v20190813,9.4.20.v20190813]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.22.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.22.tpd
new file mode 100644
index 0000000..64012c2
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.22.tpd
@@ -0,0 +1,20 @@
+target "jetty-9.4.22" with source configurePhase
+
+location jetty-9.4.22 "https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.22.v20191022/" {
+	org.eclipse.jetty.client [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.client.source [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.continuation [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.continuation.source [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.http [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.http.source [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.io [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.io.source [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.security [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.security.source [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.server [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.server.source [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.servlet [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.servlet.source [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.util [9.4.22.v20191022,9.4.22.v20191022]
+	org.eclipse.jetty.util.source [9.4.22.v20191022,9.4.22.v20191022]
+}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 9c3b926..1a8e9c9 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -49,7 +49,7 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>5.5.2-SNAPSHOT</version>
+  <version>5.6.2-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
index d4751b5..4c4634a 100644
--- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 2cb36e1..92e895c 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.pgm.test
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.api.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.diff;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.dircache;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="5.5.2",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.merge;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.pgm;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.pgm.internal;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.pgm.opt;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util.io;version="[5.5.2,5.6.0)",
+Import-Package: org.eclipse.jgit.api;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.api.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.diff;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.dircache;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="5.6.2",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.merge;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.pgm;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.pgm.opt;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util.io;version="[5.6.2,5.7.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 49535d6..9699004 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BlameTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BlameTest.java
index e806872..6da1e46 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BlameTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BlameTest.java
@@ -42,8 +42,14 @@
  */
 package org.eclipse.jgit.pgm;
 
+import static org.junit.Assert.assertTrue;
+
 import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.MergeResult;
 import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -85,7 +91,7 @@
 			git.add().addFilepattern("inIndex.txt").call();
 		}
 		assertStringArrayEquals(
-				"         (Not Committed Yet          1) index",
+				"00000000 (Not Committed Yet 2009-08-15 20:12:58 -0330 1) index",
 				execute("git blame inIndex.txt"));
 	}
 
@@ -119,4 +125,36 @@
 		thrown.expectMessage("no such path 'sub/does_not_exist.txt' in HEAD");
 		execute("git blame sub/does_not_exist.txt");
 	}
+
+	@Test
+	public void testBlameMergeConflict() throws Exception {
+		try (Git git = new Git(db)) {
+			writeTrashFile("file", "Origin\n");
+			git.add().addFilepattern("file").call();
+			git.commit().setMessage("initial commit").call();
+			git.checkout().setCreateBranch(true)
+					.setName("side").call();
+			writeTrashFile("file",
+					"Conflicting change from side branch\n");
+			git.add().addFilepattern("file").call();
+			RevCommit side = git.commit().setMessage("side commit")
+					.setCommitter(new PersonIdent("gitter", "")).call();
+			git.checkout().setName(Constants.MASTER).call();
+			writeTrashFile("file", "Change on master branch\n");
+			git.add().addFilepattern("file").call();
+			git.commit().setMessage("Commit conflict on master")
+					.setCommitter(new PersonIdent("gitter", "")).call();
+			MergeResult result = git.merge()
+					.include("side", side).call();
+			assertTrue("Expected conflict on 'file'",
+					result.getConflicts().containsKey("file"));
+		}
+		String[] expected = {
+				"00000000 (Not Committed Yet 2009-08-15 20:12:58 -0330 1) <<<<<<< HEAD",
+				"0f5b671c (gitter            2009-08-15 20:12:58 -0330 2) Change on master branch",
+				"00000000 (Not Committed Yet 2009-08-15 20:12:58 -0330 3) =======",
+				"ae78cff6 (gitter            2009-08-15 20:12:58 -0330 4) Conflicting change from side branch",
+				"00000000 (Not Committed Yet 2009-08-15 20:12:58 -0330 5) >>>>>>> side" };
+		assertArrayOfLinesEquals(expected, execute("git blame file"));
+	}
 }
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
index c31f28c..0ecbd65 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
@@ -44,6 +44,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
@@ -54,7 +55,13 @@
 import org.eclipse.jgit.junit.MockSystemReader;
 import org.eclipse.jgit.lib.CLIRepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.transport.URIish;
 import org.eclipse.jgit.util.SystemReader;
 import org.junit.Before;
@@ -90,10 +97,10 @@
 		assertEquals("expected 1 branch", 1, branches.size());
 	}
 
-	private void createInitialCommit() throws Exception {
+	private RevCommit createInitialCommit() throws Exception {
 		JGitTestUtil.writeTrashFile(db, "hello.txt", "world");
 		git.add().addFilepattern("hello.txt").call();
-		git.commit().setMessage("Initial commit").call();
+		return git.commit().setMessage("Initial commit").call();
 	}
 
 	@Test
@@ -154,4 +161,37 @@
 		assertEquals("expected 1 branch", 1, branches.size());
 		assertTrue("expected bare repository", git2.getRepository().isBare());
 	}
+
+	@Test
+	public void testCloneMirror() throws Exception {
+		ObjectId head = createInitialCommit();
+		// create a non-standard ref
+		RefUpdate ru = db.updateRef("refs/meta/foo/bar");
+		ru.setNewObjectId(head);
+		ru.update();
+
+		File gitDir = db.getDirectory();
+		String sourcePath = gitDir.getAbsolutePath();
+		String targetPath = (new File(sourcePath)).getParentFile()
+				.getParentFile().getAbsolutePath() + File.separator
+				+ "target.git";
+		String cmd = "git clone --mirror " + shellQuote(sourcePath) + " "
+				+ shellQuote(targetPath);
+		String[] result = execute(cmd);
+		assertArrayEquals(
+				new String[] { "Cloning into '" + targetPath + "'...", "", "" },
+				result);
+		Git git2 = Git.open(new File(targetPath));
+		List<Ref> branches = git2.branchList().call();
+		assertEquals("expected 1 branch", 1, branches.size());
+		assertTrue("expected bare repository", git2.getRepository().isBare());
+		StoredConfig config = git2.getRepository().getConfig();
+		RemoteConfig rc = new RemoteConfig(config, "origin");
+		assertTrue("expected mirror configuration", rc.isMirror());
+		RefSpec fetchRefSpec = rc.getFetchRefSpecs().get(0);
+		assertTrue("exected force udpate", fetchRefSpec.isForceUpdate());
+		assertEquals("refs/*", fetchRefSpec.getSource());
+		assertEquals("refs/*", fetchRefSpec.getDestination());
+		assertNotNull(git2.getRepository().exactRef("refs/meta/foo/bar"));
+	}
 }
diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
index ef6f5e7..bc7ba1e 100644
--- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 0a9074a..9c822cd 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.pgm
 Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 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.5.2,5.6.0)",
- org.eclipse.jgit.api.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.archive;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.awtui;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.blame;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.diff;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.dircache;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.gitrepo;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.ketch;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.server;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs.server.s3;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.merge;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.notes;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revplot;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.pack;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.resolver;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.sshd;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util.io;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.api;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.api.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.archive;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.awtui;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.blame;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.diff;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.dircache;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.gitrepo;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.ketch;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.server;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.merge;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.notes;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revplot;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.pack;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util.io;version="[5.6.2,5.7.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.5.2";
+Export-Package: org.eclipse.jgit.console;version="5.6.2";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="5.5.2";
+ org.eclipse.jgit.pgm;version="5.6.2";
   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.5.2";
+ org.eclipse.jgit.pgm.debug;version="5.6.2";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="5.5.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="5.5.2";
+ org.eclipse.jgit.pgm.internal;version="5.6.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="5.6.2";
   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 1ce53a5..f324bb2 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.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index 39fbe2e..062b964 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -8,6 +8,7 @@
 org.eclipse.jgit.pgm.Clone
 org.eclipse.jgit.pgm.Commit
 org.eclipse.jgit.pgm.Config
+org.eclipse.jgit.pgm.ConvertRefStorage
 org.eclipse.jgit.pgm.Daemon
 org.eclipse.jgit.pgm.Describe
 org.eclipse.jgit.pgm.Diff
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index c7bcaf1..c18b7d1 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-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 f029442..02f0543 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
@@ -223,6 +223,9 @@
 usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time
 usage_AlwaysFallback=Show uniquely abbreviated commit object as fallback
 usage_bareClone=Make a bare Git repository. That is, instead of creating [DIRECTORY] and placing the administrative files in [DIRECTORY]/.git, make the [DIRECTORY] itself the $GIT_DIR.
+usage_mirrorClone=Set up a mirror of the source repository. This implies --bare. Compared to --bare, --mirror not only maps \
+local branches of the source to local branches of the target, it maps all refs (including remote-tracking branches, notes etc.) \
+and sets up a refspec configuration such that all these refs are overwritten by a git remote update in the target repository.
 usage_Blame=Show what revision and author last modified each line
 usage_Clean=Remove untracked files from the working tree
 usage_CommandLineClientForamazonsS3Service=Command line client for Amazon's S3 service
@@ -323,6 +326,8 @@
 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_convertRefStorage=Convert ref storage to reftable
+usage_convertRefStorageFormat=Format to convert to (reftable or refdir)
 usage_createBranchAndCheckout=create branch and check out
 usage_deleteBranchEvenIfNotMerged=delete branch (even if not merged)
 usage_deleteFullyMergedBranch=delete fully merged branch
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
index f8442fa..039d094 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
@@ -136,18 +136,15 @@
 			if (v != null) {
 				item.setValue(new String(v));
 				return true;
-			} else {
-				return false;
 			}
-		} else {
-			String v = cons.readLine("%s: ", item.getPromptText()); //$NON-NLS-1$
-			if (v != null) {
-				item.setValue(v);
-				return true;
-			} else {
-				return false;
-			}
+			return false;
 		}
+		String v = cons.readLine("%s: ", item.getPromptText()); //$NON-NLS-1$
+		if (v != null) {
+			item.setValue(v);
+			return true;
+		}
+		return false;
 	}
 
 	private boolean get(CredentialItem.CharArrayType item) {
@@ -156,18 +153,15 @@
 			if (v != null) {
 				item.setValueNoCopy(v);
 				return true;
-			} else {
-				return false;
 			}
-		} else {
-			String v = cons.readLine("%s: ", item.getPromptText()); //$NON-NLS-1$
-			if (v != null) {
-				item.setValueNoCopy(v.toCharArray());
-				return true;
-			} else {
-				return false;
-			}
+			return false;
 		}
+		String v = cons.readLine("%s: ", item.getPromptText()); //$NON-NLS-1$
+		if (v != null) {
+			item.setValueNoCopy(v.toCharArray());
+			return true;
+		}
+		return false;
 	}
 
 	private boolean get(CredentialItem.InformationalMessage item) {
@@ -182,8 +176,7 @@
 		if (r != null) {
 			item.setValue(CLIText.get().answerYes.equalsIgnoreCase(r));
 			return true;
-		} else {
-			return false;
 		}
+		return false;
 	}
 }
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 b67b04c..1a4c111 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
@@ -50,7 +50,6 @@
 import static java.lang.Long.valueOf;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
 
-import java.io.File;
 import java.io.IOException;
 import java.text.MessageFormat;
 import java.text.SimpleDateFormat;
@@ -60,11 +59,11 @@
 import java.util.Map;
 import java.util.regex.Pattern;
 
+import org.eclipse.jgit.api.errors.NoHeadException;
 import org.eclipse.jgit.blame.BlameGenerator;
 import org.eclipse.jgit.blame.BlameResult;
 import org.eclipse.jgit.diff.RawText;
 import org.eclipse.jgit.diff.RawTextComparator;
-import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -134,6 +133,9 @@
 
 	private BlameResult blame;
 
+	/** Used to get a current time stamp for lines without commit. */
+	private final PersonIdent dummyDate = new PersonIdent("", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
 	/** {@inheritDoc} */
 	@Override
 	protected void run() {
@@ -186,28 +188,7 @@
 				}
 				generator.push(null, rev);
 			} else {
-				ObjectId head = db.resolve(Constants.HEAD);
-				if (head == null) {
-					throw die(MessageFormat.format(CLIText.get().noSuchRef,
-							Constants.HEAD));
-				}
-				generator.push(null, head);
-				if (!db.isBare()) {
-					DirCache dc = db.readDirCache();
-					int entry = dc.findEntry(file);
-					if (0 <= entry) {
-						generator.push(null, dc.getEntry(entry).getObjectId());
-					} else {
-						throw die(MessageFormat.format(
-								CLIText.get().noSuchPathInRef, file,
-								Constants.HEAD));
-					}
-
-					File inTree = new File(db.getWorkTree(), file);
-					if (db.getFS().isFile(inTree)) {
-						generator.push(null, new RawText(inTree));
-					}
-				}
+				generator.prepareHead();
 			}
 
 			blame = BlameResult.create(generator);
@@ -236,6 +217,10 @@
 					authorWidth = Math.max(authorWidth, author(line).length());
 					dateWidth = Math.max(dateWidth, date(line).length());
 					pathWidth = Math.max(pathWidth, path(line).length());
+				} else if (c == null) {
+					authorWidth = Math.max(authorWidth, author(line).length());
+					dateWidth = Math.max(dateWidth, date(line).length());
+					pathWidth = Math.max(pathWidth, path(line).length());
 				}
 				while (line + 1 < end
 						&& sameCommit(blame.getSourceCommit(line + 1), c)) {
@@ -280,7 +265,7 @@
 				} while (++line < end
 						&& sameCommit(blame.getSourceCommit(line), c));
 			}
-		} catch (NoWorkTreeException | IOException e) {
+		} catch (NoWorkTreeException | NoHeadException | IOException e) {
 			throw die(e.getMessage(), e);
 		}
 	}
@@ -373,10 +358,12 @@
 	}
 
 	private String date(int line) {
-		if (blame.getSourceCommit(line) == null)
-			return ""; //$NON-NLS-1$
-
-		PersonIdent author = blame.getSourceAuthor(line);
+		PersonIdent author;
+		if (blame.getSourceCommit(line) == null) {
+			author = dummyDate;
+		} else {
+			author = blame.getSourceAuthor(line);
+		}
 		if (author == null)
 			return ""; //$NON-NLS-1$
 
@@ -394,28 +381,37 @@
 		if (r != null)
 			return r;
 
-		if (showBlankBoundary && commit.getParentCount() == 0)
-			commit = null;
-
 		if (commit == null) {
-			int len = showLongRevision ? OBJECT_ID_STRING_LENGTH : (abbrev + 1);
-			StringBuilder b = new StringBuilder(len);
-			for (int i = 0; i < len; i++)
-				b.append(' ');
-			r = b.toString();
-
-		} else if (!root && commit.getParentCount() == 0) {
-			if (showLongRevision)
-				r = "^" + commit.name().substring(0, OBJECT_ID_STRING_LENGTH - 1); //$NON-NLS-1$
-			else
-				r = "^" + reader.abbreviate(commit, abbrev).name(); //$NON-NLS-1$
+			if (showLongRevision) {
+				r = ObjectId.zeroId().name();
+			} else {
+				r = ObjectId.zeroId().abbreviate(abbrev + 1).name();
+			}
 		} else {
-			if (showLongRevision)
-				r = commit.name();
-			else
-				r = reader.abbreviate(commit, abbrev + 1).name();
-		}
+			if (showBlankBoundary && commit.getParentCount() == 0)
+				commit = null;
 
+			if (commit == null) {
+				int len = showLongRevision ? OBJECT_ID_STRING_LENGTH
+						: (abbrev + 1);
+				StringBuilder b = new StringBuilder(len);
+				for (int i = 0; i < len; i++)
+					b.append(' ');
+				r = b.toString();
+
+			} else if (!root && commit.getParentCount() == 0) {
+				if (showLongRevision)
+					r = "^" + commit.name().substring(0, //$NON-NLS-1$
+							OBJECT_ID_STRING_LENGTH - 1);
+				else
+					r = "^" + reader.abbreviate(commit, abbrev).name(); //$NON-NLS-1$
+			} else {
+				if (showLongRevision)
+					r = commit.name();
+				else
+					r = reader.abbreviate(commit, abbrev + 1).name();
+			}
+		}
 		abbreviatedCommits.put(commit, r);
 		return r;
 	}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
index fe24620..a6a031e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -74,6 +74,9 @@
 	@Option(name = "--bare", usage = "usage_bareClone")
 	private boolean isBare;
 
+	@Option(name = "--mirror", usage = "usage_mirrorClone")
+	private boolean isMirror;
+
 	@Option(name = "--quiet", usage = "usage_quiet")
 	private Boolean quiet;
 
@@ -103,7 +106,7 @@
 		if (localName == null) {
 			try {
 				localName = uri.getHumanishName();
-				if (isBare) {
+				if (isBare || isMirror) {
 					localName = localName + Constants.DOT_GIT_EXT;
 				}
 				localNameF = new File(SystemReader.getInstance().getProperty(
@@ -120,6 +123,7 @@
 
 		CloneCommand command = Git.cloneRepository();
 		command.setURI(sourceUri).setRemote(remoteName).setBare(isBare)
+				.setMirror(isMirror)
 				.setNoCheckout(noCheckout).setBranch(branch)
 				.setCloneSubmodules(cloneSubmodules);
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ConvertRefStorage.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ConvertRefStorage.java
new file mode 100644
index 0000000..2fd689b
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ConvertRefStorage.java
@@ -0,0 +1,60 @@
+/*
+ * 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.pgm;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.kohsuke.args4j.Option;
+
+@Command(common=true, usage="usage_convertRefStorage")
+class ConvertRefStorage extends TextBuiltin {
+
+    @Option(name = "--format", usage = "usage_convertRefStorageFormat")
+	private String format = "reftable"; //$NON-NLS-1$
+
+    /** {@inheritDoc} */
+    @Override
+    protected void run() throws Exception {
+        ((FileRepository) db).convertRefStorage(format, true, true);
+    }
+}
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 c3887fe..23ee933 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
@@ -306,8 +306,8 @@
 			}
 			outw.flush();
 			return list.size();
-		} else
-			return 0;
+		}
+		return 0;
 	}
 
 	/**
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java
index 248eaac..27a7347 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/BenchmarkReftable.java
@@ -51,14 +51,18 @@
 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.util.List;
 
+import org.eclipse.jgit.internal.storage.file.FileReftableStack;
 import org.eclipse.jgit.internal.storage.io.BlockSource;
 import org.eclipse.jgit.internal.storage.reftable.RefCursor;
 import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.Ref;
@@ -74,7 +78,8 @@
 	enum Test {
 		SCAN,
 		SEEK_COLD, SEEK_HOT,
-		BY_ID_COLD, BY_ID_HOT;
+		BY_ID_COLD, BY_ID_HOT,
+		WRITE_STACK,
 	}
 
 	@Option(name = "--tries")
@@ -116,6 +121,9 @@
 		case BY_ID_HOT:
 			byIdHot(ObjectId.fromString(objectId));
 			break;
+		case WRITE_STACK:
+			writeStack();
+			break;
 		}
 	}
 
@@ -124,6 +132,33 @@
 	}
 
 	@SuppressWarnings({ "nls", "boxing" })
+	private void writeStack() throws Exception {
+		File dir = new File(reftablePath);
+		File stackFile = new File(reftablePath + ".stack");
+
+		dir.mkdirs();
+
+		long start = System.currentTimeMillis();
+		try (FileReftableStack stack = new FileReftableStack(stackFile, dir,
+				null, () -> new Config())) {
+
+			List<Ref> refs = readLsRemote().asList();
+			for (Ref r : refs) {
+				final long j = stack.getMergedReftable().maxUpdateIndex() + 1;
+				if (!stack.addReftable(w -> {
+					w.setMaxUpdateIndex(j).setMinUpdateIndex(j).begin()
+							.writeRef(r);
+				})) {
+					throw new IOException("should succeed");
+				}
+			}
+			long dt = System.currentTimeMillis() - start;
+			printf("%12s %10d ms  avg %6d us/write", "reftable", dt,
+					(dt * 1000) / refs.size());
+		}
+	}
+
+	@SuppressWarnings({ "nls", "boxing" })
 	private void scan() throws Exception {
 		long start, tot;
 
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 85a7444..61cf2ab 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
@@ -350,7 +350,7 @@
 		return false;
 	}
 
-	private static abstract class Algorithm {
+	private abstract static class Algorithm {
 		String name;
 
 		abstract DiffAlgorithm create();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
index 973b0d7..fe203e3 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
@@ -161,11 +161,11 @@
 		}
 	}
 
-	private static enum StoreType {
+	private enum StoreType {
 		FS, S3;
 	}
 
-	private static enum StorageClass {
+	private enum StorageClass {
 		REDUCED_REDUNDANCY, STANDARD
 	}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 8948c27..0d30f36 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -189,9 +189,8 @@
 								// rewritten.
 								queue.add(t);
 								continue REWRITE;
-							} else {
-								newParents[k] = p.newId;
 							}
+							newParents[k] = p.newId;
 						} else {
 							// We have the old parent object. Use it.
 							//
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
index af56d65..d526a53 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
@@ -83,7 +83,7 @@
 		errw.println();
 	}
 
-	static enum Format {
+	enum Format {
 		/** */
 		USAGE {
 			@Override
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 a2ea8c2..73ea2a0 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
@@ -138,9 +138,8 @@
 				Arrays.fill(buf16, (byte) 0);
 				System.arraycopy(raw, ptr, buf16, 0, end - ptr);
 				return rabin(buf16, 0);
-			} else {
-				return rabin(raw, ptr);
 			}
+			return rabin(raw, ptr);
 		}
 
 		private int rabin(byte[] raw, int ptr) {
@@ -444,7 +443,7 @@
 	}
 
 	/** Base class for any hashCode function to be tested. */
-	private static abstract class Hash extends RawTextComparator {
+	private abstract static class Hash extends RawTextComparator {
 		String name;
 
 		@Override
@@ -454,7 +453,7 @@
 	}
 
 	/** Base class for any hashCode folding function to be tested. */
-	private static abstract class Fold {
+	private abstract static class Fold {
 		String name;
 
 		/**
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
index 6cbc1b0..0320ed1 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
@@ -128,9 +128,9 @@
 				cfg.setMaxIndexLevels(indexLevels);
 			}
 
-			ReftableWriter w = new ReftableWriter(cfg);
+			ReftableWriter w = new ReftableWriter(cfg, os);
 			w.setMinUpdateIndex(min(logs)).setMaxUpdateIndex(max(logs));
-			w.begin(os);
+			w.begin();
 			w.sortAndWriteRefs(refs);
 			for (LogEntry e : logs) {
 				w.writeLog(e.ref, e.updateIndex, e.who,
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
index b97aa5b..d3aa38c 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
@@ -299,10 +299,10 @@
 	/** {@inheritDoc} */
 	@Override
 	protected OptionHandler createOptionHandler(OptionDef o, Setter setter) {
-		if (o instanceof NamedOptionDef)
+		if (o instanceof NamedOptionDef) {
 			return super.createOptionHandler(o, setter);
-		else
-			return super.createOptionHandler(new MyOptionDef(o), setter);
+		}
+		return super.createOptionHandler(new MyOptionDef(o), setter);
 
 	}
 
diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
index 794592d..822846c 100644
--- a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
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 81969e7..e97058c 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test
 Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -15,15 +15,15 @@
  org.apache.sshd.common.session;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.eclipse.jgit.api.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit.ssh;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.ssh;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.sshd;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.api.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.ssh;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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 0f2504c..18196bf 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
index 13c32a6..15ef2aa 100644
--- a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 6a11b18..a04612f 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -6,9 +6,9 @@
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.5.2";x-internal:=true;
+Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.6.2";x-internal:=true;
   uses:="org.apache.sshd.client,
    org.apache.sshd.client.auth,
    org.apache.sshd.client.auth.keyboard,
@@ -23,9 +23,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.5.2";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="5.5.2";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="5.5.2";
+ org.eclipse.jgit.internal.transport.sshd.auth;version="5.6.2";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="5.6.2";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.transport.sshd;version="5.6.2";
   uses:="org.eclipse.jgit.transport,
    org.apache.sshd.client.config.hosts,
    org.apache.sshd.common.keyprovider,
@@ -75,12 +75,12 @@
  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.5.2,5.6.0)",
- org.eclipse.jgit.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.fnmatch;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.fnmatch;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.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 8991b5b..93c8e88 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.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index f412d63..19a5c28 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
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 a1ec318..9673ff0 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
@@ -121,7 +121,7 @@
 		if (cache == null) {
 			return loadKey(session, resource, path, getPasswordFinder());
 		}
-		Throwable t[] = { null };
+		Throwable[] t = { null };
 		KeyPair key = cache.get(path, p -> {
 			try {
 				return loadKey(session, resource, p, getPasswordFinder());
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 1954abc..79bd3fa 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
@@ -179,9 +179,8 @@
 			} catch (Exception other) {
 				throw new IOException(other.getLocalizedMessage(), other);
 			}
-		} else {
-			return super.sendIdentification(ident);
 		}
+		return super.sendIdentification(ident);
 	}
 
 	@Override
@@ -205,9 +204,8 @@
 			} catch (Exception other) {
 				throw new IOException(other.getLocalizedMessage(), other);
 			}
-		} else {
-			return super.sendKexInit();
 		}
+		return super.sendKexInit();
 	}
 
 	/**
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
index f4849ce..9c0a793 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
@@ -453,9 +453,8 @@
 						prompt);
 				items.add(answer);
 				return provider.get(uri, items) && answer.getValue();
-			} else {
-				return provider.get(uri, items);
 			}
+			return provider.get(uri, items);
 		}
 
 		private Check checkMode(SocketAddress remoteAddress, boolean changed) {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
index b9b32b1..af8bf47 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
@@ -207,11 +207,10 @@
 						next++;
 					}
 					return next;
-				} else {
-					// This token must be the name of the next authentication
-					// scheme.
-					return start;
 				}
+				// This token must be the name of the next authentication
+				// scheme.
+				return start;
 			}
 			int nextStart = skipWhiteSpace(header, next + 1);
 			if (nextStart >= length) {
@@ -244,14 +243,13 @@
 					// token, and the equals sign is part of the token
 					challenge.setToken(header.substring(start, end + 1));
 					return nextStart + 1;
-				} else {
-					// Key without value...
-					challenge.addArgument(header.substring(start, end), null);
-					start = nextStart + 1;
 				}
+				// Key without value...
+				challenge.addArgument(header.substring(start, end), null);
+				start = nextStart + 1;
 			} else {
 				if (header.charAt(nextStart) == '"') {
-					int nextEnd[] = { nextStart + 1 };
+					int[] nextEnd = { nextStart + 1 };
 					String value = scanQuotedString(header, nextStart + 1,
 							nextEnd);
 					challenge.addArgument(header.substring(start, end), value);
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
index 27d6f41..b3681d1 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
@@ -281,11 +281,10 @@
 		}
 		if (i == proposals.length) {
 			return proposals;
-		} else {
-			byte[] result = new byte[i];
-			System.arraycopy(proposals, 0, result, 0, i);
-			return result;
 		}
+		byte[] result = new byte[i];
+		System.arraycopy(proposals, 0, result, 0, i);
+		return result;
 	}
 
 	private void sendConnectInfo(IoSession session) throws Exception {
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 1e8d7d1..3fc955a 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
@@ -354,9 +354,8 @@
 			if (path.charAt(0) != '/') {
 				if (cwd.charAt(cwd.length() - 1) == '/') {
 					return cwd + path;
-				} else {
-					return cwd + '/' + path;
 				}
+				return cwd + '/' + path;
 			}
 			return path;
 		}
diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
index d4751b5..4c4634a 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index 3f664f9..3d48796 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -24,6 +24,7 @@
     "revwalk/RevQueueTestCase.java",
     "revwalk/RevWalkTestCase.java",
     "transport/ObjectIdMatcher.java",
+    "transport/RequestValidatorTestCase.java",
     "transport/SpiTransport.java",
     "treewalk/filter/AlwaysCloneTreeFilter.java",
     "test/resources/SampleDataRepositoryTestCase.java",
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 42c9c57..5b8ce44 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.test
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -18,57 +18,58 @@
  org.apache.commons.compress.compressors.gzip;version="[1.15.0,2.0)",
  org.apache.commons.compress.compressors.xz;version="[1.15.0,2.0)",
  org.bouncycastle.util.encoders;version="[1.61.0,2.0.0)",
- org.eclipse.jgit.annotations;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.api;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.api.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.archive;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.attributes;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.awtui;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.blame;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.diff;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.dircache;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.events;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.fnmatch;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.gitrepo;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.hooks;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.ignore;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.ignore.internal;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.fsck;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.transport.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.internal.transport.parser;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit.ssh;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.junit.time;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lfs;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.merge;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.notes;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.patch;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.pgm;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.pgm.internal;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revplot;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.file;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.storage.pack;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.submodule;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.http;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport.resolver;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util.io;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util.sha1;version="[5.5.2,5.6.0)",
+ org.eclipse.jgit.annotations;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.api;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.api.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.archive;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.attributes;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.awtui;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.blame;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.diff;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.dircache;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.events;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.fnmatch;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.gitrepo;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.hooks;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.ignore;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.ignore.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.fsck;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.transport.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.junit.time;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lfs;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.merge;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.notes;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.patch;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.pgm;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revplot;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.file;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.storage.pack;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.submodule;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.http;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util.io;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util.sha1;version="[5.6.2,5.7.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)",
@@ -83,4 +84,4 @@
  org.tukaani.xz;version="[1.6.0,2.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.5.2";x-friends:="org.eclipse.jgit.ssh.apache.test"
+Export-Package: org.eclipse.jgit.transport.ssh;version="5.6.2";x-friends:="org.eclipse.jgit.ssh.apache.test"
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
index f597f2f..6a006e2 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
@@ -185,7 +185,7 @@
 		}
 	}
 
-	static abstract class CommitReader {
+	abstract static class CommitReader {
 		private Process proc;
 
 		CommitReader(String[] args) throws IOException {
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 91c3418..cb04748 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java
index 228df35..e1f6b12 100644
--- a/org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/events/ChangeRecorder.java
@@ -43,7 +43,6 @@
 
 package org.eclipse.jgit.events;
 
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
 import java.util.Arrays;
@@ -99,10 +98,12 @@
 		Arrays.sort(expectedModified);
 		Arrays.sort(actuallyDeleted);
 		Arrays.sort(expectedDeleted);
-		assertArrayEquals("Unexpected modifications reported", expectedModified,
-				actuallyModified);
-		assertArrayEquals("Unexpected deletions reported", expectedDeleted,
-				actuallyDeleted);
+		assertEquals("Unexpected modifications reported",
+				Arrays.toString(expectedModified),
+				Arrays.toString(actuallyModified));
+		assertEquals("Unexpected deletions reported",
+				Arrays.toString(expectedDeleted),
+				Arrays.toString(actuallyDeleted));
 		reset();
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index b28e26a..52a9dfa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -700,26 +700,26 @@
 			writer.print("content b");
 		}
 
-		ObjectInserter newObjectInserter = db.newObjectInserter();
 		DirCache dc = db.lockDirCache();
-		DirCacheBuilder builder = dc.builder();
+		try (ObjectInserter newObjectInserter = db.newObjectInserter()) {
+			DirCacheBuilder builder = dc.builder();
 
-		addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0);
-		addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1);
+			addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0);
+			addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1);
 
-		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
-			writer.print("other content");
+			try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
+				writer.print("other content");
+			}
+			addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3);
+
+			try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
+				writer.print("our content");
+			}
+			addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2)
+					.getObjectId();
+
+			builder.commit();
 		}
-		addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3);
-
-		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
-			writer.print("our content");
-		}
-		addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2)
-				.getObjectId();
-
-		builder.commit();
-
 		assertEquals(
 				"[a.txt, mode:100644, stage:1, content:content]" +
 				"[a.txt, mode:100644, stage:2, content:our content]" +
@@ -1132,41 +1132,42 @@
 			}
 		};
 
-		Git git = Git.open(db.getDirectory(), executableFs);
 		String path = "a.txt";
 		String path2 = "a.sh";
 		writeTrashFile(path, "content");
 		writeTrashFile(path2, "binary: content");
-		git.add().addFilepattern(path).addFilepattern(path2).call();
-		RevCommit commit1 = git.commit().setMessage("commit").call();
-		try (TreeWalk walk = new TreeWalk(db)) {
-			walk.addTree(commit1.getTree());
-			walk.next();
-			assertEquals(path2, walk.getPathString());
-			assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
-			walk.next();
-			assertEquals(path, walk.getPathString());
-			assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
+		try (Git git = Git.open(db.getDirectory(), executableFs)) {
+			git.add().addFilepattern(path).addFilepattern(path2).call();
+			RevCommit commit1 = git.commit().setMessage("commit").call();
+			try (TreeWalk walk = new TreeWalk(db)) {
+				walk.addTree(commit1.getTree());
+				walk.next();
+				assertEquals(path2, walk.getPathString());
+				assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
+				walk.next();
+				assertEquals(path, walk.getPathString());
+				assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
+			}
 		}
-
 		config = db.getConfig();
 		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_FILEMODE, false);
 		config.save();
 
-		Git git2 = Git.open(db.getDirectory(), executableFs);
 		writeTrashFile(path2, "content2");
 		writeTrashFile(path, "binary: content2");
-		git2.add().addFilepattern(path).addFilepattern(path2).call();
-		RevCommit commit2 = git2.commit().setMessage("commit2").call();
-		try (TreeWalk walk = new TreeWalk(db)) {
-			walk.addTree(commit2.getTree());
-			walk.next();
-			assertEquals(path2, walk.getPathString());
-			assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
-			walk.next();
-			assertEquals(path, walk.getPathString());
-			assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
+		try (Git git2 = Git.open(db.getDirectory(), executableFs)) {
+			git2.add().addFilepattern(path).addFilepattern(path2).call();
+			RevCommit commit2 = git2.commit().setMessage("commit2").call();
+			try (TreeWalk walk = new TreeWalk(db)) {
+				walk.addTree(commit2.getTree());
+				walk.next();
+				assertEquals(path2, walk.getPathString());
+				assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
+				walk.next();
+				assertEquals(path, walk.getPathString());
+				assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
+			}
 		}
 	}
 
@@ -1291,18 +1292,19 @@
 		FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder();
 		nestedBuilder.setWorkTree(gitLinkDir);
 
-		Repository nestedRepo = nestedBuilder.build();
-		nestedRepo.create();
+		try (Repository nestedRepo = nestedBuilder.build()) {
+			nestedRepo.create();
 
-		writeTrashFile(path, "README1.md", "content");
-		writeTrashFile(path, "README2.md", "content");
+			writeTrashFile(path, "README1.md", "content");
+			writeTrashFile(path, "README2.md", "content");
 
-		// Commit these changes in the subrepo
-		try (Git git = new Git(nestedRepo)) {
-			git.add().addFilepattern(".").call();
-			git.commit().setMessage("subrepo commit").call();
-		} catch (GitAPIException e) {
-			throw new RuntimeException(e);
+			// Commit these changes in the subrepo
+			try (Git git = new Git(nestedRepo)) {
+				git.add().addFilepattern(".").call();
+				git.commit().setMessage("subrepo commit").call();
+			} catch (GitAPIException e) {
+				throw new RuntimeException(e);
+			}
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
index 7e73084..4d1375a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, GitHub Inc.
+ * Copyright (C) 2011, 2019 GitHub Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -44,6 +44,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
@@ -462,6 +463,39 @@
 	}
 
 	@Test
+	public void testUnresolvedMergeConflict() throws Exception {
+		try (Git git = new Git(db)) {
+			RevCommit base = commitFile("file.txt", "Origin\n", "master");
+
+			RevCommit master = commitFile("file.txt",
+					"Change on master branch\n", "master");
+
+			git.checkout().setName("side").setCreateBranch(true)
+					.setStartPoint(base).call();
+			RevCommit side = commitFile("file.txt",
+					"Conflicting change on side\n", "side");
+
+			checkoutBranch("refs/heads/master");
+			MergeResult result = git.merge().include(side).call();
+
+			// The merge results in a conflict, which we do not resolve
+			assertTrue("Expected a conflict",
+					result.getConflicts().containsKey("file.txt"));
+
+			BlameCommand command = new BlameCommand(db);
+			command.setFilePath("file.txt");
+			BlameResult lines = command.call();
+
+			assertEquals(5, lines.getResultContents().size());
+			assertNull(lines.getSourceCommit(0));
+			assertEquals(master, lines.getSourceCommit(1));
+			assertNull(lines.getSourceCommit(2));
+			assertEquals(side, lines.getSourceCommit(3));
+			assertNull(lines.getSourceCommit(4));
+		}
+	}
+
+	@Test
 	public void testWhitespaceMerge() throws Exception {
 		try (Git git = new Git(db)) {
 			RevCommit base = commitFile("file.txt", join("0", "1", "2"), "master");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
index fbc2cf6..76958f1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
@@ -58,6 +58,8 @@
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.api.errors.MultipleParentsNotAllowedException;
 import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.events.ChangeRecorder;
+import org.eclipse.jgit.events.ListenerHandle;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
@@ -325,6 +327,59 @@
 	}
 
 	@Test
+	public void testCherryPickConflictFiresModifiedEvent() throws Exception {
+		ListenerHandle listener = null;
+		try (Git git = new Git(db)) {
+			RevCommit sideCommit = prepareCherryPick(git);
+			ChangeRecorder recorder = new ChangeRecorder();
+			listener = db.getListenerList()
+					.addWorkingTreeModifiedListener(recorder);
+			CherryPickResult result = git.cherryPick()
+					.include(sideCommit.getId()).call();
+			assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
+			recorder.assertEvent(new String[] { "a" }, ChangeRecorder.EMPTY);
+		} finally {
+			if (listener != null) {
+				listener.remove();
+			}
+		}
+	}
+
+	@Test
+	public void testCherryPickNewFileFiresModifiedEvent() throws Exception {
+		ListenerHandle listener = null;
+		try (Git git = new Git(db)) {
+			writeTrashFile("test.txt", "a");
+			git.add().addFilepattern("test.txt").call();
+			git.commit().setMessage("commit1").call();
+			git.checkout().setCreateBranch(true).setName("a").call();
+
+			writeTrashFile("side.txt", "side");
+			git.add().addFilepattern("side.txt").call();
+			RevCommit side = git.commit().setMessage("side").call();
+			assertNotNull(side);
+
+			assertNotNull(git.checkout().setName(Constants.MASTER).call());
+			writeTrashFile("test.txt", "b");
+			assertNotNull(git.add().addFilepattern("test.txt").call());
+			assertNotNull(git.commit().setMessage("commit2").call());
+
+			ChangeRecorder recorder = new ChangeRecorder();
+			listener = db.getListenerList()
+					.addWorkingTreeModifiedListener(recorder);
+			CherryPickResult result = git.cherryPick()
+					.include(side.getId()).call();
+			assertEquals(CherryPickStatus.OK, result.getStatus());
+			recorder.assertEvent(new String[] { "side.txt" },
+					ChangeRecorder.EMPTY);
+		} finally {
+			if (listener != null) {
+				listener.remove();
+			}
+		}
+	}
+
+	@Test
 	public void testCherryPickOurCommitName() throws Exception {
 		try (Git git = new Git(db)) {
 			RevCommit sideCommit = prepareCherryPick(git);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index 2270a6a..c42cee3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -67,6 +67,7 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
@@ -96,10 +97,15 @@
 		writeTrashFile("Test.txt", "Hello world");
 		git.add().addFilepattern("Test.txt").call();
 		git.commit().setMessage("Initial commit").call();
-		git.tag().setName("tag-initial").setMessage("Tag initial").call();
+		Ref head = git.tag().setName("tag-initial").setMessage("Tag initial")
+				.call();
 
 		// create a test branch and switch to it
 		git.checkout().setCreateBranch(true).setName("test").call();
+		// create a non-standard ref
+		RefUpdate ru = db.updateRef("refs/meta/foo/bar");
+		ru.setNewObjectId(head.getObjectId());
+		ru.update();
 
 		// commit something on the test branch
 		writeTrashFile("Test.txt", "Some change");
@@ -397,7 +403,6 @@
 
 	@Test
 	public void testBareCloneRepositoryOnlyOneBranch() throws Exception {
-		// Same thing, but now test with bare repo
 		File directory = createTempDirectory(
 				"testCloneRepositoryWithBranch_bare");
 		CloneCommand command = Git.cloneRepository();
@@ -425,6 +430,33 @@
 	}
 
 	@Test
+	public void testBareCloneRepositoryMirror() throws Exception {
+		File directory = createTempDirectory(
+				"testCloneRepositoryWithBranch_mirror");
+		CloneCommand command = Git.cloneRepository();
+		command.setBranch("refs/heads/master");
+		command.setMirror(true); // implies bare repository
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+		assertNotNull(git2);
+		assertTrue(git2.getRepository().isBare());
+		assertNotNull(git2.getRepository().resolve("tag-for-blob"));
+		assertNotNull(git2.getRepository().resolve("tag-initial"));
+		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
+		assertEquals("refs/heads/master, refs/heads/test", allRefNames(
+				git2.branchList().setListMode(ListMode.ALL).call()));
+		assertNotNull(git2.getRepository().exactRef("refs/meta/foo/bar"));
+		RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
+				Constants.DEFAULT_REMOTE_NAME);
+		List<RefSpec> specs = cfg.getFetchRefSpecs();
+		assertEquals(1, specs.size());
+		assertEquals(new RefSpec("+refs/*:refs/*"),
+				specs.get(0));
+	}
+
+	@Test
 	public void testCloneRepositoryOnlyOneTag() throws Exception {
 		File directory = createTempDirectory("testCloneRepositoryWithBranch");
 		CloneCommand command = Git.cloneRepository();
@@ -450,6 +482,60 @@
 				specs.get(0));
 	}
 
+	@Test
+	public void testCloneRepositoryAllBranchesTakesPreference()
+			throws Exception {
+		File directory = createTempDirectory(
+				"testCloneRepositoryAllBranchesTakesPreference");
+		CloneCommand command = Git.cloneRepository();
+		command.setCloneAllBranches(true);
+		command.setBranchesToClone(
+				Collections.singletonList("refs/heads/test"));
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+		assertNotNull(git2);
+		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/test");
+		// Expect both remote branches to exist; setCloneAllBranches(true)
+		// should override any setBranchesToClone().
+		assertNotNull(
+				git2.getRepository().resolve("refs/remotes/origin/master"));
+		assertNotNull(git2.getRepository().resolve("refs/remotes/origin/test"));
+		RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
+				Constants.DEFAULT_REMOTE_NAME);
+		List<RefSpec> specs = cfg.getFetchRefSpecs();
+		assertEquals(1, specs.size());
+		assertEquals(new RefSpec("+refs/heads/*:refs/remotes/origin/*"),
+				specs.get(0));
+	}
+
+	@Test
+	public void testCloneRepositoryAllBranchesIndependent() throws Exception {
+		File directory = createTempDirectory(
+				"testCloneRepositoryAllBranchesIndependent");
+		CloneCommand command = Git.cloneRepository();
+		command.setCloneAllBranches(true);
+		command.setBranchesToClone(
+				Collections.singletonList("refs/heads/test"));
+		command.setCloneAllBranches(false);
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+		assertNotNull(git2);
+		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/test");
+		// Expect only the test branch; allBranches was re-set to false
+		assertNull(git2.getRepository().resolve("refs/remotes/origin/master"));
+		assertNotNull(git2.getRepository().resolve("refs/remotes/origin/test"));
+		RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
+				Constants.DEFAULT_REMOTE_NAME);
+		List<RefSpec> specs = cfg.getFetchRefSpecs();
+		assertEquals(1, specs.size());
+		assertEquals(new RefSpec("+refs/heads/test:refs/remotes/origin/test"),
+				specs.get(0));
+	}
+
 	public static String allRefNames(List<Ref> refs) {
 		StringBuilder sb = new StringBuilder();
 		for (Ref f : refs) {
@@ -646,25 +732,27 @@
 		assertEquals(sub1Head, pathStatus.getHeadId());
 		assertEquals(sub1Head, pathStatus.getIndexId());
 
-		SubmoduleWalk walk = SubmoduleWalk.forIndex(git2.getRepository());
-		assertTrue(walk.next());
-		try (Repository clonedSub1 = walk.getRepository()) {
-			assertNotNull(clonedSub1);
-			assertEquals(new File(git2.getRepository().getWorkTree(),
-					walk.getPath()), clonedSub1.getWorkTree());
-			assertEquals(
-					new File(new File(git2.getRepository().getDirectory(),
-							"modules"), walk.getPath()),
-					clonedSub1.getDirectory());
-			status = new SubmoduleStatusCommand(clonedSub1);
-			statuses = status.call();
+		try (SubmoduleWalk walk = SubmoduleWalk
+				.forIndex(git2.getRepository())) {
+			assertTrue(walk.next());
+			try (Repository clonedSub1 = walk.getRepository()) {
+				assertNotNull(clonedSub1);
+				assertEquals(new File(git2.getRepository().getWorkTree(),
+						walk.getPath()), clonedSub1.getWorkTree());
+				assertEquals(
+						new File(new File(git2.getRepository().getDirectory(),
+								"modules"), walk.getPath()),
+						clonedSub1.getDirectory());
+				status = new SubmoduleStatusCommand(clonedSub1);
+				statuses = status.call();
+			}
+			assertFalse(walk.next());
 		}
 		pathStatus = statuses.get(path);
 		assertNotNull(pathStatus);
 		assertEquals(SubmoduleStatusType.INITIALIZED, pathStatus.getType());
 		assertEquals(sub2Head, pathStatus.getHeadId());
 		assertEquals(sub2Head, pathStatus.getIndexId());
-		assertFalse(walk.next());
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
index c028ca3..2c51f7b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
@@ -40,6 +40,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+
 package org.eclipse.jgit.api;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -50,14 +51,10 @@
 import static org.junit.Assume.assumeFalse;
 
 import java.io.File;
-import java.io.IOException;
 import java.io.PrintWriter;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.api.errors.NoMessageException;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -76,13 +73,12 @@
  */
 public class CommitAndLogCommandTest extends RepositoryTestCase {
 	@Test
-	public void testSomeCommits() throws JGitInternalException, IOException,
-			GitAPIException {
-
+	public void testSomeCommits() throws Exception {
 		// do 4 commits
 		try (Git git = new Git(db)) {
 			git.commit().setMessage("initial commit").call();
-			git.commit().setMessage("second commit").setCommitter(committer).call();
+			git.commit().setMessage("second commit").setCommitter(committer)
+					.call();
 			git.commit().setMessage("third commit").setAuthor(author).call();
 			git.commit().setMessage("fourth commit").setAuthor(author)
 					.setCommitter(committer).call();
@@ -90,79 +86,28 @@
 
 			// check that all commits came in correctly
 			PersonIdent defaultCommitter = new PersonIdent(db);
-			PersonIdent expectedAuthors[] = new PersonIdent[] { defaultCommitter,
-					committer, author, author };
+			PersonIdent expectedAuthors[] = new PersonIdent[] {
+					defaultCommitter, committer, author, author };
 			PersonIdent expectedCommitters[] = new PersonIdent[] {
 					defaultCommitter, committer, defaultCommitter, committer };
 			String expectedMessages[] = new String[] { "initial commit",
 					"second commit", "third commit", "fourth commit" };
 			int l = expectedAuthors.length - 1;
 			for (RevCommit c : commits) {
-				assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
-						.getName());
-				assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
-						.getName());
+				assertEquals(expectedAuthors[l].getName(),
+						c.getAuthorIdent().getName());
+				assertEquals(expectedCommitters[l].getName(),
+						c.getCommitterIdent().getName());
 				assertEquals(c.getFullMessage(), expectedMessages[l]);
 				l--;
 			}
 			assertEquals(l, -1);
 			ReflogReader reader = db.getReflogReader(Constants.HEAD);
-			assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
+			assertTrue(
+					reader.getLastEntry().getComment().startsWith("commit:"));
 			reader = db.getReflogReader(db.getBranch());
-			assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
-		}
-	}
-
-	@Test
-	public void testLogWithFilter() throws IOException, JGitInternalException,
-			GitAPIException {
-
-		try (Git git = new Git(db)) {
-			// create first file
-			File file = new File(db.getWorkTree(), "a.txt");
-			FileUtils.createNewFile(file);
-			try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
-				writer.print("content1");
-			}
-
-			// First commit - a.txt file
-			git.add().addFilepattern("a.txt").call();
-			git.commit().setMessage("commit1").setCommitter(committer).call();
-
-			// create second file
-			file = new File(db.getWorkTree(), "b.txt");
-			FileUtils.createNewFile(file);
-			try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
-				writer.print("content2");
-			}
-
-			// Second commit - b.txt file
-			git.add().addFilepattern("b.txt").call();
-			git.commit().setMessage("commit2").setCommitter(committer).call();
-
-			// First log - a.txt filter
-			int count = 0;
-			for (RevCommit c : git.log().addPath("a.txt").call()) {
-				assertEquals("commit1", c.getFullMessage());
-				count++;
-			}
-			assertEquals(1, count);
-
-			// Second log - b.txt filter
-			count = 0;
-			for (RevCommit c : git.log().addPath("b.txt").call()) {
-				assertEquals("commit2", c.getFullMessage());
-				count++;
-			}
-			assertEquals(1, count);
-
-			// Third log - without filter
-			count = 0;
-			for (RevCommit c : git.log().call()) {
-				assertEquals(committer, c.getCommitterIdent());
-				count++;
-			}
-			assertEquals(2, count);
+			assertTrue(
+					reader.getLastEntry().getComment().startsWith("commit:"));
 		}
 	}
 
@@ -204,19 +149,20 @@
 	}
 
 	@Test
-	public void testMergeEmptyBranches() throws IOException,
-			JGitInternalException, GitAPIException {
+	public void testMergeEmptyBranches() throws Exception {
 		try (Git git = new Git(db)) {
 			git.commit().setMessage("initial commit").call();
 			RefUpdate r = db.updateRef("refs/heads/side");
 			r.setNewObjectId(db.resolve(Constants.HEAD));
 			assertEquals(r.forceUpdate(), RefUpdate.Result.NEW);
-			RevCommit second = git.commit().setMessage("second commit").setCommitter(committer).call();
+			RevCommit second = git.commit().setMessage("second commit")
+					.setCommitter(committer).call();
 			db.updateRef(Constants.HEAD).link("refs/heads/side");
-			RevCommit firstSide = git.commit().setMessage("first side commit").setAuthor(author).call();
+			RevCommit firstSide = git.commit().setMessage("first side commit")
+					.setAuthor(author).call();
 
-			write(new File(db.getDirectory(), Constants.MERGE_HEAD), ObjectId
-					.toString(db.resolve("refs/heads/master")));
+			write(new File(db.getDirectory(), Constants.MERGE_HEAD),
+					ObjectId.toString(db.resolve("refs/heads/master")));
 			write(new File(db.getDirectory(), Constants.MERGE_MSG), "merging");
 
 			RevCommit commit = git.commit().call();
@@ -228,8 +174,7 @@
 	}
 
 	@Test
-	public void testAddUnstagedChanges() throws IOException,
-			JGitInternalException, GitAPIException {
+	public void testAddUnstagedChanges() throws Exception {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
 		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
@@ -260,7 +205,7 @@
 	}
 
 	@Test
-	public void testModeChange() throws IOException, GitAPIException {
+	public void testModeChange() throws Exception {
 		assumeFalse(System.getProperty("os.name").startsWith("Windows"));// SKIP
 		try (Git git = new Git(db)) {
 			// create file
@@ -278,7 +223,8 @@
 			FS fs = db.getFS();
 			fs.setExecute(file, true);
 			git.add().addFilepattern("a.txt").call();
-			git.commit().setMessage("mode change").setCommitter(committer).call();
+			git.commit().setMessage("mode change").setCommitter(committer)
+					.call();
 
 			// pure mode change should be committable with -o option
 			fs.setExecute(file, false);
@@ -289,34 +235,32 @@
 	}
 
 	@Test
-	public void testCommitRange() throws GitAPIException,
-			JGitInternalException, MissingObjectException,
-			IncorrectObjectTypeException {
+	public void testCommitRange() throws Exception {
 		// do 4 commits and set the range to the second and fourth one
 		try (Git git = new Git(db)) {
 			git.commit().setMessage("first commit").call();
 			RevCommit second = git.commit().setMessage("second commit")
 					.setCommitter(committer).call();
 			git.commit().setMessage("third commit").setAuthor(author).call();
-			RevCommit last = git.commit().setMessage("fourth commit").setAuthor(
-					author)
-					.setCommitter(committer).call();
-			Iterable<RevCommit> commits = git.log().addRange(second.getId(),
-					last.getId()).call();
+			RevCommit last = git.commit().setMessage("fourth commit")
+					.setAuthor(author).setCommitter(committer).call();
+			Iterable<RevCommit> commits = git.log()
+					.addRange(second.getId(), last.getId()).call();
 
 			// check that we have the third and fourth commit
 			PersonIdent defaultCommitter = new PersonIdent(db);
-			PersonIdent expectedAuthors[] = new PersonIdent[] { author, author };
+			PersonIdent expectedAuthors[] = new PersonIdent[] { author,
+					author };
 			PersonIdent expectedCommitters[] = new PersonIdent[] {
 					defaultCommitter, committer };
 			String expectedMessages[] = new String[] { "third commit",
 					"fourth commit" };
 			int l = expectedAuthors.length - 1;
 			for (RevCommit c : commits) {
-				assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
-						.getName());
-				assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
-						.getName());
+				assertEquals(expectedAuthors[l].getName(),
+						c.getAuthorIdent().getName());
+				assertEquals(expectedCommitters[l].getName(),
+						c.getCommitterIdent().getName());
 				assertEquals(c.getFullMessage(), expectedMessages[l]);
 				l--;
 			}
@@ -325,8 +269,7 @@
 	}
 
 	@Test
-	public void testCommitAmend() throws JGitInternalException, IOException,
-			GitAPIException {
+	public void testCommitAmend() throws Exception {
 		try (Git git = new Git(db)) {
 			git.commit().setMessage("first comit").call(); // typo
 			git.commit().setAmend(true).setMessage("first commit").call();
@@ -348,15 +291,14 @@
 	}
 
 	@Test
-	public void testInsertChangeId() throws JGitInternalException,
-			GitAPIException {
+	public void testInsertChangeId() throws Exception {
 		try (Git git = new Git(db)) {
 			String messageHeader = "Some header line\n\nSome detail explanation\n";
 			String changeIdTemplate = "\nChange-Id: I"
 					+ ObjectId.zeroId().getName() + "\n";
 			String messageFooter = "Some foooter lines\nAnother footer line\n";
-			RevCommit commit = git.commit().setMessage(
-					messageHeader + messageFooter)
+			RevCommit commit = git.commit()
+					.setMessage(messageHeader + messageFooter)
 					.setInsertChangeId(true).call();
 			// we should find a real change id (at the end of the file)
 			byte[] chars = commit.getFullMessage().getBytes(UTF_8);
@@ -364,11 +306,12 @@
 			String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1,
 					chars.length);
 			assertTrue(lastLine.contains("Change-Id:"));
-			assertFalse(lastLine.contains(
-					"Change-Id: I" + ObjectId.zeroId().getName()));
+			assertFalse(lastLine
+					.contains("Change-Id: I" + ObjectId.zeroId().getName()));
 
-			commit = git.commit().setMessage(
-					messageHeader + changeIdTemplate + messageFooter)
+			commit = git.commit()
+					.setMessage(
+							messageHeader + changeIdTemplate + messageFooter)
 					.setInsertChangeId(true).call();
 			// we should find a real change id (in the line as dictated by the
 			// template)
@@ -383,11 +326,12 @@
 			String line = RawParseUtils.decode(chars, lineStart, lineEnd);
 
 			assertTrue(line.contains("Change-Id:"));
-			assertFalse(line.contains(
-					"Change-Id: I" + ObjectId.zeroId().getName()));
+			assertFalse(line
+					.contains("Change-Id: I" + ObjectId.zeroId().getName()));
 
-			commit = git.commit().setMessage(
-					messageHeader + changeIdTemplate + messageFooter)
+			commit = git.commit()
+					.setMessage(
+							messageHeader + changeIdTemplate + messageFooter)
 					.setInsertChangeId(false).call();
 			// we should find the untouched template
 			chars = commit.getFullMessage().getBytes(UTF_8);
@@ -400,8 +344,8 @@
 
 			line = RawParseUtils.decode(chars, lineStart, lineEnd);
 
-			assertTrue(commit.getFullMessage().contains(
-					"Change-Id: I" + ObjectId.zeroId().getName()));
+			assertTrue(commit.getFullMessage()
+					.contains("Change-Id: I" + ObjectId.zeroId().getName()));
 		}
 	}
 }
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 3bde0eb..b774921 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
@@ -49,6 +49,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import java.io.File;
 import java.util.Date;
@@ -148,9 +149,10 @@
 		writeTrashFile(path, "content");
 		git.add().addFilepattern(path).call();
 		RevCommit commit1 = git.commit().setMessage("commit").call();
-		TreeWalk walk = TreeWalk.forPath(db, path, commit1.getTree());
-		assertNotNull(walk);
-		assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
+		try (TreeWalk walk = TreeWalk.forPath(db, path, commit1.getTree())) {
+			assertNotNull(walk);
+			assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
+		}
 
 		FS nonExecutableFs = new FS() {
 
@@ -204,9 +206,10 @@
 		writeTrashFile(path, "content2");
 		RevCommit commit2 = git2.commit().setOnly(path).setMessage("commit2")
 				.call();
-		walk = TreeWalk.forPath(db, path, commit2.getTree());
-		assertNotNull(walk);
-		assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
+		try (TreeWalk walk = TreeWalk.forPath(db, path, commit2.getTree())) {
+			assertNotNull(walk);
+			assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
+		}
 	}
 
 	@Test
@@ -225,15 +228,16 @@
 			assertNotNull(repo);
 			addRepoToClose(repo);
 
-			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-			assertTrue(generator.next());
-			assertEquals(path, generator.getPath());
-			assertEquals(commit, generator.getObjectId());
-			assertEquals(uri, generator.getModulesUrl());
-			assertEquals(path, generator.getModulesPath());
-			assertEquals(uri, generator.getConfigUrl());
-			try (Repository subModRepo = generator.getRepository()) {
-				assertNotNull(subModRepo);
+			try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+				assertTrue(generator.next());
+				assertEquals(path, generator.getPath());
+				assertEquals(commit, generator.getObjectId());
+				assertEquals(uri, generator.getModulesUrl());
+				assertEquals(path, generator.getModulesPath());
+				assertEquals(uri, generator.getConfigUrl());
+				try (Repository subModRepo = generator.getRepository()) {
+					assertNotNull(subModRepo);
+				}
 			}
 			assertEquals(commit, repo.resolve(Constants.HEAD));
 
@@ -275,15 +279,16 @@
 			assertNotNull(repo);
 			addRepoToClose(repo);
 
-			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-			assertTrue(generator.next());
-			assertEquals(path, generator.getPath());
-			assertEquals(commit2, generator.getObjectId());
-			assertEquals(uri, generator.getModulesUrl());
-			assertEquals(path, generator.getModulesPath());
-			assertEquals(uri, generator.getConfigUrl());
-			try (Repository subModRepo = generator.getRepository()) {
-				assertNotNull(subModRepo);
+			try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+				assertTrue(generator.next());
+				assertEquals(path, generator.getPath());
+				assertEquals(commit2, generator.getObjectId());
+				assertEquals(uri, generator.getModulesUrl());
+				assertEquals(path, generator.getModulesPath());
+				assertEquals(uri, generator.getConfigUrl());
+				try (Repository subModRepo = generator.getRepository()) {
+					assertNotNull(subModRepo);
+				}
 			}
 			assertEquals(commit2, repo.resolve(Constants.HEAD));
 
@@ -629,41 +634,64 @@
 		}
 	}
 
-	@Test
-	public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception {
+	private void nonNormalizedIndexTest(boolean executable) throws Exception {
+		String mode = executable ? "100755" : "100644";
 		try (Git git = new Git(db)) {
 			// Commit a file with CR/LF into the index
 			FileBasedConfig config = db.getConfig();
 			config.setString("core", null, "autocrlf", "false");
 			config.save();
-			writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
+			File testFile = writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
+			if (executable) {
+				FS.DETECTED.setExecute(testFile, true);
+			}
 			git.add().addFilepattern("file.txt").call();
 			git.commit().setMessage("Initial").call();
 			assertEquals(
-					"[file.txt, mode:100644, content:line 1\r\nline 2\r\n]",
+					"[file.txt, mode:" + mode
+							+ ", content:line 1\r\nline 2\r\n]",
 					indexState(CONTENT));
 			config.setString("core", null, "autocrlf", "true");
 			config.save();
 			writeTrashFile("file.txt", "line 1\r\nline 1.5\r\nline 2\r\n");
-			writeTrashFile("file2.txt", "new\r\nfile\r\n");
+			testFile = writeTrashFile("file2.txt", "new\r\nfile\r\n");
+			if (executable) {
+				FS.DETECTED.setExecute(testFile, true);
+			}
 			git.add().addFilepattern("file.txt").addFilepattern("file2.txt")
 					.call();
 			git.commit().setMessage("Second").call();
 			assertEquals(
-					"[file.txt, mode:100644, content:line 1\r\nline 1.5\r\nline 2\r\n]"
-							+ "[file2.txt, mode:100644, content:new\nfile\n]",
+					"[file.txt, mode:" + mode
+							+ ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
+							+ "[file2.txt, mode:" + mode
+							+ ", content:new\nfile\n]",
 					indexState(CONTENT));
 			writeTrashFile("file2.txt", "new\r\nfile\r\ncontent\r\n");
 			git.add().addFilepattern("file2.txt").call();
 			git.commit().setMessage("Third").call();
 			assertEquals(
-					"[file.txt, mode:100644, content:line 1\r\nline 1.5\r\nline 2\r\n]"
-							+ "[file2.txt, mode:100644, content:new\nfile\ncontent\n]",
+					"[file.txt, mode:" + mode
+							+ ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
+							+ "[file2.txt, mode:" + mode
+							+ ", content:new\nfile\ncontent\n]",
 					indexState(CONTENT));
 		}
 	}
 
 	@Test
+	public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception {
+		nonNormalizedIndexTest(false);
+	}
+
+	@Test
+	public void commitExecutableWithAutoCrlfAndNonNormalizedIndex()
+			throws Exception {
+		assumeTrue(FS.DETECTED.supportsExecute());
+		nonNormalizedIndexTest(true);
+	}
+
+	@Test
 	public void testDeletionConflictWithAutoCrlf() throws Exception {
 		try (Git git = new Git(db)) {
 			// Commit a file with CR/LF into the index
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
index 47806cb..d0dfd1a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
@@ -142,8 +142,6 @@
 
 	protected String CONTENT_MIXED;
 
-	private TreeWalk walk;
-
 	/** work tree root .gitattributes */
 	private File dotGitattributes;
 
@@ -689,27 +687,25 @@
 
 	private void collectRepositoryState() throws Exception {
 		dirCache = db.readDirCache();
-		walk = beginWalk();
-		if (dotGitattributes != null)
-			collectEntryContentAndAttributes(F, ".gitattributes", null);
-		collectEntryContentAndAttributes(F, fileCRLF.getName(), entryCRLF);
-		collectEntryContentAndAttributes(F, fileLF.getName(), entryLF);
-		collectEntryContentAndAttributes(F, fileMixed.getName(), entryMixed);
-		endWalk();
+		try (TreeWalk walk = new TreeWalk(db)) {
+			walk.addTree(new FileTreeIterator(db));
+			walk.addTree(new DirCacheIterator(db.readDirCache()));
+			if (dotGitattributes != null) {
+				collectEntryContentAndAttributes(walk, F, ".gitattributes",
+						null);
+			}
+			collectEntryContentAndAttributes(walk, F, fileCRLF.getName(),
+					entryCRLF);
+			collectEntryContentAndAttributes(walk, F, fileLF.getName(),
+					entryLF);
+			collectEntryContentAndAttributes(walk, F, fileMixed.getName(),
+					entryMixed);
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
-	private TreeWalk beginWalk() throws Exception {
-		TreeWalk newWalk = new TreeWalk(db);
-		newWalk.addTree(new FileTreeIterator(db));
-		newWalk.addTree(new DirCacheIterator(db.readDirCache()));
-		return newWalk;
-	}
-
-	private void endWalk() throws IOException {
-		assertFalse("Not all files tested", walk.next());
-	}
-
-	private void collectEntryContentAndAttributes(FileMode type, String pathName,
+	private void collectEntryContentAndAttributes(TreeWalk walk, FileMode type,
+			String pathName,
 			ActualEntry e) throws IOException {
 		assertTrue("walk has entry", walk.next());
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java
index 73a705b..08ded55 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java
@@ -343,19 +343,22 @@
 
 	private void assertSubmoduleFetchHeads(ObjectId expectedHead1,
 			ObjectId expectedHead2) throws Exception {
+		Object newHead1 = null;
+		ObjectId newHead2 = null;
 		try (SubmoduleWalk walk = SubmoduleWalk
 				.forIndex(git2.getRepository())) {
 			assertTrue(walk.next());
-			Repository r = walk.getRepository();
-			ObjectId newHead1 = r.resolve(Constants.FETCH_HEAD);
-			ObjectId newHead2;
-			try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
-				assertTrue(walk2.next());
-				newHead2 = walk2.getRepository().resolve(Constants.FETCH_HEAD);
+			try (Repository r = walk.getRepository()) {
+				newHead1 = r.resolve(Constants.FETCH_HEAD);
+				try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
+					assertTrue(walk2.next());
+					try (Repository r2 = walk2.getRepository()) {
+						newHead2 = r2.resolve(Constants.FETCH_HEAD);
+					}
+				}
 			}
-
-			assertEquals(expectedHead1, newHead1);
-			assertEquals(expectedHead2, newHead2);
 		}
+		assertEquals(expectedHead1, newHead1);
+		assertEquals(expectedHead2, newHead2);
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogFilterTest.java
new file mode 100644
index 0000000..988ca58
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogFilterTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2019, John Tipper <John_Tipper@hotmail.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.
+ */
+package org.eclipse.jgit.api;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Iterator;
+
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Testing the log command with include and exclude filters
+ */
+public class LogFilterTest extends RepositoryTestCase {
+	private Git git;
+
+	@Before
+	public void setup() throws Exception {
+		super.setUp();
+		git = new Git(db);
+
+		// create first file
+		File file = new File(db.getWorkTree(), "a.txt");
+		FileUtils.createNewFile(file);
+		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
+			writer.print("content1");
+		}
+
+		// First commit - a.txt file
+		git.add().addFilepattern("a.txt").call();
+		git.commit().setMessage("commit1").setCommitter(committer).call();
+
+		// create second file
+		file = new File(db.getWorkTree(), "b.txt");
+		FileUtils.createNewFile(file);
+		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
+			writer.print("content2");
+		}
+
+		// Second commit - b.txt file
+		git.add().addFilepattern("b.txt").call();
+		git.commit().setMessage("commit2").setCommitter(committer).call();
+
+		// create third file
+		Path includeSubdir = Paths.get(db.getWorkTree().toString(),
+				"subdir-include");
+		includeSubdir.toFile().mkdirs();
+		file = Paths.get(includeSubdir.toString(), "c.txt").toFile();
+		FileUtils.createNewFile(file);
+		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
+			writer.print("content3");
+		}
+
+		// Third commit - c.txt file
+		git.add().addFilepattern("subdir-include").call();
+		git.commit().setMessage("commit3").setCommitter(committer).call();
+
+		// create fourth file
+		Path excludeSubdir = Paths.get(db.getWorkTree().toString(),
+				"subdir-exclude");
+		excludeSubdir.toFile().mkdirs();
+		file = Paths.get(excludeSubdir.toString(), "d.txt").toFile();
+		FileUtils.createNewFile(file);
+		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
+			writer.print("content4");
+		}
+
+		// Fourth commit - d.txt file
+		git.add().addFilepattern("subdir-exclude").call();
+		git.commit().setMessage("commit4").setCommitter(committer).call();
+	}
+
+	@After
+	@Override
+	public void tearDown() throws Exception {
+		git.close();
+		super.tearDown();
+	}
+
+	@Test
+	public void testLogWithFilterCanDistinguishFilesByPath() throws Exception {
+		int count = 0;
+		for (RevCommit c : git.log().addPath("a.txt").call()) {
+			assertEquals("commit1", c.getFullMessage());
+			count++;
+		}
+		assertEquals(1, count);
+
+		count = 0;
+		for (RevCommit c : git.log().addPath("b.txt").call()) {
+			assertEquals("commit2", c.getFullMessage());
+			count++;
+		}
+		assertEquals(1, count);
+	}
+
+	@Test
+	public void testLogWithFilterCanIncludeFilesInDirectory() throws Exception {
+		int count = 0;
+		for (RevCommit c : git.log().addPath("subdir-include").call()) {
+			assertEquals("commit3", c.getFullMessage());
+			count++;
+		}
+		assertEquals(1, count);
+	}
+
+	@Test
+	public void testLogWithFilterCanExcludeFilesInDirectory() throws Exception {
+		int count = 0;
+		Iterator it = git.log().excludePath("subdir-exclude").call().iterator();
+		while (it.hasNext()) {
+			it.next();
+			count++;
+		}
+		// of all the commits, we expect to filter out only d.txt
+		assertEquals(3, count);
+	}
+
+	@Test
+	public void testLogWithoutFilter() throws Exception {
+		int count = 0;
+		for (RevCommit c : git.log().call()) {
+			assertEquals(committer, c.getCommitterIdent());
+			count++;
+		}
+		assertEquals(4, count);
+	}
+
+	@Test
+	public void testLogWithFilterCanExcludeAndIncludeFilesInDifferentDirectories()
+			throws Exception {
+		int count = 0;
+		Iterator it = git.log().addPath("subdir-include")
+				.excludePath("subdir-exclude").call().iterator();
+		while (it.hasNext()) {
+			it.next();
+			count++;
+		}
+		// we expect to include c.txt
+		assertEquals(1, count);
+	}
+
+	@Test
+	public void testLogWithFilterExcludeAndIncludeSameFileIncludesNothing()
+			throws Exception {
+		int count = 0;
+		Iterator it = git.log().addPath("subdir-exclude")
+				.excludePath("subdir-exclude").call().iterator();
+
+		while (it.hasNext()) {
+			it.next();
+			count++;
+		}
+		// we expect the exclude to trump everything
+		assertEquals(0, count);
+	}
+
+	@Test
+	public void testLogWithFilterCanExcludeFileAndDirectory() throws Exception {
+		int count = 0;
+		Iterator it = git.log().excludePath("b.txt")
+				.excludePath("subdir-exclude").call().iterator();
+
+		while (it.hasNext()) {
+			it.next();
+			count++;
+		}
+		// we expect a.txt and c.txt
+		assertEquals(2, count);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 4401bce..7c8ec23 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -57,12 +57,9 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
@@ -78,6 +75,7 @@
 import org.eclipse.jgit.errors.IllegalTodoFileModification;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.events.ChangeRecorder;
 import org.eclipse.jgit.events.ListenerHandle;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
@@ -2014,10 +2012,9 @@
 		checkoutBranch("refs/heads/topic");
 		writeTrashFile("sub/file0", "unstaged modified file0");
 
-		Set<String> modifiedFiles = new HashSet<>();
+		ChangeRecorder recorder = new ChangeRecorder();
 		ListenerHandle handle = db.getListenerList()
-				.addWorkingTreeModifiedListener(
-						event -> modifiedFiles.addAll(event.getModified()));
+				.addWorkingTreeModifiedListener(recorder);
 		try {
 			// rebase
 			assertEquals(Status.OK, git.rebase()
@@ -2035,9 +2032,8 @@
 						+ "[sub/file0, mode:100644, content:file0]",
 				indexState(CONTENT));
 		assertEquals(RepositoryState.SAFE, db.getRepositoryState());
-		List<String> modified = new ArrayList<>(modifiedFiles);
-		Collections.sort(modified);
-		assertEquals("[file1, sub/file0]", modified.toString());
+		recorder.assertEvent(new String[] { "file1", "file2", "sub/file0" },
+				new String[0]);
 	}
 
 	@Test
@@ -2136,10 +2132,12 @@
 	private List<DiffEntry> getStashedDiff() throws AmbiguousObjectException,
 			IncorrectObjectTypeException, IOException, MissingObjectException {
 		ObjectId stashId = db.resolve("stash@{0}");
-		RevWalk revWalk = new RevWalk(db);
-		RevCommit stashCommit = revWalk.parseCommit(stashId);
-		List<DiffEntry> diffs = diffWorkingAgainstHead(stashCommit, revWalk);
-		return diffs;
+		try (RevWalk revWalk = new RevWalk(db)) {
+			RevCommit stashCommit = revWalk.parseCommit(stashId);
+			List<DiffEntry> diffs = diffWorkingAgainstHead(stashCommit,
+					revWalk);
+			return diffs;
+		}
 	}
 
 	private TreeWalk createTreeWalk() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
index f3ac65c..18580ae 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
@@ -45,6 +45,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import java.io.File;
 import java.io.IOException;
@@ -54,6 +55,8 @@
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Sets;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
 import org.junit.Test;
 
 public class StatusCommandTest extends RepositoryTestCase {
@@ -168,4 +171,26 @@
 			assertEquals(Sets.of("a", "D/b", "D/D/d"), stat.getModified());
 		}
 	}
+
+	@Test
+	public void testExecutableWithNonNormalizedIndex() throws Exception {
+		assumeTrue(FS.DETECTED.supportsExecute());
+		try (Git git = new Git(db)) {
+			// Commit a file with CR/LF into the index
+			FileBasedConfig config = db.getConfig();
+			config.setString("core", null, "autocrlf", "false");
+			config.save();
+			File testFile = writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
+			FS.DETECTED.setExecute(testFile, true);
+			git.add().addFilepattern("file.txt").call();
+			git.commit().setMessage("Initial").call();
+			assertEquals(
+					"[file.txt, mode:100755, content:line 1\r\nline 2\r\n]",
+					indexState(CONTENT));
+			config.setString("core", null, "autocrlf", "true");
+			config.save();
+			Status status = git.status().call();
+			assertTrue("Expected no differences", status.isClean());
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
index 5868482..adc64d2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
@@ -69,83 +69,90 @@
 
 	private static final FileMode F = FileMode.REGULAR_FILE;
 
-	private TreeWalk walk;
-
 	@Test
 	public void testExpandNonMacro1() throws Exception {
 		setupRepo(null, null, null, "*.txt text");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("text"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("text"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testExpandNonMacro2() throws Exception {
 		setupRepo(null, null, null, "*.txt -text");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("-text"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("-text"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testExpandNonMacro3() throws Exception {
 		setupRepo(null, null, null, "*.txt !text");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs(""));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs(""));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testExpandNonMacro4() throws Exception {
 		setupRepo(null, null, null, "*.txt text=auto");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("text=auto"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("text=auto"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testExpandBuiltInMacro1() throws Exception {
 		setupRepo(null, null, null, "*.txt binary");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("binary -diff -merge -text"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt",
+					attrs("binary -diff -merge -text"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testExpandBuiltInMacro2() throws Exception {
 		setupRepo(null, null, null, "*.txt -binary");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("-binary diff merge text"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt",
+					attrs("-binary diff merge text"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testExpandBuiltInMacro3() throws Exception {
 		setupRepo(null, null, null, "*.txt !binary");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs(""));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs(""));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -153,44 +160,48 @@
 		setupRepo(
 				"[attr]foo a -b !c d=e", null, null, "*.txt foo");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("foo a -b d=e"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("foo a -b d=e"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testCustomGlobalMacro2() throws Exception {
 		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt -foo");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("-foo -a b d=e"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("-foo -a b d=e"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testCustomGlobalMacro3() throws Exception {
 		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt !foo");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs(""));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs(""));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testCustomGlobalMacro4() throws Exception {
 		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt foo=bar");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("foo=bar a -b d=bar"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("foo=bar a -b d=bar"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -198,11 +209,12 @@
 		setupRepo("[attr]foo bar1",
 				"[attr]foo bar2", null, "*.txt foo");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("foo bar2"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("foo bar2"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -211,12 +223,13 @@
 				null,
 				"[attr]foo bar3", "*.txt foo");
 
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("foo bar3"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("foo bar3"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -224,12 +237,13 @@
 		setupRepo("[attr]foo bar1",
 				"[attr]foo bar2", "[attr]foo bar3", "*.txt foo");
 
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("foo bar2"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("foo bar2"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -238,11 +252,12 @@
 				"[attr]foo x bar -foo",
 				null, null, "*.txt foo");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("foo x bar"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("foo x bar"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -250,11 +265,12 @@
 		setupRepo(
 				"[attr]foo x -bar\n[attr]bar y -foo", null, null, "*.txt foo");
 
-		walk = beginWalk();
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/.gitattributes");
-		assertIteration(F, "sub/a.txt", attrs("foo x -bar -y"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/.gitattributes");
+			assertIteration(walk, F, "sub/a.txt", attrs("foo x -bar -y"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -266,23 +282,30 @@
 		// apply to any of the files here. It would match for a
 		// further subdirectory sub/sub. The sub/ rules must match
 		// only for directories.
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "sub", attrs("global"));
-		assertIteration(F, "sub/.gitattributes", attrs("init top_sub"));
-		assertIteration(F, "sub/a.txt", attrs("init foo top top_sub"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "sub", attrs("global"));
+			assertIteration(walk, F, "sub/.gitattributes",
+					attrs("init top_sub"));
+			assertIteration(walk, F, "sub/a.txt",
+					attrs("init foo top top_sub"));
+			assertFalse("Not all files tested", walk.next());
+		}
 		// All right, let's see that they *do* apply in sub/sub:
 		writeTrashFile("sub/sub/b.txt", "b");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "sub", attrs("global"));
-		assertIteration(F, "sub/.gitattributes", attrs("init top_sub"));
-		assertIteration(F, "sub/a.txt", attrs("init foo top top_sub"));
-		assertIteration(D, "sub/sub", attrs("init subsub2 top_sub global"));
-		assertIteration(F, "sub/sub/b.txt",
-				attrs("init foo subsub top top_sub"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "sub", attrs("global"));
+			assertIteration(walk, F, "sub/.gitattributes",
+					attrs("init top_sub"));
+			assertIteration(walk, F, "sub/a.txt",
+					attrs("init foo top top_sub"));
+			assertIteration(walk, D, "sub/sub",
+					attrs("init subsub2 top_sub global"));
+			assertIteration(walk, F, "sub/sub/b.txt",
+					attrs("init foo subsub top top_sub"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -293,16 +316,17 @@
 		writeTrashFile("sub/b.jar", "bj");
 		writeTrashFile("sub/b.xml", "bx");
 		// On foo.xml/bar.jar we must not have 'xml'
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo.xml", attrs("xml"));
-		assertIteration(F, "foo.xml/bar.jar", attrs("jar"));
-		assertIteration(F, "foo.xml/bar.xml", attrs("xml"));
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(F, "sub/b.jar", attrs("jar"));
-		assertIteration(F, "sub/b.xml", attrs("xml"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo.xml", attrs("xml"));
+			assertIteration(walk, F, "foo.xml/bar.jar", attrs("jar"));
+			assertIteration(walk, F, "foo.xml/bar.xml", attrs("xml"));
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, F, "sub/b.jar", attrs("jar"));
+			assertIteration(walk, F, "sub/b.xml", attrs("xml"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -314,18 +338,19 @@
 		writeTrashFile("sub/b.jar", "bj");
 		writeTrashFile("sub/b.xml", "bx");
 		writeTrashFile("sub/foo/b.jar", "bf");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo", attrs("xml"));
-		assertIteration(F, "foo/bar.jar", attrs("jar"));
-		assertIteration(F, "foo/bar.xml");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(F, "sub/b.jar", attrs("jar"));
-		assertIteration(F, "sub/b.xml");
-		assertIteration(D, "sub/foo", attrs("sub xml"));
-		assertIteration(F, "sub/foo/b.jar", attrs("jar"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo", attrs("xml"));
+			assertIteration(walk, F, "foo/bar.jar", attrs("jar"));
+			assertIteration(walk, F, "foo/bar.xml");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, F, "sub/b.jar", attrs("jar"));
+			assertIteration(walk, F, "sub/b.xml");
+			assertIteration(walk, D, "sub/foo", attrs("sub xml"));
+			assertIteration(walk, F, "sub/foo/b.jar", attrs("jar"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -337,18 +362,19 @@
 		writeTrashFile("sub/b.xml", "bx");
 		writeTrashFile("sub/foo/b.jar", "bf");
 		// On foo.xml/bar.jar we must not have 'xml'
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(F, "foo/bar.jar", attrs("jar xml"));
-		assertIteration(F, "foo/bar.xml", attrs("xml"));
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(F, "sub/b.jar", attrs("jar"));
-		assertIteration(F, "sub/b.xml");
-		assertIteration(D, "sub/foo");
-		assertIteration(F, "sub/foo/b.jar", attrs("jar"));
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, F, "foo/bar.jar", attrs("jar xml"));
+			assertIteration(walk, F, "foo/bar.xml", attrs("xml"));
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, F, "sub/b.jar", attrs("jar"));
+			assertIteration(walk, F, "sub/b.xml");
+			assertIteration(walk, D, "sub/foo");
+			assertIteration(walk, F, "sub/foo/b.jar", attrs("jar"));
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -357,27 +383,29 @@
 		writeTrashFile("sub/a.txt", "1");
 		writeTrashFile("foo/sext", "2");
 		writeTrashFile("foo/s.txt", "3");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(F, "foo/s.txt", attrs("bar"));
-		assertIteration(F, "foo/sext", attrs("bar"));
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, F, "foo/s.txt", attrs("bar"));
+			assertIteration(walk, F, "foo/sext", attrs("bar"));
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
 	public void testPrefixMatchNot() throws Exception {
 		setupRepo(null, null, "sub/new bar", null);
 		writeTrashFile("sub/new/foo.txt", "1");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -385,14 +413,15 @@
 		setupRepo(null, null, "s[t-v]b/n[de]w bar", null);
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("sub/ndw", "2");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(F, "sub/ndw", attrs("bar"));
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, F, "sub/ndw", attrs("bar"));
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -400,15 +429,16 @@
 		setupRepo(null, null, "sub/new/* bar", null);
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("sub/new/lower/foo.txt", "2");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new");
-		assertIteration(F, "sub/new/foo.txt", attrs("bar"));
-		assertIteration(D, "sub/new/lower", attrs("bar"));
-		assertIteration(F, "sub/new/lower/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new");
+			assertIteration(walk, F, "sub/new/foo.txt", attrs("bar"));
+			assertIteration(walk, D, "sub/new/lower", attrs("bar"));
+			assertIteration(walk, F, "sub/new/lower/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -417,20 +447,21 @@
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("foo/sub/new/foo.txt", "2");
 		writeTrashFile("sub/sub/new/foo.txt", "3");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(D, "foo/sub");
-		assertIteration(D, "foo/sub/new");
-		assertIteration(F, "foo/sub/new/foo.txt");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		assertIteration(D, "sub/sub");
-		assertIteration(D, "sub/sub/new");
-		assertIteration(F, "sub/sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, D, "foo/sub");
+			assertIteration(walk, D, "foo/sub/new");
+			assertIteration(walk, F, "foo/sub/new/foo.txt");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertIteration(walk, D, "sub/sub");
+			assertIteration(walk, D, "sub/sub/new");
+			assertIteration(walk, F, "sub/sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -438,17 +469,18 @@
 		setupRepo(null, null, "**/sub/new/ bar", null);
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("foo/sub/new/foo.txt", "2");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(D, "foo/sub");
-		assertIteration(D, "foo/sub/new", attrs("bar"));
-		assertIteration(F, "foo/sub/new/foo.txt");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, D, "foo/sub");
+			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
+			assertIteration(walk, F, "foo/sub/new/foo.txt");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -457,20 +489,21 @@
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("foo/sub/new/foo.txt", "2");
 		writeTrashFile("sub/sub/new/foo.txt", "3");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(D, "foo/sub");
-		assertIteration(D, "foo/sub/new", attrs("bar"));
-		assertIteration(F, "foo/sub/new/foo.txt");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		assertIteration(D, "sub/sub");
-		assertIteration(D, "sub/sub/new", attrs("bar"));
-		assertIteration(F, "sub/sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, D, "foo/sub");
+			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
+			assertIteration(walk, F, "foo/sub/new/foo.txt");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertIteration(walk, D, "sub/sub");
+			assertIteration(walk, D, "sub/sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -479,20 +512,21 @@
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("foo/sub/new/foo.txt", "2");
 		writeTrashFile("sub/sub/new/foo.txt", "3");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(D, "foo/sub");
-		assertIteration(D, "foo/sub/new", attrs("bar"));
-		assertIteration(F, "foo/sub/new/foo.txt");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		assertIteration(D, "sub/sub");
-		assertIteration(D, "sub/sub/new", attrs("bar"));
-		assertIteration(F, "sub/sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, D, "foo/sub");
+			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
+			assertIteration(walk, F, "foo/sub/new/foo.txt");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertIteration(walk, D, "sub/sub");
+			assertIteration(walk, D, "sub/sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -500,17 +534,18 @@
 		setupRepo(null, null, "s[uv]b/n*/ bar", null);
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("foo/sub/new/foo.txt", "2");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(D, "foo/sub");
-		assertIteration(D, "foo/sub/new");
-		assertIteration(F, "foo/sub/new/foo.txt");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, D, "foo/sub");
+			assertIteration(walk, D, "foo/sub/new");
+			assertIteration(walk, F, "foo/sub/new/foo.txt");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -519,30 +554,32 @@
 		writeTrashFile("sub/new/foo.txt", "1");
 		writeTrashFile("foo/sub/new/foo.txt", "2");
 		writeTrashFile("foo/new", "3");
-		walk = beginWalk();
-		assertIteration(F, ".gitattributes");
-		assertIteration(D, "foo");
-		assertIteration(F, "foo/new");
-		assertIteration(D, "foo/sub");
-		assertIteration(D, "foo/sub/new", attrs("bar"));
-		assertIteration(F, "foo/sub/new/foo.txt");
-		assertIteration(D, "sub");
-		assertIteration(F, "sub/a.txt");
-		assertIteration(D, "sub/new", attrs("bar"));
-		assertIteration(F, "sub/new/foo.txt");
-		endWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, D, "foo");
+			assertIteration(walk, F, "foo/new");
+			assertIteration(walk, D, "foo/sub");
+			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
+			assertIteration(walk, F, "foo/sub/new/foo.txt");
+			assertIteration(walk, D, "sub");
+			assertIteration(walk, F, "sub/a.txt");
+			assertIteration(walk, D, "sub/new", attrs("bar"));
+			assertIteration(walk, F, "sub/new/foo.txt");
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	private static Collection<Attribute> attrs(String s) {
 		return new AttributesRule("*", s).getAttributes();
 	}
 
-	private void assertIteration(FileMode type, String pathName)
+	private void assertIteration(TreeWalk walk, FileMode type, String pathName)
 			throws IOException {
-		assertIteration(type, pathName, Collections.<Attribute> emptyList());
+		assertIteration(walk, type, pathName,
+				Collections.<Attribute> emptyList());
 	}
 
-	private void assertIteration(FileMode type, String pathName,
+	private void assertIteration(TreeWalk walk, FileMode type, String pathName,
 			Collection<Attribute> expectedAttrs) throws IOException {
 		assertTrue("walk has entry", walk.next());
 		assertEquals(pathName, walk.getPathString());
@@ -611,8 +648,4 @@
 		newWalk.addTree(new FileTreeIterator(db));
 		return newWalk;
 	}
-
-	private void endWalk() throws IOException {
-		assertFalse("Not all files tested", walk.next());
-	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
index 837de74..cb37d89 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
@@ -78,8 +78,6 @@
 
 	private Git git;
 
-	private TreeWalk walk;
-
 	@Override
 	@Before
 	public void setUp() throws Exception {
@@ -105,23 +103,25 @@
 		// Adds file to index
 		git.add().addFilepattern(".").call();
 
-		walk = beginWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, F, "readme.txt", asList(EOL_LF));
 
-		assertIteration(F, ".gitattributes");
-		assertIteration(F, "readme.txt", asList(EOL_LF));
+			assertIteration(walk, D, "src");
 
-		assertIteration(D, "src");
+			assertIteration(walk, D, "src/config");
+			assertIteration(walk, F, "src/config/.gitattributes");
+			assertIteration(walk, F, "src/config/readme.txt",
+					asList(DELTA_UNSET));
+			assertIteration(walk, F, "src/config/windows.file", null);
+			assertIteration(walk, F, "src/config/windows.txt",
+					asList(DELTA_UNSET));
 
-		assertIteration(D, "src/config");
-		assertIteration(F, "src/config/.gitattributes");
-		assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET));
-		assertIteration(F, "src/config/windows.file", null);
-		assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET));
+			assertIteration(walk, F, "windows.file", null);
+			assertIteration(walk, F, "windows.txt", asList(EOL_LF));
 
-		assertIteration(F, "windows.file", null);
-		assertIteration(F, "windows.txt", asList(EOL_LF));
-
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	/**
@@ -138,17 +138,18 @@
 
 		// Adds file to index
 		git.add().addFilepattern(".").call();
-		walk = beginWalk();
 
-		assertIteration(F, "l0.txt");
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, "l0.txt");
 
-		assertIteration(D, "level1");
-		assertIteration(F, "level1/l1.txt");
+			assertIteration(walk, D, "level1");
+			assertIteration(walk, F, "level1/l1.txt");
 
-		assertIteration(D, "level1/level2");
-		assertIteration(F, "level1/level2/l2.txt");
+			assertIteration(walk, D, "level1/level2");
+			assertIteration(walk, F, "level1/level2/l2.txt");
 
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	/**
@@ -166,18 +167,19 @@
 
 		// Adds file to index
 		git.add().addFilepattern(".").call();
-		walk = beginWalk();
 
-		assertIteration(F, ".gitattributes");
-		assertIteration(F, "l0.txt");
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, F, "l0.txt");
 
-		assertIteration(D, "level1");
-		assertIteration(F, "level1/l1.txt");
+			assertIteration(walk, D, "level1");
+			assertIteration(walk, F, "level1/l1.txt");
 
-		assertIteration(D, "level1/level2");
-		assertIteration(F, "level1/level2/l2.txt");
+			assertIteration(walk, D, "level1/level2");
+			assertIteration(walk, F, "level1/level2/l2.txt");
 
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -191,18 +193,19 @@
 
 		// Adds file to index
 		git.add().addFilepattern(".").call();
-		walk = beginWalk();
 
-		assertIteration(F, ".gitattributes");
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
 
-		assertIteration(D, "levelA");
-		assertIteration(F, "levelA/.gitattributes");
-		assertIteration(F, "levelA/lA.txt");
+			assertIteration(walk, D, "levelA");
+			assertIteration(walk, F, "levelA/.gitattributes");
+			assertIteration(walk, F, "levelA/lA.txt");
 
-		assertIteration(D, "levelB");
-		assertIteration(F, "levelB/.gitattributes");
+			assertIteration(walk, D, "levelB");
+			assertIteration(walk, F, "levelB/.gitattributes");
 
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -215,25 +218,27 @@
 
 		// Adds file to index
 		git.add().addFilepattern(".").call();
-		walk = beginWalk();
 
-		assertIteration(F, "gitattributes");
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, "gitattributes");
 
-		assertIteration(F, "l0.txt");
+			assertIteration(walk, F, "l0.txt");
 
-		assertIteration(D, "levelA");
-		assertIteration(F, "levelA/file.gitattributes");
-		assertIteration(F, "levelA/lA.txt");
+			assertIteration(walk, D, "levelA");
+			assertIteration(walk, F, "levelA/file.gitattributes");
+			assertIteration(walk, F, "levelA/lA.txt");
 
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
-	private void assertIteration(FileMode type, String pathName)
+	private void assertIteration(TreeWalk walk, FileMode type, String pathName)
 			throws IOException {
-		assertIteration(type, pathName, Collections.<Attribute> emptyList());
+		assertIteration(walk, type, pathName,
+				Collections.<Attribute> emptyList());
 	}
 
-	private void assertIteration(FileMode type, String pathName,
+	private void assertIteration(TreeWalk walk, FileMode type, String pathName,
 			List<Attribute> nodeAttrs) throws IOException {
 		assertTrue("walk has entry", walk.next());
 		assertEquals(pathName, walk.getPathString());
@@ -243,14 +248,14 @@
 
 		AttributesNode attributesNode = itr.getEntryAttributesNode(db
 				.newObjectReader());
-		assertAttributesNode(pathName, attributesNode, nodeAttrs);
+		assertAttributesNode(walk, pathName, attributesNode, nodeAttrs);
 
 		if (D.equals(type))
 			walk.enterSubtree();
 
 	}
 
-	private void assertAttributesNode(String pathName,
+	private void assertAttributesNode(TreeWalk walk, String pathName,
 			AttributesNode attributesNode, List<Attribute> nodeAttrs)
 					throws IOException {
 		if (attributesNode == null)
@@ -292,8 +297,4 @@
 		newWalk.addTree(new DirCacheIterator(db.readDirCache()));
 		return newWalk;
 	}
-
-	private void endWalk() throws IOException {
-		assertFalse("Not all files tested", walk.next());
-	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
index b159cca..9991ae5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
@@ -77,8 +77,6 @@
 
 	private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
 
-	private TreeWalk walk;
-
 	@Test
 	public void testRules() throws Exception {
 
@@ -102,24 +100,26 @@
 		writeTrashFile("src/config/windows.file", "");
 		writeTrashFile("src/config/windows.txt", "");
 
-		walk = beginWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, F, "global.txt", asList(EOL_LF));
+			assertIteration(walk, F, "readme.txt", asList(EOL_LF));
 
-		assertIteration(F, ".gitattributes");
-		assertIteration(F, "global.txt", asList(EOL_LF));
-		assertIteration(F, "readme.txt", asList(EOL_LF));
+			assertIteration(walk, D, "src");
 
-		assertIteration(D, "src");
+			assertIteration(walk, D, "src/config");
+			assertIteration(walk, F, "src/config/.gitattributes");
+			assertIteration(walk, F, "src/config/readme.txt",
+					asList(DELTA_UNSET));
+			assertIteration(walk, F, "src/config/windows.file", null);
+			assertIteration(walk, F, "src/config/windows.txt",
+					asList(DELTA_UNSET));
 
-		assertIteration(D, "src/config");
-		assertIteration(F, "src/config/.gitattributes");
-		assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET));
-		assertIteration(F, "src/config/windows.file", null);
-		assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET));
+			assertIteration(walk, F, "windows.file", null);
+			assertIteration(walk, F, "windows.txt", asList(EOL_LF));
 
-		assertIteration(F, "windows.file", null);
-		assertIteration(F, "windows.txt", asList(EOL_LF));
-
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	/**
@@ -134,17 +134,17 @@
 		writeTrashFile("level1/l1.txt", "");
 		writeTrashFile("level1/level2/l2.txt", "");
 
-		walk = beginWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, "l0.txt");
 
-		assertIteration(F, "l0.txt");
+			assertIteration(walk, D, "level1");
+			assertIteration(walk, F, "level1/l1.txt");
 
-		assertIteration(D, "level1");
-		assertIteration(F, "level1/l1.txt");
+			assertIteration(walk, D, "level1/level2");
+			assertIteration(walk, F, "level1/level2/l2.txt");
 
-		assertIteration(D, "level1/level2");
-		assertIteration(F, "level1/level2/l2.txt");
-
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	/**
@@ -160,18 +160,18 @@
 		writeTrashFile("level1/l1.txt", "");
 		writeTrashFile("level1/level2/l2.txt", "");
 
-		walk = beginWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
+			assertIteration(walk, F, "l0.txt");
 
-		assertIteration(F, ".gitattributes");
-		assertIteration(F, "l0.txt");
+			assertIteration(walk, D, "level1");
+			assertIteration(walk, F, "level1/l1.txt");
 
-		assertIteration(D, "level1");
-		assertIteration(F, "level1/l1.txt");
+			assertIteration(walk, D, "level1/level2");
+			assertIteration(walk, F, "level1/level2/l2.txt");
 
-		assertIteration(D, "level1/level2");
-		assertIteration(F, "level1/level2/l2.txt");
-
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
 	@Test
@@ -183,26 +183,27 @@
 
 		writeTrashFile("levelA/lA.txt", "");
 
-		walk = beginWalk();
+		try (TreeWalk walk = beginWalk()) {
+			assertIteration(walk, F, ".gitattributes");
 
-		assertIteration(F, ".gitattributes");
+			assertIteration(walk, D, "levelA");
+			assertIteration(walk, F, "levelA/.gitattributes");
+			assertIteration(walk, F, "levelA/lA.txt");
 
-		assertIteration(D, "levelA");
-		assertIteration(F, "levelA/.gitattributes");
-		assertIteration(F, "levelA/lA.txt");
+			assertIteration(walk, D, "levelB");
+			assertIteration(walk, F, "levelB/.gitattributes");
 
-		assertIteration(D, "levelB");
-		assertIteration(F, "levelB/.gitattributes");
-
-		endWalk();
+			assertFalse("Not all files tested", walk.next());
+		}
 	}
 
-	private void assertIteration(FileMode type, String pathName)
+	private void assertIteration(TreeWalk walk, FileMode type, String pathName)
 			throws IOException {
-		assertIteration(type, pathName, Collections.<Attribute> emptyList());
+		assertIteration(walk, type, pathName,
+				Collections.<Attribute> emptyList());
 	}
 
-	private void assertIteration(FileMode type, String pathName,
+	private void assertIteration(TreeWalk walk, FileMode type, String pathName,
 			List<Attribute> nodeAttrs)
 			throws IOException {
 		assertTrue("walk has entry", walk.next());
@@ -212,13 +213,13 @@
 		assertNotNull("has tree", itr);
 
 		AttributesNode attributesNode = itr.getEntryAttributesNode();
-		assertAttributesNode(pathName, attributesNode, nodeAttrs);
+		assertAttributesNode(walk, pathName, attributesNode, nodeAttrs);
 		if (D.equals(type))
 			walk.enterSubtree();
 
 	}
 
-	private void assertAttributesNode(String pathName,
+	private void assertAttributesNode(TreeWalk walk, String pathName,
 			AttributesNode attributesNode, List<Attribute> nodeAttrs)
 					throws IOException {
 		if (attributesNode == null)
@@ -259,8 +260,4 @@
 		newWalk.addTree(new FileTreeIterator(db));
 		return newWalk;
 	}
-
-	private void endWalk() throws IOException {
-		assertFalse("Not all files tested", walk.next());
-	}
 }
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 d6aead4..0291fcc 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
@@ -131,6 +131,12 @@
 	@Override
 	@After
 	public void tearDown() throws Exception {
+		if (walk != null) {
+			walk.close();
+		}
+		if (ci_walk != null) {
+			ci_walk.close();
+		}
 		super.tearDown();
 		if (customAttributeFile != null)
 			customAttributeFile.delete();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
index cbc0761..45046a3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
@@ -63,6 +63,7 @@
 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.SystemReader;
+import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -79,6 +80,13 @@
 
 	private TreeWalk walk;
 
+	@After
+	public void closeWalk() {
+		if (walk != null) {
+			walk.close();
+		}
+	}
+
 	@Test
 	public void testSimpleRootGitIgnoreGlobalIgnore() throws IOException {
 		writeIgnoreFile(".gitignore", "x");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
index 858f5b9..1d7e283 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
@@ -722,7 +722,7 @@
 
 		DfsPackDescription t1 = odb.newPack(INSERT);
 		try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
-			new ReftableWriter().begin(out).finish();
+			new ReftableWriter(out).begin().finish();
 			t1.addFileExt(REFTABLE);
 		}
 		odb.commitPack(Collections.singleton(t1), null);
@@ -754,7 +754,7 @@
 
 		DfsPackDescription t1 = odb.newPack(INSERT);
 		try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
-			new ReftableWriter().begin(out).finish();
+			new ReftableWriter(out).begin().finish();
 			t1.addFileExt(REFTABLE);
 		}
 		odb.commitPack(Collections.singleton(t1), null);
@@ -794,10 +794,10 @@
 		Ref next = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE,
 				"refs/heads/next", commit0.copy());
 		try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
-			ReftableWriter w = new ReftableWriter();
+			ReftableWriter w = new ReftableWriter(out);
 			w.setMinUpdateIndex(42);
 			w.setMaxUpdateIndex(42);
-			w.begin(out);
+			w.begin();
 			w.sortAndWriteRefs(Collections.singleton(next));
 			w.finish();
 			t1.addFileExt(REFTABLE);
@@ -876,10 +876,10 @@
 		Ref newNext = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, NEXT,
 				commit1);
 		try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
-			ReftableWriter w = new ReftableWriter();
-			w.setMinUpdateIndex(1);
-			w.setMaxUpdateIndex(1);
-			w.begin(out);
+			ReftableWriter w = new ReftableWriter(out)
+			.setMinUpdateIndex(1)
+			.setMaxUpdateIndex(1)
+			.begin();
 			w.writeRef(newNext, 1);
 			w.finish();
 			t1.addFileExt(REFTABLE);
@@ -928,10 +928,10 @@
 		Ref newNext = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, NEXT,
 				commit1);
 		try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
-			ReftableWriter w = new ReftableWriter();
-			w.setMinUpdateIndex(1);
-			w.setMaxUpdateIndex(1);
-			w.begin(out);
+			ReftableWriter w = new ReftableWriter(out)
+			.setMinUpdateIndex(1)
+			.setMaxUpdateIndex(1)
+			.begin();
 			w.writeRef(newNext, 1);
 			w.finish();
 			t1.addFileExt(REFTABLE);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
index e71ee6d..4d36144 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
@@ -92,18 +92,22 @@
 
 	@Test
 	public void testReadFromInserterSmallObjects() throws IOException {
-		ObjectInserter ins = db.newObjectInserter();
-		ObjectId id1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
-		ObjectId id2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("bar"));
-		assertEquals(0, db.getObjectDatabase().listPacks().size());
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			ObjectId id1 = ins.insert(Constants.OBJ_BLOB,
+					Constants.encode("foo"));
+			ObjectId id2 = ins.insert(Constants.OBJ_BLOB,
+					Constants.encode("bar"));
+			assertEquals(0, db.getObjectDatabase().listPacks().size());
 
-		ObjectReader reader = ins.newReader();
-		assertSame(ins, reader.getCreatedFromInserter());
-		assertEquals("foo", readString(reader.open(id1)));
-		assertEquals("bar", readString(reader.open(id2)));
-		assertEquals(0, db.getObjectDatabase().listPacks().size());
-		ins.flush();
-		assertEquals(1, db.getObjectDatabase().listPacks().size());
+			try (ObjectReader reader = ins.newReader()) {
+				assertSame(ins, reader.getCreatedFromInserter());
+				assertEquals("foo", readString(reader.open(id1)));
+				assertEquals("bar", readString(reader.open(id2)));
+				assertEquals(0, db.getObjectDatabase().listPacks().size());
+				ins.flush();
+				assertEquals(1, db.getObjectDatabase().listPacks().size());
+			}
+		}
 	}
 
 	@Test
@@ -114,17 +118,19 @@
 			.setBlockLimit(2048));
 
 		byte[] data = new TestRng(JGitTestUtil.getName()).nextBytes(8192);
-		DfsInserter ins = (DfsInserter) db.newObjectInserter();
-		ins.setCompressionLevel(Deflater.NO_COMPRESSION);
-		ObjectId id1 = ins.insert(Constants.OBJ_BLOB, data);
-		assertEquals(0, db.getObjectDatabase().listPacks().size());
+		try (DfsInserter ins = (DfsInserter) db.newObjectInserter()) {
+			ins.setCompressionLevel(Deflater.NO_COMPRESSION);
+			ObjectId id1 = ins.insert(Constants.OBJ_BLOB, data);
+			assertEquals(0, db.getObjectDatabase().listPacks().size());
 
-		ObjectReader reader = ins.newReader();
-		assertSame(ins, reader.getCreatedFromInserter());
-		assertTrue(Arrays.equals(data, readStream(reader.open(id1))));
-		assertEquals(0, db.getObjectDatabase().listPacks().size());
-		ins.flush();
+			try (ObjectReader reader = ins.newReader()) {
+				assertSame(ins, reader.getCreatedFromInserter());
+				assertTrue(Arrays.equals(data, readStream(reader.open(id1))));
+				assertEquals(0, db.getObjectDatabase().listPacks().size());
+			}
+			ins.flush();
 
+		}
 		List<DfsPackDescription> packs = db.getObjectDatabase().listPacks();
 		assertEquals(1, packs.size());
 		assertTrue(packs.get(0).getFileSize(PackExt.PACK) > 2048);
@@ -132,48 +138,58 @@
 
 	@Test
 	public void testReadFromFallback() throws IOException {
-		ObjectInserter ins = db.newObjectInserter();
-		ObjectId id1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
-		ins.flush();
-		ObjectId id2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("bar"));
-		assertEquals(1, db.getObjectDatabase().listPacks().size());
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			ObjectId id1 = ins.insert(Constants.OBJ_BLOB,
+					Constants.encode("foo"));
+			ins.flush();
+			ObjectId id2 = ins.insert(Constants.OBJ_BLOB,
+					Constants.encode("bar"));
+			assertEquals(1, db.getObjectDatabase().listPacks().size());
 
-		ObjectReader reader = ins.newReader();
-		assertSame(ins, reader.getCreatedFromInserter());
-		assertEquals("foo", readString(reader.open(id1)));
-		assertEquals("bar", readString(reader.open(id2)));
-		assertEquals(1, db.getObjectDatabase().listPacks().size());
-		ins.flush();
-		assertEquals(2, db.getObjectDatabase().listPacks().size());
+			try (ObjectReader reader = ins.newReader()) {
+				assertSame(ins, reader.getCreatedFromInserter());
+				assertEquals("foo", readString(reader.open(id1)));
+				assertEquals("bar", readString(reader.open(id2)));
+				assertEquals(1, db.getObjectDatabase().listPacks().size());
+			}
+			ins.flush();
+			assertEquals(2, db.getObjectDatabase().listPacks().size());
+		}
 	}
 
 	@Test
 	public void testReaderResolve() throws IOException {
-		ObjectInserter ins = db.newObjectInserter();
-		ObjectId id1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
-		ins.flush();
-		ObjectId id2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("bar"));
-		String abbr1 = ObjectId.toString(id1).substring(0, 4);
-		String abbr2 = ObjectId.toString(id2).substring(0, 4);
-		assertFalse(abbr1.equals(abbr2));
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			ObjectId id1 = ins.insert(Constants.OBJ_BLOB,
+					Constants.encode("foo"));
+			ins.flush();
+			ObjectId id2 = ins.insert(Constants.OBJ_BLOB,
+					Constants.encode("bar"));
+			String abbr1 = ObjectId.toString(id1).substring(0, 4);
+			String abbr2 = ObjectId.toString(id2).substring(0, 4);
+			assertFalse(abbr1.equals(abbr2));
 
-		ObjectReader reader = ins.newReader();
-		assertSame(ins, reader.getCreatedFromInserter());
-		Collection<ObjectId> objs;
-		objs = reader.resolve(AbbreviatedObjectId.fromString(abbr1));
-		assertEquals(1, objs.size());
-		assertEquals(id1, objs.iterator().next());
+			try (ObjectReader reader = ins.newReader()) {
+				assertSame(ins, reader.getCreatedFromInserter());
+				Collection<ObjectId> objs;
+				objs = reader.resolve(AbbreviatedObjectId.fromString(abbr1));
+				assertEquals(1, objs.size());
+				assertEquals(id1, objs.iterator().next());
 
-		objs = reader.resolve(AbbreviatedObjectId.fromString(abbr2));
-		assertEquals(1, objs.size());
-		assertEquals(id2, objs.iterator().next());
+				objs = reader.resolve(AbbreviatedObjectId.fromString(abbr2));
+				assertEquals(1, objs.size());
+				assertEquals(id2, objs.iterator().next());
+			}
+		}
 	}
 
 	@Test
 	public void testGarbageSelectivelyVisible() throws IOException {
-		ObjectInserter ins = db.newObjectInserter();
-		ObjectId fooId = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
-		ins.flush();
+		ObjectId fooId;
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			fooId = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
+			ins.flush();
+		}
 		assertEquals(1, db.getObjectDatabase().listPacks().size());
 
 		// Make pack 0 garbage.
@@ -187,36 +203,40 @@
 
 	@Test
 	public void testInserterIgnoresUnreachable() throws IOException {
-		ObjectInserter ins = db.newObjectInserter();
-		ObjectId fooId = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
-		ins.flush();
-		assertEquals(1, db.getObjectDatabase().listPacks().size());
+		ObjectId fooId;
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			fooId = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
+			ins.flush();
+			assertEquals(1, db.getObjectDatabase().listPacks().size());
 
-		// Make pack 0 garbage.
-		db.getObjectDatabase().listPacks().get(0).setPackSource(PackSource.UNREACHABLE_GARBAGE);
+			// Make pack 0 garbage.
+			db.getObjectDatabase().listPacks().get(0)
+					.setPackSource(PackSource.UNREACHABLE_GARBAGE);
 
-		// We shouldn't be able to see foo because it's garbage.
-		assertFalse(db.getObjectDatabase().has(fooId, true));
+			// We shouldn't be able to see foo because it's garbage.
+			assertFalse(db.getObjectDatabase().has(fooId, true));
 
-		// But if we re-insert foo, it should become visible again.
-		ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
-		ins.flush();
+			// But if we re-insert foo, it should become visible again.
+			ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
+			ins.flush();
+		}
 		assertTrue(db.getObjectDatabase().has(fooId, true));
 
 		// Verify that we have a foo in both packs, and 1 of them is garbage.
-		DfsReader reader = new DfsReader(db.getObjectDatabase());
-		DfsPackFile packs[] = db.getObjectDatabase().getPacks();
-		Set<PackSource> pack_sources = new HashSet<>();
+		try (DfsReader reader = new DfsReader(db.getObjectDatabase())) {
+			DfsPackFile packs[] = db.getObjectDatabase().getPacks();
+			Set<PackSource> pack_sources = new HashSet<>();
 
-		assertEquals(2, packs.length);
+			assertEquals(2, packs.length);
 
-		pack_sources.add(packs[0].getPackDescription().getPackSource());
-		pack_sources.add(packs[1].getPackDescription().getPackSource());
+			pack_sources.add(packs[0].getPackDescription().getPackSource());
+			pack_sources.add(packs[1].getPackDescription().getPackSource());
 
-		assertTrue(packs[0].hasObject(reader, fooId));
-		assertTrue(packs[1].hasObject(reader, fooId));
-		assertTrue(pack_sources.contains(PackSource.UNREACHABLE_GARBAGE));
-		assertTrue(pack_sources.contains(PackSource.INSERT));
+			assertTrue(packs[0].hasObject(reader, fooId));
+			assertTrue(packs[1].hasObject(reader, fooId));
+			assertTrue(pack_sources.contains(PackSource.UNREACHABLE_GARBAGE));
+			assertTrue(pack_sources.contains(PackSource.INSERT));
+		}
 	}
 
 	@Test
@@ -237,17 +257,20 @@
 		assertEquals(2, db.getObjectDatabase().listPacks().size());
 
 		// Verify that we have a foo in both INSERT packs.
-		DfsReader reader = new DfsReader(db.getObjectDatabase());
-		DfsPackFile packs[] = db.getObjectDatabase().getPacks();
+		try (DfsReader reader = new DfsReader(db.getObjectDatabase())) {
+			DfsPackFile packs[] = db.getObjectDatabase().getPacks();
 
-		assertEquals(2, packs.length);
-		DfsPackFile p1 = packs[0];
-		assertEquals(PackSource.INSERT, p1.getPackDescription().getPackSource());
-		assertTrue(p1.hasObject(reader, fooId));
+			assertEquals(2, packs.length);
+			DfsPackFile p1 = packs[0];
+			assertEquals(PackSource.INSERT,
+					p1.getPackDescription().getPackSource());
+			assertTrue(p1.hasObject(reader, fooId));
 
-		DfsPackFile p2 = packs[1];
-		assertEquals(PackSource.INSERT, p2.getPackDescription().getPackSource());
-		assertTrue(p2.hasObject(reader, fooId));
+			DfsPackFile p2 = packs[1];
+			assertEquals(PackSource.INSERT,
+					p2.getPackDescription().getPackSource());
+			assertTrue(p2.hasObject(reader, fooId));
+		}
 	}
 
 	private static String readString(ObjectLoader loader) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
index c211eab..32513c9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
@@ -61,6 +61,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import java.io.File;
@@ -109,19 +110,29 @@
 @SuppressWarnings("boxing")
 @RunWith(Parameterized.class)
 public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
-	@Parameter
+	@Parameter(0)
 	public boolean atomic;
 
-	@Parameters(name = "atomic={0}")
+	@Parameter(1)
+	public boolean useReftable;
+
+	@Parameters(name = "atomic={0} reftable={1}")
 	public static Collection<Object[]> data() {
-		return Arrays.asList(new Object[][]{ {Boolean.FALSE}, {Boolean.TRUE} });
+		return Arrays.asList(new Object[][] { { Boolean.FALSE, Boolean.FALSE },
+				{ Boolean.TRUE, Boolean.FALSE },
+				{ Boolean.FALSE, Boolean.TRUE },
+				{ Boolean.TRUE, Boolean.TRUE }, });
 	}
 
 	private Repository diskRepo;
+
 	private TestRepository<Repository> repo;
+
 	private RefDirectory refdir;
+
 	private RevCommit A;
-	private RevCommit B;
+
+	private RevCommit B; // B descends from A.
 
 	/**
 	 * When asserting the number of RefsChangedEvents you must account for one
@@ -143,11 +154,18 @@
 	public void setUp() throws Exception {
 		super.setUp();
 
-		diskRepo = createBareRepository();
+		FileRepository fileRepo = createBareRepository();
+		if (useReftable) {
+			fileRepo.convertToReftable(false, false);
+		}
+
+		diskRepo = fileRepo;
 		setLogAllRefUpdates(true);
 
-		refdir = (RefDirectory) diskRepo.getRefDatabase();
-		refdir.setRetrySleepMs(Arrays.asList(0, 0));
+		if (!useReftable) {
+			refdir = (RefDirectory) diskRepo.getRefDatabase();
+			refdir.setRetrySleepMs(Arrays.asList(0, 0));
+		}
 
 		repo = new TestRepository<>(diskRepo);
 		A = repo.commit().create();
@@ -166,11 +184,12 @@
 	@Test
 	public void packedRefsFileIsSorted() throws IOException {
 		assumeTrue(atomic);
+		assumeFalse(useReftable);
 
 		for (int i = 0; i < 2; i++) {
 			BatchRefUpdate bu = diskRepo.getRefDatabase().newBatchUpdate();
-			String b1  = String.format("refs/heads/a%d",i);
-			String b2  = String.format("refs/heads/b%d",i);
+			String b1 = String.format("refs/heads/a%d", i);
+			String b2 = String.format("refs/heads/b%d", i);
 			bu.setAtomic(atomic);
 			ReceiveCommand c1 = new ReceiveCommand(ObjectId.zeroId(), A, b1);
 			ReceiveCommand c2 = new ReceiveCommand(ObjectId.zeroId(), B, b2);
@@ -183,42 +202,36 @@
 		}
 
 		File packed = new File(diskRepo.getDirectory(), "packed-refs");
-		String packedStr = new String(Files.readAllBytes(packed.toPath()), UTF_8);
+		String packedStr = new String(Files.readAllBytes(packed.toPath()),
+				UTF_8);
 
 		int a2 = packedStr.indexOf("refs/heads/a1");
 		int b1 = packedStr.indexOf("refs/heads/b0");
-		assertTrue(a2 <  b1);
+		assertTrue(a2 < b1);
 	}
 
 	@Test
 	public void simpleNoForce() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
-				new ReceiveCommand(B, A, "refs/heads/masters", UPDATE_NONFASTFORWARD));
+				new ReceiveCommand(B, A, "refs/heads/masters",
+						UPDATE_NONFASTFORWARD));
 		execute(newBatchUpdate(cmds));
 
 		if (atomic) {
 			assertResults(cmds, TRANSACTION_ABORTED, REJECTED_NONFASTFORWARD);
-			assertRefs(
-					"refs/heads/master", A,
-					"refs/heads/masters", B);
+			assertRefs("refs/heads/master", A, "refs/heads/masters", B);
 		} else {
 			assertResults(cmds, OK, REJECTED_NONFASTFORWARD);
-			assertRefs(
-					"refs/heads/master", B,
-					"refs/heads/masters", B);
+			assertRefs("refs/heads/master", B, "refs/heads/masters", B);
 		}
 	}
 
 	@Test
 	public void simpleNoForceRefsChangedEvents() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
-		refdir.exactRef("refs/heads/master");
-		refdir.exactRef("refs/heads/masters");
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -233,26 +246,21 @@
 
 	@Test
 	public void simpleForce() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
-				new ReceiveCommand(B, A, "refs/heads/masters", UPDATE_NONFASTFORWARD));
+				new ReceiveCommand(B, A, "refs/heads/masters",
+						UPDATE_NONFASTFORWARD));
 		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
 
 		assertResults(cmds, OK, OK);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/masters", A);
+		assertRefs("refs/heads/master", B, "refs/heads/masters", A);
 	}
 
 	@Test
 	public void simpleForceRefsChangedEvents() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
-		refdir.exactRef("refs/heads/master");
-		refdir.exactRef("refs/heads/masters");
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -261,25 +269,25 @@
 						UPDATE_NONFASTFORWARD));
 		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
 
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
 	}
 
 	@Test
-	public void nonFastForwardDoesNotDoExpensiveMergeCheck() throws IOException {
+	public void nonFastForwardDoesNotDoExpensiveMergeCheck()
+			throws IOException {
 		writeLooseRef("refs/heads/master", B);
 
-		List<ReceiveCommand> cmds = Arrays.asList(
-				new ReceiveCommand(B, A, "refs/heads/master", UPDATE_NONFASTFORWARD));
+		List<ReceiveCommand> cmds = Arrays.asList(new ReceiveCommand(B, A,
+				"refs/heads/master", UPDATE_NONFASTFORWARD));
 		try (RevWalk rw = new RevWalk(diskRepo) {
-					@Override
-					public boolean isMergedInto(RevCommit base, RevCommit tip) {
-						throw new AssertionError("isMergedInto() should not be called");
-					}
-				}) {
-			newBatchUpdate(cmds)
-					.setAllowNonFastForwards(true)
-					.execute(rw, new StrictWorkMonitor());
+			@Override
+			public boolean isMergedInto(RevCommit base, RevCommit tip) {
+				throw new AssertionError("isMergedInto() should not be called");
+			}
+		}) {
+			newBatchUpdate(cmds).setAllowNonFastForwards(true).execute(rw,
+					new StrictWorkMonitor());
 		}
 
 		assertResults(cmds, OK);
@@ -290,7 +298,6 @@
 	public void nonFastForwardDoesNotDoExpensiveMergeCheckRefsChangedEvents()
 			throws IOException {
 		writeLooseRef("refs/heads/master", B);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(new ReceiveCommand(B, A,
@@ -310,8 +317,7 @@
 
 	@Test
 	public void fileDirectoryConflict() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@@ -320,29 +326,22 @@
 		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false);
 
 		if (atomic) {
-			// Atomic update sees that master and master/x are conflicting, then marks
-			// the first one in the list as LOCK_FAILURE and aborts the rest.
-			assertResults(cmds,
-					LOCK_FAILURE, TRANSACTION_ABORTED, TRANSACTION_ABORTED);
-			assertRefs(
-					"refs/heads/master", A,
-					"refs/heads/masters", B);
+			// Atomic update sees that master and master/x are conflicting, then
+			// marks the first one in the list as LOCK_FAILURE and aborts the rest.
+			assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED,
+					TRANSACTION_ABORTED);
+			assertRefs("refs/heads/master", A, "refs/heads/masters", B);
 		} else {
-			// Non-atomic updates are applied in order: master succeeds, then master/x
-			// fails due to conflict.
+			// Non-atomic updates are applied in order: master succeeds, then
+			// master/x fails due to conflict.
 			assertResults(cmds, OK, LOCK_FAILURE, LOCK_FAILURE);
-			assertRefs(
-					"refs/heads/master", B,
-					"refs/heads/masters", B);
+			assertRefs("refs/heads/master", B, "refs/heads/masters", B);
 		}
 	}
 
 	@Test
 	public void fileDirectoryConflictRefsChangedEvents() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
-		refdir.exactRef("refs/heads/master");
-		refdir.exactRef("refs/heads/masters");
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -357,8 +356,7 @@
 
 	@Test
 	public void conflictThanksToDelete() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@@ -367,17 +365,12 @@
 		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
 
 		assertResults(cmds, OK, OK, OK);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/masters/x", A);
+		assertRefs("refs/heads/master", B, "refs/heads/masters/x", A);
 	}
 
 	@Test
 	public void conflictThanksToDeleteRefsChangedEvents() throws IOException {
-		writeLooseRef("refs/heads/master", A);
-		writeLooseRef("refs/heads/masters", B);
-		refdir.exactRef("refs/heads/master");
-		refdir.exactRef("refs/heads/masters");
+		writeLooseRefs("refs/heads/master", A, "refs/heads/masters", B);
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -386,7 +379,7 @@
 				new ReceiveCommand(B, zeroId(), "refs/heads/masters", DELETE));
 		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
 
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 3, refsChangedEvents);
 	}
 
@@ -394,8 +387,8 @@
 	public void updateToMissingObject() throws IOException {
 		writeLooseRef("refs/heads/master", A);
 
-		ObjectId bad =
-				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+		ObjectId bad = ObjectId
+				.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, bad, "refs/heads/master", UPDATE),
 				new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE));
@@ -406,16 +399,13 @@
 			assertRefs("refs/heads/master", A);
 		} else {
 			assertResults(cmds, REJECTED_MISSING_OBJECT, OK);
-			assertRefs(
-					"refs/heads/master", A,
-					"refs/heads/foo2", B);
+			assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
 		}
 	}
 
 	@Test
 	public void updateToMissingObjectRefsChangedEvents() throws IOException {
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		ObjectId bad = ObjectId
@@ -433,8 +423,8 @@
 	public void addMissingObject() throws IOException {
 		writeLooseRef("refs/heads/master", A);
 
-		ObjectId bad =
-				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+		ObjectId bad = ObjectId
+				.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
 				new ReceiveCommand(zeroId(), bad, "refs/heads/foo2", CREATE));
@@ -452,7 +442,6 @@
 	@Test
 	public void addMissingObjectRefsChangedEvents() throws IOException {
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		ObjectId bad = ObjectId
@@ -498,16 +487,13 @@
 			assertRefs("refs/heads/master", A);
 		} else {
 			assertResults(cmds, LOCK_FAILURE, OK);
-			assertRefs(
-					"refs/heads/master", A,
-					"refs/heads/foo2", B);
+			assertRefs("refs/heads/master", A, "refs/heads/foo2", B);
 		}
 	}
 
 	@Test
 	public void oneRefWrongOldValueRefsChangedEvents() throws IOException {
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -540,7 +526,7 @@
 	@Test
 	public void nonExistentRefRefsChangedEvents() throws IOException {
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
+
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -557,9 +543,10 @@
 		writeRef("refs/heads/master", A);
 		int initialRefsChangedEvents = refsChangedEvents;
 
-		Map<String, ReflogEntry> oldLogs =
-				getLastReflogs("refs/heads/master", "refs/heads/branch");
-		assertEquals(Collections.singleton("refs/heads/master"), oldLogs.keySet());
+		Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
+				"refs/heads/branch");
+		assertEquals(Collections.singleton("refs/heads/master"),
+				oldLogs.keySet());
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@@ -567,10 +554,8 @@
 		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
 
 		assertResults(cmds, OK, OK);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/branch", B);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertRefs("refs/heads/master", B, "refs/heads/branch", B);
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
 		assertReflogUnchanged(oldLogs, "refs/heads/master");
 		assertReflogUnchanged(oldLogs, "refs/heads/branch");
@@ -582,25 +567,20 @@
 		writeRef("refs/heads/branch2", A);
 		int initialRefsChangedEvents = refsChangedEvents;
 
-		Map<String, ReflogEntry> oldLogs = getLastReflogs(
-				"refs/heads/master", "refs/heads/branch1", "refs/heads/branch2");
+		Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
+				"refs/heads/branch1", "refs/heads/branch2");
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
 				new ReceiveCommand(zeroId(), B, "refs/heads/branch1", CREATE));
-		execute(
-				newBatchUpdate(cmds)
-						.setAllowNonFastForwards(true)
-						.setRefLogMessage("a reflog", false));
+		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)
+				.setRefLogMessage("a reflog", false));
 
 		assertResults(cmds, OK, OK);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/branch1", B,
+		assertRefs("refs/heads/master", B, "refs/heads/branch1", B,
 				"refs/heads/branch2", A);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
-		assertReflogEquals(
-				reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
+		assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
 				getLastReflog("refs/heads/master"));
 		assertReflogEquals(
 				reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
@@ -616,23 +596,20 @@
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
-				new ReceiveCommand(B, A, "refs/heads/branch1", UPDATE_NONFASTFORWARD),
+				new ReceiveCommand(B, A, "refs/heads/branch1",
+						UPDATE_NONFASTFORWARD),
 				new ReceiveCommand(zeroId(), A, "refs/heads/branch2", CREATE));
-		execute(
-				newBatchUpdate(cmds)
-						.setAllowNonFastForwards(true)
-						.setRefLogMessage(null, true));
+		execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)
+				.setRefLogMessage(null, true));
 
 		assertResults(cmds, OK, OK, OK);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/branch1", A,
+		assertRefs("refs/heads/master", B, "refs/heads/branch1", A,
 				"refs/heads/branch2", A);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
-				: initialRefsChangedEvents + 3,
-				refsChangedEvents);
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
+				: initialRefsChangedEvents + 3, refsChangedEvents);
 		assertReflogEquals(
-				// Always forced; setAllowNonFastForwards(true) bypasses the check.
+				// Always forced; setAllowNonFastForwards(true) bypasses the
+				// check.
 				reflog(A, B, new PersonIdent(diskRepo), "forced-update"),
 				getLastReflog("refs/heads/master"));
 		assertReflogEquals(
@@ -648,8 +625,8 @@
 		writeRef("refs/heads/master", A);
 		int initialRefsChangedEvents = refsChangedEvents;
 
-		List<ReceiveCommand> cmds = Arrays.asList(
-				new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
+		List<ReceiveCommand> cmds = Arrays
+				.asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
 		execute(newBatchUpdate(cmds).setRefLogMessage(null, true));
 
 		assertResults(cmds, OK);
@@ -671,16 +648,16 @@
 		execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true));
 
 		assertResults(cmds, OK, OK);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/branch", A);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertRefs("refs/heads/master", B, "refs/heads/branch", A);
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
 		assertReflogEquals(
-				reflog(A, B, new PersonIdent(diskRepo), "a reflog: fast-forward"),
+				reflog(A, B, new PersonIdent(diskRepo),
+						"a reflog: fast-forward"),
 				getLastReflog("refs/heads/master"));
 		assertReflogEquals(
-				reflog(zeroId(), A, new PersonIdent(diskRepo), "a reflog: created"),
+				reflog(zeroId(), A, new PersonIdent(diskRepo),
+						"a reflog: created"),
 				getLastReflog("refs/heads/branch"));
 	}
 
@@ -692,26 +669,19 @@
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
 				new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
-		PersonIdent ident = new PersonIdent("A Reflog User", "reflog@example.com");
-		execute(
-				newBatchUpdate(cmds)
-						.setRefLogMessage("a reflog", false)
-						.setRefLogIdent(ident));
+		PersonIdent ident = new PersonIdent("A Reflog User",
+				"reflog@example.com");
+		execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false)
+				.setRefLogIdent(ident));
 
 		assertResults(cmds, OK, OK);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/branch", B);
-		assertReflogEquals(
-				reflog(A, B, ident, "a reflog"),
-				getLastReflog("refs/heads/master"),
-				true);
-		assertReflogEquals(
-				reflog(zeroId(), B, ident, "a reflog"),
-				getLastReflog("refs/heads/branch"),
-				true);
+		assertRefs("refs/heads/master", B, "refs/heads/branch", B);
+		assertReflogEquals(reflog(A, B, ident, "a reflog"),
+				getLastReflog("refs/heads/master"), true);
+		assertReflogEquals(reflog(zeroId(), B, ident, "a reflog"),
+				getLastReflog("refs/heads/branch"), true);
 	}
 
 	@Test
@@ -719,9 +689,8 @@
 		writeRef("refs/heads/master", A);
 		writeRef("refs/heads/branch", A);
 		int initialRefsChangedEvents = refsChangedEvents;
-
-		assertEquals(
-				2, getLastReflogs("refs/heads/master", "refs/heads/branch").size());
+		assertEquals(2, getLastReflogs("refs/heads/master", "refs/heads/branch")
+				.size());
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, zeroId(), "refs/heads/master", DELETE),
@@ -730,11 +699,17 @@
 
 		assertResults(cmds, OK, OK);
 		assertRefs("refs/heads/branch", B);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
-		assertNull(getLastReflog("refs/heads/master"));
-		assertReflogEquals(
-				reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
+		if (useReftable) {
+			// reftable retains reflog entries for deleted branches.
+			assertReflogEquals(
+					reflog(A, zeroId(), new PersonIdent(diskRepo), "a reflog"),
+					getLastReflog("refs/heads/master"));
+		} else {
+			assertNull(getLastReflog("refs/heads/master"));
+		}
+		assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
 				getLastReflog("refs/heads/branch"));
 	}
 
@@ -750,9 +725,12 @@
 
 		assertResults(cmds, OK, OK);
 		assertRefs("refs/heads/master/x", A);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
-		assertNull(getLastReflog("refs/heads/master"));
+		if (!useReftable) {
+			// reftable retains reflog entries for deleted branches.
+			assertNull(getLastReflog("refs/heads/master"));
+		}
 		assertReflogEquals(
 				reflog(zeroId(), A, new PersonIdent(diskRepo), "a reflog"),
 				getLastReflog("refs/heads/master/x"));
@@ -763,8 +741,8 @@
 		writeRef("refs/heads/master", A);
 		int initialRefsChangedEvents = refsChangedEvents;
 
-		Map<String, ReflogEntry> oldLogs =
-				getLastReflogs("refs/heads/master", "refs/heads/branch");
+		Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
+				"refs/heads/branch");
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@@ -796,22 +774,16 @@
 				new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
 		cmds.get(0).setRefLogMessage("custom log", false);
 		PersonIdent ident = new PersonIdent(diskRepo);
-		execute(
-				newBatchUpdate(cmds)
-						.setRefLogIdent(ident)
-						.setRefLogMessage("a reflog", true));
+		execute(newBatchUpdate(cmds).setRefLogIdent(ident)
+				.setRefLogMessage("a reflog", true));
 
 		assertResults(cmds, OK, OK);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
-		assertReflogEquals(
-				reflog(A, B, ident, "custom log"),
-				getLastReflog("refs/heads/master"),
-				true);
-		assertReflogEquals(
-				reflog(zeroId(), B, ident, "a reflog: created"),
-				getLastReflog("refs/heads/branch"),
-				true);
+		assertReflogEquals(reflog(A, B, ident, "custom log"),
+				getLastReflog("refs/heads/master"), true);
+		assertReflogEquals(reflog(zeroId(), B, ident, "a reflog: created"),
+				getLastReflog("refs/heads/branch"), true);
 	}
 
 	@Test
@@ -819,8 +791,8 @@
 		writeRef("refs/heads/master", A);
 		int initialRefsChangedEvents = refsChangedEvents;
 
-		Map<String, ReflogEntry> oldLogs =
-				getLastReflogs("refs/heads/master", "refs/heads/branch");
+		Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
+				"refs/heads/branch");
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
@@ -829,21 +801,24 @@
 		execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", true));
 
 		assertResults(cmds, OK, OK);
-		assertEquals(atomic ? initialRefsChangedEvents + 1
+		assertEquals(batchesRefUpdates() ? initialRefsChangedEvents + 1
 				: initialRefsChangedEvents + 2, refsChangedEvents);
 		assertReflogUnchanged(oldLogs, "refs/heads/master");
 		assertReflogEquals(
-				reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog: created"),
+				reflog(zeroId(), B, new PersonIdent(diskRepo),
+						"a reflog: created"),
 				getLastReflog("refs/heads/branch"));
 	}
 
 	@Test
 	public void refLogNotWrittenWithoutConfigOption() throws Exception {
+		assumeFalse(useReftable);
+
 		setLogAllRefUpdates(false);
 		writeRef("refs/heads/master", A);
 
-		Map<String, ReflogEntry> oldLogs =
-				getLastReflogs("refs/heads/master", "refs/heads/branch");
+		Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
+				"refs/heads/branch");
 		assertTrue(oldLogs.isEmpty());
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -858,22 +833,21 @@
 
 	@Test
 	public void forceRefLogInUpdate() throws Exception {
+		assumeFalse(useReftable);
+
 		setLogAllRefUpdates(false);
 		writeRef("refs/heads/master", A);
-		assertTrue(
-				getLastReflogs("refs/heads/master", "refs/heads/branch").isEmpty());
+		assertTrue(getLastReflogs("refs/heads/master", "refs/heads/branch")
+				.isEmpty());
 
 		List<ReceiveCommand> cmds = Arrays.asList(
 				new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
 				new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
-		execute(
-				newBatchUpdate(cmds)
-						.setRefLogMessage("a reflog", false)
-						.setForceRefLog(true));
+		execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false)
+				.setForceRefLog(true));
 
 		assertResults(cmds, OK, OK);
-		assertReflogEquals(
-				reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
+		assertReflogEquals(reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
 				getLastReflog("refs/heads/master"));
 		assertReflogEquals(
 				reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
@@ -882,11 +856,13 @@
 
 	@Test
 	public void forceRefLogInCommand() throws Exception {
+		assumeFalse(useReftable);
+
 		setLogAllRefUpdates(false);
 		writeRef("refs/heads/master", A);
 
-		Map<String, ReflogEntry> oldLogs =
-				getLastReflogs("refs/heads/master", "refs/heads/branch");
+		Map<String, ReflogEntry> oldLogs = getLastReflogs("refs/heads/master",
+				"refs/heads/branch");
 		assertTrue(oldLogs.isEmpty());
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -904,6 +880,8 @@
 
 	@Test
 	public void packedRefsLockFailure() throws Exception {
+		assumeFalse(useReftable);
+
 		writeLooseRef("refs/heads/master", A);
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -921,11 +899,10 @@
 				assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED);
 				assertRefs("refs/heads/master", A);
 			} else {
-				// Only operates on loose refs, doesn't care that packed-refs is locked.
+				// Only operates on loose refs, doesn't care that packed-refs is
+				// locked.
 				assertResults(cmds, OK, OK);
-				assertRefs(
-						"refs/heads/master", B,
-						"refs/heads/branch", B);
+				assertRefs("refs/heads/master", B, "refs/heads/branch", B);
 			}
 		} finally {
 			myLock.unlock();
@@ -934,8 +911,9 @@
 
 	@Test
 	public void packedRefsLockFailureRefsChangedEvents() throws Exception {
+		assumeFalse(useReftable);
+
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -946,10 +924,8 @@
 		try {
 			execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
 
-			assertEquals(
-					atomic ? initialRefsChangedEvents
-							: initialRefsChangedEvents + 2,
-					refsChangedEvents);
+			assertEquals(atomic ? initialRefsChangedEvents
+					: initialRefsChangedEvents + 2, refsChangedEvents);
 		} finally {
 			myLock.unlock();
 		}
@@ -957,6 +933,8 @@
 
 	@Test
 	public void oneRefLockFailure() throws Exception {
+		assumeFalse(useReftable);
+
 		writeLooseRef("refs/heads/master", A);
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -976,9 +954,7 @@
 				assertRefs("refs/heads/master", A);
 			} else {
 				assertResults(cmds, OK, LOCK_FAILURE);
-				assertRefs(
-						"refs/heads/branch", B,
-						"refs/heads/master", A);
+				assertRefs("refs/heads/branch", B, "refs/heads/master", A);
 			}
 		} finally {
 			myLock.unlock();
@@ -987,8 +963,9 @@
 
 	@Test
 	public void oneRefLockFailureRefsChangedEvents() throws Exception {
+		assumeFalse(useReftable);
+
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -1000,10 +977,8 @@
 		try {
 			execute(newBatchUpdate(cmds).setAllowNonFastForwards(true));
 
-			assertEquals(
-					atomic ? initialRefsChangedEvents
-							: initialRefsChangedEvents + 1,
-					refsChangedEvents);
+			assertEquals(atomic ? initialRefsChangedEvents
+					: initialRefsChangedEvents + 1, refsChangedEvents);
 		} finally {
 			myLock.unlock();
 		}
@@ -1011,10 +986,12 @@
 
 	@Test
 	public void singleRefUpdateDoesNotRequirePackedRefsLock() throws Exception {
+		assumeFalse(useReftable);
+
 		writeLooseRef("refs/heads/master", A);
 
-		List<ReceiveCommand> cmds = Arrays.asList(
-				new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
+		List<ReceiveCommand> cmds = Arrays
+				.asList(new ReceiveCommand(A, B, "refs/heads/master", UPDATE));
 
 		LockFile myLock = refdir.lockPackedRefs();
 		try {
@@ -1031,8 +1008,9 @@
 	@Test
 	public void singleRefUpdateDoesNotRequirePackedRefsLockRefsChangedEvents()
 			throws Exception {
+		assumeFalse(useReftable);
+
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays
@@ -1051,6 +1029,7 @@
 	@Test
 	public void atomicUpdateRespectsInProcessLock() throws Exception {
 		assumeTrue(atomic);
+		assumeFalse(useReftable);
 
 		writeLooseRef("refs/heads/master", A);
 
@@ -1073,7 +1052,8 @@
 			long timeoutSecs = 10;
 			long startNanos = System.nanoTime();
 
-			// Hold onto the lock until we observe the worker thread has attempted to
+			// Hold onto the lock until we observe the worker thread has
+			// attempted to
 			// acquire it.
 			while (l.getQueueLength() == 0) {
 				long elapsedNanos = System.nanoTime() - startNanos;
@@ -1083,7 +1063,8 @@
 				Thread.sleep(3);
 			}
 
-			// Once we unlock, the worker thread should finish the update promptly.
+			// Once we unlock, the worker thread should finish the update
+			// promptly.
 			l.unlock();
 			t.join(SECONDS.toMillis(timeoutSecs));
 			assertFalse(t.isAlive());
@@ -1094,19 +1075,16 @@
 		}
 
 		assertResults(cmds, OK, OK);
-		assertEquals(2, refsChangedEvents);
-		assertRefs(
-				"refs/heads/master", B,
-				"refs/heads/branch", B);
+		assertRefs("refs/heads/master", B, "refs/heads/branch", B);
 	}
 
 	@Test
 	public void atomicUpdateRespectsInProcessLockRefsChangedEvents()
 			throws Exception {
 		assumeTrue(atomic);
+		assumeFalse(useReftable);
 
 		writeLooseRef("refs/heads/master", A);
-		refdir.exactRef("refs/heads/master");
 		int initialRefsChangedEvents = refsChangedEvents;
 
 		List<ReceiveCommand> cmds = Arrays.asList(
@@ -1156,7 +1134,43 @@
 	}
 
 	private void writeLooseRef(String name, AnyObjectId id) throws IOException {
-		write(new File(diskRepo.getDirectory(), name), id.name() + "\n");
+		if (useReftable) {
+			writeRef(name, id);
+		} else {
+			write(new File(diskRepo.getDirectory(), name), id.name() + "\n");
+			// force the refs-changed event to be fired for the loose ref that
+			// was created. We do this to get the events fired during the test
+			// 'setup' out of the way and this allows us to now accurately
+			// assert only for the new events fired during the BatchRefUpdate.
+			refdir.exactRef(name);
+		}
+	}
+
+	private void writeLooseRefs(String name1, AnyObjectId id1, String name2,
+			AnyObjectId id2) throws IOException {
+		if (useReftable) {
+			BatchRefUpdate bru = diskRepo.getRefDatabase().newBatchUpdate();
+
+			Ref r1 = diskRepo.exactRef(name1);
+			ReceiveCommand c1 = new ReceiveCommand(
+					r1 != null ? r1.getObjectId() : ObjectId.zeroId(),
+					id1.toObjectId(), name1, r1 == null ? CREATE : UPDATE);
+
+			Ref r2 = diskRepo.exactRef(name2);
+			ReceiveCommand c2 = new ReceiveCommand(
+					r2 != null ? r2.getObjectId() : ObjectId.zeroId(),
+					id2.toObjectId(), name2, r2 == null ? CREATE : UPDATE);
+
+			bru.addCommand(c1, c2);
+			try (RevWalk rw = new RevWalk(diskRepo)) {
+				bru.execute(rw, NullProgressMonitor.INSTANCE);
+			}
+			assertEquals(c2.getResult(), ReceiveCommand.Result.OK);
+			assertEquals(c1.getResult(), ReceiveCommand.Result.OK);
+		} else {
+			writeLooseRef(name1, id1);
+			writeLooseRef(name2, id2);
+		}
 	}
 
 	private void writeRef(String name, AnyObjectId id) throws IOException {
@@ -1166,16 +1180,16 @@
 		u.setNewObjectId(id);
 		RefUpdate.Result r = u.update();
 		switch (r) {
-			case NEW:
-			case FORCED:
-				return;
-			default:
-				throw new IOException("Got " + r + " while updating " + name);
+		case NEW:
+		case FORCED:
+			return;
+		default:
+			throw new IOException("Got " + r + " while updating " + name);
 		}
 	}
 
 	private BatchRefUpdate newBatchUpdate(List<ReceiveCommand> cmds) {
-		BatchRefUpdate u = refdir.newBatchUpdate();
+		BatchRefUpdate u = diskRepo.getRefDatabase().newBatchUpdate();
 		if (atomic) {
 			assertTrue(u.isAtomic());
 		} else {
@@ -1189,10 +1203,11 @@
 		execute(u, false);
 	}
 
-	private void execute(BatchRefUpdate u, boolean strictWork) throws IOException {
+	private void execute(BatchRefUpdate u, boolean strictWork)
+			throws IOException {
 		try (RevWalk rw = new RevWalk(diskRepo)) {
-			u.execute(rw,
-					strictWork ? new StrictWorkMonitor() : NullProgressMonitor.INSTANCE);
+			u.execute(rw, strictWork ? new StrictWorkMonitor()
+					: NullProgressMonitor.INSTANCE);
 		}
 	}
 
@@ -1207,15 +1222,18 @@
 			expected.put((String) args[i], (AnyObjectId) args[i + 1]);
 		}
 
-		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
+		Map<String, Ref> refs = diskRepo.getRefDatabase()
+				.getRefs(RefDatabase.ALL);
 		Ref actualHead = refs.remove(Constants.HEAD);
 		if (actualHead != null) {
 			String actualLeafName = actualHead.getLeaf().getName();
 			assertEquals(
-					"expected HEAD to point to refs/heads/master, got: " + actualLeafName,
+					"expected HEAD to point to refs/heads/master, got: "
+							+ actualLeafName,
 					"refs/heads/master", actualLeafName);
 			AnyObjectId expectedMaster = expected.get("refs/heads/master");
-			assertNotNull("expected master ref since HEAD exists", expectedMaster);
+			assertNotNull("expected master ref since HEAD exists",
+					expectedMaster);
 			assertEquals(expectedMaster, actualHead.getObjectId());
 		}
 
@@ -1245,8 +1263,7 @@
 		}
 	}
 
-	private void assertResults(
-			List<ReceiveCommand> cmds, Result... expected) {
+	private void assertResults(List<ReceiveCommand> cmds, Result... expected) {
 		if (expected.length != cmds.size()) {
 			throw new IllegalArgumentException(
 					"expected " + cmds.size() + " result args");
@@ -1254,12 +1271,10 @@
 		for (int i = 0; i < cmds.size(); i++) {
 			ReceiveCommand c = cmds.get(i);
 			Result r = expected[i];
-			assertTrue(
-					String.format(
-							"result of command (%d) should be %s: %s %s%s",
-							Integer.valueOf(i), r, c,
-							c.getResult(),
-							c.getMessage() != null ? " (" + c.getMessage() + ")" : ""),
+			assertTrue(String.format(
+					"result of command (%d) should be %s, got %s %s%s",
+					Integer.valueOf(i), r, c, c.getResult(),
+					c.getMessage() != null ? " (" + c.getMessage() + ")" : ""),
 					r.p.test(c));
 		}
 	}
@@ -1288,18 +1303,18 @@
 		return LockFile.getLockFile(refdir.fileFor(refName));
 	}
 
-	private void assertReflogUnchanged(
-			Map<String, ReflogEntry> old, String name) throws IOException {
+	private void assertReflogUnchanged(Map<String, ReflogEntry> old,
+			String name) throws IOException {
 		assertReflogEquals(old.get(name), getLastReflog(name), true);
 	}
 
-	private static void assertReflogEquals(
-			ReflogEntry expected, ReflogEntry actual) {
+	private static void assertReflogEquals(ReflogEntry expected,
+			ReflogEntry actual) {
 		assertReflogEquals(expected, actual, false);
 	}
 
-	private static void assertReflogEquals(
-			ReflogEntry expected, ReflogEntry actual, boolean strictTime) {
+	private static void assertReflogEquals(ReflogEntry expected,
+			ReflogEntry actual, boolean strictTime) {
 		if (expected == null) {
 			assertNull(actual);
 			return;
@@ -1310,9 +1325,9 @@
 		if (strictTime) {
 			assertEquals(expected.getWho(), actual.getWho());
 		} else {
-			assertEquals(expected.getWho().getName(), actual.getWho().getName());
-			assertEquals(
-					expected.getWho().getEmailAddress(),
+			assertEquals(expected.getWho().getName(),
+					actual.getWho().getName());
+			assertEquals(expected.getWho().getEmailAddress(),
 					actual.getWho().getEmailAddress());
 		}
 		assertEquals(expected.getComment(), actual.getComment());
@@ -1347,4 +1362,8 @@
 			}
 		};
 	}
+
+	private boolean batchesRefUpdates() {
+		return atomic || useReftable;
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java
new file mode 100644
index 0000000..a2710e1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableStackTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.file;
+
+import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.eclipse.jgit.internal.storage.file.FileReftableStack.Segment;
+import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
+import org.eclipse.jgit.internal.storage.reftable.RefCursor;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class FileReftableStackTest {
+
+	private static Ref newRef(String name, ObjectId id) {
+		return new ObjectIdRef.PeeledNonTag(PACKED, name, id);
+	}
+
+	private File reftableDir;
+
+	@Before
+	public void setup() throws Exception {
+		reftableDir = FileUtils.createTempDir("rtstack", "", null);
+	}
+
+	@After
+	public void tearDown() throws Exception {
+		if (reftableDir != null) {
+			FileUtils.delete(reftableDir, FileUtils.RECURSIVE);
+		}
+	}
+
+	void writeBranches(FileReftableStack stack, String template, int start,
+			int N) throws IOException {
+		for (int i = 0; i < N; i++) {
+			while (true) {
+				final long next = stack.getMergedReftable().maxUpdateIndex()
+						+ 1;
+
+				String name = String.format(template,
+						Integer.valueOf(start + i));
+				Ref r = newRef(name, ObjectId.zeroId());
+				boolean ok = stack.addReftable(rw -> {
+					rw.setMinUpdateIndex(next).setMaxUpdateIndex(next).begin()
+							.writeRef(r);
+				});
+				if (ok) {
+					break;
+				}
+			}
+		}
+	}
+
+	public void testCompaction(int N) throws Exception {
+		try (FileReftableStack stack = new FileReftableStack(
+				new File(reftableDir, "refs"), reftableDir, null,
+				() -> new Config())) {
+			writeBranches(stack, "refs/heads/branch%d", 0, N);
+			MergedReftable table = stack.getMergedReftable();
+			for (int i = 1; i < N; i++) {
+				String name = String.format("refs/heads/branch%d",
+						Integer.valueOf(i));
+				RefCursor c = table.seekRef(name);
+				assertTrue(c.next());
+				assertEquals(ObjectId.zeroId(), c.getRef().getObjectId());
+			}
+
+			List<String> files = Arrays.asList(reftableDir.listFiles()).stream()
+					.map(File::getName).collect(Collectors.toList());
+			Collections.sort(files);
+
+			assertTrue(files.size() < 20);
+
+			FileReftableStack.CompactionStats stats = stack.getStats();
+			assertEquals(0, stats.failed);
+			assertTrue(stats.attempted < N);
+			assertTrue(stats.refCount < FileReftableStack.log(N) * N);
+		}
+	}
+
+	@Test
+	public void testCompaction9() throws Exception {
+		testCompaction(9);
+	}
+
+	@Test
+	public void testCompaction1024() throws Exception {
+		testCompaction(1024);
+	}
+
+	@Rule
+	public final ExpectedException thrown = ExpectedException.none();
+
+	@SuppressWarnings({ "resource", "unused" })
+	@Test
+	public void missingReftable() throws Exception {
+		try (FileReftableStack stack = new FileReftableStack(
+				new File(reftableDir, "refs"), reftableDir, null,
+				() -> new Config())) {
+			outer: for (int i = 0; i < 10; i++) {
+				final long next = stack.getMergedReftable().maxUpdateIndex()
+						+ 1;
+				String name = String.format("branch%d", Integer.valueOf(i));
+				Ref r = newRef(name, ObjectId.zeroId());
+				boolean ok = stack.addReftable(rw -> {
+					rw.setMinUpdateIndex(next).setMaxUpdateIndex(next).begin()
+							.writeRef(r);
+				});
+				assertTrue(ok);
+
+				List<File> files = Arrays.asList(reftableDir.listFiles());
+				for (int j = 0; j < files.size(); j++) {
+					File f = files.get(j);
+					if (f.getName().endsWith(".ref")) {
+						assertTrue(f.delete());
+						break outer;
+					}
+				}
+			}
+		}
+		thrown.expect(FileNotFoundException.class);
+		new FileReftableStack(new File(reftableDir, "refs"), reftableDir, null,
+				() -> new Config());
+	}
+
+	@Test
+	public void testSegments() {
+		long in[] = { 1024, 1024, 1536, 100, 64, 50, 25, 24 };
+		List<Segment> got = FileReftableStack.segmentSizes(in);
+		Segment want[] = { new Segment(0, 3, 10, 3584),
+				new Segment(3, 5, 6, 164), new Segment(5, 6, 5, 50),
+				new Segment(6, 8, 4, 49), };
+		assertEquals(got.size(), want.length);
+		for (int i = 0; i < want.length; i++) {
+			assertTrue(want[i].equals(got.get(i)));
+		}
+	}
+
+	@Test
+	public void testLog2() throws Exception {
+		assertEquals(10, FileReftableStack.log(1024));
+		assertEquals(10, FileReftableStack.log(1025));
+		assertEquals(10, FileReftableStack.log(2047));
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
new file mode 100644
index 0000000..4907073
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2019, Google Inc.
+ * 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.file;
+
+import static org.eclipse.jgit.lib.RefUpdate.Result.FAST_FORWARD;
+import static org.eclipse.jgit.lib.RefUpdate.Result.FORCED;
+import static org.eclipse.jgit.lib.RefUpdate.Result.IO_FAILURE;
+import static org.eclipse.jgit.lib.RefUpdate.Result.LOCK_FAILURE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
+import org.eclipse.jgit.lib.RepositoryCache;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.junit.Test;
+
+public class FileReftableTest extends SampleDataRepositoryTestCase {
+	String bCommit;
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		Ref b = db.exactRef("refs/heads/b");
+		bCommit = b.getObjectId().getName();
+		db.convertToReftable(false, false);
+	}
+
+	@SuppressWarnings("boxing")
+	@Test
+	public void testRacyReload() throws Exception {
+		ObjectId id = db.resolve("master");
+		int retry = 0;
+		try (FileRepository repo1 = new FileRepository(db.getDirectory());
+				FileRepository repo2 = new FileRepository(db.getDirectory())) {
+			FileRepository repos[] = { repo1, repo2 };
+			for (int i = 0; i < 10; i++) {
+				for (int j = 0; j < 2; j++) {
+					FileRepository repo = repos[j];
+					RefUpdate u = repo.getRefDatabase().newUpdate(
+							String.format("branch%d", i * 10 + j), false);
+
+					u.setNewObjectId(id);
+					RefUpdate.Result r = u.update();
+					if (!r.equals(Result.NEW)) {
+						retry++;
+						u = repo.getRefDatabase().newUpdate(
+								String.format("branch%d", i * 10 + j), false);
+
+						u.setNewObjectId(id);
+						r = u.update();
+						assertEquals(r, Result.NEW);
+					}
+				}
+			}
+
+			// only the first one succeeds
+			assertEquals(retry, 19);
+		}
+	}
+
+	@Test
+	public void testCompactFully() throws Exception {
+		ObjectId c1 = db.resolve("master^^");
+		ObjectId c2 = db.resolve("master^");
+		for (int i = 0; i < 5; i++) {
+			RefUpdate u = db.updateRef("refs/heads/master");
+			u.setForceUpdate(true);
+			u.setNewObjectId((i%2) == 0 ? c1 : c2);
+			assertEquals(u.update(), FORCED);
+		}
+
+		File tableDir = new File(db.getDirectory(), Constants.REFTABLE);
+		assertTrue(tableDir.listFiles().length > 2);
+		((FileReftableDatabase)db.getRefDatabase()).compactFully();
+		assertEquals(tableDir.listFiles().length,2);
+	}
+
+	@Test
+	public void testOpenConvert() throws Exception {
+		try (FileRepository repo = new FileRepository(db.getDirectory())) {
+			assertTrue(repo.getRefDatabase() instanceof FileReftableDatabase);
+		}
+	}
+
+	@Test
+	public void testConvert() throws Exception {
+		Ref h = db.exactRef("HEAD");
+		assertTrue(h.isSymbolic());
+		assertEquals("refs/heads/master", h.getTarget().getName());
+
+		Ref b = db.exactRef("refs/heads/b");
+		assertFalse(b.isSymbolic());
+		assertTrue(b.isPeeled());
+		assertEquals(bCommit, b.getObjectId().name());
+
+		assertTrue(db.getRefDatabase().hasFastTipsWithSha1());
+	}
+
+
+	@Test
+	public void testConvertBrokenObjectId() throws Exception {
+		db.convertToPackedRefs(false, false);
+		new File(db.getDirectory(), "refs/heads").mkdirs();
+
+		String invalidId = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+		File headFile = new File(db.getDirectory(), "refs/heads/broken");
+		try (OutputStream os = new FileOutputStream(headFile)) {
+			os.write(Constants.encodeASCII(invalidId + "\n"));
+		}
+
+		Ref r = db.exactRef("refs/heads/broken");
+		assertNotNull(r);
+		db.convertToReftable(true, false);
+	}
+
+	@Test
+	public void testConvertToRefdirReflog() throws Exception {
+		Ref a = db.exactRef("refs/heads/a");
+		String aCommit = a.getObjectId().getName();
+		RefUpdate u = db.updateRef("refs/heads/master");
+		u.setForceUpdate(true);
+		u.setNewObjectId(ObjectId.fromString(aCommit));
+		u.setForceRefLog(true);
+		u.setRefLogMessage("apple", false);
+		u.update();
+
+		RefUpdate v = db.updateRef("refs/heads/master");
+		v.setForceUpdate(true);
+		v.setNewObjectId(ObjectId.fromString(bCommit));
+		v.setForceRefLog(true);
+		v.setRefLogMessage("banana", false);
+		v.update();
+
+		db.convertToPackedRefs(true, false);
+		List<ReflogEntry> logs = db.getReflogReader("refs/heads/master").getReverseEntries(2);
+		assertEquals(logs.get(0).getComment(), "banana");
+		assertEquals(logs.get(1).getComment(), "apple");
+	}
+
+	@Test
+	public void testBatchrefUpdate() throws Exception {
+		ObjectId cur = db.resolve("master");
+		ObjectId prev = db.resolve("master^");
+
+		PersonIdent person = new PersonIdent("name", "mail@example.com");
+		ReceiveCommand rc1 = new ReceiveCommand(ObjectId.zeroId(), cur, "refs/heads/batch1");
+		ReceiveCommand rc2 = new ReceiveCommand(ObjectId.zeroId(), prev, "refs/heads/batch2");
+		String msg =  "message";
+		try (RevWalk rw = new RevWalk(db)) {
+			db.getRefDatabase().newBatchUpdate()
+					.addCommand(rc1, rc2)
+					.setAtomic(true)
+					.setRefLogIdent(person)
+					.setRefLogMessage(msg, false)
+					.execute(rw, NullProgressMonitor.INSTANCE);
+		}
+
+		assertEquals(rc1.getResult(), ReceiveCommand.Result.OK);
+		assertEquals(rc2.getResult(), ReceiveCommand.Result.OK);
+
+		ReflogEntry e = db.getReflogReader("refs/heads/batch1").getLastEntry();
+		assertEquals(msg, e.getComment());
+		assertEquals(person, e.getWho());
+		assertEquals(cur, e.getNewId());
+
+		e = db.getReflogReader("refs/heads/batch2").getLastEntry();
+		assertEquals(msg, e.getComment());
+		assertEquals(person, e.getWho());
+		assertEquals(prev, e.getNewId());
+
+		assertEquals(cur, db.exactRef("refs/heads/batch1").getObjectId());
+		assertEquals(prev, db.exactRef("refs/heads/batch2").getObjectId());
+	}
+
+	@Test
+	public void testFastforwardStatus() throws Exception {
+		ObjectId cur = db.resolve("master");
+		ObjectId prev = db.resolve("master^");
+		RefUpdate u = db.updateRef("refs/heads/master");
+
+		u.setNewObjectId(prev);
+		u.setForceUpdate(true);
+		assertEquals(FORCED, u.update());
+
+		RefUpdate u2 = db.updateRef("refs/heads/master");
+
+		u2.setNewObjectId(cur);
+		assertEquals(FAST_FORWARD, u2.update());
+	}
+
+	@Test
+	public void testUpdateChecksOldValue() throws Exception {
+		ObjectId cur = db.resolve("master");
+		ObjectId prev = db.resolve("master^");
+		RefUpdate u1 = db.updateRef("refs/heads/master");
+		RefUpdate u2 = db.updateRef("refs/heads/master");
+
+		u1.setExpectedOldObjectId(cur);
+		u1.setNewObjectId(prev);
+		u1.setForceUpdate(true);
+
+		u2.setExpectedOldObjectId(cur);
+		u2.setNewObjectId(prev);
+		u2.setForceUpdate(true);
+
+		assertEquals(FORCED, u1.update());
+		assertEquals(LOCK_FAILURE, u2.update());
+	}
+
+	@Test
+	public void testWritesymref() throws Exception {
+		writeSymref(Constants.HEAD, "refs/heads/a");
+		assertNotNull(db.exactRef("refs/heads/b"));
+	}
+
+	@Test
+	public void testFastforwardStatus2() throws Exception {
+		writeSymref(Constants.HEAD, "refs/heads/a");
+		ObjectId bId = db.exactRef("refs/heads/b").getObjectId();
+		RefUpdate u = db.updateRef("refs/heads/a");
+		u.setNewObjectId(bId);
+		u.setRefLogMessage("Setup", false);
+		assertEquals(FAST_FORWARD, u.update());
+	}
+
+	@Test
+	public void testDelete() throws Exception {
+		RefUpdate up = db.getRefDatabase().newUpdate("refs/heads/a", false);
+		up.setForceUpdate(true);
+		RefUpdate.Result res = up.delete();
+		assertEquals(res, FORCED);
+		assertNull(db.exactRef("refs/heads/a"));
+	}
+
+	@Test
+	public void testDeleteWithoutHead() throws IOException {
+		// Prepare repository without HEAD
+		RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
+		refUpdate.setForceUpdate(true);
+		refUpdate.setNewObjectId(ObjectId.zeroId());
+
+		RefUpdate.Result updateResult = refUpdate.update();
+		assertEquals(FORCED, updateResult);
+
+		Ref r = db.exactRef("HEAD");
+		assertEquals(ObjectId.zeroId(), r.getObjectId());
+		RefUpdate.Result deleteHeadResult = db.updateRef(Constants.HEAD)
+				.delete();
+
+		// why does doDelete say NEW ?
+		assertEquals(RefUpdate.Result.NO_CHANGE, deleteHeadResult);
+
+		// Any result is ok as long as it's not an NPE
+		db.updateRef(Constants.R_HEADS + "master").delete();
+	}
+
+	@Test
+	public void testUpdateRefDetached() throws Exception {
+		ObjectId pid = db.resolve("refs/heads/master");
+		ObjectId ppid = db.resolve("refs/heads/master^");
+		RefUpdate updateRef = db.updateRef("HEAD", true);
+		updateRef.setForceUpdate(true);
+		updateRef.setNewObjectId(ppid);
+		RefUpdate.Result update = updateRef.update();
+		assertEquals(FORCED, update);
+		assertEquals(ppid, db.resolve("HEAD"));
+		Ref ref = db.exactRef("HEAD");
+		assertEquals("HEAD", ref.getName());
+		assertTrue("is detached", !ref.isSymbolic());
+
+		// the branch HEAD referred to is left untouched
+		assertEquals(pid, db.resolve("refs/heads/master"));
+		ReflogReader reflogReader = db.getReflogReader("HEAD");
+		ReflogEntry e = reflogReader.getReverseEntries().get(0);
+		assertEquals(ppid, e.getNewId());
+		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
+		assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
+		assertEquals(1250379778000L, e.getWho().getWhen().getTime());
+		assertEquals(pid, e.getOldId());
+	}
+
+	@Test
+	public void testWriteReflog() throws Exception {
+		ObjectId pid = db.resolve("refs/heads/master^");
+		RefUpdate updateRef = db.updateRef("refs/heads/master");
+		updateRef.setNewObjectId(pid);
+		String msg = "REFLOG!";
+		updateRef.setRefLogMessage(msg, true);
+		PersonIdent person = new PersonIdent("name", "mail@example.com");
+		updateRef.setRefLogIdent(person);
+		updateRef.setForceUpdate(true);
+		RefUpdate.Result update = updateRef.update();
+		assertEquals(FORCED, update); // internal
+		ReflogReader r = db.getReflogReader("refs/heads/master");
+
+		ReflogEntry e = r.getLastEntry();
+		assertEquals(e.getNewId(), pid);
+		assertEquals(e.getComment(), "REFLOG!: FORCED");
+		assertEquals(e.getWho(), person);
+	}
+
+	@Test
+	public void testLooseDelete() throws IOException {
+		final String newRef = "refs/heads/abc";
+		assertNull(db.exactRef(newRef));
+
+		RefUpdate ref = db.updateRef(newRef);
+		ObjectId nonZero = db.resolve(Constants.HEAD);
+		assertNotEquals(nonZero, ObjectId.zeroId());
+		ref.setNewObjectId(nonZero);
+		assertEquals(RefUpdate.Result.NEW, ref.update());
+
+		ref = db.updateRef(newRef);
+		ref.setNewObjectId(db.resolve(Constants.HEAD));
+
+		assertEquals(ref.delete(), RefUpdate.Result.NO_CHANGE);
+
+		// Differs from RefupdateTest. Deleting a loose ref leaves reflog trail.
+		ReflogReader reader = db.getReflogReader("refs/heads/abc");
+		assertEquals(ObjectId.zeroId(), reader.getReverseEntry(1).getOldId());
+		assertEquals(nonZero, reader.getReverseEntry(1).getNewId());
+		assertEquals(nonZero, reader.getReverseEntry(0).getOldId());
+		assertEquals(ObjectId.zeroId(), reader.getReverseEntry(0).getNewId());
+	}
+
+	private static class SubclassedId extends ObjectId {
+		SubclassedId(AnyObjectId src) {
+			super(src);
+		}
+	}
+
+	@Test
+	public void testNoCacheObjectIdSubclass() throws IOException {
+		final String newRef = "refs/heads/abc";
+		final RefUpdate ru = updateRef(newRef);
+		final SubclassedId newid = new SubclassedId(ru.getNewObjectId());
+		ru.setNewObjectId(newid);
+		RefUpdate.Result update = ru.update();
+		assertEquals(RefUpdate.Result.NEW, update);
+		Ref r = db.exactRef(newRef);
+		assertEquals(newRef, r.getName());
+		assertNotNull(r.getObjectId());
+		assertNotSame(newid, r.getObjectId());
+		assertSame(ObjectId.class, r.getObjectId().getClass());
+		assertEquals(newid, r.getObjectId());
+		List<ReflogEntry> reverseEntries1 = db.getReflogReader("refs/heads/abc")
+				.getReverseEntries();
+		ReflogEntry entry1 = reverseEntries1.get(0);
+		assertEquals(1, reverseEntries1.size());
+		assertEquals(ObjectId.zeroId(), entry1.getOldId());
+		assertEquals(r.getObjectId(), entry1.getNewId());
+
+		assertEquals(new PersonIdent(db).toString(),
+				entry1.getWho().toString());
+		assertEquals("", entry1.getComment());
+		List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
+				.getReverseEntries();
+		assertEquals(0, reverseEntries2.size());
+	}
+
+	@Test
+	public void testDeleteSymref() throws IOException {
+		RefUpdate dst = updateRef("refs/heads/abc");
+		assertEquals(RefUpdate.Result.NEW, dst.update());
+		ObjectId id = dst.getNewObjectId();
+
+		RefUpdate u = db.updateRef("refs/symref");
+		assertEquals(RefUpdate.Result.NEW, u.link(dst.getName()));
+
+		Ref ref = db.exactRef(u.getName());
+		assertNotNull(ref);
+		assertTrue(ref.isSymbolic());
+		assertEquals(dst.getName(), ref.getLeaf().getName());
+		assertEquals(id, ref.getLeaf().getObjectId());
+
+		u = db.updateRef(u.getName());
+		u.setDetachingSymbolicRef();
+		u.setForceUpdate(true);
+		assertEquals(FORCED, u.delete());
+
+		assertNull(db.exactRef(u.getName()));
+		ref = db.exactRef(dst.getName());
+		assertNotNull(ref);
+		assertFalse(ref.isSymbolic());
+		assertEquals(id, ref.getObjectId());
+	}
+
+	@Test
+	public void writeUnbornHead() throws Exception {
+		RefUpdate.Result r = db.updateRef("HEAD").link("refs/heads/unborn");
+		assertEquals(FORCED, r);
+
+		Ref head = db.exactRef("HEAD");
+		assertTrue(head.isSymbolic());
+		assertEquals(head.getTarget().getName(), "refs/heads/unborn");
+	}
+
+	/**
+	 * Update the HEAD ref when the referenced branch is unborn
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void testUpdateRefDetachedUnbornHead() throws Exception {
+		ObjectId ppid = db.resolve("refs/heads/master^");
+		writeSymref("HEAD", "refs/heads/unborn");
+		RefUpdate updateRef = db.updateRef("HEAD", true);
+		updateRef.setForceUpdate(true);
+		updateRef.setNewObjectId(ppid);
+		RefUpdate.Result update = updateRef.update();
+		assertEquals(RefUpdate.Result.NEW, update);
+		assertEquals(ppid, db.resolve("HEAD"));
+		Ref ref = db.exactRef("HEAD");
+		assertEquals("HEAD", ref.getName());
+		assertTrue("is detached", !ref.isSymbolic());
+
+		// the branch HEAD referred to is left untouched
+		assertNull(db.resolve("refs/heads/unborn"));
+		ReflogReader reflogReader = db.getReflogReader("HEAD");
+		ReflogEntry e = reflogReader.getReverseEntries().get(0);
+		assertEquals(ObjectId.zeroId(), e.getOldId());
+		assertEquals(ppid, e.getNewId());
+		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
+		assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
+		assertEquals(1250379778000L, e.getWho().getWhen().getTime());
+	}
+
+	@Test
+	public void testDeleteNotFound() throws IOException {
+		RefUpdate ref = updateRef("refs/heads/doesnotexist");
+		assertNull(db.exactRef(ref.getName()));
+		assertEquals(RefUpdate.Result.NEW, ref.delete());
+		assertNull(db.exactRef(ref.getName()));
+	}
+
+	@Test
+	public void testRenameSymref() throws IOException {
+		db.resolve("HEAD");
+		RefRename r = db.renameRef("HEAD", "KOPF");
+		assertEquals(IO_FAILURE, r.rename());
+	}
+
+	@Test
+	public void testRenameCurrentBranch() throws IOException {
+		ObjectId rb = db.resolve("refs/heads/b");
+		writeSymref(Constants.HEAD, "refs/heads/b");
+		ObjectId oldHead = db.resolve(Constants.HEAD);
+		assertEquals("internal test condition, b == HEAD", oldHead, rb);
+		RefRename renameRef = db.renameRef("refs/heads/b",
+				"refs/heads/new/name");
+		RefUpdate.Result result = renameRef.rename();
+		assertEquals(RefUpdate.Result.RENAMED, result);
+		assertEquals(rb, db.resolve("refs/heads/new/name"));
+		assertNull(db.resolve("refs/heads/b"));
+		assertEquals(rb, db.resolve(Constants.HEAD));
+
+		List<String> names = new ArrayList<>();
+		names.add("HEAD");
+		names.add("refs/heads/b");
+		names.add("refs/heads/new/name");
+
+		for (String nm : names) {
+			ReflogReader rd = db.getReflogReader(nm);
+			assertNotNull(rd);
+			ReflogEntry last = rd.getLastEntry();
+			ObjectId id = last.getNewId();
+			assertTrue(ObjectId.zeroId().equals(id) || rb.equals(id));
+
+			id = last.getNewId();
+			assertTrue(ObjectId.zeroId().equals(id) || rb.equals(id));
+
+			String want = "Branch: renamed b to new/name";
+			assertEquals(want, last.getComment());
+		}
+	}
+
+	@Test
+	public void isGitRepository() {
+		assertTrue(RepositoryCache.FileKey.isGitRepository(db.getDirectory(), db.getFS()));
+	}
+
+	@Test
+	public void testRenameDestExists() throws IOException {
+		ObjectId rb = db.resolve("refs/heads/b");
+		writeSymref(Constants.HEAD, "refs/heads/b");
+		ObjectId oldHead = db.resolve(Constants.HEAD);
+		assertEquals("internal test condition, b == HEAD", oldHead, rb);
+		RefRename renameRef = db.renameRef("refs/heads/b", "refs/heads/a");
+		RefUpdate.Result result = renameRef.rename();
+		assertEquals(RefUpdate.Result.LOCK_FAILURE, result);
+	}
+
+	@Test
+	public void testRenameAtomic() throws IOException {
+		ObjectId prevId = db.resolve("refs/heads/master^");
+
+		RefRename rename = db.renameRef("refs/heads/master",
+				"refs/heads/newmaster");
+
+		RefUpdate updateRef = db.updateRef("refs/heads/master");
+		updateRef.setNewObjectId(prevId);
+		updateRef.setForceUpdate(true);
+		assertEquals(FORCED, updateRef.update());
+		assertEquals(RefUpdate.Result.LOCK_FAILURE, rename.rename());
+	}
+
+	@Test
+	public void compactFully() throws Exception {
+		FileReftableDatabase refDb = (FileReftableDatabase) db.getRefDatabase();
+		PersonIdent person = new PersonIdent("jane", "jane@invalid");
+
+		ObjectId aId  = db.exactRef("refs/heads/a").getObjectId();
+		ObjectId bId  = db.exactRef("refs/heads/b").getObjectId();
+
+		SecureRandom random = new SecureRandom();
+		List<String> strs = new ArrayList<>();
+		for (int i = 0; i < 1024; i++) {
+			strs.add(String.format("%02x",
+					Integer.valueOf(random.nextInt(256))));
+		}
+
+		String randomStr = String.join("", strs);
+		String refName = "branch";
+		for (long i = 0; i < 2; i++) {
+			RefUpdate ru = refDb.newUpdate(refName, false);
+			ru.setNewObjectId(i % 2 == 0 ? aId : bId);
+			ru.setForceUpdate(true);
+			// Only write a large string in the first table, so it becomes much larger
+			// than the second, and the result is not autocompacted.
+			ru.setRefLogMessage(i == 0 ? randomStr : "short", false);
+			ru.setRefLogIdent(person);
+
+			RefUpdate.Result res = ru.update();
+			assertTrue(res == Result.NEW || res == FORCED);
+		}
+
+		assertEquals(refDb.exactRef(refName).getObjectId(), bId);
+		assertTrue(randomStr.equals(refDb.getReflogReader(refName).getReverseEntry(1).getComment()));
+		refDb.compactFully();
+		assertEquals(refDb.exactRef(refName).getObjectId(), bId);
+		assertTrue(randomStr.equals(refDb.getReflogReader(refName).getReverseEntry(1).getComment()));
+	}
+
+	@Test
+	public void reftableRefsStorageClass() throws IOException {
+		Ref b = db.exactRef("refs/heads/b");
+		assertEquals(Ref.Storage.PACKED, b.getStorage());
+	}
+
+	private RefUpdate updateRef(String name) throws IOException {
+		final RefUpdate ref = db.updateRef(name);
+		ref.setNewObjectId(db.resolve(Constants.HEAD));
+		return ref;
+	}
+
+	private void writeSymref(String src, String dst) throws IOException {
+		RefUpdate u = db.updateRef(src);
+		switch (u.link(dst)) {
+		case NEW:
+		case FORCED:
+		case NO_CHANGE:
+			break;
+		default:
+			fail("link " + src + " to " + dst);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
index e54c4ad..bbbe3b9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
@@ -53,21 +53,21 @@
 import org.junit.Test;
 
 public class GcOrphanFilesTest extends GcTestCase {
-	private final static String PACK = "pack";
+	private static final String PACK = "pack";
 
-	private final static String BITMAP_File_1 = PACK + "-1.bitmap";
+	private static final String BITMAP_File_1 = PACK + "-1.bitmap";
 
 	private static final String BITMAP_File_2 = PACK + "-2.bitmap";
 
 	private static final String IDX_File_2 = PACK + "-2.idx";
 
-	private final static String IDX_File_malformed = PACK + "-1234idx";
+	private static final String IDX_File_malformed = PACK + "-1234idx";
 
 	private static final String KEEP_File_2 = PACK + "-2.keep";
 
 	private static final String PACK_File_2 = PACK + "-2.pack";
 
-	private final static String PACK_File_3 = PACK + "-3.pack";
+	private static final String PACK_File_3 = PACK + "-3.pack";
 
 	private File packDir;
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/LockFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/LockFileTest.java
index 3bfd047..4e123f4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/LockFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/LockFileTest.java
@@ -1,51 +1,23 @@
 /*
- * Copyright (C) 2012, GitHub Inc.
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2012, 2021 GitHub Inc. and others
  *
- * 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
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://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.
+ * SPDX-License-Identifier: BSD-3-Clause
  */
 package org.eclipse.jgit.internal.storage.file;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.File;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.errors.LockFailedException;
@@ -82,4 +54,167 @@
 			}
 		}
 	}
+
+	@Test
+	public void testLockTwice() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		lock.write("other".getBytes(StandardCharsets.US_ASCII));
+		lock.commit();
+		assertFalse(lock.isLocked());
+		checkFile(f, "other");
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		try (OutputStream out = lock.getOutputStream()) {
+			out.write("second".getBytes(StandardCharsets.US_ASCII));
+		}
+		lock.commit();
+		assertFalse(lock.isLocked());
+		checkFile(f, "second");
+	}
+
+	@Test
+	public void testLockTwiceUnlock() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		lock.write("other".getBytes(StandardCharsets.US_ASCII));
+		lock.unlock();
+		assertFalse(lock.isLocked());
+		checkFile(f, "content");
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		try (OutputStream out = lock.getOutputStream()) {
+			out.write("second".getBytes(StandardCharsets.US_ASCII));
+		}
+		lock.commit();
+		assertFalse(lock.isLocked());
+		checkFile(f, "second");
+	}
+
+	@Test
+	public void testLockWriteTwiceThrows1() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		lock.write("other".getBytes(StandardCharsets.US_ASCII));
+		try {
+			lock.write("second".getBytes(StandardCharsets.US_ASCII));
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		lock.unlock();
+	}
+
+	@Test
+	public void testLockWriteTwiceThrows2() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		try (OutputStream out = lock.getOutputStream()) {
+			out.write("other".getBytes(StandardCharsets.US_ASCII));
+		}
+		try {
+			lock.write("second".getBytes(StandardCharsets.US_ASCII));
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		lock.unlock();
+	}
+
+	@Test
+	public void testLockWriteTwiceThrows3() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		lock.write("other".getBytes(StandardCharsets.US_ASCII));
+		try (OutputStream out = lock.getOutputStream()) {
+			out.write("second".getBytes(StandardCharsets.US_ASCII));
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		lock.unlock();
+	}
+
+	@Test
+	public void testLockWriteTwiceThrows4() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		try (OutputStream out = lock.getOutputStream()) {
+			out.write("other".getBytes(StandardCharsets.US_ASCII));
+		}
+		try (OutputStream out = lock.getOutputStream()) {
+			out.write("second".getBytes(StandardCharsets.US_ASCII));
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		lock.unlock();
+	}
+
+	@Test
+	public void testLockUnclosedCommitThrows() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		try (OutputStream out = lock.getOutputStream()) {
+			out.write("other".getBytes(StandardCharsets.US_ASCII));
+			lock.commit();
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+	}
+
+	@Test
+	public void testLockNested() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		try {
+			lock.lock();
+			fail();
+		} catch (IllegalStateException e) {
+			// expected
+		}
+		assertTrue(lock.isLocked());
+		lock.unlock();
+	}
+
+	@Test
+	public void testLockHeld() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lock());
+		assertTrue(lock.isLocked());
+		LockFile lock2 = new LockFile(f);
+		assertFalse(lock2.lock());
+		assertFalse(lock2.isLocked());
+		assertTrue(lock.isLocked());
+		lock.unlock();
+	}
+
+	@Test
+	public void testLockForAppend() throws Exception {
+		File f = writeTrashFile("somefile", "content");
+		LockFile lock = new LockFile(f);
+		assertTrue(lock.lockForAppend());
+		assertTrue(lock.isLocked());
+		lock.write("other".getBytes(StandardCharsets.US_ASCII));
+		lock.commit();
+		assertFalse(lock.isLocked());
+		checkFile(f, "contentother");
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
index d5bc61a..9016a02 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileSnapshotTest.java
@@ -295,9 +295,8 @@
 				Files.copy(src, dstOut);
 				return dst;
 			}
-		} else {
-			return Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
 		}
+		return Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
 	}
 
 	private Path copyPack(Path base, String srcSuffix, String dstSuffix)
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 1e2341b..3f281c4 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
@@ -468,22 +468,22 @@
 	@Test
 	public void testDeltaStatistics() throws Exception {
 		config.setDeltaCompress(true);
+		// TestRepository will close repo
 		FileRepository repo = createBareRepository();
 		ArrayList<RevObject> blobs = new ArrayList<>();
 		try (TestRepository<FileRepository> testRepo = new TestRepository<>(
 				repo)) {
 			blobs.add(testRepo.blob(genDeltableData(1000)));
 			blobs.add(testRepo.blob(genDeltableData(1005)));
-		}
-
-		try (PackWriter pw = new PackWriter(repo)) {
-			NullProgressMonitor m = NullProgressMonitor.INSTANCE;
-			pw.preparePack(blobs.iterator());
-			pw.writePack(m, m, os);
-			PackStatistics stats = pw.getStatistics();
-			assertEquals(1, stats.getTotalDeltas());
-			assertTrue("Delta bytes not set.",
-					stats.byObjectType(OBJ_BLOB).getDeltaBytes() > 0);
+			try (PackWriter pw = new PackWriter(repo)) {
+				NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+				pw.preparePack(blobs.iterator());
+				pw.writePack(m, m, os);
+				PackStatistics stats = pw.getStatistics();
+				assertEquals(1, stats.getTotalDeltas());
+				assertTrue("Delta bytes not set.",
+						stats.byObjectType(OBJ_BLOB).getDeltaBytes() > 0);
+			}
 		}
 	}
 
@@ -535,6 +535,7 @@
 
 	@Test
 	public void testExclude() throws Exception {
+		// TestRepository closes repo
 		FileRepository repo = createBareRepository();
 
 		try (TestRepository<FileRepository> testRepo = new TestRepository<>(
@@ -568,98 +569,102 @@
 
 	@Test
 	public void testShallowIsMinimalDepth1() throws Exception {
-		FileRepository repo = setupRepoForShallowFetch();
+		try (FileRepository repo = setupRepoForShallowFetch()) {
+			PackIndex idx = writeShallowPack(repo, 1, wants(c2), NONE, NONE);
+			assertContent(idx, Arrays.asList(c2.getId(), c2.getTree().getId(),
+					contentA.getId(), contentB.getId()));
 
-		PackIndex idx = writeShallowPack(repo, 1, wants(c2), NONE, NONE);
-		assertContent(idx, Arrays.asList(c2.getId(), c2.getTree().getId(),
-				contentA.getId(), contentB.getId()));
-
-		// Client already has blobs A and B, verify those are not packed.
-		idx = writeShallowPack(repo, 1, wants(c5), haves(c2), shallows(c2));
-		assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
-				contentC.getId(), contentD.getId(), contentE.getId()));
+			// Client already has blobs A and B, verify those are not packed.
+			idx = writeShallowPack(repo, 1, wants(c5), haves(c2), shallows(c2));
+			assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
+					contentC.getId(), contentD.getId(), contentE.getId()));
+		}
 	}
 
 	@Test
 	public void testShallowIsMinimalDepth2() throws Exception {
-		FileRepository repo = setupRepoForShallowFetch();
+		try (FileRepository repo = setupRepoForShallowFetch()) {
+			PackIndex idx = writeShallowPack(repo, 2, wants(c2), NONE, NONE);
+			assertContent(idx,
+					Arrays.asList(c1.getId(), c2.getId(), c1.getTree().getId(),
+							c2.getTree().getId(), contentA.getId(),
+							contentB.getId()));
 
-		PackIndex idx = writeShallowPack(repo, 2, wants(c2), NONE, NONE);
-		assertContent(idx,
-				Arrays.asList(c1.getId(), c2.getId(), c1.getTree().getId(),
-						c2.getTree().getId(), contentA.getId(),
-						contentB.getId()));
-
-		// Client already has blobs A and B, verify those are not packed.
-		idx = writeShallowPack(repo, 2, wants(c5), haves(c1, c2), shallows(c1));
-		assertContent(idx,
-				Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
-						c5.getTree().getId(), contentC.getId(),
-						contentD.getId(), contentE.getId()));
+			// Client already has blobs A and B, verify those are not packed.
+			idx = writeShallowPack(repo, 2, wants(c5), haves(c1, c2),
+					shallows(c1));
+			assertContent(idx,
+					Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
+							c5.getTree().getId(), contentC.getId(),
+							contentD.getId(), contentE.getId()));
+		}
 	}
 
 	@Test
 	public void testShallowFetchShallowParentDepth1() throws Exception {
-		FileRepository repo = setupRepoForShallowFetch();
+		try (FileRepository repo = setupRepoForShallowFetch()) {
+			PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
+			assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
+					contentA.getId(), contentB.getId(), contentC.getId(),
+					contentD.getId(), contentE.getId()));
 
-		PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
-		assertContent(idx,
-				Arrays.asList(c5.getId(), c5.getTree().getId(),
-						contentA.getId(), contentB.getId(), contentC.getId(),
-						contentD.getId(), contentE.getId()));
-
-		idx = writeShallowPack(repo, 1, wants(c4), haves(c5), shallows(c5));
-		assertContent(idx, Arrays.asList(c4.getId(), c4.getTree().getId()));
+			idx = writeShallowPack(repo, 1, wants(c4), haves(c5), shallows(c5));
+			assertContent(idx, Arrays.asList(c4.getId(), c4.getTree().getId()));
+		}
 	}
 
 	@Test
 	public void testShallowFetchShallowParentDepth2() throws Exception {
-		FileRepository repo = setupRepoForShallowFetch();
+		try (FileRepository repo = setupRepoForShallowFetch()) {
+			PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
+			assertContent(idx,
+					Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
+							c5.getTree().getId(), contentA.getId(),
+							contentB.getId(), contentC.getId(),
+							contentD.getId(), contentE.getId()));
 
-		PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
-		assertContent(idx,
-				Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
-						c5.getTree().getId(), contentA.getId(),
-						contentB.getId(), contentC.getId(), contentD.getId(),
-						contentE.getId()));
-
-		idx = writeShallowPack(repo, 2, wants(c3), haves(c4, c5), shallows(c4));
-		assertContent(idx, Arrays.asList(c2.getId(), c3.getId(),
-				c2.getTree().getId(), c3.getTree().getId()));
+			idx = writeShallowPack(repo, 2, wants(c3), haves(c4, c5),
+					shallows(c4));
+			assertContent(idx, Arrays.asList(c2.getId(), c3.getId(),
+					c2.getTree().getId(), c3.getTree().getId()));
+		}
 	}
 
 	@Test
 	public void testShallowFetchShallowAncestorDepth1() throws Exception {
-		FileRepository repo = setupRepoForShallowFetch();
+		try (FileRepository repo = setupRepoForShallowFetch()) {
+			PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
+			assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
+					contentA.getId(), contentB.getId(), contentC.getId(),
+					contentD.getId(), contentE.getId()));
 
-		PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
-		assertContent(idx,
-				Arrays.asList(c5.getId(), c5.getTree().getId(),
-						contentA.getId(), contentB.getId(), contentC.getId(),
-						contentD.getId(), contentE.getId()));
-
-		idx = writeShallowPack(repo, 1, wants(c3), haves(c5), shallows(c5));
-		assertContent(idx, Arrays.asList(c3.getId(), c3.getTree().getId()));
+			idx = writeShallowPack(repo, 1, wants(c3), haves(c5), shallows(c5));
+			assertContent(idx, Arrays.asList(c3.getId(), c3.getTree().getId()));
+		}
 	}
 
 	@Test
 	public void testShallowFetchShallowAncestorDepth2() throws Exception {
-		FileRepository repo = setupRepoForShallowFetch();
+		try (FileRepository repo = setupRepoForShallowFetch()) {
+			PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
+			assertContent(idx,
+					Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
+							c5.getTree().getId(), contentA.getId(),
+							contentB.getId(), contentC.getId(),
+							contentD.getId(), contentE.getId()));
 
-		PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
-		assertContent(idx,
-				Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
-						c5.getTree().getId(), contentA.getId(),
-						contentB.getId(), contentC.getId(), contentD.getId(),
-						contentE.getId()));
-
-		idx = writeShallowPack(repo, 2, wants(c2), haves(c4, c5), shallows(c4));
-		assertContent(idx, Arrays.asList(c1.getId(), c2.getId(),
-				c1.getTree().getId(), c2.getTree().getId()));
+			idx = writeShallowPack(repo, 2, wants(c2), haves(c4, c5),
+					shallows(c4));
+			assertContent(idx, Arrays.asList(c1.getId(), c2.getId(),
+					c1.getTree().getId(), c2.getTree().getId()));
+		}
 	}
 
 	private FileRepository setupRepoForShallowFetch() throws Exception {
 		FileRepository repo = createBareRepository();
+		// TestRepository will close the repo, but we need to return an open
+		// one!
+		repo.incrementOpen();
 		try (TestRepository<Repository> r = new TestRepository<>(repo)) {
 			BranchBuilder bb = r.branch("refs/heads/master");
 			contentA = r.blob("A");
@@ -680,8 +685,9 @@
 	private static PackIndex writePack(FileRepository repo,
 			Set<? extends ObjectId> want, Set<ObjectIdSet> excludeObjects)
 					throws IOException {
-		RevWalk walk = new RevWalk(repo);
-		return writePack(repo, walk, 0, want, NONE, excludeObjects);
+		try (RevWalk walk = new RevWalk(repo)) {
+			return writePack(repo, walk, 0, want, NONE, excludeObjects);
+		}
 	}
 
 	private static PackIndex writeShallowPack(FileRepository repo, int depth,
@@ -689,9 +695,10 @@
 			Set<? extends ObjectId> shallow) throws IOException {
 		// During negotiation, UploadPack would have set up a DepthWalk and
 		// marked the client's "shallow" commits. Emulate that here.
-		DepthWalk.RevWalk walk = new DepthWalk.RevWalk(repo, depth - 1);
-		walk.assumeShallow(shallow);
-		return writePack(repo, walk, depth, want, have, EMPTY_ID_SET);
+		try (DepthWalk.RevWalk walk = new DepthWalk.RevWalk(repo, depth - 1)) {
+			walk.assumeShallow(shallow);
+			return writePack(repo, walk, depth, want, have, EMPTY_ID_SET);
+		}
 	}
 
 	private static PackIndex writePack(FileRepository repo, RevWalk walk,
@@ -707,6 +714,7 @@
 			if (depth > 0) {
 				pw.setShallowPack(depth, null);
 			}
+			// ow doesn't need to be closed; caller closes walk.
 			ObjectWalk ow = walk.toObjectWalkWithSameObjects();
 
 			pw.preparePack(NullProgressMonitor.INSTANCE, ow, want, have, NONE);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index 3a43564..bd2c203 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -45,9 +45,12 @@
 
 package org.eclipse.jgit.internal.storage.file;
 
-import static org.eclipse.jgit.junit.Assert.assertEquals;
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.junit.Assert.assertEquals;
 import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
+import static org.eclipse.jgit.lib.RefUpdate.Result.FORCED;
+import static org.eclipse.jgit.lib.RefUpdate.Result.IO_FAILURE;
+import static org.eclipse.jgit.lib.RefUpdate.Result.LOCK_FAILURE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -82,7 +85,6 @@
 import org.junit.Test;
 
 public class RefUpdateTest extends SampleDataRepositoryTestCase {
-
 	private void writeSymref(String src, String dst) throws IOException {
 		RefUpdate u = db.updateRef(src);
 		switch (u.link(dst)) {
@@ -233,6 +235,17 @@
 	}
 
 	@Test
+	public void testWriteReflog() throws IOException {
+		ObjectId pid = db.resolve("refs/heads/master^");
+		RefUpdate updateRef = db.updateRef("refs/heads/master");
+		updateRef.setNewObjectId(pid);
+		updateRef.setForceUpdate(true);
+		Result update = updateRef.update();
+		assertEquals(Result.FORCED, update);
+		assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
+	}
+
+	@Test
 	public void testLooseDelete() throws IOException {
 		final String newRef = "refs/heads/abc";
 		RefUpdate ref = updateRef(newRef);
@@ -379,6 +392,8 @@
 		refUpdate.setNewObjectId(ObjectId.zeroId());
 		Result updateResult = refUpdate.update();
 		assertEquals(Result.FORCED, updateResult);
+
+		assertEquals(ObjectId.zeroId(), db.exactRef("HEAD").getObjectId());
 		Result deleteHeadResult = db.updateRef(Constants.HEAD).delete();
 		assertEquals(Result.NO_CHANGE, deleteHeadResult);
 
@@ -903,6 +918,45 @@
 	}
 
 	@Test
+	public void testUpdateChecksOldValue() throws Exception {
+		ObjectId cur = db.resolve("master");
+		ObjectId prev = db.resolve("master^");
+		RefUpdate u1 = db.updateRef("refs/heads/master");
+		RefUpdate u2 = db.updateRef("refs/heads/master");
+
+		u1.setExpectedOldObjectId(cur);
+		u1.setNewObjectId(prev);
+		u1.setForceUpdate(true);
+
+		u2.setExpectedOldObjectId(cur);
+		u2.setNewObjectId(prev);
+		u2.setForceUpdate(true);
+
+		assertEquals(FORCED, u1.update());
+		assertEquals(LOCK_FAILURE, u2.update());
+	}
+
+	@Test
+	public void testRenameAtomic() throws IOException {
+		ObjectId prevId = db.resolve("refs/heads/master^");
+
+		RefRename rename = db.renameRef("refs/heads/master", "refs/heads/newmaster");
+
+		RefUpdate updateRef = db.updateRef("refs/heads/master");
+		updateRef.setNewObjectId(prevId);
+		updateRef.setForceUpdate(true);
+		assertEquals(FORCED, updateRef.update());
+		assertEquals(RefUpdate.Result.LOCK_FAILURE, rename.rename());
+	}
+
+	@Test
+	public void testRenameSymref() throws IOException {
+		db.resolve("HEAD");
+		RefRename r = db.renameRef("HEAD", "KOPF");
+		assertEquals(IO_FAILURE, r.rename());
+	}
+
+	@Test
 	public void testRenameRefNameColission1avoided() throws IOException {
 		// setup
 		ObjectId rb = db.resolve("refs/heads/b");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index 43f30d8..5100c1c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -145,13 +145,14 @@
 		}
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = (FileRepository) new FileRepositoryBuilder()
-				.setGitDir(theDir).build();
-		assertEqualsPath(theDir, r.getDirectory());
-		assertEqualsPath(repo1Parent, r.getWorkTree());
-		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
-		assertEqualsPath(new File(theDir, Constants.OBJECTS), r.getObjectDatabase()
-				.getDirectory());
+		try (FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(theDir).build()) {
+			assertEqualsPath(theDir, r.getDirectory());
+			assertEqualsPath(repo1Parent, r.getWorkTree());
+			assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+			assertEqualsPath(new File(theDir, Constants.OBJECTS),
+					r.getObjectDatabase().getDirectory());
+		}
 	}
 
 	/**
@@ -170,14 +171,15 @@
 		}
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = (FileRepository) new FileRepositoryBuilder()
+		try (FileRepository r = (FileRepository) new FileRepositoryBuilder()
 				.setGitDir(theDir).setWorkTree(repo1Parent.getParentFile())
-				.build();
-		assertEqualsPath(theDir, r.getDirectory());
-		assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree());
-		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
-		assertEqualsPath(new File(theDir, Constants.OBJECTS), r.getObjectDatabase()
-				.getDirectory());
+				.build()) {
+			assertEqualsPath(theDir, r.getDirectory());
+			assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree());
+			assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+			assertEqualsPath(new File(theDir, Constants.OBJECTS),
+					r.getObjectDatabase().getDirectory());
+		}
 	}
 
 	/**
@@ -195,13 +197,14 @@
 		}
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = (FileRepository) new FileRepositoryBuilder()
-				.setWorkTree(repo1Parent).build();
-		assertEqualsPath(theDir, r.getDirectory());
-		assertEqualsPath(repo1Parent, r.getWorkTree());
-		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
-		assertEqualsPath(new File(theDir, Constants.OBJECTS), r.getObjectDatabase()
-				.getDirectory());
+		try (FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setWorkTree(repo1Parent).build()) {
+			assertEqualsPath(theDir, r.getDirectory());
+			assertEqualsPath(repo1Parent, r.getWorkTree());
+			assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+			assertEqualsPath(new File(theDir, Constants.OBJECTS),
+					r.getObjectDatabase().getDirectory());
+		}
 	}
 
 	/**
@@ -224,13 +227,14 @@
 		}
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = (FileRepository) new FileRepositoryBuilder()
-				.setGitDir(theDir).build();
-		assertEqualsPath(theDir, r.getDirectory());
-		assertEqualsPath(workdir, r.getWorkTree());
-		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
-		assertEqualsPath(new File(theDir, Constants.OBJECTS), r.getObjectDatabase()
-				.getDirectory());
+		try (FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(theDir).build()) {
+			assertEqualsPath(theDir, r.getDirectory());
+			assertEqualsPath(workdir, r.getWorkTree());
+			assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+			assertEqualsPath(new File(theDir, Constants.OBJECTS),
+					r.getObjectDatabase().getDirectory());
+		}
 	}
 
 	/**
@@ -253,13 +257,14 @@
 		}
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = (FileRepository) new FileRepositoryBuilder()
-				.setGitDir(theDir).build();
-		assertEqualsPath(theDir, r.getDirectory());
-		assertEqualsPath(workdir, r.getWorkTree());
-		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
-		assertEqualsPath(new File(theDir, Constants.OBJECTS), r.getObjectDatabase()
-				.getDirectory());
+		try (FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(theDir).build()) {
+			assertEqualsPath(theDir, r.getDirectory());
+			assertEqualsPath(workdir, r.getWorkTree());
+			assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+			assertEqualsPath(new File(theDir, Constants.OBJECTS),
+					r.getObjectDatabase().getDirectory());
+		}
 	}
 
 	/**
@@ -306,17 +311,20 @@
 		// open when we create it we won't write the object file out as a loose
 		// object (as it already exists in the pack).
 		//
-		final Repository newdb = createBareRepository();
-		try (ObjectInserter oi = newdb.newObjectInserter()) {
-			final ObjectId treeId = oi.insert(new TreeFormatter());
-			assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904",
-					treeId.name());
-		}
+		try (Repository newdb = createBareRepository()) {
+			try (ObjectInserter oi = newdb.newObjectInserter()) {
+				final ObjectId treeId = oi.insert(new TreeFormatter());
+				assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+						treeId.name());
+			}
 
-		final File o = new File(new File(new File(newdb.getDirectory(),
-				Constants.OBJECTS), "4b"), "825dc642cb6eb9a060e54bf8d69288fbee4904");
-		assertTrue("Exists " + o, o.isFile());
-		assertTrue("Read-only " + o, !o.canWrite());
+			final File o = new File(
+					new File(new File(newdb.getDirectory(), Constants.OBJECTS),
+							"4b"),
+					"825dc642cb6eb9a060e54bf8d69288fbee4904");
+			assertTrue("Exists " + o, o.isFile());
+			assertTrue("Read-only " + o, !o.canWrite());
+		}
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
index 11d6439..53d13f1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
@@ -187,6 +187,19 @@
 		}
 	}
 
+	@Test
+	public void tableByIDDeletion() throws IOException {
+		List<Ref> delta1 = Arrays.asList(
+				ref("refs/heads/apple", 1),
+				ref("refs/heads/master", 2));
+		List<Ref> delta2 = Arrays.asList(ref("refs/heads/master", 3));
+
+		MergedReftable mr = merge(write(delta1), write(delta2));
+		try (RefCursor rc = mr.byObjectId(id(2))) {
+			assertFalse(rc.next());
+		}
+	}
+
 	@SuppressWarnings("boxing")
 	@Test
 	public void fourTableScan() throws IOException {
@@ -225,29 +238,6 @@
 	}
 
 	@Test
-	public void scanDuplicates() throws IOException {
-		List<Ref> delta1 = Arrays.asList(
-				ref("refs/heads/apple", 1),
-				ref("refs/heads/banana", 2));
-		List<Ref> delta2 = Arrays.asList(
-				ref("refs/heads/apple", 3),
-				ref("refs/heads/apple", 4));
-
-		MergedReftable mr = merge(write(delta1, 1000), write(delta2, 2000));
-		try (RefCursor rc = mr.allRefs()) {
-			assertTrue(rc.next());
-			assertEquals("refs/heads/apple", rc.getRef().getName());
-			assertEquals(id(3), rc.getRef().getObjectId());
-			assertEquals(2000, rc.getRef().getUpdateIndex());
-			assertTrue(rc.next());
-			assertEquals("refs/heads/banana", rc.getRef().getName());
-			assertEquals(id(2), rc.getRef().getObjectId());
-			assertEquals(1000, rc.getRef().getUpdateIndex());
-			assertFalse(rc.next());
-		}
-	}
-
-	@Test
 	public void scanIncludeDeletes() throws IOException {
 		List<Ref> delta1 = Arrays.asList(ref("refs/heads/next", 4));
 		List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
@@ -297,10 +287,10 @@
 	@Test
 	public void missedUpdate() throws IOException {
 		ByteArrayOutputStream buf = new ByteArrayOutputStream();
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(buf)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(3)
-				.begin(buf);
+				.begin();
 		writer.writeRef(ref("refs/heads/a", 1), 1);
 		writer.writeRef(ref("refs/heads/c", 3), 3);
 		writer.finish();
@@ -337,13 +327,13 @@
 		List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
 		List<Ref> delta3 = Arrays.asList(ref("refs/heads/master", 8));
 
-		ReftableCompactor compactor = new ReftableCompactor();
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		ReftableCompactor compactor = new ReftableCompactor(out);
 		compactor.addAll(Arrays.asList(
 				read(write(delta1)),
 				read(write(delta2)),
 				read(write(delta3))));
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		compactor.compact(out);
+		compactor.compact();
 		byte[] table = out.toByteArray();
 
 		ReftableReader reader = read(table);
@@ -414,7 +404,7 @@
 	}
 
 	private static MergedReftable merge(byte[]... table) {
-		List<Reftable> stack = new ArrayList<>(table.length);
+		List<ReftableReader> stack = new ArrayList<>(table.length);
 		for (byte[] b : table) {
 			stack.add(read(b));
 		}
@@ -461,10 +451,10 @@
 	private byte[] write(Collection<Ref> refs, long updateIndex)
 			throws IOException {
 		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-		new ReftableWriter()
+		new ReftableWriter(buffer)
 				.setMinUpdateIndex(updateIndex)
 				.setMaxUpdateIndex(updateIndex)
-				.begin(buffer)
+				.begin()
 				.sortAndWriteRefs(refs)
 				.finish();
 		return buffer.toByteArray();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java
index 1ea7309..0947dd2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java
@@ -52,7 +52,9 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import org.eclipse.jgit.internal.storage.io.BlockSource;
 import org.eclipse.jgit.internal.storage.reftable.ReftableWriter.Stats;
@@ -67,9 +69,10 @@
 
 	@Test
 	public void noTables() throws IOException {
-		ReftableCompactor compactor = new ReftableCompactor();
+		ReftableCompactor compactor;
 		try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-			compactor.compact(out);
+			compactor = new ReftableCompactor(out);
+			compactor.compact();
 		}
 		Stats stats = compactor.getStats();
 		assertEquals(0, stats.minUpdateIndex());
@@ -81,10 +84,10 @@
 	public void oneTable() throws IOException {
 		byte[] inTab;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(0)
 				.setMaxUpdateIndex(0)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(ref(MASTER, 1));
 			writer.finish();
@@ -92,10 +95,13 @@
 		}
 
 		byte[] outTab;
-		ReftableCompactor compactor = new ReftableCompactor();
+		ReftableCompactor compactor;
 		try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
-			compactor.tryAddFirst(read(inTab));
-			compactor.compact(outBuf);
+			compactor = new ReftableCompactor(outBuf);
+			List<ReftableReader> readers = new ArrayList<>();
+			readers.add(read(inTab));
+			compactor.addAll(readers);
+			compactor.compact();
 			outTab = outBuf.toByteArray();
 		}
 		Stats stats = compactor.getStats();
@@ -116,10 +122,10 @@
 	public void twoTablesOneRef() throws IOException {
 		byte[] inTab1;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(0)
 				.setMaxUpdateIndex(0)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(ref(MASTER, 1));
 			writer.finish();
@@ -128,10 +134,10 @@
 
 		byte[] inTab2;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(1)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(ref(MASTER, 2));
 			writer.finish();
@@ -139,10 +145,11 @@
 		}
 
 		byte[] outTab;
-		ReftableCompactor compactor = new ReftableCompactor();
+		ReftableCompactor compactor;
 		try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
+			compactor = new ReftableCompactor(outBuf);
 			compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
-			compactor.compact(outBuf);
+			compactor.compact();
 			outTab = outBuf.toByteArray();
 		}
 		Stats stats = compactor.getStats();
@@ -163,10 +170,10 @@
 	public void twoTablesTwoRefs() throws IOException {
 		byte[] inTab1;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(0)
 				.setMaxUpdateIndex(0)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(ref(MASTER, 1));
 			writer.writeRef(ref(NEXT, 2));
@@ -176,10 +183,10 @@
 
 		byte[] inTab2;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(1)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(ref(MASTER, 3));
 			writer.finish();
@@ -187,10 +194,11 @@
 		}
 
 		byte[] outTab;
-		ReftableCompactor compactor = new ReftableCompactor();
+		ReftableCompactor compactor;
 		try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
+			compactor =  new ReftableCompactor(outBuf);
 			compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
-			compactor.compact(outBuf);
+			compactor.compact();
 			outTab = outBuf.toByteArray();
 		}
 		Stats stats = compactor.getStats();
@@ -216,10 +224,10 @@
 	public void twoTablesIncludeOneDelete() throws IOException {
 		byte[] inTab1;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(0)
 				.setMaxUpdateIndex(0)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(ref(MASTER, 1));
 			writer.finish();
@@ -228,10 +236,10 @@
 
 		byte[] inTab2;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(1)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(tombstone(MASTER));
 			writer.finish();
@@ -239,11 +247,12 @@
 		}
 
 		byte[] outTab;
-		ReftableCompactor compactor = new ReftableCompactor();
+		ReftableCompactor compactor;
 		try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
+			compactor = new ReftableCompactor(outBuf);
 			compactor.setIncludeDeletes(true);
 			compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
-			compactor.compact(outBuf);
+			compactor.compact();
 			outTab = outBuf.toByteArray();
 		}
 		Stats stats = compactor.getStats();
@@ -261,10 +270,10 @@
 	public void twoTablesNotIncludeOneDelete() throws IOException {
 		byte[] inTab1;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(0)
 				.setMaxUpdateIndex(0)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(ref(MASTER, 1));
 			writer.finish();
@@ -273,10 +282,10 @@
 
 		byte[] inTab2;
 		try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
-			ReftableWriter writer = new ReftableWriter()
+			ReftableWriter writer = new ReftableWriter(inBuf)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(1)
-				.begin(inBuf);
+				.begin();
 
 			writer.writeRef(tombstone(MASTER));
 			writer.finish();
@@ -284,11 +293,12 @@
 		}
 
 		byte[] outTab;
-		ReftableCompactor compactor = new ReftableCompactor();
+		ReftableCompactor compactor;
 		try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
+			compactor = new ReftableCompactor(outBuf);
 			compactor.setIncludeDeletes(false);
 			compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
-			compactor.compact(outBuf);
+			compactor.compact();
 			outTab = outBuf.toByteArray();
 		}
 		Stats stats = compactor.getStats();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
index 0e33fa6..229c753 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
@@ -66,6 +66,8 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.io.BlockSource;
@@ -76,6 +78,7 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.lib.SymbolicRef;
+import org.hamcrest.Matchers;
 import org.junit.Test;
 
 public class ReftableTest {
@@ -134,9 +137,9 @@
 		byte[] table;
 		ReftableConfig cfg = new ReftableConfig();
 		cfg.setIndexObjects(false);
-		ReftableWriter writer = new ReftableWriter().setConfig(cfg);
 		try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
-			writer.begin(buf);
+			ReftableWriter writer = new ReftableWriter(buf).setConfig(cfg);
+			writer.begin();
 			assertEquals(92, writer.estimateTotalBytes());
 			writer.writeRef(exp);
 			assertEquals(expBytes, writer.estimateTotalBytes());
@@ -146,12 +149,13 @@
 		assertEquals(expBytes, table.length);
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void estimateCurrentBytesWithIndex() throws IOException {
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 5670; i++) {
-			refs.add(ref(String.format("refs/heads/%04d", i), i));
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("refs/heads/%04d", i), i);
+			refs.add(ref);
 		}
 
 		ReftableConfig cfg = new ReftableConfig();
@@ -160,9 +164,9 @@
 
 		int expBytes = 147860;
 		byte[] table;
-		ReftableWriter writer = new ReftableWriter().setConfig(cfg);
 		try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
-			writer.begin(buf);
+			ReftableWriter writer = new ReftableWriter(buf).setConfig(cfg);
+			writer.begin();
 			writer.sortAndWriteRefs(refs);
 			assertEquals(expBytes, writer.estimateTotalBytes());
 			writer.finish();
@@ -174,6 +178,69 @@
 	}
 
 	@Test
+	public void hasObjMapRefs() throws IOException {
+		ArrayList<Ref> refs = new ArrayList<>();
+		refs.add(ref(MASTER, 1));
+		byte[] table = write(refs);
+		ReftableReader t = read(table);
+		assertTrue(t.hasObjectMap());
+	}
+
+	@Test
+	public void hasObjMapRefsSmallTable() throws IOException {
+		ArrayList<Ref> refs = new ArrayList<>();
+		ReftableConfig cfg = new ReftableConfig();
+		cfg.setIndexObjects(false);
+		refs.add(ref(MASTER, 1));
+		byte[] table = write(refs);
+		ReftableReader t = read(table);
+		assertTrue(t.hasObjectMap());
+	}
+
+	@Test
+	public void hasObjLogs() throws IOException {
+		PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+		String msg = "test";
+		ReftableConfig cfg = new ReftableConfig();
+		cfg.setIndexObjects(false);
+
+		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+		ReftableWriter writer = new ReftableWriter(buffer)
+			.setMinUpdateIndex(1)
+			.setConfig(cfg)
+			.setMaxUpdateIndex(1)
+			.begin();
+
+		writer.writeLog("master", 1, who, ObjectId.zeroId(), id(1), msg);
+		writer.finish();
+		byte[] table = buffer.toByteArray();
+
+		ReftableReader t = read(table);
+		assertTrue(t.hasObjectMap());
+	}
+
+	@Test
+	public void hasObjMapRefsNoIndexObjects() throws IOException {
+		ArrayList<Ref> refs = new ArrayList<>();
+		ReftableConfig cfg = new ReftableConfig();
+		cfg.setIndexObjects(false);
+		cfg.setRefBlockSize(256);
+		cfg.setAlignBlocks(true);
+
+		// Fill up 5 blocks.
+		int N = 256 * 5 / 25;
+		for (int i= 0; i < N; i++) {
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("%02d/xxxxxxxxxx", i), i);
+			refs.add(ref);
+		}
+		byte[] table = write(refs, cfg);
+
+		ReftableReader t = read(table);
+		assertFalse(t.hasObjectMap());
+	}
+
+	@Test
 	public void oneIdRef() throws IOException {
 		Ref exp = ref(MASTER, 1);
 		byte[] table = write(exp);
@@ -361,12 +428,13 @@
 		}
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void indexScan() throws IOException {
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 5670; i++) {
-			refs.add(ref(String.format("refs/heads/%04d", i), i));
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("refs/heads/%04d", i), i);
+			refs.add(ref);
 		}
 
 		byte[] table = write(refs);
@@ -375,12 +443,13 @@
 		assertScan(refs, read(table));
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void indexSeek() throws IOException {
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 5670; i++) {
-			refs.add(ref(String.format("refs/heads/%04d", i), i));
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("refs/heads/%04d", i), i);
+			refs.add(ref);
 		}
 
 		byte[] table = write(refs);
@@ -389,12 +458,13 @@
 		assertSeek(refs, read(table));
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void noIndexScan() throws IOException {
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 567; i++) {
-			refs.add(ref(String.format("refs/heads/%03d", i), i));
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("refs/heads/%03d", i), i);
+			refs.add(ref);
 		}
 
 		byte[] table = write(refs);
@@ -404,12 +474,13 @@
 		assertScan(refs, read(table));
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void noIndexSeek() throws IOException {
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 567; i++) {
-			refs.add(ref(String.format("refs/heads/%03d", i), i));
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("refs/heads/%03d", i), i);
+			refs.add(ref);
 		}
 
 		byte[] table = write(refs);
@@ -418,27 +489,29 @@
 	}
 
 	@Test
-	public void invalidRefWriteOrder() throws IOException {
+	public void invalidRefWriteOrderSortAndWrite() {
 		Ref master = ref(MASTER, 1);
-		Ref next = ref(NEXT, 2);
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(new ByteArrayOutputStream())
 			.setMinUpdateIndex(1)
 			.setMaxUpdateIndex(1)
-			.begin(new ByteArrayOutputStream());
+			.begin();
 
-		writer.writeRef(next);
+		List<Ref> refs = new ArrayList<>();
+		refs.add(master);
+		refs.add(master);
+
 		IllegalArgumentException e  = assertThrows(
 			IllegalArgumentException.class,
-			() -> writer.writeRef(master));
+			() -> writer.sortAndWriteRefs(refs));
 		assertThat(e.getMessage(), containsString("records must be increasing"));
 	}
 
 	@Test
 	public void invalidReflogWriteOrderUpdateIndex() throws IOException {
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(new ByteArrayOutputStream())
 			.setMinUpdateIndex(1)
 			.setMaxUpdateIndex(2)
-			.begin(new ByteArrayOutputStream());
+			.begin();
 		PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
 		String msg = "test";
 
@@ -451,10 +524,10 @@
 
 	@Test
 	public void invalidReflogWriteOrderName() throws IOException {
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(new ByteArrayOutputStream())
 			.setMinUpdateIndex(1)
 			.setMaxUpdateIndex(1)
-			.begin(new ByteArrayOutputStream());
+			.begin();
 		PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
 		String msg = "test";
 
@@ -473,10 +546,10 @@
 		String msg = "test";
 
 		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(buffer)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(1)
-				.begin(buffer);
+				.begin();
 
 		writer.writeRef(master);
 		writer.writeRef(next);
@@ -523,16 +596,96 @@
 	}
 
 	@Test
+	public void reflogReader() throws IOException {
+		Ref master = ref(MASTER, 1);
+		Ref next = ref(NEXT, 2);
+
+		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+		ReftableWriter writer = new ReftableWriter(buffer).setMinUpdateIndex(1)
+				.setMaxUpdateIndex(1).begin();
+
+		writer.writeRef(master);
+		writer.writeRef(next);
+
+		PersonIdent who1 = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+		writer.writeLog(MASTER, 3, who1, ObjectId.zeroId(), id(1), "1");
+		PersonIdent who2 = new PersonIdent("Log", "Ger", 1500079710, -8 * 60);
+		writer.writeLog(MASTER, 2, who2, id(1), id(2), "2");
+		PersonIdent who3 = new PersonIdent("Log", "Ger", 1500079711, -8 * 60);
+		writer.writeLog(MASTER, 1, who3, id(2), id(3), "3");
+
+		writer.finish();
+		byte[] table = buffer.toByteArray();
+
+		ReentrantLock lock = new ReentrantLock();
+		ReftableReader t = read(table);
+		ReftableReflogReader rlr = new ReftableReflogReader(lock, t, MASTER);
+
+		assertEquals(rlr.getLastEntry().getWho(), who1);
+		List<PersonIdent> all = rlr.getReverseEntries().stream()
+				.map(x -> x.getWho()).collect(Collectors.toList());
+		Matchers.contains(all, who3, who2, who1);
+
+		assertEquals(rlr.getReverseEntry(1).getWho(), who2);
+
+		List<ReflogEntry> reverse2 = rlr.getReverseEntries(2);
+		Matchers.contains(reverse2, who3, who2);
+
+		List<PersonIdent> more = rlr.getReverseEntries(4).stream()
+				.map(x -> x.getWho()).collect(Collectors.toList());
+		assertEquals(all, more);
+	}
+
+	@Test
+	public void allRefs() throws IOException {
+		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+		ReftableConfig cfg = new ReftableConfig();
+		cfg.setRefBlockSize(1024);
+		cfg.setLogBlockSize(1024);
+		cfg.setAlignBlocks(true);
+		ReftableWriter writer = new ReftableWriter(buffer)
+				.setMinUpdateIndex(1)
+				.setMaxUpdateIndex(1)
+				.setConfig(cfg)
+				.begin();
+		PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+
+		// Fill out the 1st ref block.
+		List<String> names = new ArrayList<>();
+		for (int i = 0; i < 4; i++) {
+			@SuppressWarnings("boxing")
+			String name = new String(new char[220]).replace("\0", String.format("%c", i + 'a'));
+			names.add(name);
+			writer.writeRef(ref(name, i));
+		}
+
+		// Add some log data.
+		writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), "msg");
+		writer.finish();
+		byte[] table = buffer.toByteArray();
+
+		ReftableReader t = read(table);
+		RefCursor c = t.allRefs();
+
+		int j = 0;
+		while (c.next()) {
+			assertEquals(names.get(j), c.getRef().getName());
+			j++;
+		}
+	}
+
+
+	@Test
 	public void reflogSeek() throws IOException {
 		PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
 		String msg = "test";
 		String msgNext = "test next";
 
 		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(buffer)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(1)
-				.begin(buffer);
+				.begin();
 
 		writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
 		writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(2), msgNext);
@@ -572,10 +725,10 @@
 		PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
 
 		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(buffer)
 			.setMinUpdateIndex(1)
 			.setMaxUpdateIndex(1)
-			.begin(buffer);
+			.begin();
 
 		writer.writeLog("branchname", 1, who, ObjectId.zeroId(), id(1), "branchname");
 
@@ -596,10 +749,10 @@
 		String msg = "test";
 
 		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-		ReftableWriter writer = new ReftableWriter()
+		ReftableWriter writer = new ReftableWriter(buffer)
 				.setMinUpdateIndex(1)
 				.setMaxUpdateIndex(1)
-				.begin(buffer);
+				.begin();
 		writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
 		writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(2), msg);
 		writer.finish();
@@ -640,7 +793,6 @@
 		}
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void logScan() throws IOException {
 		ReftableConfig cfg = new ReftableConfig();
@@ -648,11 +800,12 @@
 		cfg.setLogBlockSize(2048);
 
 		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-		ReftableWriter writer = new ReftableWriter(cfg);
-		writer.setMinUpdateIndex(1).setMaxUpdateIndex(1).begin(buffer);
+		ReftableWriter writer = new ReftableWriter(cfg, buffer);
+		writer.setMinUpdateIndex(1).setMaxUpdateIndex(1).begin();
 
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 5670; i++) {
+			@SuppressWarnings("boxing")
 			Ref ref = ref(String.format("refs/heads/%04d", i), i);
 			refs.add(ref);
 			writer.writeRef(ref);
@@ -683,14 +836,21 @@
 			}
 			assertFalse(lc.next());
 		}
+
+		for (Ref exp : refs) {
+ 			try (LogCursor lc = t.seekLog(exp.getName())) {
+				assertTrue("has " + exp.getName(), lc.next());
+			}
+		}
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void byObjectIdOneRefNoIndex() throws IOException {
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 200; i++) {
-			refs.add(ref(String.format("refs/heads/%02d", i), i));
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("refs/heads/%02d", i), i);
+			refs.add(ref);
 		}
 		refs.add(ref("refs/heads/master", 100));
 
@@ -718,12 +878,13 @@
 		}
 	}
 
-	@SuppressWarnings("boxing")
 	@Test
 	public void byObjectIdOneRefWithIndex() throws IOException {
 		List<Ref> refs = new ArrayList<>();
 		for (int i = 1; i <= 5200; i++) {
-			refs.add(ref(String.format("refs/heads/%02d", i), i));
+			@SuppressWarnings("boxing")
+			Ref ref = ref(String.format("refs/heads/%02d", i), i);
+			refs.add(ref);
 		}
 		refs.add(ref("refs/heads/master", 100));
 
@@ -768,7 +929,7 @@
 			cfg.setRefBlockSize(64);
 
 			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-			ReftableWriter writer = new ReftableWriter(cfg).begin(buffer);
+			ReftableWriter writer = new ReftableWriter(cfg, buffer).begin();
 			writer.writeRef(ref("refs/heads/i-am-not-a-teapot", 1));
 			writer.finish();
 			fail("expected BlockSizeTooSmallException");
@@ -852,9 +1013,14 @@
 	}
 
 	private byte[] write(Collection<Ref> refs) throws IOException {
+		return write(refs, new ReftableConfig());
+	}
+
+	private byte[] write(Collection<Ref> refs, ReftableConfig cfg) throws IOException {
 		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-		stats = new ReftableWriter()
-				.begin(buffer)
+		stats = new ReftableWriter(buffer)
+				.setConfig(cfg)
+				.begin()
 				.sortAndWriteRefs(refs)
 				.finish()
 				.getStats();
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 c3f5baa..50e0ed2 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
@@ -441,8 +441,9 @@
 		ReceiveCommand cmd = command(null, B, "refs/txn/tmp");
 		BatchRefUpdate batch = refdb.newBatchUpdate();
 		batch.addCommand(cmd);
-		batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
-
+		try (RevWalk rw = new RevWalk(repo)) {
+			batch.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
 		assertEquals(MessageFormat.format(JGitText.get().invalidRefName,
 				"refs/txn/tmp"), cmd.getMessage());
@@ -461,8 +462,9 @@
 		ReceiveCommand cmd = command(null, B, "refs/heads/pu.lock");
 		BatchRefUpdate batch = refdb.newBatchUpdate();
 		batch.addCommand(cmd);
-		batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
-
+		try (RevWalk rw = new RevWalk(repo)) {
+			batch.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
 		assertEquals(JGitText.get().funnyRefname, cmd.getMessage());
 		assertEquals(txnId, getTxnCommitted());
@@ -481,7 +483,9 @@
 		ReceiveCommand cmd = command(null, B, ORIG_HEAD);
 		BatchRefUpdate batch = refdb.newBatchUpdate();
 		batch.addCommand(cmd);
-		batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+		try (RevWalk rw = new RevWalk(repo)) {
+			batch.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
 		assertEquals(
 				MessageFormat.format(JGitText.get().invalidRefName, ORIG_HEAD),
@@ -500,7 +504,9 @@
 				command(B, A, "refs/heads/masters"));
 		BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
 		batchUpdate.addCommand(commands);
-		batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+		try (RevWalk rw = new RevWalk(repo)) {
+			batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertEquals(txnId, getTxnCommitted());
 
 		assertEquals(REJECTED_NONFASTFORWARD,
@@ -523,7 +529,9 @@
 		BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
 		batchUpdate.setAllowNonFastForwards(true);
 		batchUpdate.addCommand(commands);
-		batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+		try (RevWalk rw = new RevWalk(repo)) {
+			batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertNotEquals(txnId, getTxnCommitted());
 
 		Map<String, Ref> refs = refdb.getRefs(ALL);
@@ -547,13 +555,15 @@
 		BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
 		batchUpdate.setAllowNonFastForwards(true);
 		batchUpdate.addCommand(commands);
-		batchUpdate.execute(new RevWalk(repo) {
+		try (RevWalk rw = new RevWalk(repo) {
 			@Override
 			public boolean isMergedInto(RevCommit base, RevCommit tip) {
 				fail("isMergedInto() should not be called");
 				return false;
 			}
-		}, NullProgressMonitor.INSTANCE);
+		}) {
+			batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertNotEquals(txnId, getTxnCommitted());
 
 		Map<String, Ref> refs = refdb.getRefs(ALL);
@@ -574,7 +584,9 @@
 		BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
 		batchUpdate.setAllowNonFastForwards(true);
 		batchUpdate.addCommand(commands);
-		batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+		try (RevWalk rw = new RevWalk(repo)) {
+			batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertEquals(txnId, getTxnCommitted());
 
 		assertEquals(LOCK_FAILURE, commands.get(0).getResult());
@@ -601,7 +613,9 @@
 		BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
 		batchUpdate.setAllowNonFastForwards(true);
 		batchUpdate.addCommand(commands);
-		batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+		try (RevWalk rw = new RevWalk(repo)) {
+			batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
+		}
 		assertNotEquals(txnId, getTxnCommitted());
 
 		assertEquals(OK, commands.get(0).getResult());
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 e839545..5eb4e6b 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
@@ -341,7 +341,7 @@
 		assertFalse(c.getBoolean("s", "b", true));
 	}
 
-	static enum TestEnum {
+	enum TestEnum {
 		ONE_TWO;
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
index 057e0c8..46fd902 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
@@ -341,48 +341,56 @@
 			String... path) throws GitAPIException, IOException {
 		try (Git git = new Git(db);
 				RevWalk revWalk = new RevWalk(git.getRepository())) {
-			ObjectInserter newObjectInserter;
-			newObjectInserter = git.getRepository().newObjectInserter();
-			ObjectId blobId = newObjectInserter.insert(Constants.OBJ_BLOB,
+			ObjectId blobId;
+			try (ObjectInserter newObjectInserter = git.getRepository()
+					.newObjectInserter()) {
+				blobId = newObjectInserter.insert(Constants.OBJ_BLOB,
 					"data".getBytes(UTF_8));
-			newObjectInserter = git.getRepository().newObjectInserter();
+			}
 			FileMode mode = FileMode.REGULAR_FILE;
 			ObjectId insertId = blobId;
-			for (int i = path.length - 1; i >= 0; --i) {
-				TreeFormatter treeFormatter = new TreeFormatter();
-				treeFormatter.append("goodpath", mode, insertId);
-				insertId = newObjectInserter.insert(treeFormatter);
-				mode = FileMode.TREE;
+			try (ObjectInserter newObjectInserter = git.getRepository()
+					.newObjectInserter()) {
+				for (int i = path.length - 1; i >= 0; --i) {
+					TreeFormatter treeFormatter = new TreeFormatter();
+					treeFormatter.append("goodpath", mode, insertId);
+					insertId = newObjectInserter.insert(treeFormatter);
+					mode = FileMode.TREE;
+				}
 			}
-			newObjectInserter = git.getRepository().newObjectInserter();
-			CommitBuilder commitBuilder = new CommitBuilder();
-			commitBuilder.setAuthor(author);
-			commitBuilder.setCommitter(committer);
-			commitBuilder.setMessage("foo#1");
-			commitBuilder.setTreeId(insertId);
-			ObjectId firstCommitId = newObjectInserter.insert(commitBuilder);
-
-			newObjectInserter = git.getRepository().newObjectInserter();
-			mode = FileMode.REGULAR_FILE;
-			insertId = blobId;
-			for (int i = path.length - 1; i >= 0; --i) {
-				TreeFormatter treeFormatter = new TreeFormatter();
-				treeFormatter.append(path[i].getBytes(UTF_8), 0,
-						path[i].getBytes(UTF_8).length,
-							mode, insertId, true);
-				insertId = newObjectInserter.insert(treeFormatter);
-				mode = FileMode.TREE;
+			ObjectId firstCommitId;
+			try (ObjectInserter newObjectInserter = git.getRepository()
+					.newObjectInserter()) {
+				CommitBuilder commitBuilder = new CommitBuilder();
+				commitBuilder.setAuthor(author);
+				commitBuilder.setCommitter(committer);
+				commitBuilder.setMessage("foo#1");
+				commitBuilder.setTreeId(insertId);
+				firstCommitId = newObjectInserter.insert(commitBuilder);
 			}
+			ObjectId commitId;
+			try (ObjectInserter newObjectInserter = git.getRepository()
+					.newObjectInserter()) {
+				mode = FileMode.REGULAR_FILE;
+				insertId = blobId;
+				for (int i = path.length - 1; i >= 0; --i) {
+					TreeFormatter treeFormatter = new TreeFormatter();
+					treeFormatter.append(path[i].getBytes(UTF_8), 0,
+							path[i].getBytes(UTF_8).length, mode, insertId,
+							true);
+					insertId = newObjectInserter.insert(treeFormatter);
+					mode = FileMode.TREE;
+				}
 
-			// Create another commit
-			commitBuilder = new CommitBuilder();
-			commitBuilder.setAuthor(author);
-			commitBuilder.setCommitter(committer);
-			commitBuilder.setMessage("foo#2");
-			commitBuilder.setTreeId(insertId);
-			commitBuilder.setParentId(firstCommitId);
-			ObjectId commitId = newObjectInserter.insert(commitBuilder);
-
+				// Create another commit
+				CommitBuilder commitBuilder = new CommitBuilder();
+				commitBuilder.setAuthor(author);
+				commitBuilder.setCommitter(committer);
+				commitBuilder.setMessage("foo#2");
+				commitBuilder.setTreeId(insertId);
+				commitBuilder.setParentId(firstCommitId);
+				commitId = newObjectInserter.insert(commitBuilder);
+			}
 			if (!secondCheckout)
 				git.checkout().setStartPoint(revWalk.parseCommit(firstCommitId))
 						.setName("refs/heads/master").setCreateBranch(true).call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index 8092c31..a272c8f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -349,9 +349,11 @@
 			DirCacheEditor editor = dirCache.editor();
 			for (java.util.Map.Entry<String,String> e : indexEntries.entrySet()) {
 				writeTrashFile(e.getKey(), e.getValue());
-				ObjectInserter inserter = db.newObjectInserter();
-				final ObjectId id = inserter.insert(Constants.OBJ_BLOB,
+				ObjectId id;
+				try (ObjectInserter inserter = db.newObjectInserter()) {
+					id = inserter.insert(Constants.OBJ_BLOB,
 						Constants.encode(e.getValue()));
+				}
 				editor.add(new DirCacheEditor.DeletePath(e.getKey()));
 				editor.add(new DirCacheEditor.PathEdit(e.getKey()) {
 					@Override
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
index fa7f5ab..014a587 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
@@ -60,9 +60,11 @@
 import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.submodule.SubmoduleWalk.IgnoreSubmoduleMode;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.junit.Before;
+import org.junit.Test;
 import org.junit.experimental.theories.DataPoints;
 import org.junit.experimental.theories.Theories;
 import org.junit.experimental.theories.Theory;
@@ -295,4 +297,79 @@
 				indexDiff.getAdded().toString());
 	}
 
+	@Test
+	public void testIndexDiffTwoSubmodules() throws Exception {
+		// Create a second submodule
+		try (Repository submodule2 = createWorkRepository()) {
+			JGitTestUtil.writeTrashFile(submodule2, "fileInSubmodule2",
+					"submodule2");
+			Git subGit = Git.wrap(submodule2);
+			subGit.add().addFilepattern("fileInSubmodule2").call();
+			subGit.commit().setMessage("add file to submodule2").call();
+
+			try (Repository sub2 = Git.wrap(db)
+					.submoduleAdd().setPath("modules/submodule2")
+					.setURI(submodule2.getDirectory().toURI().toString())
+					.call()) {
+				writeTrashFile("fileInRoot", "root+");
+				Git rootGit = Git.wrap(db);
+				rootGit.add().addFilepattern("fileInRoot").call();
+				rootGit.commit().setMessage("add submodule2 and root file")
+						.call();
+				// Now change files in both submodules
+				JGitTestUtil.writeTrashFile(submodule_db, "fileInSubmodule",
+						"submodule changed");
+				JGitTestUtil.writeTrashFile(sub2, "fileInSubmodule2",
+						"submodule2 changed");
+				// Set up .gitmodules
+				FileBasedConfig gitmodules = new FileBasedConfig(
+						new File(db.getWorkTree(), Constants.DOT_GIT_MODULES),
+						db.getFS());
+				gitmodules.load();
+				gitmodules.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+						"modules/submodule", ConfigConstants.CONFIG_KEY_IGNORE,
+						"all");
+				gitmodules.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+						"modules/submodule2", ConfigConstants.CONFIG_KEY_IGNORE,
+						"none");
+				gitmodules.save();
+				IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD,
+						new FileTreeIterator(db));
+				assertTrue(indexDiff.diff());
+				String[] modified = indexDiff.getModified()
+						.toArray(new String[0]);
+				Arrays.sort(modified);
+				assertEquals("[.gitmodules, modules/submodule2]",
+						Arrays.toString(modified));
+				// Try again with "dirty"
+				gitmodules.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+						"modules/submodule", ConfigConstants.CONFIG_KEY_IGNORE,
+						"dirty");
+				gitmodules.save();
+				indexDiff = new IndexDiff(db, Constants.HEAD,
+						new FileTreeIterator(db));
+				assertTrue(indexDiff.diff());
+				modified = indexDiff.getModified().toArray(new String[0]);
+				Arrays.sort(modified);
+				assertEquals("[.gitmodules, modules/submodule2]",
+						Arrays.toString(modified));
+				// Test the config override
+				StoredConfig cfg = db.getConfig();
+				cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+						"modules/submodule", ConfigConstants.CONFIG_KEY_IGNORE,
+						"none");
+				cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+						"modules/submodule2", ConfigConstants.CONFIG_KEY_IGNORE,
+						"all");
+				cfg.save();
+				indexDiff = new IndexDiff(db, Constants.HEAD,
+						new FileTreeIterator(db));
+				assertTrue(indexDiff.diff());
+				modified = indexDiff.getModified().toArray(new String[0]);
+				Arrays.sort(modified);
+				assertEquals("[.gitmodules, modules/submodule]",
+						Arrays.toString(modified));
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index ba5aaf1..cf95407 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -81,10 +81,12 @@
 
 	static PathEdit add(final Repository db, final File workdir,
 			final String path) throws FileNotFoundException, IOException {
-		ObjectInserter inserter = db.newObjectInserter();
 		final File f = new File(workdir, path);
-		final ObjectId id = inserter.insert(Constants.OBJ_BLOB,
+		ObjectId id;
+		try (ObjectInserter inserter = db.newObjectInserter()) {
+			id = inserter.insert(Constants.OBJ_BLOB,
 				IO.readFully(f));
+		}
 		return new PathEdit(path) {
 			@Override
 			public void apply(DirCacheEntry ent) {
@@ -122,9 +124,11 @@
 	public void testMissing() throws Exception {
 		File file2 = writeTrashFile("file2", "file2");
 		File file3 = writeTrashFile("dir/file3", "dir/file3");
-		Git git = Git.wrap(db);
-		git.add().addFilepattern("file2").addFilepattern("dir/file3").call();
-		git.commit().setMessage("commit").call();
+		try (Git git = new Git(db)) {
+			git.add().addFilepattern("file2").addFilepattern("dir/file3")
+					.call();
+			git.commit().setMessage("commit").call();
+		}
 		assertTrue(file2.delete());
 		assertTrue(file3.delete());
 		IndexDiff diff = new IndexDiff(db, Constants.HEAD,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java
index d98b792..f0733f4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java
@@ -88,9 +88,8 @@
 		try (InputStream in = new FileInputStream(file)) {
 			if (objectId == null) {
 				return ObjectIdSerializer.read(in);
-			} else {
-				return ObjectIdSerializer.readWithoutMarker(in);
 			}
+			return ObjectIdSerializer.readWithoutMarker(in);
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
index cbb47fa..3f77ca2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
@@ -67,9 +67,8 @@
 				existing.put("refs/heads/a/b", null /* not used */);
 				existing.put("refs/heads/q", null /* not used */);
 				return existing;
-			} else {
-				return Collections.emptyMap();
 			}
+			return Collections.emptyMap();
 		}
 
 		@Override
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 f2f277c6e..9be71c3 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,7 @@
 
 package org.eclipse.jgit.lib;
 
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
@@ -78,7 +78,10 @@
 		// set the logAllRefUpdates parameter to true and check it
 		cfg.setBoolean("core", null, "logallrefupdates", true);
 		cfg.save();
-		assertTrue(cfg.get(CoreConfig.KEY).isLogAllRefUpdates());
+		assertEquals(CoreConfig.LogRefUpdates.TRUE,
+				cfg.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES,
+						CoreConfig.LogRefUpdates.FALSE));
 
 		// do one commit and check that reflog size is increased to 1
 		commit("A Commit\n", commitTime, tz);
@@ -90,13 +93,32 @@
 		// set the logAllRefUpdates parameter to false and check it
 		cfg.setBoolean("core", null, "logallrefupdates", false);
 		cfg.save();
-		assertFalse(cfg.get(CoreConfig.KEY).isLogAllRefUpdates());
+		assertEquals(CoreConfig.LogRefUpdates.FALSE,
+				cfg.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES,
+						CoreConfig.LogRefUpdates.TRUE));
 
 		// do one commit and check that reflog size is 2
 		commit("A Commit\n", commitTime, tz);
+		commitTime += 60 * 1000;
 		assertTrue(
 				"Reflog for HEAD should contain two entries",
 				db.getReflogReader(Constants.HEAD).getReverseEntries().size() == 2);
+
+		// set the logAllRefUpdates parameter to false and check it
+		cfg.setEnum("core", null, "logallrefupdates",
+				CoreConfig.LogRefUpdates.ALWAYS);
+		cfg.save();
+		assertEquals(CoreConfig.LogRefUpdates.ALWAYS,
+				cfg.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES,
+						CoreConfig.LogRefUpdates.FALSE));
+
+		// do one commit and check that reflog size is 3
+		commit("A Commit\n", commitTime, tz);
+		assertTrue("Reflog for HEAD should contain three entries",
+				db.getReflogReader(Constants.HEAD).getReverseEntries()
+						.size() == 3);
 	}
 
 	private void commit(String commitMsg, long commitTime, int tz)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocatorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocatorTest.java
new file mode 100644
index 0000000..220b2be
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocatorTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019, 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.lib.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Locale;
+
+import org.junit.Test;
+
+public class BouncyCastleGpgKeyLocatorTest {
+
+	private static final String USER_ID = "Heinrich Heine <heinrichh@uni-duesseldorf.de>";
+
+	private static boolean match(String userId, String pattern) {
+		return BouncyCastleGpgKeyLocator.containsSigningKey(userId, pattern);
+	}
+
+	@Test
+	public void testFullMatch() throws Exception {
+		assertTrue(match(USER_ID,
+				"=Heinrich Heine <heinrichh@uni-duesseldorf.de>"));
+		assertFalse(match(USER_ID, "=Heinrich Heine"));
+		assertFalse(match(USER_ID, "= "));
+		assertFalse(match(USER_ID, "=heinrichh@uni-duesseldorf.de"));
+	}
+
+	@Test
+	public void testEmpty() throws Exception {
+		assertFalse(match(USER_ID, ""));
+		assertFalse(match(USER_ID, null));
+		assertFalse(match("", ""));
+		assertFalse(match(null, ""));
+		assertFalse(match(null, null));
+		assertFalse(match("", "something"));
+		assertFalse(match(null, "something"));
+	}
+
+	@Test
+	public void testFullEmail() throws Exception {
+		assertTrue(match(USER_ID, "<heinrichh@uni-duesseldorf.de>"));
+		assertTrue(match(USER_ID + " ", "<heinrichh@uni-duesseldorf.de>"));
+		assertFalse(match(USER_ID, "<>"));
+		assertFalse(match(USER_ID, "<h>"));
+		assertFalse(match(USER_ID, "<heinrichh>"));
+		assertFalse(match(USER_ID, "<uni-duesseldorf>"));
+		assertFalse(match(USER_ID, "<h@u>"));
+		assertFalse(match(USER_ID, "<HeinrichH@uni-duesseldorf.de>"));
+		assertFalse(match(USER_ID.substring(0, USER_ID.length() - 1),
+				"<heinrichh@uni-duesseldorf.de>"));
+		assertFalse(match("", "<>"));
+		assertFalse(match("", "<heinrichh@uni-duesseldorf.de>"));
+	}
+
+	@Test
+	public void testPartialEmail() throws Exception {
+		assertTrue(match(USER_ID, "@heinrichh@uni-duesseldorf.de"));
+		assertTrue(match(USER_ID, "@heinrichh"));
+		assertTrue(match(USER_ID, "@duesseldorf"));
+		assertTrue(match(USER_ID, "@uni-d"));
+		assertTrue(match(USER_ID, "@h"));
+		assertTrue(match(USER_ID, "@."));
+		assertTrue(match(USER_ID, "@h@u"));
+		assertFalse(match(USER_ID, "@ "));
+		assertFalse(match(USER_ID, "@"));
+		assertFalse(match(USER_ID, "@Heine"));
+		assertFalse(match(USER_ID, "@HeinrichH"));
+		assertFalse(match(USER_ID, "@Heinrich"));
+		assertFalse(match("", "@"));
+		assertFalse(match("", "@h"));
+	}
+
+	private void substringTests(String prefix) throws Exception {
+		assertTrue(match(USER_ID, prefix + "heinrichh@uni-duesseldorf.de"));
+		assertTrue(match(USER_ID, prefix + "heinrich"));
+		assertTrue(match(USER_ID, prefix + "HEIN"));
+		assertTrue(match(USER_ID, prefix + "Heine <"));
+		assertTrue(match(USER_ID, prefix + "UNI"));
+		assertTrue(match(USER_ID, prefix + "uni"));
+		assertTrue(match(USER_ID, prefix + "rich He"));
+		assertTrue(match(USER_ID, prefix + "h@u"));
+		assertTrue(match(USER_ID, prefix + USER_ID));
+		assertTrue(match(USER_ID, prefix + USER_ID.toUpperCase(Locale.ROOT)));
+		assertFalse(match(USER_ID, prefix + ""));
+		assertFalse(match(USER_ID, prefix + " "));
+		assertFalse(match(USER_ID, prefix + "yy"));
+		assertFalse(match("", prefix + ""));
+		assertFalse(match("", prefix + "uni"));
+	}
+
+	@Test
+	public void testSubstringPlain() throws Exception {
+		substringTests("");
+	}
+
+	@Test
+	public void testSubstringAsterisk() throws Exception {
+		substringTests("*");
+	}
+
+	@Test
+	public void testExplicitFingerprint() throws Exception {
+		assertFalse(match("John Fade <j.fade@example.com>", "0xfade"));
+		assertFalse(match("John Fade <0xfade@example.com>", "0xfade"));
+		assertFalse(match("", "0xfade"));
+	}
+
+	@Test
+	public void testImplicitFingerprint() throws Exception {
+		assertTrue(match("John Fade <j.fade@example.com>", "fade"));
+		assertTrue(match("John Fade <0xfade@example.com>", "fade"));
+		assertTrue(match("John Fade <j.fade@example.com>", "FADE"));
+		assertTrue(match("John Fade <0xfade@example.com>", "FADE"));
+	}
+
+	@Test
+	public void testZeroX() throws Exception {
+		assertTrue(match("John Fade <0xfade@example.com>", "0x"));
+		assertTrue(match("John Fade <0xfade@example.com>", "*0x"));
+		assertTrue(match("John Fade <0xfade@example.com>", "*0xfade"));
+		assertTrue(match("John Fade <0xfade@example.com>", "*0xFADE"));
+		assertTrue(match("John Fade <0xfade@example.com>", "@0xfade"));
+		assertFalse(match("John Fade <0xfade@example.com>", "@0xFADE"));
+		assertFalse(match("", "0x"));
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
index 19f6dcb..aa4392f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
@@ -106,23 +106,24 @@
 		boolean merge = twm.merge(new ObjectId[] { O, T });
 		assertTrue(merge);
 
-		final TreeWalk tw = new TreeWalk(db);
-		tw.setRecursive(true);
-		tw.reset(twm.getResultTreeId());
+		try (TreeWalk tw = new TreeWalk(db)) {
+			tw.setRecursive(true);
+			tw.reset(twm.getResultTreeId());
 
-		assertTrue(tw.next());
-		assertEquals("a", tw.getPathString());
-		assertCorrectId(treeO, tw);
+			assertTrue(tw.next());
+			assertEquals("a", tw.getPathString());
+			assertCorrectId(treeO, tw);
 
-		assertTrue(tw.next());
-		assertEquals("o", tw.getPathString());
-		assertCorrectId(treeO, tw);
+			assertTrue(tw.next());
+			assertEquals("o", tw.getPathString());
+			assertCorrectId(treeO, tw);
 
-		assertTrue(tw.next());
-		assertEquals("t", tw.getPathString());
-		assertCorrectId(treeT, tw);
+			assertTrue(tw.next());
+			assertEquals("t", tw.getPathString());
+			assertCorrectId(treeT, tw);
 
-		assertFalse(tw.next());
+			assertFalse(tw.next());
+		}
 	}
 
 	@Test
@@ -168,19 +169,20 @@
 		boolean merge = twm.merge(new ObjectId[] { B, T });
 		assertTrue(merge);
 
-		final TreeWalk tw = new TreeWalk(db);
-		tw.setRecursive(true);
-		tw.reset(twm.getResultTreeId());
+		try (TreeWalk tw = new TreeWalk(db)) {
+			tw.setRecursive(true);
+			tw.reset(twm.getResultTreeId());
 
-		assertTrue(tw.next());
-		assertEquals("a", tw.getPathString());
-		assertCorrectId(treeB, tw);
+			assertTrue(tw.next());
+			assertEquals("a", tw.getPathString());
+			assertCorrectId(treeB, tw);
 
-		assertTrue(tw.next());
-		assertEquals("t", tw.getPathString());
-		assertCorrectId(treeT, tw);
+			assertTrue(tw.next());
+			assertEquals("t", tw.getPathString());
+			assertCorrectId(treeT, tw);
 
-		assertFalse(tw.next());
+			assertFalse(tw.next());
+		}
 	}
 
 	private static void assertCorrectId(DirCache treeT, TreeWalk tw) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CrissCrossMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CrissCrossMergeTest.java
index a67c750..5c6636c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CrissCrossMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CrissCrossMergeTest.java
@@ -881,9 +881,9 @@
 		}
 
 		StringBuilder result = new StringBuilder();
-		ObjectReader or = r.newObjectReader();
-		try (BufferedReader br = new BufferedReader(
-				new InputStreamReader(or.open(blobId).openStream(), UTF_8))) {
+		try (ObjectReader or = r.newObjectReader();
+				BufferedReader br = new BufferedReader(new InputStreamReader(
+						or.open(blobId).openStream(), UTF_8))) {
 			String line;
 			boolean first = true;
 			while ((line = br.readLine()) != null) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index 62495fb..3379a25 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -1365,8 +1365,8 @@
 	}
 
 	private String readBlob(ObjectId treeish, String path) throws Exception {
-		try (TestRepository<?> tr = new TestRepository<>(db)) {
-			RevWalk rw = tr.getRevWalk();
+		try (TestRepository<?> tr = new TestRepository<>(db);
+				RevWalk rw = tr.getRevWalk()) {
 			RevTree tree = rw.parseTree(treeish);
 			RevObject obj = tr.get(tree, path);
 			if (obj == null) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
index dd2c2e8..6c0b165 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
@@ -174,23 +174,24 @@
 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
 		assertTrue(merge);
 
-		final TreeWalk tw = new TreeWalk(db);
-		tw.setRecursive(true);
-		tw.reset(ourMerger.getResultTreeId());
+		try (TreeWalk tw = new TreeWalk(db)) {
+			tw.setRecursive(true);
+			tw.reset(ourMerger.getResultTreeId());
 
-		assertTrue(tw.next());
-		assertEquals("Makefile", tw.getPathString());
-		assertCorrectId(treeO, tw);
+			assertTrue(tw.next());
+			assertEquals("Makefile", tw.getPathString());
+			assertCorrectId(treeO, tw);
 
-		assertTrue(tw.next());
-		assertEquals("libelf-po/a", tw.getPathString());
-		assertCorrectId(treeO, tw);
+			assertTrue(tw.next());
+			assertEquals("libelf-po/a", tw.getPathString());
+			assertCorrectId(treeO, tw);
 
-		assertTrue(tw.next());
-		assertEquals("libelf/c", tw.getPathString());
-		assertCorrectId(treeT, tw);
+			assertTrue(tw.next());
+			assertEquals("libelf/c", tw.getPathString());
+			assertCorrectId(treeT, tw);
 
-		assertFalse(tw.next());
+			assertFalse(tw.next());
+		}
 	}
 
 	@Test
@@ -226,19 +227,20 @@
 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
 		assertTrue(merge);
 
-		final TreeWalk tw = new TreeWalk(db);
-		tw.setRecursive(true);
-		tw.reset(ourMerger.getResultTreeId());
+		try (TreeWalk tw = new TreeWalk(db)) {
+			tw.setRecursive(true);
+			tw.reset(ourMerger.getResultTreeId());
 
-		assertTrue(tw.next());
-		assertEquals("d/o", tw.getPathString());
-		assertCorrectId(treeO, tw);
+			assertTrue(tw.next());
+			assertEquals("d/o", tw.getPathString());
+			assertCorrectId(treeO, tw);
 
-		assertTrue(tw.next());
-		assertEquals("d/t", tw.getPathString());
-		assertCorrectId(treeT, tw);
+			assertTrue(tw.next());
+			assertEquals("d/t", tw.getPathString());
+			assertCorrectId(treeT, tw);
 
-		assertFalse(tw.next());
+			assertFalse(tw.next());
+		}
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java
index f265315..ac157b6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java
@@ -44,7 +44,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import java.io.IOException;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -55,7 +54,6 @@
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -83,23 +81,18 @@
 		git.commit().setMessage("commit on master").call();
 		MergeResult mergeCall = merge(db.resolve("topic"));
 		ObjectId start = mergeCall.getNewHead();
-		PlotCommitList<PlotLane> commitList = createCommitList(start);
+		try (PlotWalk walk = new PlotWalk(db)) {
+			walk.markStart(walk.parseCommit(start));
+			PlotCommitList<PlotLane> commitList = new PlotCommitList<>();
+			commitList.source(walk);
+			commitList.fillTo(1000);
 
-		for (int i = 0; i < commitList.size(); i++)
-			plotRenderer.paintCommit(commitList.get(i), 30);
+			for (int i = 0; i < commitList.size(); i++)
+				plotRenderer.paintCommit(commitList.get(i), 30);
 
-		List<Integer> indentations = plotRenderer.indentations;
-		assertEquals(indentations.get(2), indentations.get(3));
-	}
-
-	private PlotCommitList<PlotLane> createCommitList(ObjectId start)
-			throws IOException {
-		TestPlotWalk walk = new TestPlotWalk(db);
-		walk.markStart(walk.parseCommit(start));
-		PlotCommitList<PlotLane> commitList = new PlotCommitList<>();
-		commitList.source(walk);
-		commitList.fillTo(1000);
-		return commitList;
+			List<Integer> indentations = plotRenderer.indentations;
+			assertEquals(indentations.get(2), indentations.get(3));
+		}
 	}
 
 	private MergeResult merge(ObjectId includeId) throws GitAPIException {
@@ -107,12 +100,6 @@
 				.include(includeId).call();
 	}
 
-	private static class TestPlotWalk extends PlotWalk {
-		public TestPlotWalk(Repository repo) {
-			super(repo);
-		}
-	}
-
 	private static class TestPlotRenderer extends
 			AbstractPlotRenderer<PlotLane, Object> {
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
index 7297de3..0c367f4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
@@ -135,18 +135,19 @@
 		final RevCommit b = commit(a);
 		final RevCommit c = commit(b);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(c.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(c.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		CommitListAssert test = new CommitListAssert(pcl);
-		test.commit(c).lanePos(0).parents(b);
-		test.commit(b).lanePos(0).parents(a);
-		test.commit(a).lanePos(0).parents();
-		test.noMoreCommits();
+			CommitListAssert test = new CommitListAssert(pcl);
+			test.commit(c).lanePos(0).parents(b);
+			test.commit(b).lanePos(0).parents(a);
+			test.commit(a).lanePos(0).parents();
+			test.noMoreCommits();
+		}
 	}
 
 	@Test
@@ -156,19 +157,20 @@
 		final RevCommit c = commit(a);
 		final RevCommit d = commit(b, c);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(d.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(d.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		CommitListAssert test = new CommitListAssert(pcl);
-		test.commit(d).lanePos(0).parents(b, c);
-		test.commit(c).lanePos(1).parents(a);
-		test.commit(b).lanePos(0).parents(a);
-		test.commit(a).lanePos(0).parents();
-		test.noMoreCommits();
+			CommitListAssert test = new CommitListAssert(pcl);
+			test.commit(d).lanePos(0).parents(b, c);
+			test.commit(c).lanePos(1).parents(a);
+			test.commit(b).lanePos(0).parents(a);
+			test.commit(a).lanePos(0).parents();
+			test.noMoreCommits();
+		}
 	}
 
 	@Test
@@ -177,20 +179,21 @@
 		final RevCommit b = commit(a);
 		final RevCommit c = commit(a);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(b.getId()));
-		pw.markStart(pw.lookupCommit(c.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(b.getId()));
+			pw.markStart(pw.lookupCommit(c.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		Set<Integer> childPositions = asSet(0, 1);
-		CommitListAssert test = new CommitListAssert(pcl);
-		test.commit(c).lanePos(childPositions).parents(a);
-		test.commit(b).lanePos(childPositions).parents(a);
-		test.commit(a).lanePos(0).parents();
-		test.noMoreCommits();
+			Set<Integer> childPositions = asSet(0, 1);
+			CommitListAssert test = new CommitListAssert(pcl);
+			test.commit(c).lanePos(childPositions).parents(a);
+			test.commit(b).lanePos(childPositions).parents(a);
+			test.commit(a).lanePos(0).parents();
+			test.noMoreCommits();
+		}
 	}
 
 	@Test
@@ -200,22 +203,23 @@
 		final RevCommit c = commit(a);
 		final RevCommit d = commit(a);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(b.getId()));
-		pw.markStart(pw.lookupCommit(c.getId()));
-		pw.markStart(pw.lookupCommit(d.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(b.getId()));
+			pw.markStart(pw.lookupCommit(c.getId()));
+			pw.markStart(pw.lookupCommit(d.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		Set<Integer> childPositions = asSet(0, 1, 2);
-		CommitListAssert test = new CommitListAssert(pcl);
-		test.commit(d).lanePos(childPositions).parents(a);
-		test.commit(c).lanePos(childPositions).parents(a);
-		test.commit(b).lanePos(childPositions).parents(a);
-		test.commit(a).lanePos(0).parents();
-		test.noMoreCommits();
+			Set<Integer> childPositions = asSet(0, 1, 2);
+			CommitListAssert test = new CommitListAssert(pcl);
+			test.commit(d).lanePos(childPositions).parents(a);
+			test.commit(c).lanePos(childPositions).parents(a);
+			test.commit(b).lanePos(childPositions).parents(a);
+			test.commit(a).lanePos(0).parents();
+			test.noMoreCommits();
+		}
 	}
 
 	@Test
@@ -228,34 +232,35 @@
 		final RevCommit f = commit(a);
 		final RevCommit g = commit(f);
 
-		PlotWalk pw = new PlotWalk(db);
-		// TODO: when we add unnecessary commit's as tips (e.g. a commit which
-		// is a parent of another tip) the walk will return those commits twice.
-		// Find out why!
-		// pw.markStart(pw.lookupCommit(a.getId()));
-		pw.markStart(pw.lookupCommit(b.getId()));
-		pw.markStart(pw.lookupCommit(c.getId()));
-		pw.markStart(pw.lookupCommit(d.getId()));
-		pw.markStart(pw.lookupCommit(e.getId()));
-		// pw.markStart(pw.lookupCommit(f.getId()));
-		pw.markStart(pw.lookupCommit(g.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			// TODO: when we add unnecessary commit's as tips (e.g. a commit
+			// which is a parent of another tip) the walk will return those
+			// commits twice. Find out why!
+			// pw.markStart(pw.lookupCommit(a.getId()));
+			pw.markStart(pw.lookupCommit(b.getId()));
+			pw.markStart(pw.lookupCommit(c.getId()));
+			pw.markStart(pw.lookupCommit(d.getId()));
+			pw.markStart(pw.lookupCommit(e.getId()));
+			// pw.markStart(pw.lookupCommit(f.getId()));
+			pw.markStart(pw.lookupCommit(g.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		Set<Integer> childPositions = asSet(0, 1, 2, 3, 4);
-		CommitListAssert test = new CommitListAssert(pcl);
-		int posG = test.commit(g).lanePos(childPositions).parents(f)
-				.getLanePos();
-		test.commit(f).lanePos(posG).parents(a);
+			Set<Integer> childPositions = asSet(0, 1, 2, 3, 4);
+			CommitListAssert test = new CommitListAssert(pcl);
+			int posG = test.commit(g).lanePos(childPositions).parents(f)
+					.getLanePos();
+			test.commit(f).lanePos(posG).parents(a);
 
-		test.commit(e).lanePos(childPositions).parents(a);
-		test.commit(d).lanePos(childPositions).parents(a);
-		test.commit(c).lanePos(childPositions).parents(a);
-		test.commit(b).lanePos(childPositions).parents(a);
-		test.commit(a).lanePos(0).parents();
-		test.noMoreCommits();
+			test.commit(e).lanePos(childPositions).parents(a);
+			test.commit(d).lanePos(childPositions).parents(a);
+			test.commit(c).lanePos(childPositions).parents(a);
+			test.commit(b).lanePos(childPositions).parents(a);
+			test.commit(a).lanePos(0).parents();
+			test.noMoreCommits();
+		}
 	}
 
 	@Test
@@ -270,25 +275,26 @@
 		final RevCommit h = commit(f);
 		final RevCommit i = commit(h);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(i.getId()));
-		pw.markStart(pw.lookupCommit(g.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(i.getId()));
+			pw.markStart(pw.lookupCommit(g.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
-		Set<Integer> childPositions = asSet(0, 1);
-		CommitListAssert test = new CommitListAssert(pcl);
-		int posI = test.commit(i).lanePos(childPositions).parents(h)
-				.getLanePos();
-		test.commit(h).lanePos(posI).parents(f);
-		test.commit(g).lanePos(childPositions).parents(a);
-		test.commit(f).lanePos(posI).parents(e, d);
-		test.commit(e).lanePos(posI).parents(c);
-		test.commit(d).lanePos(2).parents(b);
-		test.commit(c).lanePos(posI).parents(b);
-		test.commit(b).lanePos(posI).parents(a);
-		test.commit(a).lanePos(0).parents();
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
+			Set<Integer> childPositions = asSet(0, 1);
+			CommitListAssert test = new CommitListAssert(pcl);
+			int posI = test.commit(i).lanePos(childPositions).parents(h)
+					.getLanePos();
+			test.commit(h).lanePos(posI).parents(f);
+			test.commit(g).lanePos(childPositions).parents(a);
+			test.commit(f).lanePos(posI).parents(e, d);
+			test.commit(e).lanePos(posI).parents(c);
+			test.commit(d).lanePos(2).parents(b);
+			test.commit(c).lanePos(posI).parents(b);
+			test.commit(b).lanePos(posI).parents(a);
+			test.commit(a).lanePos(0).parents();
+		}
 	}
 
 	// test the history of the egit project between 9fdaf3c1 and e76ad9170f
@@ -330,67 +336,71 @@
 		final RevCommit merge_fixed_logged_npe = commit(sort_roots,
 				fix_logged_npe);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(merge_fixed_logged_npe.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(merge_fixed_logged_npe.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		CommitListAssert test = new CommitListAssert(pcl);
+			CommitListAssert test = new CommitListAssert(pcl);
 
-		// Note: all positions of side branches are rather arbitrary, but some
-		// may not overlap. Testing for the positions yielded by the current
-		// implementation, which was manually checked to not overlap.
-		final int mainPos = 0;
-		test.commit(merge_fixed_logged_npe).parents(sort_roots, fix_logged_npe)
-				.lanePos(mainPos);
-		test.commit(fix_logged_npe).parents(merge_changeset_implementation)
-				.lanePos(1);
-		test.commit(sort_roots).parents(merge_update_eclipse).lanePos(mainPos);
-		test.commit(merge_update_eclipse).parents(add_a_clear, update_eclipse)
-				.lanePos(mainPos);
-		test.commit(add_a_clear).parents(fix_broken).lanePos(mainPos);
-		test.commit(fix_broken).parents(merge_disable_comment).lanePos(mainPos);
-		test.commit(merge_disable_comment)
-				.parents(merge_resolve_handler, disable_comment)
-				.lanePos(mainPos);
-		test.commit(disable_comment).parents(clone_operation).lanePos(2);
-		test.commit(merge_resolve_handler)
-				.parents(clone_operation, resolve_handler).lanePos(mainPos);
-		test.commit(update_eclipse).parents(add_Maven).lanePos(3);
-		test.commit(clone_operation).parents(merge_changeset_implementation)
-				.lanePos(mainPos);
-		test.commit(merge_changeset_implementation)
-				.parents(merge_disable_source, changeset_implementation)
-				.lanePos(mainPos);
-		test.commit(merge_disable_source)
-				.parents(update_eclipse_iplog2, disable_source)
-				.lanePos(mainPos);
-		test.commit(update_eclipse_iplog2).parents(merge_use_remote)
-				.lanePos(mainPos);
-		test.commit(disable_source).parents(merge_use_remote).lanePos(1);
-		test.commit(merge_use_remote).parents(update_eclipse_iplog, use_remote)
-				.lanePos(mainPos);
-		test.commit(changeset_implementation).parents(clear_repositorycache)
-				.lanePos(2);
-		test.commit(update_eclipse_iplog).parents(merge_add_Maven)
-				.lanePos(mainPos);
-		test.commit(merge_add_Maven).parents(findToolBar_layout, add_Maven)
-				.lanePos(mainPos);
-		test.commit(findToolBar_layout).parents(clear_repositorycache)
-				.lanePos(mainPos);
-		test.commit(use_remote).parents(clear_repositorycache).lanePos(1);
-		test.commit(add_Maven).parents(clear_repositorycache).lanePos(3);
-		test.commit(clear_repositorycache).parents(merge_remove)
-				.lanePos(mainPos);
-		test.commit(resolve_handler).parents(merge_fix).lanePos(4);
-		test.commit(merge_remove).parents(add_simple, remove_unused)
-				.lanePos(mainPos);
-		test.commit(remove_unused).parents(merge_fix).lanePos(1);
-		test.commit(add_simple).parents(merge_fix).lanePos(mainPos);
-		test.commit(merge_fix).parents().lanePos(mainPos);
-		test.noMoreCommits();
+			// Note: all positions of side branches are rather arbitrary, but
+			// some
+			// may not overlap. Testing for the positions yielded by the current
+			// implementation, which was manually checked to not overlap.
+			final int mainPos = 0;
+			test.commit(merge_fixed_logged_npe)
+					.parents(sort_roots, fix_logged_npe).lanePos(mainPos);
+			test.commit(fix_logged_npe).parents(merge_changeset_implementation)
+					.lanePos(1);
+			test.commit(sort_roots).parents(merge_update_eclipse)
+					.lanePos(mainPos);
+			test.commit(merge_update_eclipse)
+					.parents(add_a_clear, update_eclipse).lanePos(mainPos);
+			test.commit(add_a_clear).parents(fix_broken).lanePos(mainPos);
+			test.commit(fix_broken).parents(merge_disable_comment)
+					.lanePos(mainPos);
+			test.commit(merge_disable_comment)
+					.parents(merge_resolve_handler, disable_comment)
+					.lanePos(mainPos);
+			test.commit(disable_comment).parents(clone_operation).lanePos(2);
+			test.commit(merge_resolve_handler)
+					.parents(clone_operation, resolve_handler).lanePos(mainPos);
+			test.commit(update_eclipse).parents(add_Maven).lanePos(3);
+			test.commit(clone_operation).parents(merge_changeset_implementation)
+					.lanePos(mainPos);
+			test.commit(merge_changeset_implementation)
+					.parents(merge_disable_source, changeset_implementation)
+					.lanePos(mainPos);
+			test.commit(merge_disable_source)
+					.parents(update_eclipse_iplog2, disable_source)
+					.lanePos(mainPos);
+			test.commit(update_eclipse_iplog2).parents(merge_use_remote)
+					.lanePos(mainPos);
+			test.commit(disable_source).parents(merge_use_remote).lanePos(1);
+			test.commit(merge_use_remote)
+					.parents(update_eclipse_iplog, use_remote).lanePos(mainPos);
+			test.commit(changeset_implementation).parents(clear_repositorycache)
+					.lanePos(2);
+			test.commit(update_eclipse_iplog).parents(merge_add_Maven)
+					.lanePos(mainPos);
+			test.commit(merge_add_Maven).parents(findToolBar_layout, add_Maven)
+					.lanePos(mainPos);
+			test.commit(findToolBar_layout).parents(clear_repositorycache)
+					.lanePos(mainPos);
+			test.commit(use_remote).parents(clear_repositorycache).lanePos(1);
+			test.commit(add_Maven).parents(clear_repositorycache).lanePos(3);
+			test.commit(clear_repositorycache).parents(merge_remove)
+					.lanePos(mainPos);
+			test.commit(resolve_handler).parents(merge_fix).lanePos(4);
+			test.commit(merge_remove).parents(add_simple, remove_unused)
+					.lanePos(mainPos);
+			test.commit(remove_unused).parents(merge_fix).lanePos(1);
+			test.commit(add_simple).parents(merge_fix).lanePos(mainPos);
+			test.commit(merge_fix).parents().lanePos(mainPos);
+			test.noMoreCommits();
+		}
 	}
 
 	// test a history where a merge commit has two time the same parent
@@ -403,20 +413,21 @@
 		final RevCommit s1 = commit(m2);
 		final RevCommit s2 = commit(s1);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(m3));
-		pw.markStart(pw.lookupCommit(s2));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(m3));
+			pw.markStart(pw.lookupCommit(s2));
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		CommitListAssert test = new CommitListAssert(pcl);
-		test.commit(s2).nrOfPassingLanes(0);
-		test.commit(s1).nrOfPassingLanes(0);
-		test.commit(m3).nrOfPassingLanes(1);
-		test.commit(m2).nrOfPassingLanes(0);
-		test.commit(m1).nrOfPassingLanes(0);
-		test.noMoreCommits();
+			CommitListAssert test = new CommitListAssert(pcl);
+			test.commit(s2).nrOfPassingLanes(0);
+			test.commit(s1).nrOfPassingLanes(0);
+			test.commit(m3).nrOfPassingLanes(1);
+			test.commit(m2).nrOfPassingLanes(0);
+			test.commit(m1).nrOfPassingLanes(0);
+			test.noMoreCommits();
+		}
 	}
 
 	/**
@@ -465,30 +476,31 @@
 		final RevCommit a4 = commit(a3);
 		final RevCommit a5 = commit(a3, a4);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(b3.getId()));
-		pw.markStart(pw.lookupCommit(c.getId()));
-		pw.markStart(pw.lookupCommit(e.getId()));
-		pw.markStart(pw.lookupCommit(a5.getId()));
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(b3.getId()));
+			pw.markStart(pw.lookupCommit(c.getId()));
+			pw.markStart(pw.lookupCommit(e.getId()));
+			pw.markStart(pw.lookupCommit(a5.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		// test that the commits b1, b2 and b3 are on the same position
-		int bPos = pcl.get(9).lane.position; // b1
-		assertEquals("b2 is an a different position", bPos,
-				pcl.get(7).lane.position);
-		assertEquals("b3 is on a different position", bPos,
-				pcl.get(4).lane.position);
+			// test that the commits b1, b2 and b3 are on the same position
+			int bPos = pcl.get(9).lane.position; // b1
+			assertEquals("b2 is an a different position", bPos,
+					pcl.get(7).lane.position);
+			assertEquals("b3 is on a different position", bPos,
+					pcl.get(4).lane.position);
 
-		// test that nothing blocks the connections between b1, b2 and b3
-		assertNotEquals("b lane is blocked by c", bPos,
-				pcl.get(8).lane.position);
-		assertNotEquals("b lane is blocked by a2", bPos,
-				pcl.get(6).lane.position);
-		assertNotEquals("b lane is blocked by d", bPos,
-				pcl.get(5).lane.position);
+			// test that nothing blocks the connections between b1, b2 and b3
+			assertNotEquals("b lane is blocked by c", bPos,
+					pcl.get(8).lane.position);
+			assertNotEquals("b lane is blocked by a2", bPos,
+					pcl.get(6).lane.position);
+			assertNotEquals("b lane is blocked by d", bPos,
+					pcl.get(5).lane.position);
+		}
 	}
 
 	/**
@@ -517,23 +529,24 @@
 		final RevCommit a4 = commit(a3, b2);
 		final RevCommit b3 = commit(b2);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(a4));
-		pw.markStart(pw.lookupCommit(b3));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(a4));
+			pw.markStart(pw.lookupCommit(b3));
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		Set<Integer> positions = asSet(0, 1);
-		CommitListAssert test = new CommitListAssert(pcl);
-		int posB = test.commit(b3).lanePos(positions).getLanePos();
-		int posA = test.commit(a4).lanePos(positions).getLanePos();
-		test.commit(b2).lanePos(posB);
-		test.commit(a3).lanePos(posA);
-		test.commit(a2).lanePos(posA);
-		test.commit(b1).lanePos(posB);
-		test.commit(a1).lanePos(posA);
-		test.noMoreCommits();
+			Set<Integer> positions = asSet(0, 1);
+			CommitListAssert test = new CommitListAssert(pcl);
+			int posB = test.commit(b3).lanePos(positions).getLanePos();
+			int posA = test.commit(a4).lanePos(positions).getLanePos();
+			test.commit(b2).lanePos(posB);
+			test.commit(a3).lanePos(posA);
+			test.commit(a2).lanePos(posA);
+			test.commit(b1).lanePos(posB);
+			test.commit(a1).lanePos(posA);
+			test.noMoreCommits();
+		}
 	}
 
 	/**
@@ -562,25 +575,26 @@
 		final RevCommit b3 = commit(b2);
 		final RevCommit a4 = commit(a3);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(a4));
-		pw.markStart(pw.lookupCommit(b3));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(a4));
+			pw.markStart(pw.lookupCommit(b3));
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		Set<Integer> positions = asSet(0, 1);
-		CommitListAssert test = new CommitListAssert(pcl);
-		int posA = test.commit(a4).lanePos(positions).getLanePos();
-		int posB = test.commit(b3).lanePos(positions).getLanePos();
-		test.commit(a3).lanePos(posA);
-		test.commit(b2).lanePos(posB);
-		test.commit(a2).lanePos(posA);
-		// b1 is not repositioned, uses "detour lane"
-		// (drawn as a double arc in the ascii graph above)
-		test.commit(b1).lanePos(posB);
-		test.commit(a1).lanePos(posA);
-		test.noMoreCommits();
+			Set<Integer> positions = asSet(0, 1);
+			CommitListAssert test = new CommitListAssert(pcl);
+			int posA = test.commit(a4).lanePos(positions).getLanePos();
+			int posB = test.commit(b3).lanePos(positions).getLanePos();
+			test.commit(a3).lanePos(posA);
+			test.commit(b2).lanePos(posB);
+			test.commit(a2).lanePos(posA);
+			// b1 is not repositioned, uses "detour lane"
+			// (drawn as a double arc in the ascii graph above)
+			test.commit(b1).lanePos(posB);
+			test.commit(a1).lanePos(posA);
+			test.noMoreCommits();
+		}
 	}
 
 	/**
@@ -611,24 +625,25 @@
 		final RevCommit a4 = commit(a3, b1);
 		final RevCommit b2 = commit(b1);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(a4));
-		pw.markStart(pw.lookupCommit(b2));
-		pw.markStart(pw.lookupCommit(c));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(a4));
+			pw.markStart(pw.lookupCommit(b2));
+			pw.markStart(pw.lookupCommit(c));
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		Set<Integer> positions = asSet(0, 1, 2);
-		CommitListAssert test = new CommitListAssert(pcl);
-		int posB = test.commit(b2).lanePos(positions).getLanePos();
-		int posA = test.commit(a4).lanePos(positions).getLanePos();
-		test.commit(a3).lanePos(posA);
-		test.commit(c).lanePos(positions);
-		test.commit(a2).lanePos(posA);
-		test.commit(b1).lanePos(posB); // repositioned to go around c
-		test.commit(a1).lanePos(posA);
-		test.noMoreCommits();
+			Set<Integer> positions = asSet(0, 1, 2);
+			CommitListAssert test = new CommitListAssert(pcl);
+			int posB = test.commit(b2).lanePos(positions).getLanePos();
+			int posA = test.commit(a4).lanePos(positions).getLanePos();
+			test.commit(a3).lanePos(posA);
+			test.commit(c).lanePos(positions);
+			test.commit(a2).lanePos(posA);
+			test.commit(b1).lanePos(posB); // repositioned to go around c
+			test.commit(a1).lanePos(posA);
+			test.noMoreCommits();
+		}
 	}
 
 	/**
@@ -651,22 +666,24 @@
 		final RevCommit a3 = commit(a2);
 		final RevCommit b1 = commit(a1);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(a3));
-		pw.markStart(pw.lookupCommit(b1));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(2); // don't process a1
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(a3));
+			pw.markStart(pw.lookupCommit(b1));
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(2); // don't process a1
 
-		Set<Integer> positions = asSet(0, 1);
-		CommitListAssert test = new CommitListAssert(pcl);
-		PlotLane laneB = test.commit(b1).lanePos(positions).current.getLane();
-		int posA = test.commit(a3).lanePos(positions).getLanePos();
-		test.commit(a2).lanePos(posA);
-		assertArrayEquals(
-				"Although the parent of b1, a1, is not processed yet, the b lane should still be drawn",
-				new PlotLane[] { laneB }, test.current.passingLanes);
-		test.noMoreCommits();
+			Set<Integer> positions = asSet(0, 1);
+			CommitListAssert test = new CommitListAssert(pcl);
+			PlotLane laneB = test.commit(b1).lanePos(positions).current
+					.getLane();
+			int posA = test.commit(a3).lanePos(positions).getLanePos();
+			test.commit(a2).lanePos(posA);
+			assertArrayEquals(
+					"Although the parent of b1, a1, is not processed yet, the b lane should still be drawn",
+					new PlotLane[] { laneB }, test.current.passingLanes);
+			test.noMoreCommits();
+		}
 	}
 
 	@Test
@@ -674,17 +691,18 @@
 		final RevCommit a = commit();
 		final RevCommit b = commit();
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(a));
-		pw.markStart(pw.lookupCommit(b));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(a));
+			pw.markStart(pw.lookupCommit(b));
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		CommitListAssert test = new CommitListAssert(pcl);
-		test.commit(b).lanePos(0);
-		test.commit(a).lanePos(0);
-		test.noMoreCommits();
+			CommitListAssert test = new CommitListAssert(pcl);
+			test.commit(b).lanePos(0);
+			test.commit(a).lanePos(0);
+			test.noMoreCommits();
+		}
 	}
 
 	@Test
@@ -693,17 +711,18 @@
 		final RevCommit b1 = commit();
 		final RevCommit b2 = commit(b1);
 
-		PlotWalk pw = new PlotWalk(db);
-		pw.markStart(pw.lookupCommit(a));
-		pw.markStart(pw.lookupCommit(b2));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
-		pcl.source(pw);
-		pcl.fillTo(Integer.MAX_VALUE);
+		try (PlotWalk pw = new PlotWalk(db)) {
+			pw.markStart(pw.lookupCommit(a));
+			pw.markStart(pw.lookupCommit(b2));
+			PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
+			pcl.source(pw);
+			pcl.fillTo(Integer.MAX_VALUE);
 
-		CommitListAssert test = new CommitListAssert(pcl);
-		test.commit(b2).lanePos(0);
-		test.commit(b1).lanePos(0);
-		test.commit(a).lanePos(0);
-		test.noMoreCommits();
+			CommitListAssert test = new CommitListAssert(pcl);
+			test.commit(b2).lanePos(0);
+			test.commit(b1).lanePos(0);
+			test.commit(a).lanePos(0);
+			test.noMoreCommits();
+		}
 	}
 }
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
deleted file mode 100644
index c5d4d42..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/BitmapCalculatorTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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.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/FirstParentRevWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java
index 1fc7a55..d46f48b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java
@@ -49,6 +49,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.revwalk.filter.MessageRevFilter;
 import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 import org.junit.Test;
 
 public class FirstParentRevWalkTest extends RevWalkTestCase {
@@ -425,4 +426,39 @@
 		markStart(a);
 		assertNull(rw.next());
 	}
+
+	@Test
+	public void testWithTopoSortAndTreeFilter() throws Exception {
+		RevCommit a = commit();
+		RevCommit b = commit(tree(file("0", blob("b"))), a);
+		RevCommit c = commit(tree(file("0", blob("c"))), b, a);
+		RevCommit d = commit(tree(file("0", blob("d"))), c);
+
+		rw.reset();
+		rw.setFirstParent(true);
+		rw.sort(RevSort.TOPO, true);
+		rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
+		markStart(d);
+		assertCommit(d, rw.next());
+		assertCommit(c, rw.next());
+		assertCommit(b, rw.next());
+		assertNull(rw.next());
+	}
+
+	@Test
+	public void testWithTopoSortAndTreeFilter2() throws Exception {
+		RevCommit a = commit();
+		RevCommit b = commit(tree(file("0", blob("b"))), a);
+		RevCommit c = commit(tree(file("0", blob("c"))), a, b);
+		RevCommit d = commit(tree(file("0", blob("d"))), c);
+
+		rw.reset();
+		rw.setFirstParent(true);
+		rw.sort(RevSort.TOPO, true);
+		rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
+		markStart(d);
+		assertCommit(d, rw.next());
+		assertCommit(c, rw.next());
+		assertNull(rw.next());
+	}
 }
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
index dd73e35..a4411e5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ReachabilityCheckerTestCase.java
@@ -47,6 +47,7 @@
 
 import java.util.Arrays;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
@@ -83,11 +84,11 @@
 		ReachabilityChecker checker = getChecker(repo);
 
 		assertReachable("reachable from one tip",
-				checker.areAllReachable(Arrays.asList(a), Arrays.asList(c2)));
+				checker.areAllReachable(Arrays.asList(a), Stream.of(c2)));
 		assertReachable("reachable from another tip",
-				checker.areAllReachable(Arrays.asList(a), Arrays.asList(b2)));
+				checker.areAllReachable(Arrays.asList(a), Stream.of(b2)));
 		assertReachable("reachable from itself",
-				checker.areAllReachable(Arrays.asList(a), Arrays.asList(b2)));
+				checker.areAllReachable(Arrays.asList(a), Stream.of(a)));
 	}
 
 	@Test
@@ -104,13 +105,13 @@
 
 		assertReachable("reachable through one branch",
 				checker.areAllReachable(Arrays.asList(b1),
-						Arrays.asList(merge)));
+						Stream.of(merge)));
 		assertReachable("reachable through another branch",
 				checker.areAllReachable(Arrays.asList(c1),
-						Arrays.asList(merge)));
+						Stream.of(merge)));
 		assertReachable("reachable, before the branching",
 				checker.areAllReachable(Arrays.asList(a),
-						Arrays.asList(merge)));
+						Stream.of(merge)));
 	}
 
 	@Test
@@ -123,7 +124,7 @@
 		ReachabilityChecker checker = getChecker(repo);
 
 		assertUnreachable("unreachable from the future",
-				checker.areAllReachable(Arrays.asList(b2), Arrays.asList(b1)));
+				checker.areAllReachable(Arrays.asList(b2), Stream.of(b1)));
 	}
 
 	@Test
@@ -137,7 +138,7 @@
 		ReachabilityChecker checker = getChecker(repo);
 
 		assertUnreachable("unreachable from different branch",
-				checker.areAllReachable(Arrays.asList(c1), Arrays.asList(b2)));
+				checker.areAllReachable(Arrays.asList(c1), Stream.of(b2)));
 	}
 
 	@Test
@@ -152,7 +153,7 @@
 		ReachabilityChecker checker = getChecker(repo);
 
 		assertReachable("reachable with long chain in the middle", checker
-				.areAllReachable(Arrays.asList(root), Arrays.asList(head)));
+				.areAllReachable(Arrays.asList(root), Stream.of(head)));
 	}
 
 	private static void assertReachable(String msg,
@@ -164,5 +165,4 @@
 			Optional<RevCommit> result) {
 		assertTrue(msg, result.isPresent());
 	}
-
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
index b814984..d6b63ab 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
@@ -107,18 +107,18 @@
 
 		body.append("\n");
 
-		final RevWalk rw = new RevWalk(db);
 		final RevCommit c;
 
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
 		assertNull(c.getTree());
 		assertNull(c.parents);
 
-		c.parseCanonical(rw, body.toString().getBytes(UTF_8));
-		assertNotNull(c.getTree());
-		assertEquals(treeId, c.getTree().getId());
-		assertSame(rw.lookupTree(treeId), c.getTree());
-
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, body.toString().getBytes(UTF_8));
+			assertNotNull(c.getTree());
+			assertEquals(treeId, c.getTree().getId());
+			assertSame(rw.lookupTree(treeId), c.getTree());
+		}
 		assertNotNull(c.parents);
 		assertEquals(0, c.parents.length);
 		assertEquals("", c.getFullMessage());
@@ -148,8 +148,10 @@
 
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toString().getBytes(UTF_8));
-		return c;
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toString().getBytes(UTF_8));
+			return c;
+		}
 	}
 
 	@Test
@@ -161,8 +163,9 @@
 
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toString().getBytes(UTF_8));
-
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toString().getBytes(UTF_8));
+		}
 		assertEquals("", c.getFullMessage());
 		assertEquals("", c.getShortMessage());
 	}
@@ -176,8 +179,9 @@
 
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toString().getBytes(UTF_8));
-
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toString().getBytes(UTF_8));
+		}
 		assertEquals(new PersonIdent("", "a_u_thor@example.com", 1218123387000l, 7), c.getAuthorIdent());
 		assertEquals(new PersonIdent("", "", 1218123390000l, -5), c.getCommitterIdent());
 	}
@@ -194,8 +198,9 @@
 		b.write("\u304d\u308c\u3044\n".getBytes(UTF_8));
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
-
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 		assertSame(UTF_8, c.getEncoding());
 		assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName());
 		assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage());
@@ -214,8 +219,9 @@
 		b.write("\u304d\u308c\u3044\n".getBytes(UTF_8));
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
-
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 		assertSame(UTF_8, c.getEncoding());
 		assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName());
 		assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage());
@@ -240,7 +246,9 @@
 		b.write("Hi\n".getBytes("EUC-JP"));
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("EUC-JP", c.getEncoding().name());
 		assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName());
@@ -270,7 +278,9 @@
 		b.write("Hi\n".getBytes(UTF_8));
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("EUC-JP", c.getEncoding().name());
 		assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName());
@@ -301,7 +311,9 @@
 		b.write("Hi\n".getBytes(UTF_8));
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("ISO-8859-1", c.getEncoding().name());
 		assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName());
@@ -323,7 +335,9 @@
 
 		RevCommit c = new RevCommit(
 				id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 		assertEquals("'utf8'", c.getEncodingName());
 		assertEquals("Sm\u00f6rg\u00e5sbord\n", c.getFullMessage());
 
@@ -347,7 +361,9 @@
 
 		RevCommit c = new RevCommit(
 				id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 		assertEquals("utf-8logoutputencoding=gbk", c.getEncodingName());
 		assertEquals("message\n", c.getFullMessage());
 		assertEquals("message", c.getShortMessage());
@@ -374,7 +390,9 @@
 
 		RevCommit c = new RevCommit(
 				id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 		assertEquals("it_IT.UTF8", c.getEncodingName());
 		assertEquals("message\n", c.getFullMessage());
 		assertEquals("message", c.getShortMessage());
@@ -507,7 +525,9 @@
 
 		final RevCommit c;
 		c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), commit.getBytes(UTF_8));
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, commit.getBytes(UTF_8));
+		}
 		String gpgSig = new String(c.getRawGpgSignature(), UTF_8);
 		assertTrue(gpgSig.startsWith("-----BEGIN"));
 		assertTrue(gpgSig.endsWith("END PGP SIGNATURE-----"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
index 1b45473..bbc559b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
@@ -91,17 +91,18 @@
 		b.append("tagger A U. Thor <a_u_thor@example.com> 1218123387 +0700\n");
 		b.append("\n");
 
-		final RevWalk rw = new RevWalk(db);
 		final RevTag c;
 
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
 		assertNull(c.getObject());
 		assertNull(c.getTagName());
 
-		c.parseCanonical(rw, b.toString().getBytes(UTF_8));
-		assertNotNull(c.getObject());
-		assertEquals(id, c.getObject().getId());
-		assertSame(rw.lookupAny(id, typeCode), c.getObject());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toString().getBytes(UTF_8));
+			assertNotNull(c.getObject());
+			assertEquals(id, c.getObject().getId());
+			assertSame(rw.lookupAny(id, typeCode), c.getObject());
+		}
 	}
 
 	@Test
@@ -134,18 +135,18 @@
 
 		body.append("\n");
 
-		final RevWalk rw = new RevWalk(db);
 		final RevTag c;
 
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
 		assertNull(c.getObject());
 		assertNull(c.getTagName());
 
-		c.parseCanonical(rw, body.toString().getBytes(UTF_8));
-		assertNotNull(c.getObject());
-		assertEquals(treeId, c.getObject().getId());
-		assertSame(rw.lookupTree(treeId), c.getObject());
-
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, body.toString().getBytes(UTF_8));
+			assertNotNull(c.getObject());
+			assertEquals(treeId, c.getObject().getId());
+			assertSame(rw.lookupTree(treeId), c.getObject());
+		}
 		assertNotNull(c.getTagName());
 		assertEquals(name, c.getTagName());
 		assertEquals("", c.getFullMessage());
@@ -182,17 +183,18 @@
 		body.append("\n");
 		body.append(message);
 
-		final RevWalk rw = new RevWalk(db);
 		final RevTag c;
 
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
 		assertNull(c.getObject());
 		assertNull(c.getTagName());
 
-		c.parseCanonical(rw, body.toString().getBytes(UTF_8));
-		assertNotNull(c.getObject());
-		assertEquals(treeId, c.getObject().getId());
-		assertSame(rw.lookupTree(treeId), c.getObject());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, body.toString().getBytes(UTF_8));
+			assertNotNull(c.getObject());
+			assertEquals(treeId, c.getObject().getId());
+			assertSame(rw.lookupTree(treeId), c.getObject());
+		}
 
 		assertNotNull(c.getTagName());
 		assertEquals(name, c.getTagName());
@@ -213,7 +215,9 @@
 
 		final RevTag c;
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toString().getBytes(UTF_8));
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toString().getBytes(UTF_8));
+		}
 		return c;
 	}
 
@@ -234,7 +238,9 @@
 		b.write("\u304d\u308c\u3044\n".getBytes(UTF_8));
 		final RevTag c;
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName());
 		assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage());
@@ -257,7 +263,9 @@
 		b.write("\u304d\u308c\u3044\n".getBytes(UTF_8));
 		final RevTag c;
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName());
 		assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage());
@@ -287,7 +295,9 @@
 		b.write("Hi\n".getBytes("EUC-JP"));
 		final RevTag c;
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName());
 		assertEquals("\u304d\u308c\u3044", c.getShortMessage());
@@ -320,7 +330,9 @@
 		b.write("Hi\n".getBytes(UTF_8));
 		final RevTag c;
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName());
 		assertEquals("\u304d\u308c\u3044", c.getShortMessage());
@@ -355,7 +367,9 @@
 		b.write("Hi\n".getBytes(UTF_8));
 		final RevTag c;
 		c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		c.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			c.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName());
 		assertEquals("\u304d\u308c\u3044", c.getShortMessage());
@@ -374,7 +388,9 @@
 		b.write("message\n".getBytes(UTF_8));
 
 		RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		t.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			t.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("t", t.getTaggerIdent().getName());
 		assertEquals("message", t.getShortMessage());
@@ -393,7 +409,9 @@
 		b.write("message\n".getBytes(UTF_8));
 
 		RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
-		t.parseCanonical(new RevWalk(db), b.toByteArray());
+		try (RevWalk rw = new RevWalk(db)) {
+			t.parseCanonical(rw, b.toByteArray());
+		}
 
 		assertEquals("t", t.getTaggerIdent().getName());
 		assertEquals("message", t.getShortMessage());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java
index 9548992..a345459 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/TreeRevFilterTest.java
@@ -46,25 +46,15 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
-import java.util.Collections;
-
 import org.eclipse.jgit.revwalk.filter.OrRevFilter;
 import org.eclipse.jgit.revwalk.filter.RevFilter;
 import org.eclipse.jgit.revwalk.filter.SkipRevFilter;
-import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
-import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.junit.Test;
 
 public class TreeRevFilterTest extends RevWalkTestCase {
-	private RevFilter treeRevFilter(String path) {
-		return new TreeRevFilter(rw, treeFilter(path));
-	}
-
-	private static TreeFilter treeFilter(String path) {
-		return AndTreeFilter.create(
-				PathFilterGroup.createFromStrings(Collections.singleton(path)),
-				TreeFilter.ANY_DIFF);
+	private RevFilter treeRevFilter() {
+		return new TreeRevFilter(rw, TreeFilter.ANY_DIFF);
 	}
 
 	@Test
@@ -73,7 +63,7 @@
 		RevCommit a = commit(tree(file("d/f", blob("a"))));
 		RevCommit b = commit(tree(file("d/f", blob("a"))), a);
 		RevCommit c = commit(tree(file("d/f", blob("b"))), b);
-		rw.setRevFilter(treeRevFilter("d/f"));
+		rw.setRevFilter(treeRevFilter());
 		markStart(c);
 
 		assertCommit(c, rw.next());
@@ -91,7 +81,7 @@
 		RevCommit b = commit(tree(file("d/f", blob("a"))), a);
 		RevCommit c = commit(tree(file("d/f", blob("b"))), b);
 		RevCommit d = commit(tree(file("d/f", blob("b"))), c);
-		rw.setRevFilter(treeRevFilter("d/f"));
+		rw.setRevFilter(treeRevFilter());
 		markStart(d);
 
 		// d was skipped
@@ -111,7 +101,7 @@
 		RevCommit b = commit(tree(file("d/f", blob("a"))), a);
 		RevCommit c = commit(tree(file("d/f", blob("b"))), b);
 		RevCommit d = commit(tree(file("d/f", blob("b"))), c);
-		rw.setRevFilter(treeRevFilter("d"));
+		rw.setRevFilter(treeRevFilter());
 		markStart(d);
 
 		// d was skipped
@@ -136,7 +126,7 @@
 		RevCommit g = commit(tree(file("d/f", blob("b"))), f);
 		RevCommit h = commit(tree(file("d/f", blob("b"))), g);
 		RevCommit i = commit(tree(file("d/f", blob("c"))), h);
-		rw.setRevFilter(treeRevFilter("d/f"));
+		rw.setRevFilter(treeRevFilter());
 		markStart(i);
 
 		assertCommit(i, rw.next());
@@ -156,7 +146,7 @@
 
 	@Test
 	public void testPathFilterOrOtherFilter() throws Exception {
-		RevFilter pathFilter = treeRevFilter("d/f");
+		RevFilter pathFilter = treeRevFilter();
 		RevFilter skipFilter = SkipRevFilter.create(1);
 		RevFilter orFilter = OrRevFilter.create(skipFilter, pathFilter);
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
index 1ff64a2..80e9e6b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
@@ -135,20 +135,20 @@
 				subCommit = repo.resolve(Constants.HEAD);
 			}
 
-			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-			generator.loadModulesConfig();
-			assertTrue(generator.next());
-			assertEquals(path, generator.getModuleName());
-			assertEquals(path, generator.getPath());
-			assertEquals(commit, generator.getObjectId());
-			assertEquals(uri, generator.getModulesUrl());
-			assertEquals(path, generator.getModulesPath());
-			assertEquals(uri, generator.getConfigUrl());
-			try (Repository subModRepo = generator.getRepository()) {
-				assertNotNull(subModRepo);
-				assertEquals(subCommit, commit);
+			try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+				generator.loadModulesConfig();
+				assertTrue(generator.next());
+				assertEquals(path, generator.getModuleName());
+				assertEquals(path, generator.getPath());
+				assertEquals(commit, generator.getObjectId());
+				assertEquals(uri, generator.getModulesUrl());
+				assertEquals(path, generator.getModulesPath());
+				assertEquals(uri, generator.getConfigUrl());
+				try (Repository subModRepo = generator.getRepository()) {
+					assertNotNull(subModRepo);
+					assertEquals(subCommit, commit);
+				}
 			}
-
 			Status status = Git.wrap(db).status().call();
 			assertTrue(status.getAdded().contains(Constants.DOT_GIT_MODULES));
 			assertTrue(status.getAdded().contains(path));
@@ -175,20 +175,20 @@
 				subCommit = repo.resolve(Constants.HEAD);
 			}
 
-			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-			generator.loadModulesConfig();
-			assertTrue(generator.next());
-			assertEquals(name, generator.getModuleName());
-			assertEquals(path, generator.getPath());
-			assertEquals(commit, generator.getObjectId());
-			assertEquals(uri, generator.getModulesUrl());
-			assertEquals(path, generator.getModulesPath());
-			assertEquals(uri, generator.getConfigUrl());
-			try (Repository subModRepo = generator.getRepository()) {
-				assertNotNull(subModRepo);
-				assertEquals(subCommit, commit);
+			try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+				generator.loadModulesConfig();
+				assertTrue(generator.next());
+				assertEquals(name, generator.getModuleName());
+				assertEquals(path, generator.getPath());
+				assertEquals(commit, generator.getObjectId());
+				assertEquals(uri, generator.getModulesUrl());
+				assertEquals(path, generator.getModulesPath());
+				assertEquals(uri, generator.getConfigUrl());
+				try (Repository subModRepo = generator.getRepository()) {
+					assertNotNull(subModRepo);
+					assertEquals(subCommit, commit);
+				}
 			}
-
 			Status status = Git.wrap(db).status().call();
 			assertTrue(status.getAdded().contains(Constants.DOT_GIT_MODULES));
 			assertTrue(status.getAdded().contains(path));
@@ -269,24 +269,25 @@
 			assertNotNull(repo);
 			addRepoToClose(repo);
 
-			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-			assertTrue(generator.next());
-			assertEquals(path, generator.getPath());
-			assertEquals(commit, generator.getObjectId());
-			assertEquals(uri, generator.getModulesUrl());
-			assertEquals(path, generator.getModulesPath());
-			String fullUri = db.getDirectory().getAbsolutePath();
-			if (File.separatorChar == '\\') {
-				fullUri = fullUri.replace('\\', '/');
-			}
-			assertEquals(fullUri, generator.getConfigUrl());
-			try (Repository subModRepo = generator.getRepository()) {
-				assertNotNull(subModRepo);
-				assertEquals(fullUri,
-						subModRepo.getConfig().getString(
-								ConfigConstants.CONFIG_REMOTE_SECTION,
-								Constants.DEFAULT_REMOTE_NAME,
-								ConfigConstants.CONFIG_KEY_URL));
+			try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+				assertTrue(generator.next());
+				assertEquals(path, generator.getPath());
+				assertEquals(commit, generator.getObjectId());
+				assertEquals(uri, generator.getModulesUrl());
+				assertEquals(path, generator.getModulesPath());
+				String fullUri = db.getDirectory().getAbsolutePath();
+				if (File.separatorChar == '\\') {
+					fullUri = fullUri.replace('\\', '/');
+				}
+				assertEquals(fullUri, generator.getConfigUrl());
+				try (Repository subModRepo = generator.getRepository()) {
+					assertNotNull(subModRepo);
+					assertEquals(fullUri,
+							subModRepo.getConfig().getString(
+									ConfigConstants.CONFIG_REMOTE_SECTION,
+									Constants.DEFAULT_REMOTE_NAME,
+									ConfigConstants.CONFIG_KEY_URL));
+				}
 			}
 			assertEquals(commit, repo.resolve(Constants.HEAD));
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleDeinitTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleDeinitTest.java
index 815ce9b..bb00851 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleDeinitTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleDeinitTest.java
@@ -107,7 +107,6 @@
 		assertEquals(1, updated.size());
 
 		File submoduleDir = assertSubmoduleIsInitialized();
-		SubmoduleWalk generator;
 
 		write(new File(submoduleDir, "untracked"), "untracked");
 
@@ -115,8 +114,9 @@
 		assertEquals(path, result.getPath());
 		assertEquals(SubmoduleDeinitCommand.SubmoduleDeinitStatus.DIRTY, result.getStatus());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+		}
 		assertTrue(submoduleDir.isDirectory());
 		assertNotEquals(0, submoduleDir.list().length);
 	}
@@ -132,33 +132,36 @@
 		assertEquals(1, updated.size());
 
 		File submoduleDir = assertSubmoduleIsInitialized();
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		generator.next();
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			generator.next();
 
-		//want to create a commit inside the repo...
-		try (Repository submoduleLocalRepo = generator.getRepository()) {
-			JGitTestUtil.writeTrashFile(submoduleLocalRepo, "file.txt",
-					"new data");
-			Git.wrap(submoduleLocalRepo).commit().setAll(true)
-					.setMessage("local commit").call();
+			// want to create a commit inside the repo...
+			try (Repository submoduleLocalRepo = generator.getRepository()) {
+				JGitTestUtil.writeTrashFile(submoduleLocalRepo, "file.txt",
+						"new data");
+				Git.wrap(submoduleLocalRepo).commit().setAll(true)
+						.setMessage("local commit").call();
+			}
 		}
 		SubmoduleDeinitResult result = runDeinit(new SubmoduleDeinitCommand(db).addPath("sub"));
 		assertEquals(path, result.getPath());
 		assertEquals(SubmoduleDeinitCommand.SubmoduleDeinitStatus.DIRTY, result.getStatus());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+		}
 		assertTrue(submoduleDir.isDirectory());
 		assertNotEquals(0, submoduleDir.list().length);
 	}
 
 	private File assertSubmoduleIsInitialized() throws IOException {
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		File submoduleDir = new File(db.getWorkTree(), generator.getPath());
-		assertTrue(submoduleDir.isDirectory());
-		assertNotEquals(0, submoduleDir.list().length);
-		return submoduleDir;
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			File submoduleDir = new File(db.getWorkTree(), generator.getPath());
+			assertTrue(submoduleDir.isDirectory());
+			assertNotEquals(0, submoduleDir.list().length);
+			return submoduleDir;
+		}
 	}
 
 	@Test
@@ -180,8 +183,9 @@
 		assertEquals(path, result.getPath());
 		assertEquals(SubmoduleDeinitCommand.SubmoduleDeinitStatus.FORCED, result.getStatus());
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+		}
 		assertTrue(submoduleDir.isDirectory());
 		assertEquals(0, submoduleDir.list().length);
 	}
@@ -202,8 +206,9 @@
 		assertEquals(path, result.getPath());
 		assertEquals(SubmoduleDeinitCommand.SubmoduleDeinitStatus.SUCCESS, result.getStatus());
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+		}
 		assertTrue(submoduleDir.isDirectory());
 		assertEquals(0, submoduleDir.list().length);
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
index c7a009c..9fe2fc6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
@@ -86,11 +86,11 @@
 			ConfigInvalidException, GitAPIException {
 		final String path = addSubmoduleToIndex();
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertNull(generator.getConfigUpdate());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertNull(generator.getConfigUpdate());
+		}
 		FileBasedConfig modulesConfig = new FileBasedConfig(new File(
 				db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
@@ -109,10 +109,11 @@
 		assertEquals(1, modules.size());
 		assertEquals(path, modules.iterator().next());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertEquals(url, generator.getConfigUrl());
-		assertEquals(update, generator.getConfigUpdate());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertEquals(url, generator.getConfigUrl());
+			assertEquals(update, generator.getConfigUpdate());
+		}
 	}
 
 	@Test
@@ -126,11 +127,11 @@
 				base);
 		config.save();
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertNull(generator.getConfigUpdate());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertNull(generator.getConfigUpdate());
+		}
 		FileBasedConfig modulesConfig = new FileBasedConfig(new File(
 				db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
@@ -149,10 +150,12 @@
 		assertEquals(1, modules.size());
 		assertEquals(path, modules.iterator().next());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertEquals("git://server/repo.git/sub.git", generator.getConfigUrl());
-		assertEquals(update, generator.getConfigUpdate());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertEquals("git://server/repo.git/sub.git",
+					generator.getConfigUrl());
+			assertEquals(update, generator.getConfigUpdate());
+		}
 	}
 
 	@Test
@@ -167,11 +170,11 @@
 				base);
 		config.save();
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertNull(generator.getConfigUpdate());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertNull(generator.getConfigUpdate());
+		}
 		FileBasedConfig modulesConfig = new FileBasedConfig(new File(
 				db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
@@ -190,10 +193,11 @@
 		assertEquals(1, modules.size());
 		assertEquals(path, modules.iterator().next());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertEquals("git://server/sub.git", generator.getConfigUrl());
-		assertEquals(update, generator.getConfigUpdate());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertEquals("git://server/sub.git", generator.getConfigUrl());
+			assertEquals(update, generator.getConfigUpdate());
+		}
 	}
 
 	@Test
@@ -208,11 +212,11 @@
 				base);
 		config.save();
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertNull(generator.getConfigUpdate());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertNull(generator.getConfigUpdate());
+		}
 		FileBasedConfig modulesConfig = new FileBasedConfig(new File(
 				db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
@@ -231,10 +235,11 @@
 		assertEquals(1, modules.size());
 		assertEquals(path, modules.iterator().next());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertEquals("git://server2/sub.git", generator.getConfigUrl());
-		assertEquals(update, generator.getConfigUpdate());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertEquals("git://server2/sub.git", generator.getConfigUrl());
+			assertEquals(update, generator.getConfigUpdate());
+		}
 	}
 
 	@Test
@@ -250,11 +255,11 @@
 				Constants.DEFAULT_REMOTE_NAME, ConfigConstants.CONFIG_KEY_URL);
 		config.save();
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertNull(generator.getConfigUpdate());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertNull(generator.getConfigUpdate());
+		}
 		FileBasedConfig modulesConfig = new FileBasedConfig(new File(
 				db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
@@ -273,10 +278,11 @@
 		assertEquals(1, modules.size());
 		assertEquals(path, modules.iterator().next());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertEquals(base + "/sub.git", generator.getConfigUrl());
-		assertEquals(update, generator.getConfigUpdate());
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertEquals(base + "/sub.git", generator.getConfigUrl());
+			assertEquals(update, generator.getConfigUpdate());
+		}
 	}
 
 	@Test
@@ -291,11 +297,11 @@
 				base);
 		config.save();
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertNull(generator.getConfigUpdate());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertNull(generator.getConfigUpdate());
+		}
 		FileBasedConfig modulesConfig = new FileBasedConfig(new File(
 				db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java
index 6f3b52f..1053e31 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java
@@ -119,11 +119,11 @@
 		addRepoToClose(subRepo);
 		assertNotNull(subRepo);
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertEquals(url, generator.getModulesUrl());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertEquals(url, generator.getModulesUrl());
+		}
 		SubmoduleSyncCommand command = new SubmoduleSyncCommand(db);
 		Map<String, String> synced = command.call();
 		assertNotNull(synced);
@@ -132,16 +132,17 @@
 		assertEquals(path, module.getKey());
 		assertEquals(url, module.getValue());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertEquals(url, generator.getConfigUrl());
-		try (Repository subModRepository = generator.getRepository()) {
-			StoredConfig submoduleConfig = subModRepository.getConfig();
-			assertEquals(url,
-					submoduleConfig.getString(
-							ConfigConstants.CONFIG_REMOTE_SECTION,
-							Constants.DEFAULT_REMOTE_NAME,
-							ConfigConstants.CONFIG_KEY_URL));
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertEquals(url, generator.getConfigUrl());
+			try (Repository subModRepository = generator.getRepository()) {
+				StoredConfig submoduleConfig = subModRepository.getConfig();
+				assertEquals(url,
+						submoduleConfig.getString(
+								ConfigConstants.CONFIG_REMOTE_SECTION,
+								Constants.DEFAULT_REMOTE_NAME,
+								ConfigConstants.CONFIG_KEY_URL));
+			}
 		}
 	}
 
@@ -190,11 +191,11 @@
 		assertNotNull(subRepo);
 		addRepoToClose(subRepo);
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertNull(generator.getConfigUrl());
-		assertEquals(current, generator.getModulesUrl());
-
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertNull(generator.getConfigUrl());
+			assertEquals(current, generator.getModulesUrl());
+		}
 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
 				ConfigConstants.CONFIG_KEY_URL, "../sub.git");
 		modulesConfig.save();
@@ -207,16 +208,17 @@
 		assertEquals(path, module.getKey());
 		assertEquals("git://server/sub.git", module.getValue());
 
-		generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		assertEquals("git://server/sub.git", generator.getConfigUrl());
-		try (Repository subModRepository1 = generator.getRepository()) {
-			StoredConfig submoduleConfig = subModRepository1.getConfig();
-			assertEquals("git://server/sub.git",
-					submoduleConfig.getString(
-							ConfigConstants.CONFIG_REMOTE_SECTION,
-							Constants.DEFAULT_REMOTE_NAME,
-							ConfigConstants.CONFIG_KEY_URL));
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			assertEquals("git://server/sub.git", generator.getConfigUrl());
+			try (Repository subModRepository1 = generator.getRepository()) {
+				StoredConfig submoduleConfig = subModRepository1.getConfig();
+				assertEquals("git://server/sub.git",
+						submoduleConfig.getString(
+								ConfigConstants.CONFIG_REMOTE_SECTION,
+								Constants.DEFAULT_REMOTE_NAME,
+								ConfigConstants.CONFIG_KEY_URL));
+			}
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
index bbce413..6f358b4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
@@ -119,11 +119,12 @@
 		assertEquals(1, updated.size());
 		assertEquals(path, updated.iterator().next());
 
-		SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
-		assertTrue(generator.next());
-		try (Repository subRepo = generator.getRepository()) {
-			assertNotNull(subRepo);
-			assertEquals(commit, subRepo.resolve(Constants.HEAD));
+		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
+			assertTrue(generator.next());
+			try (Repository subRepo = generator.getRepository()) {
+				assertNotNull(subRepo);
+				assertEquals(commit, subRepo.resolve(Constants.HEAD));
+			}
 		}
 	}
 
@@ -181,10 +182,11 @@
 		});
 		editor.commit();
 
-		Repository subRepo = Git.init().setBare(false)
+		try (Repository subRepo = Git.init().setBare(false)
 				.setDirectory(new File(db.getWorkTree(), path)).call()
-				.getRepository();
-		assertNotNull(subRepo);
+				.getRepository()) {
+			assertNotNull(subRepo);
+		}
 
 		SubmoduleUpdateCommand command = new SubmoduleUpdateCommand(db);
 		Collection<String> updated = command.call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
index ea1ace3..0b6047f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
@@ -98,10 +98,11 @@
 
 	@Test
 	public void repositoryWithNoSubmodules() throws IOException {
-		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
-		assertFalse(gen.next());
-		assertNull(gen.getPath());
-		assertEquals(ObjectId.zeroId(), gen.getObjectId());
+		try (SubmoduleWalk gen = SubmoduleWalk.forIndex(db)) {
+			assertFalse(gen.next());
+			assertNull(gen.getPath());
+			assertEquals(ObjectId.zeroId(), gen.getObjectId());
+		}
 	}
 
 	@Test
@@ -129,20 +130,21 @@
 		});
 		editor.commit();
 
-		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
-		assertTrue(gen.next());
-		assertEquals(path, gen.getPath());
-		assertEquals(id, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertNull(gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertNull(gen.getModulesUrl());
-		assertNull(gen.getRepository());
+		try (SubmoduleWalk gen = SubmoduleWalk.forIndex(db)) {
+			assertTrue(gen.next());
+			assertEquals(path, gen.getPath());
+			assertEquals(id, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertNull(gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertNull(gen.getModulesUrl());
+			assertNull(gen.getRepository());
+			assertFalse(gen.next());
+		}
 		Status status = Git.wrap(db).status().call();
-		assertTrue(!status.isClean());
-		assertFalse(gen.next());
+		assertFalse(status.isClean());
 	}
 
 	@Test
@@ -178,24 +180,25 @@
 		});
 		editor.commit();
 
-		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
-		assertTrue(gen.next());
-		assertEquals(path, gen.getPath());
-		assertEquals(id, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertNull(gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertNull(gen.getModulesUrl());
-		try (Repository subRepo = gen.getRepository()) {
-			assertNotNull(subRepo);
-			assertEquals(modulesGitDir.getAbsolutePath(),
-					subRepo.getDirectory().getAbsolutePath());
-			assertEquals(new File(db.getWorkTree(), path).getAbsolutePath(),
-					subRepo.getWorkTree().getAbsolutePath());
+		try (SubmoduleWalk gen = SubmoduleWalk.forIndex(db)) {
+			assertTrue(gen.next());
+			assertEquals(path, gen.getPath());
+			assertEquals(id, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertNull(gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertNull(gen.getModulesUrl());
+			try (Repository subRepo = gen.getRepository()) {
+				assertNotNull(subRepo);
+				assertEquals(modulesGitDir.getAbsolutePath(),
+						subRepo.getDirectory().getAbsolutePath());
+				assertEquals(new File(db.getWorkTree(), path).getAbsolutePath(),
+						subRepo.getWorkTree().getAbsolutePath());
+			}
+			assertFalse(gen.next());
 		}
-		assertFalse(gen.next());
 	}
 
 	@Test
@@ -232,23 +235,24 @@
 		});
 		editor.commit();
 
-		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
-		assertTrue(gen.next());
-		assertEquals(path, gen.getPath());
-		assertEquals(id, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertNull(gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertNull(gen.getModulesUrl());
-		try (Repository subRepo = gen.getRepository()) {
-			assertNotNull(subRepo);
-			assertEqualsFile(modulesGitDir, subRepo.getDirectory());
-			assertEqualsFile(new File(db.getWorkTree(), path),
-					subRepo.getWorkTree());
-			subRepo.close();
-			assertFalse(gen.next());
+		try (SubmoduleWalk gen = SubmoduleWalk.forIndex(db)) {
+			assertTrue(gen.next());
+			assertEquals(path, gen.getPath());
+			assertEquals(id, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertNull(gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertNull(gen.getModulesUrl());
+			try (Repository subRepo = gen.getRepository()) {
+				assertNotNull(subRepo);
+				assertEqualsFile(modulesGitDir, subRepo.getDirectory());
+				assertEqualsFile(new File(db.getWorkTree(), path),
+						subRepo.getWorkTree());
+				subRepo.close();
+				assertFalse(gen.next());
+			}
 		}
 	}
 
@@ -270,18 +274,19 @@
 		});
 		editor.commit();
 
-		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
-		assertTrue(gen.next());
-		assertEquals(path, gen.getPath());
-		assertEquals(id, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertNull(gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertNull(gen.getModulesUrl());
-		assertNull(gen.getRepository());
-		assertFalse(gen.next());
+		try (SubmoduleWalk gen = SubmoduleWalk.forIndex(db)) {
+			assertTrue(gen.next());
+			assertEquals(path, gen.getPath());
+			assertEquals(id, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertNull(gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertNull(gen.getModulesUrl());
+			assertNull(gen.getRepository());
+			assertFalse(gen.next());
+		}
 	}
 
 	@Test
@@ -312,12 +317,13 @@
 		});
 		editor.commit();
 
-		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
-		gen.setFilter(PathFilter.create(path1));
-		assertTrue(gen.next());
-		assertEquals(path1, gen.getPath());
-		assertEquals(id1, gen.getObjectId());
-		assertFalse(gen.next());
+		try (SubmoduleWalk gen = SubmoduleWalk.forIndex(db)) {
+			gen.setFilter(PathFilter.create(path1));
+			assertTrue(gen.next());
+			assertEquals(path1, gen.getPath());
+			assertEquals(id1, gen.getObjectId());
+			assertFalse(gen.next());
+		}
 	}
 
 	@Test
@@ -358,18 +364,19 @@
 		});
 		editor.commit();
 
-		SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
-		assertTrue(gen.next());
-		assertEquals(path, gen.getPath());
-		assertEquals(subId, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertEquals("sub", gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertEquals("git://example.com/sub", gen.getModulesUrl());
-		assertNull(gen.getRepository());
-		assertFalse(gen.next());
+		try (SubmoduleWalk gen = SubmoduleWalk.forIndex(db)) {
+			assertTrue(gen.next());
+			assertEquals(path, gen.getPath());
+			assertEquals(subId, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertEquals("sub", gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertEquals("git://example.com/sub", gen.getModulesUrl());
+			assertNull(gen.getRepository());
+			assertFalse(gen.next());
+		}
 	}
 
 	@Test
@@ -397,17 +404,19 @@
 						})
 				.create());
 
-		SubmoduleWalk gen = SubmoduleWalk.forPath(db, commit.getTree(), "sub");
-		assertEquals(path, gen.getPath());
-		assertEquals(subId, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertEquals("sub", gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertEquals("git://example.com/sub", gen.getModulesUrl());
-		assertNull(gen.getRepository());
-		assertFalse(gen.next());
+		try (SubmoduleWalk gen = SubmoduleWalk.forPath(db, commit.getTree(),
+				"sub")) {
+			assertEquals(path, gen.getPath());
+			assertEquals(subId, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertEquals("sub", gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertEquals("git://example.com/sub", gen.getModulesUrl());
+			assertNull(gen.getRepository());
+			assertFalse(gen.next());
+		}
 	}
 
 	@Test
@@ -437,17 +446,18 @@
 
 		final CanonicalTreeParser p = new CanonicalTreeParser();
 		p.reset(testDb.getRevWalk().getObjectReader(), commit.getTree());
-		SubmoduleWalk gen = SubmoduleWalk.forPath(db, p, "sub");
-		assertEquals(path, gen.getPath());
-		assertEquals(subId, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertEquals("sub", gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertEquals("git://example.com/sub", gen.getModulesUrl());
-		assertNull(gen.getRepository());
-		assertFalse(gen.next());
+		try (SubmoduleWalk gen = SubmoduleWalk.forPath(db, p, "sub")) {
+			assertEquals(path, gen.getPath());
+			assertEquals(subId, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertEquals("sub", gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertEquals("git://example.com/sub", gen.getModulesUrl());
+			assertNull(gen.getRepository());
+			assertFalse(gen.next());
+		}
 	}
 
 	@Test
@@ -477,16 +487,17 @@
 
 		final CanonicalTreeParser p = new CanonicalTreeParser();
 		p.reset(testDb.getRevWalk().getObjectReader(), commit.getTree());
-		SubmoduleWalk gen = SubmoduleWalk.forPath(db, p, "sub");
-		assertEquals(path, gen.getPath());
-		assertEquals(subId, gen.getObjectId());
-		assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
-		assertNull(gen.getConfigUpdate());
-		assertNull(gen.getConfigUrl());
-		assertEquals("sub", gen.getModulesPath());
-		assertNull(gen.getModulesUpdate());
-		assertEquals("git://example.com/sub", gen.getModulesUrl());
-		assertNull(gen.getRepository());
-		assertFalse(gen.next());
+		try (SubmoduleWalk gen = SubmoduleWalk.forPath(db, p, "sub")) {
+			assertEquals(path, gen.getPath());
+			assertEquals(subId, gen.getObjectId());
+			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
+			assertNull(gen.getConfigUpdate());
+			assertNull(gen.getConfigUrl());
+			assertEquals("sub", gen.getModulesPath());
+			assertNull(gen.getModulesUpdate());
+			assertEquals("git://example.com/sub", gen.getModulesUrl());
+			assertNull(gen.getRepository());
+			assertFalse(gen.next());
+		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
index c1e078d..d6c7a61 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
@@ -55,10 +55,9 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.lib.Constants;
+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.Repository;
 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
@@ -73,8 +72,8 @@
 	private Object ctx = new Object();
 	private InMemoryRepository server;
 	private InMemoryRepository client;
-	private ObjectId obj1;
-	private ObjectId obj2;
+	private ObjectId commit1;
+	private ObjectId commit2;
 
 	@Before
 	public void setUp() throws Exception {
@@ -92,10 +91,11 @@
 				});
 		uri = testProtocol.register(ctx, server);
 
-		try (ObjectInserter ins = client.newObjectInserter()) {
-			obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
-			obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
-			ins.flush();
+		try (TestRepository<?> clientRepo = new TestRepository<>(client)) {
+			commit1 = clientRepo.commit().noFiles().message("test commit 1")
+					.create();
+			commit2 = clientRepo.commit().noFiles().message("test commit 2")
+					.create();
 		}
 	}
 
@@ -149,13 +149,13 @@
 		List<RemoteRefUpdate> cmds = new ArrayList<>();
 		cmds.add(new RemoteRefUpdate(
 				null, null,
-				obj1, "refs/heads/one",
+				commit1, "refs/heads/one",
 				true /* force update */,
 				null /* no local tracking ref */,
 				ObjectId.zeroId()));
 		cmds.add(new RemoteRefUpdate(
 				null, null,
-				obj2, "refs/heads/two",
+				commit2, "refs/heads/two",
 				true /* force update */,
 				null /* no local tracking ref */,
 				ObjectId.zeroId()));
@@ -176,16 +176,16 @@
 		List<RemoteRefUpdate> cmds = new ArrayList<>();
 		cmds.add(new RemoteRefUpdate(
 				null, null,
-				obj1, "refs/heads/one",
+				commit1, "refs/heads/one",
 				true /* force update */,
 				null /* no local tracking ref */,
 				ObjectId.zeroId()));
 		cmds.add(new RemoteRefUpdate(
 				null, null,
-				obj2, "refs/heads/two",
+				commit2, "refs/heads/two",
 				true /* force update */,
 				null /* no local tracking ref */,
-				obj1));
+				commit1));
 		return cmds;
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
index ea15ebe..947ca97 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
@@ -133,16 +133,15 @@
 
 		@Override
 		public String getHeaderField(String name) {
-			if (!headerFields.containsKey(name))
+			if (!headerFields.containsKey(name)) {
 				return null;
-			else {
-				int n = headerFields.get(name).size();
-
-				if (n > 0)
-					return headerFields.get(name).get(n - 1);
-				else
-					return null;
 			}
+			int n = headerFields.get(name).size();
+
+			if (n > 0) {
+				return headerFields.get(name).get(n - 1);
+			}
+			return null;
 		}
 
 		@Override
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
index 4bf26b6..5db4563 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
@@ -145,7 +145,7 @@
 		ObjectId newId =
 				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
 		String line = oldId.name() + " " + newId.name() + " refs/heads/master";
-		ReceiveCommand cmd = BaseReceivePack.parseCommand(line);
+		ReceiveCommand cmd = ReceivePack.parseCommand(line);
 
 		parser.addCommand(cmd);
 		parser.addCommand(line);
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 fd1c3bf..18946e0 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
@@ -62,10 +62,9 @@
 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.lib.Constants;
+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.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -79,8 +78,8 @@
 	private Object ctx = new Object();
 	private InMemoryRepository server;
 	private InMemoryRepository client;
-	private ObjectId obj1;
-	private ObjectId obj2;
+	private ObjectId commit1;
+	private ObjectId commit2;
 	private ReceivePack receivePack;
 
 	@Override
@@ -101,10 +100,11 @@
 
 		uri = testProtocol.register(ctx, server);
 
-		try (ObjectInserter ins = client.newObjectInserter()) {
-			obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
-			obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
-			ins.flush();
+		try (TestRepository<?> clientRepo = new TestRepository<>(client)) {
+			commit1 = clientRepo.commit().noFiles().message("test commit 1")
+					.create();
+			commit2 = clientRepo.commit().noFiles().message("test commit 2")
+					.create();
 		}
 	}
 
@@ -121,12 +121,12 @@
 	private List<RemoteRefUpdate> commands(boolean atomicSafe)
 			throws IOException {
 		List<RemoteRefUpdate> cmds = new ArrayList<>();
-		cmds.add(new RemoteRefUpdate(null, null, obj1, "refs/heads/one",
+		cmds.add(new RemoteRefUpdate(null, null, commit1, "refs/heads/one",
 				true /* force update */, null /* no local tracking ref */,
 				ObjectId.zeroId()));
-		cmds.add(new RemoteRefUpdate(null, null, obj2, "refs/heads/two",
+		cmds.add(new RemoteRefUpdate(null, null, commit2, "refs/heads/two",
 				true /* force update */, null /* no local tracking ref */,
-				atomicSafe ? ObjectId.zeroId() : obj1));
+				atomicSafe ? ObjectId.zeroId() : commit1));
 		return cmds;
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReachableCommitRequestValidatorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReachableCommitRequestValidatorTest.java
new file mode 100644
index 0000000..20b490b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReachableCommitRequestValidatorTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.eclipse.jgit.transport.UploadPack.RequestValidator;
+
+/**
+ * Client may ask for any commit reachable from a reference advertised by
+ * the server.
+ */
+public class ReachableCommitRequestValidatorTest extends RequestValidatorTestCase {
+
+	@Override
+	protected RequestValidator createValidator() {
+		return new UploadPack.ReachableCommitRequestValidator();
+	}
+
+	@Override
+	protected boolean isReachableCommitValid() {
+		return true;
+	}
+
+	@Override
+	protected boolean isUnreachableCommitValid() {
+		return false;
+	}
+
+	@Override
+	protected boolean isReachableBlobValid_withBitmaps() {
+		return true;
+	}
+
+	@Override
+	protected boolean isReachableBlobValid_withoutBitmaps() {
+		return false;
+	}
+
+	@Override
+	protected boolean isUnreachableBlobValid() {
+		return false;
+	}
+
+	@Override
+	protected boolean isAdvertisedTipValid() {
+		return true;
+	}
+
+	@Override
+	protected boolean isUnadvertisedTipCommitValid() {
+		return false;
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReachableCommitTipRequestValidatorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReachableCommitTipRequestValidatorTest.java
new file mode 100644
index 0000000..5e5391d
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReachableCommitTipRequestValidatorTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.eclipse.jgit.transport.UploadPack.RequestValidator;
+
+/**
+ * Client may ask for any commit reachable from any reference, even if that
+ * reference wasn't advertised.
+ */
+public class ReachableCommitTipRequestValidatorTest
+		extends RequestValidatorTestCase {
+
+	@Override
+	protected RequestValidator createValidator() {
+		return new UploadPack.ReachableCommitTipRequestValidator();
+	}
+
+	@Override
+	protected boolean isReachableCommitValid() {
+		return true;
+	}
+
+	@Override
+	protected boolean isUnreachableCommitValid() {
+		return false;
+	}
+
+	@Override
+	protected boolean isAdvertisedTipValid() {
+		return true;
+	}
+
+	@Override
+	protected boolean isReachableBlobValid_withBitmaps() {
+		return true;
+	}
+
+	@Override
+	protected boolean isReachableBlobValid_withoutBitmaps() {
+		return false;
+	}
+
+	@Override
+	protected boolean isUnreachableBlobValid() {
+		return false;
+	}
+
+	@Override
+	protected boolean isUnadvertisedTipCommitValid() {
+		return true;
+	}
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
index 5d208ee..89ac2fe 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
@@ -174,7 +174,7 @@
 				ReceivePack rp = super.createReceivePack(dst);
 				rp.setAdvertiseRefsHook(new AdvertiseRefsHook() {
 					@Override
-					public void advertiseRefs(BaseReceivePack rp2)
+					public void advertiseRefs(ReceivePack rp2)
 							throws ServiceMayNotContinueException {
 						rp.setAdvertisedRefs(rp.getRepository().getAllRefs(),
 								null);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BaseReceivePackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackTest.java
similarity index 94%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BaseReceivePackTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackTest.java
index 7578c6e..156746d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BaseReceivePackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackTest.java
@@ -49,14 +49,14 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.junit.Test;
 
-/** Tests for base receive-pack utilities. */
-public class BaseReceivePackTest {
+/** Tests for receive-pack utilities. */
+public class ReceivePackTest {
 	@Test
 	public void parseCommand() throws Exception {
 		String o = "0000000000000000000000000000000000000000";
 		String n = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
 		String r = "refs/heads/master";
-		ReceiveCommand cmd = BaseReceivePack.parseCommand(o + " " + n + " " + r);
+		ReceiveCommand cmd = ReceivePack.parseCommand(o + " " + n + " " + r);
 		assertEquals(ObjectId.zeroId(), cmd.getOldId());
 		assertEquals("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
 				cmd.getNewId().name());
@@ -76,7 +76,7 @@
 
 	private void assertParseCommandFails(String input) {
 		try {
-			BaseReceivePack.parseCommand(input);
+			ReceivePack.parseCommand(input);
 			fail();
 		} catch (PackProtocolException e) {
 			// Expected.
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RequestValidatorTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RequestValidatorTestCase.java
new file mode 100644
index 0000000..c1e7bcf
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RequestValidatorTestCase.java
@@ -0,0 +1,319 @@
+/*
+ * 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.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.UploadPack.RequestValidator;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public abstract class RequestValidatorTestCase {
+
+	@Rule
+	public ExpectedException thrown = ExpectedException.none();
+
+	private RevCommit reachableCommit;
+
+	private RevCommit tipAdvertisedCommit;
+
+	private RevCommit tipUnadvertisedCommit;
+
+	private RevCommit unreachableCommit;
+
+	private RevBlob reachableBlob;
+
+	private RevBlob unreachableBlob;
+
+	private InMemoryRepository repo;
+
+	protected abstract RequestValidator createValidator();
+
+	@Before
+	public void setUp() throws Exception {
+		repo = new InMemoryRepository(new DfsRepositoryDescription());
+		try (TestRepository<InMemoryRepository> git = new TestRepository<>(
+				repo)) {
+			reachableBlob = git.blob("foo");
+			reachableCommit = git
+					.commit(git.tree(git.file("foo", reachableBlob)));
+			tipAdvertisedCommit = git.commit(reachableCommit);
+			git.update("advertised", tipAdvertisedCommit);
+
+			tipUnadvertisedCommit = git.commit(reachableCommit);
+			git.update("unadvertised", tipUnadvertisedCommit);
+
+			unreachableBlob = git.blob("unreachableFoo");
+			unreachableCommit = git
+					.commit(git.tree(git.file("foo", unreachableBlob)));
+		}
+	}
+
+	/**
+	 * @return true if a commit reachable from a visible tip (but not directly
+	 *         the tip) is valid
+	 */
+	protected abstract boolean isReachableCommitValid();
+
+	/** @return true if a commit not reachable from any tip is valid */
+	protected abstract boolean isUnreachableCommitValid();
+
+	/**
+	 * @return true if the commit directly pointed by an advertised ref is valid
+	 */
+	protected abstract boolean isAdvertisedTipValid();
+
+	/**
+	 * @return true if the object directly pointed by a non-advertised ref is
+	 *         valid
+	 */
+	protected abstract boolean isUnadvertisedTipCommitValid();
+
+	// UploadPack doesn't allow to ask for blobs when there is no
+	// bitmap. Test both cases separately.
+	/**
+	 * @return true if a reachable blob is valid (and the repo has bitmaps)
+	 */
+	protected abstract boolean isReachableBlobValid_withBitmaps();
+
+	/**
+	 * @return true if a reachable blob is valid (and the repo does NOT have
+	 *         bitmaps)
+	 */
+	protected abstract boolean isReachableBlobValid_withoutBitmaps();
+
+	/**
+	 * @return true if a blob unreachable from any tip is valid
+	 */
+	protected abstract boolean isUnreachableBlobValid();
+
+	@Test
+	public void validateReachableCommitWithBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isReachableCommitValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers
+					.containsString(
+							"want " + reachableCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithBitmaps()),
+				Arrays.asList(reachableCommit));
+	}
+
+	@Test
+	public void validateReachableCommitWithoutBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isReachableCommitValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + reachableCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithoutBitmaps()),
+				Arrays.asList(reachableCommit));
+	}
+
+	@Test
+	public void validateAdvertisedTipWithBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isAdvertisedTipValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + tipAdvertisedCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithBitmaps()),
+				Arrays.asList(tipAdvertisedCommit));
+	}
+
+	@Test
+	public void validateAdvertisedTipWithoutBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isAdvertisedTipValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + tipAdvertisedCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithoutBitmaps()),
+				Arrays.asList(tipAdvertisedCommit));
+	}
+
+	@Test
+	public void validateUnadvertisedTipWithBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isUnadvertisedTipCommitValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + tipUnadvertisedCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithBitmaps()),
+				Arrays.asList(tipUnadvertisedCommit));
+	}
+
+	@Test
+	public void validateUnadvertisedTipWithoutBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isUnadvertisedTipCommitValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + tipUnadvertisedCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithoutBitmaps()),
+				Arrays.asList(tipUnadvertisedCommit));
+	}
+
+	@Test
+	public void validateUnreachableCommitWithBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isUnreachableCommitValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + unreachableCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithBitmaps()),
+				Arrays.asList(unreachableCommit));
+	}
+
+	@Test
+	public void validateUnreachableCommitWithoutBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isUnreachableCommitValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + unreachableCommit.name() + " not valid"));
+
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithoutBitmaps()),
+				Arrays.asList(unreachableCommit));
+	}
+
+	@Test
+	public void validateReachableBlobWithBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isReachableBlobValid_withBitmaps()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + reachableBlob.name() + " not valid"));
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithBitmaps()),
+				Arrays.asList(reachableBlob));
+	}
+
+	@Test
+	public void validateReachableBlobWithoutBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isReachableBlobValid_withoutBitmaps()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + reachableBlob.name() + " not valid"));
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithoutBitmaps()),
+				Arrays.asList(reachableBlob));
+	}
+
+	@Test
+	public void validateUnreachableBlobWithBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isUnreachableBlobValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + unreachableBlob.name() + " not valid"));
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithBitmaps()),
+				Arrays.asList(unreachableBlob));
+	}
+
+	@Test
+	public void validateUnreachableBlobWithoutBitmaps()
+			throws PackProtocolException, IOException {
+		if (!isUnreachableBlobValid()) {
+			thrown.expect(TransportException.class);
+			thrown.expectMessage(Matchers.containsString(
+					"want " + unreachableBlob.name() + " not valid"));
+		}
+		createValidator().checkWants(getUploadPack(getRepoWithoutBitmaps()),
+				Arrays.asList(unreachableBlob));
+	}
+
+	private UploadPack getUploadPack(Repository repository) throws IOException {
+		UploadPack uploadPack = new UploadPack(repository);
+
+		Ref advertisedRef = repo.getRefDatabase().findRef("advertised");
+		Map<String, Ref> advertisedRefs = new HashMap<>();
+		advertisedRefs.put(advertisedRef.getName(), advertisedRef);
+
+		uploadPack.setAdvertisedRefs(advertisedRefs);
+		return uploadPack;
+	}
+
+	private Repository getRepoWithBitmaps() throws IOException {
+		new DfsGarbageCollector(repo).pack(null);
+		repo.scanForRepoChanges();
+		return repo;
+	}
+
+	private Repository getRepoWithoutBitmaps() {
+		return repo;
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TipRequestValidatorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TipRequestValidatorTest.java
new file mode 100644
index 0000000..565d0e6
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TipRequestValidatorTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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 org.eclipse.jgit.transport.UploadPack.RequestValidator;
+
+/**
+ * Client may ask for objects that are the tip of any reference, even if not
+ * advertised.
+ */
+public class TipRequestValidatorTest extends RequestValidatorTestCase {
+
+	@Override
+	protected RequestValidator createValidator() {
+		return new UploadPack.TipRequestValidator();
+	}
+
+	@Override
+	protected boolean isReachableCommitValid() {
+		return false;
+	}
+
+	@Override
+	protected boolean isUnreachableCommitValid() {
+		return false;
+	}
+
+	@Override
+	protected boolean isReachableBlobValid_withBitmaps() {
+		return false;
+	}
+
+	@Override
+	protected boolean isReachableBlobValid_withoutBitmaps() {
+		return false;
+	}
+
+	@Override
+	protected boolean isUnreachableBlobValid() {
+		return false;
+	}
+
+	@Override
+	protected boolean isAdvertisedTipValid() {
+		return true;
+	}
+
+	@Override
+	protected boolean isUnadvertisedTipCommitValid() {
+		return true;
+	}
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
new file mode 100644
index 0000000..7122082
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
@@ -0,0 +1,316 @@
+/*
+ * 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 static org.eclipse.jgit.lib.MoreAsserts.assertThrows;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+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.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+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.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+/**
+ * Test combinations of:
+ * <ul>
+ * <li>Fetch a blob or a commit</li>
+ * <li>Fetched object is reachable or not</li>
+ * <li>With and without bitmaps</li>
+ * </ul>
+ */
+public class UploadPackReachabilityTest {
+
+	@Rule
+	public ExpectedException thrown = ExpectedException.none();
+
+	private URIish uri;
+
+	private TestProtocol<Object> testProtocol;
+
+	private Object ctx = new Object();
+
+	private InMemoryRepository server;
+
+	private InMemoryRepository client;
+
+	private TestRepository<InMemoryRepository> remote;
+
+	@Before
+	public void setUp() throws Exception {
+		server = newRepo("server");
+		client = newRepo("client");
+
+		remote = new TestRepository<>(server);
+	}
+
+	@After
+	public void tearDown() {
+		Transport.unregister(testProtocol);
+	}
+
+	@Test
+	public void testFetchUnreachableBlobWithBitmap() throws Exception {
+		RevBlob blob = remote.blob("foo");
+		remote.commit(remote.tree(remote.file("foo", blob)));
+		generateBitmaps(server);
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			TransportException e = assertThrows(TransportException.class,
+					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
+							.singletonList(new RefSpec(blob.name()))));
+			assertThat(e.getMessage(),
+					containsString("want " + blob.name() + " not valid"));
+		}
+	}
+
+	@Test
+	public void testFetchReachableBlobWithoutBitmap() throws Exception {
+		RevBlob blob = remote.blob("foo");
+		RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
+		remote.update("master", commit);
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			TransportException e = assertThrows(TransportException.class,
+					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
+							.singletonList(new RefSpec(blob.name()))));
+			assertThat(e.getMessage(),
+					containsString(
+						"want " + blob.name() + " not valid"));
+		}
+	}
+
+	@Test
+	public void testFetchReachableBlobWithoutBitmapButFilterAllowed() throws Exception {
+		InMemoryRepository server2 = newRepo("server2");
+		try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
+				server2)) {
+			RevBlob blob = remote2.blob("foo");
+			RevCommit commit = remote2.commit(remote2.tree(remote2.file("foo", blob)));
+			remote2.update("master", commit);
+
+			server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
+					true);
+
+			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
+				UploadPack up = new UploadPack(db);
+				up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
+				return up;
+			}, null);
+			uri = testProtocol.register(ctx, server2);
+
+			assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
+
+			try (Transport tn = testProtocol.open(uri, client, "server2")) {
+				tn.fetch(NullProgressMonitor.INSTANCE,
+						Collections.singletonList(new RefSpec(blob.name())));
+				assertTrue(client.getObjectDatabase().has(blob.toObjectId()));
+			}
+		}
+	}
+
+	@Test
+	public void testFetchUnreachableBlobWithoutBitmap() throws Exception {
+		RevBlob blob = remote.blob("foo");
+		remote.commit(remote.tree(remote.file("foo", blob)));
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			TransportException e = assertThrows(TransportException.class, () ->
+			tn.fetch(NullProgressMonitor.INSTANCE,
+					Collections.singletonList(new RefSpec(blob.name()))));
+			assertThat(e.getMessage(),
+					containsString("want " + blob.name() + " not valid"));
+		}
+	}
+
+	@Test
+	public void testFetchReachableBlobWithBitmap() throws Exception {
+		RevBlob blob = remote.blob("foo");
+		RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
+		remote.update("master", commit);
+		generateBitmaps(server);
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			tn.fetch(NullProgressMonitor.INSTANCE,
+					Collections.singletonList(new RefSpec(blob.name())));
+			assertTrue(client.getObjectDatabase().has(blob.toObjectId()));
+		}
+	}
+
+	@Test
+	public void testFetchReachableCommitWithBitmap() throws Exception {
+		RevCommit commit = remote
+				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
+		remote.update("master", commit);
+		generateBitmaps(server);
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			tn.fetch(NullProgressMonitor.INSTANCE,
+					Collections.singletonList(new RefSpec(commit.name())));
+			assertTrue(client.getObjectDatabase().has(commit.toObjectId()));
+		}
+	}
+
+	@Test
+	public void testFetchReachableCommitWithoutBitmap() throws Exception {
+		RevCommit commit = remote
+				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
+		remote.update("master", commit);
+		generateBitmaps(server);
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			tn.fetch(NullProgressMonitor.INSTANCE,
+					Collections.singletonList(new RefSpec(commit.name())));
+			assertTrue(client.getObjectDatabase().has(commit.toObjectId()));
+		}
+	}
+
+	@Test
+	public void testFetchUnreachableCommitWithBitmap() throws Exception {
+		RevCommit commit = remote
+				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
+		generateBitmaps(server);
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			TransportException e = assertThrows(TransportException.class,
+					() -> tn.fetch(NullProgressMonitor.INSTANCE,
+					Collections.singletonList(new RefSpec(commit.name()))));
+			assertThat(e.getMessage(),
+					containsString("want " + commit.name() + " not valid"));
+		}
+	}
+
+	@Test
+	public void testFetchUnreachableCommitWithoutBitmap() throws Exception {
+		RevCommit commit = remote
+				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
+
+		testProtocol = generateReachableCommitUploadPackProtocol();
+		uri = testProtocol.register(ctx, server);
+
+		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
+
+		try (Transport tn = testProtocol.open(uri, client, "server")) {
+			TransportException e = assertThrows(TransportException.class,
+					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
+							.singletonList(new RefSpec(commit.name()))));
+			assertThat(e.getMessage(),
+					containsString("want " + commit.name() + " not valid"));
+		}
+	}
+
+	private static InMemoryRepository newRepo(String name) {
+		return new InMemoryRepository(new DfsRepositoryDescription(name));
+	}
+
+	private void generateBitmaps(InMemoryRepository repo) throws Exception {
+		new DfsGarbageCollector(repo).pack(null);
+		repo.scanForRepoChanges();
+	}
+
+	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);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackRefSortingForReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackRefSortingForReachabilityTest.java
new file mode 100644
index 0000000..0875a33
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackRefSortingForReachabilityTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef.Unpeeled;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Ref.Storage;
+import org.junit.Test;
+
+public class UploadPackRefSortingForReachabilityTest {
+
+	@Test
+	public void sortReferences() {
+		List<Ref> refs = Stream.of("refs/changes/12/12", "refs/changes/12/1",
+				"refs/heads/master", "refs/heads/something",
+				"refs/changes/55/1", "refs/tags/v1.1")
+				.map(s -> new Unpeeled(Storage.LOOSE, s, ObjectId.zeroId()))
+				.collect(Collectors.toList());
+		Stream<Ref> sorted = UploadPack.importantRefsFirst(refs);
+		List<String> collected = sorted.map(Ref::getName)
+				.collect(Collectors.toList());
+		assertThat(collected,
+				contains("refs/heads/master", "refs/heads/something",
+						"refs/tags/v1.1", "refs/changes/12/12",
+						"refs/changes/12/1", "refs/changes/55/1"));
+	}
+}
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 528a63f..108e5ed 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
@@ -98,14 +98,6 @@
 		repo.scanForRepoChanges();
 	}
 
-	private static TestProtocol<Object> generateReachableCommitUploadPackProtocol() {
-		return new TestProtocol<>((Object req, Repository db) -> {
-			UploadPack up = new UploadPack(db);
-			up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
-			return up;
-		}, null);
-	}
-
 	@Test
 	public void testFetchParentOfShallowCommit() throws Exception {
 		RevCommit commit0 = remote.commit().message("0").create();
@@ -134,95 +126,6 @@
 	}
 
 	@Test
-	public void testFetchUnreachableBlobWithBitmap() throws Exception {
-		RevBlob blob = remote.blob("foo");
-		remote.commit(remote.tree(remote.file("foo", blob)));
-		generateBitmaps(server);
-
-		testProtocol = generateReachableCommitUploadPackProtocol();
-		uri = testProtocol.register(ctx, server);
-
-		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
-
-		try (Transport tn = testProtocol.open(uri, client, "server")) {
-			TransportException e = assertThrows(TransportException.class,
-					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
-							.singletonList(new RefSpec(blob.name()))));
-			assertThat(e.getMessage(),
-					containsString("want " + blob.name() + " not valid"));
-		}
-	}
-
-	@Test
-	public void testFetchReachableBlobWithBitmap() throws Exception {
-		RevBlob blob = remote.blob("foo");
-		RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
-		remote.update("master", commit);
-		generateBitmaps(server);
-
-		testProtocol = generateReachableCommitUploadPackProtocol();
-		uri = testProtocol.register(ctx, server);
-
-		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
-
-		try (Transport tn = testProtocol.open(uri, client, "server")) {
-			tn.fetch(NullProgressMonitor.INSTANCE,
-					Collections.singletonList(new RefSpec(blob.name())));
-			assertTrue(client.getObjectDatabase().has(blob.toObjectId()));
-		}
-	}
-
-	@Test
-	public void testFetchReachableBlobWithoutBitmap() throws Exception {
-		RevBlob blob = remote.blob("foo");
-		RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
-		remote.update("master", commit);
-
-		testProtocol = generateReachableCommitUploadPackProtocol();
-		uri = testProtocol.register(ctx, server);
-
-		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
-
-		try (Transport tn = testProtocol.open(uri, client, "server")) {
-			TransportException e = assertThrows(TransportException.class,
-					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
-							.singletonList(new RefSpec(blob.name()))));
-			assertThat(e.getMessage(),
-					containsString(
-						"want " + blob.name() + " not valid"));
-		}
-	}
-
-	@Test
-	public void testFetchReachableBlobWithoutBitmapButFilterAllowed() throws Exception {
-		InMemoryRepository server2 = newRepo("server2");
-		try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
-				server2)) {
-			RevBlob blob = remote2.blob("foo");
-			RevCommit commit = remote2.commit(remote2.tree(remote2.file("foo", blob)));
-			remote2.update("master", commit);
-
-			server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
-					true);
-
-			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
-				UploadPack up = new UploadPack(db);
-				up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
-				return up;
-			}, null);
-			uri = testProtocol.register(ctx, server2);
-
-			assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
-
-			try (Transport tn = testProtocol.open(uri, client, "server2")) {
-				tn.fetch(NullProgressMonitor.INSTANCE,
-						Collections.singletonList(new RefSpec(blob.name())));
-				assertTrue(client.getObjectDatabase().has(blob.toObjectId()));
-			}
-		}
-	}
-
-	@Test
 	public void testFetchWithBlobNoneFilter() throws Exception {
 		InMemoryRepository server2 = newRepo("server2");
 		try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
@@ -559,7 +462,9 @@
 		assertThat(lines, containsInAnyOrder("ls-refs", "fetch", "server-option"));
 	}
 
-	private void checkUnadvertisedIfUnallowed(String fetchCapability) throws Exception {
+	private void checkUnadvertisedIfUnallowed(String configSection,
+			String configName, String fetchCapability) throws Exception {
+		server.getConfig().setBoolean(configSection, null, configName, false);
 		ByteArrayInputStream recvStream =
 				uploadPackV2Setup(null, PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
@@ -570,9 +475,9 @@
 		String line;
 		while (!PacketLineIn.isEnd((line = pckIn.readString()))) {
 			if (line.startsWith("fetch=")) {
-				assertThat(
-					Arrays.asList(line.substring(6).split(" ")),
-					hasItems("shallow"));
+				List<String> fetchItems = Arrays.asList(line.substring(6).split(" "));
+				assertThat(fetchItems, hasItems("shallow"));
+				assertFalse(fetchItems.contains(fetchCapability));
 				lines.add("fetch");
 			} else {
 				lines.add(line);
@@ -584,7 +489,7 @@
 	@Test
 	public void testV2CapabilitiesAllowFilter() throws Exception {
 		checkAdvertisedIfAllowed("uploadpack", "allowfilter", "filter");
-		checkUnadvertisedIfUnallowed("filter");
+		checkUnadvertisedIfUnallowed("uploadpack", "allowfilter", "filter");
 	}
 
 	@Test
@@ -594,13 +499,18 @@
 
 	@Test
 	public void testV2CapabilitiesRefInWantNotAdvertisedIfUnallowed() throws Exception {
-		checkUnadvertisedIfUnallowed("ref-in-want");
+		checkUnadvertisedIfUnallowed("uploadpack", "allowrefinwant",
+				"ref-in-want");
 	}
 
 	@Test
-	public void testV2CapabilitiesAllowSidebandAll() throws Exception {
-		checkAdvertisedIfAllowed("uploadpack", "allowsidebandall", "sideband-all");
-		checkUnadvertisedIfUnallowed("sideband-all");
+	public void testV2CapabilitiesAdvertiseSidebandAll() throws Exception {
+		server.getConfig().setBoolean("uploadpack", null, "allowsidebandall",
+				true);
+		checkAdvertisedIfAllowed("uploadpack", "advertisesidebandall",
+				"sideband-all");
+		checkUnadvertisedIfUnallowed("uploadpack", "advertisesidebandall",
+				"sideband-all");
 	}
 
 	@Test
@@ -2112,12 +2022,12 @@
 
 		ByteArrayInputStream recvStream = uploadPackV2(
 			"command=fetch\n",
-			PacketLineIn.DELIM,
+			PacketLineIn.delimiter(),
 			"sideband-all\n",
 			"want " + fooChild.toObjectId().getName() + "\n",
 			"want " + barChild.toObjectId().getName() + "\n",
 			"have " + fooParent.toObjectId().getName() + "\n",
-			PacketLineIn.END);
+			PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		assertThat(pckIn.readString(), is("\001acknowledgments"));
@@ -2133,11 +2043,11 @@
 		server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
 
 		ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
-				PacketLineIn.DELIM,
+				PacketLineIn.delimiter(),
 				"want " + commit.getName() + "\n",
 				"sideband-all\n",
 				"done\n",
-				PacketLineIn.END);
+				PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		String s;
@@ -2174,18 +2084,18 @@
 						assertThat(protocolsSupported, hasItems("https"));
 						if (!protocolsSupported.contains("https"))
 							return null;
-						return new PackInfo("myhash", "myuri");
+						return new PackInfo("myhash", "myuri", 100);
 					}
 
 				});
 			},
 			"command=fetch\n",
-			PacketLineIn.DELIM,
+			PacketLineIn.delimiter(),
 			"want " + commit2.getName() + "\n",
 			"sideband-all\n",
 			"packfile-uris https\n",
 			"done\n",
-			PacketLineIn.END);
+			PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		String s;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
index 4750d15..d1d7a1d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
@@ -239,9 +239,8 @@
 				loadEnvVar(ENV_SECRET_KEY, SECRET_KEY, props);
 				loadEnvVar(ENV_BUCKET_NAME, TEST_BUCKET, props);
 				return props;
-			} else {
-				return null;
 			}
+			return null;
 		}
 
 		static Properties fromEnvFile() throws Exception {
@@ -250,12 +249,10 @@
 				props.load(new FileInputStream(ENV_CONFIG_FILE));
 				if (checkTestProps(props)) {
 					return props;
-				} else {
-					throw new Error("Environment config file is incomplete.");
 				}
-			} else {
-				return null;
+				throw new Error("Environment config file is incomplete.");
 			}
+			return null;
 		}
 
 		static Properties fromSysProps() {
@@ -266,9 +263,8 @@
 				loadSysProp(SYS_SECRET_KEY, SECRET_KEY, props);
 				loadSysProp(SYS_BUCKET_NAME, TEST_BUCKET, props);
 				return props;
-			} else {
-				return null;
 			}
+			return null;
 		}
 
 		static Properties fromSysFile() throws Exception {
@@ -277,12 +273,10 @@
 				props.load(new FileInputStream(SYS_CONFIG_FILE));
 				if (checkTestProps(props)) {
 					return props;
-				} else {
-					throw new Error("System props config file is incomplete.");
 				}
-			} else {
-				return null;
+				throw new Error("System props config file is incomplete.");
 			}
+			return null;
 		}
 
 		static Properties fromConfigFile(String path) throws Exception {
@@ -292,12 +286,10 @@
 				props.load(new FileInputStream(file));
 				if (checkTestProps(props)) {
 					return props;
-				} else {
-					throw new Error("Props config file is incomplete: " + path);
 				}
-			} else {
-				return null;
+				throw new Error("Props config file is incomplete: " + path);
 			}
+			return null;
 		}
 
 		/**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java
index 58aa608..4deb188 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java
@@ -65,12 +65,13 @@
 	@Test
 	public void testCreateSubtreeIterator() throws Exception {
 		final EmptyTreeIterator etp = new EmptyTreeIterator();
-		final ObjectReader reader = db.newObjectReader();
-		final AbstractTreeIterator sub = etp.createSubtreeIterator(reader);
-		assertNotNull(sub);
-		assertTrue(sub.first());
-		assertTrue(sub.eof());
-		assertTrue(sub instanceof EmptyTreeIterator);
+		try (ObjectReader reader = db.newObjectReader()) {
+			final AbstractTreeIterator sub = etp.createSubtreeIterator(reader);
+			assertNotNull(sub);
+			assertTrue(sub.first());
+			assertTrue(sub.eof());
+			assertTrue(sub instanceof EmptyTreeIterator);
+		}
 	}
 
 	@Test
@@ -121,8 +122,9 @@
 				called[0] = true;
 			}
 		};
-		final ObjectReader reader = db.newObjectReader();
-		parent.createSubtreeIterator(reader).stopWalk();
+		try (ObjectReader reader = db.newObjectReader()) {
+			parent.createSubtreeIterator(reader).stopWalk();
+		}
 		assertTrue(called[0]);
 	}
 }
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 a3ce4ae..ffa6e5d 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
@@ -217,29 +217,29 @@
 		assertFalse(top.eof());
 		assertEquals(FileMode.TREE.getBits(), top.mode);
 
-		final ObjectReader reader = db.newObjectReader();
-		final AbstractTreeIterator sub = top.createSubtreeIterator(reader);
-		assertTrue(sub instanceof FileTreeIterator);
-		final FileTreeIterator subfti = (FileTreeIterator) sub;
-		assertTrue(sub.first());
-		assertFalse(sub.eof());
-		assertEquals(paths[2], nameOf(sub));
-		assertEquals(paths[2].length(), subfti.getEntryLength());
-		assertEquals(mtime[2], subfti.getEntryLastModifiedInstant());
+		try (ObjectReader reader = db.newObjectReader()) {
+			final AbstractTreeIterator sub = top.createSubtreeIterator(reader);
+			assertTrue(sub instanceof FileTreeIterator);
+			final FileTreeIterator subfti = (FileTreeIterator) sub;
+			assertTrue(sub.first());
+			assertFalse(sub.eof());
+			assertEquals(paths[2], nameOf(sub));
+			assertEquals(paths[2].length(), subfti.getEntryLength());
+			assertEquals(mtime[2], subfti.getEntryLastModifiedInstant());
 
-		sub.next(1);
-		assertTrue(sub.eof());
+			sub.next(1);
+			assertTrue(sub.eof());
+			top.next(1);
+			assertFalse(top.first());
+			assertFalse(top.eof());
+			assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
+			assertEquals(paths[3], nameOf(top));
+			assertEquals(paths[3].length(), top.getEntryLength());
+			assertEquals(mtime[3], top.getEntryLastModifiedInstant());
 
-		top.next(1);
-		assertFalse(top.first());
-		assertFalse(top.eof());
-		assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode);
-		assertEquals(paths[3], nameOf(top));
-		assertEquals(paths[3].length(), top.getEntryLength());
-		assertEquals(mtime[3], top.getEntryLastModifiedInstant());
-
-		top.next(1);
-		assertTrue(top.eof());
+			top.next(1);
+			assertTrue(top.eof());
+		}
 	}
 
 	@Test
@@ -272,22 +272,23 @@
 			git.add().addFilepattern("file").call();
 		}
 		DirCacheEntry dce = db.readDirCache().getEntry("file");
-		TreeWalk tw = new TreeWalk(db);
-		FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db
-				.getConfig().get(WorkingTreeOptions.KEY));
-		tw.addTree(fti);
-		DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
-		tw.addTree(dci);
-		fti.setDirCacheIterator(tw, 1);
-		while (tw.next() && !tw.getPathString().equals("file")) {
-			//
-		}
-		assertEquals(MetadataDiff.EQUAL, fti.compareMetadata(dce));
-		ObjectId fromRaw = ObjectId.fromRaw(fti.idBuffer(), fti.idOffset());
-		assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
-				fromRaw.getName());
-		try (ObjectReader objectReader = db.newObjectReader()) {
-			assertFalse(fti.isModified(dce, false, objectReader));
+		try (TreeWalk tw = new TreeWalk(db)) {
+			FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
+					db.getConfig().get(WorkingTreeOptions.KEY));
+			tw.addTree(fti);
+			DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
+			tw.addTree(dci);
+			fti.setDirCacheIterator(tw, 1);
+			while (tw.next() && !tw.getPathString().equals("file")) {
+				//
+			}
+			assertEquals(MetadataDiff.EQUAL, fti.compareMetadata(dce));
+			ObjectId fromRaw = ObjectId.fromRaw(fti.idBuffer(), fti.idOffset());
+			assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
+					fromRaw.getName());
+			try (ObjectReader objectReader = db.newObjectReader()) {
+				assertFalse(fti.isModified(dce, false, objectReader));
+			}
 		}
 	}
 
@@ -624,9 +625,8 @@
 
 	@Test
 	public void testCustomFileModeStrategy() throws Exception {
-		Repository nestedRepo = createNestedRepo();
-
-		try (Git git = new Git(nestedRepo)) {
+		try (Repository nestedRepo = createNestedRepo();
+				Git git = new Git(nestedRepo)) {
 			// validate that our custom strategy is honored
 			WorkingTreeIterator customIterator = new FileTreeIterator(
 					nestedRepo, NO_GITLINKS_STRATEGY);
@@ -641,9 +641,8 @@
 
 	@Test
 	public void testCustomFileModeStrategyFromParentIterator() throws Exception {
-		Repository nestedRepo = createNestedRepo();
-
-		try (Git git = new Git(nestedRepo)) {
+		try (Repository nestedRepo = createNestedRepo();
+				Git git = new Git(nestedRepo)) {
 			FileTreeIterator customIterator = new FileTreeIterator(nestedRepo,
 					NO_GITLINKS_STRATEGY);
 			File r = new File(nestedRepo.getWorkTree(), "sub");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
index 54c2172..e6a5322 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
@@ -84,16 +84,17 @@
 			assertEquals(1, tree1.getEntryCount());
 		}
 
-		final TreeWalk tw = new TreeWalk(db);
-		tw.addTree(new DirCacheIterator(tree0));
-		tw.addTree(new DirCacheIterator(tree1));
+		try (TreeWalk tw = new TreeWalk(db)) {
+			tw.addTree(new DirCacheIterator(tree0));
+			tw.addTree(new DirCacheIterator(tree1));
 
-		assertModes("a", REGULAR_FILE, MISSING, tw);
-		assertModes("a.b", EXECUTABLE_FILE, MISSING, tw);
-		assertModes("a", MISSING, TREE, tw);
-		tw.enterSubtree();
-		assertModes("a/b", MISSING, REGULAR_FILE, tw);
-		assertModes("a0b", SYMLINK, MISSING, tw);
+			assertModes("a", REGULAR_FILE, MISSING, tw);
+			assertModes("a.b", EXECUTABLE_FILE, MISSING, tw);
+			assertModes("a", MISSING, TREE, tw);
+			tw.enterSubtree();
+			assertModes("a/b", MISSING, REGULAR_FILE, tw);
+			assertModes("a0b", SYMLINK, MISSING, tw);
+		}
 	}
 
 	@Test
@@ -115,20 +116,21 @@
 			assertEquals(1, tree1.getEntryCount());
 		}
 
-		final NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
-		tw.addTree(new DirCacheIterator(tree0));
-		tw.addTree(new DirCacheIterator(tree1));
+		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
+			tw.addTree(new DirCacheIterator(tree0));
+			tw.addTree(new DirCacheIterator(tree1));
 
-		assertModes("a", REGULAR_FILE, TREE, tw);
-		assertTrue(tw.isDirectoryFileConflict());
-		assertTrue(tw.isSubtree());
-		tw.enterSubtree();
-		assertModes("a/b", MISSING, REGULAR_FILE, tw);
-		assertTrue(tw.isDirectoryFileConflict());
-		assertModes("a.b", EXECUTABLE_FILE, MISSING, tw);
-		assertFalse(tw.isDirectoryFileConflict());
-		assertModes("a0b", SYMLINK, MISSING, tw);
-		assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a", REGULAR_FILE, TREE, tw);
+			assertTrue(tw.isDirectoryFileConflict());
+			assertTrue(tw.isSubtree());
+			tw.enterSubtree();
+			assertModes("a/b", MISSING, REGULAR_FILE, tw);
+			assertTrue(tw.isDirectoryFileConflict());
+			assertModes("a.b", EXECUTABLE_FILE, MISSING, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a0b", SYMLINK, MISSING, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+		}
 	}
 
 	@Test
@@ -151,20 +153,21 @@
 			assertEquals(2, tree1.getEntryCount());
 		}
 
-		final NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
-		tw.addTree(new DirCacheIterator(tree0));
-		tw.addTree(new DirCacheIterator(tree1));
+		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
+			tw.addTree(new DirCacheIterator(tree0));
+			tw.addTree(new DirCacheIterator(tree1));
 
-		assertModes("a", REGULAR_FILE, TREE, tw);
-		assertTrue(tw.isSubtree());
-		assertTrue(tw.isDirectoryFileConflict());
-		tw.enterSubtree();
-		assertModes("a/b", MISSING, REGULAR_FILE, tw);
-		assertTrue(tw.isDirectoryFileConflict());
-		assertModes("a.b", EXECUTABLE_FILE, EXECUTABLE_FILE, tw);
-		assertFalse(tw.isDirectoryFileConflict());
-		assertModes("a0b", SYMLINK, MISSING, tw);
-		assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a", REGULAR_FILE, TREE, tw);
+			assertTrue(tw.isSubtree());
+			assertTrue(tw.isDirectoryFileConflict());
+			tw.enterSubtree();
+			assertModes("a/b", MISSING, REGULAR_FILE, tw);
+			assertTrue(tw.isDirectoryFileConflict());
+			assertModes("a.b", EXECUTABLE_FILE, EXECUTABLE_FILE, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a0b", SYMLINK, MISSING, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+		}
 	}
 
 	@Test
@@ -187,20 +190,21 @@
 			assertEquals(3, tree1.getEntryCount());
 		}
 
-		final NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
-		tw.addTree(new DirCacheIterator(tree0));
-		tw.addTree(new DirCacheIterator(tree1));
+		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
+			tw.addTree(new DirCacheIterator(tree0));
+			tw.addTree(new DirCacheIterator(tree1));
 
-		assertModes("a", REGULAR_FILE, TREE, tw);
-		assertTrue(tw.isSubtree());
-		assertTrue(tw.isDirectoryFileConflict());
-		tw.enterSubtree();
-		assertModes("a/b", MISSING, REGULAR_FILE, tw);
-		assertTrue(tw.isDirectoryFileConflict());
-		assertModes("a.b", MISSING, EXECUTABLE_FILE, tw);
-		assertFalse(tw.isDirectoryFileConflict());
-		assertModes("a0b", SYMLINK, SYMLINK, tw);
-		assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a", REGULAR_FILE, TREE, tw);
+			assertTrue(tw.isSubtree());
+			assertTrue(tw.isDirectoryFileConflict());
+			tw.enterSubtree();
+			assertModes("a/b", MISSING, REGULAR_FILE, tw);
+			assertTrue(tw.isDirectoryFileConflict());
+			assertModes("a.b", MISSING, EXECUTABLE_FILE, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a0b", SYMLINK, SYMLINK, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+		}
 	}
 
 	@Test
@@ -224,26 +228,27 @@
 			assertEquals(4, tree1.getEntryCount());
 		}
 
-		final NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
-		tw.addTree(new DirCacheIterator(tree0));
-		tw.addTree(new DirCacheIterator(tree1));
+		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
+			tw.addTree(new DirCacheIterator(tree0));
+			tw.addTree(new DirCacheIterator(tree1));
 
-		assertModes("0", REGULAR_FILE, REGULAR_FILE, tw);
-		assertFalse(tw.isDirectoryFileConflict());
-		assertModes("a", REGULAR_FILE, TREE, tw);
-		assertTrue(tw.isSubtree());
-		assertTrue(tw.isDirectoryFileConflict());
-		tw.enterSubtree();
-		assertModes("a/b", MISSING, REGULAR_FILE, tw);
-		assertTrue(tw.isDirectoryFileConflict());
-		assertModes("a/c", MISSING, TREE, tw);
-		assertTrue(tw.isDirectoryFileConflict());
-		tw.enterSubtree();
-		assertModes("a/c/e", MISSING, REGULAR_FILE, tw);
-		assertTrue(tw.isDirectoryFileConflict());
+			assertModes("0", REGULAR_FILE, REGULAR_FILE, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a", REGULAR_FILE, TREE, tw);
+			assertTrue(tw.isSubtree());
+			assertTrue(tw.isDirectoryFileConflict());
+			tw.enterSubtree();
+			assertModes("a/b", MISSING, REGULAR_FILE, tw);
+			assertTrue(tw.isDirectoryFileConflict());
+			assertModes("a/c", MISSING, TREE, tw);
+			assertTrue(tw.isDirectoryFileConflict());
+			tw.enterSubtree();
+			assertModes("a/c/e", MISSING, REGULAR_FILE, tw);
+			assertTrue(tw.isDirectoryFileConflict());
 
-		assertModes("a.b", MISSING, REGULAR_FILE, tw);
-		assertFalse(tw.isDirectoryFileConflict());
+			assertModes("a.b", MISSING, REGULAR_FILE, tw);
+			assertFalse(tw.isDirectoryFileConflict());
+		}
 	}
 
 	private static void assertModes(final String path, final FileMode mode0,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java
index 2f797e3..964ffca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java
@@ -101,13 +101,14 @@
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteAll();
 		writeFileWithFolderName();
-		TreeWalk treeWalk = createTreeWalk(commit);
 
-		assertTrue(treeWalk.next());
-		assertEquals("folder", treeWalk.getPathString());
-		assertTrue(treeWalk.next());
-		assertEquals("folder/file", treeWalk.getPathString());
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertTrue(treeWalk.next());
+			assertEquals("folder", treeWalk.getPathString());
+			assertTrue(treeWalk.next());
+			assertEquals("folder/file", treeWalk.getPathString());
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
@@ -115,24 +116,26 @@
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteAll();
 		writeFileWithFolderName();
-		TreeWalk treeWalk = createNonRecursiveTreeWalk(commit);
 
-		assertTrue(treeWalk.next());
-		assertEquals("folder", treeWalk.getPathString());
-		assertTrue(treeWalk.next());
-		assertEquals("folder", treeWalk.getPathString());
-		assertTrue(treeWalk.isSubtree());
-		treeWalk.enterSubtree();
-		assertTrue(treeWalk.next());
-		assertEquals("folder/file", treeWalk.getPathString());
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createNonRecursiveTreeWalk(commit)) {
+			assertTrue(treeWalk.next());
+			assertEquals("folder", treeWalk.getPathString());
+			assertTrue(treeWalk.next());
+			assertEquals("folder", treeWalk.getPathString());
+			assertTrue(treeWalk.isSubtree());
+			treeWalk.enterSubtree();
+			assertTrue(treeWalk.next());
+			assertEquals("folder/file", treeWalk.getPathString());
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileCommitted() throws Exception {
 		RevCommit commit = writeFileAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
@@ -153,89 +156,100 @@
 				"<<<<<<< HEAD\nside\n=======\nmaster\n>>>>>>> master\n");
 		writeTrashFile(FILE, "master");
 
-		TreeWalk treeWalk = createTreeWalk(side);
-		int count = 0;
-		while (treeWalk.next())
-			count++;
-		assertEquals(2, count);
+		try (TreeWalk treeWalk = createTreeWalk(side)) {
+			int count = 0;
+			while (treeWalk.next())
+				count++;
+			assertEquals(2, count);
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommitted() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testEmptyFolderCommitted() throws Exception {
 		RevCommit commit = createEmptyFolderAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileCommittedChangedNotModified() throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		writeFile();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommittedChangedNotModified() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolder();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileCommittedModified() throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		writeFileModified();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE);
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommittedModified() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolderModified();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
 	public void testFileCommittedDeleted() throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		deleteFile();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE);
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommittedDeleted() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteFileInFolder();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommittedAllDeleted() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteAll();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
 	public void testEmptyFolderCommittedDeleted() throws Exception {
 		RevCommit commit = createEmptyFolderAndCommit();
 		deleteFolder();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
@@ -243,8 +257,9 @@
 			throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		writeFileModifiedAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE);
+		}
 	}
 
 	@Test
@@ -252,8 +267,9 @@
 			throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolderModifiedAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
@@ -261,8 +277,9 @@
 			throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		deleteFileAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE);
+		}
 	}
 
 	@Test
@@ -270,8 +287,9 @@
 			throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteFileInFolderAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
@@ -279,8 +297,9 @@
 			throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteAllAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
@@ -288,96 +307,108 @@
 			throws Exception {
 		RevCommit commit = createEmptyFolderAndCommit();
 		deleteFolderAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileUntracked() throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		writeFileUntracked();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, UNTRACKED_FILE);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, UNTRACKED_FILE);
+		}
 	}
 
 	@Test
 	public void testFileInFolderUntracked() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolderUntracked();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, UNTRACKED_FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, UNTRACKED_FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
 	public void testEmptyFolderUntracked() throws Exception {
 		RevCommit commit = createEmptyFolderAndCommit();
 		createEmptyFolderUntracked();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileIgnored() throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		writeFileIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileInFolderIgnored() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolderIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileInFolderAllIgnored() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolderAllIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testEmptyFolderIgnored() throws Exception {
 		RevCommit commit = createEmptyFolderAndCommit();
 		createEmptyFolderIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileIgnoredNotHonored() throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		writeFileIgnored();
-		TreeWalk treeWalk = createTreeWalkDishonorIgnores(commit);
-		assertPaths(treeWalk, IGNORED_FILE, GITIGNORE);
+		try (TreeWalk treeWalk = createTreeWalkDishonorIgnores(commit)) {
+			assertPaths(treeWalk, IGNORED_FILE, GITIGNORE);
+		}
 	}
 
 	@Test
 	public void testFileCommittedModifiedIgnored() throws Exception {
 		RevCommit commit = writeFileAndCommit();
 		writeFileModifiedIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE);
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommittedModifiedIgnored() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolderModifiedIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommittedModifiedAllIgnored() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
 		writeFileInFolderModifiedAllIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
@@ -386,8 +417,9 @@
 		RevCommit commit = writeFileAndCommit();
 		deleteFileAndCommit();
 		rewriteFileIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE);
+		}
 	}
 
 	@Test
@@ -396,8 +428,9 @@
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteFileInFolderAndCommit();
 		rewriteFileInFolderIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
@@ -406,8 +439,9 @@
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteAllAndCommit();
 		rewriteFileInFolderAllIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
@@ -416,15 +450,17 @@
 		RevCommit commit = createEmptyFolderAndCommit();
 		deleteFolderAndCommit();
 		recreateEmptyFolderIgnored();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertFalse(treeWalk.next());
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertFalse(treeWalk.next());
+		}
 	}
 
 	@Test
 	public void testFileInFolderCommittedNonRecursive() throws Exception {
 		RevCommit commit = writeFileInFolderAndCommit();
-		TreeWalk treeWalk = createNonRecursiveTreeWalk(commit);
-		assertPaths(treeWalk, FOLDER);
+		try (TreeWalk treeWalk = createNonRecursiveTreeWalk(commit)) {
+			assertPaths(treeWalk, FOLDER);
+		}
 	}
 
 	@Test
@@ -432,8 +468,9 @@
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteAll();
 		writeFileWithFolderName();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FOLDER, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FOLDER, FILE_IN_FOLDER);
+		}
 	}
 
 	@Test
@@ -442,8 +479,9 @@
 		RevCommit commit = writeFileInFolderAndCommit();
 		deleteAll();
 		writeFileWithFolderNameAndCommit();
-		TreeWalk treeWalk = createTreeWalk(commit);
-		assertPaths(treeWalk, FOLDER, FILE_IN_FOLDER);
+		try (TreeWalk treeWalk = createTreeWalk(commit)) {
+			assertPaths(treeWalk, FOLDER, FILE_IN_FOLDER);
+		}
 	}
 
 	private void writeFile() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
index 89a2fc4..c9a3393 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
@@ -254,7 +254,9 @@
 						formatter.format(t1.toInstant()),
 						Long.valueOf(resolutionNs)), t2.compareTo(t1) > 0);
 			} finally {
-				Files.delete(f);
+				if (f != null) {
+					Files.delete(f);
+				}
 			}
 		}
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java
index a6af3a5..57d2c5e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateParserBadlyFormattedTest.java
@@ -79,7 +79,7 @@
 	}
 
 	@DataPoints
-	static public String[] getDataPoints() {
+	public static String[] getDataPoints() {
 		return new String[] { "", "1970", "3000.3000.3000", "3 yesterday ago",
 				"now yesterday ago", "yesterdays", "3.day. 2.week.ago",
 				"day ago", "Gra Feb 21 15:35:00 2007 +0100",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
index e5fcbf9..26653db 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
@@ -58,6 +58,8 @@
 import org.eclipse.jgit.hooks.PreCommitHook;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Assume;
 import org.junit.Test;
@@ -221,6 +223,107 @@
 	}
 
 	@Test
+	public void testRunHookHooksPathRelative() throws Exception {
+		assumeSupportedPlatform();
+
+		writeHookFile(PreCommitHook.NAME,
+				"#!/bin/sh\necho \"Wrong hook $1 $2\"\nread INPUT\necho $INPUT\n"
+						+ "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+		writeHookFile("../../" + PreCommitHook.NAME,
+				"#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
+						+ "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+		StoredConfig cfg = db.getConfig();
+		cfg.load();
+		cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_HOOKS_PATH, ".");
+		cfg.save();
+		try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+				ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+			ProcessResult res = FS.DETECTED.runHookIfPresent(db,
+					PreCommitHook.NAME, new String[] { "arg1", "arg2" },
+					new PrintStream(out), new PrintStream(err), "stdin");
+
+			assertEquals("unexpected hook output",
+					"test arg1 arg2\nstdin\n"
+							+ db.getDirectory().getAbsolutePath() + '\n'
+							+ db.getWorkTree().getAbsolutePath() + '\n',
+					out.toString("UTF-8"));
+			assertEquals("unexpected output on stderr stream", "stderr\n",
+					err.toString("UTF-8"));
+			assertEquals("unexpected exit code", 0, res.getExitCode());
+			assertEquals("unexpected process status", ProcessResult.Status.OK,
+					res.getStatus());
+		}
+	}
+
+	@Test
+	public void testRunHookHooksPathAbsolute() throws Exception {
+		assumeSupportedPlatform();
+
+		writeHookFile(PreCommitHook.NAME,
+				"#!/bin/sh\necho \"Wrong hook $1 $2\"\nread INPUT\necho $INPUT\n"
+						+ "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+		writeHookFile("../../" + PreCommitHook.NAME,
+				"#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
+						+ "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+		StoredConfig cfg = db.getConfig();
+		cfg.load();
+		cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_HOOKS_PATH,
+				db.getWorkTree().getAbsolutePath());
+		cfg.save();
+		try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+				ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+			ProcessResult res = FS.DETECTED.runHookIfPresent(db,
+					PreCommitHook.NAME, new String[] { "arg1", "arg2" },
+					new PrintStream(out), new PrintStream(err), "stdin");
+
+			assertEquals("unexpected hook output",
+					"test arg1 arg2\nstdin\n"
+							+ db.getDirectory().getAbsolutePath() + '\n'
+							+ db.getWorkTree().getAbsolutePath() + '\n',
+					out.toString("UTF-8"));
+			assertEquals("unexpected output on stderr stream", "stderr\n",
+					err.toString("UTF-8"));
+			assertEquals("unexpected exit code", 0, res.getExitCode());
+			assertEquals("unexpected process status", ProcessResult.Status.OK,
+					res.getStatus());
+		}
+	}
+
+	@Test
+	public void testHookPathWithBlank() throws Exception {
+		assumeSupportedPlatform();
+
+		File file = writeHookFile("../../a directory/" + PreCommitHook.NAME,
+				"#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
+						+ "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+		StoredConfig cfg = db.getConfig();
+		cfg.load();
+		cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_HOOKS_PATH,
+				file.getParentFile().getAbsolutePath());
+		cfg.save();
+		try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+				ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+			ProcessResult res = FS.DETECTED.runHookIfPresent(db,
+					PreCommitHook.NAME, new String[] { "arg1", "arg2" },
+					new PrintStream(out), new PrintStream(err), "stdin");
+
+			assertEquals("unexpected hook output",
+					"test arg1 arg2\nstdin\n"
+							+ db.getDirectory().getAbsolutePath() + '\n'
+							+ db.getWorkTree().getAbsolutePath() + '\n',
+					out.toString("UTF-8"));
+			assertEquals("unexpected output on stderr stream", "stderr\n",
+					err.toString("UTF-8"));
+			assertEquals("unexpected exit code", 0, res.getExitCode());
+			assertEquals("unexpected process status", ProcessResult.Status.OK,
+					res.getStatus());
+		}
+	}
+
+	@Test
 	public void testFailedPreCommitHookBlockCommit() throws Exception {
 		assumeSupportedPlatform();
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HttpSupportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HttpSupportTest.java
new file mode 100644
index 0000000..cbe4eb2
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HttpSupportTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+
+public class HttpSupportTest {
+
+	private static class TestProxySelector extends ProxySelector {
+
+		private static final Proxy DUMMY = new Proxy(Proxy.Type.HTTP,
+				InetSocketAddress.createUnresolved("localhost", 0));
+
+		@Override
+		public List<Proxy> select(URI uri) {
+			if ("http".equals(uri.getScheme())
+					&& "somehost".equals(uri.getHost())) {
+				return Collections.singletonList(DUMMY);
+			}
+			return Collections.singletonList(Proxy.NO_PROXY);
+		}
+
+		@Override
+		public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+			// Empty
+		}
+	}
+
+	@Test
+	public void testMalformedUri() throws Exception {
+		// Valid URL, but backslash is not allowed in a URI in the userinfo part
+		// per RFC 3986: https://tools.ietf.org/html/rfc3986#section-3.2.1 .
+		// Test that conversion to URI to call the ProxySelector does not throw
+		// an exception.
+		Proxy proxy = HttpSupport.proxyFor(new TestProxySelector(), new URL(
+				"http://infor\\c.jones@somehost/somewhere/someproject.git"));
+		assertNotNull(proxy);
+		assertEquals(Proxy.Type.HTTP, proxy.type());
+	}
+
+	@Test
+	public void testCorrectUri() throws Exception {
+		// Backslash escaped as %5C is correct.
+		Proxy proxy = HttpSupport.proxyFor(new TestProxySelector(), new URL(
+				"http://infor%5Cc.jones@somehost/somewhere/someproject.git"));
+		assertNotNull(proxy);
+		assertEquals(Proxy.Type.HTTP, proxy.type());
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringGitPathStyleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringGitPathStyleTest.java
index 9a0c96e..c09b136 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringGitPathStyleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringGitPathStyleTest.java
@@ -45,6 +45,7 @@
 
 import static java.nio.charset.StandardCharsets.ISO_8859_1;
 import static org.eclipse.jgit.util.QuotedString.GIT_PATH;
+import static org.eclipse.jgit.util.QuotedString.GIT_PATH_MINIMAL;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
@@ -67,6 +68,12 @@
 		assertEquals(exp, r);
 	}
 
+	private static void assertDequoteMinimal(String exp, String in) {
+		final byte[] b = ('"' + in + '"').getBytes(ISO_8859_1);
+		final String r = GIT_PATH_MINIMAL.dequote(b, 0, b.length);
+		assertEquals(exp, r);
+	}
+
 	@Test
 	public void testQuote_Empty() {
 		assertEquals("\"\"", GIT_PATH.quote(""));
@@ -206,4 +213,75 @@
 		assertSame("abc@2x.png", GIT_PATH.quote("abc@2x.png"));
 		assertDequote("abc@2x.png", "abc\\1002x.png");
 	}
+
+	@Test
+	public void testNoQuote() {
+		assertSame("\u00c5ngstr\u00f6m",
+				GIT_PATH_MINIMAL.quote("\u00c5ngstr\u00f6m"));
+	}
+
+	@Test
+	public void testQuoteMinimal() {
+		assertEquals("\"\u00c5n\\\\str\u00f6m\"",
+				GIT_PATH_MINIMAL.quote("\u00c5n\\str\u00f6m"));
+	}
+
+	@Test
+	public void testDequoteMinimal() {
+		assertEquals("\u00c5n\\str\u00f6m", GIT_PATH_MINIMAL
+				.dequote(GIT_PATH_MINIMAL.quote("\u00c5n\\str\u00f6m")));
+
+	}
+
+	@Test
+	public void testRoundtripMinimal() {
+		assertEquals("\u00c5ngstr\u00f6m", GIT_PATH_MINIMAL
+				.dequote(GIT_PATH_MINIMAL.quote("\u00c5ngstr\u00f6m")));
+
+	}
+
+	@Test
+	public void testQuoteMinimalDequoteNormal() {
+		assertEquals("\u00c5n\\str\u00f6m", GIT_PATH
+				.dequote(GIT_PATH_MINIMAL.quote("\u00c5n\\str\u00f6m")));
+
+	}
+
+	@Test
+	public void testQuoteNormalDequoteMinimal() {
+		assertEquals("\u00c5n\\str\u00f6m", GIT_PATH_MINIMAL
+				.dequote(GIT_PATH.quote("\u00c5n\\str\u00f6m")));
+
+	}
+
+	@Test
+	public void testRoundtripMinimalDequoteNormal() {
+		assertEquals("\u00c5ngstr\u00f6m",
+				GIT_PATH.dequote(GIT_PATH_MINIMAL.quote("\u00c5ngstr\u00f6m")));
+
+	}
+
+	@Test
+	public void testRoundtripNormalDequoteMinimal() {
+		assertEquals("\u00c5ngstr\u00f6m",
+				GIT_PATH_MINIMAL.dequote(GIT_PATH.quote("\u00c5ngstr\u00f6m")));
+
+	}
+
+	@Test
+	public void testDequote_UTF8_Minimal() {
+		assertDequoteMinimal("\u00c5ngstr\u00f6m",
+				"\\303\\205ngstr\\303\\266m");
+	}
+
+	@Test
+	public void testDequote_RawUTF8_Minimal() {
+		assertDequoteMinimal("\u00c5ngstr\u00f6m", "\303\205ngstr\303\266m");
+	}
+
+	@Test
+	public void testDequote_RawLatin1_Minimal() {
+		assertDequoteMinimal("\u00c5ngstr\u00f6m", "\305ngstr\366m");
+	}
+
 }
diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
index 525ac67..9fd92b1 100644
--- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index b143d51..60395c4 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: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ui
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="5.5.2"
-Import-Package: org.eclipse.jgit.errors;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.lib;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.nls;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revplot;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.revwalk;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.transport;version="[5.5.2,5.6.0)",
- org.eclipse.jgit.util;version="[5.5.2,5.6.0)"
+Export-Package: org.eclipse.jgit.awtui;version="5.6.2"
+Import-Package: org.eclipse.jgit.errors;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.lib;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.nls;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revplot;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.revwalk;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.transport;version="[5.6.2,5.7.0)",
+ org.eclipse.jgit.util;version="[5.6.2,5.7.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
index 5e75347..d87804d 100644
--- a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.ui - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ui.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ui;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index fe7af76..822bd51 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index a2066ec..eb652a2 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,31 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <component id="org.eclipse.jgit" version="2">
-    <resource path="src/org/eclipse/jgit/dircache/DirCacheEntry.java" type="org.eclipse.jgit.dircache.DirCacheEntry">
-        <filter id="1142947843">
+    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.transport.BaseReceivePack">
+        <filter id="305324134">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="getLastModifiedInstant()"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="mightBeRacilyClean(Instant)"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="setLastModified(Instant)"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/lib/AnyObjectId.java" type="org.eclipse.jgit.lib.AnyObjectId">
-        <filter id="1141899266">
-            <message_arguments>
-                <message_argument value="5.4"/>
-                <message_argument value="5.5"/>
-                <message_argument value="isEqual(AnyObjectId, AnyObjectId)"/>
+                <message_argument value="org.eclipse.jgit.transport.BaseReceivePack"/>
+                <message_argument value="org.eclipse.jgit_5.6.2"/>
             </message_arguments>
         </filter>
     </resource>
@@ -76,20 +55,20 @@
         </filter>
         <filter id="1142947843">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="CONFIG_FILESYSTEM_SECTION"/>
+                <message_argument value="5.6.2"/>
+                <message_argument value="CONFIG_EXTENSIONS_SECTION"/>
             </message_arguments>
         </filter>
         <filter id="1142947843">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="CONFIG_KEY_MIN_RACY_THRESHOLD"/>
+                <message_argument value="5.6.2"/>
+                <message_argument value="CONFIG_KEY_REF_STORAGE"/>
             </message_arguments>
         </filter>
         <filter id="1142947843">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="CONFIG_KEY_TIMESTAMP_RESOLUTION"/>
+                <message_argument value="5.6.2"/>
+                <message_argument value="CONFIG_REF_STORAGE_REFTABLE"/>
             </message_arguments>
         </filter>
     </resource>
@@ -100,6 +79,20 @@
                 <message_argument value="XDG_CONFIG_HOME"/>
             </message_arguments>
         </filter>
+        <filter id="1142947843">
+            <message_arguments>
+                <message_argument value="5.6.2"/>
+                <message_argument value="TABLES_LIST"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/revwalk/ReachabilityChecker.java" type="org.eclipse.jgit.revwalk.ReachabilityChecker">
+        <filter id="403804204">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.revwalk.ReachabilityChecker"/>
+                <message_argument value="areAllReachable(Collection&lt;RevCommit&gt;, Stream&lt;RevCommit&gt;)"/>
+            </message_arguments>
+        </filter>
     </resource>
     <resource path="src/org/eclipse/jgit/storage/file/WindowCacheConfig.java" type="org.eclipse.jgit.storage.file.WindowCacheConfig">
         <filter id="1142947843">
@@ -124,79 +117,82 @@
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator">
-        <filter id="1142947843">
+    <resource path="src/org/eclipse/jgit/storage/pack/PackStatistics.java" type="org.eclipse.jgit.storage.pack.PackStatistics$Accumulator">
+        <filter id="336658481">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="getEntryLastModifiedInstant()"/>
+                <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/>
+                <message_argument value="offloadedPackfileSize"/>
+            </message_arguments>
+        </filter>
+        <filter id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/>
+                <message_argument value="offloadedPackfiles"/>
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator$Entry">
-        <filter id="336695337">
+    <resource path="src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java" type="org.eclipse.jgit.transport.AbstractAdvertiseRefsHook">
+        <filter comment="Merged BaseReceivePack into ReceivePack, replace BaseReceivePack with ReceivePack" id="338792546">
             <message_arguments>
-                <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator.Entry"/>
-                <message_argument value="getLastModifiedInstant()"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="getLastModifiedInstant()"/>
+                <message_argument value="org.eclipse.jgit.transport.AbstractAdvertiseRefsHook"/>
+                <message_argument value="advertiseRefs(BaseReceivePack)"/>
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS">
-        <filter id="338792546">
+    <resource path="src/org/eclipse/jgit/transport/AdvertiseRefsHook.java" type="org.eclipse.jgit.transport.AdvertiseRefsHook">
+        <filter comment="Merged BaseReceivePack into ReceivePack, replace BaseReceivePack with ReceivePack" id="403804204">
             <message_arguments>
-                <message_argument value="org.eclipse.jgit.util.FS"/>
-                <message_argument value="getFsTimerResolution(Path)"/>
+                <message_argument value="org.eclipse.jgit.transport.AdvertiseRefsHook"/>
+                <message_argument value="advertiseRefs(ReceivePack)"/>
             </message_arguments>
         </filter>
-        <filter id="1142947843">
+        <filter comment="Merged BaseReceivePack into ReceivePack, replace BaseReceivePack with ReceivePack" id="405901410">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="getFileStoreAttributes(Path)"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="lastModifiedInstant(File)"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="lastModifiedInstant(Path)"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="setAsyncFileStoreAttributes(boolean)"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="setLastModified(Path, Instant)"/>
+                <message_argument value="org.eclipse.jgit.transport.AdvertiseRefsHook"/>
+                <message_argument value="advertiseRefs(BaseReceivePack)"/>
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS$Attributes">
-        <filter id="1142947843">
+    <resource path="src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java" type="org.eclipse.jgit.transport.AdvertiseRefsHookChain">
+        <filter comment="Merged BaseReceivePack into ReceivePack, replace BaseReceivePack with ReceivePack" id="338792546">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="getLastModifiedInstant()"/>
+                <message_argument value="org.eclipse.jgit.transport.AdvertiseRefsHookChain"/>
+                <message_argument value="advertiseRefs(BaseReceivePack)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/transport/ReceiveCommand.java" type="org.eclipse.jgit.transport.ReceiveCommand">
+        <filter comment="Merged BaseReceivePack into ReceivePack, replace BaseReceivePack with ReceivePack" id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.ReceiveCommand"/>
+                <message_argument value="execute(BaseReceivePack)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/transport/ReceivePack.java" type="org.eclipse.jgit.transport.ReceivePack">
+        <filter comment="Merged BaseReceivePack into ReceivePack, replace BaseReceivePack with ReceivePack" id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.ReceivePack"/>
+                <message_argument value="getLockMessageProcessName()"/>
+            </message_arguments>
+        </filter>
+        <filter comment="Merged BaseReceivePack into ReceivePack, replace BaseReceivePack with ReceivePack" id="338849923">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.ReceivePack"/>
+            </message_arguments>
+        </filter>
+        <filter id="421572723">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.ReceivePack"/>
+                <message_argument value="enableCapabilities()"/>
             </message_arguments>
         </filter>
     </resource>
     <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS$FileStoreAttributes">
-        <filter id="1142947843">
+        <filter id="1226833923">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="FileStoreAttributes"/>
+                <message_argument value="5.6.2"/>
+                <message_argument value="setBackground(boolean)"/>
             </message_arguments>
         </filter>
     </resource>
@@ -208,28 +204,11 @@
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/util/References.java" type="org.eclipse.jgit.util.References">
-        <filter id="1108344834">
+    <resource path="src/org/eclipse/jgit/util/QuotedString.java" type="org.eclipse.jgit.util.QuotedString">
+        <filter id="336658481">
             <message_arguments>
-                <message_argument value="5.4"/>
-                <message_argument value="5.5"/>
-                <message_argument value="org.eclipse.jgit.util.References"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/util/SimpleLruCache.java" type="org.eclipse.jgit.util.SimpleLruCache">
-        <filter id="1109393411">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="org.eclipse.jgit.util.SimpleLruCache"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/util/Stats.java" type="org.eclipse.jgit.util.Stats">
-        <filter id="1109393411">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="org.eclipse.jgit.util.Stats"/>
+                <message_argument value="org.eclipse.jgit.util.QuotedString"/>
+                <message_argument value="GIT_PATH_MINIMAL"/>
             </message_arguments>
         </filter>
     </resource>
@@ -242,18 +221,6 @@
         </filter>
         <filter id="1142947843">
             <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="getSystemConfig()"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
-                <message_argument value="5.1.9"/>
-                <message_argument value="getUserConfig()"/>
-            </message_arguments>
-        </filter>
-        <filter id="1142947843">
-            <message_arguments>
                 <message_argument value="5.5.2"/>
                 <message_argument value="getJGitConfig()"/>
             </message_arguments>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index ef6f5e7..bc7ba1e 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -92,7 +92,7 @@
 org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
 org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
 org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
 org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
 org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
 org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 69cdf30..3d8f8e4 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,12 +3,12 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 5.5.2.qualifier
+Bundle-Version: 5.6.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="5.5.2",
- org.eclipse.jgit.api;version="5.5.2";
+Export-Package: org.eclipse.jgit.annotations;version="5.6.2",
+ org.eclipse.jgit.api;version="5.6.2";
   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.5.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="5.5.2",
- org.eclipse.jgit.blame;version="5.5.2";
+ org.eclipse.jgit.api.errors;version="5.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="5.6.2",
+ org.eclipse.jgit.blame;version="5.6.2";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="5.5.2";
+ org.eclipse.jgit.diff;version="5.6.2";
   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.5.2";
+ org.eclipse.jgit.dircache;version="5.6.2";
   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.5.2";
+ org.eclipse.jgit.errors;version="5.6.2";
   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.5.2";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="5.5.2",
- org.eclipse.jgit.gitrepo;version="5.5.2";
+ org.eclipse.jgit.events;version="5.6.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="5.6.2",
+ org.eclipse.jgit.gitrepo;version="5.6.2";
   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.5.2";x-internal:=true,
- org.eclipse.jgit.hooks;version="5.5.2";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="5.5.2",
- org.eclipse.jgit.ignore.internal;version="5.5.2";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="5.5.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="5.5.2";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="5.5.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.revwalk;version="5.5.2";x-internal:=true,
- org.eclipse.jgit.internal.storage.dfs;version="5.5.2";
+ org.eclipse.jgit.gitrepo.internal;version="5.6.2";x-internal:=true,
+ org.eclipse.jgit.hooks;version="5.6.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="5.6.2",
+ org.eclipse.jgit.ignore.internal;version="5.6.2";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="5.6.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.fsck;version="5.6.2";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.ketch;version="5.6.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.revwalk;version="5.6.2";x-internal:=true,
+ org.eclipse.jgit.internal.storage.dfs;version="5.6.2";
   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.5.2";
+ org.eclipse.jgit.internal.storage.file;version="5.6.2";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -77,19 +77,19 @@
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="5.5.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="5.5.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="5.5.2";
+ org.eclipse.jgit.internal.storage.io;version="5.6.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.pack;version="5.6.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftable;version="5.6.2";
   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.5.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="5.5.2";x-internal:=true,
- org.eclipse.jgit.internal.transport.http;version="5.5.2";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="5.5.2";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="5.5.2";x-friends:="org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.lib;version="5.5.2";
+ org.eclipse.jgit.internal.storage.reftree;version="5.6.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.submodule;version="5.6.2";x-internal:=true,
+ org.eclipse.jgit.internal.transport.http;version="5.6.2";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.parser;version="5.6.2";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.transport.ssh;version="5.6.2";x-friends:="org.eclipse.jgit.ssh.apache",
+ org.eclipse.jgit.lib;version="5.6.2";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
@@ -99,33 +99,33 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.submodule",
- org.eclipse.jgit.lib.internal;version="5.5.2";x-internal:=true,
- org.eclipse.jgit.merge;version="5.5.2";
+ org.eclipse.jgit.lib.internal;version="5.6.2";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.merge;version="5.6.2";
   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.5.2",
- org.eclipse.jgit.notes;version="5.5.2";
+ org.eclipse.jgit.nls;version="5.6.2",
+ org.eclipse.jgit.notes;version="5.6.2";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="5.5.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="5.5.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="5.5.2";
+ org.eclipse.jgit.patch;version="5.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="5.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="5.6.2";
   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.5.2";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="5.5.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="5.5.2";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="5.5.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="5.5.2";
+ org.eclipse.jgit.revwalk.filter;version="5.6.2";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="5.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="5.6.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="5.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="5.6.2";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.internal.storage.pack,
@@ -138,24 +138,24 @@
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.errors,
    org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="5.5.2";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="5.5.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="5.5.2";
+ org.eclipse.jgit.transport.http;version="5.6.2";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="5.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="5.6.2";
   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.5.2";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="5.5.2";
+ org.eclipse.jgit.treewalk.filter;version="5.6.2";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="5.6.2";
   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.5.2",
- org.eclipse.jgit.util.sha1;version="5.5.2",
- org.eclipse.jgit.util.time;version="5.5.2"
+ org.eclipse.jgit.util.io;version="5.6.2",
+ org.eclipse.jgit.util.sha1;version="5.6.2",
+ org.eclipse.jgit.util.time;version="5.6.2"
 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)",
@@ -172,6 +172,7 @@
  org.bouncycastle.openpgp.operator;version="[1.61.0,2.0.0)",
  org.bouncycastle.openpgp.operator.jcajce;version="[1.61.0,2.0.0)",
  org.bouncycastle.util.encoders;version="[1.61.0,2.0.0)",
+ org.bouncycastle.util.io;version="[1.61.0,2.0.0)",
  org.slf4j;version="[1.7.0,2.0.0)",
  org.xml.sax,
  org.xml.sax.helpers
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 29db604..75494ff 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.5.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="5.5.2.qualifier";roots="."
+Bundle-Version: 5.6.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="5.6.2.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 656d4ff..517dc16 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.5.2-SNAPSHOT</version>
+    <version>5.6.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
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 14ab51c..70a0d80 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -283,11 +283,13 @@
 expectedReportForRefNotReceived={0}: expected report for ref {1} not received
 failedAtomicFileCreation=Atomic file creation failed, number of hard links to file {0} was not 2 but {1}
 failedCreateLockFile=Creating lock file {} failed
+failedToConvert=Failed to convert rest: %s
 failedToDetermineFilterDefinition=An exception occurred while determining filter definitions
 failedUpdatingRefs=failed updating refs
 failureDueToOneOfTheFollowing=Failure due to one of the following:
 failureUpdatingFETCH_HEAD=Failure updating FETCH_HEAD: {0}
 failureUpdatingTrackingRef=Failure updating tracking ref {0}: {1}
+fileAlreadyExists=File already exists: {0}
 fileCannotBeDeleted=File cannot be deleted: {0}
 fileIsTooLarge=File is too large: {0}
 fileModeNotSetForPath=FileMode not set for path {0}
@@ -300,7 +302,7 @@
 flagsAlreadyCreated={0} flags already created.
 funnyRefname=funny refname
 gcFailed=Garbage collection failed.
-gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection.  Consider adjusting gc.auto or gc.pruneExpire.
+gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection. Consider adjusting gc.auto or gc.pruneExpire.
 gpgFailedToParseSecretKey=Failed to parse secret key file in directory: {0}. Is the entered passphrase correct?
 gpgNoCredentialsProvider=missing credentials provider
 gpgNoKeyring=neither pubring.kbx nor secring.gpg files found
@@ -326,6 +328,7 @@
 incorrectOBJECT_ID_LENGTH=Incorrect OBJECT_ID_LENGTH.
 indexFileCorruptedNegativeBucketCount=Invalid negative bucket count read from pack v2 index file: {0}
 indexFileIsTooLargeForJgit=Index file is too large for jgit
+indexNumbersNotIncreasing=index numbers not increasing: ''{0}'': min {1}, last max {2}
 indexWriteException=Modified index could not be written
 initFailedBareRepoDifferentDirs=When initializing a bare repo with directory {0} and separate git-dir {1} specified both folders must point to the same location
 initFailedDirIsNoDirectory=Cannot set directory to ''{0}'' which is not a directory
@@ -351,6 +354,7 @@
 invalidGitdirRef = Invalid .git reference in file ''{0}''
 invalidGitModules=Invalid .gitmodules file
 invalidGitType=invalid git type: {0}
+invalidHooksPath=Invalid git config core.hooksPath = {0}
 invalidId=Invalid id: {0}
 invalidId0=Invalid id
 invalidIdLength=Invalid id length {0}; should be {1}
@@ -404,11 +408,14 @@
 localObjectsIncomplete=Local objects incomplete.
 localRefIsMissingObjects=Local ref {0} is missing object(s).
 localRepository=local repository
+lockAlreadyHeld=Lock on {0} already held
 lockCountMustBeGreaterOrEqual1=lockCount must be >= 1
 lockError=lock error: {0}
 lockFailedRetry=locking {0} failed after {1} retries
 lockOnNotClosed=Lock on {0} not closed.
 lockOnNotHeld=Lock on {0} not held.
+lockStreamClosed=Output to lock on {0} already closed
+lockStreamMultiple=Output to lock on {0} already opened
 maxCountMustBeNonNegative=max count must be >= 0
 mergeConflictOnNonNoteEntries=Merge conflict on non-note entries: base = {0}, ours = {1}, theirs = {2}
 mergeConflictOnNotes=Merge conflict on note {0}. base = {1}, ours = {2}, theirs = {2}
@@ -451,8 +458,10 @@
 noMergeBase=No merge base could be determined. Reason={0}. {1}
 noMergeHeadSpecified=No merge head specified
 nonBareLinkFilesNotSupported=Link files are not supported with nonbare repos
+nonCommitToHeads=Cannot point a branch to a non-commit object
 noPathAttributesFound=No Attributes found for {0}.
 noSuchRef=no such ref
+noSuchRefKnown=no such ref: {0}
 noSuchSubmodule=no such submodule {0}
 notABoolean=Not a boolean: {0}
 notABundle=not a bundle
@@ -551,7 +560,8 @@
 refAlreadyExists1=Ref {0} already exists
 reflogEntryNotFound=Entry {0} not found  in reflog for ''{1}''
 refNotResolved=Ref {0} cannot be resolved
-refTableRecordsMustIncrease=records must be increasing: last {0}, this {1}
+reftableDirExists=reftable dir exists and is nonempty
+reftableRecordsMustIncrease=records must be increasing: last {0}, this {1}
 refUpdateReturnCodeWas=RefUpdate return code was: {0}
 remoteConfigHasNoURIAssociated=Remote config "{0}" has no URIs associated
 remoteDoesNotHaveSpec=Remote does not have {0} available for fetch.
@@ -580,7 +590,7 @@
 repositoryState_merged=Merged
 repositoryState_normal=Normal
 repositoryState_rebase=Rebase
-repositoryState_rebaseInteractive=Rebase interactive
+repositoryState_rebaseInteractive=Interactive rebase
 repositoryState_rebaseOrApplyMailbox=Rebase/Apply mailbox
 repositoryState_rebaseWithMerge=Rebase w/merge
 requiredHashFunctionNotAvailable=Required hash function {0} not available.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
index f7576e9..a69aa70 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, GitHub Inc.
+ * Copyright (C) 2011, 2019 GitHub Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -42,11 +42,7 @@
  */
 package org.eclipse.jgit.api;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -56,17 +52,10 @@
 import org.eclipse.jgit.blame.BlameGenerator;
 import org.eclipse.jgit.blame.BlameResult;
 import org.eclipse.jgit.diff.DiffAlgorithm;
-import org.eclipse.jgit.diff.RawText;
 import org.eclipse.jgit.diff.RawTextComparator;
-import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.treewalk.WorkingTreeOptions;
-import org.eclipse.jgit.util.IO;
-import org.eclipse.jgit.util.io.AutoLFInputStream;
 
 /**
  * Blame command for building a {@link org.eclipse.jgit.blame.BlameResult} for a
@@ -221,69 +210,11 @@
 			else if (startCommit != null)
 				gen.push(null, startCommit);
 			else {
-				gen.push(null, repo.resolve(Constants.HEAD));
-				if (!repo.isBare()) {
-					DirCache dc = repo.readDirCache();
-					int entry = dc.findEntry(path);
-					if (0 <= entry)
-						gen.push(null, dc.getEntry(entry).getObjectId());
-
-					File inTree = new File(repo.getWorkTree(), path);
-					if (repo.getFS().isFile(inTree)) {
-						RawText rawText = getRawText(inTree);
-						gen.push(null, rawText);
-					}
-				}
+				gen.prepareHead();
 			}
 			return gen.computeBlameResult();
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 	}
-
-	private RawText getRawText(File inTree) throws IOException,
-			FileNotFoundException {
-		RawText rawText;
-
-		WorkingTreeOptions workingTreeOptions = getRepository().getConfig()
-				.get(WorkingTreeOptions.KEY);
-		AutoCRLF autoCRLF = workingTreeOptions.getAutoCRLF();
-		switch (autoCRLF) {
-		case FALSE:
-		case INPUT:
-			// Git used the repo format on checkout, but other tools
-			// may change the format to CRLF. We ignore that here.
-			rawText = new RawText(inTree);
-			break;
-		case TRUE:
-			try (AutoLFInputStream in = new AutoLFInputStream(
-					new FileInputStream(inTree), true)) {
-				// Canonicalization should lead to same or shorter length
-				// (CRLF to LF), so the file size on disk is an upper size bound
-				rawText = new RawText(toByteArray(in, (int) inTree.length()));
-			}
-			break;
-		default:
-			throw new IllegalArgumentException(
-					"Unknown autocrlf option " + autoCRLF); //$NON-NLS-1$
-		}
-		return rawText;
-	}
-
-	private static byte[] toByteArray(InputStream source, int upperSizeLimit)
-			throws IOException {
-		byte[] buffer = new byte[upperSizeLimit];
-		try {
-			int read = IO.readFully(source, buffer, 0);
-			if (read == upperSizeLimit)
-				return buffer;
-			else {
-				byte[] copy = new byte[read];
-				System.arraycopy(buffer, 0, copy, 0, read);
-				return copy;
-			}
-		} finally {
-			source.close();
-		}
-	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index e05f6f1..6d157bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -137,7 +137,7 @@
 	/**
 	 * Stage to check out, see {@link CheckoutCommand#setStage(Stage)}.
 	 */
-	public static enum Stage {
+	public enum Stage {
 		/**
 		 * Base stage (#1)
 		 */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
index c9dd547..aa63725 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
@@ -57,6 +57,7 @@
 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
@@ -129,9 +130,10 @@
 
 			// get the head commit
 			Ref headRef = repo.exactRef(Constants.HEAD);
-			if (headRef == null)
+			if (headRef == null) {
 				throw new NoHeadException(
 						JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported);
+			}
 
 			newHead = revWalk.parseCommit(headRef.getObjectId());
 
@@ -140,8 +142,9 @@
 				// get the commit to be cherry-picked
 				// handle annotated tags
 				ObjectId srcObjectId = src.getPeeledObjectId();
-				if (srcObjectId == null)
+				if (srcObjectId == null) {
 					srcObjectId = src.getObjectId();
+				}
 				RevCommit srcCommit = revWalk.parseCommit(srcObjectId);
 
 				// get the parent of the commit to cherry-pick
@@ -157,26 +160,33 @@
 				merger.setCommitNames(new String[] { "BASE", ourName, //$NON-NLS-1$
 						cherryPickName });
 				if (merger.merge(newHead, srcCommit)) {
+					if (!merger.getModifiedFiles().isEmpty()) {
+						repo.fireEvent(new WorkingTreeModifiedEvent(
+								merger.getModifiedFiles(), null));
+					}
 					if (AnyObjectId.isEqual(newHead.getTree().getId(),
-							merger.getResultTreeId()))
+							merger.getResultTreeId())) {
 						continue;
+					}
 					DirCacheCheckout dco = new DirCacheCheckout(repo,
 							newHead.getTree(), repo.lockDirCache(),
 							merger.getResultTreeId());
 					dco.setFailOnConflict(true);
 					dco.setProgressMonitor(monitor);
 					dco.checkout();
-					if (!noCommit)
+					if (!noCommit) {
 						newHead = new Git(getRepository()).commit()
 								.setMessage(srcCommit.getFullMessage())
 								.setReflogComment(reflogPrefix + " " //$NON-NLS-1$
 										+ srcCommit.getShortMessage())
 								.setAuthor(srcCommit.getAuthorIdent())
 								.setNoVerify(true).call();
+					}
 					cherryPickedRefs.add(src);
 				} else {
-					if (merger.failed())
+					if (merger.failed()) {
 						return new CherryPickResult(merger.getFailingPaths());
+					}
 
 					// there are merge conflicts
 
@@ -184,10 +194,14 @@
 							.formatWithConflicts(srcCommit.getFullMessage(),
 									merger.getUnmergedPaths());
 
-					if (!noCommit)
+					if (!noCommit) {
 						repo.writeCherryPickHead(srcCommit.getId());
+					}
 					repo.writeMergeCommitMsg(message);
 
+					repo.fireEvent(new WorkingTreeModifiedEvent(
+							merger.getModifiedFiles(), null));
+
 					return CherryPickResult.CONFLICT;
 				}
 			}
@@ -213,10 +227,11 @@
 								Integer.valueOf(srcCommit.getParentCount())));
 			srcParent = srcCommit.getParent(0);
 		} else {
-			if (mainlineParentNumber.intValue() > srcCommit.getParentCount())
+			if (mainlineParentNumber.intValue() > srcCommit.getParentCount()) {
 				throw new JGitInternalException(MessageFormat.format(
 						JGitText.get().commitDoesNotHaveGivenParent, srcCommit,
 						mainlineParentNumber));
+			}
 			srcParent = srcCommit
 					.getParent(mainlineParentNumber.intValue() - 1);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 9f63d0f..7008cd4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -77,8 +77,8 @@
 import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.transport.TagOpt;
 import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
 
 /**
  * Clone a repository into a new working directory
@@ -106,6 +106,8 @@
 
 	private boolean cloneAllBranches;
 
+	private boolean mirror;
+
 	private boolean cloneSubmodules;
 
 	private boolean noCheckout;
@@ -118,6 +120,12 @@
 
 	private boolean gitDirExistsInitially;
 
+	private FETCH_TYPE fetchType;
+
+	private enum FETCH_TYPE {
+		MULTIPLE_BRANCHES, ALL_BRANCHES, MIRROR
+	}
+
 	/**
 	 * Callback for status of clone operation.
 	 *
@@ -191,6 +199,7 @@
 			throw new InvalidRemoteException(
 					MessageFormat.format(JGitText.get().invalidURL, uri));
 		}
+		setFetchType();
 		@SuppressWarnings("resource") // Closed by caller
 		Repository repository = init();
 		FetchResult fetchResult = null;
@@ -234,6 +243,20 @@
 		return new Git(repository, true);
 	}
 
+	private void setFetchType() {
+		if (mirror) {
+			fetchType = FETCH_TYPE.MIRROR;
+			setBare(true);
+		} else if (cloneAllBranches) {
+			fetchType = FETCH_TYPE.ALL_BRANCHES;
+		} else if (branchesToClone != null && !branchesToClone.isEmpty()) {
+			fetchType = FETCH_TYPE.MULTIPLE_BRANCHES;
+		} else {
+			// Default: neither mirror nor all nor specific refs given
+			fetchType = FETCH_TYPE.ALL_BRANCHES;
+		}
+	}
+
 	private static boolean isNonEmptyDirectory(File dir) {
 		if (dir != null && dir.exists()) {
 			File[] files = dir.listFiles();
@@ -282,12 +305,11 @@
 		RemoteConfig config = new RemoteConfig(clonedRepo.getConfig(), remote);
 		config.addURI(u);
 
-		final String dst = (bare ? Constants.R_HEADS : Constants.R_REMOTES
-				+ config.getName() + '/') + '*';
-		boolean fetchAll = cloneAllBranches || branchesToClone == null
-				|| branchesToClone.isEmpty();
+		boolean fetchAll = fetchType == FETCH_TYPE.ALL_BRANCHES
+				|| fetchType == FETCH_TYPE.MIRROR;
 
-		config.setFetchRefSpecs(calculateRefSpecs(fetchAll, dst));
+		config.setFetchRefSpecs(calculateRefSpecs(fetchType, config.getName()));
+		config.setMirror(fetchType == FETCH_TYPE.MIRROR);
 		config.update(clonedRepo.getConfig());
 
 		clonedRepo.getConfig().save();
@@ -302,26 +324,33 @@
 		return command.call();
 	}
 
-	private List<RefSpec> calculateRefSpecs(boolean fetchAll, String dst) {
-		RefSpec heads = new RefSpec();
-		heads = heads.setForceUpdate(true);
-		heads = heads.setSourceDestination(Constants.R_HEADS + '*', dst);
+	private List<RefSpec> calculateRefSpecs(FETCH_TYPE type,
+			String remoteName) {
 		List<RefSpec> specs = new ArrayList<>();
-		if (!fetchAll) {
-			RefSpec tags = new RefSpec();
-			tags = tags.setForceUpdate(true);
-			tags = tags.setSourceDestination(Constants.R_TAGS + '*',
-					Constants.R_TAGS + '*');
-			for (String selectedRef : branchesToClone) {
-				if (heads.matchSource(selectedRef)) {
-					specs.add(heads.expandFromSource(selectedRef));
-				} else if (tags.matchSource(selectedRef)) {
-					specs.add(tags.expandFromSource(selectedRef));
-				}
-			}
+		if (type == FETCH_TYPE.MIRROR) {
+			specs.add(new RefSpec().setForceUpdate(true).setSourceDestination(
+					Constants.R_REFS + '*', Constants.R_REFS + '*'));
 		} else {
-			// We'll fetch the tags anyway.
-			specs.add(heads);
+			RefSpec heads = new RefSpec();
+			heads = heads.setForceUpdate(true);
+			final String dst = (bare ? Constants.R_HEADS
+					: Constants.R_REMOTES + remoteName + '/') + '*';
+			heads = heads.setSourceDestination(Constants.R_HEADS + '*', dst);
+			if (type == FETCH_TYPE.MULTIPLE_BRANCHES) {
+				RefSpec tags = new RefSpec().setForceUpdate(true)
+						.setSourceDestination(Constants.R_TAGS + '*',
+								Constants.R_TAGS + '*');
+				for (String selectedRef : branchesToClone) {
+					if (heads.matchSource(selectedRef)) {
+						specs.add(heads.expandFromSource(selectedRef));
+					} else if (tags.matchSource(selectedRef)) {
+						specs.add(tags.expandFromSource(selectedRef));
+					}
+				}
+			} else {
+				// We'll fetch the tags anyway.
+				specs.add(heads);
+			}
 		}
 		return specs;
 	}
@@ -614,6 +643,26 @@
 	}
 
 	/**
+	 * Set up a mirror of the source repository. This implies that a bare
+	 * repository will be created. Compared to {@link #setBare},
+	 * {@code #setMirror} not only maps local branches of the source to local
+	 * branches of the target, it maps all refs (including remote-tracking
+	 * branches, notes etc.) and sets up a refspec configuration such that all
+	 * these refs are overwritten by a git remote update in the target
+	 * repository.
+	 *
+	 * @param mirror
+	 *            whether to mirror all refs from the source repository
+	 *
+	 * @return {@code this}
+	 * @since 5.6
+	 */
+	public CloneCommand setMirror(boolean mirror) {
+		this.mirror = mirror;
+		return this;
+	}
+
+	/**
 	 * Set whether to clone submodules
 	 *
 	 * @param cloneSubmodules
@@ -630,8 +679,9 @@
 	 * Set the branches or tags to clone.
 	 * <p>
 	 * This is ignored if {@link #setCloneAllBranches(boolean)
-	 * setCloneAllBranches(true)} is used. If {@code branchesToClone} is
-	 * {@code null} or empty, it's also ignored and all branches will be cloned.
+	 * setCloneAllBranches(true)} or {@link #setMirror(boolean) setMirror(true)}
+	 * is used. If {@code branchesToClone} is {@code null} or empty, it's also
+	 * ignored.
 	 * </p>
 	 *
 	 * @param branchesToClone
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 b55987e..b32f7ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -143,6 +143,8 @@
 
 	private HashMap<String, PrintStream> hookOutRedirect = new HashMap<>(3);
 
+	private HashMap<String, PrintStream> hookErrRedirect = new HashMap<>(3);
+
 	private Boolean allowEmpty;
 
 	private Boolean signCommit;
@@ -188,7 +190,8 @@
 						state.name()));
 
 			if (!noVerify) {
-				Hooks.preCommit(repo, hookOutRedirect.get(PreCommitHook.NAME))
+				Hooks.preCommit(repo, hookOutRedirect.get(PreCommitHook.NAME),
+						hookErrRedirect.get(PreCommitHook.NAME))
 						.call();
 			}
 
@@ -230,7 +233,8 @@
 			if (!noVerify) {
 				message = Hooks
 						.commitMsg(repo,
-								hookOutRedirect.get(CommitMsgHook.NAME))
+								hookOutRedirect.get(CommitMsgHook.NAME),
+								hookErrRedirect.get(CommitMsgHook.NAME))
 						.setCommitMessage(message).call();
 			}
 
@@ -311,7 +315,8 @@
 						repo.writeRevertHead(null);
 					}
 					Hooks.postCommit(repo,
-							hookOutRedirect.get(PostCommitHook.NAME)).call();
+							hookOutRedirect.get(PostCommitHook.NAME),
+							hookErrRedirect.get(PostCommitHook.NAME)).call();
 
 					return revCommit;
 				}
@@ -519,7 +524,7 @@
 			int position = Collections.binarySearch(only, p);
 			if (position >= 0)
 				return position;
-			int l = p.lastIndexOf("/"); //$NON-NLS-1$
+			int l = p.lastIndexOf('/');
 			if (l < 1)
 				break;
 			p = p.substring(0, l);
@@ -891,6 +896,23 @@
 	}
 
 	/**
+	 * Set the error stream for all hook scripts executed by this command
+	 * (pre-commit, commit-msg, post-commit). If not set it defaults to
+	 * {@code System.err}.
+	 *
+	 * @param hookStdErr
+	 *            the error stream for hook scripts executed by this command
+	 * @return {@code this}
+	 * @since 5.6
+	 */
+	public CommitCommand setHookErrorStream(PrintStream hookStdErr) {
+		setHookErrorStream(PreCommitHook.NAME, hookStdErr);
+		setHookErrorStream(CommitMsgHook.NAME, hookStdErr);
+		setHookErrorStream(PostCommitHook.NAME, hookStdErr);
+		return this;
+	}
+
+	/**
 	 * Set the output stream for a selected hook script executed by this command
 	 * (pre-commit, commit-msg, post-commit). If not set it defaults to
 	 * {@code System.out}.
@@ -916,6 +938,30 @@
 	}
 
 	/**
+	 * Set the error stream for a selected hook script executed by this command
+	 * (pre-commit, commit-msg, post-commit). If not set it defaults to
+	 * {@code System.err}.
+	 *
+	 * @param hookName
+	 *            name of the hook to set the output stream for
+	 * @param hookStdErr
+	 *            the output stream to use for the selected hook
+	 * @return {@code this}
+	 * @since 5.6
+	 */
+	public CommitCommand setHookErrorStream(String hookName,
+			PrintStream hookStdErr) {
+		if (!(PreCommitHook.NAME.equals(hookName)
+				|| CommitMsgHook.NAME.equals(hookName)
+				|| PostCommitHook.NAME.equals(hookName))) {
+			throw new IllegalArgumentException(MessageFormat
+					.format(JGitText.get().illegalHookName, hookName));
+		}
+		hookErrRedirect.put(hookName, hookStdErr);
+		return this;
+	}
+
+	/**
 	 * Sets the signing key
 	 * <p>
 	 * Per spec of user.signingKey: this will be sent to the GPG program as is,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
index f65b573..1c3c790 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
@@ -136,28 +136,32 @@
 				}
 				newTree = new DirCacheIterator(repo.readDirCache());
 			} else {
-				if (oldTree == null)
+				if (oldTree == null) {
 					oldTree = new DirCacheIterator(repo.readDirCache());
-				if (newTree == null)
+				}
+				if (newTree == null) {
 					newTree = new FileTreeIterator(repo);
+				}
 			}
 
 			diffFmt.setPathFilter(pathFilter);
 
 			List<DiffEntry> result = diffFmt.scan(oldTree, newTree);
-			if (showNameAndStatusOnly)
-				return result;
-			else {
-				if (contextLines >= 0)
-					diffFmt.setContext(contextLines);
-				if (destinationPrefix != null)
-					diffFmt.setNewPrefix(destinationPrefix);
-				if (sourcePrefix != null)
-					diffFmt.setOldPrefix(sourcePrefix);
-				diffFmt.format(result);
-				diffFmt.flush();
+			if (showNameAndStatusOnly) {
 				return result;
 			}
+			if (contextLines >= 0) {
+				diffFmt.setContext(contextLines);
+			}
+			if (destinationPrefix != null) {
+				diffFmt.setNewPrefix(destinationPrefix);
+			}
+			if (sourcePrefix != null) {
+				diffFmt.setOldPrefix(sourcePrefix);
+			}
+			diffFmt.format(result);
+			diffFmt.flush();
+			return result;
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index 2c9c5f2..9020c58 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -360,17 +360,17 @@
 	 * @return whether to remove refs which no longer exist in the source
 	 */
 	public boolean isRemoveDeletedRefs() {
-		if (removeDeletedRefs != null)
+		if (removeDeletedRefs != null) {
 			return removeDeletedRefs.booleanValue();
-		else { // fall back to configuration
-			boolean result = false;
-			StoredConfig config = repo.getConfig();
-			result = config.getBoolean(ConfigConstants.CONFIG_FETCH_SECTION,
-					null, ConfigConstants.CONFIG_KEY_PRUNE, result);
-			result = config.getBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
-					remote, ConfigConstants.CONFIG_KEY_PRUNE, result);
-			return result;
 		}
+		// fall back to configuration
+		boolean result = false;
+		StoredConfig config = repo.getConfig();
+		result = config.getBoolean(ConfigConstants.CONFIG_FETCH_SECTION, null,
+				ConfigConstants.CONFIG_KEY_PRUNE, result);
+		result = config.getBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
+				remote, ConfigConstants.CONFIG_KEY_PRUNE, result);
+		return result;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
index 7ea2771..474e2f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
@@ -243,9 +243,8 @@
 			if (repo instanceof FileRepository) {
 				GC gc = new GC((FileRepository) repo);
 				return toProperties(gc.getStatistics());
-			} else {
-				return new Properties();
 			}
+			return new Properties();
 		} catch (IOException e) {
 			throw new JGitInternalException(
 					JGitText.get().couldNotGetRepoStatistics, e);
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 66de8ae..217785d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -105,6 +105,7 @@
 	private RevFilter revFilter;
 
 	private final List<PathFilter> pathFilters = new ArrayList<>();
+	private final List<TreeFilter> excludeTreeFilters = new ArrayList<>();
 
 	private int maxCount = -1;
 
@@ -133,9 +134,22 @@
 	@Override
 	public Iterable<RevCommit> call() throws GitAPIException, NoHeadException {
 		checkCallable();
-		if (!pathFilters.isEmpty())
-			walk.setTreeFilter(AndTreeFilter.create(
-					PathFilterGroup.create(pathFilters), TreeFilter.ANY_DIFF));
+		List<TreeFilter> filters = new ArrayList<>();
+		if (!pathFilters.isEmpty()) {
+			filters.add(AndTreeFilter.create(PathFilterGroup.create(pathFilters), TreeFilter.ANY_DIFF));
+		}
+		if (!excludeTreeFilters.isEmpty()) {
+			for (TreeFilter f : excludeTreeFilters) {
+				filters.add(AndTreeFilter.create(f, TreeFilter.ANY_DIFF));
+			}
+		}
+		if (!filters.isEmpty()) {
+			if (filters.size() == 1) {
+				filters.add(TreeFilter.ANY_DIFF);
+			}
+			walk.setTreeFilter(AndTreeFilter.create(filters));
+
+		}
 		if (skip > -1 && maxCount > -1)
 			walk.setRevFilter(AndRevFilter.create(SkipRevFilter.create(skip),
 					MaxCountRevFilter.create(maxCount)));
@@ -310,6 +324,24 @@
 	}
 
 	/**
+	 * Show all commits that are not within any of the specified paths. The path
+	 * must either name a file or a directory exactly and use <code>/</code>
+	 * (slash) as separator. Note that regular expressions or wildcards are not
+	 * yet supported. If a path is both added and excluded from the search, then
+	 * the exclusion wins.
+	 *
+	 * @param path
+	 *            a repository-relative path (with <code>/</code> as separator)
+	 * @return {@code this}
+	 * @since 5.6
+	 */
+	public LogCommand excludePath(String path) {
+		checkCallable();
+		excludeTreeFilters.add(PathFilter.create(path).negate());
+		return this;
+	}
+
+	/**
 	 * Skip the number of commits before starting to show the commit output.
 	 *
 	 * @param skip
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index f9a9baf..9a843f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -409,27 +409,24 @@
 							new ObjectId[] { headCommit.getId(),
 									srcCommit.getId() }, mergeStatus,
 							mergeStrategy, null, msg);
-				} else {
-					if (failingPaths != null) {
-						repo.writeMergeCommitMsg(null);
-						repo.writeMergeHeads(null);
-						return new MergeResult(null, merger.getBaseCommitId(),
-								new ObjectId[] {
-										headCommit.getId(), srcCommit.getId() },
-								MergeStatus.FAILED, mergeStrategy,
-								lowLevelResults, failingPaths, null);
-					} else {
-						String mergeMessageWithConflicts = new MergeMessageFormatter()
-								.formatWithConflicts(mergeMessage,
-										unmergedPaths);
-						repo.writeMergeCommitMsg(mergeMessageWithConflicts);
-						return new MergeResult(null, merger.getBaseCommitId(),
-								new ObjectId[] { headCommit.getId(),
-										srcCommit.getId() },
-								MergeStatus.CONFLICTING, mergeStrategy,
-								lowLevelResults, null);
-					}
 				}
+				if (failingPaths != null) {
+					repo.writeMergeCommitMsg(null);
+					repo.writeMergeHeads(null);
+					return new MergeResult(null, merger.getBaseCommitId(),
+							new ObjectId[] { headCommit.getId(),
+									srcCommit.getId() },
+							MergeStatus.FAILED, mergeStrategy, lowLevelResults,
+							failingPaths, null);
+				}
+				String mergeMessageWithConflicts = new MergeMessageFormatter()
+						.formatWithConflicts(mergeMessage, unmergedPaths);
+				repo.writeMergeCommitMsg(mergeMessageWithConflicts);
+				return new MergeResult(null, merger.getBaseCommitId(),
+						new ObjectId[] { headCommit.getId(),
+								srcCommit.getId() },
+						MergeStatus.CONFLICTING, mergeStrategy, lowLevelResults,
+						null);
 			}
 		} catch (org.eclipse.jgit.errors.CheckoutConflictException e) {
 			List<String> conflicts = (dco == null) ? Collections
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index bdb2d1bb..ea3e2d9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -90,7 +90,7 @@
  */
 public class PullCommand extends TransportCommand<PullCommand, PullResult> {
 
-	private final static String DOT = "."; //$NON-NLS-1$
+	private static final String DOT = "."; //$NON-NLS-1$
 
 	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
 
@@ -315,23 +315,24 @@
 			Ref r = null;
 			if (fetchRes != null) {
 				r = fetchRes.getAdvertisedRef(remoteBranchName);
-				if (r == null)
+				if (r == null) {
 					r = fetchRes.getAdvertisedRef(Constants.R_HEADS
 							+ remoteBranchName);
+				}
 			}
 			if (r == null) {
 				throw new RefNotAdvertisedException(MessageFormat.format(
 						JGitText.get().couldNotGetAdvertisedRef, remote,
 						remoteBranchName));
-			} else {
-				commitToMerge = r.getObjectId();
 			}
+			commitToMerge = r.getObjectId();
 		} else {
 			try {
 				commitToMerge = repo.resolve(remoteBranchName);
-				if (commitToMerge == null)
+				if (commitToMerge == null) {
 					throw new RefNotFoundException(MessageFormat.format(
 							JGitText.get().refNotResolved, remoteBranchName));
+				}
 			} catch (IOException e) {
 				throw new JGitInternalException(
 						JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
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 0dacd4d..6eed0b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -509,10 +509,10 @@
 			monitor.beginTask(MessageFormat.format(
 					JGitText.get().applyingCommit,
 					commitToPick.getShortMessage()), ProgressMonitor.UNKNOWN);
-			if (preserveMerges)
+			if (preserveMerges) {
 				return cherryPickCommitPreservingMerges(commitToPick);
-			else
-				return cherryPickCommitFlattening(commitToPick);
+			}
+			return cherryPickCommitFlattening(commitToPick);
 		} finally {
 			monitor.endTask();
 		}
@@ -539,11 +539,11 @@
 					.call();
 				switch (cherryPickResult.getStatus()) {
 				case FAILED:
-					if (operation == Operation.BEGIN)
+					if (operation == Operation.BEGIN) {
 						return abort(RebaseResult
 								.failed(cherryPickResult.getFailingPaths()));
-					else
-						return stop(commitToPick, Status.STOPPED);
+					}
+					return stop(commitToPick, Status.STOPPED);
 				case CONFLICTING:
 					return stop(commitToPick, Status.STOPPED);
 				case OK:
@@ -599,11 +599,11 @@
 					CherryPickResult cherryPickResult = pickCommand.call();
 					switch (cherryPickResult.getStatus()) {
 					case FAILED:
-						if (operation == Operation.BEGIN)
+						if (operation == Operation.BEGIN) {
 							return abort(RebaseResult.failed(
 									cherryPickResult.getFailingPaths()));
-						else
-							return stop(commitToPick, Status.STOPPED);
+						}
+						return stop(commitToPick, Status.STOPPED);
 					case CONFLICTING:
 						return stop(commitToPick, Status.STOPPED);
 					case OK:
@@ -833,7 +833,8 @@
 		sb.append("# This is a combination of ").append(count)
 				.append(" commits.\n");
 		// Add the previous message without header (i.e first line)
-		sb.append(currSquashMessage.substring(currSquashMessage.indexOf("\n") + 1));
+		sb.append(currSquashMessage
+				.substring(currSquashMessage.indexOf('\n') + 1));
 		sb.append("\n");
 		if (isSquash) {
 			sb.append("# This is the ").append(count).append(ordinal)
@@ -871,7 +872,7 @@
 	static int parseSquashFixupSequenceCount(String currSquashMessage) {
 		String regex = "This is a combination of (.*) commits"; //$NON-NLS-1$
 		String firstLine = currSquashMessage.substring(0,
-				currSquashMessage.indexOf("\n")); //$NON-NLS-1$
+				currSquashMessage.indexOf('\n'));
 		Pattern pattern = Pattern.compile(regex);
 		Matcher matcher = pattern.matcher(firstLine);
 		if (!matcher.find())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
index d7c9ad5..3031a19 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
@@ -334,10 +334,10 @@
 	}
 
 	private String getRefOrHEAD() {
-		if (ref != null)
+		if (ref != null) {
 			return ref;
-		else
-			return Constants.HEAD;
+		}
+		return Constants.HEAD;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index ddd60b6..aa0055f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -58,6 +58,7 @@
 import org.eclipse.jgit.api.errors.UnmergedPathsException;
 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
@@ -175,6 +176,10 @@
 						+ "This reverts commit " + srcCommit.getId().getName() //$NON-NLS-1$
 						+ ".\n"; //$NON-NLS-1$
 				if (merger.merge(headCommit, srcParent)) {
+					if (!merger.getModifiedFiles().isEmpty()) {
+						repo.fireEvent(new WorkingTreeModifiedEvent(
+								merger.getModifiedFiles(), null));
+					}
 					if (AnyObjectId.isEqual(headCommit.getTree().getId(),
 							merger.getResultTreeId()))
 						continue;
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 5239369..74def9e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
@@ -106,10 +106,10 @@
 	 */
 	protected String getHeadBranch(Repository subRepo) throws IOException {
 		Ref head = subRepo.exactRef(Constants.HEAD);
-		if (head != null && head.isSymbolic())
+		if (head != null && head.isSymbolic()) {
 			return Repository.shortenRefName(head.getLeaf().getName());
-		else
-			return null;
+		}
+		return null;
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java
index db6440b..30a2d62 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java
@@ -67,19 +67,26 @@
 	private final int returnCode;
 
 	/**
+	 * The stderr output of the hook.
+	 */
+	private final String hookStdErr;
+
+	/**
 	 * Constructor for AbortedByHookException
 	 *
-	 * @param message
-	 *            The error details.
+	 * @param hookStdErr
+	 *            The error details from the stderr output of the hook
 	 * @param hookName
 	 *            The name of the hook that interrupted the command, must not be
 	 *            null.
 	 * @param returnCode
 	 *            The return code of the hook process that has been run.
 	 */
-	public AbortedByHookException(String message, String hookName,
+	public AbortedByHookException(String hookStdErr, String hookName,
 			int returnCode) {
-		super(message);
+		super(MessageFormat.format(JGitText.get().commandRejectedByHook,
+				hookName, hookStdErr));
+		this.hookStdErr = hookStdErr;
 		this.hookName = hookName;
 		this.returnCode = returnCode;
 	}
@@ -102,10 +109,13 @@
 		return returnCode;
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public String getMessage() {
-		return MessageFormat.format(JGitText.get().commandRejectedByHook,
-				hookName, super.getMessage());
+	/**
+	 * Get the stderr output of the hook.
+	 *
+	 * @return A string containing the complete stderr output of the hook.
+	 * @since 5.6
+	 */
+	public String getHookStdErr() {
+		return hookStdErr;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
index f1df0da..b732050 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
@@ -67,7 +67,7 @@
 	 * The attribute value state
 	 * see also https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
 	 */
-	public static enum State {
+	public enum State {
 		/** the attribute is set */
 		SET,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
index a91d8c2..ead99c7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
@@ -91,7 +91,7 @@
 				continue;
 			}
 
-			final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$
+			final int equalsIndex = attribute.indexOf('=');
 			if (equalsIndex == -1)
 				result.add(new Attribute(attribute, State.SET));
 			else {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
index 9cec645..d0aa292 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2011, 2019 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -48,11 +48,17 @@
 import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 
 import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.NoHeadException;
 import org.eclipse.jgit.blame.Candidate.BlobCandidate;
+import org.eclipse.jgit.blame.Candidate.HeadCandidate;
 import org.eclipse.jgit.blame.Candidate.ReverseCandidate;
 import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit;
 import org.eclipse.jgit.diff.DiffAlgorithm;
@@ -63,8 +69,13 @@
 import org.eclipse.jgit.diff.RawText;
 import org.eclipse.jgit.diff.RawTextComparator;
 import org.eclipse.jgit.diff.RenameDetector;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.MutableObjectId;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
@@ -74,9 +85,12 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.IO;
 
 /**
  * Generate author information for lines based on a provided file.
@@ -313,6 +327,107 @@
 	}
 
 	/**
+	 * Pushes HEAD, index, and working tree as appropriate for blaming the file
+	 * given in the constructor {@link #BlameGenerator(Repository, String)}
+	 * against HEAD. Includes special handling in case the file is in conflict
+	 * state from an unresolved merge conflict.
+	 *
+	 * @return {@code this}
+	 * @throws NoHeadException
+	 *             if the repository has no HEAD
+	 * @throws IOException
+	 *             if an error occurs
+	 * @since 5.6
+	 */
+	public BlameGenerator prepareHead() throws NoHeadException, IOException {
+		Repository repo = getRepository();
+		ObjectId head = repo.resolve(Constants.HEAD);
+		if (head == null) {
+			throw new NoHeadException(MessageFormat
+					.format(JGitText.get().noSuchRefKnown, Constants.HEAD));
+		}
+		if (repo.isBare()) {
+			return push(null, head);
+		}
+		DirCache dc = repo.readDirCache();
+		try (TreeWalk walk = new TreeWalk(repo)) {
+			walk.setOperationType(OperationType.CHECKIN_OP);
+			FileTreeIterator iter = new FileTreeIterator(repo);
+			int fileTree = walk.addTree(iter);
+			int indexTree = walk.addTree(new DirCacheIterator(dc));
+			iter.setDirCacheIterator(walk, indexTree);
+			walk.setFilter(resultPath);
+			walk.setRecursive(true);
+			if (!walk.next()) {
+				return this;
+			}
+			DirCacheIterator dcIter = walk.getTree(indexTree,
+					DirCacheIterator.class);
+			if (dcIter == null) {
+				// Not found in index
+				return this;
+			}
+			iter = walk.getTree(fileTree, FileTreeIterator.class);
+			if (iter == null || !isFile(iter.getEntryRawMode())) {
+				return this;
+			}
+			RawText inTree;
+			long filteredLength = iter.getEntryContentLength();
+			try (InputStream stream = iter.openEntryStream()) {
+				inTree = new RawText(getBytes(iter.getEntryFile().getPath(),
+						stream, filteredLength));
+			}
+			DirCacheEntry indexEntry = dcIter.getDirCacheEntry();
+			if (indexEntry.getStage() == DirCacheEntry.STAGE_0) {
+				push(null, head);
+				push(null, indexEntry.getObjectId());
+				push(null, inTree);
+			} else {
+				// Create a special candidate using the working tree file as
+				// blob and HEAD and the MERGE_HEADs as parents.
+				HeadCandidate c = new HeadCandidate(getRepository(), resultPath,
+						getHeads(repo, head));
+				c.sourceText = inTree;
+				c.regionList = new Region(0, 0, inTree.size());
+				remaining = inTree.size();
+				push(c);
+			}
+		}
+		return this;
+	}
+
+	private List<RevCommit> getHeads(Repository repo, ObjectId head)
+			throws NoWorkTreeException, IOException {
+		List<ObjectId> mergeIds = repo.readMergeHeads();
+		if (mergeIds == null || mergeIds.isEmpty()) {
+			return Collections.singletonList(revPool.parseCommit(head));
+		}
+		List<RevCommit> heads = new ArrayList<>(mergeIds.size() + 1);
+		heads.add(revPool.parseCommit(head));
+		for (ObjectId id : mergeIds) {
+			heads.add(revPool.parseCommit(id));
+		}
+		return heads;
+	}
+
+	private static byte[] getBytes(String path, InputStream in, long maxLength)
+			throws IOException {
+		if (maxLength > Integer.MAX_VALUE) {
+			throw new IOException(
+					MessageFormat.format(JGitText.get().fileIsTooLarge, path));
+		}
+		int max = (int) maxLength;
+		byte[] buffer = new byte[max];
+		int read = IO.readFully(in, buffer, 0);
+		if (read == max) {
+			return buffer;
+		}
+		byte[] copy = new byte[read];
+		System.arraycopy(buffer, 0, copy, 0, read);
+		return copy;
+	}
+
+	/**
 	 * Push a candidate object onto the generator's traversal stack.
 	 * <p>
 	 * Candidates should be pushed in history order from oldest-to-newest.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
index 5fb7750..394aba6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
@@ -267,18 +267,18 @@
 	 */
 	public int computeNext() throws IOException {
 		BlameGenerator gen = generator;
-		if (gen == null)
+		if (gen == null) {
 			return -1;
+		}
 
 		if (gen.next()) {
 			loadFrom(gen);
 			lastLength = gen.getRegionLength();
 			return gen.getResultStart();
-		} else {
-			gen.close();
-			generator = null;
-			return -1;
 		}
+		gen.close();
+		generator = null;
+		return -1;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java
index 457d1d2..3ef4943 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2011, 2019 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -44,12 +44,14 @@
 package org.eclipse.jgit.blame;
 
 import java.io.IOException;
+import java.util.List;
 
 import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit;
 import org.eclipse.jgit.diff.Edit;
 import org.eclipse.jgit.diff.EditList;
 import org.eclipse.jgit.diff.RawText;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
@@ -392,6 +394,66 @@
 	}
 
 	/**
+	 * A {@link Candidate} to blame a working tree file in conflict state.
+	 * <p>
+	 * Contrary to {@link BlobCandidate}, it expects to be given the parent
+	 * commits (typically HEAD and the MERGE_HEADs) and behaves like a merge
+	 * commit during blame. It does <em>not</em> consider a previously pushed
+	 * Candidate as its parent.
+	 * </p>
+	 */
+	static final class HeadCandidate extends Candidate {
+
+		private List<RevCommit> parents;
+
+		HeadCandidate(Repository repo, PathFilter path,
+				List<RevCommit> parents) {
+			super(repo, null, path);
+			this.parents = parents;
+		}
+
+		@Override
+		void beginResult(RevWalk rw) {
+			// Blob candidates have nothing to prepare.
+		}
+
+		@Override
+		int getParentCount() {
+			return parents.size();
+		}
+
+		@Override
+		RevCommit getParent(int idx) {
+			return parents.get(idx);
+		}
+
+		@Override
+		boolean has(RevFlag flag) {
+			return true; // Pretend flag was added; sourceCommit is null.
+		}
+
+		@Override
+		void add(RevFlag flag) {
+			// Do nothing, sourceCommit is null.
+		}
+
+		@Override
+		void remove(RevFlag flag) {
+			// Do nothing, sourceCommit is null.
+		}
+
+		@Override
+		int getTime() {
+			return Integer.MAX_VALUE;
+		}
+
+		@Override
+		PersonIdent getAuthor() {
+			return new PersonIdent(JGitText.get().blameNotCommittedYet, ""); //$NON-NLS-1$
+		}
+	}
+
+	/**
 	 * Candidate loaded from a file source, and not a commit.
 	 * <p>
 	 * The {@link Candidate#sourceCommit} field is always null on this type of
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
index ca37a10..9071aeb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
@@ -59,7 +59,7 @@
 	public static final Config.SectionParser<DiffConfig> KEY = DiffConfig::new;
 
 	/** Permissible values for {@code diff.renames}. */
-	public static enum RenameDetectionType {
+	public enum RenameDetectionType {
 		/** Rename detection is disabled. */
 		FALSE,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
index 5c8343f..9f660ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
@@ -72,7 +72,7 @@
 	public static final String DEV_NULL = "/dev/null"; //$NON-NLS-1$
 
 	/** General type of change a single file-level patch describes. */
-	public static enum ChangeType {
+	public enum ChangeType {
 		/** Add a new file to the project */
 		ADD,
 
@@ -90,7 +90,7 @@
 	}
 
 	/** Specify the old or new side for more generalized access. */
-	public static enum Side {
+	public enum Side {
 		/** The old side of a DiffEntry. */
 		OLD,
 
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 1cecff6..d764499 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -145,6 +145,8 @@
 
 	private Repository repository;
 
+	private Boolean quotePaths;
+
 	/**
 	 * Create a new formatter with a default level of context.
 	 *
@@ -199,6 +201,11 @@
 		this.closeReader = closeReader;
 		this.reader = reader;
 		this.diffCfg = cfg.get(DiffConfig.KEY);
+		if (quotePaths == null) {
+			quotePaths = Boolean
+					.valueOf(cfg.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+							ConfigConstants.CONFIG_KEY_QUOTE_PATH, true));
+		}
 
 		ContentSource cs = ContentSource.create(reader);
 		source = new ContentSource.Pair(cs, cs);
@@ -379,6 +386,21 @@
 	}
 
 	/**
+	 * Sets whether or not path names should be quoted.
+	 * <p>
+	 * By default the setting of git config {@code core.quotePath} is active,
+	 * but this can be overridden through this method.
+	 * </p>
+	 *
+	 * @param quote
+	 *            whether to quote path names
+	 * @since 5.6
+	 */
+	public void setQuotePaths(boolean quote) {
+		quotePaths = Boolean.valueOf(quote);
+	}
+
+	/**
 	 * Set the filter to produce only specific paths.
 	 *
 	 * If the filter is an instance of
@@ -489,8 +511,8 @@
 			CanonicalTreeParser parser = new CanonicalTreeParser();
 			parser.reset(reader, tree);
 			return parser;
-		} else
-			return new EmptyTreeIterator();
+		}
+		return new EmptyTreeIterator();
 	}
 
 	/**
@@ -726,8 +748,11 @@
 		return id.name();
 	}
 
-	private static String quotePath(String name) {
-		return QuotedString.GIT_PATH.quote(name);
+	private String quotePath(String path) {
+		if (quotePaths == null || quotePaths.booleanValue()) {
+			return QuotedString.GIT_PATH.quote(path);
+		}
+		return QuotedString.GIT_PATH_MINIMAL.quote(path);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
index 5c876e8..b4e0963 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
@@ -64,7 +64,7 @@
  */
 public class Edit {
 	/** Type of edit */
-	public static enum Type {
+	public enum Type {
 		/** Sequence B has inserted the region. */
 		INSERT,
 
@@ -125,17 +125,17 @@
 	 */
 	public final Type getType() {
 		if (beginA < endA) {
-			if (beginB < endB)
+			if (beginB < endB) {
 				return Type.REPLACE;
-			else /* if (beginB == endB) */
-				return Type.DELETE;
+			}
+			return Type.DELETE;
 
-		} else /* if (beginA == endA) */{
-			if (beginB < endB)
-				return Type.INSERT;
-			else /* if (beginB == endB) */
-				return Type.EMPTY;
 		}
+		if (beginB < endB) {
+			return Type.INSERT;
+		}
+		// beginB == endB)
+		return Type.EMPTY;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
index 6c0d90e..4e79fc9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
@@ -383,15 +383,17 @@
 	 * @return the line delimiter or <code>null</code>
 	 */
 	public String getLineDelimiter() {
-		if (size() == 0)
+		if (size() == 0) {
 			return null;
+		}
 		int e = getEnd(0);
-		if (content[e - 1] != '\n')
+		if (content[e - 1] != '\n') {
 			return null;
-		if (content.length > 1 && e > 1 && content[e - 2] == '\r')
+		}
+		if (content.length > 1 && e > 1 && content[e - 2] == '\r') {
 			return "\r\n"; //$NON-NLS-1$
-		else
-			return "\n"; //$NON-NLS-1$
+		}
+		return "\n"; //$NON-NLS-1$
 	}
 
 	/**
@@ -446,7 +448,7 @@
 				}
 			}
 
-			byte data[];
+			byte[] data;
 			try {
 				data = new byte[(int)sz];
 			} catch (OutOfMemoryError e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
index d8a05c3..1b7babc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
@@ -335,14 +335,14 @@
 	}
 
 	static int nameScore(String a, String b) {
-	    int aDirLen = a.lastIndexOf("/") + 1; //$NON-NLS-1$
-	    int bDirLen = b.lastIndexOf("/") + 1; //$NON-NLS-1$
+		int aDirLen = a.lastIndexOf('/') + 1;
+		int bDirLen = b.lastIndexOf('/') + 1;
 
-	    int dirMin = Math.min(aDirLen, bDirLen);
-	    int dirMax = Math.max(aDirLen, bDirLen);
+		int dirMin = Math.min(aDirLen, bDirLen);
+		int dirMax = Math.max(aDirLen, bDirLen);
 
-	    final int dirScoreLtr;
-	    final int dirScoreRtl;
+		final int dirScoreLtr;
+		final int dirScoreRtl;
 
 		if (dirMax == 0) {
 			dirScoreLtr = 100;
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 6bc2946..3502d7a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -53,9 +53,11 @@
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.eclipse.jgit.api.errors.CanceledException;
 import org.eclipse.jgit.api.errors.FilterFailedException;
@@ -104,7 +106,8 @@
  * This class handles checking out one or two trees merging with the index.
  */
 public class DirCacheCheckout {
-	private static Logger LOG = LoggerFactory.getLogger(DirCacheCheckout.class);
+	private static final Logger LOG = LoggerFactory
+			.getLogger(DirCacheCheckout.class);
 
 	private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
 
@@ -142,6 +145,8 @@
 
 	private ArrayList<String> removed = new ArrayList<>();
 
+	private ArrayList<String> kept = new ArrayList<>();
+
 	private ObjectId mergeCommitTree;
 
 	private DirCache dc;
@@ -432,11 +437,11 @@
 					if (mtime == null || mtime.equals(Instant.EPOCH)) {
 						entry.setLastModified(f.getEntryLastModifiedInstant());
 					}
-					keep(entry, f);
+					keep(i.getEntryPathString(), entry, f);
 				}
 			} else
 				// The index contains a folder
-				keep(i.getDirCacheEntry(), f);
+				keep(i.getEntryPathString(), i.getDirCacheEntry(), f);
 		} else {
 			// There is no entry in the merge commit. Means: we want to delete
 			// what's currently in the index and working tree
@@ -496,8 +501,11 @@
 				dc.unlock();
 			} finally {
 				if (performingCheckout) {
+					Set<String> touched = new HashSet<>(conflicts);
+					touched.addAll(getUpdated().keySet());
+					touched.addAll(kept);
 					WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
-							getUpdated().keySet(), getRemoved());
+							touched, getRemoved());
 					if (!event.isEmpty()) {
 						repo.fireEvent(event);
 					}
@@ -517,10 +525,10 @@
 				prescanOneTree();
 
 			if (!conflicts.isEmpty()) {
-				if (failOnConflict)
+				if (failOnConflict) {
 					throw new CheckoutConflictException(conflicts.toArray(new String[0]));
-				else
-					cleanUpConflicts();
+				}
+				cleanUpConflicts();
 			}
 
 			// update our index
@@ -826,14 +834,14 @@
 
 				break;
 			case 0xDFD: // 3 4
-				keep(dce, f);
+				keep(name, dce, f);
 				break;
 			case 0xF0D: // 18
 				remove(name);
 				break;
 			case 0xDFF: // 5 5b 6 6b
 				if (equalIdAndMode(iId, iMode, mId, mMode))
-					keep(dce, f); // 5 6
+					keep(name, dce, f); // 5 6
 				else
 					conflict(name, dce, h, m); // 5b 6b
 				break;
@@ -863,7 +871,7 @@
 					conflict(name, dce, h, m); // 9
 				break;
 			case 0xFD0: // keep without a rule
-				keep(dce, f);
+				keep(name, dce, f);
 				break;
 			case 0xFFD: // 12 13 14
 				if (equalIdAndMode(hId, hMode, iId, iMode))
@@ -883,7 +891,7 @@
 					conflict(name, dce, h, m);
 				break;
 			default:
-				keep(dce, f);
+				keep(name, dce, f);
 			}
 			return;
 		}
@@ -895,15 +903,14 @@
 				// the workingtree entry doesn't exist or also contains a folder
 				// -> no problem
 				return;
-			} else {
-				// the workingtree entry exists and is not a folder
-				if (!idEqual(h, m)) {
-					// Because HEAD and MERGE differ we will try to update the
-					// workingtree with a folder -> return a conflict
-					conflict(name, null, null, null);
-				}
-				return;
 			}
+			// the workingtree entry exists and is not a folder
+			if (!idEqual(h, m)) {
+				// Because HEAD and MERGE differ we will try to update the
+				// workingtree with a folder -> return a conflict
+				conflict(name, null, null, null);
+			}
+			return;
 		}
 
 		if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
@@ -969,7 +976,7 @@
 					if (initialCheckout)
 						update(name, mId, mMode);
 					else
-						keep(dce, f);
+						keep(name, dce, f);
 				} else
 					conflict(name, dce, h, m);
 			}
@@ -1032,7 +1039,7 @@
 						// Nothing in Head
 						// Something in Index
 						// -> Merge contains nothing new. Keep the index.
-						keep(dce, f);
+						keep(name, dce, f);
 				} else
 					// Merge contains something and it is not the same as Index
 					// Nothing in Head
@@ -1083,15 +1090,15 @@
 							// Something in Head
 
 							if (!FileMode.TREE.equals(f.getEntryFileMode())
-									&& FileMode.TREE.equals(iMode))
+									&& FileMode.TREE.equals(iMode)) {
 								// The workingtree contains a file and the index semantically contains a folder.
 								// Git considers the workingtree file as untracked. Just keep the untracked file.
 								return;
-							else
-								// -> file is dirty and tracked but is should be
-								// removed. That's a conflict
-								conflict(name, dce, h, m);
-						} else
+							}
+							// -> file is dirty and tracked but is should be
+							// removed. That's a conflict
+							conflict(name, dce, h, m);
+						} else {
 							// file doesn't exist or is clean
 							// Index contains the same as Head
 							// Something different from a submodule in Index
@@ -1099,7 +1106,8 @@
 							// Something in Head
 							// -> Remove from index and delete the file
 							remove(name);
-					} else
+						}
+					} else {
 						// Index contains something different from Head
 						// Something different from a submodule in Index
 						// Nothing in Merge
@@ -1108,6 +1116,7 @@
 						// filesystem). But Merge wants the path to be removed.
 						// Report a conflict
 						conflict(name, dce, h, m);
+					}
 				}
 			} else {
 				// Something in Merge
@@ -1181,7 +1190,7 @@
 					// to the other one.
 					// -> In all three cases we don't touch index and file.
 
-					keep(dce, f);
+					keep(name, dce, f);
 				}
 			}
 		}
@@ -1230,13 +1239,17 @@
 		}
 	}
 
-	private void keep(DirCacheEntry e, WorkingTreeIterator f)
+	private void keep(String path, DirCacheEntry e, WorkingTreeIterator f)
 			throws IOException {
 		if (e != null && !FileMode.TREE.equals(e.getFileMode()))
 			builder.add(e);
 		if (force) {
-			if (f.isModified(e, true, this.walk.getObjectReader())) {
-				checkoutEntry(repo, e, this.walk.getObjectReader());
+			if (f.isModified(e, true, walk.getObjectReader())) {
+				kept.add(path);
+				checkoutEntry(repo, e, walk.getObjectReader(), false,
+						new CheckoutMetadata(walk.getEolStreamType(CHECKOUT_OP),
+								walk.getFilterCommand(
+										Constants.ATTR_FILTER_TYPE_SMUDGE)));
 			}
 		}
 	}
@@ -1340,13 +1353,14 @@
 	private boolean isModified_IndexTree(String path, ObjectId iId,
 			FileMode iMode, ObjectId tId, FileMode tMode, ObjectId rootTree)
 			throws CorruptObjectException, IOException {
-		if (iMode != tMode)
+		if (iMode != tMode) {
 			return true;
+		}
 		if (FileMode.TREE.equals(iMode)
-				&& (iId == null || ObjectId.zeroId().equals(iId)))
+				&& (iId == null || ObjectId.zeroId().equals(iId))) {
 			return isModifiedSubtree_IndexTree(path, rootTree);
-		else
-			return !equalIdAndMode(iId, iMode, tId, tMode);
+		}
+		return !equalIdAndMode(iId, iMode, tId, tMode);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index 0e91f0d..cbf96e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -824,10 +824,10 @@
 	}
 
 	private int getExtendedFlags() {
-		if (isExtended())
+		if (isExtended()) {
 			return NB.decodeUInt16(info, infoOffset + P_FLAGS2) << 16;
-		else
-			return 0;
+		}
+		return 0;
 	}
 
 	private static void checkPath(byte[] path) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoMergeBaseException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoMergeBaseException.java
index 97214b0..023dfe9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoMergeBaseException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoMergeBaseException.java
@@ -63,7 +63,7 @@
 	 * An enum listing the different reason why no merge base could be
 	 * determined.
 	 */
-	public static enum MergeBaseFailureReason {
+	public enum MergeBaseFailureReason {
 		/**
 		 * Multiple merge bases have been found (e.g. the commits to be merged
 		 * have multiple common predecessors) but the merge strategy doesn't
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TranslationBundleException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/TranslationBundleException.java
index 70760f3..6d18c7c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TranslationBundleException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/TranslationBundleException.java
@@ -78,7 +78,7 @@
 	 *
 	 * @return bundle class for which the exception occurred
 	 */
-	final public Class getBundleClass() {
+	public final Class getBundleClass() {
 		return bundleClass;
 	}
 
@@ -87,7 +87,7 @@
 	 *
 	 * @return locale for which the exception occurred
 	 */
-	final public Locale getLocale() {
+	public final Locale getLocale() {
 		return locale;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java
index 60669bb..2c93d55 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java
@@ -82,10 +82,10 @@
 	/** {@inheritDoc} */
 	@Override
 	public List<Head> getNextHeads(char c) {
-		if (matches(c))
+		if (matches(c)) {
 			return newHeads;
-		else
-			return FileNameMatcher.EMPTY_HEAD_LIST;
+		}
+		return FileNameMatcher.EMPTY_HEAD_LIST;
 	}
 
 	boolean isStar() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
index bfcc580..8125c35 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
@@ -304,11 +304,11 @@
 
 	private static AbstractHead createWildCardHead(
 			final Character invalidWildgetCharacter, final boolean star) {
-		if (invalidWildgetCharacter != null)
+		if (invalidWildgetCharacter != null) {
 			return new RestrictedWildCardHead(invalidWildgetCharacter
 					.charValue(), star);
-		else
-			return new WildCardHead(star);
+		}
+		return new WildCardHead(star);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 8e46341..febdb92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -293,13 +293,12 @@
 			String revision = defaultRevision;
 			if (remote == null) {
 				if (defaultRemote == null) {
-					if (filename != null)
+					if (filename != null) {
 						throw new SAXException(MessageFormat.format(
 								RepoText.get().errorNoDefaultFilename,
 								filename));
-					else
-						throw new SAXException(
-								RepoText.get().errorNoDefault);
+					}
+					throw new SAXException(RepoText.get().errorNoDefault);
 				}
 				remote = defaultRemote;
 			} else {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index cb62925..7288678 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -761,18 +761,17 @@
 			} catch (GitAPIException | IOException e) {
 				throw new ManifestErrorException(e);
 			}
-		} else {
-			try (Git git = new Git(repo)) {
-				for (RepoProject proj : filteredProjects) {
-					addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(),
-							proj.getRevision(), proj.getCopyFiles(),
-							proj.getLinkFiles(), git);
-				}
-				return git.commit().setMessage(RepoText.get().repoCommitMessage)
-						.call();
-			} catch (GitAPIException | IOException e) {
-				throw new ManifestErrorException(e);
+		}
+		try (Git git = new Git(repo)) {
+			for (RepoProject proj : filteredProjects) {
+				addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(),
+						proj.getRevision(), proj.getCopyFiles(),
+						proj.getLinkFiles(), git);
 			}
+			return git.commit().setMessage(RepoText.get().repoCommitMessage)
+					.call();
+		} catch (GitAPIException | IOException e) {
+			throw new ManifestErrorException(e);
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index d79dfa8..684d1e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -422,10 +422,10 @@
 	}
 
 	private String getPathWithSlash() {
-		if (path.endsWith("/")) //$NON-NLS-1$
+		if (path.endsWith("/")) { //$NON-NLS-1$
 			return path;
-		else
-			return path + "/"; //$NON-NLS-1$
+		}
+		return path + "/"; //$NON-NLS-1$
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/CommitMsgHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/CommitMsgHook.java
index f33168d..6dbe0a6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/CommitMsgHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/CommitMsgHook.java
@@ -72,6 +72,9 @@
 
 	/**
 	 * Constructor for CommitMsgHook
+	 * <p>
+	 * This constructor will use the default error stream.
+	 * </p>
 	 *
 	 * @param repo
 	 *            The repository
@@ -83,6 +86,24 @@
 		super(repo, outputStream);
 	}
 
+	/**
+	 * Constructor for CommitMsgHook
+	 *
+	 * @param repo
+	 *            The repository
+	 * @param outputStream
+	 *            The output stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.out}.
+	 * @param errorStream
+	 *            The error stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.err}.
+	 * @since 5.6
+	 */
+	protected CommitMsgHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		super(repo, outputStream, errorStream);
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public String call() throws IOException, AbortedByHookException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index ad43e2c..aa307c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -50,6 +50,7 @@
 import java.io.UnsupportedEncodingException;
 import java.util.concurrent.Callable;
 
+import org.bouncycastle.util.io.TeeOutputStream;
 import org.eclipse.jgit.api.errors.AbortedByHookException;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.FS;
@@ -79,7 +80,15 @@
 	protected final PrintStream outputStream;
 
 	/**
-	 * Constructor for GitHook
+	 * The error stream to be used by the hook.
+	 */
+	protected final PrintStream errorStream;
+
+	/**
+	 * Constructor for GitHook.
+	 * <p>
+	 * This constructor will use stderr for the error stream.
+	 * </p>
 	 *
 	 * @param repo
 	 *            a {@link org.eclipse.jgit.lib.Repository} object.
@@ -88,8 +97,26 @@
 	 *            in which case the hook will use {@code System.out}.
 	 */
 	protected GitHook(Repository repo, PrintStream outputStream) {
+		this(repo, outputStream, null);
+	}
+
+	/**
+	 * Constructor for GitHook
+	 *
+	 * @param repo
+	 *            a {@link org.eclipse.jgit.lib.Repository} object.
+	 * @param outputStream
+	 *            The output stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.out}.
+	 * @param errorStream
+	 *            The error stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.err}.
+	 */
+	protected GitHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
 		this.repo = repo;
 		this.outputStream = outputStream;
+		this.errorStream = errorStream;
 	}
 
 	/**
@@ -148,6 +175,16 @@
 	}
 
 	/**
+	 * Get error stream
+	 *
+	 * @return The error stream the hook must use. Never {@code null},
+	 *         {@code System.err} is returned by default.
+	 */
+	protected PrintStream getErrorStream() {
+		return errorStream == null ? System.err : errorStream;
+	}
+
+	/**
 	 * Runs the hook, without performing any validity checks.
 	 *
 	 * @throws org.eclipse.jgit.api.errors.AbortedByHookException
@@ -155,16 +192,23 @@
 	 */
 	protected void doRun() throws AbortedByHookException {
 		final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
+		final TeeOutputStream stderrStream = new TeeOutputStream(errorByteArray,
+				getErrorStream());
 		PrintStream hookErrRedirect = null;
 		try {
-			hookErrRedirect = new PrintStream(errorByteArray, false,
+			hookErrRedirect = new PrintStream(stderrStream, false,
 					UTF_8.name());
 		} catch (UnsupportedEncodingException e) {
 			// UTF-8 is guaranteed to be available
 		}
-		ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(),
-				getHookName(), getParameters(), getOutputStream(),
-				hookErrRedirect, getStdinArgs());
+		Repository repository = getRepository();
+		FS fs = repository.getFS();
+		if (fs == null) {
+			fs = FS.DETECTED;
+		}
+		ProcessResult result = fs.runHookIfPresent(repository, getHookName(),
+				getParameters(), getOutputStream(), hookErrRedirect,
+				getStdinArgs());
 		if (result.isExecutedWithError()) {
 			throw new AbortedByHookException(
 					new String(errorByteArray.toByteArray(), UTF_8),
@@ -180,7 +224,11 @@
 	 * @since 4.11
 	 */
 	public boolean isNativeHookPresent() {
-		return FS.DETECTED.findHook(getRepository(), getHookName()) != null;
+		FS fs = getRepository().getFS();
+		if (fs == null) {
+			fs = FS.DETECTED;
+		}
+		return fs.findHook(getRepository(), getHookName()) != null;
 	}
 
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
index b801d68..f29dcd1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
@@ -57,7 +57,8 @@
 public class Hooks {
 
 	/**
-	 * Create pre-commit hook for the given repository
+	 * Create pre-commit hook for the given repository with the default error
+	 * stream
 	 *
 	 * @param repo
 	 *            a {@link org.eclipse.jgit.lib.Repository} object.
@@ -71,7 +72,25 @@
 	}
 
 	/**
-	 * Create post-commit hook for the given repository
+	 * Create pre-commit hook for the given repository
+	 *
+	 * @param repo
+	 *            a {@link org.eclipse.jgit.lib.Repository} object.
+	 * @param outputStream
+	 *            The output stream, or {@code null} to use {@code System.out}
+	 * @param errorStream
+	 *            The error stream, or {@code null} to use {@code System.err}
+	 * @return The pre-commit hook for the given repository.
+	 * @since 5.6
+	 */
+	public static PreCommitHook preCommit(Repository repo,
+			PrintStream outputStream, PrintStream errorStream) {
+		return new PreCommitHook(repo, outputStream, errorStream);
+	}
+
+	/**
+	 * Create post-commit hook for the given repository with the default error
+	 * stream
 	 *
 	 * @param repo
 	 *            a {@link org.eclipse.jgit.lib.Repository} object.
@@ -86,7 +105,25 @@
 	}
 
 	/**
-	 * Create commit-msg hook for the given repository
+	 * Create post-commit hook for the given repository
+	 *
+	 * @param repo
+	 *            a {@link org.eclipse.jgit.lib.Repository} object.
+	 * @param outputStream
+	 *            The output stream, or {@code null} to use {@code System.out}
+	 * @param errorStream
+	 *            The error stream, or {@code null} to use {@code System.err}
+	 * @return The pre-commit hook for the given repository.
+	 * @since 5.6
+	 */
+	public static PostCommitHook postCommit(Repository repo,
+			PrintStream outputStream, PrintStream errorStream) {
+		return new PostCommitHook(repo, outputStream, errorStream);
+	}
+
+	/**
+	 * Create commit-msg hook for the given repository with the default error
+	 * stream
 	 *
 	 * @param repo
 	 *            a {@link org.eclipse.jgit.lib.Repository} object.
@@ -100,7 +137,25 @@
 	}
 
 	/**
-	 * Create pre-push hook for the given repository
+	 * Create commit-msg hook for the given repository
+	 *
+	 * @param repo
+	 *            a {@link org.eclipse.jgit.lib.Repository} object.
+	 * @param outputStream
+	 *            The output stream, or {@code null} to use {@code System.out}
+	 * @param errorStream
+	 *            The error stream, or {@code null} to use {@code System.err}
+	 * @return The pre-commit hook for the given repository.
+	 * @since 5.6
+	 */
+	public static CommitMsgHook commitMsg(Repository repo,
+			PrintStream outputStream, PrintStream errorStream) {
+		return new CommitMsgHook(repo, outputStream, errorStream);
+	}
+
+	/**
+	 * Create pre-push hook for the given repository with the default error
+	 * stream
 	 *
 	 * @param repo
 	 *            a {@link org.eclipse.jgit.lib.Repository} object.
@@ -127,4 +182,36 @@
 		}
 		return new PrePushHook(repo, outputStream);
 	}
+
+	/**
+	 * Create pre-push hook for the given repository
+	 *
+	 * @param repo
+	 *            a {@link org.eclipse.jgit.lib.Repository} object.
+	 * @param outputStream
+	 *            The output stream, or {@code null} to use {@code System.out}
+	 * @param errorStream
+	 *            The error stream, or {@code null} to use {@code System.err}
+	 * @return The pre-push hook for the given repository.
+	 * @since 5.6
+	 */
+	public static PrePushHook prePush(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		if (LfsFactory.getInstance().isAvailable()) {
+			PrePushHook hook = LfsFactory.getInstance().getPrePushHook(repo,
+					outputStream, errorStream);
+			if (hook != null) {
+				if (hook.isNativeHookPresent()) {
+					PrintStream ps = outputStream;
+					if (ps == null) {
+						ps = System.out;
+					}
+					ps.println(MessageFormat
+							.format(JGitText.get().lfsHookConflict, repo));
+				}
+				return hook;
+			}
+		}
+		return new PrePushHook(repo, outputStream, errorStream);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
index 24bad16..b6e576f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
@@ -61,6 +61,9 @@
 
 	/**
 	 * Constructor for PostCommitHook
+	 * <p>
+	 * This constructor will use the default error stream.
+	 * </p>
 	 *
 	 * @param repo
 	 *            The repository
@@ -72,6 +75,24 @@
 		super(repo, outputStream);
 	}
 
+	/**
+	 * Constructor for PostCommitHook
+	 *
+	 * @param repo
+	 *            The repository
+	 * @param outputStream
+	 *            The output stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.out}.
+	 * @param errorStream
+	 *            The error stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.err}.
+	 * @since 5.6
+	 */
+	protected PostCommitHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		super(repo, outputStream, errorStream);
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public Void call() throws IOException, AbortedByHookException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java
index 0d9290d..dbdaf86 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java
@@ -61,6 +61,9 @@
 
 	/**
 	 * Constructor for PreCommitHook
+	 * <p>
+	 * This constructor will use the default error stream.
+	 * </p>
 	 *
 	 * @param repo
 	 *            The repository
@@ -72,6 +75,24 @@
 		super(repo, outputStream);
 	}
 
+	/**
+	 * Constructor for PreCommitHook
+	 *
+	 * @param repo
+	 *            The repository
+	 * @param outputStream
+	 *            The output stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.out}.
+	 * @param errorStream
+	 *            The error stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.err}.
+	 * @since 5.6
+	 */
+	protected PreCommitHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		super(repo, outputStream, errorStream);
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public Void call() throws IOException, AbortedByHookException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
index 431944f..61180fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
@@ -73,6 +73,9 @@
 
 	/**
 	 * Constructor for PrePushHook
+	 * <p>
+	 * This constructor will use the default error stream.
+	 * </p>
 	 *
 	 * @param repo
 	 *            The repository
@@ -84,6 +87,24 @@
 		super(repo, outputStream);
 	}
 
+	/**
+	 * Constructor for PrePushHook
+	 *
+	 * @param repo
+	 *            The repository
+	 * @param outputStream
+	 *            The output stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.out}.
+	 * @param errorStream
+	 *            The error stream the hook must use. {@code null} is allowed,
+	 *            in which case the hook will use {@code System.err}.
+	 * @since 5.6
+	 */
+	protected PrePushHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		super(repo, outputStream, errorStream);
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	protected String getStdinArgs() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
index 31e173b..4bc926a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
@@ -62,7 +62,7 @@
  * @since 3.6
  */
 public class FastIgnoreRule {
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(FastIgnoreRule.class);
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
index 864f8bf..d47ffc2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
@@ -59,7 +59,7 @@
  */
 public class IgnoreNode {
 	/** Result from {@link IgnoreNode#isIgnored(String, boolean)}. */
-	public static enum MatchResult {
+	public enum MatchResult {
 		/** The file is not ignored, due to a rule saying its not ignored. */
 		NOT_IGNORED,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
index 3c0f17a..b7d6acc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
@@ -291,26 +291,25 @@
 					// We had a prefix match here.
 					if (!pathMatch) {
 						return true;
+					}
+					if (right == endExcl - 1) {
+						// Extra slash at the end: actually a full match.
+						// Must meet directory expectations
+						return !dirOnly || assumeDirectory;
+					}
+					// Prefix matches only if pattern ended with /**
+					if (wasWild) {
+						return true;
+					}
+					if (lastWildmatch >= 0) {
+						// Consider pattern **/x and input x/x.
+						// We've matched the prefix x/ so far: we
+						// must try to extend the **!
+						matcher = lastWildmatch + 1;
+						right = wildmatchBacktrackPos;
+						wildmatchBacktrackPos = -1;
 					} else {
-						if (right == endExcl - 1) {
-							// Extra slash at the end: actually a full match.
-							// Must meet directory expectations
-							return !dirOnly || assumeDirectory;
-						}
-						// Prefix matches only if pattern ended with /**
-						if (wasWild) {
-							return true;
-						}
-						if (lastWildmatch >= 0) {
-							// Consider pattern **/x and input x/x.
-							// We've matched the prefix x/ so far: we
-							// must try to extend the **!
-							matcher = lastWildmatch + 1;
-							right = wildmatchBacktrackPos;
-							wildmatchBacktrackPos = -1;
-						} else {
-							return false;
-						}
+						return false;
 					}
 				}
 			} else if (lastWildmatch != -1) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
index 41923ee..132109b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
@@ -192,22 +192,20 @@
 		}
 		if (pattern.indexOf('?') != -1) {
 			return true;
-		} else {
-			// check if the backslash escapes one of the glob special characters
-			// if not, backslash is not part of a regex and treated literally
-			int backSlash = pattern.indexOf('\\');
-			if (backSlash >= 0) {
-				int nextIdx = backSlash + 1;
-				if (pattern.length() == nextIdx) {
-					return false;
-				}
-				char nextChar = pattern.charAt(nextIdx);
-				if (escapedByBackslash(nextChar)) {
-					return true;
-				} else {
-					return false;
-				}
+		}
+		// check if the backslash escapes one of the glob special characters
+		// if not, backslash is not part of a regex and treated literally
+		int backSlash = pattern.indexOf('\\');
+		if (backSlash >= 0) {
+			int nextIdx = backSlash + 1;
+			if (pattern.length() == nextIdx) {
+				return false;
 			}
+			char nextChar = pattern.charAt(nextIdx);
+			if (escapedByBackslash(nextChar)) {
+				return true;
+			}
+			return false;
 		}
 		return false;
 	}
@@ -231,11 +229,11 @@
 		return PatternState.COMPLEX;
 	}
 
-	static enum PatternState {
+	enum PatternState {
 		LEADING_ASTERISK_ONLY, TRAILING_ASTERISK_ONLY, COMPLEX, NONE
 	}
 
-	final static List<String> POSIX_CHAR_CLASSES = Arrays.asList(
+	static final List<String> POSIX_CHAR_CLASSES = Arrays.asList(
 			"alnum", "alpha", "blank", "cntrl", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 			// [:alnum:] [:alpha:] [:blank:] [:cntrl:]
 			"digit", "graph", "lower", "print", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
@@ -250,7 +248,7 @@
 
 	private static final String DL = "\\p{javaDigit}\\p{javaLetter}"; //$NON-NLS-1$
 
-	final static List<String> JAVA_CHAR_CLASSES = Arrays
+	static final List<String> JAVA_CHAR_CLASSES = Arrays
 			.asList("\\p{Alnum}", "\\p{javaLetter}", "\\p{Blank}", "\\p{Cntrl}", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 					// [:alnum:] [:alpha:] [:blank:] [:cntrl:]
 					"\\p{javaDigit}", "[\\p{Graph}" + DL + "]", "\\p{Ll}", "[\\p{Print}" + DL + "]", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
@@ -263,7 +261,7 @@
 
 	// Collating symbols [[.a.]] or equivalence class expressions [[=a=]] are
 	// not supported by CLI git (at least not by 1.9.1)
-	final static Pattern UNSUPPORTED = Pattern
+	static final Pattern UNSUPPORTED = Pattern
 			.compile("\\[\\[[.=]\\w+[.=]\\]\\]"); //$NON-NLS-1$
 
 	/**
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 cd7d1e5..37a7c7d9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -1,45 +1,12 @@
 /*
  * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
- * Copyright (C) 2012, Research In Motion Limited
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2012, 2021 Research In Motion Limited and others
  *
- * 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
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://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.
+ * SPDX-License-Identifier: BSD-3-Clause
  */
 
 package org.eclipse.jgit.internal;
@@ -345,10 +312,12 @@
 	/***/ public String failedAtomicFileCreation;
 	/***/ public String failedCreateLockFile;
 	/***/ public String failedToDetermineFilterDefinition;
+	/***/ public String failedToConvert;
 	/***/ public String failedUpdatingRefs;
 	/***/ public String failureDueToOneOfTheFollowing;
 	/***/ public String failureUpdatingFETCH_HEAD;
 	/***/ public String failureUpdatingTrackingRef;
+	/***/ public String fileAlreadyExists;
 	/***/ public String fileCannotBeDeleted;
 	/***/ public String fileIsTooLarge;
 	/***/ public String fileModeNotSetForPath;
@@ -387,6 +356,7 @@
 	/***/ public String incorrectOBJECT_ID_LENGTH;
 	/***/ public String indexFileCorruptedNegativeBucketCount;
 	/***/ public String indexFileIsTooLargeForJgit;
+	/***/ public String indexNumbersNotIncreasing;
 	/***/ public String indexWriteException;
 	/***/ public String initFailedBareRepoDifferentDirs;
 	/***/ public String initFailedDirIsNoDirectory;
@@ -412,6 +382,7 @@
 	/***/ public String invalidGitdirRef;
 	/***/ public String invalidGitModules;
 	/***/ public String invalidGitType;
+	/***/ public String invalidHooksPath;
 	/***/ public String invalidId;
 	/***/ public String invalidId0;
 	/***/ public String invalidIdLength;
@@ -466,10 +437,13 @@
 	/***/ public String localRefIsMissingObjects;
 	/***/ public String localRepository;
 	/***/ public String lockCountMustBeGreaterOrEqual1;
+	/***/ public String lockAlreadyHeld;
 	/***/ public String lockError;
 	/***/ public String lockFailedRetry;
 	/***/ public String lockOnNotClosed;
 	/***/ public String lockOnNotHeld;
+	/***/ public String lockStreamClosed;
+	/***/ public String lockStreamMultiple;
 	/***/ public String maxCountMustBeNonNegative;
 	/***/ public String mergeConflictOnNonNoteEntries;
 	/***/ public String mergeConflictOnNotes;
@@ -512,8 +486,10 @@
 	/***/ public String noMergeBase;
 	/***/ public String noMergeHeadSpecified;
 	/***/ public String nonBareLinkFilesNotSupported;
+	/***/ public String nonCommitToHeads;
 	/***/ public String noPathAttributesFound;
 	/***/ public String noSuchRef;
+	/***/ public String noSuchRefKnown;
 	/***/ public String noSuchSubmodule;
 	/***/ public String notABoolean;
 	/***/ public String notABundle;
@@ -612,7 +588,8 @@
 	/***/ public String refAlreadyExists1;
 	/***/ public String reflogEntryNotFound;
 	/***/ public String refNotResolved;
-	/***/ public String refTableRecordsMustIncrease;
+	/***/ public String reftableDirExists;
+	/***/ public String reftableRecordsMustIncrease;
 	/***/ public String refUpdateReturnCodeWas;
 	/***/ public String remoteConfigHasNoURIAssociated;
 	/***/ public String remoteDoesNotHaveSpec;
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 c0364ac..67cb2f6 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
@@ -126,7 +126,7 @@
 	private static final Logger log = LoggerFactory.getLogger(KetchLeader.class);
 
 	/** Current state of the leader instance. */
-	public static enum State {
+	public enum State {
 		/** Newly created instance trying to elect itself leader. */
 		CANDIDATE,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java
index 0e8377d..52c8f29 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java
@@ -77,6 +77,7 @@
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.transport.ReceiveCommand;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.SystemReader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -532,9 +533,8 @@
 		queued.add(0, new ReplicaPushRequest(this, cmds));
 
 		if (!waitingForRetry()) {
-			long delay = KetchSystem.delay(
-					lastRetryMillis,
-					minRetryMillis, maxRetryMillis);
+			long delay = FileUtils
+				.delay(lastRetryMillis, minRetryMillis, maxRetryMillis);
 			if (log.isDebugEnabled()) {
 				log.debug("Retrying {} after {} ms", //$NON-NLS-1$
 						describeForLog(), Long.valueOf(delay));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java
index d1d4f67..fd334f1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java
@@ -350,25 +350,4 @@
 		}
 	}
 
-	/**
-	 * Compute a delay in a {@code min..max} interval with random jitter.
-	 *
-	 * @param last
-	 *            amount of delay waited before the last attempt. This is used
-	 *            to seed the next delay interval. Should be 0 if there was no
-	 *            prior delay.
-	 * @param min
-	 *            shortest amount of allowable delay between attempts.
-	 * @param max
-	 *            longest amount of allowable delay between attempts.
-	 * @return new amount of delay to wait before the next attempt.
-	 */
-	static long delay(long last, long min, long max) {
-		long r = Math.max(0, last * 3 - min);
-		if (r > 0) {
-			int c = (int) Math.min(r + 1, Integer.MAX_VALUE);
-			r = RNG.nextInt(c);
-		}
-		return Math.max(Math.min(min + r, max), min);
-	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
index 53fd198..a27a9bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
@@ -132,9 +132,8 @@
 			// TODO(sop) Check term to see if my leader was deposed.
 			if (rw.isMergedInto(head, remote)) {
 				return AHEAD;
-			} else {
-				return DIVERGENT;
 			}
+			return DIVERGENT;
 		} catch (IOException err) {
 			KetchReplica.log.error(String.format(
 					"Cannot compare %s", //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index c6e2fae..16e7a0d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -412,8 +412,7 @@
 			getStat(statMiss, key).incrementAndGet();
 			boolean credit = true;
 			try {
-				v = file.readOneBlock(requestedPosition, ctx,
-						fileChannel.get());
+				v = file.readOneBlock(position, ctx, fileChannel.get());
 				credit = false;
 			} finally {
 				if (credit) {
@@ -450,7 +449,7 @@
 	}
 
 	@SuppressWarnings("unchecked")
-	private void reserveSpace(int reserve, DfsStreamKey key) {
+	private void reserveSpace(long reserve, DfsStreamKey key) {
 		clockLock.lock();
 		try {
 			long live = LongStream.of(getCurrentSize()).sum() + reserve;
@@ -487,7 +486,7 @@
 		}
 	}
 
-	private void creditSpace(int credit, DfsStreamKey key) {
+	private void creditSpace(long credit, DfsStreamKey key) {
 		clockLock.lock();
 		try {
 			getStat(liveBytes, key).addAndGet(-credit);
@@ -497,7 +496,7 @@
 	}
 
 	@SuppressWarnings("unchecked")
-	private void addToClock(Ref ref, int credit) {
+	private void addToClock(Ref ref, long credit) {
 		clockLock.lock();
 		try {
 			if (credit != 0) {
@@ -521,17 +520,20 @@
 	 *
 	 * @param key
 	 *            the stream key of the pack.
+	 * @param position
+	 *            the position in the key. The default should be 0.
 	 * @param loader
 	 *            the function to load the reference.
 	 * @return the object reference.
 	 * @throws IOException
 	 *             the reference was not in the cache and could not be loaded.
 	 */
-	<T> Ref<T> getOrLoadRef(DfsStreamKey key, RefLoader<T> loader)
+	<T> Ref<T> getOrLoadRef(
+			DfsStreamKey key, long position, RefLoader<T> loader)
 			throws IOException {
-		int slot = slot(key, 0);
+		int slot = slot(key, position);
 		HashEntry e1 = table.get(slot);
-		Ref<T> ref = scanRef(e1, key, 0);
+		Ref<T> ref = scanRef(e1, key, position);
 		if (ref != null) {
 			getStat(statHit, key).incrementAndGet();
 			return ref;
@@ -543,7 +545,7 @@
 		try {
 			HashEntry e2 = table.get(slot);
 			if (e2 != e1) {
-				ref = scanRef(e2, key, 0);
+				ref = scanRef(e2, key, position);
 				if (ref != null) {
 					getStat(statHit, key).incrementAndGet();
 					return ref;
@@ -574,10 +576,10 @@
 	}
 
 	<T> Ref<T> putRef(DfsStreamKey key, long size, T v) {
-		return put(key, 0, (int) Math.min(size, Integer.MAX_VALUE), v);
+		return put(key, 0, size, v);
 	}
 
-	<T> Ref<T> put(DfsStreamKey key, long pos, int size, T v) {
+	<T> Ref<T> put(DfsStreamKey key, long pos, long size, T v) {
 		int slot = slot(key, pos);
 		HashEntry e1 = table.get(slot);
 		Ref<T> ref = scanRef(e1, key, pos);
@@ -720,12 +722,12 @@
 	static final class Ref<T> {
 		final DfsStreamKey key;
 		final long position;
-		final int size;
+		final long size;
 		volatile T value;
 		Ref next;
 		volatile boolean hot;
 
-		Ref(DfsStreamKey key, long position, int size, T v) {
+		Ref(DfsStreamKey key, long position, long size, T v) {
 			this.key = key;
 			this.position = position;
 			this.size = size;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
index 3605236e..9b28074 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
@@ -61,6 +61,13 @@
 	}
 
 	/**
+	 * @return the pack passed to the constructor
+	 */
+	public DfsPackFile getPackFile() {
+		return pack;
+	}
+
+	/**
 	 * Get the description of the pack.
 	 *
 	 * @return the description of the pack.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index f10a1d8..3e71d07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -744,11 +744,15 @@
 			return;
 		}
 
-		try (ReftableStack stack = ReftableStack.open(ctx, reftablesBefore)) {
-			ReftableCompactor compact = new ReftableCompactor();
+		try (DfsReftableStack stack = DfsReftableStack.open(ctx, reftablesBefore);
+		     DfsOutputStream out = objdb.writeFile(pack, REFTABLE)) {
+			ReftableCompactor compact = new ReftableCompactor(out);
 			compact.addAll(stack.readers());
 			compact.setIncludeDeletes(includeDeletes);
-			compactReftable(pack, compact);
+			compact.setConfig(configureReftable(reftableConfig, out));
+			compact.compact();
+			pack.addFileExt(REFTABLE);
+			pack.setReftableStats(compact.getStats());
 		}
 	}
 
@@ -765,24 +769,12 @@
 			throws IOException {
 		try (DfsOutputStream out = objdb.writeFile(pack, REFTABLE)) {
 			ReftableConfig cfg = configureReftable(reftableConfig, out);
-			ReftableWriter writer = new ReftableWriter(cfg)
+			ReftableWriter writer = new ReftableWriter(cfg, out)
 					.setMinUpdateIndex(reftableInitialMinUpdateIndex)
-					.setMaxUpdateIndex(reftableInitialMaxUpdateIndex)
-					.begin(out)
-					.sortAndWriteRefs(refs)
-					.finish();
+					.setMaxUpdateIndex(reftableInitialMaxUpdateIndex).begin()
+					.sortAndWriteRefs(refs).finish();
 			pack.addFileExt(REFTABLE);
 			pack.setReftableStats(writer.getStats());
 		}
 	}
-
-	private void compactReftable(DfsPackDescription pack,
-			ReftableCompactor compact) throws IOException {
-		try (DfsOutputStream out = objdb.writeFile(pack, REFTABLE)) {
-			compact.setConfig(configureReftable(reftableConfig, out));
-			compact.compact(out);
-			pack.addFileExt(REFTABLE);
-			pack.setReftableStats(compact.getStats());
-		}
-	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index 09d5937..0ee8135 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -97,7 +97,7 @@
 	 * comparator based on {@link Enum#compareTo}. Prefer {@link
 	 * #DEFAULT_COMPARATOR} or your own {@link ComparatorBuilder}.
 	 */
-	public static enum PackSource {
+	public enum PackSource {
 		/** The pack is created by ObjectInserter due to local activity. */
 		INSERT,
 
@@ -705,7 +705,7 @@
 	}
 
 	/** Snapshot of packs scanned in a single pass. */
-	public static abstract class PackList {
+	public abstract static class PackList {
 		/** All known packs, sorted. */
 		public final DfsPackFile[] packs;
 
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 6f3f2bd..083124e 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
@@ -311,12 +311,16 @@
 		DfsObjDatabase objdb = repo.getObjectDatabase();
 		Collections.sort(srcReftables, objdb.reftableComparator());
 
-		try (ReftableStack stack = ReftableStack.open(ctx, srcReftables)) {
-			initOutDesc(objdb);
-			ReftableCompactor compact = new ReftableCompactor();
+		initOutDesc(objdb);
+		try (DfsReftableStack stack = DfsReftableStack.open(ctx, srcReftables);
+		     DfsOutputStream out = objdb.writeFile(outDesc, REFTABLE)) {
+			ReftableCompactor compact = new ReftableCompactor(out);
 			compact.addAll(stack.readers());
 			compact.setIncludeDeletes(true);
-			writeReftable(objdb, outDesc, compact);
+			compact.setConfig(configureReftable(reftableConfig, out));
+			compact.compact();
+			outDesc.addFileExt(REFTABLE);
+			outDesc.setReftableStats(compact.getStats());
 		}
 	}
 
@@ -497,16 +501,6 @@
 		}
 	}
 
-	private void writeReftable(DfsObjDatabase objdb, DfsPackDescription pack,
-			ReftableCompactor compact) throws IOException {
-		try (DfsOutputStream out = objdb.writeFile(pack, REFTABLE)) {
-			compact.setConfig(configureReftable(reftableConfig, out));
-			compact.compact(out);
-			pack.addFileExt(REFTABLE);
-			pack.setReftableStats(compact.getStats());
-		}
-	}
-
 	static ReftableConfig configureReftable(ReftableConfig cfg,
 			DfsOutputStream out) {
 		int bs = out.blockSize();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index be1387e..d0f9b1c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -89,6 +89,7 @@
  */
 public final class DfsPackFile extends BlockBasedFile {
 	private static final int REC_SIZE = Constants.OBJECT_ID_LENGTH + 8;
+	private static final long REF_POSITION = 0;
 
 	/**
 	 * Lock for initialization of {@link #index} and {@link #corruptObjects}.
@@ -194,45 +195,10 @@
 
 			try {
 				DfsStreamKey idxKey = desc.getStreamKey(INDEX);
-				DfsBlockCache.Ref<PackIndex> idxref = cache.getOrLoadRef(idxKey,
-						() -> {
-							try {
-								ctx.stats.readIdx++;
-								long start = System.nanoTime();
-								try (ReadableChannel rc = ctx.db.openFile(desc,
-										INDEX)) {
-									InputStream in = Channels
-											.newInputStream(rc);
-									int wantSize = 8192;
-									int bs = rc.blockSize();
-									if (0 < bs && bs < wantSize) {
-										bs = (wantSize / bs) * bs;
-									} else if (bs <= 0) {
-										bs = wantSize;
-									}
-									PackIndex idx = PackIndex.read(
-											new BufferedInputStream(in, bs));
-									int sz = (int) Math.min(
-											idx.getObjectCount() * REC_SIZE,
-											Integer.MAX_VALUE);
-									ctx.stats.readIdxBytes += rc.position();
-									index = idx;
-									return new DfsBlockCache.Ref<>(idxKey, 0,
-											sz, idx);
-								} finally {
-									ctx.stats.readIdxMicros += elapsedMicros(
-											start);
-								}
-							} catch (EOFException e) {
-								throw new IOException(MessageFormat.format(
-										DfsText.get().shortReadOfIndex,
-										desc.getFileName(INDEX)), e);
-							} catch (IOException e) {
-								throw new IOException(MessageFormat.format(
-										DfsText.get().cannotReadIndex,
-										desc.getFileName(INDEX)), e);
-							}
-						});
+				DfsBlockCache.Ref<PackIndex> idxref = cache.getOrLoadRef(
+						idxKey,
+						REF_POSITION,
+						() -> loadPackIndex(ctx, idxKey));
 				PackIndex idx = idxref.get();
 				if (index == null && idx != null) {
 					index = idx;
@@ -267,44 +233,10 @@
 			PackIndex idx = idx(ctx);
 			PackReverseIndex revidx = getReverseIdx(ctx);
 			DfsStreamKey bitmapKey = desc.getStreamKey(BITMAP_INDEX);
-			DfsBlockCache.Ref<PackBitmapIndex> idxref = cache
-					.getOrLoadRef(bitmapKey, () -> {
-						ctx.stats.readBitmap++;
-						long start = System.nanoTime();
-						try (ReadableChannel rc = ctx.db.openFile(desc,
-								BITMAP_INDEX)) {
-							long size;
-							PackBitmapIndex bmidx;
-							try {
-								InputStream in = Channels.newInputStream(rc);
-								int wantSize = 8192;
-								int bs = rc.blockSize();
-								if (0 < bs && bs < wantSize) {
-									bs = (wantSize / bs) * bs;
-								} else if (bs <= 0) {
-									bs = wantSize;
-								}
-								in = new BufferedInputStream(in, bs);
-								bmidx = PackBitmapIndex.read(in, idx, revidx);
-							} finally {
-								size = rc.position();
-								ctx.stats.readIdxBytes += size;
-								ctx.stats.readIdxMicros += elapsedMicros(start);
-							}
-							int sz = (int) Math.min(size, Integer.MAX_VALUE);
-							bitmapIndex = bmidx;
-							return new DfsBlockCache.Ref<>(bitmapKey, 0, sz,
-									bmidx);
-						} catch (EOFException e) {
-							throw new IOException(MessageFormat.format(
-									DfsText.get().shortReadOfIndex,
-									desc.getFileName(BITMAP_INDEX)), e);
-						} catch (IOException e) {
-							throw new IOException(MessageFormat.format(
-									DfsText.get().cannotReadIndex,
-									desc.getFileName(BITMAP_INDEX)), e);
-						}
-					});
+			DfsBlockCache.Ref<PackBitmapIndex> idxref = cache.getOrLoadRef(
+					bitmapKey,
+					REF_POSITION,
+					() -> loadBitmapIndex(ctx, bitmapKey, idx, revidx));
 			PackBitmapIndex bmidx = idxref.get();
 			if (bitmapIndex == null && bmidx != null) {
 				bitmapIndex = bmidx;
@@ -326,14 +258,10 @@
 			PackIndex idx = idx(ctx);
 			DfsStreamKey revKey = new DfsStreamKey.ForReverseIndex(
 					desc.getStreamKey(INDEX));
-			DfsBlockCache.Ref<PackReverseIndex> revref = cache
-					.getOrLoadRef(revKey, () -> {
-						PackReverseIndex revidx = new PackReverseIndex(idx);
-						int sz = (int) Math.min(idx.getObjectCount() * 8,
-								Integer.MAX_VALUE);
-						reverseIndex = revidx;
-						return new DfsBlockCache.Ref<>(revKey, 0, sz, revidx);
-					});
+			DfsBlockCache.Ref<PackReverseIndex> revref = cache.getOrLoadRef(
+					revKey,
+					REF_POSITION,
+					() -> loadReverseIdx(revKey, idx));
 			PackReverseIndex revidx = revref.get();
 			if (reverseIndex == null && revidx != null) {
 				reverseIndex = revidx;
@@ -1091,4 +1019,91 @@
 			list.add(offset);
 		}
 	}
+
+	private DfsBlockCache.Ref<PackIndex> loadPackIndex(
+			DfsReader ctx, DfsStreamKey idxKey) throws IOException {
+		try {
+			ctx.stats.readIdx++;
+			long start = System.nanoTime();
+			try (ReadableChannel rc = ctx.db.openFile(desc, INDEX)) {
+				InputStream in = Channels.newInputStream(rc);
+				int wantSize = 8192;
+				int bs = rc.blockSize();
+				if (0 < bs && bs < wantSize) {
+					bs = (wantSize / bs) * bs;
+				} else if (bs <= 0) {
+					bs = wantSize;
+				}
+				PackIndex idx = PackIndex.read(new BufferedInputStream(in, bs));
+				ctx.stats.readIdxBytes += rc.position();
+				index = idx;
+				return new DfsBlockCache.Ref<>(
+						idxKey,
+						REF_POSITION,
+						idx.getObjectCount() * REC_SIZE,
+						idx);
+			} finally {
+				ctx.stats.readIdxMicros += elapsedMicros(start);
+			}
+		} catch (EOFException e) {
+			throw new IOException(MessageFormat.format(
+					DfsText.get().shortReadOfIndex,
+					desc.getFileName(INDEX)), e);
+		} catch (IOException e) {
+			throw new IOException(MessageFormat.format(
+					DfsText.get().cannotReadIndex,
+					desc.getFileName(INDEX)), e);
+		}
+	}
+
+	private DfsBlockCache.Ref<PackReverseIndex> loadReverseIdx(
+			DfsStreamKey revKey, PackIndex idx) {
+		PackReverseIndex revidx = new PackReverseIndex(idx);
+		reverseIndex = revidx;
+		return new DfsBlockCache.Ref<>(
+				revKey,
+				REF_POSITION,
+				idx.getObjectCount() * 8,
+				revidx);
+	}
+
+	private DfsBlockCache.Ref<PackBitmapIndex> loadBitmapIndex(
+			DfsReader ctx,
+			DfsStreamKey bitmapKey,
+			PackIndex idx,
+			PackReverseIndex revidx) throws IOException {
+		ctx.stats.readBitmap++;
+		long start = System.nanoTime();
+		try (ReadableChannel rc = ctx.db.openFile(desc, BITMAP_INDEX)) {
+			long size;
+			PackBitmapIndex bmidx;
+			try {
+				InputStream in = Channels.newInputStream(rc);
+				int wantSize = 8192;
+				int bs = rc.blockSize();
+				if (0 < bs && bs < wantSize) {
+					bs = (wantSize / bs) * bs;
+				} else if (bs <= 0) {
+					bs = wantSize;
+				}
+				in = new BufferedInputStream(in, bs);
+				bmidx = PackBitmapIndex.read(in, idx, revidx);
+			} finally {
+				size = rc.position();
+				ctx.stats.readIdxBytes += size;
+				ctx.stats.readIdxMicros += elapsedMicros(start);
+			}
+			bitmapIndex = bmidx;
+			return new DfsBlockCache.Ref<>(
+					bitmapKey, REF_POSITION, size, bmidx);
+		} catch (EOFException e) {
+			throw new IOException(MessageFormat.format(
+					DfsText.get().shortReadOfIndex,
+					desc.getFileName(BITMAP_INDEX)), e);
+		} catch (IOException e) {
+			throw new IOException(MessageFormat.format(
+					DfsText.get().cannotReadIndex,
+					desc.getFileName(BITMAP_INDEX)), e);
+		}
+	}
 }
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 732cd4d..b3b9e39 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
@@ -191,14 +191,11 @@
 						rw.peel(obj).copy(),
 						hasVersioning() ? leaf.getUpdateIndex()
 								: UNDEFINED_UPDATE_INDEX);
-			} else {
-				return new ObjectIdRef.PeeledNonTag(
-						leaf.getStorage(),
-						leaf.getName(),
-						leaf.getObjectId(),
-						hasVersioning() ? leaf.getUpdateIndex()
-								: UNDEFINED_UPDATE_INDEX);
 			}
+			return new ObjectIdRef.PeeledNonTag(leaf.getStorage(),
+					leaf.getName(), leaf.getObjectId(),
+					hasVersioning() ? leaf.getUpdateIndex()
+							: UNDEFINED_UPDATE_INDEX);
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableBatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableBatchRefUpdate.java
new file mode 100644
index 0000000..124630e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableBatchRefUpdate.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2019, Google Inc.
+ * 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 org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
+import org.eclipse.jgit.internal.storage.io.BlockSource;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
+import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
+import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
+import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
+import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
+
+/**
+ * {@link org.eclipse.jgit.lib.BatchRefUpdate} for
+ * {@link org.eclipse.jgit.internal.storage.dfs.DfsReftableDatabase}.
+ */
+public class DfsReftableBatchRefUpdate extends ReftableBatchRefUpdate {
+	private static final int AVG_BYTES = 36;
+
+	private final DfsReftableDatabase refdb;
+
+	private final DfsObjDatabase odb;
+
+	/**
+	 * Initialize batch update.
+	 *
+	 * @param refdb
+	 *            database the update will modify.
+	 * @param odb
+	 *            object database to store the reftable.
+	 */
+	protected DfsReftableBatchRefUpdate(DfsReftableDatabase refdb,
+			DfsObjDatabase odb) {
+		super(refdb, refdb.reftableDatabase, refdb.getLock(), refdb.getRepository());
+		this.refdb = refdb;
+		this.odb = odb;
+	}
+
+	@Override
+	protected void applyUpdates(List<Ref> newRefs, List<ReceiveCommand> pending)
+			throws IOException {
+		Set<DfsPackDescription> prune = Collections.emptySet();
+		DfsPackDescription pack = odb.newPack(PackSource.INSERT);
+		try (DfsOutputStream out = odb.writeFile(pack, REFTABLE)) {
+			ReftableConfig cfg = DfsPackCompactor
+					.configureReftable(refdb.getReftableConfig(), out);
+
+			ReftableWriter.Stats stats;
+			if (refdb.compactDuringCommit()
+					&& newRefs.size() * AVG_BYTES <= cfg.getRefBlockSize()
+					&& canCompactTopOfStack(cfg)) {
+				ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+				ReftableWriter rw = new ReftableWriter(cfg, tmp);
+				write(rw, newRefs, pending);
+				rw.finish();
+				stats = compactTopOfStack(out, cfg, tmp.toByteArray());
+				prune = toPruneTopOfStack();
+			} else {
+				ReftableWriter rw = new ReftableWriter(cfg, out);
+				write(rw, newRefs, pending);
+				rw.finish();
+				stats = rw.getStats();
+			}
+			pack.addFileExt(REFTABLE);
+			pack.setReftableStats(stats);
+		}
+
+		odb.commitPack(Collections.singleton(pack), prune);
+		odb.addReftable(pack, prune);
+		refdb.clearCache();
+	}
+
+	private boolean canCompactTopOfStack(ReftableConfig cfg)
+			throws IOException {
+		refdb.getLock().lock();
+		try {
+			DfsReftableStack stack = refdb.stack();
+			List<ReftableReader> readers = stack.readers();
+			if (readers.isEmpty()) {
+				return false;
+			}
+
+			int lastIdx = readers.size() - 1;
+			DfsReftable last = stack.files().get(lastIdx);
+			DfsPackDescription desc = last.getPackDescription();
+			if (desc.getPackSource() != PackSource.INSERT
+				|| !packOnlyContainsReftable(desc)) {
+				return false;
+			}
+
+			ReftableReader table = readers.get(lastIdx);
+			int bs = cfg.getRefBlockSize();
+			return table.size() <= 3 * bs;
+		} finally {
+			refdb.getLock().unlock();
+		}
+	}
+
+	private ReftableWriter.Stats compactTopOfStack(OutputStream out,
+			ReftableConfig cfg, byte[] newTable) throws IOException {
+		refdb.getLock().lock();
+		try {
+			List<ReftableReader> stack = refdb.stack().readers();
+
+			ReftableReader last = stack.get(stack.size() - 1);
+
+			List<ReftableReader> tables = new ArrayList<>(2);
+			tables.add(last);
+			tables.add(new ReftableReader(BlockSource.from(newTable)));
+
+			ReftableCompactor compactor = new ReftableCompactor(out);
+			compactor.setConfig(cfg);
+			compactor.setIncludeDeletes(true);
+			compactor.addAll(tables);
+			compactor.compact();
+			return compactor.getStats();
+		} finally {
+			refdb.getLock().unlock();
+		}
+	}
+
+	private Set<DfsPackDescription> toPruneTopOfStack() throws IOException {
+		refdb.getLock().lock();
+		try {
+			List<DfsReftable> stack = refdb.stack().files();
+
+			DfsReftable last = stack.get(stack.size() - 1);
+			return Collections.singleton(last.getPackDescription());
+		} finally {
+			refdb.getLock().unlock();
+		}
+	}
+
+	private boolean packOnlyContainsReftable(DfsPackDescription desc) {
+		for (PackExt ext : PackExt.values()) {
+			if (ext != REFTABLE && desc.hasFileExt(ext)) {
+				return false;
+			}
+		}
+		return true;
+	}
+}
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 6050c15..124131d 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
@@ -45,19 +45,17 @@
 
 import java.io.IOException;
 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.TreeSet;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
-import org.eclipse.jgit.internal.storage.reftable.RefCursor;
-import org.eclipse.jgit.internal.storage.reftable.Reftable;
 import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
+import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
 import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
@@ -81,13 +79,10 @@
  * and one will fail.
  */
 public class DfsReftableDatabase extends DfsRefDatabase {
-	private final ReentrantLock lock = new ReentrantLock(true);
+	final ReftableDatabase reftableDatabase;
 
 	private DfsReader ctx;
-
-	private ReftableStack tableStack;
-
-	private MergedReftable mergedTables;
+	private DfsReftableStack stack;
 
 	/**
 	 * Initialize the reference database for a repository.
@@ -97,6 +92,18 @@
 	 */
 	protected DfsReftableDatabase(DfsRepository repo) {
 		super(repo);
+		reftableDatabase = new ReftableDatabase() {
+			@Override
+			public MergedReftable openMergedReftable() throws IOException {
+				DfsReftableDatabase.this.getLock().lock();
+				try {
+					return new MergedReftable(stack().readers());
+				} finally {
+					DfsReftableDatabase.this.getLock().unlock();
+				}
+			}
+		};
+		stack = null;
 	}
 
 	/** {@inheritDoc} */
@@ -115,7 +122,7 @@
 	@Override
 	public BatchRefUpdate newBatchUpdate() {
 		DfsObjDatabase odb = getRepository().getObjectDatabase();
-		return new ReftableBatchRefUpdate(this, odb);
+		return new DfsReftableBatchRefUpdate(this, odb);
 	}
 
 	/**
@@ -124,7 +131,7 @@
 	 * @return configuration to write new reftables with.
 	 */
 	public ReftableConfig getReftableConfig() {
-		return new ReftableConfig(getRepository().getConfig());
+		return new ReftableConfig(getRepository());
 	}
 
 	/**
@@ -133,7 +140,7 @@
 	 * @return the lock protecting this instance's state.
 	 */
 	protected ReentrantLock getLock() {
-		return lock;
+		return reftableDatabase.getLock();
 	}
 
 	/**
@@ -147,134 +154,57 @@
 		return true;
 	}
 
-	/**
-	 * Obtain a handle to the merged reader.
-	 *
-	 * @return (possibly cached) handle to the merged reader.
-	 * @throws java.io.IOException
-	 *             if tables cannot be opened.
-	 */
-	protected Reftable reader() throws IOException {
-		lock.lock();
-		try {
-			if (mergedTables == null) {
-				mergedTables = new MergedReftable(stack().readers());
-			}
-			return mergedTables;
-		} finally {
-			lock.unlock();
-		}
-	}
 
 	/**
-	 * Obtain a handle to the stack of reftables.
+	 * Obtain a handle to the stack of reftables. Must hold lock.
 	 *
 	 * @return (possibly cached) handle to the stack.
 	 * @throws java.io.IOException
 	 *             if tables cannot be opened.
 	 */
-	protected ReftableStack stack() throws IOException {
-		lock.lock();
-		try {
-			if (tableStack == null) {
-				DfsObjDatabase odb = getRepository().getObjectDatabase();
-				if (ctx == null) {
-					ctx = odb.newReader();
-				}
-				tableStack = ReftableStack.open(ctx,
-						Arrays.asList(odb.getReftables()));
-			}
-			return tableStack;
-		} finally {
-			lock.unlock();
+	protected DfsReftableStack stack() throws IOException {
+		if (!getLock().isLocked()) {
+			throw new IllegalStateException("most hold lock to access stack"); //$NON-NLS-1$
 		}
+		DfsObjDatabase odb = getRepository().getObjectDatabase();
+
+		if (ctx == null) {
+			ctx = odb.newReader();
+		}
+		if (stack == null) {
+			stack = DfsReftableStack.open(ctx, Arrays.asList(odb.getReftables()));
+		}
+		return stack;
 	}
 
-	/** {@inheritDoc} */
 	@Override
 	public boolean isNameConflicting(String refName) throws IOException {
-		lock.lock();
-		try {
-			Reftable table = reader();
-
-			// Cannot be nested within an existing reference.
-			int lastSlash = refName.lastIndexOf('/');
-			while (0 < lastSlash) {
-				if (table.hasRef(refName.substring(0, lastSlash))) {
-					return true;
-				}
-				lastSlash = refName.lastIndexOf('/', lastSlash - 1);
-			}
-
-			// Cannot be the container of an existing reference.
-			return table.hasRefsWithPrefix(refName + '/');
-		} finally {
-			lock.unlock();
-		}
+		return reftableDatabase.isNameConflicting(refName, new TreeSet<>(), new HashSet<>());
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public Ref exactRef(String name) throws IOException {
-		lock.lock();
-		try {
-			Reftable table = reader();
-			Ref ref = table.exactRef(name);
-			if (ref != null && ref.isSymbolic()) {
-				return table.resolve(ref);
-			}
-			return ref;
-		} finally {
-			lock.unlock();
-		}
+		return reftableDatabase.exactRef(name);
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public Map<String, Ref> getRefs(String prefix) throws IOException {
-		RefList.Builder<Ref> all = new RefList.Builder<>();
-		lock.lock();
-		try {
-			Reftable table = reader();
-			try (RefCursor rc = ALL.equals(prefix) ? table.allRefs()
-					: (prefix.endsWith("/") ? table.seekRefsWithPrefix(prefix) //$NON-NLS-1$
-							: table.seekRef(prefix))) {
-				while (rc.next()) {
-					Ref ref = table.resolve(rc.getRef());
-					if (ref != null && ref.getObjectId() != null) {
-						all.add(ref);
-					}
-				}
-			}
-		} finally {
-			lock.unlock();
+		List<Ref> refs = reftableDatabase.getRefsByPrefix(prefix);
+		RefList.Builder<Ref> builder = new RefList.Builder<>(refs.size());
+		for (Ref r : refs) {
+			builder.add(r);
 		}
-
-		RefList<Ref> none = RefList.emptyList();
-		return new RefMap(prefix, all.toRefList(), none, none);
+		return new RefMap(prefix, builder.toRefList(), RefList.emptyList(),
+			RefList.emptyList());
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public List<Ref> getRefsByPrefix(String prefix) throws IOException {
-		List<Ref> all = new ArrayList<>();
-		lock.lock();
-		try {
-			Reftable table = reader();
-			try (RefCursor rc = ALL.equals(prefix) ? table.allRefs()
-					: table.seekRefsWithPrefix(prefix)) {
-				while (rc.next()) {
-					Ref ref = table.resolve(rc.getRef());
-					if (ref != null && ref.getObjectId() != null) {
-						all.add(ref);
-					}
-				}
-			}
-		} finally {
-			lock.unlock();
-		}
 
-		return Collections.unmodifiableList(all);
+		return reftableDatabase.getRefsByPrefix(prefix);
 	}
 
 	/** {@inheritDoc} */
@@ -283,17 +213,13 @@
 		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();
-		}
+		return reftableDatabase.getTipsWithSha1(id);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean hasFastTipsWithSha1() throws IOException {
+		return reftableDatabase.hasFastTipsWithSha1();
 	}
 
 	/** {@inheritDoc} */
@@ -314,19 +240,19 @@
 
 	@Override
 	void clearCache() {
-		lock.lock();
+		getLock().lock();
 		try {
-			if (tableStack != null) {
-				tableStack.close();
-				tableStack = null;
-			}
 			if (ctx != null) {
 				ctx.close();
 				ctx = null;
 			}
-			mergedTables = null;
+			reftableDatabase.clearCache();
+			if (stack != null) {
+				stack.close();
+				stack = null;
+			}
 		} finally {
-			lock.unlock();
+			getLock().unlock();
 		}
 	}
 
@@ -334,7 +260,7 @@
 	@Override
 	protected boolean compareAndPut(Ref oldRef, @Nullable Ref newRef)
 			throws IOException {
-		ReceiveCommand cmd = toCommand(oldRef, newRef);
+		ReceiveCommand cmd = ReftableDatabase.toCommand(oldRef, newRef);
 		try (RevWalk rw = new RevWalk(getRepository())) {
 			rw.setRetainBody(false);
 			newBatchUpdate().setAllowNonFastForwards(true).addCommand(cmd)
@@ -351,58 +277,6 @@
 		}
 	}
 
-	private static ReceiveCommand toCommand(Ref oldRef, Ref newRef) {
-		ObjectId oldId = toId(oldRef);
-		ObjectId newId = toId(newRef);
-		String name = toName(oldRef, newRef);
-
-		if (oldRef != null && oldRef.isSymbolic()) {
-			if (newRef != null) {
-				if (newRef.isSymbolic()) {
-					return ReceiveCommand.link(oldRef.getTarget().getName(),
-							newRef.getTarget().getName(), name);
-				} else {
-					return ReceiveCommand.unlink(oldRef.getTarget().getName(),
-							newId, name);
-				}
-			} else {
-				return ReceiveCommand.unlink(oldRef.getTarget().getName(),
-						ObjectId.zeroId(), name);
-			}
-		}
-
-		if (newRef != null && newRef.isSymbolic()) {
-			if (oldRef != null) {
-				if (oldRef.isSymbolic()) {
-					return ReceiveCommand.link(oldRef.getTarget().getName(),
-							newRef.getTarget().getName(), name);
-				} else {
-					return ReceiveCommand.link(oldId,
-							newRef.getTarget().getName(), name);
-				}
-			} else {
-				return ReceiveCommand.link(ObjectId.zeroId(),
-						newRef.getTarget().getName(), name);
-			}
-		}
-
-		return new ReceiveCommand(oldId, newId, name);
-	}
-
-	private static ObjectId toId(Ref ref) {
-		if (ref != null) {
-			ObjectId id = ref.getObjectId();
-			if (id != null) {
-				return id;
-			}
-		}
-		return ObjectId.zeroId();
-	}
-
-	private static String toName(Ref oldRef, Ref newRef) {
-		return oldRef != null ? oldRef.getName() : newRef.getName();
-	}
-
 	/** {@inheritDoc} */
 	@Override
 	protected boolean compareAndRemove(Ref oldRef) throws IOException {
@@ -417,12 +291,12 @@
 
 	@Override
 	void stored(Ref ref) {
-		// Unnecessary; ReftableBatchRefUpdate calls clearCache().
+		// Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
 	}
 
 	@Override
 	void removed(String refName) {
-		// Unnecessary; ReftableBatchRefUpdate calls clearCache().
+		// Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
 	}
 
 	/** {@inheritDoc} */
@@ -430,4 +304,5 @@
 	protected void cachePeeledState(Ref oldLeaf, Ref newLeaf) {
 		// Do not cache peeled state in reftable.
 	}
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableStack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableStack.java
similarity index 87%
rename from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableStack.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableStack.java
index 50ba0e0..59621a4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableStack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableStack.java
@@ -48,13 +48,13 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.eclipse.jgit.internal.storage.reftable.Reftable;
+import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
 
 /**
  * Tracks multiple open
- * {@link org.eclipse.jgit.internal.storage.reftable.Reftable} instances.
+ * {@link org.eclipse.jgit.internal.storage.reftable.ReftableReader} instances.
  */
-public class ReftableStack implements AutoCloseable {
+public class DfsReftableStack implements AutoCloseable {
 	/**
 	 * Opens a stack of tables for reading.
 	 *
@@ -67,9 +67,9 @@
 	 * @throws java.io.IOException
 	 *             a table could not be opened
 	 */
-	public static ReftableStack open(DfsReader ctx, List<DfsReftable> files)
+	public static DfsReftableStack open(DfsReader ctx, List<DfsReftable> files)
 			throws IOException {
-		ReftableStack stack = new ReftableStack(files.size());
+		DfsReftableStack stack = new DfsReftableStack(files.size());
 		boolean close = true;
 		try {
 			for (DfsReftable t : files) {
@@ -86,9 +86,9 @@
 	}
 
 	private final List<DfsReftable> files;
-	private final List<Reftable> tables;
+	private final List<ReftableReader> tables;
 
-	private ReftableStack(int tableCnt) {
+	private DfsReftableStack(int tableCnt) {
 		this.files = new ArrayList<>(tableCnt);
 		this.tables = new ArrayList<>(tableCnt);
 	}
@@ -109,14 +109,14 @@
 	 * @return unmodifiable list of tables, in the same order the files were
 	 *         passed to {@link #open(DfsReader, List)}.
 	 */
-	public List<Reftable> readers() {
+	public List<ReftableReader> readers() {
 		return Collections.unmodifiableList(tables);
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public void close() {
-		for (Reftable t : tables) {
+		for (ReftableReader t : tables) {
 			try {
 				t.close();
 			} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableBatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableBatchRefUpdate.java
deleted file mode 100644
index 07fd00f..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReftableBatchRefUpdate.java
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright (C) 2017, Google Inc.
- * 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.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
-import static org.eclipse.jgit.lib.Ref.Storage.NEW;
-import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
-import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
-import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-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;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
-import org.eclipse.jgit.internal.storage.io.BlockSource;
-import org.eclipse.jgit.internal.storage.pack.PackExt;
-import org.eclipse.jgit.internal.storage.reftable.Reftable;
-import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
-import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
-import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
-import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.ReflogEntry;
-import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * {@link org.eclipse.jgit.lib.BatchRefUpdate} for
- * {@link org.eclipse.jgit.internal.storage.dfs.DfsReftableDatabase}.
- */
-public class ReftableBatchRefUpdate extends BatchRefUpdate {
-	private static final int AVG_BYTES = 36;
-
-	private final DfsReftableDatabase refdb;
-
-	private final DfsObjDatabase odb;
-
-	private final ReentrantLock lock;
-
-	private final ReftableConfig reftableConfig;
-
-	/**
-	 * Initialize batch update.
-	 *
-	 * @param refdb
-	 *            database the update will modify.
-	 * @param odb
-	 *            object database to store the reftable.
-	 */
-	protected ReftableBatchRefUpdate(DfsReftableDatabase refdb,
-			DfsObjDatabase odb) {
-		super(refdb);
-		this.refdb = refdb;
-		this.odb = odb;
-		lock = refdb.getLock();
-		reftableConfig = refdb.getReftableConfig();
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public void execute(RevWalk rw, ProgressMonitor pm, List<String> options) {
-		List<ReceiveCommand> pending = getPending();
-		if (pending.isEmpty()) {
-			return;
-		}
-		if (options != null) {
-			setPushOptions(options);
-		}
-		try {
-			if (!checkObjectExistence(rw, pending)) {
-				return;
-			}
-			if (!checkNonFastForwards(rw, pending)) {
-				return;
-			}
-
-			lock.lock();
-			try {
-				Reftable table = refdb.reader();
-				if (!checkExpected(table, pending)) {
-					return;
-				}
-				if (!checkConflicting(pending)) {
-					return;
-				}
-				if (!blockUntilTimestamps(MAX_WAIT)) {
-					return;
-				}
-				applyUpdates(rw, pending);
-				for (ReceiveCommand cmd : pending) {
-					cmd.setResult(OK);
-				}
-			} finally {
-				lock.unlock();
-			}
-		} catch (IOException e) {
-			pending.get(0).setResult(LOCK_FAILURE, "io error"); //$NON-NLS-1$
-			ReceiveCommand.abort(pending);
-		}
-	}
-
-	private List<ReceiveCommand> getPending() {
-		return ReceiveCommand.filter(getCommands(), NOT_ATTEMPTED);
-	}
-
-	private boolean checkObjectExistence(RevWalk rw,
-			List<ReceiveCommand> pending) throws IOException {
-		for (ReceiveCommand cmd : pending) {
-			try {
-				if (!cmd.getNewId().equals(ObjectId.zeroId())) {
-					rw.parseAny(cmd.getNewId());
-				}
-			} catch (MissingObjectException e) {
-				// ReceiveCommand#setResult(Result) converts REJECTED to
-				// REJECTED_NONFASTFORWARD, even though that result is also
-				// used for a missing object. Eagerly handle this case so we
-				// can set the right result.
-				cmd.setResult(REJECTED_MISSING_OBJECT);
-				ReceiveCommand.abort(pending);
-				return false;
-			}
-		}
-		return true;
-	}
-
-	private boolean checkNonFastForwards(RevWalk rw,
-			List<ReceiveCommand> pending) throws IOException {
-		if (isAllowNonFastForwards()) {
-			return true;
-		}
-		for (ReceiveCommand cmd : pending) {
-			cmd.updateType(rw);
-			if (cmd.getType() == UPDATE_NONFASTFORWARD) {
-				cmd.setResult(REJECTED_NONFASTFORWARD);
-				ReceiveCommand.abort(pending);
-				return false;
-			}
-		}
-		return true;
-	}
-
-	private boolean checkConflicting(List<ReceiveCommand> pending)
-			throws IOException {
-		Set<String> names = new HashSet<>();
-		for (ReceiveCommand cmd : pending) {
-			names.add(cmd.getRefName());
-		}
-
-		boolean ok = true;
-		for (ReceiveCommand cmd : pending) {
-			String name = cmd.getRefName();
-			if (refdb.isNameConflicting(name)) {
-				cmd.setResult(LOCK_FAILURE);
-				ok = false;
-			} else {
-				int s = name.lastIndexOf('/');
-				while (0 < s) {
-					if (names.contains(name.substring(0, s))) {
-						cmd.setResult(LOCK_FAILURE);
-						ok = false;
-						break;
-					}
-					s = name.lastIndexOf('/', s - 1);
-				}
-			}
-		}
-		if (!ok && isAtomic()) {
-			ReceiveCommand.abort(pending);
-			return false;
-		}
-		return ok;
-	}
-
-	private boolean checkExpected(Reftable table, List<ReceiveCommand> pending)
-			throws IOException {
-		for (ReceiveCommand cmd : pending) {
-			if (!matchOld(cmd, table.exactRef(cmd.getRefName()))) {
-				cmd.setResult(LOCK_FAILURE);
-				if (isAtomic()) {
-					ReceiveCommand.abort(pending);
-					return false;
-				}
-			}
-		}
-		return true;
-	}
-
-	private static boolean matchOld(ReceiveCommand cmd, @Nullable Ref ref) {
-		if (ref == null) {
-			return AnyObjectId.isEqual(ObjectId.zeroId(), cmd.getOldId())
-					&& cmd.getOldSymref() == null;
-		} else if (ref.isSymbolic()) {
-			return ref.getTarget().getName().equals(cmd.getOldSymref());
-		}
-		ObjectId id = ref.getObjectId();
-		if (id == null) {
-			id = ObjectId.zeroId();
-		}
-		return cmd.getOldId().equals(id);
-	}
-
-	private void applyUpdates(RevWalk rw, List<ReceiveCommand> pending)
-			throws IOException {
-		List<Ref> newRefs = toNewRefs(rw, pending);
-		long updateIndex = nextUpdateIndex();
-		Set<DfsPackDescription> prune = Collections.emptySet();
-		DfsPackDescription pack = odb.newPack(PackSource.INSERT);
-		try (DfsOutputStream out = odb.writeFile(pack, REFTABLE)) {
-			ReftableConfig cfg = DfsPackCompactor
-					.configureReftable(reftableConfig, out);
-
-			ReftableWriter.Stats stats;
-			if (refdb.compactDuringCommit()
-					&& newRefs.size() * AVG_BYTES <= cfg.getRefBlockSize()
-					&& canCompactTopOfStack(cfg)) {
-				ByteArrayOutputStream tmp = new ByteArrayOutputStream();
-				write(tmp, cfg, updateIndex, newRefs, pending);
-				stats = compactTopOfStack(out, cfg, tmp.toByteArray());
-				prune = toPruneTopOfStack();
-			} else {
-				stats = write(out, cfg, updateIndex, newRefs, pending);
-			}
-			pack.addFileExt(REFTABLE);
-			pack.setReftableStats(stats);
-		}
-
-		odb.commitPack(Collections.singleton(pack), prune);
-		odb.addReftable(pack, prune);
-		refdb.clearCache();
-	}
-
-	private ReftableWriter.Stats write(OutputStream os, ReftableConfig cfg,
-			long updateIndex, List<Ref> newRefs, List<ReceiveCommand> pending)
-			throws IOException {
-		ReftableWriter writer = new ReftableWriter(cfg)
-				.setMinUpdateIndex(updateIndex).setMaxUpdateIndex(updateIndex)
-				.begin(os).sortAndWriteRefs(newRefs);
-		if (!isRefLogDisabled()) {
-			writeLog(writer, updateIndex, pending);
-		}
-		writer.finish();
-		return writer.getStats();
-	}
-
-	private void writeLog(ReftableWriter writer, long updateIndex,
-			List<ReceiveCommand> pending) throws IOException {
-		Map<String, ReceiveCommand> cmds = new HashMap<>();
-		List<String> byName = new ArrayList<>(pending.size());
-		for (ReceiveCommand cmd : pending) {
-			cmds.put(cmd.getRefName(), cmd);
-			byName.add(cmd.getRefName());
-		}
-		Collections.sort(byName);
-
-		PersonIdent ident = getRefLogIdent();
-		if (ident == null) {
-			ident = new PersonIdent(refdb.getRepository());
-		}
-		for (String name : byName) {
-			ReceiveCommand cmd = cmds.get(name);
-			if (isRefLogDisabled(cmd)) {
-				continue;
-			}
-			String msg = getRefLogMessage(cmd);
-			if (isRefLogIncludingResult(cmd)) {
-				String strResult = toResultString(cmd);
-				if (strResult != null) {
-					msg = msg.isEmpty() ? strResult : msg + ": " + strResult; //$NON-NLS-1$
-				}
-			}
-			writer.writeLog(name, updateIndex, ident, cmd.getOldId(),
-					cmd.getNewId(), msg);
-		}
-	}
-
-	private String toResultString(ReceiveCommand cmd) {
-		switch (cmd.getType()) {
-		case CREATE:
-			return ReflogEntry.PREFIX_CREATED;
-		case UPDATE:
-			// Match the behavior of a single RefUpdate. In that case, setting
-			// the force bit completely bypasses the potentially expensive
-			// isMergedInto check, by design, so the reflog message may be
-			// inaccurate.
-			//
-			// Similarly, this class bypasses the isMergedInto checks when the
-			// force bit is set, meaning we can't actually distinguish between
-			// UPDATE and UPDATE_NONFASTFORWARD when isAllowNonFastForwards()
-			// returns true.
-			return isAllowNonFastForwards() ? ReflogEntry.PREFIX_FORCED_UPDATE
-					: ReflogEntry.PREFIX_FAST_FORWARD;
-		case UPDATE_NONFASTFORWARD:
-			return ReflogEntry.PREFIX_FORCED_UPDATE;
-		default:
-			return null;
-		}
-	}
-
-	private static List<Ref> toNewRefs(RevWalk rw, List<ReceiveCommand> pending)
-			throws IOException {
-		List<Ref> refs = new ArrayList<>(pending.size());
-		for (ReceiveCommand cmd : pending) {
-			String name = cmd.getRefName();
-			ObjectId newId = cmd.getNewId();
-			String newSymref = cmd.getNewSymref();
-			if (AnyObjectId.isEqual(ObjectId.zeroId(), newId)
-					&& newSymref == null) {
-				refs.add(new ObjectIdRef.Unpeeled(NEW, name, null));
-				continue;
-			} else if (newSymref != null) {
-				refs.add(new SymbolicRef(name,
-						new ObjectIdRef.Unpeeled(NEW, newSymref, null)));
-				continue;
-			}
-
-			RevObject obj = rw.parseAny(newId);
-			RevObject peel = null;
-			if (obj instanceof RevTag) {
-				peel = rw.peel(obj);
-			}
-			if (peel != null) {
-				refs.add(new ObjectIdRef.PeeledTag(PACKED, name, newId,
-						peel.copy()));
-			} else {
-				refs.add(new ObjectIdRef.PeeledNonTag(PACKED, name, newId));
-			}
-		}
-		return refs;
-	}
-
-	private long nextUpdateIndex() throws IOException {
-		long updateIndex = 0;
-		for (Reftable r : refdb.stack().readers()) {
-			if (r instanceof ReftableReader) {
-				updateIndex = Math.max(updateIndex,
-						((ReftableReader) r).maxUpdateIndex());
-			}
-		}
-		return updateIndex + 1;
-	}
-
-	private boolean canCompactTopOfStack(ReftableConfig cfg)
-			throws IOException {
-		ReftableStack stack = refdb.stack();
-		List<Reftable> readers = stack.readers();
-		if (readers.isEmpty()) {
-			return false;
-		}
-
-		int lastIdx = readers.size() - 1;
-		DfsReftable last = stack.files().get(lastIdx);
-		DfsPackDescription desc = last.getPackDescription();
-		if (desc.getPackSource() != PackSource.INSERT
-				|| !packOnlyContainsReftable(desc)) {
-			return false;
-		}
-
-		Reftable table = readers.get(lastIdx);
-		int bs = cfg.getRefBlockSize();
-		return table instanceof ReftableReader
-				&& ((ReftableReader) table).size() <= 3 * bs;
-	}
-
-	private ReftableWriter.Stats compactTopOfStack(OutputStream out,
-			ReftableConfig cfg, byte[] newTable) throws IOException {
-		List<Reftable> stack = refdb.stack().readers();
-		Reftable last = stack.get(stack.size() - 1);
-
-		List<Reftable> tables = new ArrayList<>(2);
-		tables.add(last);
-		tables.add(new ReftableReader(BlockSource.from(newTable)));
-
-		ReftableCompactor compactor = new ReftableCompactor();
-		compactor.setConfig(cfg);
-		compactor.setIncludeDeletes(true);
-		compactor.addAll(tables);
-		compactor.compact(out);
-		return compactor.getStats();
-	}
-
-	private Set<DfsPackDescription> toPruneTopOfStack() throws IOException {
-		List<DfsReftable> stack = refdb.stack().files();
-		DfsReftable last = stack.get(stack.size() - 1);
-		return Collections.singleton(last.getPackDescription());
-	}
-
-	private boolean packOnlyContainsReftable(DfsPackDescription desc) {
-		for (PackExt ext : PackExt.values()) {
-			if (ext != REFTABLE && desc.hasFileExt(ext)) {
-				return false;
-			}
-		}
-		return true;
-	}
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
index 1f6b20a..db5b127 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
@@ -60,7 +60,7 @@
 import org.eclipse.jgit.util.FS;
 
 abstract class FileObjectDatabase extends ObjectDatabase {
-	static enum InsertLooseObjectResult {
+	enum InsertLooseObjectResult {
 		INSERTED, EXISTS_PACKED, EXISTS_LOOSE, FAILURE;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
new file mode 100644
index 0000000..e5ffd77
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
@@ -0,0 +1,669 @@
+/*
+ * 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.file;
+
+import static org.eclipse.jgit.lib.Ref.UNDEFINED_UPDATE_INDEX;
+import static org.eclipse.jgit.lib.Ref.Storage.NEW;
+import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.events.RefsChangedEvent;
+import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
+import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
+import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
+import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.RefList;
+import org.eclipse.jgit.util.RefMap;
+
+/**
+ * Implements RefDatabase using reftable for storage.
+ *
+ * This class is threadsafe.
+ */
+public class FileReftableDatabase extends RefDatabase {
+	private final ReftableDatabase reftableDatabase;
+
+	private final FileRepository fileRepository;
+
+	private final FileReftableStack reftableStack;
+
+	FileReftableDatabase(FileRepository repo) throws IOException {
+		this(repo, new File(new File(repo.getDirectory(), Constants.REFTABLE),
+				Constants.TABLES_LIST));
+	}
+
+	FileReftableDatabase(FileRepository repo, File refstackName) throws IOException {
+		this.fileRepository = repo;
+		this.reftableStack = new FileReftableStack(refstackName,
+			new File(fileRepository.getDirectory(), Constants.REFTABLE),
+			() -> fileRepository.fireEvent(new RefsChangedEvent()),
+			() -> fileRepository.getConfig());
+		this.reftableDatabase = new ReftableDatabase() {
+
+			@Override
+			public MergedReftable openMergedReftable() throws IOException {
+				return reftableStack.getMergedReftable();
+			}
+		};
+	}
+
+	ReflogReader getReflogReader(String refname) throws IOException {
+		return reftableDatabase.getReflogReader(refname);
+	}
+
+	/**
+	 * @param repoDir
+	 * @return whether the given repo uses reftable for refdb storage.
+	 */
+	public static boolean isReftable(File repoDir) {
+		return new File(repoDir, Constants.REFTABLE).isDirectory();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean hasFastTipsWithSha1() throws IOException {
+		return reftableDatabase.hasFastTipsWithSha1();
+	}
+
+	/**
+	 * Runs a full compaction for GC purposes.
+	 * @throws IOException on I/O errors
+	 */
+	public void compactFully() throws IOException {
+		reftableDatabase.getLock().lock();
+		try {
+			reftableStack.compactFully();
+			reftableDatabase.clearCache();
+		} finally {
+			reftableDatabase.getLock().unlock();
+		}
+	}
+
+	private ReentrantLock getLock() {
+		return reftableDatabase.getLock();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean performsAtomicTransactions() {
+		return true;
+	}
+
+	/** {@inheritDoc} */
+	@NonNull
+	@Override
+	public BatchRefUpdate newBatchUpdate() {
+		return new FileReftableBatchRefUpdate(this, fileRepository);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public RefUpdate newUpdate(String refName, boolean detach)
+			throws IOException {
+		boolean detachingSymbolicRef = false;
+		Ref ref = exactRef(refName);
+
+		if (ref == null) {
+			ref = new ObjectIdRef.Unpeeled(NEW, refName, null);
+		} else {
+			detachingSymbolicRef = detach && ref.isSymbolic();
+		}
+
+		RefUpdate update = new FileReftableRefUpdate(ref);
+		if (detachingSymbolicRef) {
+			update.setDetachingSymbolicRef();
+		}
+		return update;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Ref exactRef(String name) throws IOException {
+		return reftableDatabase.exactRef(name);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<Ref> getRefs() throws IOException {
+		return super.getRefs();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public Map<String, Ref> getRefs(String prefix) throws IOException {
+		List<Ref> refs = reftableDatabase.getRefsByPrefix(prefix);
+		RefList.Builder<Ref> builder = new RefList.Builder<>(refs.size());
+		for (Ref r : refs) {
+			builder.add(r);
+		}
+		return new RefMap(prefix, builder.toRefList(), RefList.emptyList(),
+				RefList.emptyList());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<Ref> getAdditionalRefs() throws IOException {
+		return Collections.emptyList();
+	}
+
+	/** {@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), hasVersioning());
+
+	}
+
+	private Ref doPeel(Ref leaf) throws IOException {
+		try (RevWalk rw = new RevWalk(fileRepository)) {
+			RevObject obj = rw.parseAny(leaf.getObjectId());
+			if (obj instanceof RevTag) {
+				return new ObjectIdRef.PeeledTag(leaf.getStorage(),
+						leaf.getName(), leaf.getObjectId(), rw.peel(obj).copy(),
+						hasVersioning() ? leaf.getUpdateIndex()
+								: UNDEFINED_UPDATE_INDEX);
+			}
+			return new ObjectIdRef.PeeledNonTag(leaf.getStorage(),
+					leaf.getName(), leaf.getObjectId(),
+					hasVersioning() ? leaf.getUpdateIndex()
+							: UNDEFINED_UPDATE_INDEX);
+
+		}
+	}
+
+	private static Ref recreate(Ref old, Ref leaf, boolean hasVersioning) {
+		if (old.isSymbolic()) {
+			Ref dst = recreate(old.getTarget(), leaf, hasVersioning);
+			return new SymbolicRef(old.getName(), dst,
+					hasVersioning ? old.getUpdateIndex()
+							: UNDEFINED_UPDATE_INDEX);
+		}
+		return leaf;
+	}
+
+	private class FileRefRename extends RefRename {
+		FileRefRename(RefUpdate src, RefUpdate dst) {
+			super(src, dst);
+		}
+
+		void writeRename(ReftableWriter w) throws IOException {
+			long idx = reftableDatabase.nextUpdateIndex();
+			w.setMinUpdateIndex(idx).setMaxUpdateIndex(idx).begin();
+			List<Ref> refs = new ArrayList<>(3);
+
+			Ref dest = destination.getRef();
+			Ref head = exactRef(Constants.HEAD);
+			if (head != null && head.isSymbolic()
+					&& head.getLeaf().getName().equals(source.getName())) {
+				head = new SymbolicRef(Constants.HEAD, dest, idx);
+				refs.add(head);
+			}
+
+			ObjectId objId = source.getRef().getObjectId();
+
+			// XXX should we check if the source is a Tag vs. NonTag?
+			refs.add(new ObjectIdRef.PeeledNonTag(Ref.Storage.NEW,
+					destination.getName(), objId));
+			refs.add(new ObjectIdRef.Unpeeled(Ref.Storage.NEW, source.getName(),
+					null));
+
+			w.sortAndWriteRefs(refs);
+			PersonIdent who = destination.getRefLogIdent();
+			if (who == null) {
+				who = new PersonIdent(fileRepository);
+			}
+
+			if (!destination.getRefLogMessage().isEmpty()) {
+				List<String> refnames = refs.stream().map(r -> r.getName())
+						.collect(Collectors.toList());
+				Collections.sort(refnames);
+				for (String s : refnames) {
+					ObjectId old = (Constants.HEAD.equals(s)
+							|| s.equals(source.getName())) ? objId
+									: ObjectId.zeroId();
+					ObjectId newId = (Constants.HEAD.equals(s)
+							|| s.equals(destination.getName())) ? objId
+									: ObjectId.zeroId();
+
+					w.writeLog(s, idx, who, old, newId,
+							destination.getRefLogMessage());
+				}
+			}
+		}
+
+		@Override
+		protected RefUpdate.Result doRename() throws IOException {
+			Ref src = exactRef(source.getName());
+			if (exactRef(destination.getName()) != null || src == null
+					|| !source.getOldObjectId().equals(src.getObjectId())) {
+				return RefUpdate.Result.LOCK_FAILURE;
+			}
+
+			if (src.isSymbolic()) {
+				// We could support this, but this is easier and compatible.
+				return RefUpdate.Result.IO_FAILURE;
+			}
+
+			if (!addReftable(this::writeRename)) {
+				return RefUpdate.Result.LOCK_FAILURE;
+			}
+
+			return RefUpdate.Result.RENAMED;
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public RefRename newRename(String fromName, String toName)
+			throws IOException {
+		RefUpdate src = newUpdate(fromName, true);
+		RefUpdate dst = newUpdate(toName, true);
+		return new FileRefRename(src, dst);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean isNameConflicting(String name) throws IOException {
+		return reftableDatabase.isNameConflicting(name, new TreeSet<>(),
+				new HashSet<>());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void close() {
+		reftableStack.close();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void create() throws IOException {
+		FileUtils.mkdir(
+				new File(fileRepository.getDirectory(), Constants.REFTABLE),
+				true);
+	}
+
+	private boolean addReftable(FileReftableStack.Writer w) throws IOException {
+		if (!reftableStack.addReftable(w)) {
+			reftableStack.reload();
+			reftableDatabase.clearCache();
+			return false;
+		}
+		reftableDatabase.clearCache();
+
+		return true;
+	}
+
+	private class FileReftableBatchRefUpdate extends ReftableBatchRefUpdate {
+		FileReftableBatchRefUpdate(FileReftableDatabase db,
+				Repository repository) {
+			super(db, db.reftableDatabase, db.getLock(), repository);
+		}
+
+		@Override
+		protected void applyUpdates(List<Ref> newRefs,
+				List<ReceiveCommand> pending) throws IOException {
+			if (!addReftable(rw -> write(rw, newRefs, pending))) {
+				for (ReceiveCommand c : pending) {
+					if (c.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+						c.setResult(RefUpdate.Result.LOCK_FAILURE);
+					}
+				}
+			}
+		}
+	}
+
+	private class FileReftableRefUpdate extends RefUpdate {
+		FileReftableRefUpdate(Ref ref) {
+			super(ref);
+		}
+
+		@Override
+		protected RefDatabase getRefDatabase() {
+			return FileReftableDatabase.this;
+		}
+
+		@Override
+		protected Repository getRepository() {
+			return FileReftableDatabase.this.fileRepository;
+		}
+
+		@Override
+		protected void unlock() {
+			// nop.
+		}
+
+		private RevWalk rw;
+
+		private Ref dstRef;
+
+		@Override
+		public Result update(RevWalk walk) throws IOException {
+			try {
+				rw = walk;
+				return super.update(walk);
+			} finally {
+				rw = null;
+			}
+		}
+
+		@Override
+		protected boolean tryLock(boolean deref) throws IOException {
+			dstRef = getRef();
+			if (deref) {
+				dstRef = dstRef.getLeaf();
+			}
+
+			Ref derefed = exactRef(dstRef.getName());
+			if (derefed != null) {
+				setOldObjectId(derefed.getObjectId());
+			}
+
+			return true;
+		}
+
+		void writeUpdate(ReftableWriter w) throws IOException {
+			Ref newRef = null;
+			if (rw != null && !ObjectId.zeroId().equals(getNewObjectId())) {
+				RevObject obj = rw.parseAny(getNewObjectId());
+				if (obj instanceof RevTag) {
+					newRef = new ObjectIdRef.PeeledTag(Ref.Storage.PACKED,
+							dstRef.getName(), getNewObjectId(),
+							rw.peel(obj).copy());
+				}
+			}
+			if (newRef == null) {
+				newRef = new ObjectIdRef.PeeledNonTag(Ref.Storage.PACKED,
+						dstRef.getName(), getNewObjectId());
+			}
+
+			long idx = reftableDatabase.nextUpdateIndex();
+			w.setMinUpdateIndex(idx).setMaxUpdateIndex(idx).begin()
+					.writeRef(newRef);
+
+			ObjectId oldId = getOldObjectId();
+			if (oldId == null) {
+				oldId = ObjectId.zeroId();
+			}
+			w.writeLog(dstRef.getName(), idx, getRefLogIdent(), oldId,
+					getNewObjectId(), getRefLogMessage());
+		}
+
+		@Override
+		public PersonIdent getRefLogIdent() {
+			PersonIdent who = super.getRefLogIdent();
+			if (who == null) {
+				who = new PersonIdent(getRepository());
+			}
+			return who;
+		}
+
+		void writeDelete(ReftableWriter w) throws IOException {
+			Ref newRef = new ObjectIdRef.Unpeeled(Ref.Storage.NEW,
+					dstRef.getName(), null);
+			long idx = reftableDatabase.nextUpdateIndex();
+			w.setMinUpdateIndex(idx).setMaxUpdateIndex(idx).begin()
+					.writeRef(newRef);
+
+			ObjectId oldId = ObjectId.zeroId();
+			Ref old = exactRef(dstRef.getName());
+			if (old != null) {
+				old = old.getLeaf();
+				if (old.getObjectId() != null) {
+					oldId = old.getObjectId();
+				}
+			}
+
+			w.writeLog(dstRef.getName(), idx, getRefLogIdent(), oldId,
+					ObjectId.zeroId(), getRefLogMessage());
+		}
+
+		@Override
+		protected Result doUpdate(Result desiredResult) throws IOException {
+			if (isRefLogIncludingResult()) {
+				setRefLogMessage(
+						getRefLogMessage() + ": " + desiredResult.toString(), //$NON-NLS-1$
+						false);
+			}
+
+			if (!addReftable(this::writeUpdate)) {
+				return Result.LOCK_FAILURE;
+			}
+
+			return desiredResult;
+		}
+
+		@Override
+		protected Result doDelete(Result desiredResult) throws IOException {
+
+			if (isRefLogIncludingResult()) {
+				setRefLogMessage(
+						getRefLogMessage() + ": " + desiredResult.toString(), //$NON-NLS-1$
+						false);
+			}
+
+			if (!addReftable(this::writeDelete)) {
+				return Result.LOCK_FAILURE;
+			}
+
+			return desiredResult;
+		}
+
+		void writeLink(ReftableWriter w) throws IOException {
+			long idx = reftableDatabase.nextUpdateIndex();
+			w.setMinUpdateIndex(idx).setMaxUpdateIndex(idx).begin()
+					.writeRef(dstRef);
+
+			ObjectId beforeId = ObjectId.zeroId();
+			Ref before = exactRef(dstRef.getName());
+			if (before != null) {
+				before = before.getLeaf();
+				if (before.getObjectId() != null) {
+					beforeId = before.getObjectId();
+				}
+			}
+
+			Ref after = dstRef.getLeaf();
+			ObjectId afterId = ObjectId.zeroId();
+			if (after.getObjectId() != null) {
+				afterId = after.getObjectId();
+			}
+
+			w.writeLog(dstRef.getName(), idx, getRefLogIdent(), beforeId,
+					afterId, getRefLogMessage());
+		}
+
+		@Override
+		protected Result doLink(String target) throws IOException {
+			if (isRefLogIncludingResult()) {
+				setRefLogMessage(
+						getRefLogMessage() + ": " + Result.FORCED.toString(), //$NON-NLS-1$
+						false);
+			}
+
+			boolean exists = exactRef(getName()) != null;
+			dstRef = new SymbolicRef(getName(),
+					new ObjectIdRef.Unpeeled(Ref.Storage.NEW, target, null),
+					reftableDatabase.nextUpdateIndex());
+
+			if (!addReftable(this::writeLink)) {
+				return Result.LOCK_FAILURE;
+			}
+			// XXX unclear if we should support FORCED here. Baseclass says
+			// NEW is OK ?
+			return exists ? Result.FORCED : Result.NEW;
+		}
+	}
+
+	private static void writeConvertTable(Repository repo, ReftableWriter w,
+			boolean writeLogs) throws IOException {
+		int size = 0;
+		List<Ref> refs = repo.getRefDatabase().getRefs();
+		if (writeLogs) {
+			for (Ref r : refs) {
+				ReflogReader rlr = repo.getReflogReader(r.getName());
+				if (rlr != null) {
+					size = Math.max(rlr.getReverseEntries().size(), size);
+				}
+			}
+		}
+		// We must use 1 here, nextUpdateIndex() on the empty stack is 1.
+		w.setMinUpdateIndex(1).setMaxUpdateIndex(size + 1).begin();
+
+		// The spec says to write the logs in the first table, and put refs in a
+		// separate table, but this complicates the compaction (when we can we drop
+		// deletions? Can we compact the .log table and the .ref table together?)
+		try (RevWalk rw = new RevWalk(repo)) {
+			List<Ref> toWrite = new ArrayList<>(refs.size());
+			for (Ref r : refs) {
+				toWrite.add(refForWrite(rw, r));
+			}
+			w.sortAndWriteRefs(toWrite);
+		}
+
+		if (writeLogs) {
+			for (Ref r : refs) {
+				long idx = size;
+				ReflogReader reader = repo.getReflogReader(r.getName());
+				if (reader == null) {
+					continue;
+				}
+				for (ReflogEntry e : reader.getReverseEntries()) {
+					w.writeLog(r.getName(), idx, e.getWho(), e.getOldId(),
+							e.getNewId(), e.getComment());
+					idx--;
+				}
+			}
+		}
+	}
+
+	private static Ref refForWrite(RevWalk rw, Ref r) throws IOException {
+		if (r.isSymbolic()) {
+			return new SymbolicRef(r.getName(), new ObjectIdRef.Unpeeled(NEW,
+					r.getTarget().getName(), null));
+		}
+		ObjectId newId = r.getObjectId();
+		RevObject peel = null;
+		try {
+			RevObject obj = rw.parseAny(newId);
+			if (obj instanceof RevTag) {
+				peel = rw.peel(obj);
+			}
+		} catch (MissingObjectException e) {
+			/* ignore this error and copy the dangling object ID into reftable too. */
+		}
+		if (peel != null) {
+				return new ObjectIdRef.PeeledTag(PACKED, r.getName(), newId,
+						peel.copy());
+			}
+
+		return new ObjectIdRef.PeeledNonTag(PACKED, r.getName(), newId);
+	}
+
+	/**
+	 * @param repo
+	 *            the repository
+	 * @param writeLogs
+	 *            whether to write reflogs
+	 * @return a reftable based RefDB from an existing repository.
+	 * @throws IOException
+	 *             on IO error
+	 */
+	public static FileReftableDatabase convertFrom(FileRepository repo,
+			boolean writeLogs) throws IOException {
+		FileReftableDatabase newDb = null;
+		File reftableList = null;
+		try {
+			File reftableDir = new File(repo.getDirectory(),
+					Constants.REFTABLE);
+			reftableList = new File(reftableDir, Constants.TABLES_LIST);
+			if (!reftableDir.isDirectory()) {
+				reftableDir.mkdir();
+			}
+
+			try (FileReftableStack stack = new FileReftableStack(reftableList,
+					reftableDir, null, () -> repo.getConfig())) {
+				stack.addReftable(rw -> writeConvertTable(repo, rw, writeLogs));
+			}
+			reftableList = null;
+		} finally {
+			if (reftableList != null) {
+				reftableList.delete();
+			}
+		}
+		return newDb;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
new file mode 100644
index 0000000..68d82c2
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
@@ -0,0 +1,766 @@
+/*
+ * 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.file;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.LockFailedException;
+import org.eclipse.jgit.internal.storage.io.BlockSource;
+import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
+import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
+import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
+import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
+import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.util.FileUtils;
+
+/**
+ * A mutable stack of reftables on local filesystem storage. Not thread-safe.
+ * This is an AutoCloseable because this object owns the file handles to the
+ * open reftables.
+ */
+public class FileReftableStack implements AutoCloseable {
+	private static class StackEntry {
+
+		String name;
+
+		ReftableReader reftableReader;
+	}
+
+	private MergedReftable mergedReftable;
+
+	private List<StackEntry> stack;
+
+	private long lastNextUpdateIndex;
+
+	private final File stackPath;
+
+	private final File reftableDir;
+
+	private final Runnable onChange;
+
+	private final Supplier<Config> configSupplier;
+
+	// Used for stats & testing.
+	static class CompactionStats {
+
+		long tables;
+
+		long bytes;
+
+		int attempted;
+
+		int failed;
+
+		long refCount;
+
+		long logCount;
+
+		CompactionStats() {
+			tables = 0;
+			bytes = 0;
+			attempted = 0;
+			failed = 0;
+			logCount = 0;
+			refCount = 0;
+		}
+	}
+
+	private final CompactionStats stats;
+
+	/**
+	 * Creates a stack corresponding to the list of reftables in the argument
+	 *
+	 * @param stackPath
+	 *            the filename for the stack.
+	 * @param reftableDir
+	 *            the dir holding the tables.
+	 * @param onChange
+	 *            hook to call if we notice a new write
+	 * @param configSupplier
+	 *            Config supplier
+	 * @throws IOException
+	 *             on I/O problems
+	 */
+	public FileReftableStack(File stackPath, File reftableDir,
+			@Nullable Runnable onChange, Supplier<Config> configSupplier)
+			throws IOException {
+		this.stackPath = stackPath;
+		this.reftableDir = reftableDir;
+		this.stack = new ArrayList<>();
+		this.configSupplier = configSupplier;
+		this.onChange = onChange;
+
+		// skip event notification
+		lastNextUpdateIndex = 0;
+		reload();
+
+		stats = new CompactionStats();
+	}
+
+	CompactionStats getStats() {
+		return stats;
+	}
+
+	/** Thrown if the update indices in the stack are not monotonic */
+	public static class ReftableNumbersNotIncreasingException
+			extends RuntimeException {
+		private static final long serialVersionUID = 1L;
+
+		String name;
+
+		long lastMax;
+
+		long min;
+
+		ReftableNumbersNotIncreasingException(String name, long lastMax,
+				long min) {
+			this.name = name;
+			this.lastMax = lastMax;
+			this.min = min;
+		}
+	}
+
+	/**
+	 * Reloads the stack, potentially reusing opened reftableReaders.
+	 *
+	 * @param names
+	 *            holds the names of the tables to load.
+	 * @throws FileNotFoundException
+	 *             load must be retried.
+	 * @throws IOException
+	 *             on other IO errors.
+	 */
+	private void reloadOnce(List<String> names)
+			throws IOException, FileNotFoundException {
+		Map<String, ReftableReader> current = stack.stream()
+				.collect(Collectors.toMap(e -> e.name, e -> e.reftableReader));
+
+		List<ReftableReader> newTables = new ArrayList<>();
+		List<StackEntry> newStack = new ArrayList<>(stack.size() + 1);
+		try {
+			ReftableReader last = null;
+			for (String name : names) {
+				StackEntry entry = new StackEntry();
+				entry.name = name;
+
+				ReftableReader t = null;
+				if (current.containsKey(name)) {
+					t = current.remove(name);
+				} else {
+					File subtable = new File(reftableDir, name);
+					FileInputStream is;
+
+					is = new FileInputStream(subtable);
+
+					t = new ReftableReader(BlockSource.from(is));
+					newTables.add(t);
+				}
+
+				if (last != null) {
+					// TODO: move this to MergedReftable
+					if (last.maxUpdateIndex() >= t.minUpdateIndex()) {
+						throw new ReftableNumbersNotIncreasingException(name,
+								last.maxUpdateIndex(), t.minUpdateIndex());
+					}
+				}
+				last = t;
+
+				entry.reftableReader = t;
+				newStack.add(entry);
+			}
+			// survived without exceptions: swap in new stack, and close
+			// dangling tables.
+			stack = newStack;
+			newTables.clear();
+
+			current.values().forEach(r -> {
+				try {
+					r.close();
+				} catch (IOException e) {
+					throw new AssertionError(e);
+				}
+			});
+		} finally {
+			newTables.forEach(t -> {
+				try {
+					t.close();
+				} catch (IOException ioe) {
+					// reader close should not generate errors.
+					throw new AssertionError(ioe);
+				}
+			});
+		}
+	}
+
+	void reload() throws IOException {
+		// Try for 2.5 seconds.
+		long deadline = System.currentTimeMillis() + 2500;
+		// A successful reftable transaction is 2 atomic file writes
+		// (open, write, close, rename), which a fast Linux system should be
+		// able to do in about ~200us. So 1 ms should be ample time.
+		long min = 1;
+		long max = 1000;
+		long delay = 0;
+		boolean success = false;
+
+		// Don't check deadline for the first 3 retries, so we can step with a
+		// debugger without worrying about deadlines.
+		int tries = 0;
+		while (tries < 3 || System.currentTimeMillis() < deadline) {
+			List<String> names = readTableNames();
+			tries++;
+			try {
+				reloadOnce(names);
+				success = true;
+				break;
+			} catch (FileNotFoundException e) {
+				List<String> changed = readTableNames();
+				if (changed.equals(names)) {
+					throw e;
+				}
+			}
+
+			delay = FileUtils.delay(delay, min, max);
+			try {
+				Thread.sleep(delay);
+			} catch (InterruptedException e) {
+				Thread.currentThread().interrupt();
+				throw new RuntimeException(e);
+			}
+		}
+
+		if (!success) {
+			throw new LockFailedException(stackPath);
+		}
+
+		mergedReftable = new MergedReftable(stack.stream()
+				.map(x -> x.reftableReader).collect(Collectors.toList()));
+		long curr = nextUpdateIndex();
+		if (lastNextUpdateIndex > 0 && lastNextUpdateIndex != curr
+				&& onChange != null) {
+			onChange.run();
+		}
+		lastNextUpdateIndex = curr;
+	}
+
+	/**
+	 * @return the merged reftable
+	 */
+	public MergedReftable getMergedReftable() {
+		return mergedReftable;
+	}
+
+	/**
+	 * Writer is a callable that writes data to a reftable under construction.
+	 * It should set the min/max update index, and then write refs and/or logs.
+	 * It should not call finish() on the writer.
+	 */
+	public interface Writer {
+		/**
+		 * Write data to reftable
+		 *
+		 * @param w
+		 *            writer to use
+		 * @throws IOException
+		 */
+		void call(ReftableWriter w) throws IOException;
+	}
+
+	private List<String> readTableNames() throws IOException {
+		List<String> names = new ArrayList<>(stack.size() + 1);
+
+		try (BufferedReader br = new BufferedReader(
+				new InputStreamReader(new FileInputStream(stackPath), UTF_8))) {
+			String line;
+			while ((line = br.readLine()) != null) {
+				if (!line.isEmpty()) {
+					names.add(line);
+				}
+			}
+		} catch (FileNotFoundException e) {
+			// file isn't there: empty repository.
+		}
+		return names;
+	}
+
+	/**
+	 * @return true if the on-disk file corresponds to the in-memory data.
+	 * @throws IOException
+	 *             on IO problem
+	 */
+	boolean isUpToDate() throws IOException {
+		// We could use FileSnapshot to avoid reading the file, but the file is
+		// small so it's probably a minor optimization.
+		try {
+			List<String> names = readTableNames();
+			if (names.size() != stack.size()) {
+				return false;
+			}
+			for (int i = 0; i < names.size(); i++) {
+				if (!names.get(i).equals(stack.get(i).name)) {
+					return false;
+				}
+			}
+		} catch (FileNotFoundException e) {
+			return stack.isEmpty();
+		}
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void close() {
+		for (StackEntry entry : stack) {
+			try {
+				entry.reftableReader.close();
+			} catch (Exception e) {
+				// we are reading; this should never fail.
+				throw new AssertionError(e);
+			}
+		}
+	}
+
+	private long nextUpdateIndex() throws IOException {
+		return stack.size() > 0
+				? stack.get(stack.size() - 1).reftableReader.maxUpdateIndex()
+						+ 1
+				: 1;
+	}
+
+	private String filename(long low, long high) {
+		return String.format("%012x-%012x", //$NON-NLS-1$
+				Long.valueOf(low), Long.valueOf(high));
+	}
+
+	/**
+	 * Tries to add a new reftable to the stack. Returns true if it succeeded,
+	 * or false if there was a lock failure, due to races with other processes.
+	 * This is package private so FileReftableDatabase can call into here.
+	 *
+	 * @param w
+	 *            writer to write data to a reftable under construction
+	 * @return true if the transaction was successful.
+	 * @throws IOException
+	 *             on I/O problems
+	 */
+	@SuppressWarnings("nls")
+	public boolean addReftable(Writer w) throws IOException {
+		LockFile lock = new LockFile(stackPath);
+		try {
+			if (!lock.lockForAppend()) {
+				return false;
+			}
+			if (!isUpToDate()) {
+				return false;
+			}
+
+			String fn = filename(nextUpdateIndex(), nextUpdateIndex());
+
+			File tmpTable = File.createTempFile(fn + "_", ".ref",
+					stackPath.getParentFile());
+
+			ReftableWriter.Stats s;
+			try (FileOutputStream fos = new FileOutputStream(tmpTable)) {
+				ReftableWriter rw = new ReftableWriter(reftableConfig(), fos);
+				w.call(rw);
+				rw.finish();
+				s = rw.getStats();
+			}
+
+			if (s.minUpdateIndex() < nextUpdateIndex()) {
+				return false;
+			}
+
+			// The spec says to name log-only files with .log, which is somewhat
+			// pointless given compaction, but we do so anyway.
+			fn += s.refCount() > 0 ? ".ref" : ".log";
+			File dest = new File(reftableDir, fn);
+
+			FileUtils.rename(tmpTable, dest, StandardCopyOption.ATOMIC_MOVE);
+			lock.write((fn + "\n").getBytes(UTF_8));
+			if (!lock.commit()) {
+				FileUtils.delete(dest);
+				return false;
+			}
+
+			reload();
+
+			autoCompact();
+		} finally {
+			lock.unlock();
+		}
+		return true;
+	}
+
+	private ReftableConfig reftableConfig() {
+		return new ReftableConfig(configSupplier.get());
+	}
+
+	/**
+	 * Write the reftable for the given range into a temp file.
+	 *
+	 * @param first
+	 *            index of first stack entry to be written
+	 * @param last
+	 *            index of last stack entry to be written
+	 * @return the file holding the replacement table.
+	 * @throws IOException
+	 *             on I/O problem
+	 */
+	private File compactLocked(int first, int last) throws IOException {
+		String fn = filename(first, last);
+
+		File tmpTable = File.createTempFile(fn + "_", ".ref", //$NON-NLS-1$//$NON-NLS-2$
+				stackPath.getParentFile());
+		try (FileOutputStream fos = new FileOutputStream(tmpTable)) {
+			ReftableCompactor c = new ReftableCompactor(fos)
+					.setConfig(reftableConfig())
+					.setIncludeDeletes(first > 0);
+
+			List<ReftableReader> compactMe = new ArrayList<>();
+			long totalBytes = 0;
+			for (int i = first; i <= last; i++) {
+				compactMe.add(stack.get(i).reftableReader);
+				totalBytes += stack.get(i).reftableReader.size();
+			}
+			c.addAll(compactMe);
+
+			c.compact();
+
+			// Even though the compaction did not definitely succeed, we keep
+			// tally here as we've expended the effort.
+			stats.bytes += totalBytes;
+			stats.tables += first - last + 1;
+			stats.attempted++;
+			stats.refCount += c.getStats().refCount();
+			stats.logCount += c.getStats().logCount();
+		}
+
+		return tmpTable;
+	}
+
+	/**
+	 * Compacts a range of the stack, following the file locking protocol
+	 * documented in the spec.
+	 *
+	 * @param first
+	 *            index of first stack entry to be considered in compaction
+	 * @param last
+	 *            index of last stack entry to be considered in compaction
+	 * @return true if a compaction was successfully applied.
+	 * @throws IOException
+	 *             on I/O problem
+	 */
+	boolean compactRange(int first, int last) throws IOException {
+		if (first >= last) {
+			return true;
+		}
+		LockFile lock = new LockFile(stackPath);
+
+		File tmpTable = null;
+		List<LockFile> subtableLocks = new ArrayList<>();
+
+		try {
+			if (!lock.lock()) {
+				return false;
+			}
+			if (!isUpToDate()) {
+				return false;
+			}
+
+			List<File> deleteOnSuccess = new ArrayList<>();
+			for (int i = first; i <= last; i++) {
+				File f = new File(reftableDir, stack.get(i).name);
+				LockFile lf = new LockFile(f);
+				if (!lf.lock()) {
+					return false;
+				}
+				subtableLocks.add(lf);
+				deleteOnSuccess.add(f);
+			}
+
+			lock.unlock();
+			lock = null;
+
+			tmpTable = compactLocked(first, last);
+
+			lock = new LockFile(stackPath);
+			if (!lock.lock()) {
+				return false;
+			}
+			if (!isUpToDate()) {
+				return false;
+			}
+
+			String fn = filename(
+					stack.get(first).reftableReader.minUpdateIndex(),
+					stack.get(last).reftableReader.maxUpdateIndex());
+
+			// The spec suggests to use .log for log-only tables, and collect
+			// all log entries in a single file at the bottom of the stack. That would
+			// require supporting overlapping ranges for the different tables. For the
+			// sake of simplicity, we simply ignore this and always produce a log +
+			// ref combined table.
+			fn += ".ref"; //$NON-NLS-1$
+			File dest = new File(reftableDir, fn);
+
+			FileUtils.rename(tmpTable, dest, StandardCopyOption.ATOMIC_MOVE);
+			tmpTable = null;
+
+			StringBuilder sb = new StringBuilder();
+
+			for (int i = 0; i < first; i++) {
+				sb.append(stack.get(i).name + "\n"); //$NON-NLS-1$
+			}
+			sb.append(fn + "\n"); //$NON-NLS-1$
+			for (int i = last + 1; i < stack.size(); i++) {
+				sb.append(stack.get(i).name + "\n"); //$NON-NLS-1$
+			}
+
+			lock.write(sb.toString().getBytes(UTF_8));
+			if (!lock.commit()) {
+				dest.delete();
+				return false;
+			}
+
+			for (File f : deleteOnSuccess) {
+				Files.delete(f.toPath());
+			}
+
+			reload();
+			return true;
+		} finally {
+			if (tmpTable != null) {
+				tmpTable.delete();
+			}
+			for (LockFile lf : subtableLocks) {
+				lf.unlock();
+			}
+			if (lock != null) {
+				lock.unlock();
+			}
+		}
+	}
+
+	/**
+	 * Calculate an approximate log2.
+	 *
+	 * @param sz
+	 * @return log2
+	 */
+	static int log(long sz) {
+		long base = 2;
+		if (sz <= 0) {
+			throw new IllegalArgumentException("log2 negative"); //$NON-NLS-1$
+		}
+		int l = 0;
+		while (sz > 0) {
+			l++;
+			sz /= base;
+		}
+
+		return l - 1;
+	}
+
+	/**
+	 * A segment is a consecutive list of reftables of the same approximate
+	 * size.
+	 */
+	static class Segment {
+		// the approximate log_2 of the size.
+		int log;
+
+		// The total bytes in this segment
+		long bytes;
+
+		int start;
+
+		int end; // exclusive.
+
+		int size() {
+			return end - start;
+		}
+
+		Segment(int start, int end, int log, long bytes) {
+			this.log = log;
+			this.start = start;
+			this.end = end;
+			this.bytes = bytes;
+		}
+
+		Segment() {
+			this(0, 0, 0, 0);
+		}
+
+		@Override
+		public int hashCode() {
+			return 0; // appease error-prone
+		}
+
+		@Override
+		public boolean equals(Object other) {
+			Segment o = (Segment) other;
+			return o.bytes == bytes && o.log == log && o.start == start
+					&& o.end == end;
+		}
+
+		@SuppressWarnings("boxing")
+		@Override
+		public String toString() {
+			return String.format("{ [%d,%d) l=%d sz=%d }", start, end, log, //$NON-NLS-1$
+					bytes);
+		}
+	}
+
+	static List<Segment> segmentSizes(long[] sizes) {
+		List<Segment> segments = new ArrayList<>();
+		Segment cur = new Segment();
+		for (int i = 0; i < sizes.length; i++) {
+			int l = log(sizes[i]);
+			if (l != cur.log && cur.bytes > 0) {
+				segments.add(cur);
+				cur = new Segment();
+				cur.start = i;
+				cur.log = l;
+			}
+
+			cur.log = l;
+			cur.end = i + 1;
+			cur.bytes += sizes[i];
+		}
+		segments.add(cur);
+		return segments;
+	}
+
+	private static Optional<Segment> autoCompactCandidate(long[] sizes) {
+		if (sizes.length == 0) {
+			return Optional.empty();
+		}
+
+		// The cost of compaction is proportional to the size, and we want to
+		// avoid frequent large compactions. We do this by playing the game 2048
+		// here: first compact together the smallest tables if there are more
+		// than one. Then try to see if the result will be big enough to match
+		// up with next up.
+
+		List<Segment> segments = segmentSizes(sizes);
+		segments = segments.stream().filter(s -> s.size() > 1)
+				.collect(Collectors.toList());
+		if (segments.isEmpty()) {
+			return Optional.empty();
+		}
+
+		Optional<Segment> optMinSeg = segments.stream()
+				.min(Comparator.comparing(s -> Integer.valueOf(s.log)));
+		// Input is non-empty, so always present.
+		Segment smallCollected = optMinSeg.get();
+		while (smallCollected.start > 0) {
+			int prev = smallCollected.start - 1;
+			long prevSize = sizes[prev];
+			if (log(smallCollected.bytes) < log(prevSize)) {
+				break;
+			}
+			smallCollected.start = prev;
+			smallCollected.bytes += prevSize;
+		}
+
+		return Optional.of(smallCollected);
+	}
+
+	/**
+	 * Heuristically tries to compact the stack if the stack has a suitable
+	 * shape.
+	 *
+	 * @throws IOException
+	 */
+	private void autoCompact() throws IOException {
+		Optional<Segment> cand = autoCompactCandidate(tableSizes());
+		if (cand.isPresent()) {
+			if (!compactRange(cand.get().start, cand.get().end - 1)) {
+				stats.failed++;
+			}
+		}
+	}
+
+	// 68b footer, 24b header = 92.
+	private static long OVERHEAD = 91;
+
+	private long[] tableSizes() throws IOException {
+		long[] sizes = new long[stack.size()];
+		for (int i = 0; i < stack.size(); i++) {
+			// If we don't subtract the overhead, the file size isn't
+			// proportional to the number of entries. This will cause us to
+			// compact too often, which is expensive.
+			sizes[i] = stack.get(i).reftableReader.size() - OVERHEAD;
+		}
+		return sizes;
+	}
+
+	void compactFully() throws IOException {
+		if (!compactRange(0, stack.size() - 1)) {
+			stats.failed++;
+		}
+	}
+}
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 4f5f8a6..9929c46 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
@@ -46,13 +46,20 @@
 
 package org.eclipse.jgit.internal.storage.file;
 
+import static java.util.stream.Collectors.toList;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.text.MessageFormat;
 import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
@@ -68,21 +75,26 @@
 import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateRepository;
 import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
 import org.eclipse.jgit.lib.BaseRepositoryBuilder;
+import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.CoreConfig.HideDotFiles;
 import org.eclipse.jgit.lib.CoreConfig.SymLinks;
+import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.transport.ReceiveCommand;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.RawParseUtils;
@@ -121,7 +133,7 @@
 	private static final String UNNAMED = "Unnamed repository; edit this file to name it for gitweb."; //$NON-NLS-1$
 
 	private final FileBasedConfig repoConfig;
-	private final RefDatabase refs;
+	private RefDatabase refs;
 	private final ObjectDirectory objectDatabase;
 
 	private final Object snapshotLock = new Object();
@@ -197,9 +209,13 @@
 				ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
 
 		String reftype = repoConfig.getString(
-				"extensions", null, "refStorage"); //$NON-NLS-1$ //$NON-NLS-2$
+				ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
+				ConfigConstants.CONFIG_KEY_REF_STORAGE);
 		if (repositoryFormatVersion >= 1 && reftype != null) {
-			if (StringUtils.equalsIgnoreCase(reftype, "reftree")) { //$NON-NLS-1$
+			if (StringUtils.equalsIgnoreCase(reftype,
+					ConfigConstants.CONFIG_REF_STORAGE_REFTABLE)) {
+				refs = new FileReftableDatabase(this);
+			} else if (StringUtils.equalsIgnoreCase(reftype, "reftree")) { //$NON-NLS-1$
 				refs = new RefTreeDatabase(this, new RefDirectory(this));
 			} else {
 				throw new IOException(JGitText.get().unknownRepositoryFormat);
@@ -358,9 +374,8 @@
 		File directory = getDirectory();
 		if (directory != null) {
 			return directory.getPath();
-		} else {
-			throw new IllegalStateException();
 		}
+		throw new IllegalStateException();
 	}
 
 	/** {@inheritDoc} */
@@ -531,10 +546,19 @@
 	/** {@inheritDoc} */
 	@Override
 	public ReflogReader getReflogReader(String refName) throws IOException {
+		if (refs instanceof FileReftableDatabase) {
+			// Cannot use findRef: reftable stores log data for deleted or renamed
+			// branches.
+			return ((FileReftableDatabase)refs).getReflogReader(refName);
+		}
+
+		// TODO: use exactRef here, which offers more predictable and therefore preferable
+		// behavior.
 		Ref ref = findRef(refName);
-		if (ref != null)
-			return new ReflogReaderImpl(this, ref.getName());
-		return null;
+		if (ref == null) {
+			return null;
+		}
+		return new ReflogReaderImpl(this, ref.getName());
 	}
 
 	/** {@inheritDoc} */
@@ -614,4 +638,218 @@
 			throw new JGitInternalException(JGitText.get().gcFailed, e);
 		}
 	}
+
+	/**
+	 * Converts the RefDatabase from reftable to RefDirectory. This operation is
+	 * not atomic.
+	 *
+	 * @param writeLogs
+	 *            whether to write reflogs
+	 * @param backup
+	 *            whether to rename or delete the old storage files. If set to
+	 *            {@code true}, the reftable list is left in {@code refs.old},
+	 *            and the {@code reftable/} dir is left alone. If set to
+	 *            {@code false}, the {@code reftable/} dir is removed, and
+	 *            {@code refs} file is removed.
+	 * @throws IOException
+	 *             on IO problem
+	 */
+	void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException {
+		List<Ref> all = refs.getRefs();
+		File packedRefs = new File(getDirectory(), Constants.PACKED_REFS);
+		if (packedRefs.exists()) {
+			throw new IOException(MessageFormat.format(JGitText.get().fileAlreadyExists,
+				packedRefs.getName()));
+		}
+
+		File refsFile = new File(getDirectory(), "refs"); //$NON-NLS-1$
+		File refsHeadsFile = new File(refsFile, "heads");//$NON-NLS-1$
+		File headFile = new File(getDirectory(), Constants.HEAD);
+		FileReftableDatabase oldDb = (FileReftableDatabase) refs;
+
+		// Remove the dummy files that ensure compatibility with older git
+		// versions (see convertToReftable). First make room for refs/heads/
+		refsHeadsFile.delete();
+		// RefDirectory wants to create the refs/ directory from scratch, so
+		// remove that too.
+		refsFile.delete();
+		// remove HEAD so its previous invalid value doesn't cause issues.
+		headFile.delete();
+
+		// This is not atomic, but there is no way to instantiate a RefDirectory
+		// that is disconnected from the current repo.
+		RefDirectory refDir = new RefDirectory(this);
+		refs = refDir;
+		refs.create();
+
+		ReflogWriter logWriter = refDir.newLogWriter(true);
+		List<Ref> symrefs = new ArrayList<>();
+		BatchRefUpdate bru = refs.newBatchUpdate();
+		for (Ref r : all) {
+			if (r.isSymbolic()) {
+				symrefs.add(r);
+			} else {
+				bru.addCommand(new ReceiveCommand(ObjectId.zeroId(),
+						r.getObjectId(), r.getName()));
+			}
+
+			if (writeLogs) {
+				List<ReflogEntry> logs = oldDb.getReflogReader(r.getName())
+					.getReverseEntries();
+				Collections.reverse(logs);
+				for (ReflogEntry e : logs) {
+					logWriter.log(r.getName(), e);
+				}
+			}
+		}
+
+		try (RevWalk rw = new RevWalk(this)) {
+			bru.execute(rw, NullProgressMonitor.INSTANCE);
+		}
+
+		List<String> failed = new ArrayList<>();
+		for (ReceiveCommand cmd : bru.getCommands()) {
+			if (cmd.getResult() != ReceiveCommand.Result.OK) {
+				failed.add(cmd.getRefName() + ": " + cmd.getResult()); //$NON-NLS-1$
+			}
+		}
+
+		if (!failed.isEmpty()) {
+			throw new IOException(String.format("%s: %s", //$NON-NLS-1$
+					JGitText.get().failedToConvert,
+					StringUtils.join(failed, ", "))); //$NON-NLS-1$
+		}
+
+		for (Ref s : symrefs) {
+			RefUpdate up = refs.newUpdate(s.getName(), false);
+			up.setForceUpdate(true);
+			RefUpdate.Result res = up.link(s.getTarget().getName());
+			if (res != RefUpdate.Result.NEW
+					&& res != RefUpdate.Result.NO_CHANGE) {
+				throw new IOException(
+						String.format("ref %s: %s", s.getName(), res)); //$NON-NLS-1$
+			}
+		}
+
+		if (!backup) {
+			File reftableDir = new File(getDirectory(), Constants.REFTABLE);
+			FileUtils.delete(reftableDir,
+					FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS);
+		}
+		repoConfig.unset(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
+				ConfigConstants.CONFIG_KEY_REF_STORAGE);
+	}
+
+	/**
+	 * Converts the RefDatabase from RefDirectory to reftable. This operation is
+	 * not atomic.
+	 *
+	 * @param writeLogs
+	 *            whether to write reflogs
+	 * @param backup
+	 *            whether to rename or delete the old storage files. If set to
+	 *            {@code true}, the loose refs are left in {@code refs.old}, the
+	 *            packed-refs in {@code packed-refs.old} and reflogs in
+	 *            {@code refs.old/}. HEAD is left in {@code HEAD.old} and also
+	 *            {@code .log} is appended to additional refs. If set to
+	 *            {@code false}, the {@code refs/} and {@code logs/} directories
+	 *            and {@code HEAD} and additional symbolic refs are removed.
+	 * @throws IOException
+	 *             on IO problem
+	 */
+	@SuppressWarnings("nls")
+	void convertToReftable(boolean writeLogs, boolean backup)
+			throws IOException {
+		File reftableDir = new File(getDirectory(), Constants.REFTABLE);
+		File headFile = new File(getDirectory(), Constants.HEAD);
+		if (reftableDir.exists() && reftableDir.listFiles().length > 0) {
+			throw new IOException(JGitText.get().reftableDirExists);
+		}
+
+		// Ignore return value, as it is tied to temporary newRefs file.
+		FileReftableDatabase.convertFrom(this, writeLogs);
+
+		File refsFile = new File(getDirectory(), "refs");
+
+		// non-atomic: remove old data.
+		File packedRefs = new File(getDirectory(), Constants.PACKED_REFS);
+		File logsDir = new File(getDirectory(), Constants.LOGS);
+
+		List<String> additional = getRefDatabase().getAdditionalRefs().stream()
+				.map(Ref::getName).collect(toList());
+		additional.add(Constants.HEAD);
+		if (backup) {
+			FileUtils.rename(refsFile, new File(getDirectory(), "refs.old"));
+			if (packedRefs.exists()) {
+				FileUtils.rename(packedRefs, new File(getDirectory(),
+						Constants.PACKED_REFS + ".old"));
+			}
+			if (logsDir.exists()) {
+				FileUtils.rename(logsDir,
+						new File(getDirectory(), Constants.LOGS + ".old"));
+			}
+			for (String r : additional) {
+				FileUtils.rename(new File(getDirectory(), r),
+					new File(getDirectory(), r + ".old"));
+			}
+		} else {
+			FileUtils.delete(packedRefs, FileUtils.SKIP_MISSING);
+			FileUtils.delete(headFile);
+			FileUtils.delete(logsDir, FileUtils.RECURSIVE);
+			FileUtils.delete(refsFile, FileUtils.RECURSIVE);
+			for (String r : additional) {
+				new File(getDirectory(), r).delete();
+			}
+		}
+
+		FileUtils.mkdir(refsFile, true);
+
+		// By putting in a dummy HEAD, old versions of Git still detect a repo
+		// (that they can't read)
+		try (OutputStream os = new FileOutputStream(headFile)) {
+			os.write(Constants.encodeASCII("ref: refs/heads/.invalid"));
+		}
+
+		// Some tools might write directly into .git/refs/heads/BRANCH. By
+		// putting a file here, this fails spectacularly.
+		FileUtils.createNewFile(new File(refsFile, "heads"));
+
+		repoConfig.setString(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
+				ConfigConstants.CONFIG_KEY_REF_STORAGE,
+				ConfigConstants.CONFIG_REF_STORAGE_REFTABLE);
+		repoConfig.setLong(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 1);
+		repoConfig.save();
+		refs.close();
+		refs = new FileReftableDatabase(this);
+	}
+
+	/**
+	 * Converts between ref storage formats.
+	 *
+	 * @param format
+	 *            the format to convert to, either "reftable" or "refdir"
+	 * @param writeLogs
+	 *            whether to write reflogs
+	 * @param backup
+	 *            whether to make a backup of the old data
+	 * @throws IOException
+	 *             on I/O problems.
+	 */
+	@SuppressWarnings("nls")
+	public void convertRefStorage(String format, boolean writeLogs,
+			boolean backup) throws IOException {
+		if (format.equals("reftable")) { //$NON-NLS-1$
+			if (refs instanceof RefDirectory) {
+				convertToReftable(writeLogs, backup);
+			}
+		} else if (format.equals("refdir")) {//$NON-NLS-1$
+			if (refs instanceof FileReftableDatabase) {
+				convertToPackedRefs(writeLogs, backup);
+			}
+		} else {
+			throw new IOException(String.format(
+					"unknown supported ref storage format '%s'", format));
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 0ff3f61..5edcf37 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -131,7 +131,7 @@
  * adapted to FileRepositories.
  */
 public class GC {
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(GC.class);
 
 	private static final String PRUNE_EXPIRE_DEFAULT = "2.weeks.ago"; //$NON-NLS-1$
@@ -773,13 +773,26 @@
 	}
 
 	/**
-	 * Packs all non-symbolic, loose refs into packed-refs.
+	 * Pack ref storage. For a RefDirectory database, this packs all
+	 * non-symbolic, loose refs into packed-refs. For Reftable, all of the data
+	 * is compacted into a single table.
 	 *
 	 * @throws java.io.IOException
 	 */
 	public void packRefs() throws IOException {
-		Collection<Ref> refs = repo.getRefDatabase()
-				.getRefsByPrefix(Constants.R_REFS);
+		RefDatabase refDb = repo.getRefDatabase();
+		if (refDb instanceof FileReftableDatabase) {
+			// TODO: abstract this more cleanly.
+			pm.beginTask(JGitText.get().packRefs, 1);
+			try {
+				((FileReftableDatabase) refDb).compactFully();
+			} finally {
+				pm.endTask();
+			}
+			return;
+		}
+
+		Collection<Ref> refs = refDb.getRefsByPrefix(Constants.R_REFS);
 		List<String> refsToBePacked = new ArrayList<>(refs.size());
 		pm.beginTask(JGitText.get().packRefs, refs.size());
 		try {
@@ -897,7 +910,10 @@
 			throw new IOException(e);
 		}
 		prunePacked();
-		deleteEmptyRefsFolders();
+		if (repo.getRefDatabase() instanceof RefDirectory) {
+			// TODO: abstract this more cleanly.
+			deleteEmptyRefsFolders();
+		}
 		deleteOrphans();
 		deleteTempPacksIdx();
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
index 82458c1..13b9e79 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
@@ -152,11 +152,10 @@
 	boolean commit() {
 		if (nonEmpty) {
 			return lock.commit();
-		} else {
-			logFile.delete();
-			lock.unlock();
-			return true;
 		}
+		logFile.delete();
+		lock.unlock();
+		return true;
 	}
 
 	/**
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 eda4da8..d06b5a7 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
@@ -1,45 +1,12 @@
 /*
  * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2006-2021, Shawn O. Pearce <spearce@spearce.org> and others
  *
- * 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
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://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.
+ * SPDX-License-Identifier: BSD-3-Clause
  */
 
 package org.eclipse.jgit.internal.storage.file;
@@ -83,7 +50,7 @@
  * name.
  */
 public class LockFile {
-	private final static Logger LOG = LoggerFactory.getLogger(LockFile.class);
+	private static final Logger LOG = LoggerFactory.getLogger(LockFile.class);
 
 	/**
 	 * Unlock the given file.
@@ -129,13 +96,17 @@
 
 	private boolean haveLck;
 
-	FileOutputStream os;
+	private FileOutputStream os;
 
 	private boolean needSnapshot;
 
-	private boolean snapshotNoConfig;
+	private boolean fsync;
 
-	boolean fsync;
+	private boolean isAppend;
+
+	private boolean written;
+
+	private boolean snapshotNoConfig;
 
 	private FileSnapshot commitSnapshot;
 
@@ -162,6 +133,10 @@
 	 *             does not hold the lock.
 	 */
 	public boolean lock() throws IOException {
+		if (haveLck) {
+			throw new IllegalStateException(
+					MessageFormat.format(JGitText.get().lockAlreadyHeld, ref));
+		}
 		FileUtils.mkdirs(lck.getParentFile(), true);
 		try {
 			token = FS.DETECTED.createNewFileAtomic(lck);
@@ -169,18 +144,15 @@
 			LOG.error(JGitText.get().failedCreateLockFile, lck, e);
 			throw e;
 		}
-		if (token.isCreated()) {
+		boolean obtainedLock = token.isCreated();
+		if (obtainedLock) {
 			haveLck = true;
-			try {
-				os = new FileOutputStream(lck);
-			} catch (IOException ioe) {
-				unlock();
-				throw ioe;
-			}
+			isAppend = false;
+			written = false;
 		} else {
 			closeToken();
 		}
-		return haveLck;
+		return obtainedLock;
 	}
 
 	/**
@@ -193,12 +165,24 @@
 	 *             does not hold the lock.
 	 */
 	public boolean lockForAppend() throws IOException {
-		if (!lock())
+		if (!lock()) {
 			return false;
+		}
 		copyCurrentContent();
+		isAppend = true;
+		written = false;
 		return true;
 	}
 
+	// For tests only
+	boolean isLocked() {
+		return haveLck;
+	}
+
+	private FileOutputStream getStream() throws IOException {
+		return new FileOutputStream(lck, isAppend);
+	}
+
 	/**
 	 * Copy the current file content into the temporary file.
 	 * <p>
@@ -220,32 +204,31 @@
 	 */
 	public void copyCurrentContent() throws IOException {
 		requireLock();
-		try {
+		try (FileOutputStream out = getStream()) {
 			try (FileInputStream fis = new FileInputStream(ref)) {
 				if (fsync) {
 					FileChannel in = fis.getChannel();
 					long pos = 0;
 					long cnt = in.size();
 					while (0 < cnt) {
-						long r = os.getChannel().transferFrom(in, pos, cnt);
+						long r = out.getChannel().transferFrom(in, pos, cnt);
 						pos += r;
 						cnt -= r;
 					}
 				} else {
 					final byte[] buf = new byte[2048];
 					int r;
-					while ((r = fis.read(buf)) >= 0)
-						os.write(buf, 0, r);
+					while ((r = fis.read(buf)) >= 0) {
+						out.write(buf, 0, r);
+					}
 				}
+			} catch (FileNotFoundException fnfe) {
+				if (ref.exists()) {
+					throw fnfe;
+				}
+				// Don't worry about a file that doesn't exist yet, it
+				// conceptually has no current content to copy.
 			}
-		} catch (FileNotFoundException fnfe) {
-			if (ref.exists()) {
-				unlock();
-				throw fnfe;
-			}
-			// Don't worry about a file that doesn't exist yet, it
-			// conceptually has no current content to copy.
-			//
 		} catch (IOException | RuntimeException | Error ioe) {
 			unlock();
 			throw ioe;
@@ -288,18 +271,22 @@
 	 */
 	public void write(byte[] content) throws IOException {
 		requireLock();
-		try {
+		try (FileOutputStream out = getStream()) {
+			if (written) {
+				throw new IOException(MessageFormat
+						.format(JGitText.get().lockStreamClosed, ref));
+			}
 			if (fsync) {
-				FileChannel fc = os.getChannel();
+				FileChannel fc = out.getChannel();
 				ByteBuffer buf = ByteBuffer.wrap(content);
-				while (0 < buf.remaining())
+				while (0 < buf.remaining()) {
 					fc.write(buf);
+				}
 				fc.force(true);
 			} else {
-				os.write(content);
+				out.write(content);
 			}
-			os.close();
-			os = null;
+			written = true;
 		} catch (IOException | RuntimeException | Error ioe) {
 			unlock();
 			throw ioe;
@@ -318,36 +305,67 @@
 	public OutputStream getOutputStream() {
 		requireLock();
 
-		final OutputStream out;
-		if (fsync)
-			out = Channels.newOutputStream(os.getChannel());
-		else
-			out = os;
+		if (written || os != null) {
+			throw new IllegalStateException(MessageFormat
+					.format(JGitText.get().lockStreamMultiple, ref));
+		}
 
 		return new OutputStream() {
+
+			private OutputStream out;
+
+			private boolean closed;
+
+			private OutputStream get() throws IOException {
+				if (written) {
+					throw new IOException(MessageFormat
+							.format(JGitText.get().lockStreamMultiple, ref));
+				}
+				if (out == null) {
+					os = getStream();
+					if (fsync) {
+						out = Channels.newOutputStream(os.getChannel());
+					} else {
+						out = os;
+					}
+				}
+				return out;
+			}
+
 			@Override
-			public void write(byte[] b, int o, int n)
-					throws IOException {
-				out.write(b, o, n);
+			public void write(byte[] b, int o, int n) throws IOException {
+				get().write(b, o, n);
 			}
 
 			@Override
 			public void write(byte[] b) throws IOException {
-				out.write(b);
+				get().write(b);
 			}
 
 			@Override
 			public void write(int b) throws IOException {
-				out.write(b);
+				get().write(b);
 			}
 
 			@Override
 			public void close() throws IOException {
+				if (closed) {
+					return;
+				}
+				closed = true;
 				try {
-					if (fsync)
-						os.getChannel().force(true);
-					out.close();
-					os = null;
+					if (written) {
+						throw new IOException(MessageFormat
+								.format(JGitText.get().lockStreamClosed, ref));
+					}
+					if (out != null) {
+						if (fsync) {
+							os.getChannel().force(true);
+						}
+						out.close();
+						os = null;
+					}
+					written = true;
 				} catch (IOException | RuntimeException | Error ioe) {
 					unlock();
 					throw ioe;
@@ -357,7 +375,7 @@
 	}
 
 	void requireLock() {
-		if (os == null) {
+		if (!haveLck) {
 			unlock();
 			throw new IllegalStateException(MessageFormat.format(JGitText.get().lockOnNotHeld, ref));
 		}
@@ -461,6 +479,8 @@
 		try {
 			FileUtils.rename(lck, ref, StandardCopyOption.ATOMIC_MOVE);
 			haveLck = false;
+			isAppend = false;
+			written = false;
 			closeToken();
 			return true;
 		} catch (IOException e) {
@@ -551,6 +571,8 @@
 				closeToken();
 			}
 		}
+		isAppend = false;
+		written = false;
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 968ade6..b4cad35 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -109,7 +109,7 @@
  * considered.
  */
 public class ObjectDirectory extends FileObjectDatabase {
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(ObjectDirectory.class);
 
 	private static final PackList NO_PACKS = new PackList(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
index e5a54e3..09f2021 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
@@ -109,10 +109,9 @@
 		ObjectId id = idFor(type, data, off, len);
 		if (!createDuplicate && db.has(id)) {
 			return id;
-		} else {
-			File tmp = toTemp(type, data, off, len);
-			return insertOneObject(tmp, id, createDuplicate);
 		}
+		File tmp = toTemp(type, data, off, len);
+		return insertOneObject(tmp, id, createDuplicate);
 	}
 
 	/** {@inheritDoc} */
@@ -141,12 +140,11 @@
 			int actLen = IO.readFully(is, buf, 0);
 			return insert(type, buf, 0, actLen, createDuplicate);
 
-		} else {
-			SHA1 md = digest();
-			File tmp = toTemp(md, type, len, is);
-			ObjectId id = md.toObjectId();
-			return insertOneObject(tmp, id, createDuplicate);
 		}
+		SHA1 md = digest();
+		File tmp = toTemp(md, type, len, is);
+		ObjectId id = md.toObjectId();
+		return insertOneObject(tmp, id, createDuplicate);
 	}
 
 	private ObjectId insertOneObject(
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 86e90c6..e7d917b 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
@@ -103,7 +103,7 @@
  * objects are similar.
  */
 public class PackFile implements Iterable<PackIndex.MutableEntry> {
-	private final static Logger LOG = LoggerFactory.getLogger(PackFile.class);
+	private static final 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
@@ -845,19 +845,20 @@
 				case Constants.OBJ_TREE:
 				case Constants.OBJ_BLOB:
 				case Constants.OBJ_TAG: {
-					if (delta != null || sz < curs.getStreamFileThreshold())
+					if (delta != null || sz < curs.getStreamFileThreshold()) {
 						data = decompress(pos + p, (int) sz, curs);
+					}
 
 					if (delta != null) {
 						type = typeCode;
 						break SEARCH;
 					}
 
-					if (data != null)
+					if (data != null) {
 						return new ObjectLoader.SmallObject(typeCode, data);
-					else
-						return new LargePackedWholeObject(typeCode, sz, pos, p,
-								this, curs.db);
+					}
+					return new LargePackedWholeObject(typeCode, sz, pos, p,
+							this, curs.db);
 				}
 
 				case Constants.OBJ_OFS_DELTA: {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
index e45b53e..9023570 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
@@ -366,12 +366,8 @@
 			List<ReceiveCommand> commands) throws IOException {
 		// Construct a new RefList by merging the old list with the updates.
 		// This assumes that each ref occurs at most once as a ReceiveCommand.
-		Collections.sort(commands, new Comparator<ReceiveCommand>() {
-			@Override
-			public int compare(ReceiveCommand a, ReceiveCommand b) {
-				return a.getRefName().compareTo(b.getRefName());
-			}
-		});
+		Collections.sort(commands,
+				Comparator.comparing(ReceiveCommand::getRefName));
 
 		int delta = 0;
 		for (ReceiveCommand c : commands) {
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 8cc9383..7fdf8bf 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
@@ -130,7 +130,7 @@
  * overall size of a Git repository on disk.
  */
 public class RefDirectory extends RefDatabase {
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(RefDirectory.class);
 
 	/** Magic string denoting the start of a symbolic reference file. */
@@ -565,10 +565,9 @@
 			if (obj instanceof RevTag) {
 				return new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf
 						.getName(), leaf.getObjectId(), rw.peel(obj).copy());
-			} else {
-				return new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf
-						.getName(), leaf.getObjectId());
 			}
+			return new ObjectIdRef.PeeledNonTag(leaf.getStorage(),
+					leaf.getName(), leaf.getObjectId());
 		}
 	}
 
@@ -865,10 +864,9 @@
 		if (peeledObjectId != null) {
 			return new ObjectIdRef.PeeledTag(PACKED, f.getName(),
 					f.getObjectId(), peeledObjectId);
-		} else {
-			return new ObjectIdRef.PeeledNonTag(PACKED, f.getName(),
-					f.getObjectId());
 		}
+		return new ObjectIdRef.PeeledNonTag(PACKED, f.getName(),
+				f.getObjectId());
 	}
 
 	void log(boolean force, RefUpdate update, String msg, boolean deref)
@@ -1379,7 +1377,7 @@
 		LooseRef peel(ObjectIdRef newLeaf);
 	}
 
-	private final static class LoosePeeledTag extends ObjectIdRef.PeeledTag
+	private static final class LoosePeeledTag extends ObjectIdRef.PeeledTag
 			implements LooseRef {
 		private final FileSnapshot snapShot;
 
@@ -1400,7 +1398,7 @@
 		}
 	}
 
-	private final static class LooseNonTag extends ObjectIdRef.PeeledNonTag
+	private static final class LooseNonTag extends ObjectIdRef.PeeledNonTag
 			implements LooseRef {
 		private final FileSnapshot snapShot;
 
@@ -1421,7 +1419,7 @@
 		}
 	}
 
-	private final static class LooseUnpeeled extends ObjectIdRef.Unpeeled
+	private static final class LooseUnpeeled extends ObjectIdRef.Unpeeled
 			implements LooseRef {
 		private FileSnapshot snapShot;
 
@@ -1451,14 +1449,12 @@
 			if (peeledObjectId != null) {
 				return new LoosePeeledTag(snapShot, getName(),
 						objectId, peeledObjectId);
-			} else {
-				return new LooseNonTag(snapShot, getName(),
-						objectId);
 			}
+			return new LooseNonTag(snapShot, getName(), objectId);
 		}
 	}
 
-	private final static class LooseSymbolicRef extends SymbolicRef implements
+	private static final class LooseSymbolicRef extends SymbolicRef implements
 			LooseRef {
 		private final FileSnapshot snapShot;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
index 1a0d695..dc4967f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
@@ -90,9 +90,8 @@
 			dst = database.findRef(name);
 			setOldObjectId(dst != null ? dst.getObjectId() : null);
 			return true;
-		} else {
-			return false;
 		}
+		return false;
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
index 08a14b2..3cdd904 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
@@ -140,9 +140,9 @@
 	/** {@inheritDoc} */
 	@Override
 	public CheckoutEntry parseCheckout() {
-		if (getComment().startsWith(CheckoutEntryImpl.CHECKOUT_MOVING_FROM))
+		if (getComment().startsWith(CheckoutEntryImpl.CHECKOUT_MOVING_FROM)) {
 			return new CheckoutEntryImpl(this);
-		else
-			return null;
+		}
+		return null;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
index 131f716..98d6ea0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
@@ -61,6 +61,7 @@
 import java.text.MessageFormat;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.CoreConfig;
 import org.eclipse.jgit.lib.ObjectId;
@@ -68,6 +69,7 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.FileUtils;
 
 /**
@@ -239,7 +241,7 @@
 	private ReflogWriter log(String refName, byte[] rec) throws IOException {
 		File log = refdb.logFor(refName);
 		boolean write = forceWrite
-				|| (isLogAllRefUpdates() && shouldAutoCreateLog(refName))
+				|| shouldAutoCreateLog(refName)
 				|| log.isFile();
 		if (!write)
 			return this;
@@ -260,15 +262,27 @@
 		return this;
 	}
 
-	private boolean isLogAllRefUpdates() {
-		return refdb.getRepository().getConfig().get(CoreConfig.KEY)
-				.isLogAllRefUpdates();
-	}
-
 	private boolean shouldAutoCreateLog(String refName) {
-		return refName.equals(HEAD)
-				|| refName.startsWith(R_HEADS)
-				|| refName.startsWith(R_REMOTES)
-				|| refName.startsWith(R_NOTES);
+		Repository repo = refdb.getRepository();
+		CoreConfig.LogRefUpdates value = repo.isBare()
+				? CoreConfig.LogRefUpdates.FALSE
+				: CoreConfig.LogRefUpdates.TRUE;
+		value = repo.getConfig().getEnum(ConfigConstants.CONFIG_CORE_SECTION,
+				null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, value);
+		if (value != null) {
+			switch (value) {
+			case FALSE:
+				break;
+			case TRUE:
+				return refName.equals(HEAD) || refName.startsWith(R_HEADS)
+						|| refName.startsWith(R_REMOTES)
+						|| refName.startsWith(R_NOTES);
+			case ALWAYS:
+				return refName.equals(HEAD) || refName.startsWith(R_REFS);
+			default:
+				break;
+			}
+		}
+		return false;
 	}
 }
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
index 79f1307..fb06623 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
@@ -139,49 +139,48 @@
 				}
 				return new LargeObject(type, size, path, id, wc.db);
 
-			} else {
-				readSome(in, hdr, 2, 18);
-				int c = hdr[0] & 0xff;
-				int type = (c >> 4) & 7;
-				long size = c & 15;
-				int shift = 4;
-				int p = 1;
-				while ((c & 0x80) != 0) {
-					c = hdr[p++] & 0xff;
-					size += ((long) (c & 0x7f)) << shift;
-					shift += 7;
-				}
-
-				switch (type) {
-				case Constants.OBJ_COMMIT:
-				case Constants.OBJ_TREE:
-				case Constants.OBJ_BLOB:
-				case Constants.OBJ_TAG:
-					// Acceptable types for a loose object.
-					break;
-				default:
-					throw new CorruptObjectException(id,
-							JGitText.get().corruptObjectInvalidType);
-				}
-
-				if (path == null && Integer.MAX_VALUE < size) {
-					LargeObjectException.ExceedsByteArrayLimit e;
-					e = new LargeObjectException.ExceedsByteArrayLimit();
-					e.setObjectId(id);
-					throw e;
-				}
-				if (size < wc.getStreamFileThreshold() || path == null) {
-					in.reset();
-					IO.skipFully(in, p);
-					Inflater inf = wc.inflater();
-					InputStream zIn = inflate(in, inf);
-					byte[] data = new byte[(int) size];
-					IO.readFully(zIn, data, 0, data.length);
-					checkValidEndOfStream(in, inf, id, hdr);
-					return new ObjectLoader.SmallObject(type, data);
-				}
-				return new LargeObject(type, size, path, id, wc.db);
 			}
+			readSome(in, hdr, 2, 18);
+			int c = hdr[0] & 0xff;
+			int type = (c >> 4) & 7;
+			long size = c & 15;
+			int shift = 4;
+			int p = 1;
+			while ((c & 0x80) != 0) {
+				c = hdr[p++] & 0xff;
+				size += ((long) (c & 0x7f)) << shift;
+				shift += 7;
+			}
+
+			switch (type) {
+			case Constants.OBJ_COMMIT:
+			case Constants.OBJ_TREE:
+			case Constants.OBJ_BLOB:
+			case Constants.OBJ_TAG:
+				// Acceptable types for a loose object.
+				break;
+			default:
+				throw new CorruptObjectException(id,
+						JGitText.get().corruptObjectInvalidType);
+			}
+
+			if (path == null && Integer.MAX_VALUE < size) {
+				LargeObjectException.ExceedsByteArrayLimit e;
+				e = new LargeObjectException.ExceedsByteArrayLimit();
+				e.setObjectId(id);
+				throw e;
+			}
+			if (size < wc.getStreamFileThreshold() || path == null) {
+				in.reset();
+				IO.skipFully(in, p);
+				Inflater inf = wc.inflater();
+				InputStream zIn = inflate(in, inf);
+				byte[] data = new byte[(int) size];
+				IO.readFully(zIn, data, 0, data.length);
+				checkValidEndOfStream(in, inf, id, hdr);
+				return new ObjectLoader.SmallObject(type, data);
+			}
+			return new LargeObject(type, size, path, id, wc.db);
 		} catch (ZipException badStream) {
 			throw new CorruptObjectException(id,
 					JGitText.get().corruptObjectBadStream);
@@ -213,19 +212,18 @@
 							JGitText.get().corruptObjectNegativeSize);
 				return size;
 
-			} else {
-				readSome(in, hdr, 2, 18);
-				int c = hdr[0] & 0xff;
-				long size = c & 15;
-				int shift = 4;
-				int p = 1;
-				while ((c & 0x80) != 0) {
-					c = hdr[p++] & 0xff;
-					size += ((long) (c & 0x7f)) << shift;
-					shift += 7;
-				}
-				return size;
 			}
+			readSome(in, hdr, 2, 18);
+			int c = hdr[0] & 0xff;
+			long size = c & 15;
+			int shift = 4;
+			int p = 1;
+			while ((c & 0x80) != 0) {
+				c = hdr[p++] & 0xff;
+				size += ((long) (c & 0x7f)) << shift;
+				shift += 7;
+			}
+			return size;
 		} catch (ZipException badStream) {
 			throw new CorruptObjectException(id,
 					JGitText.get().corruptObjectBadStream);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
index ea0d269..616447a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
@@ -126,17 +126,19 @@
 			for (int n = 0; n < MAX_CHAIN;) {
 				ObjectId obj = ids.get(i);
 				if (obj == null) {
-					if (ids.compareAndSet(i, null, toAdd.copy()))
+					if (ids.compareAndSet(i, null, toAdd.copy())) {
 						return true;
-					else
-						continue;
+					}
+					continue;
 				}
 
-				if (AnyObjectId.isEqual(obj, toAdd))
+				if (AnyObjectId.isEqual(obj, toAdd)) {
 					return true;
+				}
 
-				if (++i == ids.length())
+				if (++i == ids.length()) {
 					i = 0;
+				}
 				n++;
 			}
 			return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPackUriProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPackUriProvider.java
index 5cbc2ba..9496d78 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPackUriProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPackUriProvider.java
@@ -76,14 +76,22 @@
 		private final String hash;
 		private final String uri;
 
+		private final long size;
+
 		/**
 		 * Constructs an object containing information about a packfile.
-		 * @param hash the hash of the packfile as a hexadecimal string
-		 * @param uri the URI corresponding to the packfile
+		 *
+		 * @param hash
+		 *            the hash of the packfile as a hexadecimal string
+		 * @param uri
+		 *            the URI corresponding to the packfile
+		 * @param size
+		 *            the size of the packfile in bytes
 		 */
-		public PackInfo(String hash, String uri) {
+		public PackInfo(String hash, String uri, long size) {
 			this.hash = hash;
 			this.uri = uri;
+			this.size = size;
 		}
 
 		/**
@@ -99,5 +107,12 @@
 		public String getUri() {
 			return uri;
 		}
+
+		/**
+		 * @return the size of the packfile in bytes (-1 if unknown)
+		 */
+		public long getSize() {
+			return size;
+		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
index 292fd36..b4143c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
@@ -81,7 +81,7 @@
 	 *            the file extension.
 	 * @return the PackExt for the ext
 	 */
-	public synchronized static PackExt newPackExt(String ext) {
+	public static synchronized PackExt newPackExt(String ext) {
 		PackExt[] dst = new PackExt[VALUES.length + 1];
 		for (int i = 0; i < VALUES.length; i++) {
 			PackExt packExt = VALUES[i];
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 43067d3..fcf9ca5 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
@@ -225,7 +225,7 @@
 	}
 
 	@SuppressWarnings("unchecked")
-	BlockList<ObjectToPack> objectsLists[] = new BlockList[OBJ_TAG + 1];
+	BlockList<ObjectToPack>[] objectsLists = new BlockList[OBJ_TAG + 1];
 	{
 		objectsLists[OBJ_COMMIT] = new BlockList<>();
 		objectsLists[OBJ_TREE] = new BlockList<>();
@@ -270,7 +270,7 @@
 
 	private List<ObjectToPack> sortedByName;
 
-	private byte packcsum[];
+	private byte[] packcsum;
 
 	private boolean deltaBaseAsOffset;
 
@@ -1230,6 +1230,8 @@
 					if (packInfo != null) {
 						o.writeString(packInfo.getHash() + ' ' +
 								packInfo.getUri() + '\n');
+						stats.offloadedPackfiles += 1;
+						stats.offloadedPackfileSize += packInfo.getSize();
 					} else {
 						unwrittenCachedPacks.add(pack);
 					}
@@ -1749,23 +1751,23 @@
 							NullProgressMonitor.INSTANCE,
 							Collections.singleton(otp));
 					continue;
-				} else {
-					// Object writing already started, we cannot recover.
-					//
-					CorruptObjectException coe;
-					coe = new CorruptObjectException(otp, ""); //$NON-NLS-1$
-					coe.initCause(gone);
-					throw coe;
 				}
+				// Object writing already started, we cannot recover.
+				//
+				CorruptObjectException coe;
+				coe = new CorruptObjectException(otp, ""); //$NON-NLS-1$
+				coe.initCause(gone);
+				throw coe;
 			}
 		}
 
 		// If we reached here, reuse wasn't possible.
 		//
-		if (otp.isDeltaRepresentation())
+		if (otp.isDeltaRepresentation()) {
 			writeDeltaObjectDeflate(out, otp);
-		else
+		} else {
 			writeWholeObjectDeflate(out, otp);
+		}
 		out.endObject();
 		otp.setCRC((int) crc32.getValue());
 	}
@@ -2430,7 +2432,7 @@
 	}
 
 	/** Possible states that a PackWriter can be in. */
-	public static enum PackingPhase {
+	public enum PackingPhase {
 		/** Counting objects phase. */
 		COUNTING,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
index b66751b..6ae7e45 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
@@ -92,7 +92,6 @@
 class BlockReader {
 	private byte blockType;
 	private long endPosition;
-	private boolean truncated;
 
 	private byte[] buf;
 	private int bufLen;
@@ -112,10 +111,6 @@
 		return blockType;
 	}
 
-	boolean truncated() {
-		return truncated;
-	}
-
 	long endPosition() {
 		return endPosition;
 	}
@@ -331,16 +326,8 @@
 			// Log blocks must be inflated after the header.
 			long deflatedSize = inflateBuf(src, pos, blockLen, fileBlockSize);
 			endPosition = pos + 4 + deflatedSize;
-		}
-		if (bufLen < blockLen) {
-			if (blockType != INDEX_BLOCK_TYPE) {
-				throw invalidBlock();
-			}
-			// Its OK during sequential scan for an index block to have been
-			// partially read and be truncated in-memory. This happens when
-			// the index block is larger than the file's blockSize. Caller
-			// will break out of its scan loop once it sees the blockType.
-			truncated = true;
+		} else if (bufLen < blockLen) {
+			readBlockIntoBuf(src, pos, blockLen);
 		} else if (bufLen > blockLen) {
 			bufLen = blockLen;
 		}
@@ -405,7 +392,7 @@
 	}
 
 	void verifyIndex() throws IOException {
-		if (blockType != INDEX_BLOCK_TYPE || truncated) {
+		if (blockType != INDEX_BLOCK_TYPE) {
 			throw invalidBlock();
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockWriter.java
index b3173e8..1ed1801 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockWriter.java
@@ -286,7 +286,7 @@
 		return aLen - bLen;
 	}
 
-	static abstract class Entry {
+	abstract static class Entry {
 		static int compare(Entry ea, Entry eb) {
 			byte[] a = ea.key;
 			byte[] b = eb.key;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java
index 486fd28..5c98242 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java
@@ -45,6 +45,7 @@
 
 import java.io.IOException;
 
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.lib.ReflogEntry;
 
 /**
@@ -78,8 +79,9 @@
 	/**
 	 * Get current log entry.
 	 *
-	 * @return current log entry.
+	 * @return current log entry. Maybe null if we are producing deletions.
 	 */
+	@Nullable
 	public abstract ReflogEntry getReflogEntry();
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
index c740bf2..d272d09 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
@@ -67,13 +67,11 @@
  * A {@code MergedReftable} is not thread-safe.
  */
 public class MergedReftable extends Reftable {
-	private final Reftable[] tables;
+	private final ReftableReader[] tables;
 
 	/**
 	 * Initialize a merged table reader.
 	 * <p>
-	 * The tables in {@code tableStack} will be closed when this
-	 * {@code MergedReftable} is closed.
 	 *
 	 * @param tableStack
 	 *            stack of tables to read from. The base of the stack is at
@@ -81,16 +79,44 @@
 	 *            {@code tableStack.size() - 1}. The top of the stack (higher
 	 *            index) shadows the base of the stack (lower index).
 	 */
-	public MergedReftable(List<Reftable> tableStack) {
-		tables = tableStack.toArray(new Reftable[0]);
+	public MergedReftable(List<ReftableReader> tableStack) {
+		tables = tableStack.toArray(new ReftableReader[0]);
 
 		// Tables must expose deletes to this instance to correctly
 		// shadow references from lower tables.
-		for (Reftable t : tables) {
+		for (ReftableReader t : tables) {
 			t.setIncludeDeletes(true);
 		}
 	}
 
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public long maxUpdateIndex() throws IOException {
+		return tables.length > 0 ? tables[tables.length - 1].maxUpdateIndex()
+				: 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public long minUpdateIndex() throws IOException {
+		return tables.length > 0 ? tables[0].minUpdateIndex()
+			: 0;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean hasObjectMap() throws IOException {
+		boolean has = true;
+		for (int i = 0; has && i < tables.length; i++) {
+			has = has && tables[i].hasObjectMap();
+		}
+		return has;
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public RefCursor allRefs() throws IOException {
@@ -124,7 +150,7 @@
 	/** {@inheritDoc} */
 	@Override
 	public RefCursor byObjectId(AnyObjectId name) throws IOException {
-		MergedRefCursor m = new MergedRefCursor();
+		MergedRefCursor m = new FilteringMergedRefCursor(name);
 		for (int i = 0; i < tables.length; i++) {
 			m.add(new RefQueueEntry(tables[i].byObjectId(name), i));
 		}
@@ -152,14 +178,6 @@
 		return m;
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	public void close() throws IOException {
-		for (Reftable t : tables) {
-			t.close();
-		}
-	}
-
 	int queueSize() {
 		return Math.max(1, tables.length);
 	}
@@ -251,6 +269,42 @@
 		}
 	}
 
+	private class FilteringMergedRefCursor extends MergedRefCursor {
+		final AnyObjectId filterId;
+		Ref filteredRef;
+
+		FilteringMergedRefCursor(AnyObjectId id) {
+			filterId = id;
+			filteredRef = null;
+		}
+
+		@Override
+		public Ref getRef() {
+			return filteredRef;
+		}
+
+		@Override
+		public boolean next() throws IOException {
+			for (;;) {
+				boolean ok = super.next();
+				if (!ok) {
+					return false;
+				}
+
+				String name = super.getRef().getName();
+
+				try (RefCursor c = seekRef(name)) {
+					if (c.next()) {
+						if (filterId.equals(c.getRef().getObjectId())) {
+							filteredRef = c.getRef();
+							return true;
+						}
+					}
+				}
+			}
+		}
+	}
+
 	private static class RefQueueEntry {
 		static int compare(RefQueueEntry a, RefQueueEntry b) {
 			int cmp = a.name().compareTo(b.name());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
index cb02628..a7e8252 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
@@ -58,7 +58,7 @@
 /**
  * Abstract table of references.
  */
-public abstract class Reftable implements AutoCloseable {
+public abstract class Reftable {
 	/**
 	 * References to convert into a reftable
 	 *
@@ -72,9 +72,9 @@
 			cfg.setIndexObjects(false);
 			cfg.setAlignBlocks(false);
 			ByteArrayOutputStream buf = new ByteArrayOutputStream();
-			new ReftableWriter()
+			new ReftableWriter(buf)
 				.setConfig(cfg)
-				.begin(buf)
+				.begin()
 				.sortAndWriteRefs(refs)
 				.finish();
 			return new ReftableReader(BlockSource.from(buf.toByteArray()));
@@ -99,6 +99,28 @@
 	}
 
 	/**
+	 * Get the maximum update index for ref entries that appear in this
+	 * reftable.
+	 *
+	 * @return the maximum update index for ref entries that appear in this
+	 *         reftable.
+	 * @throws java.io.IOException
+	 *             file cannot be read.
+	 */
+	public abstract long maxUpdateIndex() throws IOException;
+
+	/**
+	 * Get the minimum update index for ref entries that appear in this
+	 * reftable.
+	 *
+	 * @return the minimum update index for ref entries that appear in this
+	 *         reftable.
+	 * @throws java.io.IOException
+	 *             file cannot be read.
+	 */
+	public abstract long minUpdateIndex() throws IOException;
+
+	/**
 	 * Seek to the first reference, to iterate in order.
 	 *
 	 * @return cursor to iterate.
@@ -149,6 +171,12 @@
 	public abstract RefCursor byObjectId(AnyObjectId id) throws IOException;
 
 	/**
+	 * @return whether this reftable can do a fast SHA1 => ref lookup.
+	 * @throws IOException on I/O problems.
+	 */
+	public abstract boolean hasObjectMap() throws IOException;
+
+	/**
 	 * Seek reader to read log records.
 	 *
 	 * @return cursor to iterate; empty cursor if no logs are present.
@@ -282,8 +310,4 @@
 		}
 		return new SymbolicRef(ref.getName(), dst, ref.getUpdateIndex());
 	}
-
-	/** {@inheritDoc} */
-	@Override
-	public abstract void close() throws IOException;
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableBatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableBatchRefUpdate.java
new file mode 100644
index 0000000..592120d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableBatchRefUpdate.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2019, Google Inc.
+ * 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.reftable;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
+import java.util.stream.Collectors;
+
+import static org.eclipse.jgit.lib.Ref.Storage.NEW;
+import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
+
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
+import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
+
+/**
+ * {@link org.eclipse.jgit.lib.BatchRefUpdate} for Reftable based RefDatabase.
+ */
+public abstract class ReftableBatchRefUpdate extends BatchRefUpdate {
+	private final Lock lock;
+
+	private final ReftableDatabase refDb;
+
+	private final Repository repository;
+
+	/**
+	 * Initialize.
+	 *
+	 * @param refdb
+	 *            The RefDatabase
+	 * @param reftableDb
+	 *            The ReftableDatabase
+	 * @param lock
+	 *            A lock protecting the refdatabase's state
+	 * @param repository
+	 *            The repository on which this update will run
+	 */
+	protected ReftableBatchRefUpdate(RefDatabase refdb, ReftableDatabase reftableDb, Lock lock,
+			Repository repository) {
+		super(refdb);
+		this.refDb = reftableDb;
+		this.lock = lock;
+		this.repository = repository;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void execute(RevWalk rw, ProgressMonitor pm, List<String> options) {
+		List<ReceiveCommand> pending = getPending();
+		if (pending.isEmpty()) {
+			return;
+		}
+		if (options != null) {
+			setPushOptions(options);
+		}
+		try {
+			if (!checkObjectExistence(rw, pending)) {
+				return;
+			}
+			// if we are here, checkObjectExistence might have flagged some problems
+			// but the transaction is not atomic, so we should proceed with the other
+			// pending commands.
+			pending = getPending();
+			if (!checkNonFastForwards(rw, pending)) {
+				return;
+			}
+			pending = getPending();
+
+			lock.lock();
+			try {
+				if (!checkExpected(pending)) {
+					return;
+				}
+				pending = getPending();
+				if (!checkConflicting(pending)) {
+					return;
+				}
+				pending = getPending();
+				if (!blockUntilTimestamps(MAX_WAIT)) {
+					return;
+				}
+
+				List<Ref> newRefs = toNewRefs(rw, pending);
+				applyUpdates(newRefs, pending);
+				for (ReceiveCommand cmd : pending) {
+					if (cmd.getResult() == NOT_ATTEMPTED) {
+						// XXX this is a bug in DFS ?
+						cmd.setResult(OK);
+					}
+				}
+			} finally {
+				lock.unlock();
+			}
+		} catch (IOException e) {
+			pending.get(0).setResult(LOCK_FAILURE, "io error"); //$NON-NLS-1$
+			ReceiveCommand.abort(pending);
+		}
+	}
+
+	/**
+	 * Implements the storage-specific part of the update.
+	 *
+	 * @param newRefs
+	 *            the new refs to create
+	 * @param pending
+	 *            the pending receive commands to be executed
+	 * @throws IOException
+	 *             if any of the writes fail.
+	 */
+	protected abstract void applyUpdates(List<Ref> newRefs,
+			List<ReceiveCommand> pending) throws IOException;
+
+	private List<ReceiveCommand> getPending() {
+		return ReceiveCommand.filter(getCommands(), NOT_ATTEMPTED);
+	}
+
+	private boolean checkObjectExistence(RevWalk rw,
+			List<ReceiveCommand> pending) throws IOException {
+		for (ReceiveCommand cmd : pending) {
+			try {
+				if (!cmd.getNewId().equals(ObjectId.zeroId())) {
+					rw.parseAny(cmd.getNewId());
+				}
+			} catch (MissingObjectException e) {
+				// ReceiveCommand#setResult(Result) converts REJECTED to
+				// REJECTED_NONFASTFORWARD, even though that result is also
+				// used for a missing object. Eagerly handle this case so we
+				// can set the right result.
+				cmd.setResult(REJECTED_MISSING_OBJECT);
+				if (isAtomic()) {
+					ReceiveCommand.abort(pending);
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	private boolean checkNonFastForwards(RevWalk rw,
+			List<ReceiveCommand> pending) throws IOException {
+		if (isAllowNonFastForwards()) {
+			return true;
+		}
+		for (ReceiveCommand cmd : pending) {
+			cmd.updateType(rw);
+			if (cmd.getType() == UPDATE_NONFASTFORWARD) {
+				cmd.setResult(REJECTED_NONFASTFORWARD);
+				if (isAtomic()) {
+					ReceiveCommand.abort(pending);
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	private boolean checkConflicting(List<ReceiveCommand> pending)
+			throws IOException {
+		TreeSet<String> added = new TreeSet<>();
+		Set<String> deleted =
+				pending.stream()
+						.filter(cmd -> cmd.getType() == DELETE)
+						.map(c -> c.getRefName())
+						.collect(Collectors.toSet());
+
+		boolean ok = true;
+		for (ReceiveCommand cmd : pending) {
+			if (cmd.getType() == DELETE) {
+				continue;
+			}
+
+			String name = cmd.getRefName();
+			if (refDb.isNameConflicting(name, added, deleted)) {
+				if (isAtomic()) {
+					cmd.setResult(
+							ReceiveCommand.Result.REJECTED_OTHER_REASON, JGitText.get().transactionAborted);
+				} else {
+					cmd.setResult(LOCK_FAILURE);
+				}
+
+				ok = false;
+			}
+			added.add(name);
+		}
+
+		if (isAtomic()) {
+			if (!ok) {
+				pending.stream()
+						.filter(cmd -> cmd.getResult() == NOT_ATTEMPTED)
+						.forEach(cmd -> cmd.setResult(LOCK_FAILURE));
+			}
+			return ok;
+		}
+
+		for (ReceiveCommand cmd : pending) {
+			if (cmd.getResult() == NOT_ATTEMPTED) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	private boolean checkExpected(List<ReceiveCommand> pending)
+			throws IOException {
+		for (ReceiveCommand cmd : pending) {
+			if (!matchOld(cmd, refDb.exactRef(cmd.getRefName()))) {
+				cmd.setResult(LOCK_FAILURE);
+				if (isAtomic()) {
+					ReceiveCommand.abort(pending);
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	private static boolean matchOld(ReceiveCommand cmd, @Nullable Ref ref) {
+		if (ref == null) {
+			return AnyObjectId.isEqual(ObjectId.zeroId(), cmd.getOldId())
+				&& cmd.getOldSymref() == null;
+		} else if (ref.isSymbolic()) {
+			return ref.getTarget().getName().equals(cmd.getOldSymref());
+		}
+		ObjectId id = ref.getObjectId();
+		if (id == null) {
+			id = ObjectId.zeroId();
+		}
+		return cmd.getOldId().equals(id);
+	}
+
+	/**
+	 * Writes the refs to the writer, and calls finish.
+	 *
+	 * @param writer
+	 *            the writer on which we should write.
+	 * @param newRefs
+	 *            the ref data to write..
+	 * @param pending
+	 *            the log data to write.
+	 * @throws IOException
+	 *            in case of problems.
+	 */
+	protected void write(ReftableWriter writer, List<Ref> newRefs,
+			List<ReceiveCommand> pending) throws IOException {
+		long updateIndex = refDb.nextUpdateIndex();
+		writer.setMinUpdateIndex(updateIndex).setMaxUpdateIndex(updateIndex)
+				.begin().sortAndWriteRefs(newRefs);
+		if (!isRefLogDisabled()) {
+			writeLog(writer, updateIndex, pending);
+		}
+	}
+
+	private void writeLog(ReftableWriter writer, long updateIndex,
+			List<ReceiveCommand> pending) throws IOException {
+		Map<String, ReceiveCommand> cmds = new HashMap<>();
+		List<String> byName = new ArrayList<>(pending.size());
+		for (ReceiveCommand cmd : pending) {
+			cmds.put(cmd.getRefName(), cmd);
+			byName.add(cmd.getRefName());
+		}
+		Collections.sort(byName);
+
+		PersonIdent ident = getRefLogIdent();
+		if (ident == null) {
+			ident = new PersonIdent(repository);
+		}
+		for (String name : byName) {
+			ReceiveCommand cmd = cmds.get(name);
+			if (isRefLogDisabled(cmd)) {
+				continue;
+			}
+			String msg = getRefLogMessage(cmd);
+			if (isRefLogIncludingResult(cmd)) {
+				String strResult = toResultString(cmd);
+				if (strResult != null) {
+					msg = msg.isEmpty() ? strResult : msg + ": " + strResult; //$NON-NLS-1$
+				}
+			}
+			writer.writeLog(name, updateIndex, ident, cmd.getOldId(),
+					cmd.getNewId(), msg);
+		}
+	}
+
+	private String toResultString(ReceiveCommand cmd) {
+		switch (cmd.getType()) {
+		case CREATE:
+			return ReflogEntry.PREFIX_CREATED;
+		case UPDATE:
+			// Match the behavior of a single RefUpdate. In that case, setting
+			// the force bit completely bypasses the potentially expensive
+			// isMergedInto check, by design, so the reflog message may be
+			// inaccurate.
+			//
+			// Similarly, this class bypasses the isMergedInto checks when the
+			// force bit is set, meaning we can't actually distinguish between
+			// UPDATE and UPDATE_NONFASTFORWARD when isAllowNonFastForwards()
+			// returns true.
+			return isAllowNonFastForwards() ? ReflogEntry.PREFIX_FORCED_UPDATE
+					: ReflogEntry.PREFIX_FAST_FORWARD;
+		case UPDATE_NONFASTFORWARD:
+			return ReflogEntry.PREFIX_FORCED_UPDATE;
+		default:
+			return null;
+		}
+	}
+
+	// Extracts and peels the refs out of the ReceiveCommands
+	private static List<Ref> toNewRefs(RevWalk rw, List<ReceiveCommand> pending)
+		throws IOException {
+		List<Ref> refs = new ArrayList<>(pending.size());
+		for (ReceiveCommand cmd : pending) {
+			if (cmd.getResult() != NOT_ATTEMPTED) {
+				continue;
+			}
+
+			String name = cmd.getRefName();
+			ObjectId newId = cmd.getNewId();
+			String newSymref = cmd.getNewSymref();
+			if (AnyObjectId.isEqual(ObjectId.zeroId(), newId)
+				&& newSymref == null) {
+				refs.add(new ObjectIdRef.Unpeeled(NEW, name, null));
+				continue;
+			} else if (newSymref != null) {
+				refs.add(new SymbolicRef(name,
+					new ObjectIdRef.Unpeeled(NEW, newSymref, null)));
+				continue;
+			}
+
+			RevObject obj = rw.parseAny(newId);
+			RevObject peel = null;
+			if (obj instanceof RevTag) {
+				peel = rw.peel(obj);
+			}
+			if (peel != null) {
+				refs.add(new ObjectIdRef.PeeledTag(PACKED, name, newId,
+					peel.copy()));
+			} else {
+				refs.add(new ObjectIdRef.PeeledNonTag(PACKED, name, newId));
+			}
+		}
+		return refs;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java
index c4e8f69..a586d42 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableCompactor.java
@@ -61,25 +61,34 @@
  * to shadow any lower reftable that may have the reference present.
  * <p>
  * By default all log entries within the range defined by
- * {@link #setMinUpdateIndex(long)} and {@link #setMaxUpdateIndex(long)} are
+ * {@link #setReflogExpireMinUpdateIndex(long)} and {@link #setReflogExpireMaxUpdateIndex(long)} are
  * copied, even if no references in the output file match the log records.
  * Callers may truncate the log to a more recent time horizon with
- * {@link #setOldestReflogTimeMillis(long)}, or disable the log altogether with
+ * {@link #setReflogExpireOldestReflogTimeMillis(long)}, or disable the log altogether with
  * {@code setOldestReflogTimeMillis(Long.MAX_VALUE)}.
  */
 public class ReftableCompactor {
-	private final ReftableWriter writer = new ReftableWriter();
-	private final ArrayDeque<Reftable> tables = new ArrayDeque<>();
+	private final ReftableWriter writer;
+	private final ArrayDeque<ReftableReader> tables = new ArrayDeque<>();
 
-	private long compactBytesLimit;
-	private long bytesToCompact;
 	private boolean includeDeletes;
-	private long minUpdateIndex = -1;
-	private long maxUpdateIndex;
-	private long oldestReflogTimeMillis;
+	private long reflogExpireMinUpdateIndex = 0;
+	private long reflogExpireMaxUpdateIndex = Long.MAX_VALUE;
+	private long reflogExpireOldestReflogTimeMillis;
 	private Stats stats;
 
 	/**
+	 * Creates a new compactor.
+	 *
+	 * @param out
+	 *            stream to write the compacted tables to. Caller is responsible
+	 *            for closing {@code out}.
+	 */
+	public ReftableCompactor(OutputStream out) {
+		writer = new ReftableWriter(out);
+	}
+
+	/**
 	 * Set configuration for the reftable.
 	 *
 	 * @param cfg
@@ -92,18 +101,6 @@
 	}
 
 	/**
-	 * Set limit on number of bytes from source tables to compact.
-	 *
-	 * @param bytes
-	 *            limit on number of bytes from source tables to compact.
-	 * @return {@code this}
-	 */
-	public ReftableCompactor setCompactBytesLimit(long bytes) {
-		compactBytesLimit = bytes;
-		return this;
-	}
-
-	/**
 	 * Whether to include deletions in the output, which may be necessary for
 	 * partial compaction.
 	 *
@@ -128,8 +125,8 @@
 	 *            in a stack.
 	 * @return {@code this}
 	 */
-	public ReftableCompactor setMinUpdateIndex(long min) {
-		minUpdateIndex = min;
+	public ReftableCompactor setReflogExpireMinUpdateIndex(long min) {
+		reflogExpireMinUpdateIndex = min;
 		return this;
 	}
 
@@ -144,8 +141,8 @@
 	 *            used in a stack.
 	 * @return {@code this}
 	 */
-	public ReftableCompactor setMaxUpdateIndex(long max) {
-		maxUpdateIndex = max;
+	public ReftableCompactor setReflogExpireMaxUpdateIndex(long max) {
+		reflogExpireMaxUpdateIndex = max;
 		return this;
 	}
 
@@ -159,16 +156,13 @@
 	 *            Specified in Java standard milliseconds since the epoch.
 	 * @return {@code this}
 	 */
-	public ReftableCompactor setOldestReflogTimeMillis(long timeMillis) {
-		oldestReflogTimeMillis = timeMillis;
+	public ReftableCompactor setReflogExpireOldestReflogTimeMillis(long timeMillis) {
+		reflogExpireOldestReflogTimeMillis = timeMillis;
 		return this;
 	}
 
 	/**
 	 * Add all of the tables, in the specified order.
-	 * <p>
-	 * Unconditionally adds all tables, ignoring the
-	 * {@link #setCompactBytesLimit(long)}.
 	 *
 	 * @param readers
 	 *            tables to compact. Tables should be ordered oldest first/most
@@ -177,67 +171,26 @@
 	 * @throws java.io.IOException
 	 *             update indexes of a reader cannot be accessed.
 	 */
-	public void addAll(List<? extends Reftable> readers) throws IOException {
-		tables.addAll(readers);
-		for (Reftable r : readers) {
-			if (r instanceof ReftableReader) {
-				adjustUpdateIndexes((ReftableReader) r);
-			}
+	public void addAll(List<ReftableReader> readers) throws IOException {
+		for (ReftableReader r : readers) {
+			tables.add(r);
 		}
 	}
 
 	/**
-	 * Try to add this reader at the bottom of the stack.
-	 * <p>
-	 * A reader may be rejected by returning {@code false} if the compactor is
-	 * already rewriting its {@link #setCompactBytesLimit(long)}. When this
-	 * happens the caller should stop trying to add tables, and execute the
-	 * compaction.
-	 *
-	 * @param reader
-	 *            the reader to insert at the bottom of the stack. Caller is
-	 *            responsible for closing the reader.
-	 * @return {@code true} if the compactor accepted this table; {@code false}
-	 *         if the compactor has reached its limit.
-	 * @throws java.io.IOException
-	 *             if size of {@code reader}, or its update indexes cannot be read.
-	 */
-	public boolean tryAddFirst(ReftableReader reader) throws IOException {
-		long sz = reader.size();
-		if (compactBytesLimit > 0 && bytesToCompact + sz > compactBytesLimit) {
-			return false;
-		}
-		bytesToCompact += sz;
-		adjustUpdateIndexes(reader);
-		tables.addFirst(reader);
-		return true;
-	}
-
-	private void adjustUpdateIndexes(ReftableReader reader) throws IOException {
-		if (minUpdateIndex == -1) {
-			minUpdateIndex = reader.minUpdateIndex();
-		} else {
-			minUpdateIndex = Math.min(minUpdateIndex, reader.minUpdateIndex());
-		}
-		maxUpdateIndex = Math.max(maxUpdateIndex, reader.maxUpdateIndex());
-	}
-
-	/**
 	 * Write a compaction to {@code out}.
 	 *
-	 * @param out
-	 *            stream to write the compacted tables to. Caller is responsible
-	 *            for closing {@code out}.
 	 * @throws java.io.IOException
 	 *             if tables cannot be read, or cannot be written.
 	 */
-	public void compact(OutputStream out) throws IOException {
+	public void compact() throws IOException {
 		MergedReftable mr = new MergedReftable(new ArrayList<>(tables));
 		mr.setIncludeDeletes(includeDeletes);
 
-		writer.setMinUpdateIndex(Math.max(minUpdateIndex, 0));
-		writer.setMaxUpdateIndex(maxUpdateIndex);
-		writer.begin(out);
+		writer.setMaxUpdateIndex(mr.maxUpdateIndex());
+		writer.setMinUpdateIndex(mr.minUpdateIndex());
+
+		writer.begin();
 		mergeRefs(mr);
 		mergeLogs(mr);
 		writer.finish();
@@ -262,16 +215,14 @@
 	}
 
 	private void mergeLogs(MergedReftable mr) throws IOException {
-		if (oldestReflogTimeMillis == Long.MAX_VALUE) {
+		if (reflogExpireOldestReflogTimeMillis == Long.MAX_VALUE) {
 			return;
 		}
 
 		try (LogCursor lc = mr.allLogs()) {
 			while (lc.next()) {
 				long updateIndex = lc.getUpdateIndex();
-				if (updateIndex < minUpdateIndex
-						|| updateIndex > maxUpdateIndex) {
-					// Cannot merge log records outside the header's range.
+				if (updateIndex > reflogExpireMaxUpdateIndex || updateIndex < reflogExpireMinUpdateIndex) {
 					continue;
 				}
 
@@ -285,7 +236,7 @@
 				}
 
 				PersonIdent who = log.getWho();
-				if (who.getWhen().getTime() >= oldestReflogTimeMillis) {
+				if (who.getWhen().getTime() >= reflogExpireOldestReflogTimeMillis) {
 					writer.writeLog(
 							refName,
 							updateIndex,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
new file mode 100644
index 0000000..696347e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableDatabase.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2017, 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.reftable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.ReflogReader;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/**
+ * Operations on {@link MergedReftable} that is common to various reftable-using
+ * subclasses of {@link RefDatabase}. See
+ * {@link org.eclipse.jgit.internal.storage.dfs.DfsReftableDatabase} for an
+ * example.
+ */
+public abstract class ReftableDatabase {
+	// Protects mergedTables.
+	private final ReentrantLock lock = new ReentrantLock(true);
+
+	private Reftable mergedTables;
+
+	/**
+	 * ReftableDatabase lazily initializes its merged reftable on the first read after
+	 * construction or clearCache() call. This function should always instantiate a new
+	 * MergedReftable based on the list of reftables specified by the underlying storage.
+	 *
+	 * @return the ReftableStack for this instance
+	 * @throws IOException
+	 *             on I/O problems.
+	 */
+	protected abstract MergedReftable openMergedReftable() throws IOException;
+
+	/**
+	 * @return the next available logical timestamp for an additional reftable
+	 *         in the stack.
+	 * @throws java.io.IOException
+	 *             on I/O problems.
+	 */
+	public long nextUpdateIndex() throws IOException {
+		lock.lock();
+		try {
+			return reader().maxUpdateIndex() + 1;
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/**
+	 * @return a ReflogReader for the given ref
+	 * @param refname
+	 *            the name of the ref.
+	 * @throws IOException
+	 *             on I/O problems
+	 */
+	public ReflogReader getReflogReader(String refname) throws IOException {
+		lock.lock();
+		try {
+			return new ReftableReflogReader(lock, reader(), refname);
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/**
+	 * @return a ReceiveCommand for the change from oldRef to newRef
+	 * @param oldRef
+	 *            a ref
+	 * @param newRef
+	 *            a ref
+	 */
+	public static ReceiveCommand toCommand(Ref oldRef, Ref newRef) {
+		ObjectId oldId = toId(oldRef);
+		ObjectId newId = toId(newRef);
+		String name = oldRef != null ? oldRef.getName() : newRef.getName();
+
+		if (oldRef != null && oldRef.isSymbolic()) {
+			if (newRef != null) {
+				if (newRef.isSymbolic()) {
+					return ReceiveCommand.link(oldRef.getTarget().getName(),
+							newRef.getTarget().getName(), name);
+				}
+				// This should pass in oldId for compat with
+				// RefDirectoryUpdate
+				return ReceiveCommand.unlink(oldRef.getTarget().getName(),
+						newId, name);
+			}
+			return ReceiveCommand.unlink(oldRef.getTarget().getName(),
+					ObjectId.zeroId(), name);
+		}
+
+		if (newRef != null && newRef.isSymbolic()) {
+			if (oldRef != null) {
+				if (oldRef.isSymbolic()) {
+					return ReceiveCommand.link(oldRef.getTarget().getName(),
+							newRef.getTarget().getName(), name);
+				}
+				return ReceiveCommand.link(oldId,
+						newRef.getTarget().getName(), name);
+			}
+			return ReceiveCommand.link(ObjectId.zeroId(),
+					newRef.getTarget().getName(), name);
+		}
+
+		return new ReceiveCommand(oldId, newId, name);
+	}
+
+	private static ObjectId toId(Ref ref) {
+		if (ref != null) {
+			ObjectId id = ref.getObjectId();
+			if (id != null) {
+				return id;
+			}
+		}
+		return ObjectId.zeroId();
+	}
+
+	/**
+	 * @return the lock protecting underlying ReftableReaders against concurrent
+	 *         reads.
+	 */
+	public ReentrantLock getLock() {
+		return lock;
+	}
+
+	/**
+	 * @return the merged reftable that is implemented by the stack of
+	 *         reftables. Return value must be accessed under lock.
+	 * @throws IOException
+	 *             on I/O problems
+	 */
+	private Reftable reader() throws IOException {
+		if (!lock.isLocked()) {
+			throw new IllegalStateException(
+					"must hold lock to access merged table"); //$NON-NLS-1$
+		}
+		if (mergedTables == null) {
+			mergedTables = openMergedReftable();
+		}
+		return mergedTables;
+	}
+
+	/**
+	 * @return whether the given refName would be illegal in a repository that
+	 *         uses loose refs.
+	 * @param refName
+	 *            the name to check
+	 * @param added
+	 *            a sorted set of refs we pretend have been added to the
+	 *            database.
+	 * @param deleted
+	 *            a set of refs we pretend have been removed from the database.
+	 * @throws IOException
+	 *             on I/O problems
+	 */
+	public boolean isNameConflicting(String refName, TreeSet<String> added,
+			Set<String> deleted) throws IOException {
+		lock.lock();
+		try {
+			Reftable table = reader();
+
+			// Cannot be nested within an existing reference.
+			int lastSlash = refName.lastIndexOf('/');
+			while (0 < lastSlash) {
+				String prefix = refName.substring(0, lastSlash);
+				if (!deleted.contains(prefix)
+						&& (table.hasRef(prefix) || added.contains(prefix))) {
+					return true;
+				}
+				lastSlash = refName.lastIndexOf('/', lastSlash - 1);
+			}
+
+			// Cannot be the container of an existing reference.
+			String prefix = refName + '/';
+			RefCursor c = table.seekRefsWithPrefix(prefix);
+			while (c.next()) {
+				if (!deleted.contains(c.getRef().getName())) {
+					return true;
+				}
+			}
+
+			String it = added.ceiling(refName + '/');
+			if (it != null && it.startsWith(prefix)) {
+				return true;
+			}
+			return false;
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/**
+	 * Read a single reference.
+	 * <p>
+	 * This method expects an unshortened reference name and does not search
+	 * using the standard search path.
+	 *
+	 * @param name
+	 *            the unabbreviated name of the reference.
+	 * @return the reference (if it exists); else {@code null}.
+	 * @throws java.io.IOException
+	 *             the reference space cannot be accessed.
+	 */
+	@Nullable
+	public Ref exactRef(String name) throws IOException {
+		lock.lock();
+		try {
+			Reftable table = reader();
+			Ref ref = table.exactRef(name);
+			if (ref != null && ref.isSymbolic()) {
+				return table.resolve(ref);
+			}
+			return ref;
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/**
+	 * Returns refs whose names start with a given prefix.
+	 *
+	 * @param prefix
+	 *            string that names of refs should start with; may be empty (to
+	 *            return all refs).
+	 * @return immutable list of refs whose names start with {@code prefix}.
+	 * @throws java.io.IOException
+	 *             the reference space cannot be accessed.
+	 */
+	public List<Ref> getRefsByPrefix(String prefix) throws IOException {
+		List<Ref> all = new ArrayList<>();
+		lock.lock();
+		try {
+			Reftable table = reader();
+			try (RefCursor rc = RefDatabase.ALL.equals(prefix) ? table.allRefs()
+					: table.seekRefsWithPrefix(prefix)) {
+				while (rc.next()) {
+					Ref ref = table.resolve(rc.getRef());
+					if (ref != null && ref.getObjectId() != null) {
+						all.add(ref);
+					}
+				}
+			}
+		} finally {
+			lock.unlock();
+		}
+
+		return Collections.unmodifiableList(all);
+	}
+
+	/**
+	 * @return whether there is a fast SHA1 to ref map.
+	 * @throws IOException in case of I/O problems.
+	 */
+	public boolean hasFastTipsWithSha1() throws IOException {
+		lock.lock();
+		try {
+			return reader().hasObjectMap();
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/**
+	 * Returns all refs that resolve directly to the given {@link ObjectId}.
+	 * Includes peeled {@linkObjectId}s.
+	 *
+	 * @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
+	 *             on I/O errors.
+	 */
+	public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
+		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();
+		}
+	}
+
+	/**
+	 * Drops all data that might be cached in memory.
+	 */
+	public void clearCache() {
+		lock.lock();
+		try {
+			mergedTables = null;
+		} finally {
+			lock.unlock();
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
index 4f0ff2d..3b912ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
@@ -78,7 +78,7 @@
  * {@code ReftableReader} is not thread-safe. Concurrent readers need their own
  * instance to read from the same file.
  */
-public class ReftableReader extends Reftable {
+public class ReftableReader extends Reftable implements AutoCloseable {
 	private final BlockSource src;
 
 	private int blockSize = -1;
@@ -128,16 +128,20 @@
 		return blockSize;
 	}
 
+	@Override
+	public boolean hasObjectMap() throws IOException {
+		if (objIndexPosition == -1) {
+			readFileFooter();
+		}
+
+		// We have the map, we have no refs, or the table is small.
+		return (objPosition > 0 || refEnd == 24 || refIndexPosition == 0);
+	}
+
 	/**
-	 * Get the minimum update index for log entries that appear in this
-	 * reftable.
-	 *
-	 * @return the minimum update index for log entries that appear in this
-	 *         reftable. This should be 1 higher than the prior reftable's
-	 *         {@code maxUpdateIndex} if this table is used in a stack.
-	 * @throws java.io.IOException
-	 *             file cannot be read.
+	 * {@inheritDoc}
 	 */
+	@Override
 	public long minUpdateIndex() throws IOException {
 		if (blockSize == -1) {
 			readFileHeader();
@@ -146,15 +150,9 @@
 	}
 
 	/**
-	 * Get the maximum update index for log entries that appear in this
-	 * reftable.
-	 *
-	 * @return the maximum update index for log entries that appear in this
-	 *         reftable. This should be 1 higher than the prior reftable's
-	 *         {@code maxUpdateIndex} if this table is used in a stack.
-	 * @throws java.io.IOException
-	 *             file cannot be read.
+	 * {@inheritDoc}
 	 */
+	@Override
 	public long maxUpdateIndex() throws IOException {
 		if (blockSize == -1) {
 			readFileHeader();
@@ -169,11 +167,13 @@
 			readFileHeader();
 		}
 
-		long end = refEnd > 0 ? refEnd : (src.size() - FILE_FOOTER_LEN);
-		src.adviseSequentialRead(0, end);
+		if (refEnd == 0) {
+			readFileFooter();
+		}
+		src.adviseSequentialRead(0, refEnd);
 
-		RefCursorImpl i = new RefCursorImpl(end, null, false);
-		i.block = readBlock(0, end);
+		RefCursorImpl i = new RefCursorImpl(refEnd, null, false);
+		i.block = readBlock(0, refEnd);
 		return i;
 	}
 
@@ -468,7 +468,7 @@
 
 		BlockReader b = new BlockReader();
 		b.readBlock(src, pos, sz);
-		if (b.type() == INDEX_BLOCK_TYPE && !b.truncated()) {
+		if (b.type() == INDEX_BLOCK_TYPE) {
 			if (indexCache == null) {
 				indexCache = new LongMap<>();
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReflogReader.java
new file mode 100644
index 0000000..c75d3cf
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReflogReader.java
@@ -0,0 +1,125 @@
+/*
+ * 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.reftable;
+
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * Implement the ReflogReader interface for a reflog stored in reftable.
+ */
+public class ReftableReflogReader implements ReflogReader {
+	private final Lock lock;
+
+	private final Reftable reftable;
+
+	private final String refname;
+
+	ReftableReflogReader(Lock lock, Reftable merged, String refname) {
+		this.lock = lock;
+		this.reftable = merged;
+		this.refname = refname;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public ReflogEntry getLastEntry() throws IOException {
+		lock.lock();
+		try {
+			LogCursor cursor = reftable.seekLog(refname);
+			return cursor.next() ? cursor.getReflogEntry() : null;
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<ReflogEntry> getReverseEntries() throws IOException {
+		return getReverseEntries(Integer.MAX_VALUE);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public ReflogEntry getReverseEntry(int number) throws IOException {
+		lock.lock();
+		try {
+			LogCursor cursor = reftable.seekLog(refname);
+			while (true) {
+				if (!cursor.next() || number < 0) {
+					return null;
+				}
+				if (number == 0) {
+					return cursor.getReflogEntry();
+				}
+				number--;
+			}
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<ReflogEntry> getReverseEntries(int max) throws IOException {
+		lock.lock();
+		try {
+			LogCursor cursor = reftable.seekLog(refname);
+
+			List<ReflogEntry> result = new ArrayList<>();
+			while (cursor.next() && result.size() < max) {
+				result.add(cursor.getReflogEntry());
+			}
+
+			return result;
+		} finally {
+			lock.unlock();
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java
index 6459c27..96f8cb1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableWriter.java
@@ -108,6 +108,7 @@
 	private long minUpdateIndex;
 	private long maxUpdateIndex;
 
+	private OutputStream outputStream;
 	private ReftableOutputStream out;
 	private ObjectIdSubclassMap<RefList> obj2ref;
 
@@ -122,21 +123,27 @@
 
 	/**
 	 * Initialize a writer with a default configuration.
+	 *
+	 * @param os
+	 *            output stream.
 	 */
-	public ReftableWriter() {
-		this(new ReftableConfig());
+	public ReftableWriter(OutputStream os) {
+		this(new ReftableConfig(), os);
 		lastRef = null;
 		lastLog = null;
 	}
 
 	/**
-	 * Initialize a writer with a specific configuration.
+	 * Initialize a writer with a configuration.
 	 *
 	 * @param cfg
-	 *            configuration for the writer.
+	 *            configuration for the writer
+	 * @param os
+	 *            output stream.
 	 */
-	public ReftableWriter(ReftableConfig cfg) {
+	public ReftableWriter(ReftableConfig cfg, OutputStream os) {
 		config = cfg;
+		outputStream = os;
 	}
 
 	/**
@@ -183,16 +190,16 @@
 	}
 
 	/**
-	 * Begin writing the reftable.
+	 * Begin writing the reftable. Should be called only once. Call this
+	 * if a stream was passed to the constructor.
 	 *
-	 * @param os
-	 *            stream to write the table to. Caller is responsible for
-	 *            closing the stream after invoking {@link #finish()}.
 	 * @return {@code this}
-	 * @throws java.io.IOException
-	 *             if reftable header cannot be written.
 	 */
-	public ReftableWriter begin(OutputStream os) throws IOException {
+	public ReftableWriter begin() {
+		if (out != null) {
+			throw new IllegalStateException("begin() called twice.");//$NON-NLS-1$
+		}
+
 		refBlockSize = config.getRefBlockSize();
 		logBlockSize = config.getLogBlockSize();
 		restartInterval = config.getRestartInterval();
@@ -212,7 +219,7 @@
 			restartInterval = refBlockSize < (60 << 10) ? 16 : 64;
 		}
 
-		out = new ReftableOutputStream(os, refBlockSize, alignBlocks);
+		out = new ReftableOutputStream(outputStream, refBlockSize, alignBlocks);
 		refs = new Section(REF_BLOCK_TYPE);
 		if (indexObjects) {
 			obj2ref = new ObjectIdSubclassMap<>();
@@ -223,6 +230,7 @@
 
 	/**
 	 * Sort a collection of references and write them to the reftable.
+	 * The input refs may not have duplicate names.
 	 *
 	 * @param refsToPack
 	 *            references to sort and write.
@@ -236,10 +244,16 @@
 				.map(r -> new RefEntry(r, maxUpdateIndex - minUpdateIndex))
 				.sorted(Entry::compare)
 				.iterator();
+		RefEntry last = null;
 		while (itr.hasNext()) {
 			RefEntry entry = itr.next();
+			if (last != null && Entry.compare(last, entry) == 0) {
+				throwIllegalEntry(last, entry);
+			}
+
 			long blockPos = refs.write(entry);
 			indexRef(entry.ref, blockPos);
+			last = entry;
 		}
 		return this;
 	}
@@ -288,7 +302,7 @@
 
 	private void throwIllegalEntry(Entry last, Entry now) {
 		throw new IllegalArgumentException(MessageFormat.format(
-				JGitText.get().refTableRecordsMustIncrease,
+				JGitText.get().reftableRecordsMustIncrease,
 				new String(last.key, UTF_8), new String(now.key, UTF_8)));
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
index da98e3f..9c5423f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
@@ -102,9 +102,8 @@
 					if (isAtomic()) {
 						ReceiveCommand.abort(getCommands());
 						return;
-					} else {
-						continue;
 					}
+					continue;
 				}
 			}
 			todo.add(new Command(rw, c));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java
index 882b2d0..39a67af 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java
@@ -82,9 +82,8 @@
 	public static NetscapeCookieFileCache getInstance(HttpConfig config) {
 		if (instance == null) {
 			return new NetscapeCookieFileCache(config);
-		} else {
-			return instance;
 		}
+		return instance;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index c1e94a0..ee6adee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -384,9 +384,8 @@
 	private static boolean isHostMatch(String pattern, String name) {
 		if (pattern.startsWith("!")) { //$NON-NLS-1$
 			return !patternMatchesHost(pattern.substring(1), name);
-		} else {
-			return patternMatchesHost(pattern, name);
 		}
+		return patternMatchesHost(pattern, name);
 	}
 
 	private static boolean patternMatchesHost(String pattern, String name) {
@@ -399,10 +398,9 @@
 			}
 			fn.append(name);
 			return fn.isMatch();
-		} else {
-			// Not a pattern but a full host name
-			return pattern.equals(name);
 		}
+		// Not a pattern but a full host name
+		return pattern.equals(name);
 	}
 
 	private static String dequote(String value) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index 4f90e69..24850ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -302,10 +302,10 @@
 	/** {@inheritDoc} */
 	@Override
 	public final boolean equals(Object o) {
-		if (o instanceof AnyObjectId)
+		if (o instanceof AnyObjectId) {
 			return equals((AnyObjectId) o);
-		else
-			return false;
+		}
+		return false;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
index 96e5066..98a46f3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -103,25 +103,29 @@
 	private static File getSymRef(File workTree, File dotGit, FS fs)
 			throws IOException {
 		byte[] content = IO.readFully(dotGit);
-		if (!isSymRef(content))
+		if (!isSymRef(content)) {
 			throw new IOException(MessageFormat.format(
 					JGitText.get().invalidGitdirRef, dotGit.getAbsolutePath()));
+		}
 
 		int pathStart = 8;
 		int lineEnd = RawParseUtils.nextLF(content, pathStart);
 		while (content[lineEnd - 1] == '\n' ||
-		       (content[lineEnd - 1] == '\r' && SystemReader.getInstance().isWindows()))
+				(content[lineEnd - 1] == '\r'
+						&& SystemReader.getInstance().isWindows())) {
 			lineEnd--;
-		if (lineEnd == pathStart)
+		}
+		if (lineEnd == pathStart) {
 			throw new IOException(MessageFormat.format(
 					JGitText.get().invalidGitdirRef, dotGit.getAbsolutePath()));
+		}
 
 		String gitdirPath = RawParseUtils.decode(content, pathStart, lineEnd);
 		File gitdirFile = fs.resolve(workTree, gitdirPath);
-		if (gitdirFile.isAbsolute())
+		if (gitdirFile.isAbsolute()) {
 			return gitdirFile;
-		else
-			return new File(workTree, gitdirPath).getCanonicalFile();
+		}
+		return new File(workTree, gitdirPath).getCanonicalFile();
 	}
 
 	private FS fs;
@@ -723,9 +727,8 @@
 								.getAbsolutePath(), err.getMessage()));
 			}
 			return cfg;
-		} else {
-			return new Config();
 		}
+		return new Config();
 	}
 
 	private File guessWorkTreeOrFail() throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
index be53c4b..cad747b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
@@ -232,9 +232,9 @@
 
 	private String getRemoteOrDefault() {
 		String remote = getRemote();
-		if (remote == null)
+		if (remote == null) {
 			return Constants.DEFAULT_REMOTE_NAME;
-		else
-			return remote;
+		}
+		return remote;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index d26b7fd..2ef3653 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -1407,12 +1407,11 @@
 				}
 				trailingSpaces.append(cc);
 				continue;
-			} else {
-				inLeadingSpace = false;
-				if (trailingSpaces != null) {
-					value.append(trailingSpaces);
-					trailingSpaces.setLength(0);
-				}
+			}
+			inLeadingSpace = false;
+			if (trailingSpaces != null) {
+				value.append(trailingSpaces);
+				trailingSpaces.setLength(0);
 			}
 
 			if ('\\' == c) {
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 078bf78..99512a6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -149,6 +149,18 @@
 	 */
 	public static final String CONFIG_KEY_GPGSIGN = "gpgSign";
 
+	/**
+	 * The "hooksPath" key.
+	 * @since 5.6
+	 */
+	public static final String CONFIG_KEY_HOOKS_PATH = "hooksPath";
+
+	/**
+	 * The "quotePath" key.
+	 * @since 5.6
+	 */
+	public static final String CONFIG_KEY_QUOTE_PATH = "quotePath";
+
 	/** The "algorithm" key */
 	public static final String CONFIG_KEY_ALGORITHM = "algorithm";
 
@@ -432,6 +444,12 @@
 	public static final String CONFIG_RENAMELIMIT_COPIES = "copies";
 
 	/**
+	 * A "refStorage" value in the "extensions".
+	 * @since 5.6.2
+	 */
+	public static final String CONFIG_REF_STORAGE_REFTABLE = "reftable";
+
+	/**
 	 * The "renames" key in the "diff" section
 	 * @since 3.0
 	 */
@@ -526,9 +544,24 @@
 	 */
 	public static final String CONFIG_KEY_MIN_RACY_THRESHOLD = "minRacyThreshold";
 
+
+	/**
+	 * The "refStorage" key
+	 *
+	 * @since 5.6.2
+	 */
+	public static final String CONFIG_KEY_REF_STORAGE = "refStorage";
+
 	/**
 	 * The "jmx" section
 	 * @since 5.1.13
 	 */
 	public static final String CONFIG_JMX_SECTION = "jmx";
+
+	/**
+	 * The "extensions" section
+	 *
+	 * @since 5.6.2
+	 */
+	public static final String CONFIG_EXTENSIONS_SECTION = "extensions";
 }
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 9274fc6..3c05cab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -281,6 +281,18 @@
 	 */
 	public static final String OBJECTS = "objects";
 
+	/**
+	 * Reftable folder name
+	 * @since 5.6
+	 */
+	public static final String REFTABLE = "reftable";
+
+	/**
+	 * Reftable table list name.
+	 * @since 5.6.2
+	 */
+	public static final String TABLES_LIST = "tables.list";
+
 	/** Info refs folder */
 	public static final String INFO_REFS = "info/refs";
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 98de3a9..0056ad7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -60,7 +60,7 @@
 	public static final Config.SectionParser<CoreConfig> KEY = CoreConfig::new;
 
 	/** Permissible values for {@code core.autocrlf}. */
-	public static enum AutoCRLF {
+	public enum AutoCRLF {
 		/** Automatic CRLF-&gt;LF conversion is disabled. */
 		FALSE,
 
@@ -78,45 +78,45 @@
 	 *
 	 * @since 4.3
 	 */
-	public static enum EOL {
-		/** checkin with LF, checkout with CRLF. */
+	public enum EOL {
+		/** Check in with LF, check out with CRLF. */
 		CRLF,
 
-		/** checkin with LF, checkout without conversion. */
+		/** Check in with LF, check out without conversion. */
 		LF,
 
-		/** use the platform's native line ending. */
+		/** Use the platform's native line ending. */
 		NATIVE;
 	}
 
 	/**
-	 * EOL stream conversion protocol
+	 * EOL stream conversion protocol.
 	 *
 	 * @since 4.3
 	 */
-	public static enum EolStreamType {
-		/** convert to CRLF without binary detection */
+	public enum EolStreamType {
+		/** Convert to CRLF without binary detection. */
 		TEXT_CRLF,
 
-		/** convert to LF without binary detection */
+		/** Convert to LF without binary detection. */
 		TEXT_LF,
 
-		/** convert to CRLF with binary detection */
+		/** Convert to CRLF with binary detection. */
 		AUTO_CRLF,
 
-		/** convert to LF with binary detection */
+		/** Convert to LF with binary detection. */
 		AUTO_LF,
 
-		/** do not convert */
+		/** Do not convert. */
 		DIRECT;
 	}
 
 	/**
-	 * Permissible values for {@code core.checkstat}
+	 * Permissible values for {@code core.checkstat}.
 	 *
 	 * @since 3.0
 	 */
-	public static enum CheckStat {
+	public enum CheckStat {
 		/**
 		 * Only check the size and whole second part of time stamp when
 		 * comparing the stat info in the dircache with actual file stat info.
@@ -130,11 +130,30 @@
 		DEFAULT
 	}
 
+	/**
+	 * Permissible values for {@code core.logAllRefUpdates}.
+	 *
+	 * @since 5.6
+	 */
+	public enum LogRefUpdates {
+		/** Don't create ref logs; default for bare repositories. */
+		FALSE,
+
+		/**
+		 * Create ref logs for refs/heads/**, refs/remotes/**, refs/notes/**,
+		 * and for HEAD. Default for non-bare repositories.
+		 */
+		TRUE,
+
+		/** Create ref logs for all refs/** and for HEAD. */
+		ALWAYS
+	}
+
 	private final int compression;
 
 	private final int packIndexVersion;
 
-	private final boolean logAllRefUpdates;
+	private final LogRefUpdates logAllRefUpdates;
 
 	private final String excludesfile;
 
@@ -145,24 +164,27 @@
 	 *
 	 * @since 3.3
 	 */
-	public static enum SymLinks {
-		/** Checkout symbolic links as plain files */
+	public enum SymLinks {
+		/** Check out symbolic links as plain files . */
 		FALSE,
-		/** Checkout symbolic links as links */
+
+		/** Check out symbolic links as links. */
 		TRUE
 	}
 
 	/**
-	 * Options for hiding files whose names start with a period
+	 * Options for hiding files whose names start with a period.
 	 *
 	 * @since 3.5
 	 */
-	public static enum HideDotFiles {
-		/** Do not hide .files */
+	public enum HideDotFiles {
+		/** Do not hide .files. */
 		FALSE,
-		/** Hide add .files */
+
+		/** Hide add .files. */
 		TRUE,
-		/** Hide only .git */
+
+		/** Hide only .git. */
 		DOTGITONLY
 	}
 
@@ -171,8 +193,9 @@
 				ConfigConstants.CONFIG_KEY_COMPRESSION, DEFAULT_COMPRESSION);
 		packIndexVersion = rc.getInt(ConfigConstants.CONFIG_PACK_SECTION,
 				ConfigConstants.CONFIG_KEY_INDEXVERSION, 2);
-		logAllRefUpdates = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
-				ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
+		logAllRefUpdates = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES,
+				LogRefUpdates.TRUE);
 		excludesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_EXCLUDESFILE);
 		attributesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION,
@@ -201,9 +224,14 @@
 	 * Whether to log all refUpdates
 	 *
 	 * @return whether to log all refUpdates
+	 * @deprecated since 5.6; default value depends on whether the repository is
+	 *             bare. Use
+	 *             {@link Config#getEnum(String, String, String, Enum)}
+	 *             directly.
 	 */
+	@Deprecated
 	public boolean isLogAllRefUpdates() {
-		return logAllRefUpdates;
+		return !LogRefUpdates.FALSE.equals(logAllRefUpdates);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index e865da8..23e8de0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -134,11 +134,9 @@
 			throw new IllegalArgumentException(
 					MessageFormat.format(JGitText.get().enumValueNotSupported3,
 							section, subsection, name, value));
-		} else {
-			throw new IllegalArgumentException(
-					MessageFormat.format(JGitText.get().enumValueNotSupported2,
-							section, name, value));
 		}
+		throw new IllegalArgumentException(MessageFormat.format(
+				JGitText.get().enumValueNotSupported2, section, name, value));
 	}
 
 	/** {@inheritDoc} */
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 ce1eb59..cbb3e54 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -108,7 +108,7 @@
 	 * @see IndexDiff#getConflictingStageStates()
 	 * @since 3.0
 	 */
-	public static enum StageState {
+	public enum StageState {
 		/**
 		 * Exists in base, but neither in ours nor in theirs.
 		 */
@@ -243,11 +243,11 @@
 		}
 	}
 
-	private final static int TREE = 0;
+	private static final int TREE = 0;
 
-	private final static int INDEX = 1;
+	private static final int INDEX = 1;
 
-	private final static int WORKDIR = 2;
+	private static final int WORKDIR = 2;
 
 	private final Repository repository;
 
@@ -384,7 +384,32 @@
 	 * @throws java.io.IOException
 	 */
 	public boolean diff() throws IOException {
-		return diff(null, 0, 0, ""); //$NON-NLS-1$
+		return diff(null);
+	}
+
+	/**
+	 * Run the diff operation. Until this is called, all lists will be empty.
+	 * Use
+	 * {@link #diff(ProgressMonitor, int, int, String, RepositoryBuilderFactory)}
+	 * if a progress monitor is required.
+	 * <p>
+	 * The operation may create repositories for submodules using builders
+	 * provided by the given {@code factory}, if any, and will also close these
+	 * submodule repositories again.
+	 * </p>
+	 *
+	 * @param factory
+	 *            the {@link RepositoryBuilderFactory} to use to create builders
+	 *            to create submodule repositories, if needed; if {@code null},
+	 *            submodule repositories will be built using a plain
+	 *            {@link RepositoryBuilder}.
+	 * @return if anything is different between index, tree, and workdir
+	 * @throws java.io.IOException
+	 * @since 5.6
+	 */
+	public boolean diff(RepositoryBuilderFactory factory)
+			throws IOException {
+		return diff(null, 0, 0, "", factory); //$NON-NLS-1$
 	}
 
 	/**
@@ -410,6 +435,45 @@
 	public boolean diff(final ProgressMonitor monitor, int estWorkTreeSize,
 			int estIndexSize, final String title)
 			throws IOException {
+		return diff(monitor, estWorkTreeSize, estIndexSize, title, null);
+	}
+
+	/**
+	 * Run the diff operation. Until this is called, all lists will be empty.
+	 * <p>
+	 * The operation may be aborted by the progress monitor. In that event it
+	 * will report what was found before the cancel operation was detected.
+	 * Callers should ignore the result if monitor.isCancelled() is true. If a
+	 * progress monitor is not needed, callers should use {@link #diff()}
+	 * instead. Progress reporting is crude and approximate and only intended
+	 * for informing the user.
+	 * </p>
+	 * <p>
+	 * The operation may create repositories for submodules using builders
+	 * provided by the given {@code factory}, if any, and will also close these
+	 * submodule repositories again.
+	 * </p>
+	 *
+	 * @param monitor
+	 *            for reporting progress, may be null
+	 * @param estWorkTreeSize
+	 *            number or estimated files in the working tree
+	 * @param estIndexSize
+	 *            number of estimated entries in the cache
+	 * @param title
+	 *            a {@link java.lang.String} object.
+	 * @param factory
+	 *            the {@link RepositoryBuilderFactory} to use to create builders
+	 *            to create submodule repositories, if needed; if {@code null},
+	 *            submodule repositories will be built using a plain
+	 *            {@link RepositoryBuilder}.
+	 * @return if anything is different between index, tree, and workdir
+	 * @throws java.io.IOException
+	 * @since 5.6
+	 */
+	public boolean diff(ProgressMonitor monitor, int estWorkTreeSize,
+			int estIndexSize, String title, RepositoryBuilderFactory factory)
+			throws IOException {
 		dirCache = repository.readDirCache();
 
 		try (TreeWalk treeWalk = new TreeWalk(repository)) {
@@ -535,64 +599,69 @@
 		}
 
 		if (ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL) {
-			IgnoreSubmoduleMode localIgnoreSubmoduleMode = ignoreSubmoduleMode;
-			SubmoduleWalk smw = SubmoduleWalk.forIndex(repository);
-			while (smw.next()) {
-				try {
-					if (localIgnoreSubmoduleMode == null)
-						localIgnoreSubmoduleMode = smw.getModulesIgnore();
-					if (IgnoreSubmoduleMode.ALL
-							.equals(localIgnoreSubmoduleMode))
-						continue;
-				} catch (ConfigInvalidException e) {
-					throw new IOException(MessageFormat.format(
-							JGitText.get().invalidIgnoreParamSubmodule,
-							smw.getPath()), e);
-				}
-				try (Repository subRepo = smw.getRepository()) {
-					String subRepoPath = smw.getPath();
-					if (subRepo != null) {
-						ObjectId subHead = subRepo.resolve("HEAD"); //$NON-NLS-1$
-						if (subHead != null
-								&& !subHead.equals(smw.getObjectId())) {
-							modified.add(subRepoPath);
-							recordFileMode(subRepoPath, FileMode.GITLINK);
-						} else if (ignoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY) {
-							IndexDiff smid = submoduleIndexDiffs.get(smw
-									.getPath());
-							if (smid == null) {
-								smid = new IndexDiff(subRepo,
-										smw.getObjectId(),
-										wTreeIt.getWorkingTreeIterator(subRepo));
-								submoduleIndexDiffs.put(subRepoPath, smid);
-							}
-							if (smid.diff()) {
-								if (ignoreSubmoduleMode == IgnoreSubmoduleMode.UNTRACKED
-										&& smid.getAdded().isEmpty()
-										&& smid.getChanged().isEmpty()
-										&& smid.getConflicting().isEmpty()
-										&& smid.getMissing().isEmpty()
-										&& smid.getModified().isEmpty()
-										&& smid.getRemoved().isEmpty()) {
-									continue;
-								}
+			try (SubmoduleWalk smw = new SubmoduleWalk(repository)) {
+				smw.setTree(new DirCacheIterator(dirCache));
+				smw.setBuilderFactory(factory);
+				while (smw.next()) {
+					IgnoreSubmoduleMode localIgnoreSubmoduleMode = ignoreSubmoduleMode;
+					try {
+						if (localIgnoreSubmoduleMode == null)
+							localIgnoreSubmoduleMode = smw.getModulesIgnore();
+						if (IgnoreSubmoduleMode.ALL
+								.equals(localIgnoreSubmoduleMode))
+							continue;
+					} catch (ConfigInvalidException e) {
+						throw new IOException(MessageFormat.format(
+								JGitText.get().invalidIgnoreParamSubmodule,
+								smw.getPath()), e);
+					}
+					try (Repository subRepo = smw.getRepository()) {
+						String subRepoPath = smw.getPath();
+						if (subRepo != null) {
+							ObjectId subHead = subRepo.resolve("HEAD"); //$NON-NLS-1$
+							if (subHead != null
+									&& !subHead.equals(smw.getObjectId())) {
 								modified.add(subRepoPath);
 								recordFileMode(subRepoPath, FileMode.GITLINK);
+							} else if (localIgnoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY) {
+								IndexDiff smid = submoduleIndexDiffs
+										.get(smw.getPath());
+								if (smid == null) {
+									smid = new IndexDiff(subRepo,
+											smw.getObjectId(),
+											wTreeIt.getWorkingTreeIterator(
+													subRepo));
+									submoduleIndexDiffs.put(subRepoPath, smid);
+								}
+								if (smid.diff(factory)) {
+									if (localIgnoreSubmoduleMode == IgnoreSubmoduleMode.UNTRACKED
+											&& smid.getAdded().isEmpty()
+											&& smid.getChanged().isEmpty()
+											&& smid.getConflicting().isEmpty()
+											&& smid.getMissing().isEmpty()
+											&& smid.getModified().isEmpty()
+											&& smid.getRemoved().isEmpty()) {
+										continue;
+									}
+									modified.add(subRepoPath);
+									recordFileMode(subRepoPath,
+											FileMode.GITLINK);
+								}
 							}
-						}
-					} else if (missingSubmodules.remove(subRepoPath)) {
-						// If the directory is there and empty but the submodule
-						// repository in .git/modules doesn't exist yet it isn't
-						// "missing".
-						File gitDir = new File(
-								new File(repository.getDirectory(),
-										Constants.MODULES),
-								subRepoPath);
-						if (!gitDir.isDirectory()) {
-							File dir = SubmoduleWalk.getSubmoduleDirectory(
-									repository, subRepoPath);
-							if (dir.isDirectory() && !hasFiles(dir)) {
-								missing.remove(subRepoPath);
+						} else if (missingSubmodules.remove(subRepoPath)) {
+							// If the directory is there and empty but the
+							// submodule repository in .git/modules doesn't
+							// exist yet it isn't "missing".
+							File gitDir = new File(
+									new File(repository.getDirectory(),
+											Constants.MODULES),
+									subRepoPath);
+							if (!gitDir.isDirectory()) {
+								File dir = SubmoduleWalk.getSubmoduleDirectory(
+										repository, subRepoPath);
+								if (dir.isDirectory() && !hasFiles(dir)) {
+									missing.remove(subRepoPath);
+								}
 							}
 						}
 					}
@@ -602,16 +671,17 @@
 		}
 
 		// consume the remaining work
-		if (monitor != null)
+		if (monitor != null) {
 			monitor.endTask();
+		}
 
 		ignored = indexDiffFilter.getIgnoredPaths();
 		if (added.isEmpty() && changed.isEmpty() && removed.isEmpty()
 				&& missing.isEmpty() && modified.isEmpty()
-				&& untracked.isEmpty())
+				&& untracked.isEmpty()) {
 			return false;
-		else
-			return true;
+		}
+		return true;
 	}
 
 	private boolean hasFiles(File directory) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/InflaterCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/InflaterCache.java
index dd0f18e..314bde2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/InflaterCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/InflaterCache.java
@@ -73,7 +73,7 @@
 		return r != null ? r : new Inflater(false);
 	}
 
-	private synchronized static Inflater getImpl() {
+	private static synchronized Inflater getImpl() {
 		if (openInflaterCount > 0) {
 			final Inflater r = inflaterCache[--openInflaterCount];
 			inflaterCache[openInflaterCount] = null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java
index 74c712c..8730d66 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java
@@ -355,7 +355,7 @@
 	}
 
 	/** Type of entry stored in the {@link ObjectIdOwnerMap}. */
-	public static abstract class Entry extends ObjectId {
+	public abstract static class Entry extends ObjectId {
 		transient Entry next;
 
 		/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
index 77fa1b2..edbb4b1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
@@ -98,7 +98,7 @@
 	}
 
 	/** Wraps a delegate ObjectInserter. */
-	public static abstract class Filter extends ObjectInserter {
+	public abstract static class Filter extends ObjectInserter {
 		/** @return delegate ObjectInserter to handle all processing. */
 		protected abstract ObjectInserter delegate();
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
index 2e52f03..b473421 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
@@ -333,7 +333,7 @@
 	 *
 	 * @since 4.10
 	 */
-	public static abstract class Filter extends ObjectLoader {
+	public abstract static class Filter extends ObjectLoader {
 		/**
 		 * @return delegate ObjectLoader to handle all processing.
 		 * @since 4.10
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index 700b9db..6d54467 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -293,9 +293,8 @@
 				if (idItr.hasNext()) {
 					cur = idItr.next();
 					return true;
-				} else {
-					return false;
 				}
+				return false;
 			}
 
 			@Override
@@ -383,9 +382,8 @@
 					cur = idItr.next();
 					sz = getObjectSize(cur, OBJ_ANY);
 					return true;
-				} else {
-					return false;
 				}
+				return false;
 			}
 
 			@Override
@@ -497,7 +495,7 @@
 	 *
 	 * @since 4.4
 	 */
-	public static abstract class Filter extends ObjectReader {
+	public abstract static class Filter extends ObjectReader {
 		/**
 		 * @return delegate ObjectReader to handle all processing.
 		 * @since 4.4
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoLine.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoLine.java
index 4e235b0..f893fd1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoLine.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoLine.java
@@ -58,7 +58,7 @@
 	 * Describes rebase actions
 	 */
 	@SuppressWarnings("nls")
-	public static enum Action {
+	public enum Action {
 		/** Use commit */
 		PICK("pick", "p"),
 
@@ -105,7 +105,7 @@
 		 * @param token
 		 * @return the Action
 		 */
-		static public Action parse(String token) {
+		public static Action parse(String token) {
 			for (Action action : Action.values()) {
 				if (action.token.equals(token)
 						|| action.shortToken.equals(token))
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 4d9450e..9b5a1fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -497,6 +497,20 @@
 	}
 
 	/**
+	 * If the ref database does not support fast inverse queries, it may
+	 * be advantageous to build a complete SHA1 to ref map in advance for
+	 * multiple uses. To let applications decide on this decision,
+	 * this function indicates whether the inverse map is available.
+	 *
+	 * @return whether this RefDatabase supports fast inverse ref queries.
+	 * @throws IOException on I/O problems.
+	 * @since 5.6
+	 */
+	public boolean hasFastTipsWithSha1() throws IOException {
+		return false;
+	}
+
+	/**
 	 * Check if any refs exist in the ref database.
 	 * <p>
 	 * This uses the same definition of refs as {@link #getRefs()}. In
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 eca15c0..eb5e139 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -66,7 +66,7 @@
 	 * unknown values are failures, and may generally treat them the same as
 	 * {@link #REJECTED_OTHER_REASON}.
 	 */
-	public static enum Result {
+	public enum Result {
 		/** The ref update/delete has not been attempted by the caller. */
 		NOT_ATTEMPTED,
 
@@ -827,7 +827,7 @@
 	 * Handle the abstraction of storing a ref update. This is because both
 	 * updating and deleting of a ref have merge testing in common.
 	 */
-	private static abstract class Store {
+	private abstract static class Store {
 		abstract Result execute(Result status) throws IOException;
 	}
 }
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 68866ea..0e9cf58 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -503,9 +503,8 @@
 			if (resolved instanceof String) {
 				final Ref ref = findRef((String) resolved);
 				return ref != null ? ref.getLeaf().getObjectId() : null;
-			} else {
-				return (ObjectId) resolved;
 			}
+			return (ObjectId) resolved;
 		}
 	}
 
@@ -527,11 +526,12 @@
 		try (RevWalk rw = new RevWalk(this)) {
 			rw.setRetainBody(true);
 			Object resolved = resolve(rw, revstr);
-			if (resolved != null)
-				if (resolved instanceof String)
+			if (resolved != null) {
+				if (resolved instanceof String) {
 					return (String) resolved;
-				else
-					return ((AnyObjectId) resolved).getName();
+				}
+				return ((AnyObjectId) resolved).getName();
+			}
 			return null;
 		}
 	}
@@ -760,15 +760,15 @@
 						if (name == null)
 							throw new RevisionSyntaxException(revstr);
 					} else if (time.matches("^-\\d+$")) { //$NON-NLS-1$
-						if (name != null)
+						if (name != null) {
 							throw new RevisionSyntaxException(revstr);
-						else {
-							String previousCheckout = resolveReflogCheckout(-Integer
-									.parseInt(time));
-							if (ObjectId.isId(previousCheckout))
-								rev = parseSimple(rw, previousCheckout);
-							else
-								name = previousCheckout;
+						}
+						String previousCheckout = resolveReflogCheckout(
+								-Integer.parseInt(time));
+						if (ObjectId.isId(previousCheckout)) {
+							rev = parseSimple(rw, previousCheckout);
+						} else {
+							name = previousCheckout;
 						}
 					} else {
 						if (name == null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilderFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilderFactory.java
new file mode 100644
index 0000000..fc12516
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilderFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.util.function.Supplier;
+
+/**
+ * A factory for {@link BaseRepositoryBuilder}s.
+ * <p>
+ * Note that a {@link BaseRepositoryBuilder} should be used only once to build a
+ * repository. Otherwise subsequently built repositories may be built using
+ * settings made for earlier built repositories.
+ * </p>
+ *
+ * @since 5.6
+ */
+public interface RepositoryBuilderFactory extends
+		Supplier<BaseRepositoryBuilder<? extends BaseRepositoryBuilder, ? extends Repository>> {
+	// Empty
+}
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 fa113bf..5a6063e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -66,7 +66,7 @@
  * Cache of active {@link org.eclipse.jgit.lib.Repository} instances.
  */
 public class RepositoryCache {
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(RepositoryCache.class);
 
 	private static final RepositoryCache cache = new RepositoryCache();
@@ -476,7 +476,8 @@
 		public static boolean isGitRepository(File dir, FS fs) {
 			return fs.resolve(dir, Constants.OBJECTS).exists()
 					&& fs.resolve(dir, "refs").exists() //$NON-NLS-1$
-					&& isValidHead(new File(dir, Constants.HEAD));
+					&& (fs.resolve(dir, Constants.REFTABLE).exists()
+							|| isValidHead(new File(dir, Constants.HEAD)));
 		}
 
 		private static boolean isValidHead(File head) {
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 11db7c5..f28334c 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
@@ -72,12 +72,14 @@
 import org.bouncycastle.gpg.keybox.UserID;
 import org.bouncycastle.gpg.keybox.jcajce.JcaKeyBoxBuilder;
 import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPKeyFlags;
 import org.bouncycastle.openpgp.PGPPublicKey;
 import org.bouncycastle.openpgp.PGPPublicKeyRing;
 import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
 import org.bouncycastle.openpgp.PGPSecretKey;
 import org.bouncycastle.openpgp.PGPSecretKeyRing;
 import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSignature;
 import org.bouncycastle.openpgp.PGPUtil;
 import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
 import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
@@ -90,6 +92,7 @@
 import org.eclipse.jgit.errors.UnsupportedCredentialItem;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.StringUtils;
 import org.eclipse.jgit.util.SystemReader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -190,19 +193,92 @@
 		}
 	}
 
-	private boolean containsSigningKey(String userId) {
-		return userId.toLowerCase(Locale.ROOT)
-				.contains(signingKey.toLowerCase(Locale.ROOT));
+	/**
+	 * Checks whether a given OpenPGP {@code userId} matches a given
+	 * {@code signingKeySpec}, which is supposed to have one of the formats
+	 * defined by GPG.
+	 * <p>
+	 * Not all formats are supported; only formats starting with '=', '&lt;',
+	 * '@', and '*' are handled. Any other format results in a case-insensitive
+	 * substring match.
+	 * </p>
+	 *
+	 * @param userId
+	 *            of a key
+	 * @param signingKeySpec
+	 *            GPG key identification
+	 * @return whether the {@code userId} matches
+	 * @see <a href=
+	 *      "https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html">GPG
+	 *      Documentation: How to Specify a User ID</a>
+	 */
+	static boolean containsSigningKey(String userId, String signingKeySpec) {
+		if (StringUtils.isEmptyOrNull(userId)
+				|| StringUtils.isEmptyOrNull(signingKeySpec)) {
+			return false;
+		}
+		String toMatch = signingKeySpec;
+		if (toMatch.startsWith("0x") && toMatch.trim().length() > 2) { //$NON-NLS-1$
+			return false; // Explicit fingerprint
+		}
+		int command = toMatch.charAt(0);
+		switch (command) {
+		case '=':
+		case '<':
+		case '@':
+		case '*':
+			toMatch = toMatch.substring(1);
+			if (toMatch.isEmpty()) {
+				return false;
+			}
+			break;
+		default:
+			break;
+		}
+		switch (command) {
+		case '=':
+			return userId.equals(toMatch);
+		case '<': {
+			int begin = userId.indexOf('<');
+			int end = userId.indexOf('>', begin + 1);
+			int stop = toMatch.indexOf('>');
+			return begin >= 0 && end > begin + 1 && stop > 0
+					&& userId.substring(begin + 1, end)
+							.equals(toMatch.substring(0, stop));
+		}
+		case '@': {
+			int begin = userId.indexOf('<');
+			int end = userId.indexOf('>', begin + 1);
+			return begin >= 0 && end > begin + 1
+					&& userId.substring(begin + 1, end).contains(toMatch);
+		}
+		default:
+			if (toMatch.trim().isEmpty()) {
+				return false;
+			}
+			return userId.toLowerCase(Locale.ROOT)
+					.contains(toMatch.toLowerCase(Locale.ROOT));
+		}
+	}
+
+	private String toFingerprint(String keyId) {
+		if (keyId.startsWith("0x")) { //$NON-NLS-1$
+			return keyId.substring(2);
+		}
+		return keyId;
 	}
 
 	private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob)
 			throws IOException {
-		String keyId = signingKey.toLowerCase(Locale.ROOT);
+		String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
+		if (keyId.isEmpty()) {
+			return null;
+		}
 		for (KeyInformation keyInfo : keyBlob.getKeyInformation()) {
 			String fingerprint = Hex.toHexString(keyInfo.getFingerprint())
 					.toLowerCase(Locale.ROOT);
 			if (fingerprint.endsWith(keyId)) {
-				return getFirstPublicKey(keyBlob);
+				return getPublicKey(keyBlob, keyInfo.getFingerprint());
 			}
 		}
 		return null;
@@ -211,8 +287,8 @@
 	private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob)
 			throws IOException {
 		for (UserID userID : keyBlob.getUserIds()) {
-			if (containsSigningKey(userID.getUserIDAsString())) {
-				return getFirstPublicKey(keyBlob);
+			if (containsSigningKey(userID.getUserIDAsString(), signingKey)) {
+				return getSigningPublicKey(keyBlob);
 			}
 		}
 		return null;
@@ -444,7 +520,7 @@
 					PGPUtil.getDecoderStream(new BufferedInputStream(in)),
 					new JcaKeyFingerprintCalculator());
 
-			String keyId = signingkey.toLowerCase(Locale.ROOT);
+			String keyId = toFingerprint(signingkey).toLowerCase(Locale.ROOT);
 			Iterator<PGPSecretKeyRing> keyrings = pgpSec.getKeyRings();
 			while (keyrings.hasNext()) {
 				PGPSecretKeyRing keyRing = keyrings.next();
@@ -462,7 +538,7 @@
 					Iterator<String> userIDs = key.getUserIDs();
 					while (userIDs.hasNext()) {
 						String userId = userIDs.next();
-						if (containsSigningKey(userId)) {
+						if (containsSigningKey(userId, signingKey)) {
 							return key;
 						}
 					}
@@ -490,7 +566,7 @@
 					new BufferedInputStream(in),
 					new JcaKeyFingerprintCalculator());
 
-			String keyId = signingKey.toLowerCase(Locale.ROOT);
+			String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
 			Iterator<PGPPublicKeyRing> keyrings = pgpPub.getKeyRings();
 			while (keyrings.hasNext()) {
 				PGPPublicKeyRing keyRing = keyrings.next();
@@ -507,7 +583,7 @@
 					Iterator<String> userIDs = key.getUserIDs();
 					while (userIDs.hasNext()) {
 						String userId = userIDs.next();
-						if (containsSigningKey(userId)) {
+						if (containsSigningKey(userId, signingKey)) {
 							return key;
 						}
 					}
@@ -517,9 +593,42 @@
 		return null;
 	}
 
-	private PGPPublicKey getFirstPublicKey(KeyBlob keyBlob) throws IOException {
-		return ((PublicKeyRingBlob) keyBlob).getPGPPublicKeyRing()
-				.getPublicKey();
+	private PGPPublicKey getPublicKey(KeyBlob blob, byte[] fingerprint)
+			throws IOException {
+		return ((PublicKeyRingBlob) blob).getPGPPublicKeyRing()
+				.getPublicKey(fingerprint);
+	}
+
+	private PGPPublicKey getSigningPublicKey(KeyBlob blob) throws IOException {
+		PGPPublicKey masterKey = null;
+		Iterator<PGPPublicKey> keys = ((PublicKeyRingBlob) blob)
+				.getPGPPublicKeyRing().getPublicKeys();
+		while (keys.hasNext()) {
+			PGPPublicKey key = keys.next();
+			// only consider keys that have the [S] usage flag set
+			if (isSigningKey(key)) {
+				if (key.isMasterKey()) {
+					masterKey = key;
+				} else {
+					return key;
+				}
+			}
+		}
+		// return the master key if no other signing key was found or null if
+		// the master key did not have the signing flag set
+		return masterKey;
+	}
+
+	private boolean isSigningKey(PGPPublicKey key) {
+		Iterator signatures = key.getSignatures();
+		while (signatures.hasNext()) {
+			PGPSignature sig = (PGPSignature) signatures.next();
+			if ((sig.getHashedSubPackets().getKeyFlags()
+					& PGPKeyFlags.CAN_SIGN) > 0) {
+				return true;
+			}
+		}
+		return false;
 	}
 
 	private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
index cfe0931..cfa67ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
@@ -115,7 +115,7 @@
 			NoSuchAlgorithmException, NoSuchProviderException, PGPException,
 			URISyntaxException {
 		if (gpgSigningKey == null || gpgSigningKey.isEmpty()) {
-			gpgSigningKey = committer.getEmailAddress();
+			gpgSigningKey = '<' + committer.getEmailAddress() + '>';
 		}
 
 		BouncyCastleGpgKeyLocator keyHelper = new BouncyCastleGpgKeyLocator(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
index a77cb4f..3ff38dc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
@@ -83,7 +83,7 @@
 
 	// An special edit which acts as a sentinel value by marking the end the
 	// list of edits
-	private final static Edit END_EDIT = new Edit(Integer.MAX_VALUE,
+	private static final Edit END_EDIT = new Edit(Integer.MAX_VALUE,
 			Integer.MAX_VALUE);
 
 	@SuppressWarnings("ReferenceEquality")
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
index cdbe3cd..12f353e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
@@ -166,10 +166,10 @@
 		String mergeOptions = config.getString(
 				ConfigConstants.CONFIG_BRANCH_SECTION, branch,
 				ConfigConstants.CONFIG_KEY_MERGEOPTIONS);
-		if (mergeOptions != null)
+		if (mergeOptions != null) {
 			return mergeOptions.split("\\s"); //$NON-NLS-1$
-		else
-			return new String[0];
+		}
+		return new String[0];
 	}
 
 	private static class MergeConfigSectionParser implements
@@ -188,10 +188,10 @@
 
 		@Override
 		public boolean equals(Object obj) {
-			if (obj instanceof MergeConfigSectionParser)
+			if (obj instanceof MergeConfigSectionParser) {
 				return branch.equals(((MergeConfigSectionParser) obj).branch);
-			else
-				return false;
+			}
+			return false;
 		}
 
 		@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
index ca0e18a..ca2f37a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
@@ -153,15 +153,16 @@
 	private static void addConflictsMessage(List<String> conflictingPaths,
 			StringBuilder sb) {
 		sb.append("Conflicts:\n"); //$NON-NLS-1$
-		for (String conflictingPath : conflictingPaths)
+		for (String conflictingPath : conflictingPaths) {
 			sb.append('\t').append(conflictingPath).append('\n');
+		}
 	}
 
 	private static String joinNames(List<String> names, String singular,
 			String plural) {
-		if (names.size() == 1)
+		if (names.size() == 1) {
 			return singular + " " + names.get(0); //$NON-NLS-1$
-		else
-			return plural + " " + StringUtils.join(names, ", ", " and "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+		return plural + " " + StringUtils.join(names, ", ", " and "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 	}
 }
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 0b423fb..34b521e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -143,7 +143,7 @@
 	 *
 	 * @since 3.0
 	 */
-	protected String commitNames[];
+	protected String[] commitNames;
 
 	/**
 	 * Index of the base tree within the {@link #tw tree walk}.
@@ -652,42 +652,40 @@
 				keep(ourDce);
 				// no checkout needed!
 				return true;
-			} else {
-				// same content but different mode on OURS and THEIRS.
-				// Try to merge the mode and report an error if this is
-				// not possible.
-				int newMode = mergeFileModes(modeB, modeO, modeT);
-				if (newMode != FileMode.MISSING.getBits()) {
-					if (newMode == modeO)
-						// ours version is preferred
-						keep(ourDce);
-					else {
-						// the preferred version THEIRS has a different mode
-						// than ours. Check it out!
-						if (isWorktreeDirty(work, ourDce))
-							return false;
-						// we know about length and lastMod only after we have written the new content.
-						// This will happen later. Set these values to 0 for know.
-						DirCacheEntry e = add(tw.getRawPath(), theirs,
-								DirCacheEntry.STAGE_0, EPOCH, 0);
-						addToCheckout(tw.getPathString(), e, attributes);
-					}
-					return true;
+			}
+			// same content but different mode on OURS and THEIRS.
+			// Try to merge the mode and report an error if this is
+			// not possible.
+			int newMode = mergeFileModes(modeB, modeO, modeT);
+			if (newMode != FileMode.MISSING.getBits()) {
+				if (newMode == modeO) {
+					// ours version is preferred
+					keep(ourDce);
 				} else {
-					// FileModes are not mergeable. We found a conflict on modes.
-					// For conflicting entries we don't know lastModified and length.
-					add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-					add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-					add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH,
-							0);
-					unmergedPaths.add(tw.getPathString());
-					mergeResults.put(
-							tw.getPathString(),
-							new MergeResult<>(Collections
-									.<RawText> emptyList()));
+					// the preferred version THEIRS has a different mode
+					// than ours. Check it out!
+					if (isWorktreeDirty(work, ourDce)) {
+						return false;
+					}
+					// we know about length and lastMod only after we have
+					// written the new content.
+					// This will happen later. Set these values to 0 for know.
+					DirCacheEntry e = add(tw.getRawPath(), theirs,
+							DirCacheEntry.STAGE_0, EPOCH, 0);
+					addToCheckout(tw.getPathString(), e, attributes);
 				}
 				return true;
 			}
+			// FileModes are not mergeable. We found a conflict on modes.
+			// For conflicting entries we don't know lastModified and
+			// length.
+			add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
+			add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
+			add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+			unmergedPaths.add(tw.getPathString());
+			mergeResults.put(tw.getPathString(),
+					new MergeResult<>(Collections.<RawText> emptyList()));
+			return true;
 		}
 
 		if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
@@ -716,21 +714,20 @@
 					addToCheckout(tw.getPathString(), e, attributes);
 				}
 				return true;
-			} else {
-				// we want THEIRS ... but THEIRS contains a folder or the
-				// deletion of the path. Delete what's in the working tree,
-				// which we know to be clean.
-				if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) {
-					// Not present in working tree, so nothing to delete
-					return true;
-				}
-				if (modeT != 0 && modeT == modeB) {
-					// Base, ours, and theirs all contain a folder: don't delete
-					return true;
-				}
-				addDeletion(tw.getPathString(), nonTree(modeO), attributes);
+			}
+			// we want THEIRS ... but THEIRS contains a folder or the
+			// deletion of the path. Delete what's in the working tree,
+			// which we know to be clean.
+			if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) {
+				// Not present in working tree, so nothing to delete
 				return true;
 			}
+			if (modeT != 0 && modeT == modeB) {
+				// Base, ours, and theirs all contain a folder: don't delete
+				return true;
+			}
+			addDeletion(tw.getPathString(), nonTree(modeO), attributes);
+			return true;
 		}
 
 		if (tw.isSubtree()) {
@@ -1310,10 +1307,9 @@
 		if (getUnmergedPaths().isEmpty() && !failed()) {
 			resultTree = dircache.writeTree(getObjectInserter());
 			return true;
-		} else {
-			resultTree = null;
-			return false;
 		}
+		resultTree = null;
+		return false;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
index 2fc0f4f..d56e5c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
@@ -143,17 +143,17 @@
 	 * @throws java.io.IOException
 	 */
 	protected AbstractTreeIterator mergeBase() throws IOException {
-		if (baseTree != null)
+		if (baseTree != null) {
 			return openTree(baseTree);
+		}
 		RevCommit baseCommit = (baseCommitId != null) ? walk
 				.parseCommit(baseCommitId) : getBaseCommit(sourceCommits[0],
 				sourceCommits[1]);
 		if (baseCommit == null) {
 			baseCommitId = null;
 			return new EmptyTreeIterator();
-		} else {
-			baseCommitId = baseCommit.toObjectId();
-			return openTree(baseCommit.getTree());
 		}
+		baseCommitId = baseCommit.toObjectId();
+		return openTree(baseCommit.getTree());
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
index 375cd32..d9fb1b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
@@ -133,8 +133,8 @@
 		return b.get(type);
 	}
 
-	final private Locale locale;
-	final private ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<>();
+	private final Locale locale;
+	private final ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<>();
 
 	private NLS(Locale locale) {
 		this.locale = locale;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
index 7827a9a..c1616b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
@@ -167,10 +167,10 @@
 
 			@Override
 			public Note next() {
-				if (hasNext())
+				if (hasNext()) {
 					return itr.next();
-				else
-					throw new NoSuchElementException();
+				}
+				throw new NoSuchElementException();
 			}
 
 			@Override
@@ -214,30 +214,31 @@
 		NoteBucket b = table[cell];
 
 		if (b == null) {
-			if (noteData == null)
+			if (noteData == null) {
 				return this;
+			}
 
 			LeafBucket n = new LeafBucket(prefixLen + 2);
 			table[cell] = n.set(noteOn, noteData, or);
 			cnt++;
 			return this;
 
-		} else {
-			NoteBucket n = b.set(noteOn, noteData, or);
-			if (n == null) {
-				table[cell] = null;
-				cnt--;
-
-				if (cnt == 0)
-					return null;
-
-				return contractIfTooSmall(noteOn, or);
-
-			} else if (n != b) {
-				table[cell] = n;
-			}
-			return this;
 		}
+		NoteBucket n = b.set(noteOn, noteData, or);
+		if (n == null) {
+			table[cell] = null;
+			cnt--;
+
+			if (cnt == 0) {
+				return null;
+			}
+
+			return contractIfTooSmall(noteOn, or);
+
+		} else if (n != b) {
+			table[cell] = n;
+		}
+		return this;
 	}
 
 	InMemoryNoteBucket contractIfTooSmall(AnyObjectId noteOn, ObjectReader or)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
index 6723b63..0fa2a63 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
@@ -129,10 +129,10 @@
 
 			@Override
 			public Note next() {
-				if (hasNext())
+				if (hasNext()) {
 					return notes[idx++];
-				else
-					throw new NoSuchElementException();
+				}
+				throw new NoSuchElementException();
 			}
 
 			@Override
@@ -156,25 +156,23 @@
 				notes[p].setData(noteData.copy());
 				return this;
 
-			} else {
-				System.arraycopy(notes, p + 1, notes, p, cnt - p - 1);
-				cnt--;
-				return 0 < cnt ? this : null;
 			}
+			System.arraycopy(notes, p + 1, notes, p, cnt - p - 1);
+			cnt--;
+			return 0 < cnt ? this : null;
 
 		} else if (noteData != null) {
 			if (shouldSplit()) {
 				return split().set(noteOn, noteData, or);
-
-			} else {
-				growIfFull();
-				p = -(p + 1);
-				if (p < cnt)
-					System.arraycopy(notes, p, notes, p + 1, cnt - p);
-				notes[p] = new Note(noteOn, noteData.copy());
-				cnt++;
-				return this;
 			}
+			growIfFull();
+			p = -(p + 1);
+			if (p < cnt) {
+				System.arraycopy(notes, p, notes, p + 1, cnt - p);
+			}
+			notes[p] = new Note(noteOn, noteData.copy());
+			cnt++;
+			return this;
 
 		} else {
 			return this;
@@ -234,12 +232,10 @@
 	InMemoryNoteBucket append(Note note) {
 		if (shouldSplit()) {
 			return split().append(note);
-
-		} else {
-			growIfFull();
-			notes[cnt++] = note;
-			return this;
 		}
+		growIfFull();
+		notes[cnt++] = note;
+		return this;
 	}
 
 	private void growIfFull() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
index cbef613..e4eef43 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
@@ -278,10 +278,10 @@
 	public byte[] getCachedBytes(AnyObjectId id, int sizeLimit)
 			throws LargeObjectException, MissingObjectException, IOException {
 		ObjectId dataId = get(id);
-		if (dataId != null)
+		if (dataId != null) {
 			return reader.open(dataId).getCachedBytes(sizeLimit);
-		else
-			return null;
+		}
+		return null;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
index ba7223b..6ff1402 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
@@ -307,10 +307,10 @@
 
 	private static InMemoryNoteBucket addIfNotNull(InMemoryNoteBucket result,
 			Note note) {
-		if (note != null)
+		if (note != null) {
 			return result.append(note);
-		else
-			return result;
+		}
+		return result;
 	}
 
 	private NonNoteEntry mergeNonNotes(NonNoteEntry baseList,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteParser.java
index 8ef3af1..7dfc47d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteParser.java
@@ -181,9 +181,8 @@
 			} catch (ArrayIndexOutOfBoundsException notHex) {
 				return -1;
 			}
-		} else {
-			return -1;
 		}
+		return -1;
 	}
 
 	private void storeNonNote() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/BinaryHunk.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/BinaryHunk.java
index 95391ec..c1ee701 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/BinaryHunk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/BinaryHunk.java
@@ -58,7 +58,7 @@
 	private static final byte[] DELTA = encodeASCII("delta "); //$NON-NLS-1$
 
 	/** Type of information stored in a binary hunk. */
-	public static enum Type {
+	public enum Type {
 		/** The full content is stored, deflated. */
 		LITERAL_DEFLATED,
 
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 74eec81..244f804 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
@@ -58,7 +58,7 @@
  * Hunk header for a hunk appearing in a "diff --cc" style patch.
  */
 public class CombinedHunkHeader extends HunkHeader {
-	private static abstract class CombinedOldImage extends OldImage {
+	private abstract static class CombinedOldImage extends OldImage {
 		int nContext;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
index 1f4beb0..959109c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
@@ -104,7 +104,7 @@
 	static final byte[] NEW_NAME = encodeASCII("+++ "); //$NON-NLS-1$
 
 	/** Type of patch used by this file. */
-	public static enum PatchType {
+	public enum PatchType {
 		/** A traditional unified diff style patch of a text file. */
 		UNIFIED,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java
index 10ea778..3896687 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java
@@ -54,7 +54,7 @@
  */
 public class FormatError {
 	/** Classification of an error. */
-	public static enum Severity {
+	public enum Severity {
 		/** The error is unexpected, but can be worked around. */
 		WARNING,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
index 45508ce..2bb45c5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
@@ -391,11 +391,10 @@
 					return pos.intValue();
 				}
 			return positionsAllocated++;
-		} else {
-			final Integer min = freePositions.first();
-			freePositions.remove(min);
-			return min.intValue();
 		}
+		final Integer min = freePositions.first();
+		freePositions.remove(min);
+		return min.intValue();
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
index ee18fe7..19e40b5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
@@ -176,11 +176,10 @@
 		Collection<Ref> list = reverseRefMap.get(commitId);
 		if (list == null) {
 			return PlotCommit.NO_REFS;
-		} else {
-			Ref[] tags = list.toArray(new Ref[0]);
-			Arrays.sort(tags, new PlotRefComparator());
-			return tags;
 		}
+		Ref[] tags = list.toArray(new Ref[0]);
+		Arrays.sort(tags, new PlotRefComparator());
+		return tags;
 	}
 
 	class PlotRefComparator implements Comparator<Ref> {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java
deleted file mode 100644
index 14e9567..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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 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 doesn't refer to a commit or a tag
-	 * @throws IOException
-	 *             if the walk cannot open a packfile or loose object
-	 */
-	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
index 6e510f6..bf831e3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
@@ -45,13 +45,18 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
+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.NullProgressMonitor;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
 
 /**
  * Checks the reachability using bitmaps.
@@ -84,37 +89,108 @@
 	 * 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
+	 * starters (e.g. refs/heads tips) at the beginning.
 	 */
 	@Override
 	public Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
-			Collection<RevCommit> starters) throws MissingObjectException,
+			Stream<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();
+
+		walk.reset();
+		walk.sort(RevSort.TOPO);
+
+		// Filter emits only commits that are unreachable from previously
+		// visited commits. Internally it keeps a bitmap of everything
+		// reachable so far, which we use to discard reachable targets.
+		BitmapIndex repoBitmaps = walk.getObjectReader().getBitmapIndex();
+		ReachedFilter reachedFilter = new ReachedFilter(repoBitmaps);
+		walk.setRevFilter(reachedFilter);
+
+		Iterator<RevCommit> startersIter = starters.iterator();
+		while (startersIter.hasNext()) {
+			walk.markStart(startersIter.next());
+			while (walk.next() != null) {
+				remainingTargets.removeIf(reachedFilter::isReachable);
+
+				if (remainingTargets.isEmpty()) {
+					return Optional.empty();
+				}
 			}
+			walk.reset();
 		}
 
 		return Optional.of(remainingTargets.get(0));
 	}
 
+	/**
+	 * This filter emits commits that were not bitmap-reachable from anything
+	 * visited before. Or in other words, commits that add something (themselves
+	 * or their bitmap) to the "reached" bitmap.
+	 *
+	 * Current progress can be queried via {@link #isReachable(RevCommit)}.
+	 */
+	private static class ReachedFilter extends RevFilter {
+
+		private final BitmapIndex repoBitmaps;
+		private final BitmapBuilder reached;
+
+		/**
+		 * Create a filter that emits only previously unreachable commits.
+		 *
+		 * @param repoBitmaps
+		 *            bitmap index of the repo
+		 */
+		public ReachedFilter(BitmapIndex repoBitmaps) {
+			this.repoBitmaps = repoBitmaps;
+			this.reached = repoBitmaps.newBitmapBuilder();
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public final boolean include(RevWalk walker, RevCommit cmit) {
+			Bitmap commitBitmap;
+
+			if (reached.contains(cmit)) {
+				// already seen or included
+				dontFollow(cmit);
+				return false;
+			}
+
+			if ((commitBitmap = repoBitmaps.getBitmap(cmit)) != null) {
+				reached.or(commitBitmap);
+				// Emit the commit because there are new contents in the bitmap
+				// but don't follow parents (they are already in the bitmap)
+				dontFollow(cmit);
+				return true;
+			}
+
+			// No bitmaps, keep going
+			reached.addObject(cmit, Constants.OBJ_COMMIT);
+			return true;
+		}
+
+		private static final void dontFollow(RevCommit cmit) {
+			for (RevCommit p : cmit.getParents()) {
+				p.add(RevFlag.SEEN);
+			}
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public final RevFilter clone() {
+			throw new UnsupportedOperationException();
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public final boolean requiresCommitBody() {
+			return false;
+		}
+
+		boolean isReachable(RevCommit commit) {
+			return reached.contains(commit);
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
index bba3c5c..da9e759 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
@@ -44,7 +44,9 @@
 
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -75,7 +77,7 @@
 
 	@Override
 	public Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
-			Collection<RevCommit> starters)
+			Stream<RevCommit> starters)
 					throws MissingObjectException, IncorrectObjectTypeException,
 					IOException {
 		walk.reset();
@@ -87,8 +89,9 @@
 			walk.markStart(target);
 		}
 
-		for (RevCommit starter : starters) {
-			walk.markUninteresting(starter);
+		Iterator<RevCommit> iterator = starters.iterator();
+		while (iterator.hasNext()) {
+			walk.markUninteresting(iterator.next());
 		}
 
 		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
index 2ed06d1..6a9c641 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
@@ -45,6 +45,7 @@
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -82,9 +83,43 @@
 	 * @throws IOException
 	 *             if any of the underlying indexes or readers can not be
 	 *             opened.
+	 *
+	 * @deprecated see {{@link #areAllReachable(Collection, Stream)}
+	 */
+	@Deprecated
+	default Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
+                       Collection<RevCommit> starters) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		return areAllReachable(targets, starters.stream());
+	}
+
+	/**
+	 * 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.
+	 * @since 5.6
 	 */
 	Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
-			Collection<RevCommit> starters)
+			Stream<RevCommit> starters)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException;
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
index 2e26641..b77407b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
@@ -103,11 +103,15 @@
 		final int nParents = pList.length;
 		for (int i = 0; i < nParents; i++) {
 			final RevCommit oldp = pList[i];
-			if (firstParent && i > 0) {
-				c.parents = new RevCommit[] { rewrite(oldp) };
+			final RevCommit newp = rewrite(oldp);
+			if (firstParent) {
+				if (newp == null) {
+					c.parents = RevCommit.NO_PARENTS;
+				} else {
+					c.parents = new RevCommit[] { newp };
+				}
 				return c;
 			}
-			final RevCommit newp = rewrite(oldp);
 			if (oldp != newp) {
 				pList[i] = newp;
 				rewrote = true;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
index a2c9ef6..e0325c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
@@ -80,11 +80,11 @@
 			if (c == null) {
 				break;
 			}
-			for (int i = 0; i < c.parents.length; i++) {
-				if (firstParent && i > 0) {
+			for (RevCommit p : c.parents) {
+				p.inDegree++;
+				if (firstParent) {
 					break;
 				}
-				c.parents[i].inDegree++;
 			}
 			pending.add(c);
 		}
@@ -119,11 +119,7 @@
 			// All of our children have already produced,
 			// so it is OK for us to produce now as well.
 			//
-			for (int i = 0; i < c.parents.length; i++) {
-				if (firstParent && i > 0) {
-					break;
-				}
-				RevCommit p = c.parents[i];
+			for (RevCommit p : c.parents) {
 				if (--p.inDegree == 0 && (p.flags & TOPO_DELAY) != 0) {
 					// This parent tried to come before us, but we are
 					// his last child. unpop the parent so it goes right
@@ -132,6 +128,9 @@
 					p.flags &= ~TOPO_DELAY;
 					pending.unpop(p);
 				}
+				if (firstParent) {
+					break;
+				}
 			}
 			return c;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java
index f7c3218..090d1e1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TreeRevFilter.java
@@ -169,19 +169,19 @@
 				//
 				c.flags |= rewriteFlag;
 				return false;
-			} else {
-				// We have interesting items, but neither of the special
-				// cases denoted above.
-				//
-				if (adds > 0 && tw.getFilter() instanceof FollowFilter) {
-					// One of the paths we care about was added in this
-					// commit. We need to update our filter to its older
-					// name, if we can discover it. Find out what that is.
-					//
-					updateFollowFilter(trees, ((FollowFilter) tw.getFilter()).cfg);
-				}
-				return true;
 			}
+
+			// We have interesting items, but neither of the special
+			// cases denoted above.
+			//
+			if (adds > 0 && tw.getFilter() instanceof FollowFilter) {
+				// One of the paths we care about was added in this
+				// commit. We need to update our filter to its older
+				// name, if we can discover it. Find out what that is.
+				//
+				updateFollowFilter(trees, ((FollowFilter) tw.getFilter()).cfg);
+			}
+			return true;
 		} else if (nParents == 0) {
 			// We have no parents to compare against. Consider us to be
 			// REWRITE only if we have no paths matching our filter.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 9bec2e1..380c23d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -77,7 +77,7 @@
  * The configuration file that is stored in the file of the file system.
  */
 public class FileBasedConfig extends StoredConfig {
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(FileBasedConfig.class);
 
 	private final File configFile;
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 e6e3d4f..645da0a 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
@@ -271,6 +271,20 @@
 		public long treesTraversed;
 
 		/**
+		 * Amount of packfile uris sent to the client to download via HTTP.
+		 *
+		 * @since 5.6
+		 */
+		public long offloadedPackfiles;
+
+		/**
+		 * Total size (in bytes) offloaded to HTTP downloads.
+		 *
+		 * @since 5.6
+		 */
+		public long offloadedPackfileSize;
+
+		/**
 		 * Statistics about each object type in the pack (commits, tags, trees
 		 * and blobs.)
 		 */
@@ -598,6 +612,22 @@
 	}
 
 	/**
+	 * @return amount of packfiles offloaded (sent as "packfile-uri")/
+	 * @since 5.6
+	 */
+	public long getOffloadedPackfiles() {
+		return statistics.offloadedPackfiles;
+	}
+
+	/**
+	 * @return total size (in bytes) offloaded to HTTP downloads.
+	 * @since 5.6
+	 */
+	public long getOffloadedPackfilesSize() {
+		return statistics.offloadedPackfileSize;
+	}
+
+	/**
 	 * 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 e5559de..2e5776d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -57,6 +57,7 @@
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BaseRepositoryBuilder;
 import org.eclipse.jgit.lib.BlobBasedConfig;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ConfigConstants;
@@ -66,6 +67,7 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryBuilder;
+import org.eclipse.jgit.lib.RepositoryBuilderFactory;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
@@ -260,15 +262,41 @@
 	 */
 	public static Repository getSubmoduleRepository(final File parent,
 			final String path, FS fs) throws IOException {
+		return getSubmoduleRepository(parent, path, fs,
+				new RepositoryBuilder());
+	}
+
+	/**
+	 * Get submodule repository at path, using the specified file system
+	 * abstraction and the specified builder
+	 *
+	 * @param parent
+	 *            {@link Repository} that contains the submodule
+	 * @param path
+	 *            of the working tree of the submodule
+	 * @param fs
+	 *            {@link FS} to use
+	 * @param builder
+	 *            {@link BaseRepositoryBuilder} to use to build the submodule
+	 *            repository
+	 * @return the {@link Repository} of the submodule, or {@code null} if it
+	 *         doesn't exist
+	 * @throws IOException
+	 *             on errors
+	 * @since 5.6
+	 */
+	public static Repository getSubmoduleRepository(File parent, String path,
+			FS fs, BaseRepositoryBuilder<?, ? extends Repository> builder)
+			throws IOException {
 		File subWorkTree = new File(parent, path);
-		if (!subWorkTree.isDirectory())
+		if (!subWorkTree.isDirectory()) {
 			return null;
-		File workTree = new File(parent, path);
+		}
 		try {
-			return new RepositoryBuilder() //
+			return builder //
 					.setMustExist(true) //
 					.setFS(fs) //
-					.setWorkTree(workTree) //
+					.setWorkTree(subWorkTree) //
 					.build();
 		} catch (RepositoryNotFoundException e) {
 			return null;
@@ -366,6 +394,8 @@
 
 	private Map<String, String> pathToName;
 
+	private RepositoryBuilderFactory factory;
+
 	/**
 	 * Create submodule generator
 	 *
@@ -639,7 +669,25 @@
 	}
 
 	/**
-	 * The module name for the current submodule entry (used for the section name of .git/config)
+	 * Sets the {@link RepositoryBuilderFactory} to use for creating submodule
+	 * repositories. If none is set, a plain {@link RepositoryBuilder} is used.
+	 *
+	 * @param factory
+	 *            to set
+	 * @since 5.6
+	 */
+	public void setBuilderFactory(RepositoryBuilderFactory factory) {
+		this.factory = factory;
+	}
+
+	private BaseRepositoryBuilder<?, ? extends Repository> getBuilder() {
+		return factory != null ? factory.get() : new RepositoryBuilder();
+	}
+
+	/**
+	 * The module name for the current submodule entry (used for the section
+	 * name of .git/config)
+	 *
 	 * @since 4.10
 	 * @return name
 	 */
@@ -735,6 +783,13 @@
 	 */
 	public IgnoreSubmoduleMode getModulesIgnore() throws IOException,
 			ConfigInvalidException {
+		IgnoreSubmoduleMode mode = repoConfig.getEnum(
+				IgnoreSubmoduleMode.values(),
+				ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
+				ConfigConstants.CONFIG_KEY_IGNORE, null);
+		if (mode != null) {
+			return mode;
+		}
 		lazyLoadModulesConfig();
 		return modulesConfig.getEnum(IgnoreSubmoduleMode.values(),
 				ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
@@ -748,7 +803,8 @@
 	 * @throws java.io.IOException
 	 */
 	public Repository getRepository() throws IOException {
-		return getSubmoduleRepository(repository, path);
+		return getSubmoduleRepository(repository.getWorkTree(), path,
+				repository.getFS(), getBuilder());
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java
index 4bf0d26..ed90012 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java
@@ -67,7 +67,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public void advertiseRefs(BaseReceivePack receivePack)
+	public void advertiseRefs(ReceivePack receivePack)
 			throws ServiceMayNotContinueException {
 		Map<String, Ref> refs = getAdvertisedRefs(receivePack.getRepository(),
 				receivePack.getRevWalk());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
index 8512f2d..eb1aef9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
@@ -51,8 +51,8 @@
 	/**
 	 * A simple hook that advertises the default refs.
 	 * <p>
-	 * The method implementations do nothing to preserve the default behavior; see
-	 * {@link UploadPack#setAdvertisedRefs(java.util.Map)} and
+	 * The method implementations do nothing to preserve the default behavior;
+	 * see {@link UploadPack#setAdvertisedRefs(java.util.Map)} and
 	 * {@link ReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}.
 	 */
 	AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() {
@@ -62,7 +62,7 @@
 		}
 
 		@Override
-		public void advertiseRefs(BaseReceivePack receivePack) {
+		public void advertiseRefs(ReceivePack receivePack) {
 			// Do nothing.
 		}
 	};
@@ -89,7 +89,8 @@
 	 *            if necessary.
 	 * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
 	 *             abort; the message will be sent to the user.
+	 * @since 5.6
 	 */
-	void advertiseRefs(BaseReceivePack receivePack)
+	void advertiseRefs(ReceivePack receivePack)
 			throws ServiceMayNotContinueException;
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
index 12238a1..54c1978 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
@@ -53,7 +53,7 @@
  * modify the results of the previous hooks in the chain by calling
  * {@link org.eclipse.jgit.transport.UploadPack#getAdvertisedRefs()}, or
  * {@link org.eclipse.jgit.transport.ReceivePack#getAdvertisedRefs()} or
- * {@link org.eclipse.jgit.transport.BaseReceivePack#getAdvertisedObjects()}.
+ * {@link org.eclipse.jgit.transport.ReceivePack#getAdvertisedObjects()}.
  */
 public class AdvertiseRefsHookChain implements AdvertiseRefsHook {
 	private final AdvertiseRefsHook[] hooks;
@@ -82,7 +82,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public void advertiseRefs(BaseReceivePack rp)
+	public void advertiseRefs(ReceivePack rp)
 			throws ServiceMayNotContinueException {
 		for (int i = 0; i < count; i++)
 			hooks[i].advertiseRefs(rp);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 35ea35e..12ade4d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -391,7 +391,7 @@
 				refNameEnd = refLine.length();
 			} else if (refLine.startsWith("ng ")) { //$NON-NLS-1$
 				ok = false;
-				refNameEnd = refLine.indexOf(" ", 3); //$NON-NLS-1$
+				refNameEnd = refLine.indexOf(' ', 3);
 			}
 			if (refNameEnd == -1)
 				throw new PackProtocolException(MessageFormat.format(JGitText.get().unexpectedReportLine2
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
deleted file mode 100644
index 36a10cc..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ /dev/null
@@ -1,1971 +0,0 @@
-/*
- * Copyright (C) 2008-2010, Google Inc.
- * 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 static java.nio.charset.StandardCharsets.UTF_8;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
-import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
-import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
-import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
-import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
-import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.text.MessageFormat;
-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.TimeUnit;
-
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.errors.InvalidObjectIdException;
-import org.eclipse.jgit.errors.LargeObjectException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.PackProtocolException;
-import org.eclipse.jgit.errors.TooLargePackException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.file.PackLock;
-import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
-import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
-import org.eclipse.jgit.internal.transport.parser.FirstCommand;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.GitmoduleEntry;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectChecker;
-import org.eclipse.jgit.lib.ObjectDatabase;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdSubclassMap;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.ObjectWalk;
-import org.eclipse.jgit.revwalk.RevBlob;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
-import org.eclipse.jgit.transport.ReceiveCommand.Result;
-import org.eclipse.jgit.util.io.InterruptTimer;
-import org.eclipse.jgit.util.io.LimitedInputStream;
-import org.eclipse.jgit.util.io.TimeoutInputStream;
-import org.eclipse.jgit.util.io.TimeoutOutputStream;
-
-/**
- * Base implementation of the side of a push connection that receives objects.
- * <p>
- * Contains high-level operations for initializing and closing streams,
- * advertising refs, reading commands, and receiving and applying a pack.
- * Subclasses compose these operations into full service implementations.
- */
-public abstract class BaseReceivePack {
-	/**
-	 * Data in the first line of a request, the line itself plus capabilities.
-	 *
-	 * @deprecated Use {@link FirstCommand} instead.
-	 */
-	@Deprecated
-	public static class FirstLine {
-		private final FirstCommand command;
-
-		/**
-		 * Parse the first line of a receive-pack request.
-		 *
-		 * @param line
-		 *            line from the client.
-		 */
-		public FirstLine(String line) {
-			command = FirstCommand.fromLine(line);
-		}
-
-		/** @return non-capabilities part of the line. */
-		public String getLine() {
-			return command.getLine();
-		}
-
-		/** @return capabilities parsed from the line. */
-		public Set<String> getCapabilities() {
-			return command.getCapabilities();
-		}
-	}
-
-	/** Database we write the stored objects into. */
-	final Repository db;
-
-	/** Revision traversal support over {@link #db}. */
-	final RevWalk walk;
-
-	/**
-	 * Is the client connection a bi-directional socket or pipe?
-	 * <p>
-	 * If true, this class assumes it can perform multiple read and write cycles
-	 * with the client over the input and output streams. This matches the
-	 * functionality available with a standard TCP/IP connection, or a local
-	 * operating system or in-memory pipe.
-	 * <p>
-	 * If false, this class runs in a read everything then output results mode,
-	 * making it suitable for single round-trip systems RPCs such as HTTP.
-	 */
-	private boolean biDirectionalPipe = true;
-
-	/** Expecting data after the pack footer */
-	private boolean expectDataAfterPackFooter;
-
-	/** Should an incoming transfer validate objects? */
-	private ObjectChecker objectChecker;
-
-	/** Should an incoming transfer permit create requests? */
-	private boolean allowCreates;
-
-	/** Should an incoming transfer permit delete requests? */
-	private boolean allowAnyDeletes;
-	private boolean allowBranchDeletes;
-
-	/** Should an incoming transfer permit non-fast-forward requests? */
-	private boolean allowNonFastForwards;
-
-	/** Should an incoming transfer permit push options? **/
-	private boolean allowPushOptions;
-
-	/**
-	 * Should the requested ref updates be performed as a single atomic
-	 * transaction?
-	 */
-	private boolean atomic;
-
-	private boolean allowOfsDelta;
-	private boolean allowQuiet = true;
-
-	/** Identity to record action as within the reflog. */
-	private PersonIdent refLogIdent;
-
-	/** Hook used while advertising the refs to the client. */
-	private AdvertiseRefsHook advertiseRefsHook;
-
-	/** Filter used while advertising the refs to the client. */
-	RefFilter refFilter;
-
-	/** Timeout in seconds to wait for client interaction. */
-	private int timeout;
-
-	/** Timer to manage {@link #timeout}. */
-	private InterruptTimer timer;
-
-	private TimeoutInputStream timeoutIn;
-
-	// Original stream passed to init(), since rawOut may be wrapped in a
-	// sideband.
-	private OutputStream origOut;
-
-	/** Raw input stream. */
-	protected InputStream rawIn;
-
-	/** Raw output stream. */
-	protected OutputStream rawOut;
-
-	/** Optional message output stream. */
-	protected OutputStream msgOut;
-	private SideBandOutputStream errOut;
-
-	/** Packet line input stream around {@link #rawIn}. */
-	protected PacketLineIn pckIn;
-
-	/** Packet line output stream around {@link #rawOut}. */
-	protected PacketLineOut pckOut;
-
-	private final MessageOutputWrapper msgOutWrapper = new MessageOutputWrapper();
-
-	private PackParser parser;
-
-	/** The refs we advertised as existing at the start of the connection. */
-	Map<String, Ref> refs;
-
-	/** All SHA-1s shown to the client, which can be possible edges. */
-	Set<ObjectId> advertisedHaves;
-
-	/** Capabilities requested by the client. */
-	private Set<String> enabledCapabilities;
-	String userAgent;
-	private Set<ObjectId> clientShallowCommits;
-	private List<ReceiveCommand> commands;
-	private long maxCommandBytes;
-	private long maxDiscardBytes;
-
-	private StringBuilder advertiseError;
-
-	/** If {@link BasePackPushConnection#CAPABILITY_SIDE_BAND_64K} is enabled. */
-	private boolean sideBand;
-
-	private boolean quiet;
-
-	/** Lock around the received pack file, while updating refs. */
-	private PackLock packLock;
-
-	private boolean checkReferencedIsReachable;
-
-	/** Git object size limit */
-	private long maxObjectSizeLimit;
-
-	/** Total pack size limit */
-	private long maxPackSizeLimit = -1;
-
-	/** The size of the received pack, including index size */
-	private Long packSize;
-
-	private PushCertificateParser pushCertificateParser;
-	private SignedPushConfig signedPushConfig;
-	PushCertificate pushCert;
-	private ReceivedPackStatistics stats;
-
-	/**
-	 * Get the push certificate used to verify the pusher's identity.
-	 * <p>
-	 * Only valid after commands are read from the wire.
-	 *
-	 * @return the parsed certificate, or null if push certificates are disabled
-	 *         or no cert was presented by the client.
-	 * @since 4.1
-	 * @deprecated use {@link ReceivePack#getPushCertificate}.
-	 */
-	@Deprecated
-	public abstract PushCertificate getPushCertificate();
-
-	/**
-	 * Set the push certificate used to verify the pusher's identity.
-	 * <p>
-	 * Should only be called if reconstructing an instance without going through
-	 * the normal {@link #recvCommands()} flow.
-	 *
-	 * @param cert
-	 *            the push certificate to set.
-	 * @since 4.1
-	 * @deprecated use {@link ReceivePack#setPushCertificate(PushCertificate)}.
-	 */
-	@Deprecated
-	public abstract void setPushCertificate(PushCertificate cert);
-
-	/**
-	 * Create a new pack receive for an open repository.
-	 *
-	 * @param into
-	 *            the destination repository.
-	 */
-	protected BaseReceivePack(Repository into) {
-		db = into;
-		walk = new RevWalk(db);
-		walk.setRetainBody(false);
-
-		TransferConfig tc = db.getConfig().get(TransferConfig.KEY);
-		objectChecker = tc.newReceiveObjectChecker();
-
-		ReceiveConfig rc = db.getConfig().get(ReceiveConfig::new);
-		allowCreates = rc.allowCreates;
-		allowAnyDeletes = true;
-		allowBranchDeletes = rc.allowDeletes;
-		allowNonFastForwards = rc.allowNonFastForwards;
-		allowOfsDelta = rc.allowOfsDelta;
-		allowPushOptions = rc.allowPushOptions;
-		maxCommandBytes = rc.maxCommandBytes;
-		maxDiscardBytes = rc.maxDiscardBytes;
-		advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
-		refFilter = RefFilter.DEFAULT;
-		advertisedHaves = new HashSet<>();
-		clientShallowCommits = new HashSet<>();
-		signedPushConfig = rc.signedPush;
-	}
-
-	/** Configuration for receive operations. */
-	protected static class ReceiveConfig {
-		final boolean allowCreates;
-		final boolean allowDeletes;
-		final boolean allowNonFastForwards;
-		final boolean allowOfsDelta;
-		final boolean allowPushOptions;
-		final long maxCommandBytes;
-		final long maxDiscardBytes;
-		final SignedPushConfig signedPush;
-
-		ReceiveConfig(Config config) {
-			allowCreates = true;
-			allowDeletes = !config.getBoolean("receive", "denydeletes", false); //$NON-NLS-1$ //$NON-NLS-2$
-			allowNonFastForwards = !config.getBoolean("receive", //$NON-NLS-1$
-					"denynonfastforwards", false); //$NON-NLS-1$
-			allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$
-					true);
-			allowPushOptions = config.getBoolean("receive", "pushoptions", //$NON-NLS-1$ //$NON-NLS-2$
-					false);
-			maxCommandBytes = config.getLong("receive", //$NON-NLS-1$
-					"maxCommandBytes", //$NON-NLS-1$
-					3 << 20);
-			maxDiscardBytes = config.getLong("receive", //$NON-NLS-1$
-					"maxCommandDiscardBytes", //$NON-NLS-1$
-					-1);
-			signedPush = SignedPushConfig.KEY.parse(config);
-		}
-	}
-
-	/**
-	 * Output stream that wraps the current {@link #msgOut}.
-	 * <p>
-	 * We don't want to expose {@link #msgOut} directly because it can change
-	 * several times over the course of a session.
-	 */
-	class MessageOutputWrapper extends OutputStream {
-		@Override
-		public void write(int ch) {
-			if (msgOut != null) {
-				try {
-					msgOut.write(ch);
-				} catch (IOException e) {
-					// Ignore write failures.
-				}
-			}
-		}
-
-		@Override
-		public void write(byte[] b, int off, int len) {
-			if (msgOut != null) {
-				try {
-					msgOut.write(b, off, len);
-				} catch (IOException e) {
-					// Ignore write failures.
-				}
-			}
-		}
-
-		@Override
-		public void write(byte[] b) {
-			write(b, 0, b.length);
-		}
-
-		@Override
-		public void flush() {
-			if (msgOut != null) {
-				try {
-					msgOut.flush();
-				} catch (IOException e) {
-					// Ignore write failures.
-				}
-			}
-		}
-	}
-
-	/**
-	 * Get the process name used for pack lock messages.
-	 *
-	 * @return the process name used for pack lock messages.
-	 */
-	protected abstract String getLockMessageProcessName();
-
-	/**
-	 * Get the repository this receive completes into.
-	 *
-	 * @return the repository this receive completes into.
-	 * @deprecated use {@link ReceivePack#getRepository}
-	 */
-	@Deprecated
-	public abstract Repository getRepository();
-
-	/**
-	 * Get the RevWalk instance used by this connection.
-	 *
-	 * @return the RevWalk instance used by this connection.
-	 * @deprecated use {@link ReceivePack#getRevWalk}
-	 */
-	@Deprecated
-	public abstract RevWalk getRevWalk();
-
-	/**
-	 * Get refs which were advertised to the client.
-	 *
-	 * @return all refs which were advertised to the client, or null if
-	 *         {@link #setAdvertisedRefs(Map, Set)} has not been called yet.
-	 * @deprecated use {@link ReceivePack#getAdvertisedRefs}
-	 */
-	@Deprecated
-	public abstract Map<String, Ref> getAdvertisedRefs();
-
-	/**
-	 * Set the refs advertised by this ReceivePack.
-	 * <p>
-	 * Intended to be called from a
-	 * {@link org.eclipse.jgit.transport.PreReceiveHook}.
-	 *
-	 * @param allRefs
-	 *            explicit set of references to claim as advertised by this
-	 *            ReceivePack instance. This overrides any references that may
-	 *            exist in the source repository. The map is passed to the
-	 *            configured {@link #getRefFilter()}. If null, assumes all refs
-	 *            were advertised.
-	 * @param additionalHaves
-	 *            explicit set of additional haves to claim as advertised. If
-	 *            null, assumes the default set of additional haves from the
-	 *            repository.
-	 * @deprecated use {@link ReceivePack#setAdvertisedRefs}
-	 */
-	@Deprecated
-	public abstract void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves);
-
-	/**
-	 * Get objects advertised to the client.
-	 *
-	 * @return the set of objects advertised to the as present in this repository,
-	 *         or null if {@link #setAdvertisedRefs(Map, Set)} has not been called
-	 *         yet.
-	 */
-	public final Set<ObjectId> getAdvertisedObjects() {
-		return advertisedHaves;
-	}
-
-	/**
-	 * Whether this instance will validate all referenced, but not supplied by
-	 * the client, objects are reachable from another reference.
-	 *
-	 * @return true if this instance will validate all referenced, but not
-	 *         supplied by the client, objects are reachable from another
-	 *         reference.
-	 */
-	public boolean isCheckReferencedObjectsAreReachable() {
-		return checkReferencedIsReachable;
-	}
-
-	/**
-	 * Validate all referenced but not supplied objects are reachable.
-	 * <p>
-	 * If enabled, this instance will verify that references to objects not
-	 * contained within the received pack are already reachable through at least
-	 * one other reference displayed as part of {@link #getAdvertisedRefs()}.
-	 * <p>
-	 * This feature is useful when the application doesn't trust the client to
-	 * not provide a forged SHA-1 reference to an object, in an attempt to
-	 * access parts of the DAG that they aren't allowed to see and which have
-	 * been hidden from them via the configured
-	 * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} or
-	 * {@link org.eclipse.jgit.transport.RefFilter}.
-	 * <p>
-	 * Enabling this feature may imply at least some, if not all, of the same
-	 * functionality performed by {@link #setCheckReceivedObjects(boolean)}.
-	 * Applications are encouraged to enable both features, if desired.
-	 *
-	 * @param b
-	 *            {@code true} to enable the additional check.
-	 */
-	public void setCheckReferencedObjectsAreReachable(boolean b) {
-		this.checkReferencedIsReachable = b;
-	}
-
-	/**
-	 * Whether this class expects a bi-directional pipe opened between the
-	 * client and itself.
-	 *
-	 * @return true if this class expects a bi-directional pipe opened between
-	 *         the client and itself. The default is true.
-	 */
-	public boolean isBiDirectionalPipe() {
-		return biDirectionalPipe;
-	}
-
-	/**
-	 * Whether this class will assume the socket is a fully bidirectional pipe
-	 * between the two peers and takes advantage of that by first transmitting
-	 * the known refs, then waiting to read commands.
-	 *
-	 * @param twoWay
-	 *            if true, this class will assume the socket is a fully
-	 *            bidirectional pipe between the two peers and takes advantage
-	 *            of that by first transmitting the known refs, then waiting to
-	 *            read commands. If false, this class assumes it must read the
-	 *            commands before writing output and does not perform the
-	 *            initial advertising.
-	 */
-	public void setBiDirectionalPipe(boolean twoWay) {
-		biDirectionalPipe = twoWay;
-	}
-
-	/**
-	 * Whether there is data expected after the pack footer.
-	 *
-	 * @return {@code true} if there is data expected after the pack footer.
-	 */
-	public boolean isExpectDataAfterPackFooter() {
-		return expectDataAfterPackFooter;
-	}
-
-	/**
-	 * Whether there is additional data in InputStream after pack.
-	 *
-	 * @param e
-	 *            {@code true} if there is additional data in InputStream after
-	 *            pack.
-	 */
-	public void setExpectDataAfterPackFooter(boolean e) {
-		expectDataAfterPackFooter = e;
-	}
-
-	/**
-	 * Whether this instance will verify received objects are formatted
-	 * correctly.
-	 *
-	 * @return {@code true} if this instance will verify received objects are
-	 *         formatted correctly. Validating objects requires more CPU time on
-	 *         this side of the connection.
-	 */
-	public boolean isCheckReceivedObjects() {
-		return objectChecker != null;
-	}
-
-	/**
-	 * Whether to enable checking received objects
-	 *
-	 * @param check
-	 *            {@code true} to enable checking received objects; false to
-	 *            assume all received objects are valid.
-	 * @see #setObjectChecker(ObjectChecker)
-	 */
-	public void setCheckReceivedObjects(boolean check) {
-		if (check && objectChecker == null)
-			setObjectChecker(new ObjectChecker());
-		else if (!check && objectChecker != null)
-			setObjectChecker(null);
-	}
-
-	/**
-	 * Set the object checking instance to verify each received object with
-	 *
-	 * @param impl
-	 *            if non-null the object checking instance to verify each
-	 *            received object with; null to disable object checking.
-	 * @since 3.4
-	 */
-	public void setObjectChecker(ObjectChecker impl) {
-		objectChecker = impl;
-	}
-
-	/**
-	 * Whether the client can request refs to be created.
-	 *
-	 * @return {@code true} if the client can request refs to be created.
-	 */
-	public boolean isAllowCreates() {
-		return allowCreates;
-	}
-
-	/**
-	 * Whether to permit create ref commands to be processed.
-	 *
-	 * @param canCreate
-	 *            {@code true} to permit create ref commands to be processed.
-	 */
-	public void setAllowCreates(boolean canCreate) {
-		allowCreates = canCreate;
-	}
-
-	/**
-	 * Whether the client can request refs to be deleted.
-	 *
-	 * @return {@code true} if the client can request refs to be deleted.
-	 */
-	public boolean isAllowDeletes() {
-		return allowAnyDeletes;
-	}
-
-	/**
-	 * Whether to permit delete ref commands to be processed.
-	 *
-	 * @param canDelete
-	 *            {@code true} to permit delete ref commands to be processed.
-	 */
-	public void setAllowDeletes(boolean canDelete) {
-		allowAnyDeletes = canDelete;
-	}
-
-	/**
-	 * Whether the client can delete from {@code refs/heads/}.
-	 *
-	 * @return {@code true} if the client can delete from {@code refs/heads/}.
-	 * @since 3.6
-	 */
-	public boolean isAllowBranchDeletes() {
-		return allowBranchDeletes;
-	}
-
-	/**
-	 * Configure whether to permit deletion of branches from the
-	 * {@code refs/heads/} namespace.
-	 *
-	 * @param canDelete
-	 *            {@code true} to permit deletion of branches from the
-	 *            {@code refs/heads/} namespace.
-	 * @since 3.6
-	 */
-	public void setAllowBranchDeletes(boolean canDelete) {
-		allowBranchDeletes = canDelete;
-	}
-
-	/**
-	 * Whether the client can request non-fast-forward updates of a ref,
-	 * possibly making objects unreachable.
-	 *
-	 * @return {@code true} if the client can request non-fast-forward updates
-	 *         of a ref, possibly making objects unreachable.
-	 */
-	public boolean isAllowNonFastForwards() {
-		return allowNonFastForwards;
-	}
-
-	/**
-	 * Configure whether to permit the client to ask for non-fast-forward
-	 * updates of an existing ref.
-	 *
-	 * @param canRewind
-	 *            {@code true} to permit the client to ask for non-fast-forward
-	 *            updates of an existing ref.
-	 */
-	public void setAllowNonFastForwards(boolean canRewind) {
-		allowNonFastForwards = canRewind;
-	}
-
-	/**
-	 * Whether the client's commands should be performed as a single atomic
-	 * transaction.
-	 *
-	 * @return {@code true} if the client's commands should be performed as a
-	 *         single atomic transaction.
-	 * @since 4.4
-	 */
-	public boolean isAtomic() {
-		return atomic;
-	}
-
-	/**
-	 * Configure whether to perform the client's commands as a single atomic
-	 * transaction.
-	 *
-	 * @param atomic
-	 *            {@code true} to perform the client's commands as a single
-	 *            atomic transaction.
-	 * @since 4.4
-	 */
-	public void setAtomic(boolean atomic) {
-		this.atomic = atomic;
-	}
-
-	/**
-	 * Get identity of the user making the changes in the reflog.
-	 *
-	 * @return identity of the user making the changes in the reflog.
-	 */
-	public PersonIdent getRefLogIdent() {
-		return refLogIdent;
-	}
-
-	/**
-	 * Set the identity of the user appearing in the affected reflogs.
-	 * <p>
-	 * The timestamp portion of the identity is ignored. A new identity with the
-	 * current timestamp will be created automatically when the updates occur
-	 * and the log records are written.
-	 *
-	 * @param pi
-	 *            identity of the user. If null the identity will be
-	 *            automatically determined based on the repository
-	 *            configuration.
-	 */
-	public void setRefLogIdent(PersonIdent pi) {
-		refLogIdent = pi;
-	}
-
-	/**
-	 * Get the hook used while advertising the refs to the client
-	 *
-	 * @return the hook used while advertising the refs to the client
-	 */
-	public AdvertiseRefsHook getAdvertiseRefsHook() {
-		return advertiseRefsHook;
-	}
-
-	/**
-	 * Get the filter used while advertising the refs to the client
-	 *
-	 * @return the filter used while advertising the refs to the client
-	 */
-	public RefFilter getRefFilter() {
-		return refFilter;
-	}
-
-	/**
-	 * Set the hook used while advertising the refs to the client.
-	 * <p>
-	 * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to
-	 * call {@link #setAdvertisedRefs(Map,Set)}, only refs set by this hook
-	 * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter}
-	 * will be shown to the client. Clients may still attempt to create or
-	 * update a reference not advertised by the configured
-	 * {@link org.eclipse.jgit.transport.AdvertiseRefsHook}. These attempts
-	 * should be rejected by a matching
-	 * {@link org.eclipse.jgit.transport.PreReceiveHook}.
-	 *
-	 * @param advertiseRefsHook
-	 *            the hook; may be null to show all refs.
-	 */
-	public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
-		if (advertiseRefsHook != null)
-			this.advertiseRefsHook = advertiseRefsHook;
-		else
-			this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
-	}
-
-	/**
-	 * Set the filter used while advertising the refs to the client.
-	 * <p>
-	 * Only refs allowed by this filter will be shown to the client. The filter
-	 * is run against the refs specified by the
-	 * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable).
-	 *
-	 * @param refFilter
-	 *            the filter; may be null to show all refs.
-	 */
-	public void setRefFilter(RefFilter refFilter) {
-		this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
-	}
-
-	/**
-	 * Get timeout (in seconds) before aborting an IO operation.
-	 *
-	 * @return timeout (in seconds) before aborting an IO operation.
-	 */
-	public int getTimeout() {
-		return timeout;
-	}
-
-	/**
-	 * Set the timeout before willing to abort an IO call.
-	 *
-	 * @param seconds
-	 *            number of seconds to wait (with no data transfer occurring)
-	 *            before aborting an IO read or write operation with the
-	 *            connected client.
-	 */
-	public void setTimeout(int seconds) {
-		timeout = seconds;
-	}
-
-	/**
-	 * Set the maximum number of command bytes to read from the client.
-	 *
-	 * @param limit
-	 *            command limit in bytes; if 0 there is no limit.
-	 * @since 4.7
-	 */
-	public void setMaxCommandBytes(long limit) {
-		maxCommandBytes = limit;
-	}
-
-	/**
-	 * Set the maximum number of command bytes to discard from the client.
-	 * <p>
-	 * Discarding remaining bytes allows this instance to consume the rest of
-	 * the command block and send a human readable over-limit error via the
-	 * side-band channel. If the client sends an excessive number of bytes this
-	 * limit kicks in and the instance disconnects, resulting in a non-specific
-	 * 'pipe closed', 'end of stream', or similar generic error at the client.
-	 * <p>
-	 * When the limit is set to {@code -1} the implementation will default to
-	 * the larger of {@code 3 * maxCommandBytes} or {@code 3 MiB}.
-	 *
-	 * @param limit
-	 *            discard limit in bytes; if 0 there is no limit; if -1 the
-	 *            implementation tries to set a reasonable default.
-	 * @since 4.7
-	 */
-	public void setMaxCommandDiscardBytes(long limit) {
-		maxDiscardBytes = limit;
-	}
-
-	/**
-	 * Set the maximum allowed Git object size.
-	 * <p>
-	 * If an object is larger than the given size the pack-parsing will throw an
-	 * exception aborting the receive-pack operation.
-	 *
-	 * @param limit
-	 *            the Git object size limit. If zero then there is not limit.
-	 */
-	public void setMaxObjectSizeLimit(long limit) {
-		maxObjectSizeLimit = limit;
-	}
-
-	/**
-	 * Set the maximum allowed pack size.
-	 * <p>
-	 * A pack exceeding this size will be rejected.
-	 *
-	 * @param limit
-	 *            the pack size limit, in bytes
-	 * @since 3.3
-	 */
-	public void setMaxPackSizeLimit(long limit) {
-		if (limit < 0)
-			throw new IllegalArgumentException(MessageFormat.format(
-					JGitText.get().receivePackInvalidLimit, Long.valueOf(limit)));
-		maxPackSizeLimit = limit;
-	}
-
-	/**
-	 * Check whether the client expects a side-band stream.
-	 *
-	 * @return true if the client has advertised a side-band capability, false
-	 *     otherwise.
-	 * @throws org.eclipse.jgit.transport.RequestNotYetReadException
-	 *             if the client's request has not yet been read from the wire, so
-	 *             we do not know if they expect side-band. Note that the client
-	 *             may have already written the request, it just has not been
-	 *             read.
-	 */
-	public boolean isSideBand() throws RequestNotYetReadException {
-		checkRequestWasRead();
-		return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
-	}
-
-	/**
-	 * Whether clients may request avoiding noisy progress messages.
-	 *
-	 * @return true if clients may request avoiding noisy progress messages.
-	 * @since 4.0
-	 */
-	public boolean isAllowQuiet() {
-		return allowQuiet;
-	}
-
-	/**
-	 * Configure if clients may request the server skip noisy messages.
-	 *
-	 * @param allow
-	 *            true to allow clients to request quiet behavior; false to
-	 *            refuse quiet behavior and send messages anyway. This may be
-	 *            necessary if processing is slow and the client-server network
-	 *            connection can timeout.
-	 * @since 4.0
-	 */
-	public void setAllowQuiet(boolean allow) {
-		allowQuiet = allow;
-	}
-
-	/**
-	 * Whether the server supports receiving push options.
-	 *
-	 * @return true if the server supports receiving push options.
-	 * @since 4.5
-	 */
-	public boolean isAllowPushOptions() {
-		return allowPushOptions;
-	}
-
-	/**
-	 * Configure if the server supports receiving push options.
-	 *
-	 * @param allow
-	 *            true to optionally accept option strings from the client.
-	 * @since 4.5
-	 */
-	public void setAllowPushOptions(boolean allow) {
-		allowPushOptions = allow;
-	}
-
-	/**
-	 * True if the client wants less verbose output.
-	 *
-	 * @return true if the client has requested the server to be less verbose.
-	 * @throws org.eclipse.jgit.transport.RequestNotYetReadException
-	 *             if the client's request has not yet been read from the wire,
-	 *             so we do not know if they expect side-band. Note that the
-	 *             client may have already written the request, it just has not
-	 *             been read.
-	 * @since 4.0
-	 */
-	public boolean isQuiet() throws RequestNotYetReadException {
-		checkRequestWasRead();
-		return quiet;
-	}
-
-	/**
-	 * Set the configuration for push certificate verification.
-	 *
-	 * @param cfg
-	 *            new configuration; if this object is null or its {@link
-	 *            SignedPushConfig#getCertNonceSeed()} is null, push certificate
-	 *            verification will be disabled.
-	 * @since 4.1
-	 */
-	public void setSignedPushConfig(SignedPushConfig cfg) {
-		signedPushConfig = cfg;
-	}
-
-	private PushCertificateParser getPushCertificateParser() {
-		if (pushCertificateParser == null) {
-			pushCertificateParser = new PushCertificateParser(db, signedPushConfig);
-		}
-		return pushCertificateParser;
-	}
-
-	/**
-	 * Get the user agent of the client.
-	 * <p>
-	 * If the client is new enough to use {@code agent=} capability that value
-	 * will be returned. Older HTTP clients may also supply their version using
-	 * the HTTP {@code User-Agent} header. The capability overrides the HTTP
-	 * header if both are available.
-	 * <p>
-	 * When an HTTP request has been received this method returns the HTTP
-	 * {@code User-Agent} header value until capabilities have been parsed.
-	 *
-	 * @return user agent supplied by the client. Available only if the client
-	 *         is new enough to advertise its user agent.
-	 * @since 4.0
-	 */
-	public String getPeerUserAgent() {
-		return UserAgent.getAgent(enabledCapabilities, userAgent);
-	}
-
-	/**
-	 * Get all of the command received by the current request.
-	 *
-	 * @return all of the command received by the current request.
-	 */
-	public List<ReceiveCommand> getAllCommands() {
-		return Collections.unmodifiableList(commands);
-	}
-
-	/**
-	 * Send an error message to the client.
-	 * <p>
-	 * If any error messages are sent before the references are advertised to
-	 * the client, the errors will be sent instead of the advertisement and the
-	 * receive operation will be aborted. All clients should receive and display
-	 * such early stage errors.
-	 * <p>
-	 * If the reference advertisements have already been sent, messages are sent
-	 * in a side channel. If the client doesn't support receiving messages, the
-	 * message will be discarded, with no other indication to the caller or to
-	 * the client.
-	 * <p>
-	 * {@link org.eclipse.jgit.transport.PreReceiveHook}s should always try to
-	 * use
-	 * {@link org.eclipse.jgit.transport.ReceiveCommand#setResult(Result, String)}
-	 * with a result status of
-	 * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#REJECTED_OTHER_REASON}
-	 * to indicate any reasons for rejecting an update. Messages attached to a
-	 * command are much more likely to be returned to the client.
-	 *
-	 * @param what
-	 *            string describing the problem identified by the hook. The
-	 *            string must not end with an LF, and must not contain an LF.
-	 */
-	public void sendError(String what) {
-		if (refs == null) {
-			if (advertiseError == null)
-				advertiseError = new StringBuilder();
-			advertiseError.append(what).append('\n');
-		} else {
-			msgOutWrapper.write(Constants.encode("error: " + what + "\n")); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-	}
-
-	private void fatalError(String msg) {
-		if (errOut != null) {
-			try {
-				errOut.write(Constants.encode(msg));
-				errOut.flush();
-			} catch (IOException e) {
-				// Ignore write failures
-			}
-		} else {
-			sendError(msg);
-		}
-	}
-
-	/**
-	 * Send a message to the client, if it supports receiving them.
-	 * <p>
-	 * If the client doesn't support receiving messages, the message will be
-	 * discarded, with no other indication to the caller or to the client.
-	 *
-	 * @param what
-	 *            string describing the problem identified by the hook. The
-	 *            string must not end with an LF, and must not contain an LF.
-	 */
-	public void sendMessage(String what) {
-		msgOutWrapper.write(Constants.encode(what + "\n")); //$NON-NLS-1$
-	}
-
-	/**
-	 * Get an underlying stream for sending messages to the client.
-	 *
-	 * @return an underlying stream for sending messages to the client.
-	 */
-	public OutputStream getMessageOutputStream() {
-		return msgOutWrapper;
-	}
-
-	/**
-	 * Get the size of the received pack file including the index size.
-	 *
-	 * This can only be called if the pack is already received.
-	 *
-	 * @return the size of the received pack including index size
-	 * @throws java.lang.IllegalStateException
-	 *             if called before the pack has been received
-	 * @since 3.3
-	 */
-	public long getPackSize() {
-		if (packSize != null)
-			return packSize.longValue();
-		throw new IllegalStateException(JGitText.get().packSizeNotSetYet);
-	}
-
-	/**
-	 * Get the commits from the client's shallow file.
-	 *
-	 * @return if the client is a shallow repository, the list of edge commits
-	 *     that define the client's shallow boundary. Empty set if the client
-	 *     is earlier than Git 1.9, or is a full clone.
-	 * @since 3.5
-	 */
-	protected Set<ObjectId> getClientShallowCommits() {
-		return clientShallowCommits;
-	}
-
-	/**
-	 * Whether any commands to be executed have been read.
-	 *
-	 * @return {@code true} if any commands to be executed have been read.
-	 */
-	protected boolean hasCommands() {
-		return !commands.isEmpty();
-	}
-
-	/**
-	 * Whether an error occurred that should be advertised.
-	 *
-	 * @return true if an error occurred that should be advertised.
-	 */
-	protected boolean hasError() {
-		return advertiseError != null;
-	}
-
-	/**
-	 * Initialize the instance with the given streams.
-	 *
-	 * @param input
-	 *            raw input to read client commands and pack data from. Caller
-	 *            must ensure the input is buffered, otherwise read performance
-	 *            may suffer.
-	 * @param output
-	 *            response back to the Git network client. Caller must ensure
-	 *            the output is buffered, otherwise write performance may
-	 *            suffer.
-	 * @param messages
-	 *            secondary "notice" channel to send additional messages out
-	 *            through. When run over SSH this should be tied back to the
-	 *            standard error channel of the command execution. For most
-	 *            other network connections this should be null.
-	 */
-	protected void init(final InputStream input, final OutputStream output,
-			final OutputStream messages) {
-		origOut = output;
-		rawIn = input;
-		rawOut = output;
-		msgOut = messages;
-
-		if (timeout > 0) {
-			final Thread caller = Thread.currentThread();
-			timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
-			timeoutIn = new TimeoutInputStream(rawIn, timer);
-			TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
-			timeoutIn.setTimeout(timeout * 1000);
-			o.setTimeout(timeout * 1000);
-			rawIn = timeoutIn;
-			rawOut = o;
-		}
-
-		pckIn = new PacketLineIn(rawIn);
-		pckOut = new PacketLineOut(rawOut);
-		pckOut.setFlushOnEnd(false);
-
-		enabledCapabilities = new HashSet<>();
-		commands = new ArrayList<>();
-	}
-
-	/**
-	 * Get advertised refs, or the default if not explicitly advertised.
-	 *
-	 * @return advertised refs, or the default if not explicitly advertised.
-	 */
-	protected Map<String, Ref> getAdvertisedOrDefaultRefs() {
-		if (refs == null)
-			setAdvertisedRefs(null, null);
-		return refs;
-	}
-
-	/**
-	 * Receive a pack from the stream and check connectivity if necessary.
-	 *
-	 * @throws java.io.IOException
-	 *             an error occurred during unpacking or connectivity checking.
-	 */
-	protected void receivePackAndCheckConnectivity() throws IOException {
-		receivePack();
-		if (needCheckConnectivity()) {
-			checkSubmodules();
-			checkConnectivity();
-		}
-		parser = null;
-	}
-
-	/**
-	 * Unlock the pack written by this object.
-	 *
-	 * @throws java.io.IOException
-	 *             the pack could not be unlocked.
-	 */
-	protected void unlockPack() throws IOException {
-		if (packLock != null) {
-			packLock.unlock();
-			packLock = null;
-		}
-	}
-
-	/**
-	 * Generate an advertisement of available refs and capabilities.
-	 *
-	 * @param adv
-	 *            the advertisement formatter.
-	 * @throws java.io.IOException
-	 *             the formatter failed to write an advertisement.
-	 * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
-	 *             the hook denied advertisement.
-	 */
-	public void sendAdvertisedRefs(RefAdvertiser adv)
-			throws IOException, ServiceMayNotContinueException {
-		if (advertiseError != null) {
-			adv.writeOne("ERR " + advertiseError); //$NON-NLS-1$
-			return;
-		}
-
-		try {
-			advertiseRefsHook.advertiseRefs(this);
-		} catch (ServiceMayNotContinueException fail) {
-			if (fail.getMessage() != null) {
-				adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$
-				fail.setOutput();
-			}
-			throw fail;
-		}
-
-		adv.init(db);
-		adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
-		adv.advertiseCapability(CAPABILITY_DELETE_REFS);
-		adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
-		if (allowQuiet)
-			adv.advertiseCapability(CAPABILITY_QUIET);
-		String nonce = getPushCertificateParser().getAdvertiseNonce();
-		if (nonce != null) {
-			adv.advertiseCapability(nonce);
-		}
-		if (db.getRefDatabase().performsAtomicTransactions())
-			adv.advertiseCapability(CAPABILITY_ATOMIC);
-		if (allowOfsDelta)
-			adv.advertiseCapability(CAPABILITY_OFS_DELTA);
-		if (allowPushOptions) {
-			adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS);
-		}
-		adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
-		adv.send(getAdvertisedOrDefaultRefs().values());
-		for (ObjectId obj : advertisedHaves)
-			adv.advertiseHave(obj);
-		if (adv.isEmpty())
-			adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
-		adv.end();
-	}
-
-	/**
-	 * Returns the statistics on the received pack if available. This should be
-	 * called after {@link #receivePack} is called.
-	 *
-	 * @return ReceivedPackStatistics
-	 * @since 4.6
-	 */
-	@Nullable
-	public ReceivedPackStatistics getReceivedPackStatistics() {
-		return stats;
-	}
-
-	/**
-	 * Receive a list of commands from the input.
-	 *
-	 * @throws java.io.IOException
-	 */
-	protected void recvCommands() throws IOException {
-		PacketLineIn pck = maxCommandBytes > 0
-				? new PacketLineIn(rawIn, maxCommandBytes)
-				: pckIn;
-		PushCertificateParser certParser = getPushCertificateParser();
-		boolean firstPkt = true;
-		try {
-			for (;;) {
-				String line;
-				try {
-					line = pck.readString();
-				} catch (EOFException eof) {
-					if (commands.isEmpty())
-						return;
-					throw eof;
-				}
-				if (PacketLineIn.isEnd(line)) {
-					break;
-				}
-
-				if (line.length() >= 48 && line.startsWith("shallow ")) { //$NON-NLS-1$
-					parseShallow(line.substring(8, 48));
-					continue;
-				}
-
-				if (firstPkt) {
-					firstPkt = false;
-					FirstCommand firstLine = FirstCommand.fromLine(line);
-					enabledCapabilities = firstLine.getCapabilities();
-					line = firstLine.getLine();
-					enableCapabilities();
-
-					if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) {
-						certParser.receiveHeader(pck, !isBiDirectionalPipe());
-						continue;
-					}
-				}
-
-				if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
-					certParser.receiveSignature(pck);
-					continue;
-				}
-
-				ReceiveCommand cmd = parseCommand(line);
-				if (cmd.getRefName().equals(Constants.HEAD)) {
-					cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
-				} else {
-					cmd.setRef(refs.get(cmd.getRefName()));
-				}
-				commands.add(cmd);
-				if (certParser.enabled()) {
-					certParser.addCommand(cmd);
-				}
-			}
-			pushCert = certParser.build();
-			if (hasCommands()) {
-				readPostCommands(pck);
-			}
-		} catch (PackProtocolException e) {
-			discardCommands();
-			fatalError(e.getMessage());
-			throw e;
-		} catch (InputOverLimitIOException e) {
-			String msg = JGitText.get().tooManyCommands;
-			discardCommands();
-			fatalError(msg);
-			throw new PackProtocolException(msg);
-		}
-	}
-
-	private void discardCommands() {
-		if (sideBand) {
-			long max = maxDiscardBytes;
-			if (max < 0) {
-				max = Math.max(3 * maxCommandBytes, 3L << 20);
-			}
-			try {
-				new PacketLineIn(rawIn, max).discardUntilEnd();
-			} catch (IOException e) {
-				// Ignore read failures attempting to discard.
-			}
-		}
-	}
-
-	private void parseShallow(String idStr) throws PackProtocolException {
-		ObjectId id;
-		try {
-			id = ObjectId.fromString(idStr);
-		} catch (InvalidObjectIdException e) {
-			throw new PackProtocolException(e.getMessage(), e);
-		}
-		clientShallowCommits.add(id);
-	}
-
-	static ReceiveCommand parseCommand(String line) throws PackProtocolException {
-		if (line == null || line.length() < 83) {
-			throw new PackProtocolException(
-					JGitText.get().errorInvalidProtocolWantedOldNewRef);
-		}
-		String oldStr = line.substring(0, 40);
-		String newStr = line.substring(41, 81);
-		ObjectId oldId, newId;
-		try {
-			oldId = ObjectId.fromString(oldStr);
-			newId = ObjectId.fromString(newStr);
-		} catch (InvalidObjectIdException e) {
-			throw new PackProtocolException(
-					JGitText.get().errorInvalidProtocolWantedOldNewRef, e);
-		}
-		String name = line.substring(82);
-		if (!Repository.isValidRefName(name)) {
-			throw new PackProtocolException(
-					JGitText.get().errorInvalidProtocolWantedOldNewRef);
-		}
-		return new ReceiveCommand(oldId, newId, name);
-	}
-
-	/**
-	 * @param in
-	 *            request stream.
-	 * @throws IOException
-	 *             request line cannot be read.
-	 */
-	void readPostCommands(PacketLineIn in) throws IOException {
-		// Do nothing by default.
-	}
-
-	/**
-	 * Enable capabilities based on a previously read capabilities line.
-	 */
-	protected void enableCapabilities() {
-		sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
-		quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
-		if (sideBand) {
-			OutputStream out = rawOut;
-
-			rawOut = new SideBandOutputStream(CH_DATA, MAX_BUF, out);
-			msgOut = new SideBandOutputStream(CH_PROGRESS, MAX_BUF, out);
-			errOut = new SideBandOutputStream(CH_ERROR, MAX_BUF, out);
-
-			pckOut = new PacketLineOut(rawOut);
-			pckOut.setFlushOnEnd(false);
-		}
-	}
-
-	/**
-	 * Check if the peer requested a capability.
-	 *
-	 * @param name
-	 *            protocol name identifying the capability.
-	 * @return true if the peer requested the capability to be enabled.
-	 */
-	protected boolean isCapabilityEnabled(String name) {
-		return enabledCapabilities.contains(name);
-	}
-
-	void checkRequestWasRead() {
-		if (enabledCapabilities == null)
-			throw new RequestNotYetReadException();
-	}
-
-	/**
-	 * Whether a pack is expected based on the list of commands.
-	 *
-	 * @return {@code true} if a pack is expected based on the list of commands.
-	 */
-	protected boolean needPack() {
-		for (ReceiveCommand cmd : commands) {
-			if (cmd.getType() != ReceiveCommand.Type.DELETE)
-				return true;
-		}
-		return false;
-	}
-
-	/**
-	 * Receive a pack from the input and store it in the repository.
-	 *
-	 * @throws IOException
-	 *             an error occurred reading or indexing the pack.
-	 */
-	private void receivePack() throws IOException {
-		// It might take the client a while to pack the objects it needs
-		// to send to us.  We should increase our timeout so we don't
-		// abort while the client is computing.
-		//
-		if (timeoutIn != null)
-			timeoutIn.setTimeout(10 * timeout * 1000);
-
-		ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
-		ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
-		if (sideBand && !quiet)
-			resolving = new SideBandProgressMonitor(msgOut);
-
-		try (ObjectInserter ins = db.newObjectInserter()) {
-			String lockMsg = "jgit receive-pack"; //$NON-NLS-1$
-			if (getRefLogIdent() != null)
-				lockMsg += " from " + getRefLogIdent().toExternalString(); //$NON-NLS-1$
-
-			parser = ins.newPackParser(packInputStream());
-			parser.setAllowThin(true);
-			parser.setNeedNewObjectIds(checkReferencedIsReachable);
-			parser.setNeedBaseObjectIds(checkReferencedIsReachable);
-			parser.setCheckEofAfterPackFooter(!biDirectionalPipe
-					&& !isExpectDataAfterPackFooter());
-			parser.setExpectDataAfterPackFooter(isExpectDataAfterPackFooter());
-			parser.setObjectChecker(objectChecker);
-			parser.setLockMessage(lockMsg);
-			parser.setMaxObjectSizeLimit(maxObjectSizeLimit);
-			packLock = parser.parse(receiving, resolving);
-			packSize = Long.valueOf(parser.getPackSize());
-			stats = parser.getReceivedPackStatistics();
-			ins.flush();
-		}
-
-		if (timeoutIn != null)
-			timeoutIn.setTimeout(timeout * 1000);
-	}
-
-	private InputStream packInputStream() {
-		InputStream packIn = rawIn;
-		if (maxPackSizeLimit >= 0) {
-			packIn = new LimitedInputStream(packIn, maxPackSizeLimit) {
-				@Override
-				protected void limitExceeded() throws TooLargePackException {
-					throw new TooLargePackException(limit);
-				}
-			};
-		}
-		return packIn;
-	}
-
-	private boolean needCheckConnectivity() {
-		return isCheckReceivedObjects()
-				|| isCheckReferencedObjectsAreReachable()
-				|| !getClientShallowCommits().isEmpty();
-	}
-
-	private void checkSubmodules()
-			throws IOException {
-		ObjectDatabase odb = db.getObjectDatabase();
-		if (objectChecker == null) {
-			return;
-		}
-		for (GitmoduleEntry entry : objectChecker.getGitsubmodules()) {
-			AnyObjectId blobId = entry.getBlobId();
-			ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB);
-
-			try {
-				SubmoduleValidator.assertValidGitModulesFile(
-						new String(blob.getBytes(), UTF_8));
-			} catch (LargeObjectException | SubmoduleValidationException e) {
-				throw new IOException(e);
-			}
-		}
-	}
-
-	private void checkConnectivity() throws IOException {
-		ObjectIdSubclassMap<ObjectId> baseObjects = null;
-		ObjectIdSubclassMap<ObjectId> providedObjects = null;
-		ProgressMonitor checking = NullProgressMonitor.INSTANCE;
-		if (sideBand && !quiet) {
-			SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut);
-			m.setDelayStart(750, TimeUnit.MILLISECONDS);
-			checking = m;
-		}
-
-		if (checkReferencedIsReachable) {
-			baseObjects = parser.getBaseObjectIds();
-			providedObjects = parser.getNewObjectIds();
-		}
-		parser = null;
-
-		try (ObjectWalk ow = new ObjectWalk(db)) {
-			if (baseObjects != null) {
-				ow.sort(RevSort.TOPO);
-				if (!baseObjects.isEmpty())
-					ow.sort(RevSort.BOUNDARY, true);
-			}
-
-			for (ReceiveCommand cmd : commands) {
-				if (cmd.getResult() != Result.NOT_ATTEMPTED)
-					continue;
-				if (cmd.getType() == ReceiveCommand.Type.DELETE)
-					continue;
-				ow.markStart(ow.parseAny(cmd.getNewId()));
-			}
-			for (ObjectId have : advertisedHaves) {
-				RevObject o = ow.parseAny(have);
-				ow.markUninteresting(o);
-
-				if (baseObjects != null && !baseObjects.isEmpty()) {
-					o = ow.peel(o);
-					if (o instanceof RevCommit)
-						o = ((RevCommit) o).getTree();
-					if (o instanceof RevTree)
-						ow.markUninteresting(o);
-				}
-			}
-
-			checking.beginTask(JGitText.get().countingObjects,
-					ProgressMonitor.UNKNOWN);
-			RevCommit c;
-			while ((c = ow.next()) != null) {
-				checking.update(1);
-				if (providedObjects != null //
-						&& !c.has(RevFlag.UNINTERESTING) //
-						&& !providedObjects.contains(c))
-					throw new MissingObjectException(c, Constants.TYPE_COMMIT);
-			}
-
-			RevObject o;
-			while ((o = ow.nextObject()) != null) {
-				checking.update(1);
-				if (o.has(RevFlag.UNINTERESTING))
-					continue;
-
-				if (providedObjects != null) {
-					if (providedObjects.contains(o))
-						continue;
-					else
-						throw new MissingObjectException(o, o.getType());
-				}
-
-				if (o instanceof RevBlob && !db.getObjectDatabase().has(o))
-					throw new MissingObjectException(o, Constants.TYPE_BLOB);
-			}
-			checking.endTask();
-
-			if (baseObjects != null) {
-				for (ObjectId id : baseObjects) {
-					o = ow.parseAny(id);
-					if (!o.has(RevFlag.UNINTERESTING))
-						throw new MissingObjectException(o, o.getType());
-				}
-			}
-		}
-	}
-
-	/**
-	 * Validate the command list.
-	 */
-	protected void validateCommands() {
-		for (ReceiveCommand cmd : commands) {
-			final Ref ref = cmd.getRef();
-			if (cmd.getResult() != Result.NOT_ATTEMPTED)
-				continue;
-
-			if (cmd.getType() == ReceiveCommand.Type.DELETE) {
-				if (!isAllowDeletes()) {
-					// Deletes are not supported on this repository.
-					cmd.setResult(Result.REJECTED_NODELETE);
-					continue;
-				}
-				if (!isAllowBranchDeletes()
-						&& ref.getName().startsWith(Constants.R_HEADS)) {
-					// Branches cannot be deleted, but other refs can.
-					cmd.setResult(Result.REJECTED_NODELETE);
-					continue;
-				}
-			}
-
-			if (cmd.getType() == ReceiveCommand.Type.CREATE) {
-				if (!isAllowCreates()) {
-					cmd.setResult(Result.REJECTED_NOCREATE);
-					continue;
-				}
-
-				if (ref != null && !isAllowNonFastForwards()) {
-					// Creation over an existing ref is certainly not going
-					// to be a fast-forward update. We can reject it early.
-					//
-					cmd.setResult(Result.REJECTED_NONFASTFORWARD);
-					continue;
-				}
-
-				if (ref != null) {
-					// A well behaved client shouldn't have sent us a
-					// create command for a ref we advertised to it.
-					//
-					cmd.setResult(Result.REJECTED_OTHER_REASON,
-							JGitText.get().refAlreadyExists);
-					continue;
-				}
-			}
-
-			if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null) {
-				ObjectId id = ref.getObjectId();
-				if (id == null) {
-					id = ObjectId.zeroId();
-				}
-				if (!ObjectId.zeroId().equals(cmd.getOldId())
-						&& !id.equals(cmd.getOldId())) {
-					// Delete commands can be sent with the old id matching our
-					// advertised value, *OR* with the old id being 0{40}. Any
-					// other requested old id is invalid.
-					//
-					cmd.setResult(Result.REJECTED_OTHER_REASON,
-							JGitText.get().invalidOldIdSent);
-					continue;
-				}
-			}
-
-			if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
-				if (ref == null) {
-					// The ref must have been advertised in order to be updated.
-					//
-					cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().noSuchRef);
-					continue;
-				}
-				ObjectId id = ref.getObjectId();
-				if (id == null) {
-					// We cannot update unborn branch
-					cmd.setResult(Result.REJECTED_OTHER_REASON,
-							JGitText.get().cannotUpdateUnbornBranch);
-					continue;
-				}
-
-				if (!id.equals(cmd.getOldId())) {
-					// A properly functioning client will send the same
-					// object id we advertised.
-					//
-					cmd.setResult(Result.REJECTED_OTHER_REASON,
-							JGitText.get().invalidOldIdSent);
-					continue;
-				}
-
-				// Is this possibly a non-fast-forward style update?
-				//
-				RevObject oldObj, newObj;
-				try {
-					oldObj = walk.parseAny(cmd.getOldId());
-				} catch (IOException e) {
-					cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
-							.getOldId().name());
-					continue;
-				}
-
-				try {
-					newObj = walk.parseAny(cmd.getNewId());
-				} catch (IOException e) {
-					cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
-							.getNewId().name());
-					continue;
-				}
-
-				if (oldObj instanceof RevCommit && newObj instanceof RevCommit) {
-					try {
-						if (walk.isMergedInto((RevCommit) oldObj,
-								(RevCommit) newObj))
-							cmd.setTypeFastForwardUpdate();
-						else
-							cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
-					} catch (MissingObjectException e) {
-						cmd.setResult(Result.REJECTED_MISSING_OBJECT, e
-								.getMessage());
-					} catch (IOException e) {
-						cmd.setResult(Result.REJECTED_OTHER_REASON);
-					}
-				} else {
-					cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
-				}
-
-				if (cmd.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD
-						&& !isAllowNonFastForwards()) {
-					cmd.setResult(Result.REJECTED_NONFASTFORWARD);
-					continue;
-				}
-			}
-
-			if (!cmd.getRefName().startsWith(Constants.R_REFS)
-					|| !Repository.isValidRefName(cmd.getRefName())) {
-				cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().funnyRefname);
-			}
-		}
-	}
-
-	/**
-	 * Whether any commands have been rejected so far.
-	 *
-	 * @return if any commands have been rejected so far.
-	 * @since 3.6
-	 */
-	protected boolean anyRejects() {
-		for (ReceiveCommand cmd : commands) {
-			if (cmd.getResult() != Result.NOT_ATTEMPTED && cmd.getResult() != Result.OK)
-				return true;
-		}
-		return false;
-	}
-
-	/**
-	 * Set the result to fail for any command that was not processed yet.
-	 *
-	 * @since 3.6
-	 */
-	protected void failPendingCommands() {
-		ReceiveCommand.abort(commands);
-	}
-
-	/**
-	 * Filter the list of commands according to result.
-	 *
-	 * @param want
-	 *            desired status to filter by.
-	 * @return a copy of the command list containing only those commands with the
-	 *         desired status.
-	 */
-	protected List<ReceiveCommand> filterCommands(Result want) {
-		return ReceiveCommand.filter(commands, want);
-	}
-
-	/**
-	 * Execute commands to update references.
-	 */
-	protected void executeCommands() {
-		List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
-		if (toApply.isEmpty())
-			return;
-
-		ProgressMonitor updating = NullProgressMonitor.INSTANCE;
-		if (sideBand) {
-			SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
-			pm.setDelayStart(250, TimeUnit.MILLISECONDS);
-			updating = pm;
-		}
-
-		BatchRefUpdate batch = db.getRefDatabase().newBatchUpdate();
-		batch.setAllowNonFastForwards(isAllowNonFastForwards());
-		batch.setAtomic(isAtomic());
-		batch.setRefLogIdent(getRefLogIdent());
-		batch.setRefLogMessage("push", true); //$NON-NLS-1$
-		batch.addCommand(toApply);
-		try {
-			batch.setPushCertificate(getPushCertificate());
-			batch.execute(walk, updating);
-		} catch (IOException err) {
-			for (ReceiveCommand cmd : toApply) {
-				if (cmd.getResult() == Result.NOT_ATTEMPTED)
-					cmd.reject(err);
-			}
-		}
-	}
-
-	/**
-	 * Send a status report.
-	 *
-	 * @param forClient
-	 *            true if this report is for a Git client, false if it is for an
-	 *            end-user.
-	 * @param unpackError
-	 *            an error that occurred during unpacking, or {@code null}
-	 * @param out
-	 *            the reporter for sending the status strings.
-	 * @throws java.io.IOException
-	 *             an error occurred writing the status report.
-	 */
-	protected void sendStatusReport(final boolean forClient,
-			final Throwable unpackError, final Reporter out) throws IOException {
-		if (unpackError != null) {
-			out.sendString("unpack error " + unpackError.getMessage()); //$NON-NLS-1$
-			if (forClient) {
-				for (ReceiveCommand cmd : commands) {
-					out.sendString("ng " + cmd.getRefName() //$NON-NLS-1$
-							+ " n/a (unpacker error)"); //$NON-NLS-1$
-				}
-			}
-			return;
-		}
-
-		if (forClient)
-			out.sendString("unpack ok"); //$NON-NLS-1$
-		for (ReceiveCommand cmd : commands) {
-			if (cmd.getResult() == Result.OK) {
-				if (forClient)
-					out.sendString("ok " + cmd.getRefName()); //$NON-NLS-1$
-				continue;
-			}
-
-			final StringBuilder r = new StringBuilder();
-			if (forClient)
-				r.append("ng ").append(cmd.getRefName()).append(" "); //$NON-NLS-1$ //$NON-NLS-2$
-			else
-				r.append(" ! [rejected] ").append(cmd.getRefName()).append(" ("); //$NON-NLS-1$ //$NON-NLS-2$
-
-			switch (cmd.getResult()) {
-			case NOT_ATTEMPTED:
-				r.append("server bug; ref not processed"); //$NON-NLS-1$
-				break;
-
-			case REJECTED_NOCREATE:
-				r.append("creation prohibited"); //$NON-NLS-1$
-				break;
-
-			case REJECTED_NODELETE:
-				r.append("deletion prohibited"); //$NON-NLS-1$
-				break;
-
-			case REJECTED_NONFASTFORWARD:
-				r.append("non-fast forward"); //$NON-NLS-1$
-				break;
-
-			case REJECTED_CURRENT_BRANCH:
-				r.append("branch is currently checked out"); //$NON-NLS-1$
-				break;
-
-			case REJECTED_MISSING_OBJECT:
-				if (cmd.getMessage() == null)
-					r.append("missing object(s)"); //$NON-NLS-1$
-				else if (cmd.getMessage().length() == Constants.OBJECT_ID_STRING_LENGTH) {
-					r.append("object "); //$NON-NLS-1$
-					r.append(cmd.getMessage());
-					r.append(" missing"); //$NON-NLS-1$
-				} else
-					r.append(cmd.getMessage());
-				break;
-
-			case REJECTED_OTHER_REASON:
-				if (cmd.getMessage() == null)
-					r.append("unspecified reason"); //$NON-NLS-1$
-				else
-					r.append(cmd.getMessage());
-				break;
-
-			case LOCK_FAILURE:
-				r.append("failed to lock"); //$NON-NLS-1$
-				break;
-
-			case OK:
-				// We shouldn't have reached this case (see 'ok' case above).
-				continue;
-			}
-			if (!forClient)
-				r.append(")"); //$NON-NLS-1$
-			out.sendString(r.toString());
-		}
-	}
-
-	/**
-	 * Close and flush (if necessary) the underlying streams.
-	 *
-	 * @throws java.io.IOException
-	 */
-	protected void close() throws IOException {
-		if (sideBand) {
-			// If we are using side band, we need to send a final
-			// flush-pkt to tell the remote peer the side band is
-			// complete and it should stop decoding. We need to
-			// use the original output stream as rawOut is now the
-			// side band data channel.
-			//
-			((SideBandOutputStream) msgOut).flushBuffer();
-			((SideBandOutputStream) rawOut).flushBuffer();
-
-			PacketLineOut plo = new PacketLineOut(origOut);
-			plo.setFlushOnEnd(false);
-			plo.end();
-		}
-
-		if (biDirectionalPipe) {
-			// If this was a native git connection, flush the pipe for
-			// the caller. For smart HTTP we don't do this flush and
-			// instead let the higher level HTTP servlet code do it.
-			//
-			if (!sideBand && msgOut != null)
-				msgOut.flush();
-			rawOut.flush();
-		}
-	}
-
-	/**
-	 * Release any resources used by this object.
-	 *
-	 * @throws java.io.IOException
-	 *             the pack could not be unlocked.
-	 */
-	protected void release() throws IOException {
-		walk.close();
-		unlockPack();
-		timeoutIn = null;
-		rawIn = null;
-		rawOut = null;
-		msgOut = null;
-		pckIn = null;
-		pckOut = null;
-		refs = null;
-		// Keep the capabilities. If responses are sent after this release
-		// we need to remember at least whether sideband communication has to be
-		// used
-		commands = null;
-		if (timer != null) {
-			try {
-				timer.terminate();
-			} finally {
-				timer = null;
-			}
-		}
-	}
-
-	/** Interface for reporting status messages. */
-	static abstract class Reporter {
-			abstract void sendString(String s) throws IOException;
-	}
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
index d901021..6325a23 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
@@ -107,10 +107,9 @@
 		if (provider.get(uri, v)) {
 			passphrase = v.getValue();
 			return true;
-		} else {
-			passphrase = null;
-			return false;
 		}
+		passphrase = null;
+		return false;
 	}
 
 	/** {@inheritDoc} */
@@ -120,10 +119,9 @@
 		if (provider.get(uri, p)) {
 			password = new String(p.getValue());
 			return true;
-		} else {
-			password = null;
-			return false;
 		}
+		password = null;
+		return false;
 	}
 
 	private CredentialItem.StringType newPrompt(String msg) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index e3c0bc6..2fd0746 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -280,7 +280,7 @@
 	 */
 	public static final String COMMAND_FETCH = "fetch"; //$NON-NLS-1$
 
-	static enum MultiAck {
+	enum MultiAck {
 		OFF, CONTINUE, DETAILED;
 	}
 
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 01f6fec..72e95e6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
@@ -135,9 +135,8 @@
 
 		if (nonceStampSlop <= slop) {
 			return NonceStatus.OK;
-		} else {
-			return NonceStatus.SLOP;
 		}
+		return NonceStatus.SLOP;
 	}
 
 	private static final String HEX = "0123456789ABCDEF"; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 2b2795f..a9cd677 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -103,7 +103,7 @@
 	private static final int BUFFER_SIZE = 8192;
 
 	/** Location data is being obtained from. */
-	public static enum Source {
+	public enum Source {
 		/** Data is read from the incoming stream. */
 		INPUT,
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
index d73e193..bbdd555 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
@@ -95,7 +95,7 @@
 	@Deprecated
 	public static final String DELIM = new StringBuilder(0).toString(); 	/* must not string pool */
 
-	static enum AckNackResult {
+	enum AckNackResult {
 		/** NAK */
 		NAK,
 		/** ACK */
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 14ccddf..c2f69e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -215,7 +215,7 @@
 					&& line2.equals(OPTION_SIDEBAND_ALL)) {
 				reqBuilder.setSidebandAll(true);
 			} else if (line2.startsWith("packfile-uris ")) { //$NON-NLS-1$
-				for (String s : line2.substring(14).split(",")) {
+				for (String s : line2.substring(14).split(",")) { //$NON-NLS-1$
 					reqBuilder.addPackfileUriProtocol(s);
 				}
 			} else {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
index 89c1a93..3653879 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
@@ -43,7 +43,7 @@
 
 package org.eclipse.jgit.transport;
 
-import static org.eclipse.jgit.transport.BaseReceivePack.parseCommand;
+import static org.eclipse.jgit.transport.ReceivePack.parseCommand;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_CERT;
 
 import java.io.EOFException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
index a9a995c..d58bc69 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
@@ -65,14 +65,14 @@
 
 /**
  * A command being processed by
- * {@link org.eclipse.jgit.transport.BaseReceivePack}.
+ * {@link org.eclipse.jgit.transport.ReceivePack}.
  * <p>
  * This command instance roughly translates to the server side representation of
  * the {@link org.eclipse.jgit.transport.RemoteRefUpdate} created by the client.
  */
 public class ReceiveCommand {
 	/** Type of operation requested. */
-	public static enum Type {
+	public enum Type {
 		/** Create a new ref; the ref must not already exist. */
 		CREATE,
 
@@ -98,7 +98,7 @@
 	}
 
 	/** Result of the update command. */
-	public static enum Result {
+	public enum Result {
 		/** The command has not yet been attempted by the server. */
 		NOT_ATTEMPTED,
 
@@ -290,7 +290,7 @@
 
 	/**
 	 * Create a new command for
-	 * {@link org.eclipse.jgit.transport.BaseReceivePack}.
+	 * {@link org.eclipse.jgit.transport.ReceivePack}.
 	 *
 	 * @param oldId
 	 *            the expected old object id; must not be null. Use
@@ -334,7 +334,7 @@
 
 	/**
 	 * Create a new command for
-	 * {@link org.eclipse.jgit.transport.BaseReceivePack}.
+	 * {@link org.eclipse.jgit.transport.ReceivePack}.
 	 *
 	 * @param oldId
 	 *            the old object id; must not be null. Use
@@ -768,9 +768,9 @@
 	 *
 	 * @param rp
 	 *            receive-pack session.
-	 * @since 2.0
+	 * @since 5.6
 	 */
-	public void execute(BaseReceivePack rp) {
+	public void execute(ReceivePack rp) {
 		try {
 			String expTarget = getOldSymref();
 			boolean detach = getNewSymref() != null
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index d6adf1e..fab9890 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -43,36 +43,255 @@
 
 package org.eclipse.jgit.transport;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.eclipse.jgit.lib.Constants.HEAD;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
+import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
+import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
+import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
+import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.text.MessageFormat;
 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.TimeUnit;
 
 import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.errors.TooLargePackException;
 import org.eclipse.jgit.errors.UnpackException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackLock;
+import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
+import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
+import org.eclipse.jgit.internal.transport.parser.FirstCommand;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GitmoduleEntry;
 import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectChecker;
+import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSubclassMap;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
 import org.eclipse.jgit.transport.ReceiveCommand.Result;
 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
+import org.eclipse.jgit.util.io.InterruptTimer;
+import org.eclipse.jgit.util.io.LimitedInputStream;
+import org.eclipse.jgit.util.io.TimeoutInputStream;
+import org.eclipse.jgit.util.io.TimeoutOutputStream;
 
 /**
  * Implements the server side of a push connection, receiving objects.
  */
-public class ReceivePack extends BaseReceivePack {
+public class ReceivePack {
+	/**
+	 * Data in the first line of a request, the line itself plus capabilities.
+	 *
+	 * @deprecated Use {@link FirstCommand} instead.
+	 * @since 5.6
+	 */
+	@Deprecated
+	public static class FirstLine {
+		private final FirstCommand command;
+
+		/**
+		 * Parse the first line of a receive-pack request.
+		 *
+		 * @param line
+		 *            line from the client.
+		 */
+		public FirstLine(String line) {
+			command = FirstCommand.fromLine(line);
+		}
+
+		/** @return non-capabilities part of the line. */
+		public String getLine() {
+			return command.getLine();
+		}
+
+		/** @return capabilities parsed from the line. */
+		public Set<String> getCapabilities() {
+			return command.getCapabilities();
+		}
+	}
+
+	/** Database we write the stored objects into. */
+	private final Repository db;
+
+	/** Revision traversal support over {@link #db}. */
+	private final RevWalk walk;
+
+	/**
+	 * Is the client connection a bi-directional socket or pipe?
+	 * <p>
+	 * If true, this class assumes it can perform multiple read and write cycles
+	 * with the client over the input and output streams. This matches the
+	 * functionality available with a standard TCP/IP connection, or a local
+	 * operating system or in-memory pipe.
+	 * <p>
+	 * If false, this class runs in a read everything then output results mode,
+	 * making it suitable for single round-trip systems RPCs such as HTTP.
+	 */
+	private boolean biDirectionalPipe = true;
+
+	/** Expecting data after the pack footer */
+	private boolean expectDataAfterPackFooter;
+
+	/** Should an incoming transfer validate objects? */
+	private ObjectChecker objectChecker;
+
+	/** Should an incoming transfer permit create requests? */
+	private boolean allowCreates;
+
+	/** Should an incoming transfer permit delete requests? */
+	private boolean allowAnyDeletes;
+
+	private boolean allowBranchDeletes;
+
+	/** Should an incoming transfer permit non-fast-forward requests? */
+	private boolean allowNonFastForwards;
+
+	/** Should an incoming transfer permit push options? **/
+	private boolean allowPushOptions;
+
+	/**
+	 * Should the requested ref updates be performed as a single atomic
+	 * transaction?
+	 */
+	private boolean atomic;
+
+	private boolean allowOfsDelta;
+
+	private boolean allowQuiet = true;
+
+	/** Identity to record action as within the reflog. */
+	private PersonIdent refLogIdent;
+
+	/** Hook used while advertising the refs to the client. */
+	private AdvertiseRefsHook advertiseRefsHook;
+
+	/** Filter used while advertising the refs to the client. */
+	private RefFilter refFilter;
+
+	/** Timeout in seconds to wait for client interaction. */
+	private int timeout;
+
+	/** Timer to manage {@link #timeout}. */
+	private InterruptTimer timer;
+
+	private TimeoutInputStream timeoutIn;
+
+	// Original stream passed to init(), since rawOut may be wrapped in a
+	// sideband.
+	private OutputStream origOut;
+
+	/** Raw input stream. */
+	private InputStream rawIn;
+
+	/** Raw output stream. */
+	private OutputStream rawOut;
+
+	/** Optional message output stream. */
+	private OutputStream msgOut;
+
+	private SideBandOutputStream errOut;
+
+	/** Packet line input stream around {@link #rawIn}. */
+	private PacketLineIn pckIn;
+
+	/** Packet line output stream around {@link #rawOut}. */
+	private PacketLineOut pckOut;
+
+	private final MessageOutputWrapper msgOutWrapper = new MessageOutputWrapper();
+
+	private PackParser parser;
+
+	/** The refs we advertised as existing at the start of the connection. */
+	private Map<String, Ref> refs;
+
+	/** All SHA-1s shown to the client, which can be possible edges. */
+	private Set<ObjectId> advertisedHaves;
+
+	/** Capabilities requested by the client. */
+	private Set<String> enabledCapabilities;
+
+	String userAgent;
+
+	private Set<ObjectId> clientShallowCommits;
+
+	private List<ReceiveCommand> commands;
+
+	private long maxCommandBytes;
+
+	private long maxDiscardBytes;
+
+	private StringBuilder advertiseError;
+
+	/**
+	 * If {@link BasePackPushConnection#CAPABILITY_SIDE_BAND_64K} is enabled.
+	 */
+	private boolean sideBand;
+
+	private boolean quiet;
+
+	/** Lock around the received pack file, while updating refs. */
+	private PackLock packLock;
+
+	private boolean checkReferencedIsReachable;
+
+	/** Git object size limit */
+	private long maxObjectSizeLimit;
+
+	/** Total pack size limit */
+	private long maxPackSizeLimit = -1;
+
+	/** The size of the received pack, including index size */
+	private Long packSize;
+
+	private PushCertificateParser pushCertificateParser;
+
+	private SignedPushConfig signedPushConfig;
+
+	private PushCertificate pushCert;
+
+	private ReceivedPackStatistics stats;
+
 	/** Hook to validate the update commands before execution. */
 	private PreReceiveHook preReceive;
 
@@ -93,18 +312,120 @@
 	 *            the destination repository.
 	 */
 	public ReceivePack(Repository into) {
-		super(into);
+		db = into;
+		walk = new RevWalk(db);
+		walk.setRetainBody(false);
+
+		TransferConfig tc = db.getConfig().get(TransferConfig.KEY);
+		objectChecker = tc.newReceiveObjectChecker();
+
+		ReceiveConfig rc = db.getConfig().get(ReceiveConfig::new);
+		allowCreates = rc.allowCreates;
+		allowAnyDeletes = true;
+		allowBranchDeletes = rc.allowDeletes;
+		allowNonFastForwards = rc.allowNonFastForwards;
+		allowOfsDelta = rc.allowOfsDelta;
+		allowPushOptions = rc.allowPushOptions;
+		maxCommandBytes = rc.maxCommandBytes;
+		maxDiscardBytes = rc.maxDiscardBytes;
+		advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
+		refFilter = RefFilter.DEFAULT;
+		advertisedHaves = new HashSet<>();
+		clientShallowCommits = new HashSet<>();
+		signedPushConfig = rc.signedPush;
 		preReceive = PreReceiveHook.NULL;
 		postReceive = PostReceiveHook.NULL;
 	}
 
+	/** Configuration for receive operations. */
+	private static class ReceiveConfig {
+		final boolean allowCreates;
+
+		final boolean allowDeletes;
+
+		final boolean allowNonFastForwards;
+
+		final boolean allowOfsDelta;
+
+		final boolean allowPushOptions;
+
+		final long maxCommandBytes;
+
+		final long maxDiscardBytes;
+
+		final SignedPushConfig signedPush;
+
+		ReceiveConfig(Config config) {
+			allowCreates = true;
+			allowDeletes = !config.getBoolean("receive", "denydeletes", false); //$NON-NLS-1$ //$NON-NLS-2$
+			allowNonFastForwards = !config.getBoolean("receive", //$NON-NLS-1$
+					"denynonfastforwards", false); //$NON-NLS-1$
+			allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$
+					true);
+			allowPushOptions = config.getBoolean("receive", "pushoptions", //$NON-NLS-1$ //$NON-NLS-2$
+					false);
+			maxCommandBytes = config.getLong("receive", //$NON-NLS-1$
+					"maxCommandBytes", //$NON-NLS-1$
+					3 << 20);
+			maxDiscardBytes = config.getLong("receive", //$NON-NLS-1$
+					"maxCommandDiscardBytes", //$NON-NLS-1$
+					-1);
+			signedPush = SignedPushConfig.KEY.parse(config);
+		}
+	}
+
+	/**
+	 * Output stream that wraps the current {@link #msgOut}.
+	 * <p>
+	 * We don't want to expose {@link #msgOut} directly because it can change
+	 * several times over the course of a session.
+	 */
+	class MessageOutputWrapper extends OutputStream {
+		@Override
+		public void write(int ch) {
+			if (msgOut != null) {
+				try {
+					msgOut.write(ch);
+				} catch (IOException e) {
+					// Ignore write failures.
+				}
+			}
+		}
+
+		@Override
+		public void write(byte[] b, int off, int len) {
+			if (msgOut != null) {
+				try {
+					msgOut.write(b, off, len);
+				} catch (IOException e) {
+					// Ignore write failures.
+				}
+			}
+		}
+
+		@Override
+		public void write(byte[] b) {
+			write(b, 0, b.length);
+		}
+
+		@Override
+		public void flush() {
+			if (msgOut != null) {
+				try {
+					msgOut.flush();
+				} catch (IOException e) {
+					// Ignore write failures.
+				}
+			}
+		}
+	}
+
 	/**
 	 * Get the repository this receive completes into.
 	 *
 	 * @return the repository this receive completes into.
 	 */
-	@Override
-	public final Repository getRepository() {
+	public Repository getRepository() {
 		return db;
 	}
 
@@ -113,8 +434,7 @@
 	 *
 	 * @return the RevWalk instance used by this connection.
 	 */
-	@Override
-	public final RevWalk getRevWalk() {
+	public RevWalk getRevWalk() {
 		return walk;
 	}
 
@@ -124,8 +444,7 @@
 	 * @return all refs which were advertised to the client, or null if
 	 *         {@link #setAdvertisedRefs(Map, Set)} has not been called yet.
 	 */
-	@Override
-	public final Map<String, Ref> getAdvertisedRefs() {
+	public Map<String, Ref> getAdvertisedRefs() {
 		return refs;
 	}
 
@@ -146,8 +465,8 @@
 	 *            null, assumes the default set of additional haves from the
 	 *            repository.
 	 */
-	@Override
-	public void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves) {
+	public void setAdvertisedRefs(Map<String, Ref> allRefs,
+			Set<ObjectId> additionalHaves) {
 		refs = allRefs != null ? allRefs : db.getAllRefs();
 		refs = refFilter.filter(refs);
 		advertisedHaves.clear();
@@ -170,6 +489,1525 @@
 	}
 
 	/**
+	 * Get objects advertised to the client.
+	 *
+	 * @return the set of objects advertised to the as present in this
+	 *         repository, or null if {@link #setAdvertisedRefs(Map, Set)} has
+	 *         not been called yet.
+	 */
+	public final Set<ObjectId> getAdvertisedObjects() {
+		return advertisedHaves;
+	}
+
+	/**
+	 * Whether this instance will validate all referenced, but not supplied by
+	 * the client, objects are reachable from another reference.
+	 *
+	 * @return true if this instance will validate all referenced, but not
+	 *         supplied by the client, objects are reachable from another
+	 *         reference.
+	 */
+	public boolean isCheckReferencedObjectsAreReachable() {
+		return checkReferencedIsReachable;
+	}
+
+	/**
+	 * Validate all referenced but not supplied objects are reachable.
+	 * <p>
+	 * If enabled, this instance will verify that references to objects not
+	 * contained within the received pack are already reachable through at least
+	 * one other reference displayed as part of {@link #getAdvertisedRefs()}.
+	 * <p>
+	 * This feature is useful when the application doesn't trust the client to
+	 * not provide a forged SHA-1 reference to an object, in an attempt to
+	 * access parts of the DAG that they aren't allowed to see and which have
+	 * been hidden from them via the configured
+	 * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} or
+	 * {@link org.eclipse.jgit.transport.RefFilter}.
+	 * <p>
+	 * Enabling this feature may imply at least some, if not all, of the same
+	 * functionality performed by {@link #setCheckReceivedObjects(boolean)}.
+	 * Applications are encouraged to enable both features, if desired.
+	 *
+	 * @param b
+	 *            {@code true} to enable the additional check.
+	 */
+	public void setCheckReferencedObjectsAreReachable(boolean b) {
+		this.checkReferencedIsReachable = b;
+	}
+
+	/**
+	 * Whether this class expects a bi-directional pipe opened between the
+	 * client and itself.
+	 *
+	 * @return true if this class expects a bi-directional pipe opened between
+	 *         the client and itself. The default is true.
+	 */
+	public boolean isBiDirectionalPipe() {
+		return biDirectionalPipe;
+	}
+
+	/**
+	 * Whether this class will assume the socket is a fully bidirectional pipe
+	 * between the two peers and takes advantage of that by first transmitting
+	 * the known refs, then waiting to read commands.
+	 *
+	 * @param twoWay
+	 *            if true, this class will assume the socket is a fully
+	 *            bidirectional pipe between the two peers and takes advantage
+	 *            of that by first transmitting the known refs, then waiting to
+	 *            read commands. If false, this class assumes it must read the
+	 *            commands before writing output and does not perform the
+	 *            initial advertising.
+	 */
+	public void setBiDirectionalPipe(boolean twoWay) {
+		biDirectionalPipe = twoWay;
+	}
+
+	/**
+	 * Whether there is data expected after the pack footer.
+	 *
+	 * @return {@code true} if there is data expected after the pack footer.
+	 */
+	public boolean isExpectDataAfterPackFooter() {
+		return expectDataAfterPackFooter;
+	}
+
+	/**
+	 * Whether there is additional data in InputStream after pack.
+	 *
+	 * @param e
+	 *            {@code true} if there is additional data in InputStream after
+	 *            pack.
+	 */
+	public void setExpectDataAfterPackFooter(boolean e) {
+		expectDataAfterPackFooter = e;
+	}
+
+	/**
+	 * Whether this instance will verify received objects are formatted
+	 * correctly.
+	 *
+	 * @return {@code true} if this instance will verify received objects are
+	 *         formatted correctly. Validating objects requires more CPU time on
+	 *         this side of the connection.
+	 */
+	public boolean isCheckReceivedObjects() {
+		return objectChecker != null;
+	}
+
+	/**
+	 * Whether to enable checking received objects
+	 *
+	 * @param check
+	 *            {@code true} to enable checking received objects; false to
+	 *            assume all received objects are valid.
+	 * @see #setObjectChecker(ObjectChecker)
+	 */
+	public void setCheckReceivedObjects(boolean check) {
+		if (check && objectChecker == null)
+			setObjectChecker(new ObjectChecker());
+		else if (!check && objectChecker != null)
+			setObjectChecker(null);
+	}
+
+	/**
+	 * Set the object checking instance to verify each received object with
+	 *
+	 * @param impl
+	 *            if non-null the object checking instance to verify each
+	 *            received object with; null to disable object checking.
+	 * @since 3.4
+	 */
+	public void setObjectChecker(ObjectChecker impl) {
+		objectChecker = impl;
+	}
+
+	/**
+	 * Whether the client can request refs to be created.
+	 *
+	 * @return {@code true} if the client can request refs to be created.
+	 */
+	public boolean isAllowCreates() {
+		return allowCreates;
+	}
+
+	/**
+	 * Whether to permit create ref commands to be processed.
+	 *
+	 * @param canCreate
+	 *            {@code true} to permit create ref commands to be processed.
+	 */
+	public void setAllowCreates(boolean canCreate) {
+		allowCreates = canCreate;
+	}
+
+	/**
+	 * Whether the client can request refs to be deleted.
+	 *
+	 * @return {@code true} if the client can request refs to be deleted.
+	 */
+	public boolean isAllowDeletes() {
+		return allowAnyDeletes;
+	}
+
+	/**
+	 * Whether to permit delete ref commands to be processed.
+	 *
+	 * @param canDelete
+	 *            {@code true} to permit delete ref commands to be processed.
+	 */
+	public void setAllowDeletes(boolean canDelete) {
+		allowAnyDeletes = canDelete;
+	}
+
+	/**
+	 * Whether the client can delete from {@code refs/heads/}.
+	 *
+	 * @return {@code true} if the client can delete from {@code refs/heads/}.
+	 * @since 3.6
+	 */
+	public boolean isAllowBranchDeletes() {
+		return allowBranchDeletes;
+	}
+
+	/**
+	 * Configure whether to permit deletion of branches from the
+	 * {@code refs/heads/} namespace.
+	 *
+	 * @param canDelete
+	 *            {@code true} to permit deletion of branches from the
+	 *            {@code refs/heads/} namespace.
+	 * @since 3.6
+	 */
+	public void setAllowBranchDeletes(boolean canDelete) {
+		allowBranchDeletes = canDelete;
+	}
+
+	/**
+	 * Whether the client can request non-fast-forward updates of a ref,
+	 * possibly making objects unreachable.
+	 *
+	 * @return {@code true} if the client can request non-fast-forward updates
+	 *         of a ref, possibly making objects unreachable.
+	 */
+	public boolean isAllowNonFastForwards() {
+		return allowNonFastForwards;
+	}
+
+	/**
+	 * Configure whether to permit the client to ask for non-fast-forward
+	 * updates of an existing ref.
+	 *
+	 * @param canRewind
+	 *            {@code true} to permit the client to ask for non-fast-forward
+	 *            updates of an existing ref.
+	 */
+	public void setAllowNonFastForwards(boolean canRewind) {
+		allowNonFastForwards = canRewind;
+	}
+
+	/**
+	 * Whether the client's commands should be performed as a single atomic
+	 * transaction.
+	 *
+	 * @return {@code true} if the client's commands should be performed as a
+	 *         single atomic transaction.
+	 * @since 4.4
+	 */
+	public boolean isAtomic() {
+		return atomic;
+	}
+
+	/**
+	 * Configure whether to perform the client's commands as a single atomic
+	 * transaction.
+	 *
+	 * @param atomic
+	 *            {@code true} to perform the client's commands as a single
+	 *            atomic transaction.
+	 * @since 4.4
+	 */
+	public void setAtomic(boolean atomic) {
+		this.atomic = atomic;
+	}
+
+	/**
+	 * Get identity of the user making the changes in the reflog.
+	 *
+	 * @return identity of the user making the changes in the reflog.
+	 */
+	public PersonIdent getRefLogIdent() {
+		return refLogIdent;
+	}
+
+	/**
+	 * Set the identity of the user appearing in the affected reflogs.
+	 * <p>
+	 * The timestamp portion of the identity is ignored. A new identity with the
+	 * current timestamp will be created automatically when the updates occur
+	 * and the log records are written.
+	 *
+	 * @param pi
+	 *            identity of the user. If null the identity will be
+	 *            automatically determined based on the repository
+	 *            configuration.
+	 */
+	public void setRefLogIdent(PersonIdent pi) {
+		refLogIdent = pi;
+	}
+
+	/**
+	 * Get the hook used while advertising the refs to the client
+	 *
+	 * @return the hook used while advertising the refs to the client
+	 */
+	public AdvertiseRefsHook getAdvertiseRefsHook() {
+		return advertiseRefsHook;
+	}
+
+	/**
+	 * Get the filter used while advertising the refs to the client
+	 *
+	 * @return the filter used while advertising the refs to the client
+	 */
+	public RefFilter getRefFilter() {
+		return refFilter;
+	}
+
+	/**
+	 * Set the hook used while advertising the refs to the client.
+	 * <p>
+	 * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to
+	 * call {@link #setAdvertisedRefs(Map,Set)}, only refs set by this hook
+	 * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter}
+	 * will be shown to the client. Clients may still attempt to create or
+	 * update a reference not advertised by the configured
+	 * {@link org.eclipse.jgit.transport.AdvertiseRefsHook}. These attempts
+	 * should be rejected by a matching
+	 * {@link org.eclipse.jgit.transport.PreReceiveHook}.
+	 *
+	 * @param advertiseRefsHook
+	 *            the hook; may be null to show all refs.
+	 */
+	public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
+		if (advertiseRefsHook != null)
+			this.advertiseRefsHook = advertiseRefsHook;
+		else
+			this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
+	}
+
+	/**
+	 * Set the filter used while advertising the refs to the client.
+	 * <p>
+	 * Only refs allowed by this filter will be shown to the client. The filter
+	 * is run against the refs specified by the
+	 * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable).
+	 *
+	 * @param refFilter
+	 *            the filter; may be null to show all refs.
+	 */
+	public void setRefFilter(RefFilter refFilter) {
+		this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
+	}
+
+	/**
+	 * Get timeout (in seconds) before aborting an IO operation.
+	 *
+	 * @return timeout (in seconds) before aborting an IO operation.
+	 */
+	public int getTimeout() {
+		return timeout;
+	}
+
+	/**
+	 * Set the timeout before willing to abort an IO call.
+	 *
+	 * @param seconds
+	 *            number of seconds to wait (with no data transfer occurring)
+	 *            before aborting an IO read or write operation with the
+	 *            connected client.
+	 */
+	public void setTimeout(int seconds) {
+		timeout = seconds;
+	}
+
+	/**
+	 * Set the maximum number of command bytes to read from the client.
+	 *
+	 * @param limit
+	 *            command limit in bytes; if 0 there is no limit.
+	 * @since 4.7
+	 */
+	public void setMaxCommandBytes(long limit) {
+		maxCommandBytes = limit;
+	}
+
+	/**
+	 * Set the maximum number of command bytes to discard from the client.
+	 * <p>
+	 * Discarding remaining bytes allows this instance to consume the rest of
+	 * the command block and send a human readable over-limit error via the
+	 * side-band channel. If the client sends an excessive number of bytes this
+	 * limit kicks in and the instance disconnects, resulting in a non-specific
+	 * 'pipe closed', 'end of stream', or similar generic error at the client.
+	 * <p>
+	 * When the limit is set to {@code -1} the implementation will default to
+	 * the larger of {@code 3 * maxCommandBytes} or {@code 3 MiB}.
+	 *
+	 * @param limit
+	 *            discard limit in bytes; if 0 there is no limit; if -1 the
+	 *            implementation tries to set a reasonable default.
+	 * @since 4.7
+	 */
+	public void setMaxCommandDiscardBytes(long limit) {
+		maxDiscardBytes = limit;
+	}
+
+	/**
+	 * Set the maximum allowed Git object size.
+	 * <p>
+	 * If an object is larger than the given size the pack-parsing will throw an
+	 * exception aborting the receive-pack operation.
+	 *
+	 * @param limit
+	 *            the Git object size limit. If zero then there is not limit.
+	 */
+	public void setMaxObjectSizeLimit(long limit) {
+		maxObjectSizeLimit = limit;
+	}
+
+	/**
+	 * Set the maximum allowed pack size.
+	 * <p>
+	 * A pack exceeding this size will be rejected.
+	 *
+	 * @param limit
+	 *            the pack size limit, in bytes
+	 * @since 3.3
+	 */
+	public void setMaxPackSizeLimit(long limit) {
+		if (limit < 0)
+			throw new IllegalArgumentException(
+					MessageFormat.format(JGitText.get().receivePackInvalidLimit,
+							Long.valueOf(limit)));
+		maxPackSizeLimit = limit;
+	}
+
+	/**
+	 * Check whether the client expects a side-band stream.
+	 *
+	 * @return true if the client has advertised a side-band capability, false
+	 *         otherwise.
+	 * @throws org.eclipse.jgit.transport.RequestNotYetReadException
+	 *             if the client's request has not yet been read from the wire,
+	 *             so we do not know if they expect side-band. Note that the
+	 *             client may have already written the request, it just has not
+	 *             been read.
+	 */
+	public boolean isSideBand() throws RequestNotYetReadException {
+		checkRequestWasRead();
+		return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
+	}
+
+	/**
+	 * Whether clients may request avoiding noisy progress messages.
+	 *
+	 * @return true if clients may request avoiding noisy progress messages.
+	 * @since 4.0
+	 */
+	public boolean isAllowQuiet() {
+		return allowQuiet;
+	}
+
+	/**
+	 * Configure if clients may request the server skip noisy messages.
+	 *
+	 * @param allow
+	 *            true to allow clients to request quiet behavior; false to
+	 *            refuse quiet behavior and send messages anyway. This may be
+	 *            necessary if processing is slow and the client-server network
+	 *            connection can timeout.
+	 * @since 4.0
+	 */
+	public void setAllowQuiet(boolean allow) {
+		allowQuiet = allow;
+	}
+
+	/**
+	 * Whether the server supports receiving push options.
+	 *
+	 * @return true if the server supports receiving push options.
+	 * @since 4.5
+	 */
+	public boolean isAllowPushOptions() {
+		return allowPushOptions;
+	}
+
+	/**
+	 * Configure if the server supports receiving push options.
+	 *
+	 * @param allow
+	 *            true to optionally accept option strings from the client.
+	 * @since 4.5
+	 */
+	public void setAllowPushOptions(boolean allow) {
+		allowPushOptions = allow;
+	}
+
+	/**
+	 * True if the client wants less verbose output.
+	 *
+	 * @return true if the client has requested the server to be less verbose.
+	 * @throws org.eclipse.jgit.transport.RequestNotYetReadException
+	 *             if the client's request has not yet been read from the wire,
+	 *             so we do not know if they expect side-band. Note that the
+	 *             client may have already written the request, it just has not
+	 *             been read.
+	 * @since 4.0
+	 */
+	public boolean isQuiet() throws RequestNotYetReadException {
+		checkRequestWasRead();
+		return quiet;
+	}
+
+	/**
+	 * Set the configuration for push certificate verification.
+	 *
+	 * @param cfg
+	 *            new configuration; if this object is null or its
+	 *            {@link SignedPushConfig#getCertNonceSeed()} is null, push
+	 *            certificate verification will be disabled.
+	 * @since 4.1
+	 */
+	public void setSignedPushConfig(SignedPushConfig cfg) {
+		signedPushConfig = cfg;
+	}
+
+	private PushCertificateParser getPushCertificateParser() {
+		if (pushCertificateParser == null) {
+			pushCertificateParser = new PushCertificateParser(db,
+					signedPushConfig);
+		}
+		return pushCertificateParser;
+	}
+
+	/**
+	 * Get the user agent of the client.
+	 * <p>
+	 * If the client is new enough to use {@code agent=} capability that value
+	 * will be returned. Older HTTP clients may also supply their version using
+	 * the HTTP {@code User-Agent} header. The capability overrides the HTTP
+	 * header if both are available.
+	 * <p>
+	 * When an HTTP request has been received this method returns the HTTP
+	 * {@code User-Agent} header value until capabilities have been parsed.
+	 *
+	 * @return user agent supplied by the client. Available only if the client
+	 *         is new enough to advertise its user agent.
+	 * @since 4.0
+	 */
+	public String getPeerUserAgent() {
+		return UserAgent.getAgent(enabledCapabilities, userAgent);
+	}
+
+	/**
+	 * Get all of the command received by the current request.
+	 *
+	 * @return all of the command received by the current request.
+	 */
+	public List<ReceiveCommand> getAllCommands() {
+		return Collections.unmodifiableList(commands);
+	}
+
+	/**
+	 * Send an error message to the client.
+	 * <p>
+	 * If any error messages are sent before the references are advertised to
+	 * the client, the errors will be sent instead of the advertisement and the
+	 * receive operation will be aborted. All clients should receive and display
+	 * such early stage errors.
+	 * <p>
+	 * If the reference advertisements have already been sent, messages are sent
+	 * in a side channel. If the client doesn't support receiving messages, the
+	 * message will be discarded, with no other indication to the caller or to
+	 * the client.
+	 * <p>
+	 * {@link org.eclipse.jgit.transport.PreReceiveHook}s should always try to
+	 * use
+	 * {@link org.eclipse.jgit.transport.ReceiveCommand#setResult(Result, String)}
+	 * with a result status of
+	 * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#REJECTED_OTHER_REASON}
+	 * to indicate any reasons for rejecting an update. Messages attached to a
+	 * command are much more likely to be returned to the client.
+	 *
+	 * @param what
+	 *            string describing the problem identified by the hook. The
+	 *            string must not end with an LF, and must not contain an LF.
+	 */
+	public void sendError(String what) {
+		if (refs == null) {
+			if (advertiseError == null)
+				advertiseError = new StringBuilder();
+			advertiseError.append(what).append('\n');
+		} else {
+			msgOutWrapper.write(Constants.encode("error: " + what + "\n")); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	private void fatalError(String msg) {
+		if (errOut != null) {
+			try {
+				errOut.write(Constants.encode(msg));
+				errOut.flush();
+			} catch (IOException e) {
+				// Ignore write failures
+			}
+		} else {
+			sendError(msg);
+		}
+	}
+
+	/**
+	 * Send a message to the client, if it supports receiving them.
+	 * <p>
+	 * If the client doesn't support receiving messages, the message will be
+	 * discarded, with no other indication to the caller or to the client.
+	 *
+	 * @param what
+	 *            string describing the problem identified by the hook. The
+	 *            string must not end with an LF, and must not contain an LF.
+	 */
+	public void sendMessage(String what) {
+		msgOutWrapper.write(Constants.encode(what + "\n")); //$NON-NLS-1$
+	}
+
+	/**
+	 * Get an underlying stream for sending messages to the client.
+	 *
+	 * @return an underlying stream for sending messages to the client.
+	 */
+	public OutputStream getMessageOutputStream() {
+		return msgOutWrapper;
+	}
+
+	/**
+	 * Get whether or not a pack has been received.
+	 *
+	 * This can be called before calling {@link #getPackSize()} to avoid causing
+	 * {@code IllegalStateException} when the pack size was not set because no
+	 * pack was received.
+	 *
+	 * @return true if a pack has been received.
+	 * @since 5.6
+	 */
+	public boolean hasReceivedPack() {
+		return packSize != null;
+	}
+
+	/**
+	 * Get the size of the received pack file including the index size.
+	 *
+	 * This can only be called if the pack is already received.
+	 *
+	 * @return the size of the received pack including index size
+	 * @throws java.lang.IllegalStateException
+	 *             if called before the pack has been received
+	 * @since 3.3
+	 */
+	public long getPackSize() {
+		if (packSize != null)
+			return packSize.longValue();
+		throw new IllegalStateException(JGitText.get().packSizeNotSetYet);
+	}
+
+	/**
+	 * Get the commits from the client's shallow file.
+	 *
+	 * @return if the client is a shallow repository, the list of edge commits
+	 *         that define the client's shallow boundary. Empty set if the
+	 *         client is earlier than Git 1.9, or is a full clone.
+	 */
+	private Set<ObjectId> getClientShallowCommits() {
+		return clientShallowCommits;
+	}
+
+	/**
+	 * Whether any commands to be executed have been read.
+	 *
+	 * @return {@code true} if any commands to be executed have been read.
+	 */
+	private boolean hasCommands() {
+		return !commands.isEmpty();
+	}
+
+	/**
+	 * Whether an error occurred that should be advertised.
+	 *
+	 * @return true if an error occurred that should be advertised.
+	 */
+	private boolean hasError() {
+		return advertiseError != null;
+	}
+
+	/**
+	 * Initialize the instance with the given streams.
+	 *
+	 * Visible for out-of-tree subclasses (e.g. tests that need to set the
+	 * streams without going through the {@link #service()} method).
+	 *
+	 * @param input
+	 *            raw input to read client commands and pack data from. Caller
+	 *            must ensure the input is buffered, otherwise read performance
+	 *            may suffer.
+	 * @param output
+	 *            response back to the Git network client. Caller must ensure
+	 *            the output is buffered, otherwise write performance may
+	 *            suffer.
+	 * @param messages
+	 *            secondary "notice" channel to send additional messages out
+	 *            through. When run over SSH this should be tied back to the
+	 *            standard error channel of the command execution. For most
+	 *            other network connections this should be null.
+	 */
+	protected void init(final InputStream input, final OutputStream output,
+			final OutputStream messages) {
+		origOut = output;
+		rawIn = input;
+		rawOut = output;
+		msgOut = messages;
+
+		if (timeout > 0) {
+			final Thread caller = Thread.currentThread();
+			timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
+			timeoutIn = new TimeoutInputStream(rawIn, timer);
+			TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
+			timeoutIn.setTimeout(timeout * 1000);
+			o.setTimeout(timeout * 1000);
+			rawIn = timeoutIn;
+			rawOut = o;
+		}
+
+		pckIn = new PacketLineIn(rawIn);
+		pckOut = new PacketLineOut(rawOut);
+		pckOut.setFlushOnEnd(false);
+
+		enabledCapabilities = new HashSet<>();
+		commands = new ArrayList<>();
+	}
+
+	/**
+	 * Get advertised refs, or the default if not explicitly advertised.
+	 *
+	 * @return advertised refs, or the default if not explicitly advertised.
+	 */
+	private Map<String, Ref> getAdvertisedOrDefaultRefs() {
+		if (refs == null)
+			setAdvertisedRefs(null, null);
+		return refs;
+	}
+
+	/**
+	 * Receive a pack from the stream and check connectivity if necessary.
+	 *
+	 * Visible for out-of-tree subclasses. Subclasses overriding this method
+	 * should invoke this implementation, as it alters the instance state (e.g.
+	 * it reads the pack from the input and parses it before running the
+	 * connectivity checks).
+	 *
+	 * @throws java.io.IOException
+	 *             an error occurred during unpacking or connectivity checking.
+	 */
+	protected void receivePackAndCheckConnectivity() throws IOException {
+		receivePack();
+		if (needCheckConnectivity()) {
+			checkSubmodules();
+			checkConnectivity();
+		}
+		parser = null;
+	}
+
+	/**
+	 * Unlock the pack written by this object.
+	 *
+	 * @throws java.io.IOException
+	 *             the pack could not be unlocked.
+	 */
+	private void unlockPack() throws IOException {
+		if (packLock != null) {
+			packLock.unlock();
+			packLock = null;
+		}
+	}
+
+	/**
+	 * Generate an advertisement of available refs and capabilities.
+	 *
+	 * @param adv
+	 *            the advertisement formatter.
+	 * @throws java.io.IOException
+	 *             the formatter failed to write an advertisement.
+	 * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
+	 *             the hook denied advertisement.
+	 */
+	public void sendAdvertisedRefs(RefAdvertiser adv)
+			throws IOException, ServiceMayNotContinueException {
+		if (advertiseError != null) {
+			adv.writeOne("ERR " + advertiseError); //$NON-NLS-1$
+			return;
+		}
+
+		try {
+			advertiseRefsHook.advertiseRefs(this);
+		} catch (ServiceMayNotContinueException fail) {
+			if (fail.getMessage() != null) {
+				adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$
+				fail.setOutput();
+			}
+			throw fail;
+		}
+
+		adv.init(db);
+		adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
+		adv.advertiseCapability(CAPABILITY_DELETE_REFS);
+		adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
+		if (allowQuiet)
+			adv.advertiseCapability(CAPABILITY_QUIET);
+		String nonce = getPushCertificateParser().getAdvertiseNonce();
+		if (nonce != null) {
+			adv.advertiseCapability(nonce);
+		}
+		if (db.getRefDatabase().performsAtomicTransactions())
+			adv.advertiseCapability(CAPABILITY_ATOMIC);
+		if (allowOfsDelta)
+			adv.advertiseCapability(CAPABILITY_OFS_DELTA);
+		if (allowPushOptions) {
+			adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS);
+		}
+		adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
+		adv.send(getAdvertisedOrDefaultRefs().values());
+		for (ObjectId obj : advertisedHaves)
+			adv.advertiseHave(obj);
+		if (adv.isEmpty())
+			adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
+		adv.end();
+	}
+
+	/**
+	 * Returns the statistics on the received pack if available. This should be
+	 * called after {@link #receivePack} is called.
+	 *
+	 * @return ReceivedPackStatistics
+	 * @since 4.6
+	 */
+	@Nullable
+	public ReceivedPackStatistics getReceivedPackStatistics() {
+		return stats;
+	}
+
+	/**
+	 * Receive a list of commands from the input.
+	 *
+	 * @throws java.io.IOException
+	 */
+	private void recvCommands() throws IOException {
+		PacketLineIn pck = maxCommandBytes > 0
+				? new PacketLineIn(rawIn, maxCommandBytes)
+				: pckIn;
+		PushCertificateParser certParser = getPushCertificateParser();
+		boolean firstPkt = true;
+		try {
+			for (;;) {
+				String line;
+				try {
+					line = pck.readString();
+				} catch (EOFException eof) {
+					if (commands.isEmpty())
+						return;
+					throw eof;
+				}
+				if (PacketLineIn.isEnd(line)) {
+					break;
+				}
+
+				if (line.length() >= 48 && line.startsWith("shallow ")) { //$NON-NLS-1$
+					parseShallow(line.substring(8, 48));
+					continue;
+				}
+
+				if (firstPkt) {
+					firstPkt = false;
+					FirstCommand firstLine = FirstCommand.fromLine(line);
+					enabledCapabilities = firstLine.getCapabilities();
+					line = firstLine.getLine();
+					enableCapabilities();
+
+					if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) {
+						certParser.receiveHeader(pck, !isBiDirectionalPipe());
+						continue;
+					}
+				}
+
+				if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
+					certParser.receiveSignature(pck);
+					continue;
+				}
+
+				ReceiveCommand cmd = parseCommand(line);
+				if (cmd.getRefName().equals(Constants.HEAD)) {
+					cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
+				} else {
+					cmd.setRef(refs.get(cmd.getRefName()));
+				}
+				commands.add(cmd);
+				if (certParser.enabled()) {
+					certParser.addCommand(cmd);
+				}
+			}
+			pushCert = certParser.build();
+			if (hasCommands()) {
+				readPostCommands(pck);
+			}
+		} catch (PackProtocolException e) {
+			discardCommands();
+			fatalError(e.getMessage());
+			throw e;
+		} catch (InputOverLimitIOException e) {
+			String msg = JGitText.get().tooManyCommands;
+			discardCommands();
+			fatalError(msg);
+			throw new PackProtocolException(msg);
+		}
+	}
+
+	private void discardCommands() {
+		if (sideBand) {
+			long max = maxDiscardBytes;
+			if (max < 0) {
+				max = Math.max(3 * maxCommandBytes, 3L << 20);
+			}
+			try {
+				new PacketLineIn(rawIn, max).discardUntilEnd();
+			} catch (IOException e) {
+				// Ignore read failures attempting to discard.
+			}
+		}
+	}
+
+	private void parseShallow(String idStr) throws PackProtocolException {
+		ObjectId id;
+		try {
+			id = ObjectId.fromString(idStr);
+		} catch (InvalidObjectIdException e) {
+			throw new PackProtocolException(e.getMessage(), e);
+		}
+		clientShallowCommits.add(id);
+	}
+
+	/**
+	 * @param in
+	 *            request stream.
+	 * @throws IOException
+	 *             request line cannot be read.
+	 */
+	void readPostCommands(PacketLineIn in) throws IOException {
+		if (usePushOptions) {
+			pushOptions = new ArrayList<>(4);
+			for (;;) {
+				String option = in.readString();
+				if (PacketLineIn.isEnd(option)) {
+					break;
+				}
+				pushOptions.add(option);
+			}
+		}
+	}
+
+	/**
+	 * Enable capabilities based on a previously read capabilities line.
+	 */
+	private void enableCapabilities() {
+		reportStatus = isCapabilityEnabled(CAPABILITY_REPORT_STATUS);
+		usePushOptions = isCapabilityEnabled(CAPABILITY_PUSH_OPTIONS);
+		sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
+		quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
+		if (sideBand) {
+			OutputStream out = rawOut;
+
+			rawOut = new SideBandOutputStream(CH_DATA, MAX_BUF, out);
+			msgOut = new SideBandOutputStream(CH_PROGRESS, MAX_BUF, out);
+			errOut = new SideBandOutputStream(CH_ERROR, MAX_BUF, out);
+
+			pckOut = new PacketLineOut(rawOut);
+			pckOut.setFlushOnEnd(false);
+		}
+	}
+
+	/**
+	 * Check if the peer requested a capability.
+	 *
+	 * @param name
+	 *            protocol name identifying the capability.
+	 * @return true if the peer requested the capability to be enabled.
+	 */
+	private boolean isCapabilityEnabled(String name) {
+		return enabledCapabilities.contains(name);
+	}
+
+	private void checkRequestWasRead() {
+		if (enabledCapabilities == null)
+			throw new RequestNotYetReadException();
+	}
+
+	/**
+	 * Whether a pack is expected based on the list of commands.
+	 *
+	 * @return {@code true} if a pack is expected based on the list of commands.
+	 */
+	private boolean needPack() {
+		for (ReceiveCommand cmd : commands) {
+			if (cmd.getType() != ReceiveCommand.Type.DELETE)
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Receive a pack from the input and store it in the repository.
+	 *
+	 * @throws IOException
+	 *             an error occurred reading or indexing the pack.
+	 */
+	private void receivePack() throws IOException {
+		// It might take the client a while to pack the objects it needs
+		// to send to us. We should increase our timeout so we don't
+		// abort while the client is computing.
+		//
+		if (timeoutIn != null)
+			timeoutIn.setTimeout(10 * timeout * 1000);
+
+		ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
+		ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
+		if (sideBand && !quiet)
+			resolving = new SideBandProgressMonitor(msgOut);
+
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			String lockMsg = "jgit receive-pack"; //$NON-NLS-1$
+			if (getRefLogIdent() != null)
+				lockMsg += " from " + getRefLogIdent().toExternalString(); //$NON-NLS-1$
+
+			parser = ins.newPackParser(packInputStream());
+			parser.setAllowThin(true);
+			parser.setNeedNewObjectIds(checkReferencedIsReachable);
+			parser.setNeedBaseObjectIds(checkReferencedIsReachable);
+			parser.setCheckEofAfterPackFooter(
+					!biDirectionalPipe && !isExpectDataAfterPackFooter());
+			parser.setExpectDataAfterPackFooter(isExpectDataAfterPackFooter());
+			parser.setObjectChecker(objectChecker);
+			parser.setLockMessage(lockMsg);
+			parser.setMaxObjectSizeLimit(maxObjectSizeLimit);
+			packLock = parser.parse(receiving, resolving);
+			packSize = Long.valueOf(parser.getPackSize());
+			stats = parser.getReceivedPackStatistics();
+			ins.flush();
+		}
+
+		if (timeoutIn != null)
+			timeoutIn.setTimeout(timeout * 1000);
+	}
+
+	private InputStream packInputStream() {
+		InputStream packIn = rawIn;
+		if (maxPackSizeLimit >= 0) {
+			packIn = new LimitedInputStream(packIn, maxPackSizeLimit) {
+				@Override
+				protected void limitExceeded() throws TooLargePackException {
+					throw new TooLargePackException(limit);
+				}
+			};
+		}
+		return packIn;
+	}
+
+	private boolean needCheckConnectivity() {
+		return isCheckReceivedObjects()
+				|| isCheckReferencedObjectsAreReachable()
+				|| !getClientShallowCommits().isEmpty();
+	}
+
+	private void checkSubmodules() throws IOException {
+		ObjectDatabase odb = db.getObjectDatabase();
+		if (objectChecker == null) {
+			return;
+		}
+		for (GitmoduleEntry entry : objectChecker.getGitsubmodules()) {
+			AnyObjectId blobId = entry.getBlobId();
+			ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB);
+
+			try {
+				SubmoduleValidator.assertValidGitModulesFile(
+						new String(blob.getBytes(), UTF_8));
+			} catch (LargeObjectException | SubmoduleValidationException e) {
+				throw new IOException(e);
+			}
+		}
+	}
+
+	private void checkConnectivity() throws IOException {
+		ObjectIdSubclassMap<ObjectId> baseObjects = null;
+		ObjectIdSubclassMap<ObjectId> providedObjects = null;
+		ProgressMonitor checking = NullProgressMonitor.INSTANCE;
+		if (sideBand && !quiet) {
+			SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut);
+			m.setDelayStart(750, TimeUnit.MILLISECONDS);
+			checking = m;
+		}
+
+		if (checkReferencedIsReachable) {
+			baseObjects = parser.getBaseObjectIds();
+			providedObjects = parser.getNewObjectIds();
+		}
+		parser = null;
+
+		try (ObjectWalk ow = new ObjectWalk(db)) {
+			if (baseObjects != null) {
+				ow.sort(RevSort.TOPO);
+				if (!baseObjects.isEmpty())
+					ow.sort(RevSort.BOUNDARY, true);
+			}
+
+			for (ReceiveCommand cmd : commands) {
+				if (cmd.getResult() != Result.NOT_ATTEMPTED)
+					continue;
+				if (cmd.getType() == ReceiveCommand.Type.DELETE)
+					continue;
+				ow.markStart(ow.parseAny(cmd.getNewId()));
+			}
+			for (ObjectId have : advertisedHaves) {
+				RevObject o = ow.parseAny(have);
+				ow.markUninteresting(o);
+
+				if (baseObjects != null && !baseObjects.isEmpty()) {
+					o = ow.peel(o);
+					if (o instanceof RevCommit)
+						o = ((RevCommit) o).getTree();
+					if (o instanceof RevTree)
+						ow.markUninteresting(o);
+				}
+			}
+
+			checking.beginTask(JGitText.get().countingObjects,
+					ProgressMonitor.UNKNOWN);
+			RevCommit c;
+			while ((c = ow.next()) != null) {
+				checking.update(1);
+				if (providedObjects != null //
+						&& !c.has(RevFlag.UNINTERESTING) //
+						&& !providedObjects.contains(c))
+					throw new MissingObjectException(c, Constants.TYPE_COMMIT);
+			}
+
+			RevObject o;
+			while ((o = ow.nextObject()) != null) {
+				checking.update(1);
+				if (o.has(RevFlag.UNINTERESTING))
+					continue;
+
+				if (providedObjects != null) {
+					if (providedObjects.contains(o)) {
+						continue;
+					}
+					throw new MissingObjectException(o, o.getType());
+				}
+
+				if (o instanceof RevBlob && !db.getObjectDatabase().has(o))
+					throw new MissingObjectException(o, Constants.TYPE_BLOB);
+			}
+			checking.endTask();
+
+			if (baseObjects != null) {
+				for (ObjectId id : baseObjects) {
+					o = ow.parseAny(id);
+					if (!o.has(RevFlag.UNINTERESTING))
+						throw new MissingObjectException(o, o.getType());
+				}
+			}
+		}
+	}
+
+	/**
+	 * Validate the command list.
+	 */
+	private void validateCommands() {
+		for (ReceiveCommand cmd : commands) {
+			final Ref ref = cmd.getRef();
+			if (cmd.getResult() != Result.NOT_ATTEMPTED)
+				continue;
+
+			if (cmd.getType() == ReceiveCommand.Type.DELETE) {
+				if (!isAllowDeletes()) {
+					// Deletes are not supported on this repository.
+					cmd.setResult(Result.REJECTED_NODELETE);
+					continue;
+				}
+				if (!isAllowBranchDeletes()
+						&& ref.getName().startsWith(Constants.R_HEADS)) {
+					// Branches cannot be deleted, but other refs can.
+					cmd.setResult(Result.REJECTED_NODELETE);
+					continue;
+				}
+			}
+
+			if (cmd.getType() == ReceiveCommand.Type.CREATE) {
+				if (!isAllowCreates()) {
+					cmd.setResult(Result.REJECTED_NOCREATE);
+					continue;
+				}
+
+				if (ref != null && !isAllowNonFastForwards()) {
+					// Creation over an existing ref is certainly not going
+					// to be a fast-forward update. We can reject it early.
+					//
+					cmd.setResult(Result.REJECTED_NONFASTFORWARD);
+					continue;
+				}
+
+				if (ref != null) {
+					// A well behaved client shouldn't have sent us a
+					// create command for a ref we advertised to it.
+					//
+					cmd.setResult(Result.REJECTED_OTHER_REASON,
+							JGitText.get().refAlreadyExists);
+					continue;
+				}
+			}
+
+			if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null) {
+				ObjectId id = ref.getObjectId();
+				if (id == null) {
+					id = ObjectId.zeroId();
+				}
+				if (!ObjectId.zeroId().equals(cmd.getOldId())
+						&& !id.equals(cmd.getOldId())) {
+					// Delete commands can be sent with the old id matching our
+					// advertised value, *OR* with the old id being 0{40}. Any
+					// other requested old id is invalid.
+					//
+					cmd.setResult(Result.REJECTED_OTHER_REASON,
+							JGitText.get().invalidOldIdSent);
+					continue;
+				}
+			}
+
+			if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
+				if (ref == null) {
+					// The ref must have been advertised in order to be updated.
+					//
+					cmd.setResult(Result.REJECTED_OTHER_REASON,
+							JGitText.get().noSuchRef);
+					continue;
+				}
+				ObjectId id = ref.getObjectId();
+				if (id == null) {
+					// We cannot update unborn branch
+					cmd.setResult(Result.REJECTED_OTHER_REASON,
+							JGitText.get().cannotUpdateUnbornBranch);
+					continue;
+				}
+
+				if (!id.equals(cmd.getOldId())) {
+					// A properly functioning client will send the same
+					// object id we advertised.
+					//
+					cmd.setResult(Result.REJECTED_OTHER_REASON,
+							JGitText.get().invalidOldIdSent);
+					continue;
+				}
+
+				// Is this possibly a non-fast-forward style update?
+				//
+				RevObject oldObj, newObj;
+				try {
+					oldObj = walk.parseAny(cmd.getOldId());
+				} catch (IOException e) {
+					cmd.setResult(Result.REJECTED_MISSING_OBJECT,
+							cmd.getOldId().name());
+					continue;
+				}
+
+				try {
+					newObj = walk.parseAny(cmd.getNewId());
+				} catch (IOException e) {
+					cmd.setResult(Result.REJECTED_MISSING_OBJECT,
+							cmd.getNewId().name());
+					continue;
+				}
+
+				if (oldObj instanceof RevCommit
+						&& newObj instanceof RevCommit) {
+					try {
+						if (walk.isMergedInto((RevCommit) oldObj,
+								(RevCommit) newObj))
+							cmd.setTypeFastForwardUpdate();
+						else
+							cmd.setType(
+									ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
+					} catch (MissingObjectException e) {
+						cmd.setResult(Result.REJECTED_MISSING_OBJECT,
+								e.getMessage());
+					} catch (IOException e) {
+						cmd.setResult(Result.REJECTED_OTHER_REASON);
+					}
+				} else {
+					cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
+				}
+
+				if (cmd.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD
+						&& !isAllowNonFastForwards()) {
+					cmd.setResult(Result.REJECTED_NONFASTFORWARD);
+					continue;
+				}
+			}
+
+			if (!cmd.getRefName().startsWith(Constants.R_REFS)
+					|| !Repository.isValidRefName(cmd.getRefName())) {
+				cmd.setResult(Result.REJECTED_OTHER_REASON,
+						JGitText.get().funnyRefname);
+			}
+		}
+	}
+
+	/**
+	 * Whether any commands have been rejected so far.
+	 *
+	 * @return if any commands have been rejected so far.
+	 */
+	private boolean anyRejects() {
+		for (ReceiveCommand cmd : commands) {
+			if (cmd.getResult() != Result.NOT_ATTEMPTED
+					&& cmd.getResult() != Result.OK)
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Set the result to fail for any command that was not processed yet.
+	 *
+	 */
+	private void failPendingCommands() {
+		ReceiveCommand.abort(commands);
+	}
+
+	/**
+	 * Filter the list of commands according to result.
+	 *
+	 * @param want
+	 *            desired status to filter by.
+	 * @return a copy of the command list containing only those commands with
+	 *         the desired status.
+	 */
+	private List<ReceiveCommand> filterCommands(Result want) {
+		return ReceiveCommand.filter(commands, want);
+	}
+
+	/**
+	 * Execute commands to update references.
+	 */
+	private void executeCommands() {
+		List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
+		if (toApply.isEmpty())
+			return;
+
+		ProgressMonitor updating = NullProgressMonitor.INSTANCE;
+		if (sideBand) {
+			SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
+			pm.setDelayStart(250, TimeUnit.MILLISECONDS);
+			updating = pm;
+		}
+
+		BatchRefUpdate batch = db.getRefDatabase().newBatchUpdate();
+		batch.setAllowNonFastForwards(isAllowNonFastForwards());
+		batch.setAtomic(isAtomic());
+		batch.setRefLogIdent(getRefLogIdent());
+		batch.setRefLogMessage("push", true); //$NON-NLS-1$
+		batch.addCommand(toApply);
+		try {
+			batch.setPushCertificate(getPushCertificate());
+			batch.execute(walk, updating);
+		} catch (IOException err) {
+			for (ReceiveCommand cmd : toApply) {
+				if (cmd.getResult() == Result.NOT_ATTEMPTED)
+					cmd.reject(err);
+			}
+		}
+	}
+
+	/**
+	 * Send a status report.
+	 *
+	 * @param forClient
+	 *            true if this report is for a Git client, false if it is for an
+	 *            end-user.
+	 * @param unpackError
+	 *            an error that occurred during unpacking, or {@code null}
+	 * @param out
+	 *            the reporter for sending the status strings.
+	 * @throws java.io.IOException
+	 *             an error occurred writing the status report.
+	 * @since 5.6
+	 */
+	private void sendStatusReport(final boolean forClient,
+			final Throwable unpackError, final Reporter out)
+			throws IOException {
+		if (unpackError != null) {
+			out.sendString("unpack error " + unpackError.getMessage()); //$NON-NLS-1$
+			if (forClient) {
+				for (ReceiveCommand cmd : commands) {
+					out.sendString("ng " + cmd.getRefName() //$NON-NLS-1$
+							+ " n/a (unpacker error)"); //$NON-NLS-1$
+				}
+			}
+			return;
+		}
+
+		if (forClient)
+			out.sendString("unpack ok"); //$NON-NLS-1$
+		for (ReceiveCommand cmd : commands) {
+			if (cmd.getResult() == Result.OK) {
+				if (forClient)
+					out.sendString("ok " + cmd.getRefName()); //$NON-NLS-1$
+				continue;
+			}
+
+			final StringBuilder r = new StringBuilder();
+			if (forClient)
+				r.append("ng ").append(cmd.getRefName()).append(" "); //$NON-NLS-1$ //$NON-NLS-2$
+			else
+				r.append(" ! [rejected] ").append(cmd.getRefName()) //$NON-NLS-1$
+						.append(" ("); //$NON-NLS-1$
+
+			switch (cmd.getResult()) {
+			case NOT_ATTEMPTED:
+				r.append("server bug; ref not processed"); //$NON-NLS-1$
+				break;
+
+			case REJECTED_NOCREATE:
+				r.append("creation prohibited"); //$NON-NLS-1$
+				break;
+
+			case REJECTED_NODELETE:
+				r.append("deletion prohibited"); //$NON-NLS-1$
+				break;
+
+			case REJECTED_NONFASTFORWARD:
+				r.append("non-fast forward"); //$NON-NLS-1$
+				break;
+
+			case REJECTED_CURRENT_BRANCH:
+				r.append("branch is currently checked out"); //$NON-NLS-1$
+				break;
+
+			case REJECTED_MISSING_OBJECT:
+				if (cmd.getMessage() == null)
+					r.append("missing object(s)"); //$NON-NLS-1$
+				else if (cmd.getMessage()
+						.length() == Constants.OBJECT_ID_STRING_LENGTH) {
+					r.append("object "); //$NON-NLS-1$
+					r.append(cmd.getMessage());
+					r.append(" missing"); //$NON-NLS-1$
+				} else
+					r.append(cmd.getMessage());
+				break;
+
+			case REJECTED_OTHER_REASON:
+				if (cmd.getMessage() == null)
+					r.append("unspecified reason"); //$NON-NLS-1$
+				else
+					r.append(cmd.getMessage());
+				break;
+
+			case LOCK_FAILURE:
+				r.append("failed to lock"); //$NON-NLS-1$
+				break;
+
+			case OK:
+				// We shouldn't have reached this case (see 'ok' case above).
+				continue;
+			}
+			if (!forClient)
+				r.append(")"); //$NON-NLS-1$
+			out.sendString(r.toString());
+		}
+	}
+
+	/**
+	 * Close and flush (if necessary) the underlying streams.
+	 *
+	 * @throws java.io.IOException
+	 */
+	private void close() throws IOException {
+		if (sideBand) {
+			// If we are using side band, we need to send a final
+			// flush-pkt to tell the remote peer the side band is
+			// complete and it should stop decoding. We need to
+			// use the original output stream as rawOut is now the
+			// side band data channel.
+			//
+			((SideBandOutputStream) msgOut).flushBuffer();
+			((SideBandOutputStream) rawOut).flushBuffer();
+
+			PacketLineOut plo = new PacketLineOut(origOut);
+			plo.setFlushOnEnd(false);
+			plo.end();
+		}
+
+		if (biDirectionalPipe) {
+			// If this was a native git connection, flush the pipe for
+			// the caller. For smart HTTP we don't do this flush and
+			// instead let the higher level HTTP servlet code do it.
+			//
+			if (!sideBand && msgOut != null)
+				msgOut.flush();
+			rawOut.flush();
+		}
+	}
+
+	/**
+	 * Release any resources used by this object.
+	 *
+	 * @throws java.io.IOException
+	 *             the pack could not be unlocked.
+	 */
+	private void release() throws IOException {
+		walk.close();
+		unlockPack();
+		timeoutIn = null;
+		rawIn = null;
+		rawOut = null;
+		msgOut = null;
+		pckIn = null;
+		pckOut = null;
+		refs = null;
+		// Keep the capabilities. If responses are sent after this release
+		// we need to remember at least whether sideband communication has to be
+		// used
+		commands = null;
+		if (timer != null) {
+			try {
+				timer.terminate();
+			} finally {
+				timer = null;
+			}
+		}
+	}
+
+	/** Interface for reporting status messages. */
+	abstract static class Reporter {
+		abstract void sendString(String s) throws IOException;
+	}
+
+	/**
 	 * Get the push certificate used to verify the pusher's identity.
 	 * <p>
 	 * Only valid after commands are read from the wire.
@@ -178,7 +2016,6 @@
 	 *         or no cert was presented by the client.
 	 * @since 4.1
 	 */
-	@Override
 	public PushCertificate getPushCertificate() {
 		return pushCert;
 	}
@@ -193,7 +2030,6 @@
 	 *            the push certificate to set.
 	 * @since 4.1
 	 */
-	@Override
 	public void setPushCertificate(PushCertificate cert) {
 		pushCert = cert;
 	}
@@ -334,28 +2170,6 @@
 		}
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	protected void enableCapabilities() {
-		reportStatus = isCapabilityEnabled(CAPABILITY_REPORT_STATUS);
-		usePushOptions = isCapabilityEnabled(CAPABILITY_PUSH_OPTIONS);
-		super.enableCapabilities();
-	}
-
-	@Override
-	void readPostCommands(PacketLineIn in) throws IOException {
-		if (usePushOptions) {
-			pushOptions = new ArrayList<>(4);
-			for (;;) {
-				String option = in.readString();
-				if (PacketLineIn.isEnd(option)) {
-					break;
-				}
-				pushOptions.add(option);
-			}
-		}
-	}
-
 	private void service() throws IOException {
 		if (isBiDirectionalPipe()) {
 			sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
@@ -377,8 +2191,7 @@
 
 			try {
 				if (unpackError == null) {
-					boolean atomic = isCapabilityEnabled(CAPABILITY_ATOMIC);
-					setAtomic(atomic);
+					setAtomic(isCapabilityEnabled(CAPABILITY_ATOMIC));
 
 					validateCommands();
 					if (atomic && anyRejects()) {
@@ -437,9 +2250,27 @@
 		repo.autoGC(NullProgressMonitor.INSTANCE);
 	}
 
-	/** {@inheritDoc} */
-	@Override
-	protected String getLockMessageProcessName() {
-		return "jgit receive-pack"; //$NON-NLS-1$
+	static ReceiveCommand parseCommand(String line)
+			throws PackProtocolException {
+		if (line == null || line.length() < 83) {
+			throw new PackProtocolException(
+					JGitText.get().errorInvalidProtocolWantedOldNewRef);
+		}
+		String oldStr = line.substring(0, 40);
+		String newStr = line.substring(41, 81);
+		ObjectId oldId, newId;
+		try {
+			oldId = ObjectId.fromString(oldStr);
+			newId = ObjectId.fromString(newStr);
+		} catch (InvalidObjectIdException e) {
+			throw new PackProtocolException(
+					JGitText.get().errorInvalidProtocolWantedOldNewRef, e);
+		}
+		String name = line.substring(82);
+		if (!Repository.isValidRefName(name)) {
+			throw new PackProtocolException(
+					JGitText.get().errorInvalidProtocolWantedOldNewRef);
+		}
+		return new ReceiveCommand(oldId, newId, name);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
index 0a621f1..4474d1a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
@@ -292,22 +292,26 @@
 
 	private String replaceUri(final String uri,
 			final Map<String, String> replacements) {
-		if (replacements.isEmpty())
+		if (replacements.isEmpty()) {
 			return uri;
+		}
 		Entry<String, String> match = null;
 		for (Entry<String, String> replacement : replacements.entrySet()) {
 			// Ignore current entry if not longer than previous match
 			if (match != null
-					&& match.getKey().length() > replacement.getKey().length())
+					&& match.getKey().length() > replacement.getKey()
+							.length()) {
 				continue;
-			if (!uri.startsWith(replacement.getKey()))
+			}
+			if (!uri.startsWith(replacement.getKey())) {
 				continue;
+			}
 			match = replacement;
 		}
-		if (match != null)
+		if (match != null) {
 			return match.getValue() + uri.substring(match.getKey().length());
-		else
-			return uri;
+		}
+		return uri;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index c34e3b8..faa2c76 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
@@ -72,7 +72,7 @@
 	/**
 	 * Represent current status of a remote ref update.
 	 */
-	public static enum Status {
+	public enum Status {
 		/**
 		 * Push process hasn't yet attempted to update this ref. This is the
 		 * default status, prior to push process execution.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index 758d74c..100b433 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -132,6 +132,7 @@
 	private final boolean allowReachableSha1InWant;
 	private final boolean allowFilter;
 	private final boolean allowSidebandAll;
+	private final boolean advertiseSidebandAll;
 	final @Nullable ProtocolVersion protocolVersion;
 	final String[] hideRefs;
 
@@ -213,6 +214,8 @@
 		hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
 		allowSidebandAll = rc.getBoolean(
 				"uploadpack", "allowsidebandall", false);
+		advertiseSidebandAll = rc.getBoolean("uploadpack",
+				"advertisesidebandall", false);
 	}
 
 	/**
@@ -295,7 +298,8 @@
 	}
 
 	/**
-	 * @return true if clients are allowed to specify a "sideband-all" line
+	 * @return true if the server accepts sideband-all requests (see
+	 *         {{@link #isAdvertiseSidebandAll()} for the advertisement)
 	 * @since 5.5
 	 */
 	public boolean isAllowSidebandAll() {
@@ -303,6 +307,14 @@
 	}
 
 	/**
+	 * @return true to advertise sideband all to the clients
+	 * @since 5.6
+	 */
+	public boolean isAdvertiseSidebandAll() {
+		return advertiseSidebandAll && allowSidebandAll;
+	}
+
+	/**
 	 * Get {@link org.eclipse.jgit.transport.RefFilter} respecting configured
 	 * hidden refs.
 	 *
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 0b79070..2382f4c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -302,11 +302,12 @@
 			URISyntaxException, TransportException {
 		if (local != null) {
 			final RemoteConfig cfg = new RemoteConfig(local.getConfig(), remote);
-			if (doesNotExist(cfg))
+			if (doesNotExist(cfg)) {
 				return open(local, new URIish(remote), null);
+			}
 			return open(local, cfg, op);
-		} else
-			return open(new URIish(remote));
+		}
+		return open(new URIish(remote));
 
 	}
 
@@ -708,11 +709,11 @@
 		// try to find matching tracking refs
 		for (RefSpec fetchSpec : fetchSpecs) {
 			if (fetchSpec.matchSource(remoteName)) {
-				if (fetchSpec.isWildcard())
+				if (fetchSpec.isWildcard()) {
 					return fetchSpec.expandFromSource(remoteName)
 							.getDestination();
-				else
-					return fetchSpec.getDestination();
+				}
+				return fetchSpec.getDestination();
 			}
 		}
 		return null;
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 3029327..2637a02 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -698,9 +698,8 @@
 		public CredentialItem[] items() {
 			if (forRepo == null) {
 				return new CredentialItem[] { message, now, always };
-			} else {
-				return new CredentialItem[] { message, now, forRepo, always };
 			}
+			return new CredentialItem[] { message, now, forRepo, always };
 		}
 	}
 
@@ -1076,13 +1075,11 @@
 		host = host.toLowerCase(Locale.ROOT);
 		if (host.equals(cookieDomain)) {
 			return true;
-		} else {
-			if (!host.endsWith(cookieDomain)) {
-				return false;
-			}
-			return host
-					.charAt(host.length() - cookieDomain.length() - 1) == '.';
 		}
+		if (!host.endsWith(cookieDomain)) {
+			return false;
+		}
+		return host.charAt(host.length() - cookieDomain.length() - 1) == '.';
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportProtocol.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportProtocol.java
index de58aa9..5a8cd32 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportProtocol.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportProtocol.java
@@ -90,7 +90,7 @@
  */
 public abstract class TransportProtocol {
 	/** Fields within a {@link URIish} that a transport uses. */
-	public static enum URIishField {
+	public enum URIishField {
 		/** the user field */
 		USER,
 		/** the pass (aka password) field */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 7ca9cc1..d5e03eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -351,10 +351,7 @@
 	}
 
 	private String n2e(String s) {
-		if (s == null)
-			return ""; //$NON-NLS-1$
-		else
-			return s;
+		return s == null ? "" : s; //$NON-NLS-1$
 	}
 
 	// takes care to cut of a leading slash if a windows drive letter or a
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 20b4588..8a88e17 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -44,6 +44,7 @@
 package org.eclipse.jgit.transport;
 
 import static java.util.Collections.unmodifiableMap;
+import static java.util.Objects.requireNonNull;
 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;
@@ -84,7 +85,9 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
@@ -134,7 +137,7 @@
  */
 public class UploadPack {
 	/** Policy the server uses to validate client requests */
-	public static enum RequestPolicy {
+	public enum RequestPolicy {
 		/** Client may only ask for objects the server advertised a reference for. */
 		ADVERTISED,
 
@@ -282,6 +285,8 @@
 
 	private OutputStream msgOut = NullOutputStream.INSTANCE;
 
+	private ErrorWriter errOut = new PackProtocolErrorWriter();
+
 	/**
 	 * Refs eligible for advertising to the client, set using
 	 * {@link #setAdvertisedRefs}.
@@ -756,9 +761,59 @@
 	/**
 	 * Execute the upload task on the socket.
 	 *
-	 * <p>If the client passed extra parameters (e.g., "version=2") through a
-	 * side channel, the caller must call setExtraParameters first to supply
-	 * them.
+	 * <p>
+	 * Same as {@link #uploadWithExceptionPropagation} except that the thrown
+	 * exceptions are handled in the method, and the error messages are sent to
+	 * the clients.
+	 *
+	 * <p>
+	 * Call this method if the caller does not have an error handling mechanism.
+	 * Call {@link #uploadWithExceptionPropagation} if the caller wants to have
+	 * its own error handling mechanism.
+	 *
+	 * @param input
+	 * @param output
+	 * @param messages
+	 * @throws java.io.IOException
+	 */
+	public void upload(InputStream input, OutputStream output,
+			@Nullable OutputStream messages) throws IOException {
+		try {
+			uploadWithExceptionPropagation(input, output, messages);
+		} catch (ServiceMayNotContinueException err) {
+			if (!err.isOutput() && err.getMessage() != null) {
+				try {
+					errOut.writeError(err.getMessage());
+				} catch (IOException e) {
+					err.addSuppressed(e);
+					throw err;
+				}
+				err.setOutput();
+			}
+			throw err;
+		} catch (IOException | RuntimeException | Error err) {
+			if (rawOut != null) {
+				String msg = err instanceof PackProtocolException
+						? err.getMessage()
+						: JGitText.get().internalServerError;
+				try {
+					errOut.writeError(msg);
+				} catch (IOException e) {
+					err.addSuppressed(e);
+					throw err;
+				}
+				throw new UploadPackInternalServerErrorException(err);
+			}
+			throw err;
+		}
+	}
+
+	/**
+	 * Execute the upload task on the socket.
+	 *
+	 * <p>
+	 * If the client passed extra parameters (e.g., "version=2") through a side
+	 * channel, the caller must call setExtraParameters first to supply them.
 	 *
 	 * @param input
 	 *            raw input to read client commands from. Caller must ensure the
@@ -772,15 +827,21 @@
 	 *            through. When run over SSH this should be tied back to the
 	 *            standard error channel of the command execution. For most
 	 *            other network connections this should be null.
-	 * @throws java.io.IOException
+	 * @throws ServiceMayNotContinueException
+	 *             thrown if one of the hooks throws this.
+	 * @throws IOException
+	 *             thrown if the server or the client I/O fails, or there's an
+	 *             internal server error.
+	 * @since 5.6
 	 */
-	public void upload(InputStream input, OutputStream output,
-			@Nullable OutputStream messages) throws IOException {
-		PacketLineOut pckOut = null;
+	public void uploadWithExceptionPropagation(InputStream input,
+			OutputStream output, @Nullable OutputStream messages)
+			throws ServiceMayNotContinueException, IOException {
 		try {
 			rawIn = input;
-			if (messages != null)
+			if (messages != null) {
 				msgOut = messages;
+			}
 
 			if (timeout > 0) {
 				final Thread caller = Thread.currentThread();
@@ -800,42 +861,12 @@
 			}
 
 			pckIn = new PacketLineIn(rawIn);
-			pckOut = new PacketLineOut(rawOut);
+			PacketLineOut pckOut = new PacketLineOut(rawOut);
 			if (useProtocolV2()) {
 				serviceV2(pckOut);
 			} else {
 				service(pckOut);
 			}
-		} catch (UploadPackInternalServerErrorException err) {
-			// UploadPackInternalServerErrorException is a special exception
-			// that indicates an error is already written to the client. Do
-			// nothing.
-			throw err;
-		} catch (ServiceMayNotContinueException err) {
-			if (!err.isOutput() && err.getMessage() != null && pckOut != null) {
-				try {
-					pckOut.writeString("ERR " + err.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
-				} catch (IOException e) {
-					err.addSuppressed(e);
-					throw err;
-				}
-				err.setOutput();
-			}
-			throw err;
-		} catch (IOException | RuntimeException | Error err) {
-			if (pckOut != null) {
-				String msg = err instanceof PackProtocolException
-						? err.getMessage()
-						: JGitText.get().internalServerError;
-				try {
-					pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
-				} catch (IOException e) {
-					err.addSuppressed(e);
-					throw err;
-				}
-				throw new UploadPackInternalServerErrorException(err);
-			}
-			throw err;
 		} finally {
 			msgOut = NullOutputStream.INSTANCE;
 			walk.close();
@@ -1296,7 +1327,7 @@
 		caps.add(COMMAND_FETCH + '='
 				+ (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "")
 				+ (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "")
-				+ (transferConfig.isAllowSidebandAll()
+				+ (transferConfig.isAdvertiseSidebandAll()
 						? OPTION_SIDEBAND_ALL + ' '
 						: "")
 				+ (cachedPackUriProvider != null ? "packfile-uris " : "")
@@ -1844,8 +1875,7 @@
 		@Override
 		public void checkWants(UploadPack up, List<ObjectId> wants)
 				throws PackProtocolException, IOException {
-			checkNotAdvertisedWants(up, wants,
-					refIdSet(up.getAdvertisedRefs().values()));
+			checkNotAdvertisedWants(up, wants, up.getAdvertisedRefs().values());
 		}
 	}
 
@@ -1882,7 +1912,7 @@
 		public void checkWants(UploadPack up, List<ObjectId> wants)
 				throws PackProtocolException, IOException {
 			checkNotAdvertisedWants(up, wants,
-					refIdSet(up.getRepository().getRefDatabase().getRefs()));
+					up.getRepository().getRefDatabase().getRefs());
 		}
 	}
 
@@ -1942,12 +1972,14 @@
 	}
 
 	private static void checkNotAdvertisedWants(UploadPack up,
-			List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
+			List<ObjectId> notAdvertisedWants, Collection<Ref> visibleRefs)
 			throws IOException {
 
 		ObjectReader reader = up.getRevWalk().getObjectReader();
+
 		try (RevWalk walk = new RevWalk(reader)) {
 			walk.setRetainBody(false);
+			Set<ObjectId> reachableFrom = refIdSet(visibleRefs);
 			// Missing "wants" throw exception here
 			List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk,
 					notAdvertisedWants);
@@ -1994,10 +2026,13 @@
 			ReachabilityChecker reachabilityChecker = walk
 					.createReachabilityChecker();
 
-			List<RevCommit> starters = objectIdsToRevCommits(walk,
-					reachableFrom);
+			Stream<RevCommit> reachableCommits = importantRefsFirst(visibleRefs)
+					.map(UploadPack::refToObjectId)
+					.map(objId -> objectIdToRevCommit(walk, objId))
+					.filter(Objects::nonNull); // Ignore missing tips
+
 			Optional<RevCommit> unreachable = reachabilityChecker
-					.areAllReachable(wantsAsCommits, starters);
+					.areAllReachable(wantsAsCommits, reachableCommits);
 			if (unreachable.isPresent()) {
 				throw new WantNotValidException(unreachable.get());
 			}
@@ -2007,6 +2042,50 @@
 		}
 	}
 
+	static Stream<Ref> importantRefsFirst(
+			Collection<Ref> visibleRefs) {
+		Predicate<Ref> startsWithRefsHeads = ref -> ref.getName()
+				.startsWith(Constants.R_HEADS);
+		Predicate<Ref> startsWithRefsTags = ref -> ref.getName()
+				.startsWith(Constants.R_TAGS);
+		Predicate<Ref> allOther = ref -> !startsWithRefsHeads.test(ref)
+				&& !startsWithRefsTags.test(ref);
+
+		return Stream.concat(
+				visibleRefs.stream().filter(startsWithRefsHeads),
+				Stream.concat(
+						visibleRefs.stream().filter(startsWithRefsTags),
+						visibleRefs.stream().filter(allOther)));
+	}
+
+	private static ObjectId refToObjectId(Ref ref) {
+		return ref.getObjectId() != null ? ref.getObjectId()
+				: ref.getPeeledObjectId();
+	}
+
+	/**
+	 * Translate an object id to a RevCommit.
+	 *
+	 * @param walk
+	 *            walk on the relevant object storae
+	 * @param objectId
+	 *            Object Id
+	 * @return RevCommit instance or null if the object is missing
+	 */
+	@Nullable
+	private static RevCommit objectIdToRevCommit(RevWalk walk,
+			ObjectId objectId) {
+		if (objectId == null) {
+			return null;
+		}
+
+		try {
+			return walk.parseCommit(objectId);
+		} catch (IOException e) {
+			return null;
+		}
+	}
+
 	// Resolve the ObjectIds into RevObjects. Any missing object raises an
 	// exception
 	private static List<RevObject> objectIdsToRevObjects(RevWalk walk,
@@ -2019,23 +2098,6 @@
 		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);
@@ -2114,54 +2176,42 @@
 		Set<String> caps = req.getClientCapabilities();
 		boolean sideband = caps.contains(OPTION_SIDE_BAND)
 				|| caps.contains(OPTION_SIDE_BAND_64K);
-		if (sideband) {
-			try {
-				sendPack(true, req, accumulator, allTags, unshallowCommits,
-						deepenNots, pckOut);
-			} catch (ServiceMayNotContinueException err) {
-				String message = err.getMessage();
-				if (message == null) {
-					message = JGitText.get().internalServerError;
-				}
-				try {
-					reportInternalServerErrorOverSideband(message);
-				} catch (IOException e) {
-					err.addSuppressed(e);
-					throw err;
-				}
-				throw new UploadPackInternalServerErrorException(err);
-			} catch (IOException | RuntimeException | Error err) {
-				try {
-					reportInternalServerErrorOverSideband(
-							JGitText.get().internalServerError);
-				} catch (IOException e) {
-					err.addSuppressed(e);
-					throw err;
-				}
-				throw new UploadPackInternalServerErrorException(err);
-			}
-		} else {
-			sendPack(false, req, accumulator, allTags, unshallowCommits, deepenNots,
-					pckOut);
-		}
-	}
 
-	private void reportInternalServerErrorOverSideband(String message)
-			throws IOException {
-		@SuppressWarnings("resource" /* java 7 */)
-		SideBandOutputStream err = new SideBandOutputStream(
-				SideBandOutputStream.CH_ERROR, SideBandOutputStream.SMALL_BUF,
-				rawOut);
-		err.write(Constants.encode(message));
-		err.flush();
+		if (sideband) {
+			errOut = new SideBandErrorWriter();
+
+			int bufsz = SideBandOutputStream.SMALL_BUF;
+			if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K)) {
+				bufsz = SideBandOutputStream.MAX_BUF;
+			}
+			OutputStream packOut = new SideBandOutputStream(
+					SideBandOutputStream.CH_DATA, bufsz, rawOut);
+
+			ProgressMonitor pm = NullProgressMonitor.INSTANCE;
+			if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) {
+				msgOut = new SideBandOutputStream(
+						SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
+				pm = new SideBandProgressMonitor(msgOut);
+			}
+
+			sendPack(pm, pckOut, packOut, req, accumulator, allTags,
+					unshallowCommits, deepenNots);
+			pckOut.end();
+		} else {
+			sendPack(NullProgressMonitor.INSTANCE, pckOut, rawOut, req,
+					accumulator, allTags, unshallowCommits, deepenNots);
+		}
 	}
 
 	/**
 	 * Send the requested objects to the client.
 	 *
-	 * @param sideband
-	 *            whether to wrap the pack in side-band pkt-lines, interleaved
-	 *            with progress messages and errors.
+	 * @param pm
+	 *            progress monitor
+	 * @param pckOut
+	 *            PacketLineOut that shares the output with packOut
+	 * @param packOut
+	 *            packfile output
 	 * @param req
 	 *            request being processed
 	 * @param accumulator
@@ -2173,35 +2223,14 @@
 	 *            shallow commits on the client that are now becoming unshallow
 	 * @param deepenNots
 	 *            objects that the client specified using --shallow-exclude
-	 * @param pckOut
-	 *            output writer
 	 * @throws IOException
 	 *             if an error occurred while generating or writing the pack.
 	 */
-	private void sendPack(final boolean sideband,
-			FetchRequest req,
+	private void sendPack(ProgressMonitor pm, PacketLineOut pckOut,
+			OutputStream packOut, FetchRequest req,
 			PackStatistics.Accumulator accumulator,
-			@Nullable Collection<Ref> allTags,
-			List<ObjectId> unshallowCommits,
-			List<ObjectId> deepenNots,
-			PacketLineOut pckOut) throws IOException {
-		ProgressMonitor pm = NullProgressMonitor.INSTANCE;
-		OutputStream packOut = rawOut;
-
-		if (sideband) {
-			int bufsz = SideBandOutputStream.SMALL_BUF;
-			if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K))
-				bufsz = SideBandOutputStream.MAX_BUF;
-
-			packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
-					bufsz, rawOut);
-			if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) {
-				msgOut = new SideBandOutputStream(
-						SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
-				pm = new SideBandProgressMonitor(msgOut);
-			}
-		}
-
+			@Nullable Collection<Ref> allTags, List<ObjectId> unshallowCommits,
+			List<ObjectId> deepenNots) throws IOException {
 		if (wantAll.isEmpty()) {
 			preUploadHook.onSendPack(this, wantIds, commonBase);
 		} else {
@@ -2344,9 +2373,6 @@
 			}
 			pw.close();
 		}
-
-		if (sideband)
-			pckOut.end();
 	}
 
 	private static void findSymrefs(
@@ -2385,12 +2411,12 @@
 		}
 
 		@Override
-		public void write(byte b[]) throws IOException {
+		public void write(byte[] b) throws IOException {
 			out.write(b);
 		}
 
 		@Override
-		public void write(byte b[], int off, int len) throws IOException {
+		public void write(byte[] b, int off, int len) throws IOException {
 			out.write(b, off, len);
 		}
 
@@ -2411,4 +2437,28 @@
 			}
 		}
 	}
+
+	private interface ErrorWriter {
+		void writeError(String message) throws IOException;
+	}
+
+	private class SideBandErrorWriter implements ErrorWriter {
+		@Override
+		public void writeError(String message) throws IOException {
+			@SuppressWarnings("resource" /* java 7 */)
+			SideBandOutputStream err = new SideBandOutputStream(
+					SideBandOutputStream.CH_ERROR,
+					SideBandOutputStream.SMALL_BUF, requireNonNull(rawOut));
+			err.write(Constants.encode(message));
+			err.flush();
+		}
+	}
+
+	private class PackProtocolErrorWriter implements ErrorWriter {
+		@Override
+		public void writeError(String message) throws IOException {
+			new PacketLineOut(requireNonNull(rawOut))
+					.writeString("ERR " + message + '\n'); //$NON-NLS-1$
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
index 7a973af..fb8a55f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
@@ -324,7 +324,7 @@
 	 * Base implementation of JGit symmetric encryption. Supports V2 properties
 	 * format.
 	 */
-	static abstract class SymmetricEncryption extends WalkEncryption
+	abstract static class SymmetricEncryption extends WalkEncryption
 			implements Keys, Vals {
 
 		/** Encryption profile, root name of group of related properties. */
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 b4a7af0..9a08c08 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
@@ -124,8 +124,8 @@
 					// are responsible for closing the repository if we
 					// complete successfully.
 					return db;
-				} else
-					throw new ServiceNotEnabledException();
+				}
+				throw new ServiceNotEnabledException();
 
 			} catch (RuntimeException | IOException e) {
 				db.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 4f3eb05..5f5da58 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -275,7 +275,7 @@
 		/**
 		 * a singleton instance of the default FileModeStrategy
 		 */
-		public final static DefaultFileModeStrategy INSTANCE =
+		public static final DefaultFileModeStrategy INSTANCE =
 				new DefaultFileModeStrategy();
 
 		@Override
@@ -285,9 +285,8 @@
 			} else if (attributes.isDirectory()) {
 				if (new File(f, Constants.DOT_GIT).exists()) {
 					return FileMode.GITLINK;
-				} else {
-					return FileMode.TREE;
 				}
+				return FileMode.TREE;
 			} else if (attributes.isExecutable()) {
 				return FileMode.EXECUTABLE_FILE;
 			} else {
@@ -309,7 +308,7 @@
 		/**
 		 * a singleton instance of the default FileModeStrategy
 		 */
-		public final static NoGitlinksStrategy INSTANCE = new NoGitlinksStrategy();
+		public static final NoGitlinksStrategy INSTANCE = new NoGitlinksStrategy();
 
 		@Override
 		public FileMode getMode(File f, FS.Attributes attributes) {
@@ -425,9 +424,8 @@
 			if (attributes.isSymbolicLink()) {
 				return new ByteArrayInputStream(fs.readSymLink(getFile())
 						.getBytes(UTF_8));
-			} else {
-				return new FileInputStream(getFile());
 			}
+			return new FileInputStream(getFile());
 		}
 
 		/**
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 65d8512..e545565 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -111,7 +111,7 @@
 	/**
 	 * @since 4.2
 	 */
-	public static enum OperationType {
+	public enum OperationType {
 		/**
 		 * Represents a checkout operation (for example a checkout or reset
 		 * operation).
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 8bb68dc..35d6e41 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -101,7 +101,6 @@
 import org.eclipse.jgit.util.RawParseUtils;
 import org.eclipse.jgit.util.TemporaryBuffer;
 import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
-import org.eclipse.jgit.util.io.AutoLFInputStream;
 import org.eclipse.jgit.util.io.EolStreamTypeUtil;
 import org.eclipse.jgit.util.sha1.SHA1;
 
@@ -686,10 +685,10 @@
 	public InputStream openEntryStream() throws IOException {
 		InputStream rawis = current().openInputStream();
 		if (getCleanFilterCommand() == null
-				&& getEolStreamType() == EolStreamType.DIRECT)
+				&& getEolStreamType() == EolStreamType.DIRECT) {
 			return rawis;
-		else
-			return filterClean(rawis);
+		}
+		return filterClean(rawis);
 	}
 
 	/**
@@ -980,13 +979,13 @@
 		MetadataDiff diff = compareMetadata(entry);
 		switch (diff) {
 		case DIFFER_BY_TIMESTAMP:
-			if (forceContentCheck)
+			if (forceContentCheck) {
 				// But we are told to look at content even though timestamps
 				// tell us about modification
 				return contentCheck(entry, reader);
-			else
-				// We are told to assume a modification if timestamps differs
-				return true;
+			}
+			// We are told to assume a modification if timestamps differs
+			return true;
 		case SMUDGED:
 			// The file is clean by timestamps but the entry was smudged.
 			// Lets do a content check
@@ -1087,47 +1086,13 @@
 			entry.setLength((int) getEntryLength());
 
 			return false;
-		} else {
-			if (mode == FileMode.SYMLINK.getBits()) {
-				return !new File(readSymlinkTarget(current())).equals(
-						new File(readContentAsNormalizedString(entry, reader)));
-			}
-			// Content differs: that's a real change, perhaps
-			if (reader == null) // deprecated use, do no further checks
-				return true;
-
-			switch (getEolStreamType()) {
-			case DIRECT:
-				return true;
-			default:
-				try {
-					ObjectLoader loader = reader.open(entry.getObjectId());
-					if (loader == null)
-						return true;
-
-					// We need to compute the length, but only if it is not
-					// a binary stream.
-					long dcInLen;
-					try (InputStream dcIn = new AutoLFInputStream(
-							loader.openStream(), true,
-							true /* abort if binary */)) {
-						dcInLen = computeLength(dcIn);
-					} catch (AutoLFInputStream.IsBinaryException e) {
-						return true;
-					}
-
-					try (InputStream dcIn = new AutoLFInputStream(
-							loader.openStream(), true)) {
-						byte[] autoCrLfHash = computeHash(dcIn, dcInLen);
-						boolean changed = getEntryObjectId()
-								.compareTo(autoCrLfHash, 0) != 0;
-						return changed;
-					}
-				} catch (IOException e) {
-					return true;
-				}
-			}
 		}
+		if (mode == FileMode.SYMLINK.getBits()) {
+			return !new File(readSymlinkTarget(current())).equals(
+					new File(readContentAsNormalizedString(entry, reader)));
+		}
+		// Content differs: that's a real change
+		return true;
 	}
 
 	private static String readContentAsNormalizedString(DirCacheEntry entry,
@@ -1215,7 +1180,7 @@
 	 *
 	 * @since 5.0
 	 */
-	public static abstract class Entry {
+	public abstract static class Entry {
 		byte[] encodedName;
 
 		int encodedNameLen;
@@ -1535,7 +1500,7 @@
 		}
 		// Read blob from index and check for CR/LF-delimited text.
 		DirCacheEntry entry = dirCache.getDirCacheEntry();
-		if (FileMode.REGULAR_FILE.equals(entry.getFileMode())) {
+		if ((entry.getRawMode() & FileMode.TYPE_MASK) == FileMode.TYPE_FILE) {
 			ObjectId blobId = entry.getObjectId();
 			if (entry.getStage() > 0
 					&& entry.getStage() != DirCacheEntry.STAGE_2) {
@@ -1552,7 +1517,10 @@
 						break;
 					}
 					if (entry.getStage() == DirCacheEntry.STAGE_2) {
-						blobId = entry.getObjectId();
+						if ((entry.getRawMode()
+								& FileMode.TYPE_MASK) == FileMode.TYPE_FILE) {
+							blobId = entry.getObjectId();
+						}
 						break;
 					}
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
index 6cca582..52fb888 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
@@ -212,10 +212,9 @@
 				// If i is cnt then the path does not appear in any other tree,
 				// and this working tree entry can be safely ignored.
 				return i != cnt;
-			} else {
-				// In working tree and not ignored, and not in DirCache.
-				return true;
 			}
+			// In working tree and not ignored, and not in DirCache.
+			return true;
 		}
 
 		// Always include subtrees as WorkingTreeIterator cannot provide
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java
index 3d9f875..11896e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathSuffixFilter.java
@@ -99,10 +99,10 @@
 	@Override
 	public boolean include(TreeWalk walker) throws MissingObjectException,
 			IncorrectObjectTypeException, IOException {
-		if (walker.isSubtree())
+		if (walker.isSubtree()) {
 			return true;
-		else
-			return walker.isPathSuffix(pathRaw, pathRaw.length);
+		}
+		return walker.isPathSuffix(pathRaw, pathRaw.length);
 
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java
index 69f8547..119c96e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java
@@ -28,26 +28,26 @@
  */
 public class Base64 {
 	/** The equals sign (=) as a byte. */
-	private final static byte EQUALS_SIGN = (byte) '=';
+	private static final byte EQUALS_SIGN = (byte) '=';
 
 	/** Indicates equals sign in encoding. */
-	private final static byte EQUALS_SIGN_DEC = -1;
+	private static final byte EQUALS_SIGN_DEC = -1;
 
 	/** Indicates white space in encoding. */
-	private final static byte WHITE_SPACE_DEC = -2;
+	private static final byte WHITE_SPACE_DEC = -2;
 
 	/** Indicates an invalid byte during decoding. */
-	private final static byte INVALID_DEC = -3;
+	private static final byte INVALID_DEC = -3;
 
 	/** The 64 valid Base64 values. */
-	private final static byte[] ENC;
+	private static final byte[] ENC;
 
 	/**
 	 * Translates a Base64 value to either its 6-bit reconstruction value or a
 	 * negative number indicating some other meaning. The table is only 7 bits
 	 * wide, as the 8th bit is discarded during decoding.
 	 */
-	private final static byte[] DEC;
+	private static final byte[] DEC;
 
 	static {
 		ENC = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" // //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
index c8e6645..957cb5a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
@@ -173,20 +173,19 @@
 			boolean replaceExisting) {
 		int indexOfChangeId = indexOfChangeId(message, "\n"); //$NON-NLS-1$
 		if (indexOfChangeId > 0) {
-			if (!replaceExisting)
+			if (!replaceExisting) {
 				return message;
-			else {
-				StringBuilder ret = new StringBuilder(message.substring(0,
-						indexOfChangeId));
-				ret.append(CHANGE_ID);
-				ret.append(" I"); //$NON-NLS-1$
-				ret.append(ObjectId.toString(changeId));
-				int indexOfNextLineBreak = message.indexOf("\n", //$NON-NLS-1$
-						indexOfChangeId);
-				if (indexOfNextLineBreak > 0)
-					ret.append(message.substring(indexOfNextLineBreak));
-				return ret.toString();
 			}
+			StringBuilder ret = new StringBuilder(
+					message.substring(0, indexOfChangeId));
+			ret.append(CHANGE_ID);
+			ret.append(" I"); //$NON-NLS-1$
+			ret.append(ObjectId.toString(changeId));
+			int indexOfNextLineBreak = message.indexOf('\n',
+					indexOfChangeId);
+			if (indexOfNextLineBreak > 0)
+				ret.append(message.substring(indexOfNextLineBreak));
+			return ret.toString();
 		}
 
 		String[] lines = message.split("\n"); //$NON-NLS-1$
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 0aa0e36..41bfde9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -61,6 +61,7 @@
 import java.nio.file.AccessDeniedException;
 import java.nio.file.FileStore;
 import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileTime;
@@ -98,6 +99,7 @@
 import org.eclipse.jgit.errors.LockFailedException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.file.FileSnapshot;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
@@ -147,15 +149,15 @@
 		 */
 		public FS detect(Boolean cygwinUsed) {
 			if (SystemReader.getInstance().isWindows()) {
-				if (cygwinUsed == null)
+				if (cygwinUsed == null) {
 					cygwinUsed = Boolean.valueOf(FS_Win32_Cygwin.isCygwin());
-				if (cygwinUsed.booleanValue())
+				}
+				if (cygwinUsed.booleanValue()) {
 					return new FS_Win32_Cygwin();
-				else
-					return new FS_Win32();
-			} else {
-				return new FS_POSIX();
+				}
+				return new FS_Win32();
 			}
+			return new FS_POSIX();
 		}
 	}
 
@@ -211,7 +213,7 @@
 	 *
 	 * @since 5.1.9
 	 */
-	public final static class FileStoreAttributes {
+	public static final class FileStoreAttributes {
 
 		private static final Duration UNDEFINED_DURATION = Duration
 				.ofNanos(Long.MAX_VALUE);
@@ -240,13 +242,21 @@
 
 		private static Map<FileStore, Lock> locks = new ConcurrentHashMap<>();
 
-		private static void setBackground(boolean async) {
+		/**
+		 * Whether FileStore attributes should be determined asynchronously
+		 *
+		 * @param async
+		 *            whether FileStore attributes should be determined
+		 *            asynchronously. If false access to cached attributes may block
+		 *            for some seconds for the first call per FileStore
+		 * @since 5.6.2
+		 */
+		public static void setBackground(boolean async) {
 			background.set(async);
 		}
 
-		private static final String javaVersionPrefix = SystemReader
-				.getInstance().getHostname() + '|'
-				+ System.getProperty("java.vendor") + '|' //$NON-NLS-1$
+		private static final String javaVersionPrefix = System
+				.getProperty("java.vendor") + '|' //$NON-NLS-1$
 				+ System.getProperty("java.version") + '|'; //$NON-NLS-1$
 
 		private static final Duration FALLBACK_MIN_RACY_INTERVAL = Duration
@@ -689,7 +699,7 @@
 	/** The auto-detected implementation selected for this operating system and JRE. */
 	public static final FS DETECTED = detect();
 
-	private volatile static FSFactory factory;
+	private static volatile FSFactory factory;
 
 	/**
 	 * Auto-detect the appropriate file system abstraction.
@@ -708,7 +718,9 @@
 	 *            asynchronously. If false access to cached attributes may block
 	 *            for some seconds for the first call per FileStore
 	 * @since 5.1.9
+	 * @deprecated Use {@link FileStoreAttributes#setBackground} instead
 	 */
+	@Deprecated
 	public static void setAsyncFileStoreAttributes(boolean asynch) {
 		FileStoreAttributes.setBackground(asynch);
 	}
@@ -837,7 +849,7 @@
 				try {
 					FileUtils.delete(tempFile);
 				} catch (IOException e) {
-					throw new RuntimeException(e); // panic
+					LOG.error(JGitText.get().cannotDeleteFile, tempFile);
 				}
 			}
 		}
@@ -1192,14 +1204,13 @@
 					gobbler.join();
 					if (rc == 0 && !gobbler.fail.get()) {
 						return r;
-					} else {
-						if (debug) {
-							LOG.debug("readpipe rc=" + rc); //$NON-NLS-1$
-						}
-						throw new CommandFailedException(rc,
-								gobbler.errorMessage.get(),
-								gobbler.exception.get());
 					}
+					if (debug) {
+						LOG.debug("readpipe rc=" + rc); //$NON-NLS-1$
+					}
+					throw new CommandFailedException(rc,
+							gobbler.errorMessage.get(),
+							gobbler.exception.get());
 				} catch (InterruptedException ie) {
 					// Stop bothering me, I have a zombie to reap.
 				}
@@ -1726,20 +1737,18 @@
 			final String hookName, String[] args, PrintStream outRedirect,
 			PrintStream errRedirect, String stdinArgs)
 			throws JGitInternalException {
-		final File hookFile = findHook(repository, hookName);
-		if (hookFile == null)
+		File hookFile = findHook(repository, hookName);
+		if (hookFile == null || hookName == null) {
 			return new ProcessResult(Status.NOT_PRESENT);
+		}
 
-		final String hookPath = hookFile.getAbsolutePath();
-		final File runDirectory;
-		if (repository.isBare())
-			runDirectory = repository.getDirectory();
-		else
-			runDirectory = repository.getWorkTree();
-		final String cmd = relativize(runDirectory.getAbsolutePath(),
-				hookPath);
-		ProcessBuilder hookProcess = runInShell(cmd, args);
-		hookProcess.directory(runDirectory);
+		File runDirectory = getRunDirectory(repository, hookName);
+		if (runDirectory == null) {
+			return new ProcessResult(Status.NOT_PRESENT);
+		}
+		String cmd = hookFile.getAbsolutePath();
+		ProcessBuilder hookProcess = runInShell(shellQuote(cmd), args);
+		hookProcess.directory(runDirectory.getAbsoluteFile());
 		Map<String, String> environment = hookProcess.environment();
 		environment.put(Constants.GIT_DIR_KEY,
 				repository.getDirectory().getAbsolutePath());
@@ -1761,6 +1770,21 @@
 		}
 	}
 
+	/**
+	 * Quote a string (such as a file system path obtained from a Java
+	 * {@link File} or {@link Path} object) such that it can be passed as first
+	 * argument to {@link #runInShell(String, String[])}.
+	 * <p>
+	 * This default implementation returns the string unchanged.
+	 * </p>
+	 *
+	 * @param cmd
+	 *            the String to quote
+	 * @return the quoted string
+	 */
+	String shellQuote(String cmd) {
+		return cmd;
+	}
 
 	/**
 	 * Tries to find a hook matching the given one in the given repository.
@@ -1774,12 +1798,71 @@
 	 * @since 4.0
 	 */
 	public File findHook(Repository repository, String hookName) {
-		File gitDir = repository.getDirectory();
-		if (gitDir == null)
+		if (hookName == null) {
 			return null;
-		final File hookFile = new File(new File(gitDir,
-				Constants.HOOKS), hookName);
-		return hookFile.isFile() ? hookFile : null;
+		}
+		File hookDir = getHooksDirectory(repository);
+		if (hookDir == null) {
+			return null;
+		}
+		File hookFile = new File(hookDir, hookName);
+		if (hookFile.isAbsolute()) {
+			if (!hookFile.exists() || (FS.DETECTED.supportsExecute()
+					&& !FS.DETECTED.canExecute(hookFile))) {
+				return null;
+			}
+		} else {
+			try {
+				File runDirectory = getRunDirectory(repository, hookName);
+				if (runDirectory == null) {
+					return null;
+				}
+				Path hookPath = runDirectory.getAbsoluteFile().toPath()
+						.resolve(hookFile.toPath());
+				FS fs = repository.getFS();
+				if (fs == null) {
+					fs = FS.DETECTED;
+				}
+				if (!Files.exists(hookPath) || (fs.supportsExecute()
+						&& !fs.canExecute(hookPath.toFile()))) {
+					return null;
+				}
+				hookFile = hookPath.toFile();
+			} catch (InvalidPathException e) {
+				LOG.warn(MessageFormat.format(JGitText.get().invalidHooksPath,
+						hookFile));
+				return null;
+			}
+		}
+		return hookFile;
+	}
+
+	private File getRunDirectory(Repository repository,
+			@NonNull String hookName) {
+		if (repository.isBare()) {
+			return repository.getDirectory();
+		}
+		switch (hookName) {
+		case "pre-receive": //$NON-NLS-1$
+		case "update": //$NON-NLS-1$
+		case "post-receive": //$NON-NLS-1$
+		case "post-update": //$NON-NLS-1$
+		case "push-to-checkout": //$NON-NLS-1$
+			return repository.getDirectory();
+		default:
+			return repository.getWorkTree();
+		}
+	}
+
+	private File getHooksDirectory(Repository repository) {
+		Config config = repository.getConfig();
+		String hooksDir = config.getString(ConfigConstants.CONFIG_CORE_SECTION,
+				null, ConfigConstants.CONFIG_KEY_HOOKS_PATH);
+		if (hooksDir != null) {
+			return new File(hooksDir);
+		}
+		File dir = repository.getDirectory();
+		return dir == null ? null : new File(dir, Constants.HOOKS);
 	}
 
 	/**
@@ -2213,7 +2296,7 @@
 
 		void copy() throws IOException {
 			boolean writeFailure = false;
-			byte buffer[] = new byte[4096];
+			byte[] buffer = new byte[4096];
 			int readBytes;
 			while ((readBytes = in.read(buffer)) != -1) {
 				// Do not try to write again after a failure, but keep
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index 6a1eef2..9c8dab6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -74,7 +74,6 @@
 import org.eclipse.jgit.errors.CommandFailedException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.slf4j.Logger;
@@ -86,7 +85,7 @@
  * @since 3.0
  */
 public class FS_POSIX extends FS {
-	private final static Logger LOG = LoggerFactory.getLogger(FS_POSIX.class);
+	private static final Logger LOG = LoggerFactory.getLogger(FS_POSIX.class);
 
 	private static final int DEFAULT_UMASK = 0022;
 	private volatile int umask = -1;
@@ -270,6 +269,11 @@
 		return proc;
 	}
 
+	@Override
+	String shellQuote(String cmd) {
+		return QuotedString.BOURNE.quote(cmd);
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public ProcessResult runHookIfPresent(Repository repository, String hookName,
@@ -311,20 +315,6 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public File findHook(Repository repository, String hookName) {
-		final File gitdir = repository.getDirectory();
-		if (gitdir == null) {
-			return null;
-		}
-		final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
-				.resolve(hookName);
-		if (Files.isExecutable(hookPath))
-			return hookPath.toFile();
-		return null;
-	}
-
-	/** {@inheritDoc} */
-	@Override
 	public boolean supportsAtomicCreateNewFile() {
 		if (supportsAtomicFileCreation == AtomicFileCreation.UNDEFINED) {
 			try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index 1e64a38..aedf43c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -72,7 +72,7 @@
  * @since 3.0
  */
 public class FS_Win32 extends FS {
-	private final static Logger LOG = LoggerFactory.getLogger(FS_Win32.class);
+	private static final Logger LOG = LoggerFactory.getLogger(FS_Win32.class);
 
 	/**
 	 * Constructor
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 9a163e8..ac788a6 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
@@ -47,8 +47,6 @@
 
 import java.io.File;
 import java.io.PrintStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
@@ -57,7 +55,6 @@
 
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.errors.CommandFailedException;
-import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -68,7 +65,7 @@
  * @since 3.0
  */
 public class FS_Win32_Cygwin extends FS_Win32 {
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(FS_Win32_Cygwin.class);
 
 	private static String cygpath;
@@ -160,6 +157,11 @@
 		return proc;
 	}
 
+	@Override
+	String shellQuote(String cmd) {
+		return QuotedString.BOURNE.quote(cmd.replace(File.separatorChar, '/'));
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public String relativize(String base, String other) {
@@ -175,18 +177,4 @@
 		return internalRunHookIfPresent(repository, hookName, args, outRedirect,
 				errRedirect, stdinArgs);
 	}
-
-	/** {@inheritDoc} */
-	@Override
-	public File findHook(Repository repository, String hookName) {
-		final File gitdir = repository.getDirectory();
-		if (gitdir == null) {
-			return null;
-		}
-		final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
-				.resolve(hookName);
-		if (Files.isExecutable(hookPath))
-			return hookPath.toFile();
-		return null;
-	}
 }
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 4d791e4..e026e92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -73,6 +73,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Random;
 import java.util.regex.Pattern;
 
 import org.eclipse.jgit.internal.JGitText;
@@ -87,6 +88,8 @@
 public class FileUtils {
 	private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class);
 
+	private static final Random RNG = new Random();
+
 	/**
 	 * Option to delete given {@code File}
 	 */
@@ -986,4 +989,28 @@
 		}
 		Files.setLastModifiedTime(f, FileTime.from(Instant.now()));
 	}
+
+	/**
+	 * Compute a delay in a {@code min..max} interval with random jitter.
+	 *
+	 * @param last
+	 *            amount of delay waited before the last attempt. This is used
+	 *            to seed the next delay interval. Should be 0 if there was no
+	 *            prior delay.
+	 * @param min
+	 *            shortest amount of allowable delay between attempts.
+	 * @param max
+	 *            longest amount of allowable delay between attempts.
+	 * @return new amount of delay to wait before the next attempt.
+	 *
+	 * @since 5.6
+	 */
+	public static long delay(long last, long min, long max) {
+		long r = Math.max(0, last * 3 - min);
+		if (r > 0) {
+			int c = (int) Math.min(r + 1, Integer.MAX_VALUE);
+			r = RNG.nextInt(c);
+		}
+		return Math.max(Math.min(min + r, max), min);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
index e461902..e9f65d2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
@@ -68,7 +68,7 @@
 	/**
 	 * Git and JGit formats
 	 */
-	static public enum Format {
+	public enum Format {
 
 		/**
 		 * Git format: Time and original time zone
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
index 56a1731..c6a6899 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
@@ -144,7 +144,7 @@
 	 * <li>"yesterday"</li>
 	 * <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br>
 	 * Multiple specs can be combined like in "2 weeks 3 days ago". Instead of '
-	 * ' one can use '.' to seperate the words</li>
+	 * ' one can use '.' to separate the words</li>
 	 * <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
 	 * <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
 	 * <li>"yyyy-MM-dd"</li>
@@ -186,7 +186,7 @@
 	 * <li>"yesterday"</li>
 	 * <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br>
 	 * Multiple specs can be combined like in "2 weeks 3 days ago". Instead of '
-	 * ' one can use '.' to seperate the words</li>
+	 * ' one can use '.' to separate the words</li>
 	 * <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
 	 * <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
 	 * <li>"yyyy-MM-dd"</li>
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 640670d..d897255 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -51,6 +51,7 @@
 import java.net.ConnectException;
 import java.net.Proxy;
 import java.net.ProxySelector;
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLEncoder;
@@ -299,7 +300,9 @@
 	public static Proxy proxyFor(ProxySelector proxySelector, URL u)
 			throws ConnectException {
 		try {
-			return proxySelector.select(u.toURI()).get(0);
+			URI uri = new URI(u.getProtocol(), null, u.getHost(), u.getPort(),
+					null, null, null);
+			return proxySelector.select(uri).get(0);
 		} catch (URISyntaxException e) {
 			final ConnectException err;
 			err = new ConnectException(MessageFormat.format(JGitText.get().cannotDetermineProxyFor, u));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
index a07a4fd..391598d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -345,13 +345,14 @@
 					c = s.charAt(++i);
 					l.add(sb.toString());
 					sb.setLength(0);
-					if (c != '\n')
+					if (c != '\n') {
 						sb.append(c);
+					}
 					continue;
-				} else { // EOF
-					l.add(sb.toString());
-					break;
 				}
+				// EOF
+				l.add(sb.toString());
+				break;
 			}
 			sb.append(c);
 		}
@@ -401,20 +402,18 @@
 				}
 				resetAndSkipFully(in, n);
 			}
-		} else {
-			StringBuilder buf = sizeHint > 0
-					? new StringBuilder(sizeHint)
-					: new StringBuilder();
-			int i;
-			while ((i = in.read()) != -1) {
-				char c = (char) i;
-				buf.append(c);
-				if (c == '\n') {
-					break;
-				}
-			}
-			return buf.toString();
 		}
+		StringBuilder buf = sizeHint > 0 ? new StringBuilder(sizeHint)
+				: new StringBuilder();
+		int i;
+		while ((i = in.read()) != -1) {
+			char c = (char) i;
+			buf.append(c);
+			if (c == '\n') {
+				break;
+			}
+		}
+		return buf.toString();
 	}
 
 	private static void resetAndSkipFully(Reader fd, long toSkip) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
index 96636b7..0f6620e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
@@ -145,7 +145,7 @@
 	}
 
 	/**
-	 * Retrieve a pre-push hook to be applied.
+	 * Retrieve a pre-push hook to be applied using the default error stream.
 	 *
 	 * @param repo
 	 *            the {@link Repository} the hook is applied to.
@@ -159,6 +159,22 @@
 	}
 
 	/**
+	 * Retrieve a pre-push hook to be applied.
+	 *
+	 * @param repo
+	 *            the {@link Repository} the hook is applied to.
+	 * @param outputStream
+	 * @param errorStream
+	 * @return a {@link PrePushHook} implementation or <code>null</code>
+	 * @since 5.6
+	 */
+	@Nullable
+	public PrePushHook getPrePushHook(Repository repo, PrintStream outputStream,
+			PrintStream errorStream) {
+		return getPrePushHook(repo, outputStream);
+	}
+
+	/**
 	 * Retrieve an {@link LfsInstallCommand} which can be used to enable LFS
 	 * support (if available) either per repository or for the user.
 	 *
@@ -297,7 +313,7 @@
 		}
 
 		@Override
-		public int read(byte b[], int off, int len) throws IOException {
+		public int read(byte[] b, int off, int len) throws IOException {
 			return stream.read(b, off, len);
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java
index 83bf695..500c236 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Monitoring.java
@@ -33,7 +33,7 @@
  * @since 5.1.13
  */
 public class Monitoring {
-	private final static Logger LOG = LoggerFactory.getLogger(Monitoring.class);
+	private static final Logger LOG = LoggerFactory.getLogger(Monitoring.class);
 
 	/**
 	 * Register a MBean with the platform MBean server
@@ -49,7 +49,7 @@
 			String metricName) {
 		boolean register = false;
 		try {
-			Class<?> interfaces[] = mbean.getClass().getInterfaces();
+			Class<?>[] interfaces = mbean.getClass().getInterfaces();
 			for (Class<?> i : interfaces) {
 				register = SystemReader.getInstance().getUserConfig()
 						.getBoolean(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java
index 9267a32..1180d4c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java
@@ -51,7 +51,7 @@
 	/**
 	 * Status of a process' execution.
 	 */
-	public static enum Status {
+	public enum Status {
 		/**
 		 * The script was found and launched properly. It may still have exited
 		 * with a non-zero {@link #exitCode}.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/QuotedString.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/QuotedString.java
index a55cad3..2b2358a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/QuotedString.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/QuotedString.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Google Inc.
+ * Copyright (C) 2008, 2019 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -54,7 +54,15 @@
  */
 public abstract class QuotedString {
 	/** Quoting style that obeys the rules Git applies to file names */
-	public static final GitPathStyle GIT_PATH = new GitPathStyle();
+	public static final GitPathStyle GIT_PATH = new GitPathStyle(true);
+
+	/**
+	 * Quoting style that obeys the rules Git applies to file names when
+	 * {@code core.quotePath = false}.
+	 *
+	 * @since 5.6
+	 */
+	public static final QuotedString GIT_PATH_MINIMAL = new GitPathStyle(false);
 
 	/**
 	 * Quoting style used by the Bourne shell.
@@ -256,40 +264,48 @@
 			quote['"'] = '"';
 		}
 
+		private final boolean quoteHigh;
+
 		@Override
 		public String quote(String instr) {
-			if (instr.length() == 0)
+			if (instr.isEmpty()) {
 				return "\"\""; //$NON-NLS-1$
+			}
 			boolean reuse = true;
 			final byte[] in = Constants.encode(instr);
-			final StringBuilder r = new StringBuilder(2 + in.length);
-			r.append('"');
+			final byte[] out = new byte[4 * in.length + 2];
+			int o = 0;
+			out[o++] = '"';
 			for (int i = 0; i < in.length; i++) {
 				final int c = in[i] & 0xff;
 				if (c < quote.length) {
 					final byte style = quote[c];
 					if (style == 0) {
-						r.append((char) c);
+						out[o++] = (byte) c;
 						continue;
 					}
 					if (style > 0) {
 						reuse = false;
-						r.append('\\');
-						r.append((char) style);
+						out[o++] = '\\';
+						out[o++] = style;
 						continue;
 					}
+				} else if (!quoteHigh) {
+					out[o++] = (byte) c;
+					continue;
 				}
 
 				reuse = false;
-				r.append('\\');
-				r.append((char) (((c >> 6) & 03) + '0'));
-				r.append((char) (((c >> 3) & 07) + '0'));
-				r.append((char) (((c >> 0) & 07) + '0'));
+				out[o++] = '\\';
+				out[o++] = (byte) (((c >> 6) & 03) + '0');
+				out[o++] = (byte) (((c >> 3) & 07) + '0');
+				out[o++] = (byte) (((c >> 0) & 07) + '0');
 			}
-			if (reuse)
+			if (reuse) {
 				return instr;
-			r.append('"');
-			return r.toString();
+			}
+			out[o++] = '"';
+			return new String(out, 0, o, UTF_8);
 		}
 
 		@Override
@@ -375,8 +391,8 @@
 			return RawParseUtils.decode(UTF_8, r, 0, rPtr);
 		}
 
-		private GitPathStyle() {
-			// Singleton
+		private GitPathStyle(boolean doQuote) {
+			quoteHigh = doQuote;
 		}
 	}
 }
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 9663e3c..ce1308f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
@@ -191,12 +191,11 @@
 			Ref prior = loose.get(name);
 			loose = loose.set(idx, value);
 			return prior;
-		} else {
-			Ref prior = get(keyName);
-			loose = loose.add(idx, value);
-			sizeIsValid = false;
-			return prior;
 		}
+		Ref prior = get(keyName);
+		loose = loose.add(idx, value);
+		sizeIsValid = false;
+		return prior;
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
index 83c60c6..55f39c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
@@ -52,19 +52,19 @@
  * in the format defined by {@code git log --relative-date}.
  */
 public class RelativeDateFormatter {
-	final static long SECOND_IN_MILLIS = 1000;
+	static final long SECOND_IN_MILLIS = 1000;
 
-	final static long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+	static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
 
-	final static long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+	static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
 
-	final static long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
+	static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
 
-	final static long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+	static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
 
-	final static long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+	static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
 
-	final static long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+	static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
 
 	/**
 	 * Get age of given {@link java.util.Date} compared to now formatted in the
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 e16a886..87ce475 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -85,7 +85,7 @@
  */
 public abstract class SystemReader {
 
-	private final static Logger LOG = LoggerFactory
+	private static final Logger LOG = LoggerFactory
 			.getLogger(SystemReader.class);
 
 	private static final SystemReader DEFAULT;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java
index 9ab2caa..e437c11 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java
@@ -98,15 +98,16 @@
 	@Override
 	public int read() throws IOException {
 		if (left == 0) {
-			if (in.available() == 0)
+			if (in.available() == 0) {
 				return -1;
-			else
-				limitExceeded();
+			}
+			limitExceeded();
 		}
 
 		int result = in.read();
-		if (result != -1)
+		if (result != -1) {
 			--left;
+		}
 		return result;
 	}
 
@@ -114,16 +115,17 @@
 	@Override
 	public int read(byte[] b, int off, int len) throws IOException {
 		if (left == 0) {
-			if (in.available() == 0)
+			if (in.available() == 0) {
 				return -1;
-			else
-				limitExceeded();
+			}
+			limitExceeded();
 		}
 
 		len = (int) Math.min(len, left);
 		int result = in.read(b, off, len);
-		if (result != -1)
+		if (result != -1) {
 			left -= result;
+		}
 		return result;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
index 1ad6602..e6971ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
@@ -78,7 +78,7 @@
  * @since 4.7
  */
 public class SHA1 {
-	private static Logger LOG = LoggerFactory.getLogger(SHA1.class);
+	private static final Logger LOG = LoggerFactory.getLogger(SHA1.class);
 	private static final boolean DETECT_COLLISIONS;
 
 	static {
diff --git a/pom.xml b/pom.xml
index d0335b0..7d2d40f 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.5.2-SNAPSHOT</version>
+  <version>5.6.2-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -180,6 +180,8 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <maven.compiler.target>1.8</maven.compiler.target>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
     <jgit-last-release-version>5.4.0.201906121030-r</jgit-last-release-version>
@@ -193,7 +195,7 @@
     <commons-compress-version>1.18</commons-compress-version>
     <osgi-core-version>4.3.1</osgi-core-version>
     <servlet-api-version>3.1.0</servlet-api-version>
-    <jetty-version>9.4.20.v20190813</jetty-version>
+    <jetty-version>9.4.22.v20191022</jetty-version>
     <japicmp-version>0.14.1</japicmp-version>
     <httpclient-version>4.5.6</httpclient-version>
     <httpcore-version>4.4.10</httpcore-version>
@@ -202,7 +204,7 @@
     <maven-javadoc-plugin-version>3.3.1</maven-javadoc-plugin-version>
     <tycho-extras-version>1.7.0</tycho-extras-version>
     <gson-version>2.8.2</gson-version>
-    <bouncycastle-version>1.61</bouncycastle-version>
+    <bouncycastle-version>1.64</bouncycastle-version>
     <maven-project-info-reports-plugin-version>3.1.2</maven-project-info-reports-plugin-version>
     <maven-jxr-plugin-version>3.1.1</maven-jxr-plugin-version>
     <maven-surefire-plugin-version>3.0.0-M5</maven-surefire-plugin-version>
@@ -378,7 +380,7 @@
             <dependency><!-- add support for ssh/scp -->
               <groupId>org.apache.maven.wagon</groupId>
               <artifactId>wagon-ssh</artifactId>
-              <version>3.3.3</version>
+              <version>3.3.4</version>
             </dependency>
           </dependencies>
         </plugin>
@@ -439,7 +441,7 @@
             <configuration>
               <rules>
                 <requireMavenVersion>
-                  <version>3.5.2</version>
+                  <version>3.6.2</version>
                 </requireMavenVersion>
               </rules>
             </configuration>
@@ -925,7 +927,7 @@
               <dependency>
                 <groupId>org.eclipse.jdt</groupId>
                 <artifactId>ecj</artifactId>
-                <version>3.18.0</version>
+                <version>3.19.0</version>
               </dependency>
             </dependencies>
           </plugin>
diff --git a/tools/bazelisk_version.bzl b/tools/bazelisk_version.bzl
new file mode 100644
index 0000000..d8b3d10
--- /dev/null
+++ b/tools/bazelisk_version.bzl
@@ -0,0 +1,16 @@
+_template = """
+load("@bazel_skylib//lib:versions.bzl", "versions")
+
+def check_bazel_version():
+  versions.check(minimum_bazel_version = "{version}")
+""".strip()
+
+def _impl(repository_ctx):
+    repository_ctx.symlink(Label("@//:.bazelversion"), ".bazelversion")
+    bazelversion = repository_ctx.read(".bazelversion").strip()
+
+    repository_ctx.file("BUILD", executable = False)
+
+    repository_ctx.file("check.bzl", executable = False, content = _template.format(version = bazelversion))
+
+bazelisk_version = repository_rule(implementation = _impl)