Merge branch 'stable-5.9'

* stable-5.9:
  Fix stamping to produce stable file timestamps

Change-Id: Icd092cd9b883556fcbd115c17346a9d88dc172ce
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/.bazelversion b/.bazelversion
index 1545d96..8faff82 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.5.0
+4.0.0rc2
diff --git a/Documentation/config-options.md b/Documentation/config-options.md
index 94ef5b9..d463551 100644
--- a/Documentation/config-options.md
+++ b/Documentation/config-options.md
@@ -11,15 +11,47 @@
 
 |  option | default | git option | description |
 |---------|---------|------------|-------------|
-| `core.bigFileThreshold` | `52428800` (50 MiB) | &#x2705; | Maximum file size that will be delta compressed. Files larger than this size are stored deflated, without attempting delta compression. |
-| `core.compression` | `-1` (default compression) | &#x2705; | An integer -1..9, indicating a default compression level. -1 is the zlib default. 0 means no compression, and 1..9 are various speed/size tradeoffs, 9 being slowest.|
+| `core.attributesFile` | | &#x2705; | In addition to `.gitattributes` (per-directory) and `.git/info/attributes`, Git looks into this file for attributes . Path expansions are made the same way as for `core.excludesFile`. |
+| `core.autocrlf` | `false` | &#x2705; | Setting this variable to `true` is the same as setting the text attribute to `auto` on all files and `core.eol` to `crlf`. Set to `true` if you want to have CRLF line endings in your working directory and the repository has LF line endings. This variable can be set to `input`, in which case no output conversion is performed. |
+| `core.bare` | set automatically on init or clone | &#x2705; | If true this repository is assumed to be bare and has no working directory associated with it. If this is the case a number of commands that require a working directory will be disabled |
+| `core.bigFileThreshold` | `50 MiB` | &#x2705; | Files larger than this size are stored deflated, without attempting delta compression. Storing large files without delta compression avoids excessive memory usage, at the slight expense of increased disk usage. Additionally files larger than this size are always treated as binary. |
+| `core.checkstat` |  | &#x2705; | When missing or is set to `default`, many fields in the stat structure are checked to detect if a file has been modified since Git looked at it. Checks as much of the dircache stat info as possible (in JGit limited by Java filesystem API). When set to `minimum` only checks the size and whole second part of time stamp when comparing the stat info in the dircache with actual file stat info. |
+| `core.compression` | `-1` (zlib default) | &#x2705; | An integer `-1..9`, indicating a default compression level. `-1` is the zlib default. `0` means no compression, and `1..9` are various speed/size tradeoffs, `9` being slowest.|
+| `core.deltaBaseCacheLimit` | `10 MiB` | &#x2705; | Maximum number of bytes to reserve for caching base objects that multiple deltafied objects reference. By storing the entire decompressed base object in a cache Git is able to avoid unpacking and decompressing frequently used base objects multiple times. |
+| `core.dfs.blockLimit` | `30 MiB` | &#x20DE; | Maximum number bytes of heap memory to dedicate to caching pack file data in DFS block cache. |
+| `core.dfs.blockSize` | `64 kiB` | &#x20DE; | Size in bytes of a single window read in from the pack file into the DFS block cache. |
+| `core.dfs.concurrencyLevel` | `32` | &#x20DE; | The estimated number of threads concurrently accessing the DFS block cache. |
+| `core.dfs.deltaBaseCacheLimit` | `10 MiB` | &#x20DE; | Maximum number of bytes to hold in per-reader DFS delta base cache. |
+| `core.dfs.streamFileThreshold` | `50 MiB` | &#x20DE; | The size threshold beyond which objects must be streamed. |
+| `core.dfs.streamBuffer` | Block size of the pack | &#x20DE; | Number of bytes to use for buffering when streaming a pack file during copying. If 0 the block size of the pack is used|
+| `core.dfs.streamRatio` | `0.30` | &#x20DE; | Ratio of DFS block cache to occupy with a copied pack. Values between `0` and `1.0`. |
+| `core.dirNoGitLinks` | `false` | &#x20DE; | If set to `true` avoid checking for submodules. See [bug 436200](https://bugs.eclipse.org/bugs/show_bug.cgi?id=436200). |
+| `core.eol` | `native` | &#x2705; | Sets the line ending type to use in the working directory for files that are marked as text (either by having the text attribute set, or by having `text=auto` and Git auto-detecting the contents as text). Alternatives are `lf`, `crlf` and `native`, which uses the platform’s native line ending. |
+| `core.excludesFile` | | &#x2705; | Specifies the pathname to the file that contains patterns to describe paths that are not meant to be tracked, in addition to `.gitignore` (per-directory) and `.git/info/exclude`. |
+| `core.fileMode` | Auto detects if file modes are supported | &#x2705; | Tells Git if the executable bit of files in the working tree is to be honored. |
+| `core.hideDotFiles` | `dotGitOnly` | &#x2705; | Windows only. If `true`, mark newly-created directories and files whose name starts with a dot as hidden. If `dotGitOnly`, only the `.git/` directory is hidden, but no other files starting with a dot. |
+| `core.hooksPath` | `$GIT_DIR/hooks` | &#x2705; | Path to look for hooks. |
+| `core.logAllRefUpdates` | `true` in a repository with working tree, `false` in bare repository | &#x2705; | Enable the reflog. |
+| `core.packedGitLimit` | `10 MiB` | &#x2705; | Maximum number of bytes to cache in memory from pack files. |
+| `core.packedGitMmap` | `false` | &#x2705; | Whether to use Java NIO virtual memory mapping for JGit buffer cache. When set to `true` enables use of Java NIO virtual memory mapping for cache windows, `false` reads entire window into a `byte[]` with standard read calls. `true` is experimental and may cause instabilities and crashes since Java doesn't support explicit unmapping of file regions mapped to virtual memory. |
+| `core.packedGitOpenFiles` | `128` | &#x20DE; | Maximum number of streams to open at a time. Open packs count against the process limits. |
+| `core.packedGitUseStrongRefs` | `false` | &#x20DE; | Whether the window cache should use strong references (`true`) or SoftReferences (`false`). When `false` the JVM will drop data cached in the JGit block cache when heap usage comes close to the maximum heap size. |
+| `core.packedGitWindowSize` | `8 kiB` | &#x2705; | Number of bytes of a pack file to load into memory in a single read operation. This is the "page size" of the JGit buffer cache, used for all pack access operations. All disk IO occurs as single window reads. Setting this too large may cause the process to load more data than is required; setting this too small may increase the frequency of read() system calls. |
+| `core.precomposeUnicode` | `true` on Mac OS | &#x2705; | MacOS only. When `true`, JGit reverts the unicode decomposition of filenames done by Mac OS. |
+| `core.quotePath` | `true` | &#x2705; | Commands that output paths (e.g. ls-files, diff), will quote "unusual" characters in the pathname by enclosing the pathname in double-quotes and escaping those characters with backslashes in the same way C escapes control characters (e.g. `\t` for TAB, `\n` for LF, `\\` for backslash) or bytes with values larger than `0x80` (e.g. octal `\302\265` for "micro" in UTF-8). |
+| `core.repositoryFormatVersion` | `1` | &#x20DE; | Internal version identifying the repository format and layout version. Don't set manually. |
+| `core.streamFileThreshold` | `50 MiB` | &#x20DE; | The size threshold beyond which objects must be streamed. |
+| `core.supportsAtomicFileCreation` | `true` | &#x20DE; | Whether the filesystem supports atomic file creation. |
+| `core.symlinks` | Auto detect if filesystem supports symlinks| &#x2705; | If false, symbolic links are checked out as small plain files that contain the link text. |
+| `core.trustFolderStat` | `true` | &#x20DE; | Whether to trust the pack folder's modification time. If `false` JGit will always scan the `.git/objects/pack` folder to check for new pack files. This can help to workaround caching issues on NFS, but reduces performance. If set to `true` it uses the `lastmodified` attribute of the folder and assumes that no new pack files can be in this folder if its modification time has not changed. |
+| `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | &#x2705; | The path to the root of the working tree. |
 
 ## __gc__ options
 
 |  option | default | git option | description |
 |---------|---------|------------|-------------|
-| `gc.aggressiveDepth` | 50 | &#x2705; | The depth parameter used in the delta compression algorithm used by aggressive garbage collection. |
-| `gc.aggressiveWindow` | 250 | &#x2705; | The window size parameter used in the delta compression algorithm used by aggressive garbage collection. |
+| `gc.aggressiveDepth` | `50` | &#x2705; | The depth parameter used in the delta compression algorithm used by aggressive garbage collection. |
+| `gc.aggressiveWindow` | `250` | &#x2705; | The window size parameter used in the delta compression algorithm used by aggressive garbage collection. |
 | `gc.auto` | `6700` | &#x2705; | Number of loose objects until auto gc combines all loose objects into a pack and consolidates all existing packs into one. Setting to 0 disables automatic packing of loose objects. |
 | `gc.autoDetach` | `true` |  &#x2705; | Make auto gc return immediately and run in background. |
 | `gc.autoPackLimit` | `50` |  &#x2705; | Number of packs until auto gc consolidates existing packs (except those marked with a .keep file) into a single pack. Setting `gc.autoPackLimit` to 0 disables automatic consolidation of packs. |
@@ -41,11 +73,11 @@
 | `pack.compression` | `core.compression` | &#x2705; | Compression level applied to objects in the pack. |
 | `pack.cutDeltaChains` | `false` | &#x20DE; | Whether existing delta chains should be cut at {@link #getMaxDeltaDepth() |
 | `pack.deltaCacheLimit` | `100` | &#x2705; | Maximum size in bytes of a delta to cache. |
-| `pack.deltaCacheSize` | `52428800` (50 MiB) | &#x2705; | Size of the in-memory delta cache. |
+| `pack.deltaCacheSize` | `50 MiB` | &#x2705; | Size of the in-memory delta cache. |
 | `pack.deltaCompression` | `true` | &#x20DE; | Whether the writer will create new deltas on the fly. `true` if the pack writer will create a new delta when either `pack.reuseDeltas` is false, or no suitable delta is available for reuse. |
 | `pack.depth` | `50` | &#x2705; | Maximum depth of delta chain set up for the pack writer. |
 | `pack.indexVersion` | `2` | &#x2705; | Pack index file format version. |
-| `pack.minSizePreventRacyPack` | `104857600` (100 MiB) | &#x20DE; | Minimum packfile size for which we wait before opening a newly written pack to prevent its lastModified timestamp could be racy if `pack.waitPreventRacyPack` is `true`. |
+| `pack.minSizePreventRacyPack` | `100 MiB` | &#x20DE; | Minimum packfile size for which we wait before opening a newly written pack to prevent its lastModified timestamp could be racy if `pack.waitPreventRacyPack` is `true`. |
 | `pack.preserveOldPacks` | `false` | &#x20DE; | Whether to preserve old packs in a preserved directory. |
 | `prunePreserved`, only via API of PackConfig | `false` | &#x20DE; | Whether to remove preserved pack files in a preserved directory. |
 | `pack.reuseDeltas` | `true` |&#x20DE; | Whether to reuse deltas existing in repository. |
diff --git a/WORKSPACE b/WORKSPACE
index 9cd666f..ad04d14 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -27,6 +27,34 @@
     "maven_jar",
 )
 
+http_archive(
+    name = "openjdk15_linux_archive",
+    build_file_content = """
+java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])
+exports_files(["WORKSPACE"], visibility = ["//visibility:public"])
+""",
+    sha256 = "0a38f1138c15a4f243b75eb82f8ef40855afcc402e3c2a6de97ce8235011b1ad",
+    strip_prefix = "zulu15.27.17-ca-jdk15.0.0-linux_x64",
+    urls = [
+        "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz",
+        "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz",
+    ],
+)
+
+http_archive(
+    name = "openjdk15_darwin_archive",
+    build_file_content = """
+java_runtime(name = 'runtime', srcs =  glob(['**']), visibility = ['//visibility:public'])
+exports_files(["WORKSPACE"], visibility = ["//visibility:public"])
+""",
+    sha256 = "f80b2e0512d9d8a92be24497334c974bfecc8c898fc215ce0e76594f00437482",
+    strip_prefix = "zulu15.27.17-ca-jdk15.0.0-macosx_x64",
+    urls = [
+        "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz",
+        "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz",
+    ],
+)
+
 JMH_VERS = "1.21"
 
 maven_jar(
@@ -119,14 +147,14 @@
 
 maven_jar(
     name = "log-api",
-    artifact = "org.slf4j:slf4j-api:1.7.2",
-    sha1 = "0081d61b7f33ebeab314e07de0cc596f8e858d97",
+    artifact = "org.slf4j:slf4j-api:1.7.30",
+    sha1 = "b5a4b6d16ab13e34a88fae84c35cd5d68cac922c",
 )
 
 maven_jar(
     name = "slf4j-simple",
-    artifact = "org.slf4j:slf4j-simple:1.7.2",
-    sha1 = "760055906d7353ba4f7ce1b8908bc6b2e91f39fa",
+    artifact = "org.slf4j:slf4j-simple:1.7.30",
+    sha1 = "e606eac955f55ecf1d8edcccba04eb8ac98088dd",
 )
 
 maven_jar(
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 3dd5840..b853c6a 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
@@ -51,8 +51,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index d660518..6e0cfa0 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.ant.tasks;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.13,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index d2dd5a1..6d1debe 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index bd021e9..0607696 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)"
+  org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.ant;version="5.9.1",
- org.eclipse.jgit.ant.tasks;version="5.9.1";
+Export-Package: org.eclipse.jgit.ant;version="5.11.0",
+ org.eclipse.jgit.ant.tasks;version="5.11.0";
   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 d10feaa..84f2274 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ant;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 342e84d..2ec358c 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -15,7 +15,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index 96be945..0297b17 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.9.1.qualifier
+Bundle-Version: 5.11.0.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.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.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.9.1";
+Export-Package: org.eclipse.jgit.archive;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.api,
    org.apache.commons.compress.archivers,
    org.osgi.framework",
- org.eclipse.jgit.archive.internal;version="5.9.1";x-internal:=true
+ org.eclipse.jgit.archive.internal;version="5.11.0";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 aa31f32..e20f008 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 1277f45..5031972 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-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 e4d8703..d2e7f00 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -14,7 +14,7 @@
   <modelVersion>4.0.0</modelVersion>
 
   <groupId>org.eclipse.jgit</groupId>
-  <version>5.9.1-SNAPSHOT</version>
+  <version>5.11.0-SNAPSHOT</version>
   <artifactId>org.eclipse.jgit.benchmarks</artifactId>
   <packaging>jar</packaging>
 
@@ -179,7 +179,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-project-info-reports-plugin</artifactId>
-          <version>3.0.0</version>
+          <version>3.1.1</version>
         </plugin>
       </plugins>
     </pluginManagement>
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index 14a22f5..42680e9 100644
--- a/org.eclipse.jgit.coverage/pom.xml
+++ b/org.eclipse.jgit.coverage/pom.xml
@@ -14,7 +14,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
@@ -27,88 +27,88 @@
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.archive</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.apache</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.server</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ui</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.test</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ant.test</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.http.test</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm.test</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.test</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
   </dependencies>
 
diff --git a/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs
index 822846c..cba893f 100644
--- a/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.gpg.bc.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
index 22ff017..35a418c 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -3,11 +3,11 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.gpg.bc.test
 Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.test
-Bundle-Version: 5.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.gpg.bc.internal;version="[5.9.1,5.10.0)",
+Import-Package: org.eclipse.jgit.gpg.bc.internal;version="[5.11.0,5.12.0)",
  org.junit;version="[4.13,5.0.0)"
 Export-Package: org.eclipse.jgit.gpg.bc.internal;x-internal:=true
 Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index 780a273..f244fb4 100644
--- a/org.eclipse.jgit.gpg.bc.test/pom.xml
+++ b/org.eclipse.jgit.gpg.bc.test/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.gpg.bc.test</artifactId>
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
index 544cf6f..655dcca 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
@@ -3,10 +3,10 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.gpg.bc
 Bundle-SymbolicName: org.eclipse.jgit.gpg.bc;singleton:=true
-Fragment-Host: org.eclipse.jgit;bundle-version="[5.9.1,5.10.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[5.11.0,5.12.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
-Bundle-Version: 5.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
  org.bouncycastle.gpg;version="[1.65.0,2.0.0)",
@@ -17,13 +17,13 @@
  org.bouncycastle.openpgp.operator;version="[1.65.0,2.0.0)",
  org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)",
  org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)",
- org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc.internal;version="5.9.1";
+Export-Package: org.eclipse.jgit.gpg.bc.internal;version="5.11.0";
   x-friends:="org.eclipse.jgit.gpg.bc.test"
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
index 5a44e2a..307e5d0 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.gpg.bc - Sources
 Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml
index 9b09db5..d94517c 100644
--- a/org.eclipse.jgit.gpg.bc/pom.xml
+++ b/org.eclipse.jgit.gpg.bc/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.gpg.bc</artifactId>
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
index ea159c5..449c4a4 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
@@ -38,6 +38,8 @@
 import org.eclipse.jgit.lib.CommitBuilder;
 import org.eclipse.jgit.lib.GpgSignature;
 import org.eclipse.jgit.lib.GpgSigner;
+import org.eclipse.jgit.lib.GpgObjectSigner;
+import org.eclipse.jgit.lib.ObjectBuilder;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.transport.CredentialsProvider;
 import org.eclipse.jgit.util.StringUtils;
@@ -45,7 +47,8 @@
 /**
  * GPG Signer using BouncyCastle library
  */
-public class BouncyCastleGpgSigner extends GpgSigner {
+public class BouncyCastleGpgSigner extends GpgSigner
+		implements GpgObjectSigner {
 
 	private static void registerBouncyCastleProviderIfNecessary() {
 		if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
@@ -98,6 +101,13 @@
 	public void sign(@NonNull CommitBuilder commit,
 			@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
 			CredentialsProvider credentialsProvider) throws CanceledException {
+		signObject(commit, gpgSigningKey, committer, credentialsProvider);
+	}
+
+	@Override
+	public void signObject(@NonNull ObjectBuilder object,
+			@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
+			CredentialsProvider credentialsProvider) throws CanceledException {
 		try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
 				credentialsProvider)) {
 			BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
@@ -158,10 +168,10 @@
 			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 			try (BCPGOutputStream out = new BCPGOutputStream(
 					new ArmoredOutputStream(buffer))) {
-				signatureGenerator.update(commit.build());
+				signatureGenerator.update(object.build());
 				signatureGenerator.generate().encode(out);
 			}
-			commit.setGpgSignature(new GpgSignature(buffer.toByteArray()));
+			object.setGpgSignature(new GpgSignature(buffer.toByteArray()));
 		} catch (PGPException | IOException | NoSuchAlgorithmException
 				| NoSuchProviderException | URISyntaxException e) {
 			throw new JGitInternalException(e.getMessage(), e);
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index 944d27a..a385203 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
@@ -25,11 +25,11 @@
  org.apache.http.impl.conn;version="[4.3.0,5.0.0)",
  org.apache.http.params;version="[4.3.0,5.0.0)",
  org.apache.http.ssl;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="5.9.1";
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="5.11.0";
   uses:="org.apache.http.client,
    org.eclipse.jgit.transport.http,
    org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
index 1569ae7..4fdfb75 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 26d0eae..0d24fa5 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -15,7 +15,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index c966c45..467204d 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.http.server;version="5.9.1",
- org.eclipse.jgit.http.server.glue;version="5.9.1";
+Export-Package: org.eclipse.jgit.http.server;version="5.11.0",
+ org.eclipse.jgit.http.server.glue;version="5.11.0";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="5.9.1";
+ org.eclipse.jgit.http.server.resolver;version="5.11.0";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -18,14 +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.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.transport.parser;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.resolver;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)"
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.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 bdbbf89..4d1cc64 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 572fd4b..5a37b64 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -19,7 +19,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
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 3dd5840..b853c6a 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
@@ -51,8 +51,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 52e4bed..44ce9c4 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.9.1.qualifier
+Bundle-Version: 5.11.0.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.9.1,5.10.0)",
- org.eclipse.jgit.http.server;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.http.server.glue;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.http.server.resolver;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.resolver;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.http.server;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.http.server.glue;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.http.server.resolver;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.hamcrest;version="[1.1.0,2.0.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 6c259e6..40960b6 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -18,7 +18,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 9654c91..7844845 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.9.1.qualifier
+Bundle-Version: 5.11.0.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.9.1,5.10.0)",
- org.eclipse.jgit.http.server;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.resolver;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.http.server;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.11.0,5.12.0)",
  org.junit;version="[4.13,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="5.9.1";
+Export-Package: org.eclipse.jgit.junit.http;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.junit,
    javax.servlet.http,
diff --git a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
index 8732283..f3b9307 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index e7193e6..90a570c 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 4e78d61..46b59fe 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
@@ -30,16 +30,16 @@
  org.apache.sshd.server.shell;version="[2.4.0,2.5.0)",
  org.apache.sshd.server.subsystem;version="[2.4.0,2.5.0)",
  org.apache.sshd.server.subsystem.sftp;version="[2.4.0,2.5.0)",
- org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.junit.ssh;version="5.9.1"
+Export-Package: org.eclipse.jgit.junit.ssh;version="5.11.0"
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
index 7ec99eb..0e9e527 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index a1e635b..335ddca 100644
--- a/org.eclipse.jgit.junit.ssh/pom.xml
+++ b/org.eclipse.jgit.junit.ssh/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.ssh</artifactId>
diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
index 7970685..90d981b 100644
--- a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
+++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestHarness.java
@@ -76,6 +76,8 @@
 
 	protected File publicKey1;
 
+	protected File publicKey2;
+
 	protected SshTestGitServer server;
 
 	private SshSessionFactory factory;
@@ -110,7 +112,7 @@
 		privateKey1 = new File(sshDir, "first_key");
 		privateKey2 = new File(sshDir, "second_key");
 		publicKey1 = createKeyPair(generator.generateKeyPair(), privateKey1);
-		createKeyPair(generator.generateKeyPair(), privateKey2);
+		publicKey2 = createKeyPair(generator.generateKeyPair(), privateKey2);
 		// Create a host key
 		KeyPair hostKey = generator.generateKeyPair();
 		// Start a server with our test user and the first key.
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 993dc72..2295bf4 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.dircache;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.merge;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="5.9.1",
- org.eclipse.jgit.treewalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.io;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.time;version="[5.9.1,5.10.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.dircache;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.merge;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="5.11.0",
+ org.eclipse.jgit.treewalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.io;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.time;version="[5.11.0,5.12.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.rules;version="[4.13,5.0.0)",
  org.junit.runner;version="[4.13,5.0.0)",
  org.junit.runners;version="[4.13,5.0.0)",
  org.junit.runners.model;version="[4.13,5.0.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="5.9.1";
+Export-Package: org.eclipse.jgit.junit;version="5.11.0";
   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.9.1";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="5.11.0";uses:="org.eclipse.jgit.util.time"
diff --git a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
index 0f204a3..7782269 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index c6c7c4f..26331c3 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -19,7 +19,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
index cc84f19..64556ac 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
@@ -46,6 +46,7 @@
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
 import org.junit.Before;
 
 /**
@@ -187,6 +188,13 @@
 		trash = db.getWorkTree();
 	}
 
+	@Override
+	@After
+	public void tearDown() throws Exception {
+		db.close();
+		super.tearDown();
+	}
+
 	/**
 	 * Represent the state of the index in one String. This representation is
 	 * useful when writing tests which do assertions on the state of the index.
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 2f398a0..63e9082 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.9.1.qualifier
+Bundle-Version: 5.11.0.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.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.server;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.test;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.server;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.test;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.rules;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index a4f7bd1..90a6365 100644
--- a/org.eclipse.jgit.lfs.server.test/pom.xml
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index cfb413c..db1bc2a 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs.server;version="5.9.1";
+Export-Package: org.eclipse.jgit.lfs.server;version="5.11.0";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="5.9.1";
+ org.eclipse.jgit.lfs.server.fs;version="5.11.0";
   uses:="javax.servlet,
    javax.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="5.9.1";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="5.9.1";
+ org.eclipse.jgit.lfs.server.internal;version="5.11.0";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="5.11.0";
   uses:="org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -25,15 +25,15 @@
  javax.servlet.http;version="[3.1.0,4.0.0)",
  org.apache.http;version="[4.3.0,5.0.0)",
  org.apache.http.client;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.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 708a62d..96752f7 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index abb2891..290587c 100644
--- a/org.eclipse.jgit.lfs.server/pom.xml
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server</artifactId>
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 3dd5840..b853c6a 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
@@ -51,8 +51,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index 0d02971..b4c892e 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.runner;version="[4.13,5.0.0)",
  org.junit.runners;version="[4.13,5.0.0)"
-Export-Package: org.eclipse.jgit.lfs.test;version="5.9.1";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="5.11.0";x-friends:="org.eclipse.jgit.lfs.server.test"
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index 22fd3a7..0a8126e 100644
--- a/org.eclipse.jgit.lfs.test/pom.xml
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index c7a3b84..84985e8 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs;version="5.9.1",
- org.eclipse.jgit.lfs.errors;version="5.9.1",
- org.eclipse.jgit.lfs.internal;version="5.9.1";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.9.1"
+Export-Package: org.eclipse.jgit.lfs;version="5.11.0",
+ org.eclipse.jgit.lfs.errors;version="5.11.0",
+ org.eclipse.jgit.lfs.internal;version="5.11.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="5.11.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
  com.google.gson.stream;version="[2.8.2,3.0.0)",
  org.apache.http.impl.client;version="[4.2.6,5.0.0)",
  org.apache.http.impl.conn;version="[4.2.6,5.0.0)",
- org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.attributes;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.diff;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.hooks;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.pack;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.io;version="[5.9.1,5.10.0)"
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.attributes;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.diff;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.hooks;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.pack;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.io;version="[5.11.0,5.12.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
index 59d2b7f..d501443 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 2ee2f34..d4018b2 100644
--- a/org.eclipse.jgit.lfs/pom.xml
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs</artifactId>
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 040e564..e2ba814 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.9.1.qualifier"
+      version="5.11.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -36,25 +36,4 @@
          version="0.0.0"
          unpack="false"/>
 
-   <plugin
-         id="javaewah"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.apache.commons.compress"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.slf4j.api"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
 </feature>
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 3959f9e..843da3f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
index 19f9c44..7751955 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.gpg.bc"
       label="%featureName"
-      version="5.9.1.qualifier"
+      version="5.11.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="5.9.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -34,25 +34,4 @@
          fragment="true"
          unpack="false"/>
 
-   <plugin
-         id="org.bouncycastle.bcpg"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.bouncycastle.bcpkix"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.bouncycastle.bcprov"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
index 0e48821..190b7a7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index cb41fc5..2c0ab36 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.9.1.qualifier"
+      version="5.11.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="5.9.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -33,25 +33,4 @@
          version="0.0.0"
          unpack="false"/>
 
-   <plugin
-         id="org.apache.httpcomponents.httpcore"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.apache.httpcomponents.httpclient"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.apache.commons.codec"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
 </feature>
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 277e1f5..ad8ff86 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
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 7ccc894..0c9bae6 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.9.1.qualifier"
+      version="5.11.0.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.9.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
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 21bb3c8..ba1c873 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
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index 8077793..148a365 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.9.1.qualifier"
+      version="5.11.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="5.9.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -40,11 +40,4 @@
          version="0.0.0"
          unpack="false"/>
 
-   <plugin
-         id="com.google.gson"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
 </feature>
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 a104305..2cbfbd1 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
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index fa24b82..a758e39 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.9.1.qualifier"
+      version="5.11.0.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.9.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="5.9.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.ssh.apache" version="5.9.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="5.11.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.ssh.apache" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -54,25 +54,4 @@
          version="0.0.0"
          unpack="false"/>
 
-   <plugin
-         id="org.apache.commons.compress"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.kohsuke.args4j"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="javaewah"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
 </feature>
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 804057e..70a4d9d 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
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index abda695..a56cf0a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -39,6 +39,138 @@
    <bundle id="org.eclipse.jgit.ui" version="0.0.0">
       <category name="JGit-additional-bundles"/>
    </bundle>
+   <bundle id="com.google.gson">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="com.google.gson.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="com.jcraft.jsch">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="com.jcraft.jsch.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="com.jcraft.jzlib">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="com.jcraft.jzlib.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="javaewah">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="javaewah.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="javax.servlet">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="javax.servlet.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="net.i2p.crypto.eddsa">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="net.i2p.crypto.eddsa.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.ant">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.ant.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.commons.codec">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.commons.codec.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.commons.compress">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.commons.compress.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.commons.logging">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.commons.logging.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.httpcomponents.httpclient">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.httpcomponents.httpclient.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.httpcomponents.httpcore">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.httpcomponents.httpcore.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.log4j">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.log4j.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.sshd.osgi">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.sshd.osgi.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.sshd.sftp">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.apache.sshd.sftp.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.bouncycastle.bcpg">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.bouncycastle.bcpg.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.bouncycastle.bcpkix">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.bouncycastle.bcpkix.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.bouncycastle.bcprov">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.bouncycastle.bcprov.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.kohsuke.args4j">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.kohsuke.args4j.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.slf4j.api">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.slf4j.api.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.slf4j.binding.log4j12">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.slf4j.binding.log4j12.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.tukaani.xz">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
+   <bundle id="org.tukaani.xz.source">
+      <category name="JGit-dependency-bundles"/>
+   </bundle>
    <category-def name="JGit" label="Java implementation of Git">
       <description>
          Java implementation of Git
@@ -49,4 +181,5 @@
          Java implementation of Git - additional bundles
       </description>
    </category-def>
+   <category-def name="JGit-dependency-bundles" label="JGit dependency bundles"/>
 </site>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index 7cb65b3..296cdd1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
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 1cf7eef..acdbf3c 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.9.1.qualifier"
+      version="5.11.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="5.9.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
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 4f63e6a..a7c9b97 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
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
@@ -30,7 +30,7 @@
     <dependency>
       <groupId>org.eclipse.jgit.feature</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>5.9.1-SNAPSHOT</version>
+      <version>5.11.0-SNAPSHOT</version>
     </dependency>
   </dependencies>
 
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 4bd9e96..2cab8ee 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.9.1.qualifier"
+      version="5.11.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="5.9.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -33,25 +33,4 @@
          version="0.0.0"
          unpack="false"/>
 
-   <plugin
-         id="org.apache.sshd.osgi"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.apache.sshd.sftp"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="net.i2p.crypto.eddsa"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
index 0d1e81a..d9b12a4 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
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
index ed5c6a7..8e6b8bb 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.ssh.jsch"
       label="%featureName"
-      version="5.9.1.qualifier"
+      version="5.11.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import plugin="org.eclipse.jgit" version="5.9.1" match="equivalent"/>
+      <import plugin="org.eclipse.jgit" version="5.11.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -34,18 +34,4 @@
          fragment="true"
          unpack="false"/>
 
-   <plugin
-         id="com.jcraft.jsch"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="com.jcraft.jzlib"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
index 3b2a45d..a8eba20 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-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 fd90f99..8b9500e 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.9.1.qualifier
+Bundle-Version: 5.11.0.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 ad0816c..d7e1585 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,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.10" sequenceNumber="1598915260">
+<target name="jgit-4.10" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 c0a2db8..d84f801 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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 75f893f..42ef49c 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,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.11" sequenceNumber="1598915260">
+<target name="jgit-4.11" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 de349bd..df221e6 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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 2afbcb6..17634cf 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,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.12" sequenceNumber="1598915260">
+<target name="jgit-4.12" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 4243073..56fd714 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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 34c497a..04b536e 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,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.13" sequenceNumber="1598915260">
+<target name="jgit-4.13" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 d886fd4..46069c3 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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
index 33bfc43..c1722ff 100644
--- 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
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.14" sequenceNumber="1598915258">
+<target name="jgit-4.14" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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.14.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.tpd
index eb92809..f517ab2 100644
--- 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
@@ -1,7 +1,7 @@
 target "jgit-4.14" with source configurePhase
 
 include "projects/jetty-9.4.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-12.tpd"
 
 location "https://download.eclipse.org/releases/2019-12/201912181000/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
index 03b5cb7..7f5f5a1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.15" sequenceNumber="1598915258">
+<target name="jgit-4.15" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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.15.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd
index 95249b4..1e4a0ee 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.15" with source configurePhase
 
 include "projects/jetty-9.4.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-12.tpd"
 
 location "https://download.eclipse.org/releases/2020-03/202003181000/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
index a7470c1..bba090a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.16" sequenceNumber="1598915255">
+<target name="jgit-4.16" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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.16.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd
index 7e6caac..2435c48 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.tpd
@@ -1,7 +1,7 @@
 target "jgit-4.16" with source configurePhase
 
 include "projects/jetty-9.4.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-12.tpd"
 
 location "https://download.eclipse.org/releases/2020-06/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.tpd
deleted file mode 100644
index 07745cb..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.17-staging" with source configurePhase
-
-include "projects/jetty-9.4.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
-
-location "https://download.eclipse.org/staging/2020-09/" {
-	org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
similarity index 90%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 79e0211..d7c1728 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.17-staging" sequenceNumber="1598916122">
+<target name="jgit-4.17" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,17 +78,17 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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/staging/2020-09/"/>
+      <repository location="https://download.eclipse.org/releases/2020-09/"/>
     </location>
   </locations>
 </target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
new file mode 100644
index 0000000..4327415
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.17" with source configurePhase
+
+include "projects/jetty-9.4.x.tpd"
+include "orbit/R20201130205003-2020-12.tpd"
+
+location "https://download.eclipse.org/releases/2020-09/" {
+	org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.target
similarity index 91%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.target
index 79e0211..913009d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.17-staging" sequenceNumber="1598916122">
+<target name="jgit-4.18-staging" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,17 +78,17 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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/staging/2020-09/"/>
+      <repository location="https://download.eclipse.org/staging/2020-12/"/>
     </location>
   </locations>
 </target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.tpd
new file mode 100644
index 0000000..d8e3445
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18-staging.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.18-staging" with source configurePhase
+
+include "projects/jetty-9.4.x.tpd"
+include "orbit/R20201130205003-2020-12.tpd"
+
+location "https://download.eclipse.org/staging/2020-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 566ef33..061dcf2 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.6" sequenceNumber="1598915275">
+<target name="jgit-4.6" sequenceNumber="1606854620">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 d0024b6..c7fbf02 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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 5a690e8..b96c54d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.7" sequenceNumber="1598915265">
+<target name="jgit-4.7" sequenceNumber="1606854608">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 ee9253f..585e04b 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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 fe237d0..3ddc0bc 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.8" sequenceNumber="1598915261">
+<target name="jgit-4.8" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 36c42f8..694fc67 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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 a812e7a..f119fc1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.9" sequenceNumber="1598915260">
+<target name="jgit-4.9" sequenceNumber="1606854603">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.30.v20200611"/>
@@ -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.8.v20200515-1239"/>
-      <unit id="org.apache.ant.source" version="1.10.8.v20200515-1239"/>
+      <unit id="org.apache.ant" version="1.10.9.v20201106-1946"/>
+      <unit id="org.apache.ant.source" version="1.10.9.v20201106-1946"/>
       <unit id="org.apache.commons.codec" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.codec.source" version="1.14.0.v20200818-1422"/>
       <unit id="org.apache.commons.compress" version="1.19.0.v20200106-2343"/>
@@ -78,13 +78,13 @@
       <unit id="org.mockito.source" version="2.23.0.v20200310-1642"/>
       <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.slf4j.api" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.api.source" version="1.7.30.v20200204-2150"/>
+      <unit id="org.slf4j.binding.log4j12" version="1.7.30.v20201108-2042"/>
+      <unit id="org.slf4j.binding.log4j12.source" version="1.7.30.v20201108-2042"/>
       <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/R20200831200620/repository"/>
+      <repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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 da2ea7e..ae5390c 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.x.tpd"
-include "orbit/R20200831200620-2020-09.tpd"
+include "orbit/R20201130205003-2020-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/R20201130205003-2020-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
new file mode 100644
index 0000000..08a0846
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
@@ -0,0 +1,66 @@
+target "R20201130205003-2020-12" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/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.7.v20200107-0831,1.1.7.v20200107-0831]
+	javaewah.source [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
+	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.9.v20201106-1946,1.10.9.v20201106-1946]
+	org.apache.ant.source [1.10.9.v20201106-1946,1.10.9.v20201106-1946]
+	org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
+	org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
+	org.apache.commons.compress [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
+	org.apache.commons.compress.source [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
+	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.10.v20200830-2311,4.5.10.v20200830-2311]
+	org.apache.httpcomponents.httpclient.source [4.5.10.v20200830-2311,4.5.10.v20200830-2311]
+	org.apache.httpcomponents.httpcore [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
+	org.apache.httpcomponents.httpcore.source [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
+	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.4.0.v20200318-1614,2.4.0.v20200318-1614]
+	org.apache.sshd.osgi.source [2.4.0.v20200318-1614,2.4.0.v20200318-1614]
+	org.apache.sshd.sftp [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
+	org.apache.sshd.sftp.source [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
+	org.assertj [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
+	org.assertj.source [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
+	org.bouncycastle.bcpg [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+	org.bouncycastle.bcpg.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+	org.bouncycastle.bcpkix [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+	org.bouncycastle.bcpkix.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
+	org.bouncycastle.bcprov [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
+	org.bouncycastle.bcprov.source [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
+	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.13.0.v20200204-1500,4.13.0.v20200204-1500]
+	org.junit.source [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
+	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.v20200310-1642,2.23.0.v20200310-1642]
+	org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
+	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.30.v20200204-2150,1.7.30.v20200204-2150]
+	org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
+	org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
+	org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
+	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 020580d..fff8cd3 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
@@ -16,7 +16,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 935d9c5..6c77a55 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -16,7 +16,7 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>5.9.1-SNAPSHOT</version>
+  <version>5.11.0-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 3dd5840..b853c6a 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
@@ -51,8 +51,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 5ad5180..e5b0456 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.diff;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.dircache;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="5.9.1",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.merge;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.pgm;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.pgm.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.pgm.opt;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.io;version="[5.9.1,5.10.0)",
+Import-Package: org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.diff;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.dircache;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="5.11.0",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.merge;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.pgm;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.pgm.opt;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.io;version="[5.11.0,5.12.0)",
  org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.rules;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index db4e93e..5874457 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/LsRemoteTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/LsRemoteTest.java
index 4ecaeb6..46eec74 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/LsRemoteTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/LsRemoteTest.java
@@ -33,7 +33,7 @@
 		git.add().addFilepattern("Test.txt").call();
 		git.commit().setMessage("Initial commit").call();
 
-		// create a master branch and switch to it
+		// create a test branch and switch to it
 		git.branchCreate().setName("test").call();
 		RefUpdate rup = db.updateRef(Constants.HEAD);
 		rup.link("refs/heads/test");
@@ -104,4 +104,22 @@
 				"" }, result.toArray());
 	}
 
+	@Test
+	public void testLsRemoteSymRefs() throws Exception {
+		final List<String> result = CLIGitCommand.execute(
+				"git ls-remote --symref " + shellQuote(db.getDirectory()), db);
+		assertArrayEquals(new String[] {
+				"ref: refs/heads/test	HEAD",
+				"d0b1ef2b3dea02bb2ca824445c04e6def012c32c	HEAD",
+				"d0b1ef2b3dea02bb2ca824445c04e6def012c32c	refs/heads/master",
+				"d0b1ef2b3dea02bb2ca824445c04e6def012c32c	refs/heads/test",
+				"efc02078d83a5226986ae917323acec7e1e8b7cb	refs/tags/tag1",
+				"d0b1ef2b3dea02bb2ca824445c04e6def012c32c	refs/tags/tag1^{}",
+				"4e4b837e0fd4ba83c003678b03592dc1509a4115	refs/tags/tag2",
+				"d0b1ef2b3dea02bb2ca824445c04e6def012c32c	refs/tags/tag2^{}",
+				"489384bf8ace47522fe32093d2ceb85b65a6cbb1	refs/tags/tag3",
+				"d0b1ef2b3dea02bb2ca824445c04e6def012c32c	refs/tags/tag3^{}",
+				"" }, result.toArray());
+	}
+
 }
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 218f568..6a20396 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -13,48 +13,48 @@
  org.eclipse.jetty.servlet;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util;version="[9.4.5,10.0.0)",
  org.eclipse.jetty.util.component;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.api;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.archive;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.awtui;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.blame;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.diff;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.dircache;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.gitrepo;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.ketch;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.server;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.server.fs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs.server.s3;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.merge;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.notes;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revplot;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.pack;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http.apache;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.resolver;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.sshd;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.io;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.archive;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.awtui;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.blame;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.diff;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.dircache;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.gitrepo;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.ketch;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.server;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.merge;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.notes;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revplot;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.pack;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http.apache;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.io;version="[5.11.0,5.12.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.9.1";
+Export-Package: org.eclipse.jgit.console;version="5.11.0";
  uses:="org.eclipse.jgit.transport,
   org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="5.9.1";
+ org.eclipse.jgit.pgm;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util.io,
    org.eclipse.jgit.awtui,
@@ -66,14 +66,14 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.api,
    javax.swing",
- org.eclipse.jgit.pgm.debug;version="5.9.1";
+ org.eclipse.jgit.pgm.debug;version="5.11.0";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm,
    org.eclipse.jetty.servlet",
- org.eclipse.jgit.pgm.internal;version="5.9.1";
+ org.eclipse.jgit.pgm.internal;version="5.11.0";
   x-friends:="org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="5.9.1";
+ org.eclipse.jgit.pgm.opt;version="5.11.0";
   uses:="org.kohsuke.args4j,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 4df8f90..b24adcd 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 0c26505..ba08b75 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index f3e1fe3..bf24552 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
@@ -143,6 +143,7 @@
 metaVar_s3StorageClass=STORAGE-CLASS
 metaVar_seconds=SECONDS
 metaVar_service=SERVICE
+metaVar_tagLocalUser=<GPG key ID>
 metaVar_treeish=tree-ish
 metaVar_uriish=uri-ish
 metaVar_url=URL
@@ -256,6 +257,7 @@
 usage_LsRemote=List references in a remote repository
 usage_lsRemoteHeads=Show only refs starting with refs/heads
 usage_lsRemoteTags=Show only refs starting with refs/tags
+usage_lsRemoteSymref=In addition to the object pointed at, show the underlying ref pointed at when showing a symbolic ref.
 usage_LsTree=List the contents of a tree object
 usage_MakeCacheTree=Show the current cache tree structure
 usage_Match=Only consider tags matching the given glob(7) pattern or patterns, excluding the "refs/tags/" prefix.
@@ -294,6 +296,7 @@
 usage_StopTrackingAFile=Stop tracking a file
 usage_TextHashFunctions=Scan repository to compute maximum number of collisions for hash functions
 usage_UpdateRemoteRepositoryFromLocalRefs=Update remote repository from local refs
+usage_UseAll=Use all refs found in refs/
 usage_UseTags=Use any tag including lightweight tags
 usage_WriteDirCache=Write the DirCache
 usage_abbrevCommits=abbreviate commits to N + 1 digits
@@ -419,8 +422,12 @@
 usage_symbolicVersionForTheProject=Symbolic version for the project
 usage_tags=fetch all tags
 usage_notags=do not fetch tags
+usage_tagAnnotated=create an annotated tag, unsigned unless -s or -u are given, or config tag.gpgSign is true
 usage_tagDelete=delete tag
-usage_tagMessage=tag message
+usage_tagLocalUser=create a signed annotated tag using the specified GPG key ID
+usage_tagMessage=create an annotated tag with the given message, unsigned unless -s or -u are given, or config tag.gpgSign is true, or tar.forceSignAnnotated is true and -a is not given
+usage_tagSign=create a signed annotated tag
+usage_tagNoSign=suppress signing the tag
 usage_untrackedFilesMode=show untracked files
 usage_updateRef=reference to update
 usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
index 610b647..8aa119a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
@@ -32,6 +32,9 @@
 	@Option(name = "--long", usage = "usage_LongFormat")
 	private boolean longDesc;
 
+	@Option(name = "--all", usage = "usage_UseTags")
+	private boolean useAll;
+
 	@Option(name = "--tags", usage = "usage_UseTags")
 	private boolean useTags;
 
@@ -50,6 +53,7 @@
 				cmd.setTarget(tree);
 			}
 			cmd.setLong(longDesc);
+			cmd.setAll(useAll);
 			cmd.setTags(useTags);
 			cmd.setAlways(always);
 			cmd.setMatch(patterns.toArray(new String[0]));
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
index 36812c0..055b48a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
@@ -34,6 +34,9 @@
 	@Option(name = "--timeout", metaVar = "metaVar_service", usage = "usage_abortConnectionIfNoActivity")
 	int timeout = -1;
 
+	@Option(name = "--symref", usage = "usage_lsRemoteSymref")
+	private boolean symref;
+
 	@Argument(index = 0, metaVar = "metaVar_uriish", required = true)
 	private String remote;
 
@@ -47,6 +50,9 @@
 		try {
 			refs.addAll(command.call());
 			for (Ref r : refs) {
+				if (symref && r.isSymbolic()) {
+					show(r.getTarget(), r.getName());
+				}
 				show(r.getObjectId(), r.getName());
 				if (r.getPeeledObjectId() != null) {
 					show(r.getPeeledObjectId(), r.getName() + "^{}"); //$NON-NLS-1$
@@ -70,4 +76,13 @@
 		outw.print(name);
 		outw.println();
 	}
+
+	private void show(Ref ref, String name)
+			throws IOException {
+		outw.print("ref: ");
+		outw.print(ref.getName());
+		outw.print('\t');
+		outw.print(name);
+		outw.println();
+	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index b408b78..4cc62b3 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
@@ -4,7 +4,7 @@
  * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg.lists@dewire.com>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 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 v. 1.0 which is available at
@@ -40,8 +40,24 @@
 	@Option(name = "-d", usage = "usage_tagDelete")
 	private boolean delete;
 
+	@Option(name = "--annotate", aliases = {
+			"-a" }, usage = "usage_tagAnnotated")
+	private boolean annotated;
+
 	@Option(name = "-m", metaVar = "metaVar_message", usage = "usage_tagMessage")
-	private String message = ""; //$NON-NLS-1$
+	private String message;
+
+	@Option(name = "--sign", aliases = { "-s" }, forbids = {
+			"--no-sign" }, usage = "usage_tagSign")
+	private boolean sign;
+
+	@Option(name = "--no-sign", usage = "usage_tagNoSign", forbids = {
+			"--sign" })
+	private boolean noSign;
+
+	@Option(name = "--local-user", aliases = {
+			"-u" }, metaVar = "metaVar_tagLocalUser", usage = "usage_tagLocalUser")
+	private String gpgKeyId;
 
 	@Argument(index = 0, metaVar = "metaVar_name")
 	private String tagName;
@@ -70,6 +86,18 @@
 							command.setObjectId(walk.parseAny(object));
 						}
 					}
+					if (noSign) {
+						command.setSigned(false);
+					} else if (sign) {
+						command.setSigned(true);
+					}
+					if (annotated) {
+						command.setAnnotated(true);
+					} else if (message == null && !sign && gpgKeyId == null) {
+						// None of -a, -m, -s, -u given
+						command.setAnnotated(false);
+					}
+					command.setSigningKey(gpgKeyId);
 					try {
 						command.call();
 					} catch (RefAlreadyExistsException e) {
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 822846c..cba893f 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
@@ -51,8 +51,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
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 5c9ce9c..e959242 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -11,20 +11,22 @@
  org.apache.sshd.common;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.auth;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.config.keys;version="[2.4.0,2.5.0)",
+ org.apache.sshd.common.helpers;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.keyprovider;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.session;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.util.net;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.util.security;version="[2.4.0,2.5.0)",
  org.apache.sshd.server;version="[2.4.0,2.5.0)",
- org.eclipse.jgit.api;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit.ssh;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.sshd;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.apache.sshd.server.forward;version="[2.4.0,2.5.0)",
+ org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.sshd;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
  org.junit.runner;version="[4.13,5.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml
index df319ad..bf8f61c 100644
--- a/org.eclipse.jgit.ssh.apache.test/pom.xml
+++ b/org.eclipse.jgit.ssh.apache.test/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
index 651ae7d..3427da6 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2020 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
@@ -11,19 +11,39 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import java.io.BufferedWriter;
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
+
 import org.apache.sshd.client.config.hosts.KnownHostEntry;
+import org.apache.sshd.client.config.hosts.KnownHostHashValue;
 import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.forward.StaticDecisionForwardingFilter;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.TransportException;
 import org.eclipse.jgit.junit.ssh.SshTestBase;
@@ -211,4 +231,380 @@
 			git.fetch().call();
 		}
 	}
+
+	/**
+	 * Creates a simple proxy server. Accepts only publickey authentication from
+	 * the given user with the given key, allows all forwardings. Adds the
+	 * proxy's host key to {@link #knownHosts}.
+	 *
+	 * @param user
+	 *            to accept
+	 * @param userKey
+	 *            public key of that user at this server
+	 * @param report
+	 *            single-element array to report back the forwarded address.
+	 * @return the started server
+	 * @throws Exception
+	 */
+	private SshServer createProxy(String user, File userKey,
+			SshdSocketAddress[] report) throws Exception {
+		SshServer proxy = SshServer.setUpDefaultServer();
+		// Give the server its own host key
+		KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
+		generator.initialize(2048);
+		KeyPair proxyHostKey = generator.generateKeyPair();
+		proxy.setKeyPairProvider(
+				session -> Collections.singletonList(proxyHostKey));
+		// Allow (only) publickey authentication
+		proxy.setUserAuthFactories(Collections.singletonList(
+				ServerAuthenticationManager.DEFAULT_USER_AUTH_PUBLIC_KEY_FACTORY));
+		// Install the user's public key
+		PublicKey userProxyKey = AuthorizedKeyEntry
+				.readAuthorizedKeys(userKey.toPath()).get(0)
+				.resolvePublicKey(null, PublicKeyEntryResolver.IGNORING);
+		proxy.setPublickeyAuthenticator(
+				(userName, publicKey, session) -> user.equals(userName)
+						&& KeyUtils.compareKeys(userProxyKey, publicKey));
+		// Allow forwarding
+		proxy.setForwardingFilter(new StaticDecisionForwardingFilter(true) {
+
+			@Override
+			protected boolean checkAcceptance(String request, Session session,
+					SshdSocketAddress target) {
+				report[0] = target;
+				return super.checkAcceptance(request, session, target);
+			}
+		});
+		proxy.start();
+		// Add the proxy's host key to knownhosts
+		try (BufferedWriter writer = Files.newBufferedWriter(
+				knownHosts.toPath(), StandardCharsets.US_ASCII,
+				StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
+			writer.append('\n');
+			KnownHostHashValue.appendHostPattern(writer, "localhost",
+					proxy.getPort());
+			writer.append(',');
+			KnownHostHashValue.appendHostPattern(writer, "127.0.0.1",
+					proxy.getPort());
+			writer.append(' ');
+			PublicKeyEntry.appendPublicKeyEntry(writer,
+					proxyHostKey.getPublic());
+			writer.append('\n');
+		}
+		return proxy;
+	}
+
+	@Test
+	public void testJumpHost() throws Exception {
+		SshdSocketAddress[] forwarded = { null };
+		try (SshServer proxy = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded)) {
+			try {
+				// Now try to clone via the proxy
+				cloneWith("ssh://server/doesntmatter", defaultCloneDir, null, //
+						"Host server", //
+						"HostName localhost", //
+						"Port " + testPort, //
+						"User " + TEST_USER, //
+						"IdentityFile " + privateKey1.getAbsolutePath(), //
+						"ProxyJump " + TEST_USER + "X@proxy:" + proxy.getPort(), //
+						"", //
+						"Host proxy", //
+						"Hostname localhost", //
+						"IdentityFile " + privateKey2.getAbsolutePath());
+				assertNotNull(forwarded[0]);
+				assertEquals(testPort, forwarded[0].getPort());
+			} finally {
+				proxy.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostWrongKeyAtProxy() throws Exception {
+		// Test that we find the proxy server's URI in the exception message
+		SshdSocketAddress[] forwarded = { null };
+		try (SshServer proxy = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded)) {
+			try {
+				// Now try to clone via the proxy
+				TransportException e = assertThrows(TransportException.class,
+						() -> cloneWith("ssh://server/doesntmatter",
+								defaultCloneDir, null, //
+								"Host server", //
+								"HostName localhost", //
+								"Port " + testPort, //
+								"User " + TEST_USER, //
+								"IdentityFile " + privateKey1.getAbsolutePath(),
+								"ProxyJump " + TEST_USER + "X@proxy:"
+										+ proxy.getPort(), //
+								"", //
+								"Host proxy", //
+								"Hostname localhost", //
+								"IdentityFile "
+										+ privateKey1.getAbsolutePath()));
+				String message = e.getMessage();
+				assertTrue(message.contains("localhost:" + proxy.getPort()));
+				assertTrue(message.contains("proxy:" + proxy.getPort()));
+			} finally {
+				proxy.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostWrongKeyAtServer() throws Exception {
+		// Test that we find the target server's URI in the exception message
+		SshdSocketAddress[] forwarded = { null };
+		try (SshServer proxy = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded)) {
+			try {
+				// Now try to clone via the proxy
+				TransportException e = assertThrows(TransportException.class,
+						() -> cloneWith("ssh://server/doesntmatter",
+								defaultCloneDir, null, //
+								"Host server", //
+								"HostName localhost", //
+								"Port " + testPort, //
+								"User " + TEST_USER, //
+								"IdentityFile " + privateKey2.getAbsolutePath(),
+								"ProxyJump " + TEST_USER + "X@proxy:"
+										+ proxy.getPort(), //
+								"", //
+								"Host proxy", //
+								"Hostname localhost", //
+								"IdentityFile "
+										+ privateKey2.getAbsolutePath()));
+				String message = e.getMessage();
+				assertTrue(message.contains("localhost:" + testPort));
+				assertTrue(message.contains("ssh://server"));
+			} finally {
+				proxy.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostNonSsh() throws Exception {
+		SshdSocketAddress[] forwarded = { null };
+		try (SshServer proxy = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded)) {
+			try {
+				TransportException e = assertThrows(TransportException.class,
+						() -> cloneWith("ssh://server/doesntmatter",
+								defaultCloneDir, null, //
+								"Host server", //
+								"HostName localhost", //
+								"Port " + testPort, //
+								"User " + TEST_USER, //
+								"IdentityFile " + privateKey1.getAbsolutePath(), //
+								"ProxyJump http://" + TEST_USER + "X@proxy:"
+										+ proxy.getPort(), //
+								"", //
+								"Host proxy", //
+								"Hostname localhost", //
+								"IdentityFile "
+										+ privateKey2.getAbsolutePath()));
+				// Find the expected message
+				Throwable t = e;
+				while (t != null) {
+					if (t instanceof URISyntaxException) {
+						break;
+					}
+					t = t.getCause();
+				}
+				assertNotNull(t);
+				assertTrue(t.getMessage().contains("Non-ssh"));
+			} finally {
+				proxy.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostWithPath() throws Exception {
+		SshdSocketAddress[] forwarded = { null };
+		try (SshServer proxy = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded)) {
+			try {
+				TransportException e = assertThrows(TransportException.class,
+						() -> cloneWith("ssh://server/doesntmatter",
+								defaultCloneDir, null, //
+								"Host server", //
+								"HostName localhost", //
+								"Port " + testPort, //
+								"User " + TEST_USER, //
+								"IdentityFile " + privateKey1.getAbsolutePath(), //
+								"ProxyJump ssh://" + TEST_USER + "X@proxy:"
+										+ proxy.getPort() + "/wrongPath", //
+								"", //
+								"Host proxy", //
+								"Hostname localhost", //
+								"IdentityFile "
+										+ privateKey2.getAbsolutePath()));
+				// Find the expected message
+				Throwable t = e;
+				while (t != null) {
+					if (t instanceof URISyntaxException) {
+						break;
+					}
+					t = t.getCause();
+				}
+				assertNotNull(t);
+				assertTrue(t.getMessage().contains("wrongPath"));
+			} finally {
+				proxy.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostWithPathShort() throws Exception {
+		SshdSocketAddress[] forwarded = { null };
+		try (SshServer proxy = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded)) {
+			try {
+				TransportException e = assertThrows(TransportException.class,
+						() -> cloneWith("ssh://server/doesntmatter",
+								defaultCloneDir, null, //
+								"Host server", //
+								"HostName localhost", //
+								"Port " + testPort, //
+								"User " + TEST_USER, //
+								"IdentityFile " + privateKey1.getAbsolutePath(), //
+								"ProxyJump " + TEST_USER + "X@proxy:wrongPath", //
+								"", //
+								"Host proxy", //
+								"Hostname localhost", //
+								"Port " + proxy.getPort(), //
+								"IdentityFile "
+										+ privateKey2.getAbsolutePath()));
+				// Find the expected message
+				Throwable t = e;
+				while (t != null) {
+					if (t instanceof URISyntaxException) {
+						break;
+					}
+					t = t.getCause();
+				}
+				assertNotNull(t);
+				assertTrue(t.getMessage().contains("wrongPath"));
+			} finally {
+				proxy.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostChain() throws Exception {
+		SshdSocketAddress[] forwarded1 = { null };
+		SshdSocketAddress[] forwarded2 = { null };
+		try (SshServer proxy1 = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded1);
+				SshServer proxy2 = createProxy("foo", publicKey1, forwarded2)) {
+			try {
+				// Clone proxy1 -> proxy2 -> server
+				cloneWith("ssh://server/doesntmatter", defaultCloneDir, null, //
+						"Host server", //
+						"HostName localhost", //
+						"Port " + testPort, //
+						"User " + TEST_USER, //
+						"IdentityFile " + privateKey1.getAbsolutePath(), //
+						"ProxyJump proxy2," + TEST_USER + "X@proxy:"
+								+ proxy1.getPort(), //
+						"", //
+						"Host proxy", //
+						"Hostname localhost", //
+						"IdentityFile " + privateKey2.getAbsolutePath(), //
+						"", //
+						"Host proxy2", //
+						"Hostname localhost", //
+						"User foo", //
+						"Port " + proxy2.getPort(), //
+						"IdentityFile " + privateKey1.getAbsolutePath());
+				assertNotNull(forwarded1[0]);
+				assertEquals(proxy2.getPort(), forwarded1[0].getPort());
+				assertNotNull(forwarded2[0]);
+				assertEquals(testPort, forwarded2[0].getPort());
+			} finally {
+				proxy1.stop();
+				proxy2.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostCascade() throws Exception {
+		SshdSocketAddress[] forwarded1 = { null };
+		SshdSocketAddress[] forwarded2 = { null };
+		try (SshServer proxy1 = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded1);
+				SshServer proxy2 = createProxy("foo", publicKey1, forwarded2)) {
+			try {
+				// Clone proxy2 -> proxy1 -> server
+				cloneWith("ssh://server/doesntmatter", defaultCloneDir, null, //
+						"Host server", //
+						"HostName localhost", //
+						"Port " + testPort, //
+						"User " + TEST_USER, //
+						"IdentityFile " + privateKey1.getAbsolutePath(), //
+						"ProxyJump " + TEST_USER + "X@proxy", //
+						"", //
+						"Host proxy", //
+						"Hostname localhost", //
+						"Port " + proxy1.getPort(), //
+						"ProxyJump ssh://proxy2:" + proxy2.getPort(), //
+						"IdentityFile " + privateKey2.getAbsolutePath(), //
+						"", //
+						"Host proxy2", //
+						"Hostname localhost", //
+						"User foo", //
+						"IdentityFile " + privateKey1.getAbsolutePath());
+				assertNotNull(forwarded1[0]);
+				assertEquals(testPort, forwarded1[0].getPort());
+				assertNotNull(forwarded2[0]);
+				assertEquals(proxy1.getPort(), forwarded2[0].getPort());
+			} finally {
+				proxy1.stop();
+				proxy2.stop();
+			}
+		}
+	}
+
+	@Test
+	public void testJumpHostRecursion() throws Exception {
+		SshdSocketAddress[] forwarded1 = { null };
+		SshdSocketAddress[] forwarded2 = { null };
+		try (SshServer proxy1 = createProxy(TEST_USER + 'X', publicKey2,
+				forwarded1);
+				SshServer proxy2 = createProxy("foo", publicKey1, forwarded2)) {
+			try {
+				TransportException e = assertThrows(TransportException.class,
+						() -> cloneWith(
+						"ssh://server/doesntmatter", defaultCloneDir, null, //
+						"Host server", //
+						"HostName localhost", //
+						"Port " + testPort, //
+						"User " + TEST_USER, //
+						"IdentityFile " + privateKey1.getAbsolutePath(), //
+						"ProxyJump " + TEST_USER + "X@proxy", //
+						"", //
+						"Host proxy", //
+						"Hostname localhost", //
+						"Port " + proxy1.getPort(), //
+						"ProxyJump ssh://proxy2:" + proxy2.getPort(), //
+						"IdentityFile " + privateKey2.getAbsolutePath(), //
+						"", //
+						"Host proxy2", //
+						"Hostname localhost", //
+						"User foo", //
+						"ProxyJump " + TEST_USER + "X@proxy", //
+						"IdentityFile " + privateKey1.getAbsolutePath()));
+				assertTrue(e.getMessage().contains("proxy"));
+			} finally {
+				proxy1.stop();
+				proxy2.stop();
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 44265b3..59eafa4 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.9.1";x-internal:=true;
+Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.11.0";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.9.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="5.9.1";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="5.9.1";
+ org.eclipse.jgit.internal.transport.sshd.auth;version="5.11.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="5.11.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.transport.sshd;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.apache.sshd.client.config.hosts,
    org.apache.sshd.common.keyprovider,
@@ -45,6 +45,7 @@
  org.apache.sshd.client.future;version="[2.4.0,2.5.0)",
  org.apache.sshd.client.keyverifier;version="[2.4.0,2.5.0)",
  org.apache.sshd.client.session;version="[2.4.0,2.5.0)",
+ org.apache.sshd.client.session.forward;version="[2.4.0,2.5.0)",
  org.apache.sshd.client.subsystem.sftp;version="[2.4.0,2.5.0)",
  org.apache.sshd.common;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.auth;version="[2.4.0,2.5.0)",
@@ -75,12 +76,12 @@
  org.apache.sshd.common.util.net;version="[2.4.0,2.5.0)",
  org.apache.sshd.common.util.security;version="[2.4.0,2.5.0)",
  org.apache.sshd.server.auth;version="[2.4.0,2.5.0)",
- org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.fnmatch;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.fnmatch;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.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 c05bd95..f4a31a4 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index e995200..9d57e8c 100644
--- a/org.eclipse.jgit.ssh.apache/pom.xml
+++ b/org.eclipse.jgit.ssh.apache/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.apache</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
index b89bc60..f810fd4 100644
--- a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
+++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
@@ -1,11 +1,14 @@
-authenticationCanceled=Authentication canceled: no password
+authenticationCanceled=SSH authentication canceled: no password given
 authenticationOnClosedSession=Authentication canceled: session is already closing or closed
 closeListenerFailed=Ssh session close listener failed
 configInvalidPath=Invalid path in ssh config key {0}: {1}
 configInvalidPattern=Invalid pattern in ssh config key {0}: {1}
 configInvalidPositive=Ssh config entry {0} must be a strictly positive number but is ''{1}''
+configInvalidProxyJump=Ssh config, host ''{0}'': Cannot parse ProxyJump ''{1}''
 configNoKnownHostKeyAlgorithms=No implementations for any of the algorithms ''{0}'' given in HostKeyAlgorithms in the ssh config; using the default.
 configNoRemainingHostKeyAlgorithms=Ssh config removed all host key algorithms: HostKeyAlgorithms ''{0}''
+configProxyJumpNotSsh=Non-ssh URI in ProxyJump ssh config
+configProxyJumpWithPath=ProxyJump ssh config: jump host specification must not have a path
 ftpCloseFailed=Closing the SFTP channel failed
 gssapiFailure=GSS-API error for mechanism OID {0}
 gssapiInitFailure=GSS-API initialization failure for mechanism {0}
@@ -46,12 +49,14 @@
 knownHostsUnknownKeyType=Cannot read server key from known hosts file {0}; line {1}
 knownHostsUserAskCreationMsg=File {0} does not exist.
 knownHostsUserAskCreationPrompt=Create file {0} ?
+loginDenied=Cannot log in at {0}:{1}
 passwordPrompt=Password
 proxyCannotAuthenticate=Cannot authenticate to proxy {0}
 proxyHttpFailure=HTTP Proxy connection to {0} failed with code {1}: {2}
 proxyHttpInvalidUserName=HTTP proxy connection {0} with invalid user name; must not contain colons: {1}
 proxyHttpUnexpectedReply=Unexpected HTTP proxy response from {0}: {1}
 proxyHttpUnspecifiedFailureReason=unspecified reason
+proxyJumpAbort=ProxyJump chain too long at {0}
 proxyPasswordPrompt=Proxy password
 proxySocksAuthenticationFailed=Authentication to SOCKS5 proxy {0} failed
 proxySocksFailureForbidden=SOCKS5 proxy {0}: connection to {1} not allowed by ruleset
@@ -80,4 +85,5 @@
 sshClosingDown=Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
 sshCommandTimeout={0} timed out after {1} seconds while opening the channel
 sshProcessStillRunning={0} is not yet completed, cannot get exit code
+sshProxySessionCloseFailed=Error while closing proxy session {0}
 unknownProxyProtocol=Ignoring unknown proxy protocol {0}
\ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/AuthenticationCanceledException.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/AuthenticationCanceledException.java
new file mode 100644
index 0000000..aa46235
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/AuthenticationCanceledException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020, 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import java.util.concurrent.CancellationException;
+
+/**
+ * An exception to report that the user canceled the SSH authentication.
+ */
+public class AuthenticationCanceledException extends CancellationException {
+
+	// If this is not a CancellationException sshd will try other authentication
+	// mechanisms.
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Creates a new {@link AuthenticationCanceledException}.
+	 */
+	public AuthenticationCanceledException() {
+		super(SshdText.get().authenticationCanceled);
+	}
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
index 0a7082c..4abd6e9 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java
@@ -9,8 +9,6 @@
  */
 package org.eclipse.jgit.internal.transport.sshd;
 
-import java.util.concurrent.CancellationException;
-
 import org.apache.sshd.client.ClientAuthenticationManager;
 import org.apache.sshd.client.auth.keyboard.UserInteraction;
 import org.apache.sshd.client.auth.password.UserAuthPassword;
@@ -49,7 +47,7 @@
 		}
 		String password = getPassword(session, interaction);
 		if (password == null) {
-			throw new CancellationException();
+			throw new AuthenticationCanceledException();
 		}
 		// sendPassword takes a buffer as first argument, but actually doesn't
 		// use it and creates its own buffer...
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
index 1825fb3..beaaeca 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
@@ -49,6 +49,7 @@
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.eclipse.jgit.internal.transport.sshd.JGitClientSession.ChainingAttributes;
 import org.eclipse.jgit.internal.transport.sshd.JGitClientSession.SessionAttributes;
 import org.eclipse.jgit.internal.transport.sshd.proxy.HttpClientConnector;
@@ -82,6 +83,16 @@
 	 */
 	public static final AttributeKey<String> PREFERRED_AUTHENTICATIONS = new AttributeKey<>();
 
+	/**
+	 * An attribute key for storing an alternate local address to connect to if
+	 * a local forward from a ProxyJump ssh config is present. If set,
+	 * {@link #connect(HostConfigEntry, AttributeRepository, SocketAddress)}
+	 * will not connect to the address obtained from the {@link HostConfigEntry}
+	 * but to the address stored in this key (which is assumed to forward the
+	 * {@code HostConfigEntry} address).
+	 */
+	public static final AttributeKey<SshdSocketAddress> LOCAL_FORWARD_ADDRESS = new AttributeKey<>();
+
 	private KeyCache keyCache;
 
 	private CredentialsProvider credentialsProvider;
@@ -102,25 +113,37 @@
 			throw new IllegalStateException("SshClient not started."); //$NON-NLS-1$
 		}
 		Objects.requireNonNull(hostConfig, "No host configuration"); //$NON-NLS-1$
-		String host = ValidateUtils.checkNotNullAndNotEmpty(
+		String originalHost = ValidateUtils.checkNotNullAndNotEmpty(
 				hostConfig.getHostName(), "No target host"); //$NON-NLS-1$
-		int port = hostConfig.getPort();
-		ValidateUtils.checkTrue(port > 0, "Invalid port: %d", port); //$NON-NLS-1$
+		int originalPort = hostConfig.getPort();
+		ValidateUtils.checkTrue(originalPort > 0, "Invalid port: %d", //$NON-NLS-1$
+				originalPort);
+		InetSocketAddress originalAddress = new InetSocketAddress(originalHost,
+				originalPort);
+		InetSocketAddress targetAddress = originalAddress;
 		String userName = hostConfig.getUsername();
+		String id = userName + '@' + originalAddress;
 		AttributeRepository attributes = chain(context, this);
-		InetSocketAddress address = new InetSocketAddress(host, port);
-		ConnectFuture connectFuture = new DefaultConnectFuture(
-				userName + '@' + address, null);
-		SshFutureListener<IoConnectFuture> listener = createConnectCompletionListener(
-				connectFuture, userName, address, hostConfig);
-		attributes = sessionAttributes(attributes, hostConfig, address);
-		// Proxy support
-		ProxyData proxy = getProxyData(address);
-		if (proxy != null) {
-			address = configureProxy(proxy, address);
-			proxy.clearPassword();
+		SshdSocketAddress localForward = attributes
+				.resolveAttribute(LOCAL_FORWARD_ADDRESS);
+		if (localForward != null) {
+			targetAddress = new InetSocketAddress(localForward.getHostName(),
+					localForward.getPort());
+			id += '/' + targetAddress.toString();
 		}
-		connector.connect(address, attributes, localAddress)
+		ConnectFuture connectFuture = new DefaultConnectFuture(id, null);
+		SshFutureListener<IoConnectFuture> listener = createConnectCompletionListener(
+				connectFuture, userName, originalAddress, hostConfig);
+		attributes = sessionAttributes(attributes, hostConfig, originalAddress);
+		// Proxy support
+		if (localForward == null) {
+			ProxyData proxy = getProxyData(targetAddress);
+			if (proxy != null) {
+				targetAddress = configureProxy(proxy, targetAddress);
+				proxy.clearPassword();
+			}
+		}
+		connector.connect(targetAddress, attributes, localAddress)
 				.addListener(listener);
 		return connectFuture;
 	}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
index 22966f9..13bb3eb 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
@@ -24,8 +24,11 @@
 	/***/ public String configInvalidPath;
 	/***/ public String configInvalidPattern;
 	/***/ public String configInvalidPositive;
+	/***/ public String configInvalidProxyJump;
 	/***/ public String configNoKnownHostKeyAlgorithms;
 	/***/ public String configNoRemainingHostKeyAlgorithms;
+	/***/ public String configProxyJumpNotSsh;
+	/***/ public String configProxyJumpWithPath;
 	/***/ public String ftpCloseFailed;
 	/***/ public String gssapiFailure;
 	/***/ public String gssapiInitFailure;
@@ -58,12 +61,14 @@
 	/***/ public String knownHostsUnknownKeyType;
 	/***/ public String knownHostsUserAskCreationMsg;
 	/***/ public String knownHostsUserAskCreationPrompt;
+	/***/ public String loginDenied;
 	/***/ public String passwordPrompt;
 	/***/ public String proxyCannotAuthenticate;
 	/***/ public String proxyHttpFailure;
 	/***/ public String proxyHttpInvalidUserName;
 	/***/ public String proxyHttpUnexpectedReply;
 	/***/ public String proxyHttpUnspecifiedFailureReason;
+	/***/ public String proxyJumpAbort;
 	/***/ public String proxyPasswordPrompt;
 	/***/ public String proxySocksAuthenticationFailed;
 	/***/ public String proxySocksFailureForbidden;
@@ -92,6 +97,7 @@
 	/***/ public String sshClosingDown;
 	/***/ public String sshCommandTimeout;
 	/***/ public String sshProcessStillRunning;
+	/***/ public String sshProxySessionCloseFailed;
 	/***/ public String unknownProxyProtocol;
 
 }
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 d5b8037..0500a63 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
@@ -13,6 +13,8 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.eclipse.jgit.util.HttpSupport;
+
 /**
  * A basic parser for HTTP response headers. Handles status lines and
  * authentication headers (WWW-Authenticate, Proxy-Authenticate).
@@ -135,7 +137,7 @@
 		int length = header.length();
 		for (int i = 0; i < length;) {
 			int start = skipWhiteSpace(header, i);
-			int end = scanToken(header, start);
+			int end = HttpSupport.scanToken(header, start);
 			if (end <= start) {
 				break;
 			}
@@ -156,7 +158,7 @@
 			// optional legacy whitespace around the equals sign), where the
 			// value can be either a token or a quoted string.
 			start = skipWhiteSpace(header, start);
-			int end = scanToken(header, start);
+			int end = HttpSupport.scanToken(header, start);
 			if (end == start) {
 				// Nothing found. Either at end or on a comma.
 				if (start < header.length() && header.charAt(start) == ',') {
@@ -222,7 +224,7 @@
 					challenge.addArgument(header.substring(start, end), value);
 					start = nextEnd[0];
 				} else {
-					int nextEnd = scanToken(header, nextStart);
+					int nextEnd = HttpSupport.scanToken(header, nextStart);
 					challenge.addArgument(header.substring(start, end),
 							header.substring(nextStart, nextEnd));
 					start = nextEnd;
@@ -244,49 +246,6 @@
 		return i;
 	}
 
-	private static int scanToken(String header, int from) {
-		int length = header.length();
-		int i = from;
-		while (i < length) {
-			char c = header.charAt(i);
-			switch (c) {
-			case '!':
-			case '#':
-			case '$':
-			case '%':
-			case '&':
-			case '\'':
-			case '*':
-			case '+':
-			case '-':
-			case '.':
-			case '^':
-			case '_':
-			case '`':
-			case '|':
-			case '0':
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-			case '8':
-			case '9':
-				i++;
-				break;
-			default:
-				if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
-					i++;
-					break;
-				}
-				return i;
-			}
-		}
-		return i;
-	}
-
 	private static String scanQuotedString(String header, int from, int[] to) {
 		StringBuilder result = new StringBuilder();
 		int length = header.length();
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java
index 004d3f8..dd6894b 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java
@@ -19,13 +19,14 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.CancellationException;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException;
 import org.eclipse.jgit.internal.transport.sshd.SshdText;
 import org.eclipse.jgit.transport.CredentialItem;
 import org.eclipse.jgit.transport.CredentialsProvider;
 import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.StringUtils;
 
 /**
  * A {@link KeyPasswordProvider} based on a {@link CredentialsProvider}.
@@ -155,35 +156,84 @@
 		state.incCount();
 		String message = state.count == 1 ? SshdText.get().keyEncryptedMsg
 				: SshdText.get().keyEncryptedRetry;
-		char[] pass = getPassword(uri, message);
+		char[] pass = getPassword(uri, format(message, uri));
 		state.setPassword(pass);
 		return pass;
 	}
 
-	private char[] getPassword(URIish uri, String message) {
+	/**
+	 * Retrieves the JGit {@link CredentialsProvider} to use for user
+	 * interaction.
+	 *
+	 * @return the {@link CredentialsProvider} or {@code null} if none
+	 *         configured
+	 * @since 5.10
+	 */
+	protected CredentialsProvider getCredentialsProvider() {
+		return provider;
+	}
+
+	/**
+	 * Obtains the passphrase/password for an encrypted private key via the
+	 * {@link #getCredentialsProvider() configured CredentialsProvider}.
+	 *
+	 * @param uri
+	 *            identifying the resource to obtain a password for
+	 * @param message
+	 *            optional message text to display; may be {@code null} or empty
+	 *            if none
+	 * @return the password entered, or {@code null} if no
+	 *         {@link CredentialsProvider} is configured or none was entered
+	 * @throws java.util.concurrent.CancellationException
+	 *             if the user canceled the operation
+	 * @since 5.10
+	 */
+	protected char[] getPassword(URIish uri, String message) {
 		if (provider == null) {
 			return null;
 		}
-		List<CredentialItem> items = new ArrayList<>(2);
-		items.add(new CredentialItem.InformationalMessage(
-				format(message, uri)));
+		boolean haveMessage = !StringUtils.isEmptyOrNull(message);
+		List<CredentialItem> items = new ArrayList<>(haveMessage ? 2 : 1);
+		if (haveMessage) {
+			items.add(new CredentialItem.InformationalMessage(message));
+		}
 		CredentialItem.Password password = new CredentialItem.Password(
 				SshdText.get().keyEncryptedPrompt);
 		items.add(password);
 		try {
-			provider.get(uri, items);
+			boolean completed = provider.get(uri, items);
 			char[] pass = password.getValue();
-			if (pass == null) {
-				throw new CancellationException(
-						SshdText.get().authenticationCanceled);
+			if (!completed) {
+				cancelAuthentication();
+				return null;
 			}
-			return pass.clone();
+			return pass == null ? null : pass.clone();
 		} finally {
 			password.clear();
 		}
 	}
 
 	/**
+	 * Cancels the authentication process. Called by
+	 * {@link #getPassword(URIish, String)} when the user interaction has been
+	 * canceled. If this throws a
+	 * {@link java.util.concurrent.CancellationException}, the authentication
+	 * process is aborted; otherwise it may continue with the next configured
+	 * authentication mechanism, if any.
+	 * <p>
+	 * This default implementation always throws a
+	 * {@link java.util.concurrent.CancellationException}.
+	 * </p>
+	 *
+	 * @throws java.util.concurrent.CancellationException
+	 *             always
+	 * @since 5.10
+	 */
+	protected void cancelAuthentication() {
+		throw new AuthenticationCanceledException();
+	}
+
+	/**
 	 * Invoked to inform the password provider about the decoding result.
 	 *
 	 * @param uri
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 dfd7cca..0fb0610 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2020 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
@@ -10,36 +10,53 @@
 package org.eclipse.jgit.transport.sshd;
 
 import static java.text.MessageFormat.format;
+import static org.apache.sshd.common.SshConstants.SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE;
 
+import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.URISyntaxException;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Supplier;
+import java.util.regex.Pattern;
 
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ChannelExec;
 import org.apache.sshd.client.channel.ClientChannelEvent;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.future.ConnectFuture;
 import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.forward.PortForwardingTracker;
 import org.apache.sshd.client.subsystem.sftp.SftpClient;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.CopyMode;
 import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.common.AttributeRepository;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.subsystem.sftp.SftpException;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.transport.sshd.JGitSshClient;
 import org.eclipse.jgit.internal.transport.sshd.SshdText;
 import org.eclipse.jgit.transport.FtpChannel;
 import org.eclipse.jgit.transport.RemoteSession;
+import org.eclipse.jgit.transport.SshConstants;
 import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,6 +70,11 @@
 	private static final Logger LOG = LoggerFactory
 			.getLogger(SshdSession.class);
 
+	private static final Pattern SHORT_SSH_FORMAT = Pattern
+			.compile("[-\\w.]+(?:@[-\\w.]+)?(?::\\d+)?"); //$NON-NLS-1$
+
+	private static final int MAX_DEPTH = 10;
+
 	private final CopyOnWriteArrayList<SessionCloseListener> listeners = new CopyOnWriteArrayList<>();
 
 	private final URIish uri;
@@ -71,32 +93,169 @@
 			client.start();
 		}
 		try {
-			String username = uri.getUser();
-			String host = uri.getHost();
-			int port = uri.getPort();
-			long t = timeout.toMillis();
-			if (t <= 0) {
-				session = client.connect(username, host, port).verify()
-						.getSession();
-			} else {
-				session = client.connect(username, host, port)
-						.verify(timeout.toMillis()).getSession();
-			}
-			session.addSessionListener(new SessionListener() {
-
-				@Override
-				public void sessionClosed(Session s) {
-					notifyCloseListeners();
-				}
-			});
-			// Authentication timeout is by default 2 minutes.
-			session.auth().verify(session.getAuthTimeout());
+			session = connect(uri, Collections.emptyList(),
+					future -> notifyCloseListeners(), timeout, MAX_DEPTH);
 		} catch (IOException e) {
 			disconnect(e);
 			throw e;
 		}
 	}
 
+	private ClientSession connect(URIish target, List<URIish> jumps,
+			SshFutureListener<CloseFuture> listener, Duration timeout,
+			int depth) throws IOException {
+		if (--depth < 0) {
+			throw new IOException(
+					format(SshdText.get().proxyJumpAbort, target));
+		}
+		HostConfigEntry hostConfig = getHostConfig(target.getUser(),
+				target.getHost(), target.getPort());
+		String host = hostConfig.getHostName();
+		int port = hostConfig.getPort();
+		List<URIish> hops = determineHops(jumps, hostConfig, target.getHost());
+		ClientSession resultSession = null;
+		ClientSession proxySession = null;
+		PortForwardingTracker portForward = null;
+		try {
+			if (!hops.isEmpty()) {
+				URIish hop = hops.remove(0);
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("Connecting to jump host {}", hop); //$NON-NLS-1$
+				}
+				proxySession = connect(hop, hops, null, timeout, depth);
+			}
+			AttributeRepository context = null;
+			if (proxySession != null) {
+				SshdSocketAddress remoteAddress = new SshdSocketAddress(host,
+						port);
+				portForward = proxySession.createLocalPortForwardingTracker(
+						SshdSocketAddress.LOCALHOST_ADDRESS, remoteAddress);
+				// We must connect to the locally bound address, not the one
+				// from the host config.
+				context = AttributeRepository.ofKeyValuePair(
+						JGitSshClient.LOCAL_FORWARD_ADDRESS,
+						portForward.getBoundAddress());
+			}
+			resultSession = connect(hostConfig, context, timeout);
+			if (proxySession != null) {
+				final PortForwardingTracker tracker = portForward;
+				final ClientSession pSession = proxySession;
+				resultSession.addCloseFutureListener(future -> {
+					IoUtils.closeQuietly(tracker);
+					String sessionName = pSession.toString();
+					try {
+						pSession.close();
+					} catch (IOException e) {
+						LOG.error(format(
+								SshdText.get().sshProxySessionCloseFailed,
+								sessionName), e);
+					}
+				});
+				portForward = null;
+				proxySession = null;
+			}
+			if (listener != null) {
+				resultSession.addCloseFutureListener(listener);
+			}
+			// Authentication timeout is by default 2 minutes.
+			resultSession.auth().verify(resultSession.getAuthTimeout());
+			return resultSession;
+		} catch (IOException e) {
+			close(portForward, e);
+			close(proxySession, e);
+			close(resultSession, e);
+			if (e instanceof SshException && ((SshException) e)
+					.getDisconnectCode() == SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE) {
+				// Ensure the user gets to know on which URI the authentication
+				// was denied.
+				throw new TransportException(target,
+						format(SshdText.get().loginDenied, host,
+								Integer.toString(port)),
+						e);
+			}
+			throw e;
+		}
+	}
+
+	private ClientSession connect(HostConfigEntry config,
+			AttributeRepository context, Duration timeout)
+			throws IOException {
+		ConnectFuture connected = client.connect(config, context, null);
+		long timeoutMillis = timeout.toMillis();
+		if (timeoutMillis <= 0) {
+			connected = connected.verify();
+		} else {
+			connected = connected.verify(timeoutMillis);
+		}
+		return connected.getSession();
+	}
+
+	private void close(Closeable toClose, Throwable error) {
+		if (toClose != null) {
+			try {
+				toClose.close();
+			} catch (IOException e) {
+				error.addSuppressed(e);
+			}
+		}
+	}
+
+	private HostConfigEntry getHostConfig(String username, String host,
+			int port) throws IOException {
+		HostConfigEntry entry = client.getHostConfigEntryResolver()
+				.resolveEffectiveHost(host, port, null, username, null);
+		if (entry == null) {
+			if (SshdSocketAddress.isIPv6Address(host)) {
+				return new HostConfigEntry("", host, port, username); //$NON-NLS-1$
+			}
+			return new HostConfigEntry(host, host, port, username);
+		}
+		return entry;
+	}
+
+	private List<URIish> determineHops(List<URIish> currentHops,
+			HostConfigEntry hostConfig, String host) throws IOException {
+		if (currentHops.isEmpty()) {
+			String jumpHosts = hostConfig.getProperty(SshConstants.PROXY_JUMP);
+			if (!StringUtils.isEmptyOrNull(jumpHosts)) {
+				try {
+					return parseProxyJump(jumpHosts);
+				} catch (URISyntaxException e) {
+					throw new IOException(
+							format(SshdText.get().configInvalidProxyJump, host,
+									jumpHosts),
+							e);
+				}
+			}
+		}
+		return currentHops;
+	}
+
+	private List<URIish> parseProxyJump(String proxyJump)
+			throws URISyntaxException {
+		String[] hops = proxyJump.split(","); //$NON-NLS-1$
+		List<URIish> result = new LinkedList<>();
+		for (String hop : hops) {
+			// There shouldn't be any whitespace, but let's be lenient
+			hop = hop.trim();
+			if (SHORT_SSH_FORMAT.matcher(hop).matches()) {
+				// URIish doesn't understand the short SSH format
+				// user@host:port, only user@host:path
+				hop = SshConstants.SSH_SCHEME + "://" + hop; //$NON-NLS-1$
+			}
+			URIish to = new URIish(hop);
+			if (!SshConstants.SSH_SCHEME.equalsIgnoreCase(to.getScheme())) {
+				throw new URISyntaxException(hop,
+						SshdText.get().configProxyJumpNotSsh);
+			} else if (!StringUtils.isEmptyOrNull(to.getPath())) {
+				throw new URISyntaxException(hop,
+						SshdText.get().configProxyJumpWithPath);
+			}
+			result.add(to);
+		}
+		return result;
+	}
+
 	/**
 	 * Adds a {@link SessionCloseListener} to this session. Has no effect if the
 	 * given {@code listener} is already registered with this session.
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index 0f7ab84..df0e1d2 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -34,6 +34,7 @@
 import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory;
 import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
 import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.loader.openssh.kdf.BCryptKdfOptions;
@@ -41,6 +42,7 @@
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
+import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException;
 import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider;
 import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory;
 import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory;
@@ -230,7 +232,16 @@
 			return session;
 		} catch (Exception e) {
 			unregister(session);
-			throw new TransportException(uri, e.getMessage(), e);
+			if (e instanceof TransportException) {
+				throw (TransportException) e;
+			}
+			Throwable cause = e;
+			if (e instanceof SshException && e
+					.getCause() instanceof AuthenticationCanceledException) {
+				// Results in a nicer error message
+				cause = e.getCause();
+			}
+			throw new TransportException(uri, cause.getMessage(), cause);
 		}
 	}
 
diff --git a/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs
index 2bc2cf3..c16c986 100644
--- a/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ssh.jsch.test/.settings/org.eclipse.jdt.core.prefs
@@ -52,8 +52,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
index 345c3cf..c1e1d36 100644
--- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -3,21 +3,21 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ssh.jsch.test
 Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.test
-Bundle-Version: 5.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.jcraft.jsch;version="[0.1.54,0.2.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit.ssh;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
  org.junit.runner;version="[4.13,5.0.0)"
-Export-Package: org.eclipse.jgit.transport;version="5.9.1";
+Export-Package: org.eclipse.jgit.transport;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
     org.eclipse.jgit.junit,
     org.eclipse.jgit.junit.ssh"
diff --git a/org.eclipse.jgit.ssh.jsch.test/pom.xml b/org.eclipse.jgit.ssh.jsch.test/pom.xml
index 24e1109..54cce81 100644
--- a/org.eclipse.jgit.ssh.jsch.test/pom.xml
+++ b/org.eclipse.jgit.ssh.jsch.test/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.jsch.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
index 715495a..bd0d4cc 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
@@ -3,24 +3,24 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ssh.jsch
 Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch;singleton:=true
-Fragment-Host: org.eclipse.jgit;bundle-version="[5.9.1,5.10.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[5.11.0,5.12.0)"
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
-Bundle-Version: 5.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.internal.transport.jsch;version="5.9.1";x-friends:="org.eclipse.egit.core",
- org.eclipse.jgit.transport;version="5.9.1";
+Export-Package: org.eclipse.jgit.internal.transport.jsch;version="5.11.0";x-friends:="org.eclipse.egit.core",
+ org.eclipse.jgit.transport;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.internal.transport.ssh,
    org.eclipse.jgit.util,
    com.jcraft.jsch"
 Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.io;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.io;version="[5.11.0,5.12.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
index aaf7c2d..7ccf3ca 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.ssh.jsch - Sources
 Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml
index 5916157..48fecf4 100644
--- a/org.eclipse.jgit.ssh.jsch/pom.xml
+++ b/org.eclipse.jgit.ssh.jsch/pom.xml
@@ -17,7 +17,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
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 3dd5840..b853c6a 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
@@ -51,8 +51,8 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 1ce4c2b..148456d 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -16,58 +16,59 @@
  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.assertj.core.api;version="[3.14.0,4.0.0)",
- org.eclipse.jgit.annotations;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.api.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.archive;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.attributes;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.awtui;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.blame;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.diff;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.dircache;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.events;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.fnmatch;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.gitrepo;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.hooks;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.ignore;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.ignore.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.fsck;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.io;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.pack;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.transport.connectivity;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.transport.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.internal.transport.parser;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.junit.time;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lfs;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.merge;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.notes;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.patch;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.pgm;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.pgm.internal;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revplot;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.file;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.storage.pack;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.submodule;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.http;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport.resolver;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.treewalk.filter;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.io;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util.sha1;version="[5.9.1,5.10.0)",
+ org.eclipse.jgit.annotations;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.api.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.archive;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.attributes;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.awtui;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.blame;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.diff;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.dircache;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.events;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.fnmatch;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.gitrepo;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.hooks;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.ignore;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.ignore.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.fsck;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.io;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.transport.connectivity;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.transport.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.junit.time;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lfs;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.logging;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.merge;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.notes;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.patch;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.pgm;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.pgm.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revplot;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.file;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.storage.pack;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.submodule;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.http;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.io;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.sha1;version="[5.11.0,5.12.0)",
  org.junit;version="[4.13,5.0.0)",
  org.junit.experimental.theories;version="[4.13,5.0.0)",
  org.junit.function;version="[4.13.0,5.0.0)",
diff --git a/org.eclipse.jgit.test/build.properties b/org.eclipse.jgit.test/build.properties
index 7dc26c0..b527a74 100644
--- a/org.eclipse.jgit.test/build.properties
+++ b/org.eclipse.jgit.test/build.properties
@@ -8,4 +8,4 @@
                bin-tst/,\
                bin/
 additional.bundles = org.apache.log4j,\
-                     org.slf4j.impl.log4j12
+                     org.slf4j.binding.log4j12
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index d9c9a3d..8781fb3 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -19,7 +19,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
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 b737bbe..de25870 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
@@ -92,7 +92,6 @@
 		command.setURI(fileUri());
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 		ObjectId id = git2.getRepository().resolve("tag-for-blob");
 		assertNotNull(id);
 		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/test");
@@ -277,8 +276,7 @@
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
 
-		assertNotNull(git2);
-		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
+		assertEquals("refs/heads/master", git2.getRepository().getFullBranch());
 		assertEquals(
 				"refs/heads/master, refs/remotes/origin/master, refs/remotes/origin/test",
 				allRefNames(git2.branchList().setListMode(ListMode.ALL).call()));
@@ -293,7 +291,6 @@
 		git2 = command.call();
 		addRepoToClose(git2.getRepository());
 
-		assertNotNull(git2);
 		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
 		assertEquals("refs/remotes/origin/master, refs/remotes/origin/test",
 				allRefNames(git2.branchList().setListMode(ListMode.ALL).call()));
@@ -308,8 +305,7 @@
 		git2 = command.call();
 		addRepoToClose(git2.getRepository());
 
-		assertNotNull(git2);
-		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
+		assertEquals("refs/heads/master", git2.getRepository().getFullBranch());
 		assertEquals("refs/heads/master, refs/heads/test", allRefNames(git2
 				.branchList().setListMode(ListMode.ALL).call()));
 	}
@@ -324,7 +320,6 @@
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
 
-		assertNotNull(git2);
 		assertEquals("refs/heads/test", git2.getRepository().getFullBranch());
 	}
 
@@ -338,7 +333,6 @@
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
 
-		assertNotNull(git2);
 		ObjectId taggedCommit = db.resolve("tag-initial^{commit}");
 		assertEquals(taggedCommit.name(), git2
 				.getRepository().getFullBranch());
@@ -355,10 +349,9 @@
 		command.setURI(fileUri());
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 		assertNull(git2.getRepository().resolve("tag-for-blob"));
 		assertNotNull(git2.getRepository().resolve("tag-initial"));
-		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
+		assertEquals("refs/heads/master", git2.getRepository().getFullBranch());
 		assertEquals("refs/remotes/origin/master", allRefNames(git2
 				.branchList().setListMode(ListMode.REMOTE).call()));
 		RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
@@ -383,10 +376,9 @@
 		command.setBare(true);
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 		assertNull(git2.getRepository().resolve("tag-for-blob"));
 		assertNotNull(git2.getRepository().resolve("tag-initial"));
-		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
+		assertEquals("refs/heads/master", git2.getRepository().getFullBranch());
 		assertEquals("refs/heads/master", allRefNames(git2.branchList()
 				.setListMode(ListMode.ALL).call()));
 		RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
@@ -409,11 +401,10 @@
 		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", git2.getRepository().getFullBranch());
 		assertEquals("refs/heads/master, refs/heads/test", allRefNames(
 				git2.branchList().setListMode(ListMode.ALL).call()));
 		assertNotNull(git2.getRepository().exactRef("refs/meta/foo/bar"));
@@ -436,7 +427,6 @@
 		command.setURI(fileUri());
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 		assertNull(git2.getRepository().resolve("tag-for-blob"));
 		assertNull(git2.getRepository().resolve("refs/heads/master"));
 		assertNotNull(git2.getRepository().resolve("tag-initial"));
@@ -464,8 +454,7 @@
 		command.setURI(fileUri());
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
-		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/test");
+		assertEquals("refs/heads/test", git2.getRepository().getFullBranch());
 		// Expect both remote branches to exist; setCloneAllBranches(true)
 		// should override any setBranchesToClone().
 		assertNotNull(
@@ -492,8 +481,7 @@
 		command.setURI(fileUri());
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
-		assertEquals(git2.getRepository().getFullBranch(), "refs/heads/test");
+		assertEquals("refs/heads/test", git2.getRepository().getFullBranch());
 		// 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"));
@@ -525,7 +513,6 @@
 		command.setURI(fileUri());
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 		// clone again
 		command = Git.cloneRepository();
 		command.setDirectory(directory);
@@ -551,7 +538,6 @@
 		clone.setURI(fileUri());
 		Git git2 = clone.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 
 		assertEquals(Constants.MASTER, git2.getRepository().getBranch());
 	}
@@ -595,7 +581,6 @@
 		clone.setURI(fileUri());
 		Git git2 = clone.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 
 		assertEquals(Constants.MASTER, git2.getRepository().getBranch());
 		assertTrue(new File(git2.getRepository().getWorkTree(), path
@@ -683,7 +668,6 @@
 		clone.setURI(git.getRepository().getDirectory().toURI().toString());
 		Git git2 = clone.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 
 		assertEquals(Constants.MASTER, git2.getRepository().getBranch());
 		assertTrue(new File(git2.getRepository().getWorkTree(), path
@@ -813,7 +797,6 @@
 		command.setNoTags();
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 		assertNotNull(git2.getRepository().resolve("refs/heads/test"));
 		assertNull(git2.getRepository().resolve("tag-initial"));
 		assertNull(git2.getRepository().resolve("tag-for-blob"));
@@ -833,13 +816,41 @@
 		command.setTagOption(TagOpt.FETCH_TAGS);
 		Git git2 = command.call();
 		addRepoToClose(git2.getRepository());
-		assertNotNull(git2);
 		assertNull(git2.getRepository().resolve("refs/heads/test"));
 		assertNotNull(git2.getRepository().resolve("tag-initial"));
 		assertNotNull(git2.getRepository().resolve("tag-for-blob"));
 		assertTagOption(git2.getRepository(), TagOpt.FETCH_TAGS);
 	}
 
+	@Test
+	public void testCloneWithHeadSymRefIsMasterCopy() throws IOException, GitAPIException {
+		// create a branch with the same head as master and switch to it
+		git.checkout().setStartPoint("master").setCreateBranch(true).setName("master-copy").call();
+
+		// when we clone the HEAD symref->master-copy means we start on master-copy and not master
+		File directory = createTempDirectory("testCloneRepositorySymRef_master-copy");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+		assertEquals("refs/heads/master-copy", git2.getRepository().getFullBranch());
+	}
+
+	@Test
+	public void testCloneWithHeadSymRefIsNonMasterCopy() throws IOException, GitAPIException {
+		// create a branch with the same head as test and switch to it
+		git.checkout().setStartPoint("test").setCreateBranch(true).setName("test-copy").call();
+
+		File directory = createTempDirectory("testCloneRepositorySymRef_test-copy");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+		assertEquals("refs/heads/test-copy", git2.getRepository().getFullBranch());
+	}
+
 	private void assertTagOption(Repository repo, TagOpt expectedTagOption)
 			throws URISyntaxException {
 		RemoteConfig remoteConfig = new RemoteConfig(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
index 7ba7c8d..b460e3f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
@@ -310,7 +310,7 @@
 			assertEquals(
 					"2 commits for describe commit increment expected since lightweight tag: c4 and c3",
 					"t2-2-g119892b", describe(c4)); // 2 commits: c4 and c3
-		} else if (!useAnnotatedTags && !describeUseAllTags) {
+		} else if (!useAnnotatedTags) {
 			assertEquals("no matching commits expected", null, describe(c4));
 		} else {
 			assertEquals(
@@ -405,6 +405,46 @@
 		}
 	}
 
+	@Test
+	public void testDescribeUseAllRefsMaster() throws Exception {
+		final ObjectId c1 = modify("aaa");
+		tag("t1");
+
+		if (useAnnotatedTags || describeUseAllTags) {
+			assertEquals("t1", describe(c1));
+		} else {
+			assertEquals(null, describe(c1));
+		}
+		assertEquals("heads/master", describeAll(c1));
+	}
+
+	/**
+	 * Branch off from master and then tag
+	 *
+	 * <pre>
+	 * c1 -+ -> c2
+	 *     |
+	 *     +-> t1
+	 * </pre>
+	 * @throws Exception
+	 * */
+	@Test
+	public void testDescribeUseAllRefsBranch() throws Exception {
+		final ObjectId c1 = modify("aaa");
+		modify("bbb");
+
+		branch("b", c1);
+		final ObjectId c3 = modify("ccc");
+		tag("t1");
+
+		if (!useAnnotatedTags && !describeUseAllTags) {
+			assertEquals(null, describe(c3));
+		} else {
+			assertEquals("t1", describe(c3));
+		}
+		assertEquals("heads/b", describeAll(c3));
+	}
+
 	private ObjectId merge(ObjectId c2) throws GitAPIException {
 		return git.merge().include(c2).call().getNewHead();
 	}
@@ -444,6 +484,11 @@
 		return describe(c1, false, false);
 	}
 
+	private String describeAll(ObjectId c1) throws GitAPIException, IOException {
+		return git.describe().setTarget(c1).setTags(describeUseAllTags)
+				.setLong(false).setAlways(false).setAll(true).call();
+	}
+
 	private String describe(ObjectId c1, String... patterns) throws Exception {
 		return git.describe().setTarget(c1).setTags(describeUseAllTags)
 				.setMatch(patterns).call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java
index 00f84e9..12ec2aa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java
@@ -11,9 +11,11 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.util.Collection;
+import java.util.Optional;
 
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
@@ -34,7 +36,7 @@
 		git.add().addFilepattern("Test.txt").call();
 		git.commit().setMessage("Initial commit").call();
 
-		// create a master branch and switch to it
+		// create a test branch and switch to it
 		git.branchCreate().setName("test").call();
 		RefUpdate rup = db.updateRef(Constants.HEAD);
 		rup.link("refs/heads/test");
@@ -104,6 +106,28 @@
 		assertEquals(2, refs.size());
 	}
 
+	@Test
+	public void testLsRemoteWithSymRefs() throws Exception {
+		File directory = createTempDirectory("testRepository");
+		CloneCommand command = Git.cloneRepository();
+		command.setDirectory(directory);
+		command.setURI(fileUri());
+		command.setCloneAllBranches(true);
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+
+		LsRemoteCommand lsRemoteCommand = git2.lsRemote();
+		Collection<Ref> refs = lsRemoteCommand.call();
+		assertNotNull(refs);
+		assertEquals(6, refs.size());
+
+		Optional<Ref> headRef = refs.stream().filter(ref -> ref.getName().equals(Constants.HEAD)).findFirst();
+		assertTrue("expected a HEAD Ref", headRef.isPresent());
+		assertTrue("expected HEAD Ref to be a Symbolic", headRef.get().isSymbolic());
+		assertEquals("refs/heads/test", headRef.get().getTarget().getName());
+	}
+
 	private String fileUri() {
 		return "file://" + git.getRepository().getWorkTree().getAbsolutePath();
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
index e0a1c1d..f52b715 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Kevin Sawicki <kevin@github.com> and others
+ * Copyright (C) 2011, 2020 Kevin Sawicki <kevin@github.com> 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
@@ -24,6 +24,7 @@
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.lib.StoredConfig;
@@ -310,6 +311,16 @@
 	}
 
 	@Test
+	public void testCheckoutFileWithConflict() throws Exception {
+		setupConflictingState();
+		assertEquals('[' + FILE1 + ']',
+				git.status().call().getConflicting().toString());
+		git.checkout().setStartPoint(Constants.HEAD).addPath(FILE1).call();
+		assertEquals("3", read(FILE1));
+		assertTrue(git.status().call().isClean());
+	}
+
+	@Test
 	public void testCheckoutOursWhenNoBase() throws Exception {
 		String file = "added.txt";
 
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 7e0de82..5311edb 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
@@ -160,4 +160,25 @@
 			assertTrue("Expected no differences", status.isClean());
 		}
 	}
+
+	@Test
+	public void testFolderPrefix() throws Exception {
+		// "audio" is a prefix of "audio-new" and "audio.new".
+		try (Git git = new Git(db)) {
+			// Order here is the git order, but that doesn't really matter.
+			// They are processed by StatusCommand in this order even if written
+			// in a different order. Bug 566799 would, when having processed
+			// audio/foo, remove previously recorded untracked folders that have
+			// "audio" as a prefix: audio-new and audio.new.
+			writeTrashFile("audi", "foo", "foo");
+			writeTrashFile("audio-new", "foo", "foo");
+			writeTrashFile("audio.new", "foo", "foo");
+			writeTrashFile("audio", "foo", "foo");
+			writeTrashFile("audio_new", "foo", "foo");
+			Status stat = git.status().call();
+			assertEquals(Sets.of("audi", "audio-new", "audio.new", "audio",
+					"audio_new"), stat.getUntrackedFolders());
+		}
+	}
+
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
index b1c54b9..9903417 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020 Chris Aniszczyk <caniszczyk@gmail.com> 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
@@ -11,6 +11,9 @@
 
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
@@ -19,8 +22,10 @@
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidTagNameException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -29,6 +34,59 @@
 public class TagCommandTest extends RepositoryTestCase {
 
 	@Test
+	public void testTagKind() {
+		try (Git git = new Git(db)) {
+			assertTrue(git.tag().isAnnotated());
+			assertTrue(git.tag().setSigned(true).isAnnotated());
+			assertTrue(git.tag().setSigned(false).isAnnotated());
+			assertTrue(git.tag().setSigningKey(null).isAnnotated());
+			assertTrue(git.tag().setSigningKey("something").isAnnotated());
+			assertTrue(git.tag().setSigned(false).setSigningKey(null)
+					.isAnnotated());
+			assertTrue(git.tag().setSigned(false).setSigningKey("something")
+					.isAnnotated());
+			assertTrue(git.tag().setSigned(true).setSigningKey(null)
+					.isAnnotated());
+			assertTrue(git.tag().setSigned(true).setSigningKey("something")
+					.isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).isAnnotated());
+			assertTrue(
+					git.tag().setAnnotated(true).setSigned(true).isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).setSigned(false)
+					.isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).setSigningKey(null)
+					.isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).setSigningKey("something")
+					.isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).setSigned(false)
+					.setSigningKey(null).isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).setSigned(false)
+					.setSigningKey("something").isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).setSigned(true)
+					.setSigningKey(null).isAnnotated());
+			assertTrue(git.tag().setAnnotated(true).setSigned(true)
+					.setSigningKey("something").isAnnotated());
+			assertFalse(git.tag().setAnnotated(false).isAnnotated());
+			assertTrue(git.tag().setAnnotated(false).setSigned(true)
+					.isAnnotated());
+			assertFalse(git.tag().setAnnotated(false).setSigned(false)
+					.isAnnotated());
+			assertFalse(git.tag().setAnnotated(false).setSigningKey(null)
+					.isAnnotated());
+			assertTrue(git.tag().setAnnotated(false).setSigningKey("something")
+					.isAnnotated());
+			assertFalse(git.tag().setAnnotated(false).setSigned(false)
+					.setSigningKey(null).isAnnotated());
+			assertTrue(git.tag().setAnnotated(false).setSigned(false)
+					.setSigningKey("something").isAnnotated());
+			assertTrue(git.tag().setAnnotated(false).setSigned(true)
+					.setSigningKey(null).isAnnotated());
+			assertTrue(git.tag().setAnnotated(false).setSigned(true)
+					.setSigningKey("something").isAnnotated());
+		}
+	}
+
+	@Test
 	public void testTaggingOnHead() throws GitAPIException, IOException {
 		try (Git git = new Git(db);
 				RevWalk walk = new RevWalk(db)) {
@@ -67,6 +125,29 @@
 	}
 
 	@Test
+	public void testForceNoChangeLightweight() throws GitAPIException {
+		try (Git git = new Git(db)) {
+			git.commit().setMessage("initial commit").call();
+			RevCommit commit = git.commit().setMessage("second commit").call();
+			git.commit().setMessage("third commit").call();
+			Ref tagRef = git.tag().setObjectId(commit).setName("tag")
+					.setAnnotated(false).call();
+			assertEquals(commit.getId(), tagRef.getObjectId());
+			// Without force, we want to get a RefAlreadyExistsException
+			RefAlreadyExistsException e = assertThrows(
+					RefAlreadyExistsException.class,
+					() -> git.tag().setObjectId(commit).setName("tag")
+							.setAnnotated(false).call());
+			assertEquals(RefUpdate.Result.NO_CHANGE, e.getUpdateResult());
+			// With force the call should work
+			assertEquals(commit.getId(),
+					git.tag().setObjectId(commit).setName("tag")
+							.setAnnotated(false).setForceUpdate(true).call()
+							.getObjectId());
+		}
+	}
+
+	@Test
 	public void testEmptyTagName() throws GitAPIException {
 		try (Git git = new Git(db)) {
 			git.commit().setMessage("initial commit").call();
@@ -93,19 +174,6 @@
 		}
 	}
 
-	@Test
-	public void testFailureOnSignedTags() throws GitAPIException {
-		try (Git git = new Git(db)) {
-			git.commit().setMessage("initial commit").call();
-			try {
-				git.tag().setSigned(true).setName("tag").call();
-				fail("We should have failed with an UnsupportedOperationException due to signed tag");
-			} catch (UnsupportedOperationException e) {
-				// should hit here
-			}
-		}
-	}
-
 	private List<Ref> getTags() throws Exception {
 		return db.getRefDatabase().getRefsByPrefix(R_TAGS);
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
index 5477f56..8e84dfa 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
@@ -242,6 +242,46 @@
 	}
 
 	@Test
+	public void testSetStage() {
+		DirCacheEntry e = new DirCacheEntry("some/path", DirCacheEntry.STAGE_1);
+		e.setAssumeValid(true);
+		e.setCreationTime(2L);
+		e.setFileMode(FileMode.EXECUTABLE_FILE);
+		e.setLastModified(EPOCH.plusMillis(3L));
+		e.setLength(100L);
+		e.setObjectId(ObjectId
+				.fromString("0123456789012345678901234567890123456789"));
+		e.setUpdateNeeded(true);
+		e.setStage(DirCacheEntry.STAGE_2);
+
+		assertTrue(e.isAssumeValid());
+		assertEquals(2L, e.getCreationTime());
+		assertEquals(
+				ObjectId.fromString("0123456789012345678901234567890123456789"),
+				e.getObjectId());
+		assertEquals(FileMode.EXECUTABLE_FILE, e.getFileMode());
+		assertEquals(EPOCH.plusMillis(3L), e.getLastModifiedInstant());
+		assertEquals(100L, e.getLength());
+		assertEquals(DirCacheEntry.STAGE_2, e.getStage());
+		assertTrue(e.isUpdateNeeded());
+		assertEquals("some/path", e.getPathString());
+
+		e.setStage(DirCacheEntry.STAGE_0);
+
+		assertTrue(e.isAssumeValid());
+		assertEquals(2L, e.getCreationTime());
+		assertEquals(
+				ObjectId.fromString("0123456789012345678901234567890123456789"),
+				e.getObjectId());
+		assertEquals(FileMode.EXECUTABLE_FILE, e.getFileMode());
+		assertEquals(EPOCH.plusMillis(3L), e.getLastModifiedInstant());
+		assertEquals(100L, e.getLength());
+		assertEquals(DirCacheEntry.STAGE_0, e.getStage());
+		assertTrue(e.isUpdateNeeded());
+		assertEquals("some/path", e.getPathString());
+	}
+
+	@Test
 	public void testCopyMetaDataWithStage() {
 		copyMetaDataHelper(false);
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
index 39a1f01..5778d28 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Robin Rosenberg and others
+ * Copyright (C) 2011, 2020 Robin Rosenberg 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
@@ -124,6 +124,32 @@
 	}
 
 	@Test
+	public void testPathEditWithStagesAndReset() throws Exception {
+		DirCache dc = DirCache.newInCore();
+		DirCacheBuilder builder = new DirCacheBuilder(dc, 3);
+		builder.add(createEntry("a", DirCacheEntry.STAGE_1));
+		builder.add(createEntry("a", DirCacheEntry.STAGE_2));
+		builder.add(createEntry("a", DirCacheEntry.STAGE_3));
+		builder.finish();
+
+		DirCacheEditor editor = dc.editor();
+		PathEdit edit = new PathEdit("a") {
+
+			@Override
+			public void apply(DirCacheEntry ent) {
+				ent.setStage(DirCacheEntry.STAGE_0);
+			}
+		};
+		editor.add(edit);
+		editor.finish();
+
+		assertEquals(1, dc.getEntryCount());
+		DirCacheEntry entry = dc.getEntry(0);
+		assertEquals("a", entry.getPathString());
+		assertEquals(DirCacheEntry.STAGE_0, entry.getStage());
+	}
+
+	@Test
 	public void testFileReplacesTree() throws Exception {
 		DirCache dc = DirCache.newInCore();
 		DirCacheEditor editor = dc.editor();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index e8d1cd3..d007dd4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -80,6 +80,29 @@
 	}
 
 	@Theory
+	public void testPack2Commits_noPackFolder(boolean aggressive) throws Exception {
+		File packDir = repo.getObjectDatabase().getPackDirectory();
+		assertTrue(packDir.delete());
+
+		BranchBuilder bb = tr.branch("refs/heads/master");
+		bb.commit().add("A", "A").add("B", "B").create();
+		bb.commit().add("A", "A2").add("B", "B2").create();
+
+		stats = gc.getStatistics();
+		assertEquals(8, stats.numberOfLooseObjects);
+		assertEquals(0, stats.numberOfPackedObjects);
+		configureGc(gc, aggressive);
+		gc.gc();
+		stats = gc.getStatistics();
+		assertEquals(0, stats.numberOfLooseObjects);
+		assertEquals(8, stats.numberOfPackedObjects);
+		assertEquals(1, stats.numberOfPackFiles);
+		assertEquals(2, stats.numberOfBitmaps);
+
+		assertTrue(packDir.exists());
+	}
+
+	@Theory
 	public void testPackAllObjectsInOnePack(boolean aggressive)
 			throws Exception {
 		tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java
index dee58f9..2f1bada 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/CommitBuilderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, Salesforce. and others
+ * Copyright (C) 2018, 2020 Salesforce. 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
@@ -53,7 +53,7 @@
 	private void assertGpgSignatureStringOutcome(String signature,
 			String expectedOutcome) throws IOException {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		CommitBuilder.writeGpgSignatureString(signature, out);
+		ObjectBuilder.writeMultiLineHeader(signature, out, true);
 		String formatted_signature = new String(out.toByteArray(), US_ASCII);
 		assertEquals(expectedOutcome, formatted_signature);
 	}
@@ -85,8 +85,8 @@
 		String signature = "Ü Ä";
 		IllegalArgumentException e = assertThrows(
 				IllegalArgumentException.class,
-				() -> CommitBuilder.writeGpgSignatureString(signature,
-						new ByteArrayOutputStream()));
+				() -> ObjectBuilder.writeMultiLineHeader(signature,
+						new ByteArrayOutputStream(), true));
 		String message = MessageFormat.format(JGitText.get().notASCIIString,
 				signature);
 		assertEquals(message, e.getMessage());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TagBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TagBuilderTest.java
new file mode 100644
index 0000000..5786022
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TagBuilderTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.junit.Test;
+
+public class TagBuilderTest {
+
+	// @formatter:off
+	private static final String SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" +
+			"Version: BCPG v1.60\n" +
+			"\n" +
+			"iQEcBAABCAAGBQJb9cVhAAoJEKX+6Axg/6TZeFsH/0CY0WX/z7U8+7S5giFX4wH4\n" +
+			"opvBwqyt6OX8lgNwTwBGHFNt8LdmDCCmKoq/XwkNi3ARVjLhe3gBcKXNoavvPk2Z\n" +
+			"gIg5ChevGkU4afWCOMLVEYnkCBGw2+86XhrK1P7gTHEk1Rd+Yv1ZRDJBY+fFO7yz\n" +
+			"uSBuF5RpEY2sJiIvp27Gub/rY3B5NTR/feO/z+b9oiP/fMUhpRwG5KuWUsn9NPjw\n" +
+			"3tvbgawYpU/2UnS+xnavMY4t2fjRYjsoxndPLb2MUX8X7vC7FgWLBlmI/rquLZVM\n" +
+			"IQEKkjnA+lhejjK1rv+ulq4kGZJFKGYWYYhRDwFg5PTkzhudhN2SGUq5Wxq1Eg4=\n" +
+			"=b9OI\n" +
+			"-----END PGP SIGNATURE-----";
+
+	// @formatter:on
+
+	private static final String TAGGER_LINE = "A U. Thor <a_u_thor@example.com> 1218123387 +0700";
+
+	private static final PersonIdent TAGGER = RawParseUtils
+			.parsePersonIdent(TAGGER_LINE);
+
+	@Test
+	public void testTagSimple() throws Exception {
+		TagBuilder t = new TagBuilder();
+		t.setTag("sometag");
+		t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+		t.setEncoding(US_ASCII);
+		t.setMessage("Short message only");
+		t.setTagger(TAGGER);
+		String tag = new String(t.build(), UTF_8);
+		String expected = "object 0000000000000000000000000000000000000000\n"
+				+ "type commit\n" //
+				+ "tag sometag\n" //
+				+ "tagger " + TAGGER_LINE + '\n' //
+				+ "encoding US-ASCII\n" //
+				+ '\n' //
+				+ "Short message only";
+		assertEquals(expected, tag);
+	}
+
+	@Test
+	public void testTagWithSignatureShortMessageEndsInLF() throws Exception {
+		TagBuilder t = new TagBuilder();
+		t.setTag("sometag");
+		t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+		t.setEncoding(US_ASCII);
+		t.setMessage("Short message only\n");
+		t.setTagger(TAGGER);
+		t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+		String tag = new String(t.build(), UTF_8);
+		String expected = "object 0000000000000000000000000000000000000000\n"
+				+ "type commit\n" //
+				+ "tag sometag\n" //
+				+ "tagger " + TAGGER_LINE + '\n' //
+				+ "encoding US-ASCII\n" //
+				+ '\n' //
+				+ "Short message only\n" //
+				+ SIGNATURE + '\n';
+		assertEquals(expected, tag);
+	}
+
+	@Test
+	public void testTagWithSignatureMessageNoLF() {
+		TagBuilder t = new TagBuilder();
+		t.setTag("sometag");
+		t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+		t.setEncoding(US_ASCII);
+		t.setMessage("A message\n\nthat does not end in LF");
+		t.setTagger(TAGGER);
+		t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+		Throwable ex = assertThrows(Throwable.class, t::build);
+		assertEquals(JGitText.get().signedTagMessageNoLf, ex.getMessage());
+	}
+
+	@Test
+	public void testTagWithSignatureNoParagraphsMessage() throws Exception {
+		TagBuilder t = new TagBuilder();
+		t.setTag("sometag");
+		t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+		t.setEncoding(US_ASCII);
+		t.setMessage("A strange\ntag message\n");
+		t.setTagger(TAGGER);
+		t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+		String tag = new String(t.build(), UTF_8);
+		String expected = "object 0000000000000000000000000000000000000000\n"
+				+ "type commit\n" //
+				+ "tag sometag\n" //
+				+ "tagger " + TAGGER_LINE + '\n' //
+				+ "encoding US-ASCII\n" //
+				+ '\n' //
+				+ "A strange\ntag message\n" //
+				+ SIGNATURE + '\n';
+		assertEquals(expected, tag);
+	}
+
+	@Test
+	public void testTagWithSignatureLongMessage() throws Exception {
+		TagBuilder t = new TagBuilder();
+		t.setTag("sometag");
+		t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+		t.setMessage("Short message\n\nFollowed by explanations.\n");
+		t.setTagger(TAGGER);
+		t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+		String tag = new String(t.build(), UTF_8);
+		String expected = "object 0000000000000000000000000000000000000000\n"
+				+ "type commit\n" //
+				+ "tag sometag\n" //
+				+ "tagger " + TAGGER_LINE + '\n' //
+				+ '\n' //
+				+ "Short message\n\nFollowed by explanations.\n" //
+				+ SIGNATURE + '\n';
+		assertEquals(expected, tag);
+	}
+
+	@Test
+	public void testTagWithSignatureEmptyMessage() throws Exception {
+		TagBuilder t = new TagBuilder();
+		t.setTag("sometag");
+		t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+		t.setTagger(TAGGER);
+		t.setMessage("");
+		String emptyMsg = new String(t.build(), UTF_8);
+		t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+		String tag = new String(t.build(), UTF_8);
+		String expected = "object 0000000000000000000000000000000000000000\n"
+				+ "type commit\n" //
+				+ "tag sometag\n" //
+				+ "tagger " + TAGGER_LINE + '\n' //
+				+ '\n';
+		assertEquals(expected, emptyMsg);
+		assertEquals(expected + SIGNATURE + '\n', tag);
+	}
+
+	@Test
+	public void testTagWithSignatureOnly() throws Exception {
+		TagBuilder t = new TagBuilder();
+		t.setTag("sometag");
+		t.setObjectId(ObjectId.zeroId(), Constants.OBJ_COMMIT);
+		t.setTagger(TAGGER);
+		String emptyMsg = new String(t.build(), UTF_8);
+		t.setGpgSignature(new GpgSignature(SIGNATURE.getBytes(US_ASCII)));
+		String tag = new String(t.build(), UTF_8);
+		String expected = "object 0000000000000000000000000000000000000000\n"
+				+ "type commit\n" //
+				+ "tag sometag\n" //
+				+ "tagger " + TAGGER_LINE + '\n' //
+				+ '\n';
+		assertEquals(expected, emptyMsg);
+		assertEquals(expected + SIGNATURE + '\n', tag);
+	}
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/logging/PerformanceLogContextTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/logging/PerformanceLogContextTest.java
new file mode 100644
index 0000000..f3c1dde
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/logging/PerformanceLogContextTest.java
@@ -0,0 +1,95 @@
+package org.eclipse.jgit.logging;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import java.util.List;
+
+/**
+ * Tests for performance log context utilities.
+ */
+public class PerformanceLogContextTest {
+
+	@Test
+	public void testAddEvent() {
+		PerformanceLogRecord record = new PerformanceLogRecord("record", 0);
+		PerformanceLogContext.getInstance().addEvent(record);
+
+		List<PerformanceLogRecord> eventRecords = PerformanceLogContext
+				.getInstance().getEventRecords();
+		assertTrue(eventRecords.contains(record));
+		assertEquals(1, eventRecords.size());
+	}
+
+	@Test
+	public void testCleanEvents() {
+		PerformanceLogRecord record1 = new PerformanceLogRecord("record1", 0);
+		PerformanceLogContext.getInstance().addEvent(record1);
+
+		PerformanceLogRecord record2 = new PerformanceLogRecord("record2", 0);
+		PerformanceLogContext.getInstance().addEvent(record2);
+
+		PerformanceLogContext.getInstance().cleanEvents();
+		List<PerformanceLogRecord> eventRecords = PerformanceLogContext
+				.getInstance().getEventRecords();
+		assertEquals(0, eventRecords.size());
+	}
+
+	@Test
+	public void testAddEventsTwoThreads() throws InterruptedException {
+		TestRunnable runnable1 = new TestRunnable("record1", 1);
+		TestRunnable runnable2 = new TestRunnable("record2", 2);
+
+		Thread thread1 = new Thread(runnable1);
+		Thread thread2 = new Thread(runnable2);
+
+		thread1.start();
+		thread2.start();
+
+		thread1.join();
+		thread2.join();
+		assertEquals(1, runnable1.getEventRecordsCount());
+		assertEquals(1, runnable2.getEventRecordsCount());
+		assertFalse(runnable1.isThrown());
+		assertFalse(runnable2.isThrown());
+	}
+
+	private static class TestRunnable implements Runnable {
+		private String name;
+
+		private long durationMs;
+
+		private long eventRecordsCount;
+
+		private boolean thrown = false;
+
+		public TestRunnable(String name, long durationMs) {
+			this.name = name;
+			this.durationMs = durationMs;
+		}
+
+		public boolean isThrown() {
+			return thrown;
+		}
+
+		public long getEventRecordsCount() {
+			return eventRecordsCount;
+		}
+
+		@Override
+		public void run() {
+			PerformanceLogRecord record = new PerformanceLogRecord(name,
+					durationMs);
+			try {
+				PerformanceLogContext.getInstance().addEvent(record);
+				eventRecordsCount = PerformanceLogContext.getInstance()
+						.getEventRecords().size();
+				PerformanceLogContext.getInstance().cleanEvents();
+			} catch (Exception e) {
+				thrown = true;
+			}
+		}
+	}
+}
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 b92a072..edddc33 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2010, Google Inc. and others
+ * Copyright (C) 2008, 2020, Google Inc. 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
@@ -11,6 +11,7 @@
 package org.eclipse.jgit.revwalk;
 
 import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.US_ASCII;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -18,6 +19,7 @@
 import static org.junit.Assert.assertSame;
 
 import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
 
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -117,6 +119,7 @@
 		assertNotNull(c.getTagName());
 		assertEquals(name, c.getTagName());
 		assertEquals("", c.getFullMessage());
+		assertNull(c.getRawGpgSignature());
 
 		final PersonIdent cTagger = c.getTaggerIdent();
 		assertNotNull(cTagger);
@@ -128,13 +131,12 @@
 	public void testParseOldStyleNoTagger() throws Exception {
 		final ObjectId treeId = id("9788669ad918b6fcce64af8882fc9a81cb6aba67");
 		final String name = "v1.2.3.4.5";
-		final String message = "test\n" //
-				+ "\n" //
-				+ "-----BEGIN PGP SIGNATURE-----\n" //
+		final String fakeSignature = "-----BEGIN PGP SIGNATURE-----\n" //
 				+ "Version: GnuPG v1.4.1 (GNU/Linux)\n" //
 				+ "\n" //
 				+ "iD8DBQBC0b9oF3Y\n" //
-				+ "-----END PGP SIGNATURE------n";
+				+ "-----END PGP SIGNATURE-----";
+		final String message = "test\n\n" + fakeSignature + '\n';
 
 		final StringBuilder body = new StringBuilder();
 
@@ -167,6 +169,8 @@
 		assertEquals(name, c.getTagName());
 		assertEquals("test", c.getShortMessage());
 		assertEquals(message, c.getFullMessage());
+		assertEquals(fakeSignature + '\n',
+				new String(c.getRawGpgSignature(), US_ASCII));
 
 		assertNull(c.getTaggerIdent());
 	}
@@ -386,6 +390,108 @@
 	}
 
 	@Test
+	public void testParse_gpgSignature() throws Exception {
+		final String signature = "-----BEGIN PGP SIGNATURE-----\n\n"
+				+ "wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n"
+				+ "U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n"
+				+ "znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n"
+				+ "wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n"
+				+ "SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n"
+				+ "xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n"
+				+ "=TClh\n" + "-----END PGP SIGNATURE-----";
+		ByteArrayOutputStream b = new ByteArrayOutputStream();
+		b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
+		b.write("type tree\n".getBytes(UTF_8));
+		b.write("tag v1.0\n".getBytes(UTF_8));
+		b.write("tagger t <t@example.com> 1218123387 +0700\n".getBytes(UTF_8));
+		b.write('\n');
+		b.write("message\n\n".getBytes(UTF_8));
+		b.write(signature.getBytes(US_ASCII));
+		b.write('\n');
+
+		RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+		try (RevWalk rw = new RevWalk(db)) {
+			t.parseCanonical(rw, b.toByteArray());
+		}
+
+		assertEquals("t", t.getTaggerIdent().getName());
+		assertEquals("message", t.getShortMessage());
+		assertEquals("message\n\n" + signature + '\n', t.getFullMessage());
+		String gpgSig = new String(t.getRawGpgSignature(), UTF_8);
+		assertEquals(signature + '\n', gpgSig);
+	}
+
+	@Test
+	public void testParse_gpgSignature2() throws Exception {
+		final String signature = "-----BEGIN PGP SIGNATURE-----\n\n"
+				+ "wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n"
+				+ "U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n"
+				+ "znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n"
+				+ "wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n"
+				+ "SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n"
+				+ "xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n"
+				+ "=TClh\n" + "-----END PGP SIGNATURE-----";
+		ByteArrayOutputStream b = new ByteArrayOutputStream();
+		b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
+		b.write("type tree\n".getBytes(UTF_8));
+		b.write("tag v1.0\n".getBytes(UTF_8));
+		b.write("tagger t <t@example.com> 1218123387 +0700\n".getBytes(UTF_8));
+		b.write('\n');
+		String message = "message\n\n" + signature.replace("xXXy", "aAAb")
+				+ '\n';
+		b.write(message.getBytes(UTF_8));
+		b.write(signature.getBytes(US_ASCII));
+		b.write('\n');
+
+		RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+		try (RevWalk rw = new RevWalk(db)) {
+			t.parseCanonical(rw, b.toByteArray());
+		}
+
+		assertEquals("t", t.getTaggerIdent().getName());
+		assertEquals("message", t.getShortMessage());
+		assertEquals(message + signature + '\n', t.getFullMessage());
+		String gpgSig = new String(t.getRawGpgSignature(), UTF_8);
+		assertEquals(signature + '\n', gpgSig);
+	}
+
+	@Test
+	public void testParse_gpgSignature3() throws Exception {
+		final String signature = "-----BEGIN PGP SIGNATURE-----\n\n"
+				+ "wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n"
+				+ "U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n"
+				+ "znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n"
+				+ "wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n"
+				+ "SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n"
+				+ "xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n"
+				+ "=TClh\n" + "-----END PGP SIGNATURE-----";
+		ByteArrayOutputStream b = new ByteArrayOutputStream();
+		b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+				.getBytes(UTF_8));
+		b.write("type tree\n".getBytes(UTF_8));
+		b.write("tag v1.0\n".getBytes(UTF_8));
+		b.write("tagger t <t@example.com> 1218123387 +0700\n".getBytes(UTF_8));
+		b.write('\n');
+		String message = "message\n\n-----BEGIN PGP SIGNATURE-----\n";
+		b.write(message.getBytes(UTF_8));
+		b.write(signature.getBytes(US_ASCII));
+		b.write('\n');
+
+		RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+		try (RevWalk rw = new RevWalk(db)) {
+			t.parseCanonical(rw, b.toByteArray());
+		}
+
+		assertEquals("t", t.getTaggerIdent().getName());
+		assertEquals("message", t.getShortMessage());
+		assertEquals(message + signature + '\n', t.getFullMessage());
+		String gpgSig = new String(t.getRawGpgSignature(), UTF_8);
+		assertEquals(signature + '\n', gpgSig);
+	}
+
+	@Test
 	public void testParse_NoMessage() throws Exception {
 		final String msg = "";
 		final RevTag c = create(msg);
@@ -447,7 +553,8 @@
 	}
 
 	@Test
-	public void testParse_PublicParseMethod() throws CorruptObjectException {
+	public void testParse_PublicParseMethod()
+			throws CorruptObjectException, UnsupportedEncodingException {
 		TagBuilder src = new TagBuilder();
 		try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
 			src.setObjectId(fmt.idFor(Constants.OBJ_TREE, new byte[] {}),
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 70175c8..ea994f0 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
@@ -455,6 +455,7 @@
 		final CanonicalTreeParser p = new CanonicalTreeParser();
 		p.reset(testDb.getRevWalk().getObjectReader(), commit.getTree());
 		try (SubmoduleWalk gen = SubmoduleWalk.forPath(db, p, "sub")) {
+			assertEquals(arbitraryName, gen.getModuleName());
 			assertEquals(path, gen.getPath());
 			assertEquals(subId, gen.getObjectId());
 			assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java
new file mode 100644
index 0000000..64b16f6
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BasePackConnectionTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020, Lee Worrall 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasKey;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.junit.Test;
+
+public class BasePackConnectionTest {
+
+	@Test
+	public void testExtractSymRefsFromCapabilities() {
+		final Map<String, String> symRefs = BasePackConnection
+				.extractSymRefsFromCapabilities(
+						Arrays.asList("symref=HEAD:refs/heads/main",
+								"symref=refs/heads/sym:refs/heads/other"));
+
+		assertEquals(2, symRefs.size());
+		assertEquals("refs/heads/main", symRefs.get("HEAD"));
+		assertEquals("refs/heads/other", symRefs.get("refs/heads/sym"));
+	}
+
+	@Test
+	public void testUpdateWithSymRefsAdds() {
+		final Ref mainRef = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE,
+				"refs/heads/main", ObjectId.fromString(
+						"0000000000000000000000000000000000000001"));
+
+		final Map<String, Ref> refMap = new HashMap<>();
+		refMap.put(mainRef.getName(), mainRef);
+		refMap.put("refs/heads/other",
+				new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/other",
+						ObjectId.fromString(
+								"0000000000000000000000000000000000000002")));
+
+		final Map<String, String> symRefs = new HashMap<>();
+		symRefs.put("HEAD", "refs/heads/main");
+
+		BasePackConnection.updateWithSymRefs(refMap, symRefs);
+
+		assertThat(refMap, hasKey("HEAD"));
+		final Ref headRef = refMap.get("HEAD");
+		assertThat(headRef, instanceOf(SymbolicRef.class));
+		final SymbolicRef headSymRef = (SymbolicRef) headRef;
+		assertEquals("HEAD", headSymRef.getName());
+		assertSame(mainRef, headSymRef.getTarget());
+	}
+
+	@Test
+	public void testUpdateWithSymRefsReplaces() {
+		final Ref mainRef = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE,
+				"refs/heads/main", ObjectId.fromString(
+						"0000000000000000000000000000000000000001"));
+
+		final Map<String, Ref> refMap = new HashMap<>();
+		refMap.put(mainRef.getName(), mainRef);
+		refMap.put("HEAD", new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "HEAD",
+				mainRef.getObjectId()));
+		refMap.put("refs/heads/other",
+				new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/other",
+						ObjectId.fromString(
+								"0000000000000000000000000000000000000002")));
+
+		final Map<String, String> symRefs = new HashMap<>();
+		symRefs.put("HEAD", "refs/heads/main");
+
+		BasePackConnection.updateWithSymRefs(refMap, symRefs);
+
+		assertThat(refMap, hasKey("HEAD"));
+		final Ref headRef = refMap.get("HEAD");
+		assertThat(headRef, instanceOf(SymbolicRef.class));
+		final SymbolicRef headSymRef = (SymbolicRef) headRef;
+		assertEquals("HEAD", headSymRef.getName());
+		assertSame(mainRef, headSymRef.getTarget());
+	}
+
+	@Test
+	public void testUpdateWithSymRefsWithIndirectsAdds() {
+		final Ref mainRef = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE,
+				"refs/heads/main", ObjectId.fromString(
+						"0000000000000000000000000000000000000001"));
+
+		final Map<String, Ref> refMap = new HashMap<>();
+		refMap.put(mainRef.getName(), mainRef);
+		refMap.put("refs/heads/other",
+				new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/other",
+						ObjectId.fromString(
+								"0000000000000000000000000000000000000002")));
+
+		final Map<String, String> symRefs = new LinkedHashMap<>(); // Ordered
+		symRefs.put("refs/heads/sym3", "refs/heads/sym2"); // Forward reference
+		symRefs.put("refs/heads/sym1", "refs/heads/main");
+		symRefs.put("refs/heads/sym2", "refs/heads/sym1"); // Backward reference
+
+		BasePackConnection.updateWithSymRefs(refMap, symRefs);
+
+		assertThat(refMap, hasKey("refs/heads/sym1"));
+		final Ref sym1Ref = refMap.get("refs/heads/sym1");
+		assertThat(sym1Ref, instanceOf(SymbolicRef.class));
+		final SymbolicRef sym1SymRef = (SymbolicRef) sym1Ref;
+		assertEquals("refs/heads/sym1", sym1SymRef.getName());
+		assertSame(mainRef, sym1SymRef.getTarget());
+
+		assertThat(refMap, hasKey("refs/heads/sym2"));
+		final Ref sym2Ref = refMap.get("refs/heads/sym2");
+		assertThat(sym2Ref, instanceOf(SymbolicRef.class));
+		final SymbolicRef sym2SymRef = (SymbolicRef) sym2Ref;
+		assertEquals("refs/heads/sym2", sym2SymRef.getName());
+		assertSame(sym1SymRef, sym2SymRef.getTarget());
+
+		assertThat(refMap, hasKey("refs/heads/sym3"));
+		final Ref sym3Ref = refMap.get("refs/heads/sym3");
+		assertThat(sym3Ref, instanceOf(SymbolicRef.class));
+		final SymbolicRef sym3SymRef = (SymbolicRef) sym3Ref;
+		assertEquals("refs/heads/sym3", sym3SymRef.getName());
+		assertSame(sym2SymRef, sym3SymRef.getTarget());
+	}
+
+	@Test
+	public void testUpdateWithSymRefsWithIndirectsReplaces() {
+		final Ref mainRef = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE,
+				"refs/heads/main", ObjectId.fromString(
+						"0000000000000000000000000000000000000001"));
+
+		final Map<String, Ref> refMap = new HashMap<>();
+		refMap.put(mainRef.getName(), mainRef);
+		refMap.put("refs/heads/sym1", new ObjectIdRef.Unpeeled(
+				Ref.Storage.LOOSE, "refs/heads/sym1", mainRef.getObjectId()));
+		refMap.put("refs/heads/sym2", new ObjectIdRef.Unpeeled(
+				Ref.Storage.LOOSE, "refs/heads/sym2", mainRef.getObjectId()));
+		refMap.put("refs/heads/sym3", new ObjectIdRef.Unpeeled(
+				Ref.Storage.LOOSE, "refs/heads/sym3", mainRef.getObjectId()));
+		refMap.put("refs/heads/other",
+				new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/other",
+						ObjectId.fromString(
+								"0000000000000000000000000000000000000002")));
+
+		final Map<String, String> symRefs = new LinkedHashMap<>(); // Ordered
+		symRefs.put("refs/heads/sym3", "refs/heads/sym2"); // Forward reference
+		symRefs.put("refs/heads/sym1", "refs/heads/main");
+		symRefs.put("refs/heads/sym2", "refs/heads/sym1"); // Backward reference
+
+		BasePackConnection.updateWithSymRefs(refMap, symRefs);
+
+		assertThat(refMap, hasKey("refs/heads/sym1"));
+		final Ref sym1Ref = refMap.get("refs/heads/sym1");
+		assertThat(sym1Ref, instanceOf(SymbolicRef.class));
+		final SymbolicRef sym1SymRef = (SymbolicRef) sym1Ref;
+		assertEquals("refs/heads/sym1", sym1SymRef.getName());
+		assertSame(mainRef, sym1SymRef.getTarget());
+
+		assertThat(refMap, hasKey("refs/heads/sym2"));
+		final Ref sym2Ref = refMap.get("refs/heads/sym2");
+		assertThat(sym2Ref, instanceOf(SymbolicRef.class));
+		final SymbolicRef sym2SymRef = (SymbolicRef) sym2Ref;
+		assertEquals("refs/heads/sym2", sym2SymRef.getName());
+		assertSame(sym1SymRef, sym2SymRef.getTarget());
+
+		assertThat(refMap, hasKey("refs/heads/sym3"));
+		final Ref sym3Ref = refMap.get("refs/heads/sym3");
+		assertThat(sym3Ref, instanceOf(SymbolicRef.class));
+		final SymbolicRef sym3SymRef = (SymbolicRef) sym3Ref;
+		assertEquals("refs/heads/sym3", sym3SymRef.getName());
+		assertSame(sym2SymRef, sym3SymRef.getTarget());
+	}
+
+	@Test
+	public void testUpdateWithSymRefsIgnoresSelfReference() {
+		final Ref mainRef = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE,
+				"refs/heads/main", ObjectId.fromString(
+						"0000000000000000000000000000000000000001"));
+
+		final Map<String, Ref> refMap = new HashMap<>();
+		refMap.put(mainRef.getName(), mainRef);
+		refMap.put("refs/heads/other",
+				new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/other",
+						ObjectId.fromString(
+								"0000000000000000000000000000000000000002")));
+
+		final Map<String, String> symRefs = new LinkedHashMap<>();
+		symRefs.put("refs/heads/sym1", "refs/heads/sym1");
+
+		BasePackConnection.updateWithSymRefs(refMap, symRefs);
+
+		assertEquals(2, refMap.size());
+		assertThat(refMap, not(hasKey("refs/heads/sym1")));
+	}
+
+	@Test
+	public void testUpdateWithSymRefsIgnoreCircularReference() {
+		final Ref mainRef = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE,
+				"refs/heads/main", ObjectId.fromString(
+						"0000000000000000000000000000000000000001"));
+
+		final Map<String, Ref> refMap = new HashMap<>();
+		refMap.put(mainRef.getName(), mainRef);
+		refMap.put("refs/heads/other",
+				new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/other",
+						ObjectId.fromString(
+								"0000000000000000000000000000000000000002")));
+
+		final Map<String, String> symRefs = new LinkedHashMap<>();
+		symRefs.put("refs/heads/sym2", "refs/heads/sym1");
+		symRefs.put("refs/heads/sym1", "refs/heads/sym2");
+
+		BasePackConnection.updateWithSymRefs(refMap, symRefs);
+
+		assertEquals(2, refMap.size());
+		assertThat(refMap, not(hasKey("refs/heads/sym1")));
+		assertThat(refMap, not(hasKey("refs/heads/sym2")));
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpConfigTest.java
index fcbec52..5336dd7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpConfigTest.java
@@ -13,7 +13,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import org.eclipse.jgit.junit.MockSystemReader;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.util.SystemReader;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -25,6 +27,7 @@
 
 	private static final String DEFAULT = "[http]\n" + "\tpostBuffer = 1\n"
 			+ "\tsslVerify= true\n" + "\tfollowRedirects = true\n"
+			+ "\textraHeader = x: y\n" + "\tuserAgent = Test/0.1\n"
 			+ "\tmaxRedirects = 5\n\n";
 
 	private Config config;
@@ -95,7 +98,8 @@
 	@Test
 	public void testMatchWithInvalidUriInConfig() throws Exception {
 		config.fromText(
-				DEFAULT + "[http \"///\"]\n" + "\tpostBuffer = 1024\n");
+				DEFAULT + "[http \"///#expectedWarning\"]\n"
+						+ "\tpostBuffer = 1024\n");
 		HttpConfig http = new HttpConfig(config,
 				new URIish("http://example.com/path/repo.git"));
 		assertEquals(1, http.getPostBuffer());
@@ -103,7 +107,8 @@
 
 	@Test
 	public void testMatchWithInvalidAndValidUriInConfig() throws Exception {
-		config.fromText(DEFAULT + "[http \"///\"]\n" + "\tpostBuffer = 1024\n"
+		config.fromText(DEFAULT + "[http \"///#expectedWarning\"]\n"
+				+ "\tpostBuffer = 1024\n"
 				+ "[http \"http://example.com\"]\n" + "\tpostBuffer = 2048\n");
 		HttpConfig http = new HttpConfig(config,
 				new URIish("http://example.com/path/repo.git"));
@@ -174,4 +179,93 @@
 				new URIish("http://user@example.com/path"));
 		assertEquals(1024, http.getPostBuffer());
 	}
+
+	@Test
+	public void testExtraHeaders() throws Exception {
+		config.fromText(DEFAULT + "[http \"http://example.com\"]\n"
+				+ "\textraHeader=foo: bar\n");
+		HttpConfig http = new HttpConfig(config,
+				new URIish("http://example.com/"));
+		assertEquals(1, http.getExtraHeaders().size());
+		assertEquals("foo: bar", http.getExtraHeaders().get(0));
+	}
+
+	@Test
+	public void testExtraHeadersMultiple() throws Exception {
+		config.fromText(DEFAULT + "[http \"http://example.com\"]\n"
+				+ "\textraHeader=foo: bar\n" //
+				+ "\textraHeader=bar: foo\n");
+		HttpConfig http = new HttpConfig(config,
+				new URIish("http://example.com/"));
+		assertEquals(2, http.getExtraHeaders().size());
+		assertEquals("foo: bar", http.getExtraHeaders().get(0));
+		assertEquals("bar: foo", http.getExtraHeaders().get(1));
+	}
+
+	@Test
+	public void testExtraHeadersReset() throws Exception {
+		config.fromText(DEFAULT + "[http \"http://example.com\"]\n"
+				+ "\textraHeader=foo: bar\n" //
+				+ "\textraHeader=bar: foo\n" //
+				+ "\textraHeader=\n");
+		HttpConfig http = new HttpConfig(config,
+				new URIish("http://example.com/"));
+		assertTrue(http.getExtraHeaders().isEmpty());
+	}
+
+	@Test
+	public void testExtraHeadersResetAndMore() throws Exception {
+		config.fromText(DEFAULT + "[http \"http://example.com\"]\n"
+				+ "\textraHeader=foo: bar\n" //
+				+ "\textraHeader=bar: foo\n" //
+				+ "\textraHeader=\n" //
+				+ "\textraHeader=baz: something\n");
+		HttpConfig http = new HttpConfig(config,
+				new URIish("http://example.com/"));
+		assertEquals(1, http.getExtraHeaders().size());
+		assertEquals("baz: something", http.getExtraHeaders().get(0));
+	}
+
+	@Test
+	public void testUserAgent() throws Exception {
+		config.fromText(DEFAULT + "[http \"http://example.com\"]\n"
+				+ "\tuserAgent=DummyAgent/4.0\n");
+		HttpConfig http = new HttpConfig(config,
+				new URIish("http://example.com/"));
+		assertEquals("DummyAgent/4.0", http.getUserAgent());
+	}
+
+	@Test
+	public void testUserAgentEnvOverride() throws Exception {
+		String mockAgent = "jgit-test/5.10.0";
+		SystemReader originalReader = SystemReader.getInstance();
+		SystemReader.setInstance(new MockSystemReader() {
+
+			@Override
+			public String getenv(String variable) {
+				if ("GIT_HTTP_USER_AGENT".equals(variable)) {
+					return mockAgent;
+				}
+				return super.getenv(variable);
+			}
+		});
+		try {
+			config.fromText(DEFAULT + "[http \"http://example.com\"]\n"
+					+ "\tuserAgent=DummyAgent/4.0\n");
+			HttpConfig http = new HttpConfig(config,
+					new URIish("http://example.com/"));
+			assertEquals(mockAgent, http.getUserAgent());
+		} finally {
+			SystemReader.setInstance(originalReader);
+		}
+	}
+
+	@Test
+	public void testUserAgentNonAscii() throws Exception {
+		config.fromText(DEFAULT + "[http \"http://example.com\"]\n"
+				+ "\tuserAgent= d ümmy Agent -5.10\n");
+		HttpConfig http = new HttpConfig(config,
+				new URIish("http://example.com/"));
+		assertEquals("d.mmy.Agent.-5.10", http.getUserAgent());
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java
index 5d7f881..7f03357 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java
@@ -12,8 +12,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.ByteArrayInputStream;
@@ -111,20 +111,25 @@
 		final String act = in.readString();
 		assertEquals("", act);
 		assertFalse(PacketLineIn.isEnd(act));
+		assertFalse(PacketLineIn.isDelimiter(act));
 		assertEOF();
 	}
 
 	@Test
 	public void testReadString_End() throws IOException {
 		init("0000");
-		assertTrue(PacketLineIn.isEnd(in.readString()));
+		String act = in.readString();
+		assertTrue(PacketLineIn.isEnd(act));
+		assertFalse(PacketLineIn.isDelimiter(act));
 		assertEOF();
 	}
 
 	@Test
 	public void testReadString_Delim() throws IOException {
 		init("0001");
-		assertTrue(PacketLineIn.isDelimiter(in.readString()));
+		String act = in.readString();
+		assertTrue(PacketLineIn.isDelimiter(act));
+		assertFalse(PacketLineIn.isEnd(act));
 		assertEOF();
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
index b84b6b2..029b45e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
@@ -18,7 +18,9 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.Set;
 
 import org.eclipse.jgit.internal.transport.http.NetscapeCookieFile;
@@ -155,4 +157,50 @@
 					cookieFile.exists());
 		}
 	}
+
+	private void assertHeaders(String expected, String... headersToAdd) {
+		HttpConnection fake = Mockito.mock(HttpConnection.class);
+		Map<String, String> headers = new LinkedHashMap<>();
+		Mockito.doAnswer(invocation -> {
+			Object[] args = invocation.getArguments();
+			headers.put(args[0].toString(), args[1].toString());
+			return null;
+		}).when(fake).setRequestProperty(ArgumentMatchers.anyString(),
+				ArgumentMatchers.anyString());
+		TransportHttp.addHeaders(fake, Arrays.asList(headersToAdd));
+		Assert.assertEquals(expected, headers.toString());
+	}
+
+	@Test
+	public void testAddHeaders() {
+		assertHeaders("{a=b, c=d}", "a: b", "c :d");
+	}
+
+	@Test
+	public void testAddHeaderEmptyValue() {
+		assertHeaders("{a-x=b, c=, d=e}", "a-x: b", "c:", "d:e");
+	}
+
+	@Test
+	public void testSkipHeaderWithEmptyKey() {
+		assertHeaders("{a=b, c=d}", "a: b", " : x", "c :d");
+		assertHeaders("{a=b, c=d}", "a: b", ": x", "c :d");
+	}
+
+	@Test
+	public void testSkipHeaderWithoutKey() {
+		assertHeaders("{a=b, c=d}", "a: b", "x", "c :d");
+	}
+
+	@Test
+	public void testSkipHeaderWithInvalidKey() {
+		assertHeaders("{a=b, c=d}", "a: b", "q/p: x", "c :d");
+		assertHeaders("{a=b, c=d}", "a: b", "ä: x", "c :d");
+	}
+
+	@Test
+	public void testSkipHeaderWithNonAsciiValue() {
+		assertHeaders("{a=b, c=d}", "a: b", "q/p: x", "c :d");
+		assertHeaders("{a=b, c=d}", "a: b", "x: ä", "c :d");
+	}
 }
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 46ba3aa..5045e94 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
@@ -362,23 +362,22 @@
 	}
 
 	/*
-	 * Invokes UploadPack with protocol v2 and sends it the given lines,
+	 * Invokes UploadPack with specified protocol version and sends it the given lines,
 	 * and returns UploadPack's output stream.
 	 */
-	private ByteArrayInputStream uploadPackV2Setup(
+	private ByteArrayInputStream uploadPackSetup(String version,
 			Consumer<UploadPack> postConstructionSetup, String... inputLines)
 			throws Exception {
 
 		ByteArrayInputStream send = linesAsInputStream(inputLines);
 
 		server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION,
-				null, ConfigConstants.CONFIG_KEY_VERSION,
-				TransferConfig.ProtocolVersion.V2.version());
+				null, ConfigConstants.CONFIG_KEY_VERSION, version);
 		UploadPack up = new UploadPack(server);
 		if (postConstructionSetup != null) {
 			postConstructionSetup.accept(up);
 		}
-		up.setExtraParameters(Sets.of("version=2"));
+		up.setExtraParameters(Sets.of("version=".concat(version)));
 
 		ByteArrayOutputStream recv = new ByteArrayOutputStream();
 		up.upload(send, recv, null);
@@ -406,6 +405,30 @@
 	}
 
 	/*
+	 * Invokes UploadPack with protocol v1 and sends it the given lines.
+	 * Returns UploadPack's output stream, not including the capability
+	 * advertisement by the server.
+	 */
+	private ByteArrayInputStream uploadPackV1(
+			Consumer<UploadPack> postConstructionSetup,
+			String... inputLines)
+			throws Exception {
+		ByteArrayInputStream recvStream =
+				uploadPackSetup("1", postConstructionSetup, inputLines);
+		PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+		// drain capabilities
+		while (!PacketLineIn.isEnd(pckIn.readString())) {
+			// do nothing
+		}
+		return recvStream;
+	}
+
+	private ByteArrayInputStream uploadPackV1(String... inputLines) throws Exception {
+		return uploadPackV1(null, inputLines);
+	}
+
+	/*
 	 * Invokes UploadPack with protocol v2 and sends it the given lines.
 	 * Returns UploadPack's output stream, not including the capability
 	 * advertisement by the server.
@@ -414,8 +437,9 @@
 			Consumer<UploadPack> postConstructionSetup,
 			String... inputLines)
 			throws Exception {
-		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(postConstructionSetup, inputLines);
+		ByteArrayInputStream recvStream = uploadPackSetup(
+				TransferConfig.ProtocolVersion.V2.version(),
+				postConstructionSetup, inputLines);
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		// drain capabilities
@@ -455,9 +479,11 @@
 	@Test
 	public void testV2Capabilities() throws Exception {
 		TestV2Hook hook = new TestV2Hook();
-		ByteArrayInputStream recvStream = uploadPackV2Setup(
-				(UploadPack up) -> {up.setProtocolV2Hook(hook);},
-				PacketLineIn.end());
+		ByteArrayInputStream recvStream = uploadPackSetup(
+				TransferConfig.ProtocolVersion.V2.version(),
+				(UploadPack up) -> {
+					up.setProtocolV2Hook(hook);
+				}, PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 		assertThat(hook.capabilitiesRequest, notNullValue());
 		assertThat(pckIn.readString(), is("version 2"));
@@ -477,8 +503,9 @@
 	private void checkAdvertisedIfAllowed(String configSection, String configName,
 			String fetchCapability) throws Exception {
 		server.getConfig().setBoolean(configSection, null, configName, true);
-		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(null, PacketLineIn.end());
+		ByteArrayInputStream recvStream = uploadPackSetup(
+				TransferConfig.ProtocolVersion.V2.version(), null,
+				PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		assertThat(pckIn.readString(), is("version 2"));
@@ -501,8 +528,9 @@
 	private void checkUnadvertisedIfUnallowed(String configSection,
 			String configName, String fetchCapability) throws Exception {
 		server.getConfig().setBoolean(configSection, null, configName, false);
-		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(null, PacketLineIn.end());
+		ByteArrayInputStream recvStream = uploadPackSetup(
+				TransferConfig.ProtocolVersion.V2.version(), null,
+				PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		assertThat(pckIn.readString(), is("version 2"));
@@ -553,8 +581,9 @@
 	public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() throws Exception {
 		server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
 		server.getConfig().setBoolean("uploadpack", null, "advertiserefinwant", false);
-		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(null, PacketLineIn.end());
+		ByteArrayInputStream recvStream = uploadPackSetup(
+				TransferConfig.ProtocolVersion.V2.version(), null,
+				PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		assertThat(pckIn.readString(), is("version 2"));
@@ -718,7 +747,10 @@
 				PacketLineIn.end() };
 
 		TestV2Hook testHook = new TestV2Hook();
-		uploadPackV2Setup((UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
+		uploadPackSetup(TransferConfig.ProtocolVersion.V2.version(),
+				(UploadPack up) -> {
+					up.setProtocolV2Hook(testHook);
+				}, lines);
 
 		LsRefsV2Request req = testHook.lsRefsRequest;
 		assertEquals(2, req.getServerOptions().size());
@@ -1063,6 +1095,70 @@
 	}
 
 	@Test
+	public void testUploadNewBytes() throws Exception {
+		String commonInBlob = "abcdefghijklmnopqrstuvwx";
+
+		RevBlob parentBlob = remote.blob(commonInBlob + "a");
+		RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
+		RevBlob childBlob = remote.blob(commonInBlob + "b");
+		RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
+		remote.update("branch1", child);
+
+		ByteArrayInputStream recvStream = uploadPackV2(
+			"command=fetch\n",
+			PacketLineIn.delimiter(),
+			"want " + child.toObjectId().getName() + "\n",
+			"ofs-delta\n",
+			"done\n",
+				PacketLineIn.end());
+		PacketLineIn pckIn = new PacketLineIn(recvStream);
+		assertThat(pckIn.readString(), is("packfile"));
+		ReceivedPackStatistics receivedStats = parsePack(recvStream);
+		assertTrue(receivedStats.getNumBytesDuplicated() == 0);
+		assertTrue(receivedStats.getNumObjectsDuplicated() == 0);
+	}
+
+	@Test
+	public void testUploadRedundantBytes() throws Exception {
+		String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+		RevBlob parentBlob = remote.blob(commonInBlob + "a");
+		RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
+		RevBlob childBlob = remote.blob(commonInBlob + "b");
+		RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
+		remote.update("branch1", child);
+
+		try (TestRepository<InMemoryRepository> local = new TestRepository<>(
+				client)) {
+			RevBlob localParentBlob = local.blob(commonInBlob + "a");
+			RevCommit localParent = local
+					.commit(local.tree(local.file("foo", localParentBlob)));
+			RevBlob localChildBlob = local.blob(commonInBlob + "b");
+			RevCommit localChild = local.commit(
+					local.tree(local.file("foo", localChildBlob)), localParent);
+			local.update("branch1", localChild);
+		}
+
+		ByteArrayInputStream recvStream = uploadPackV2(
+				"command=fetch\n",
+				PacketLineIn.delimiter(),
+				"want " + child.toObjectId().getName() + "\n",
+				"ofs-delta\n",
+				"done\n",
+					PacketLineIn.end());
+		PacketLineIn pckIn = new PacketLineIn(recvStream);
+		assertThat(pckIn.readString(), is("packfile"));
+		ReceivedPackStatistics receivedStats = parsePack(recvStream);
+
+		long sizeOfHeader = 12;
+		long sizeOfTrailer = 20;
+		long expectedSize = receivedStats.getNumBytesRead() - sizeOfHeader
+				- sizeOfTrailer;
+		assertTrue(receivedStats.getNumBytesDuplicated() == expectedSize);
+		assertTrue(receivedStats.getNumObjectsDuplicated() == 6);
+	}
+
+	@Test
 	public void testV2FetchOfsDelta() throws Exception {
 		String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
 
@@ -1474,7 +1570,10 @@
 				PacketLineIn.end() };
 
 		TestV2Hook testHook = new TestV2Hook();
-		uploadPackV2Setup((UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
+		uploadPackSetup(TransferConfig.ProtocolVersion.V2.version(),
+				(UploadPack up) -> {
+					up.setProtocolV2Hook(testHook);
+				}, lines);
 
 		FetchV2Request req = testHook.fetchRequest;
 		assertNotNull(req);
@@ -2329,6 +2428,93 @@
 		assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
 	}
 
+	@Test
+	public void testNotAdvertisedWantsV1Fetch() throws Exception {
+		String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+		RevBlob parentBlob = remote.blob(commonInBlob + "a");
+		RevCommit parent = remote
+				.commit(remote.tree(remote.file("foo", parentBlob)));
+		RevBlob childBlob = remote.blob(commonInBlob + "b");
+		RevCommit child = remote
+				.commit(remote.tree(remote.file("foo", childBlob)), parent);
+		remote.update("branch1", child);
+
+		uploadPackV1("want " + child.toObjectId().getName() + "\n",
+				PacketLineIn.end(),
+				"have " + parent.toObjectId().getName() + "\n",
+				"done\n", PacketLineIn.end());
+
+		assertEquals(0, stats.getNotAdvertisedWants());
+	}
+
+	@Test
+	public void testNotAdvertisedWantsV1FetchRequestPolicyReachableCommit() throws Exception {
+		String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+		RevBlob parentBlob = remote.blob(commonInBlob + "a");
+		RevCommit parent = remote
+				.commit(remote.tree(remote.file("foo", parentBlob)));
+		RevBlob childBlob = remote.blob(commonInBlob + "b");
+		RevCommit child = remote
+				.commit(remote.tree(remote.file("foo", childBlob)), parent);
+
+		remote.update("branch1", child);
+
+		uploadPackV1((UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
+				"want " + parent.toObjectId().getName() + "\n",
+				PacketLineIn.end(),
+				"done\n", PacketLineIn.end());
+
+		assertEquals(1, stats.getNotAdvertisedWants());
+	}
+
+	@Test
+	public void testNotAdvertisedWantsV2FetchThinPack() throws Exception {
+		String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+		RevBlob parentBlob = remote.blob(commonInBlob + "a");
+		RevCommit parent = remote
+				.commit(remote.tree(remote.file("foo", parentBlob)));
+		RevBlob childBlob = remote.blob(commonInBlob + "b");
+		RevCommit child = remote
+				.commit(remote.tree(remote.file("foo", childBlob)), parent);
+		remote.update("branch1", child);
+
+		ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
+				PacketLineIn.delimiter(),
+				"want " + child.toObjectId().getName() + "\n",
+				"have " + parent.toObjectId().getName() + "\n", "thin-pack\n",
+				"done\n", PacketLineIn.end());
+		PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+		assertThat(pckIn.readString(), is("packfile"));
+
+		assertEquals(0, stats.getNotAdvertisedWants());
+	}
+
+	@Test
+	public void testNotAdvertisedWantsV2FetchRequestPolicyReachableCommit() throws Exception {
+		String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
+
+		RevBlob parentBlob = remote.blob(commonInBlob + "a");
+		RevCommit parent = remote
+				.commit(remote.tree(remote.file("foo", parentBlob)));
+		RevBlob childBlob = remote.blob(commonInBlob + "b");
+		RevCommit child = remote
+				.commit(remote.tree(remote.file("foo", childBlob)), parent);
+
+		remote.update("branch1", child);
+
+		uploadPackV2((UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
+				"command=fetch\n",
+				PacketLineIn.delimiter(),
+				"want " + parent.toObjectId().getName() + "\n", "thin-pack\n",
+				"done\n", PacketLineIn.end());
+
+		assertEquals(1, stats.getNotAdvertisedWants());
+	}
+
 	private class RefCallsCountingRepository extends InMemoryRepository {
 		private final InMemoryRepository.MemRefDatabase refdb;
 		private int numRefCalls;
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index d9b263f..3b3acd5 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="5.9.1"
-Import-Package: org.eclipse.jgit.errors;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.lib;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.nls;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revplot;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.revwalk;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.transport;version="[5.9.1,5.10.0)",
- org.eclipse.jgit.util;version="[5.9.1,5.10.0)"
+Export-Package: org.eclipse.jgit.awtui;version="5.11.0"
+Import-Package: org.eclipse.jgit.errors;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.lib;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.nls;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revplot;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.revwalk;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util;version="[5.11.0,5.12.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
index 52de826..f5db7d4 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ui;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 3c5c137..5acd545 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -19,7 +19,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-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
new file mode 100644
index 0000000..035ed37
--- /dev/null
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jgit" version="2">
+    <resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants">
+        <filter id="1141899266">
+            <message_arguments>
+                <message_argument value="5.9"/>
+                <message_argument value="5.10"/>
+                <message_argument value="CONFIG_PROTOCOL_SECTION"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/lib/TypedConfigGetter.java" type="org.eclipse.jgit.lib.TypedConfigGetter">
+        <filter id="404000815">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+                <message_argument value="getPath(Config, String, String, String, FS, File, Path)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <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="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/>
+                <message_argument value="notAdvertisedWants"/>
+            </message_arguments>
+        </filter>
+        <filter id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/>
+                <message_argument value="reachabilityCheckDuration"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/transport/HttpConfig.java" type="org.eclipse.jgit.transport.HttpConfig">
+        <filter id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.HttpConfig"/>
+                <message_argument value="EXTRA_HEADER"/>
+            </message_arguments>
+        </filter>
+        <filter id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.HttpConfig"/>
+                <message_argument value="USER_AGENT"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/transport/TransferConfig.java" type="org.eclipse.jgit.transport.TransferConfig$ProtocolVersion">
+        <filter id="1175453698">
+            <message_arguments>
+                <message_argument value="5.9"/>
+                <message_argument value="5.10"/>
+                <message_argument value="org.eclipse.jgit.transport.TransferConfig.ProtocolVersion"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 1348bde..c85cb89 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.9.1.qualifier
+Bundle-Version: 5.11.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %Bundle-Vendor
 Eclipse-ExtensibleAPI: true
-Export-Package: org.eclipse.jgit.annotations;version="5.9.1",
- org.eclipse.jgit.api;version="5.9.1";
+Export-Package: org.eclipse.jgit.annotations;version="5.11.0",
+ org.eclipse.jgit.api;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.notes,
    org.eclipse.jgit.dircache,
@@ -23,18 +23,18 @@
    org.eclipse.jgit.revwalk.filter,
    org.eclipse.jgit.blame,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="5.9.1";
+ org.eclipse.jgit.api.errors;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="5.9.1";
+ org.eclipse.jgit.attributes;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.blame;version="5.9.1";
+ org.eclipse.jgit.blame;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="5.9.1";
+ org.eclipse.jgit.diff;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.revwalk,
@@ -42,47 +42,47 @@
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="5.9.1";
+ org.eclipse.jgit.dircache;version="5.11.0";
   uses:="org.eclipse.jgit.events,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.errors;version="5.9.1";
+ org.eclipse.jgit.errors;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.internal.storage.pack",
- org.eclipse.jgit.events;version="5.9.1";
+ org.eclipse.jgit.events;version="5.11.0";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="5.9.1",
- org.eclipse.jgit.gitrepo;version="5.9.1";
+ org.eclipse.jgit.fnmatch;version="5.11.0",
+ org.eclipse.jgit.gitrepo;version="5.11.0";
   uses:="org.xml.sax.helpers,
    org.eclipse.jgit.api,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="5.9.1";x-internal:=true,
- org.eclipse.jgit.hooks;version="5.9.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="5.9.1",
- org.eclipse.jgit.ignore.internal;version="5.9.1";
+ org.eclipse.jgit.gitrepo.internal;version="5.11.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="5.11.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="5.11.0",
+ org.eclipse.jgit.ignore.internal;version="5.11.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="5.9.1";
+ org.eclipse.jgit.internal;version="5.11.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="5.9.1";
+ org.eclipse.jgit.internal.fsck;version="5.11.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="5.9.1";
+ org.eclipse.jgit.internal.ketch;version="5.11.0";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.revwalk;version="5.9.1";x-internal:=true,
- org.eclipse.jgit.internal.storage.dfs;version="5.9.1";
+ org.eclipse.jgit.internal.revwalk;version="5.11.0";x-internal:=true,
+ org.eclipse.jgit.internal.storage.dfs;version="5.11.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.server,
    org.eclipse.jgit.http.test,
    org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="5.9.1";
+ org.eclipse.jgit.internal.storage.file;version="5.11.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -91,35 +91,35 @@
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test,
    org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="5.9.1";
+ org.eclipse.jgit.internal.storage.io;version="5.11.0";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="5.9.1";
+ org.eclipse.jgit.internal.storage.pack;version="5.11.0";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="5.9.1";
+ org.eclipse.jgit.internal.storage.reftable;version="5.11.0";
   x-friends:="org.eclipse.jgit.http.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="5.9.1";
+ org.eclipse.jgit.internal.storage.reftree;version="5.11.0";
   x-friends:="org.eclipse.jgit.junit,
    org.eclipse.jgit.test,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="5.9.1";x-internal:=true,
- org.eclipse.jgit.internal.transport.connectivity;version="5.9.1";
+ org.eclipse.jgit.internal.submodule;version="5.11.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.connectivity;version="5.11.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.http;version="5.9.1";
+ org.eclipse.jgit.internal.transport.http;version="5.11.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="5.9.1";
+ org.eclipse.jgit.internal.transport.parser;version="5.11.0";
   x-friends:="org.eclipse.jgit.http.server,
    org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="5.9.1";
+ org.eclipse.jgit.internal.transport.ssh;version="5.11.0";
   x-friends:="org.eclipse.jgit.ssh.apache,
    org.eclipse.jgit.ssh.jsch",
- org.eclipse.jgit.lib;version="5.9.1";
+ org.eclipse.jgit.lib;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util.sha1,
    org.eclipse.jgit.dircache,
@@ -133,9 +133,10 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.lib.internal;version="5.9.1";
+ org.eclipse.jgit.lib.internal;version="5.11.0";
   x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.merge;version="5.9.1";
+ org.eclipse.jgit.logging;version="5.11.0",
+ org.eclipse.jgit.merge;version="5.11.0";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -144,40 +145,40 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.api,
    org.eclipse.jgit.attributes",
- org.eclipse.jgit.nls;version="5.9.1",
- org.eclipse.jgit.notes;version="5.9.1";
+ org.eclipse.jgit.nls;version="5.11.0",
+ org.eclipse.jgit.notes;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="5.9.1";
+ org.eclipse.jgit.patch;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="5.9.1";
+ org.eclipse.jgit.revplot;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="5.9.1";
+ org.eclipse.jgit.revwalk;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.revwalk.filter,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.revwalk.filter;version="5.9.1";
+ org.eclipse.jgit.revwalk.filter;version="5.11.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="5.9.1";
+ org.eclipse.jgit.storage.file;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="5.9.1";
+ org.eclipse.jgit.storage.pack;version="5.11.0";
   uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="5.9.1";
+ org.eclipse.jgit.submodule;version="5.11.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util",
- org.eclipse.jgit.transport;version="5.9.1";
+ org.eclipse.jgit.transport;version="5.11.0";
   uses:="javax.crypto,
    org.eclipse.jgit.util.io,
    org.eclipse.jgit.lib,
@@ -190,21 +191,21 @@
    org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.storage.pack,
    org.eclipse.jgit.errors",
- org.eclipse.jgit.transport.http;version="5.9.1";
+ org.eclipse.jgit.transport.http;version="5.11.0";
   uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="5.9.1";
+ org.eclipse.jgit.transport.resolver;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.lib",
- org.eclipse.jgit.treewalk;version="5.9.1";
+ org.eclipse.jgit.treewalk;version="5.11.0";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util",
- org.eclipse.jgit.treewalk.filter;version="5.9.1";
+ org.eclipse.jgit.treewalk.filter;version="5.11.0";
   uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="5.9.1";
+ org.eclipse.jgit.util;version="5.11.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.hooks,
    org.eclipse.jgit.revwalk,
@@ -217,12 +218,12 @@
    org.eclipse.jgit.treewalk,
    javax.net.ssl,
    org.eclipse.jgit.util.time",
- org.eclipse.jgit.util.io;version="5.9.1";
+ org.eclipse.jgit.util.io;version="5.11.0";
   uses:="org.eclipse.jgit.attributes,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util.sha1;version="5.9.1",
- org.eclipse.jgit.util.time;version="5.9.1"
+ org.eclipse.jgit.util.sha1;version="5.11.0",
+ org.eclipse.jgit.util.time;version="5.11.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
  javax.crypto,
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index c561b7e..e55a69d 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.9.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="5.9.1.qualifier";roots="."
+Bundle-Version: 5.11.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="5.11.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index c899dfd..fa5718d 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>5.9.1-SNAPSHOT</version>
+    <version>5.11.0-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 0e51eb4..2b5f929 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -348,6 +348,9 @@
 invalidGitdirRef = Invalid .git reference in file ''{0}''
 invalidGitModules=Invalid .gitmodules file
 invalidGitType=invalid git type: {0}
+invalidHeaderFormat=Invalid header from git config http.extraHeader ignored: no colon or empty key in header ''{0}''
+invalidHeaderKey=Invalid header from git config http.extraHeader ignored: key contains illegal characters; see RFC 7230: ''{0}''
+invalidHeaderValue=Invalid header from git config http.extraHeader ignored: value should be 7bit-ASCII characters only: ''{0}''
 invalidHexString=Invalid hex string: {0}
 invalidHomeDirectory=Invalid home directory: {0}
 invalidHooksPath=Invalid git config core.hooksPath = {0}
@@ -614,7 +617,7 @@
 shortReadOfBlock=Short read of block.
 shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section.
 shortSkipOfBlock=Short skip of block.
-signingNotSupportedOnTag=Signing isn't supported on tag operations yet.
+signedTagMessageNoLf=A non-empty message of a signed tag must end in LF.
 signingServiceUnavailable=Signing service is not available
 similarityScoreMustBeWithinBounds=Similarity score must be between 0 and 100.
 skipMustBeNonNegative=skip must be >= 0
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 0dc5d5e..847ab0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
- * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> and others
+ * Copyright (C) 2011, 2020 Matthias Sohn <matthias.sohn@sap.com> 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
@@ -503,6 +503,11 @@
 			editor.add(new PathEdit(path) {
 				@Override
 				public void apply(DirCacheEntry ent) {
+					if (ent.getStage() != DirCacheEntry.STAGE_0) {
+						// A checkout on a conflicting file stages the checked
+						// out file and resolves the conflict.
+						ent.setStage(DirCacheEntry.STAGE_0);
+					}
 					ent.setObjectId(blobId);
 					ent.setFileMode(mode);
 					checkoutPath(ent, r,
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 30d7f9a..aba86fc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -413,6 +413,10 @@
 			return null;
 		}
 
+		if (idHEAD != null && idHEAD.isSymbolic()) {
+			return idHEAD.getTarget();
+		}
+
 		Ref master = result.getAdvertisedRef(Constants.R_HEADS
 				+ Constants.MASTER);
 		ObjectId objectId = master != null ? master.getObjectId() : null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index 8d42738..6a9fbd4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -9,6 +9,7 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.eclipse.jgit.lib.Constants.R_REFS;
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
 
 import java.io.IOException;
@@ -73,6 +74,11 @@
 	private List<FileNameMatcher> matchers = new ArrayList<>();
 
 	/**
+	 * Whether to use all refs in the refs/ namespace
+	 */
+	private boolean useAll;
+
+	/**
 	 * Whether to use all tags (incl. lightweight) or not.
 	 */
 	private boolean useTags;
@@ -153,6 +159,22 @@
 	}
 
 	/**
+	 * Instead of using only the annotated tags, use any ref found in refs/
+	 * namespace. This option enables matching any known branch,
+	 * remote-tracking branch, or lightweight tag.
+	 *
+	 * @param all
+	 *            <code>true</code> enables matching any ref found in refs/
+	 *            like setting option --all in c git
+	 * @return {@code this}
+	 * @since 5.10
+	 */
+	public DescribeCommand setAll(boolean all) {
+		this.useAll = all;
+		return this;
+	}
+
+	/**
 	 * Instead of using only the annotated tags, use any tag found in refs/tags
 	 * namespace. This option enables matching lightweight (non-annotated) tags
 	 * or not.
@@ -186,7 +208,7 @@
 	private String longDescription(Ref tag, int depth, ObjectId tip)
 			throws IOException {
 		return String.format(
-				"%s-%d-g%s", tag.getName().substring(R_TAGS.length()), //$NON-NLS-1$
+				"%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$
 				Integer.valueOf(depth), w.getObjectReader().abbreviate(tip)
 						.name());
 	}
@@ -244,8 +266,7 @@
 			for (FileNameMatcher matcher : matchers) {
 				Stream<Ref> m = tags.stream().filter(
 						tag -> {
-							matcher.append(
-									tag.getName().substring(R_TAGS.length()));
+							matcher.append(formatRefName(tag.getName()));
 							boolean result = matcher.isMatch();
 							matcher.reset();
 							return result;
@@ -283,7 +304,7 @@
 			}
 
 			Collection<Ref> tagList = repo.getRefDatabase()
-					.getRefsByPrefix(R_TAGS);
+					.getRefsByPrefix(useAll ? R_REFS : R_TAGS);
 			Map<ObjectId, List<Ref>> tags = tagList.stream()
 					.filter(this::filterLightweightTags)
 					.collect(Collectors.groupingBy(this::getObjectIdFromRef));
@@ -336,7 +357,7 @@
 			Optional<Ref> bestMatch = getBestMatch(tags.get(target));
 			if (bestMatch.isPresent()) {
 				return longDesc ? longDescription(bestMatch.get(), 0, target) :
-						bestMatch.get().getName().substring(R_TAGS.length());
+						formatRefName(bestMatch.get().getName());
 			}
 
 			w.markStart(target);
@@ -408,6 +429,16 @@
 	}
 
 	/**
+	 * Removes the refs/ or refs/tags prefix from tag names
+	 * @param name the name of the tag
+	 * @return the tag name with its prefix removed
+	 */
+	private String formatRefName(String name) {
+		return name.startsWith(R_TAGS) ? name.substring(R_TAGS.length()) :
+				name.substring(R_REFS.length());
+	}
+
+	/**
 	 * Whether we use lightweight tags or not for describe Candidates
 	 *
 	 * @param ref
@@ -419,7 +450,7 @@
 	private boolean filterLightweightTags(Ref ref) {
 		ObjectId id = ref.getObjectId();
 		try {
-			return this.useTags || (id != null && (w.parseTag(id) != null));
+			return this.useAll || this.useTags || (id != null && (w.parseTag(id) != null));
 		} catch (IOException e) {
 			return false;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java
index ac17d3a..f4b8ac2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java
@@ -20,8 +20,10 @@
 import java.util.List;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidConfigurationException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.api.errors.NoHeadException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -119,6 +121,8 @@
 				}
 			}
 			return results;
+		} catch (ConfigInvalidException e) {
+			throw new InvalidConfigurationException(e.getMessage(), e);
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
index 9a328a6..75f942d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020 Chris Aniszczyk <caniszczyk@gmail.com> 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
@@ -18,8 +18,13 @@
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.api.errors.NoHeadException;
 import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
+import org.eclipse.jgit.api.errors.ServiceUnavailableException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgObjectSigner;
+import org.eclipse.jgit.lib.GpgSigner;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.PersonIdent;
@@ -29,8 +34,10 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.lib.TagBuilder;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.CredentialsProvider;
 
 /**
  * Create/update an annotated tag object or a simple unannotated tag
@@ -56,6 +63,7 @@
  *      >Git documentation about Tag</a>
  */
 public class TagCommand extends GitCommand<Ref> {
+
 	private RevObject id;
 
 	private String name;
@@ -64,11 +72,17 @@
 
 	private PersonIdent tagger;
 
-	private boolean signed;
+	private Boolean signed;
 
 	private boolean forceUpdate;
 
-	private boolean annotated = true;
+	private Boolean annotated;
+
+	private String signingKey;
+
+	private GpgObjectSigner gpgSigner;
+
+	private CredentialsProvider credentialsProvider;
 
 	/**
 	 * <p>Constructor for TagCommand.</p>
@@ -77,6 +91,7 @@
 	 */
 	protected TagCommand(Repository repo) {
 		super(repo);
+		this.credentialsProvider = CredentialsProvider.getDefault();
 	}
 
 	/**
@@ -108,10 +123,7 @@
 				id = revWalk.parseCommit(objectId);
 			}
 
-			if (!annotated) {
-				if (message != null || tagger != null)
-					throw new JGitInternalException(
-							JGitText.get().messageAndTaggerNotAllowedInUnannotatedTags);
+			if (!isAnnotated()) {
 				return updateTagRef(id, revWalk, name,
 						"SimpleTag[" + name + " : " + id //$NON-NLS-1$ //$NON-NLS-2$
 								+ "]"); //$NON-NLS-1$
@@ -124,6 +136,11 @@
 			newTag.setTagger(tagger);
 			newTag.setObjectId(id);
 
+			if (gpgSigner != null) {
+				gpgSigner.signObject(newTag, signingKey, tagger,
+						credentialsProvider);
+			}
+
 			// write the tag object
 			try (ObjectInserter inserter = repo.newObjectInserter()) {
 				ObjectId tagId = inserter.insert(newTag);
@@ -158,9 +175,17 @@
 			throw new ConcurrentRefUpdateException(
 					JGitText.get().couldNotLockHEAD, tagRef.getRef(),
 					updateResult);
+		case NO_CHANGE:
+			if (forceUpdate) {
+				return repo.exactRef(refName);
+			}
+			throw new RefAlreadyExistsException(MessageFormat
+					.format(JGitText.get().tagAlreadyExists, newTagToString),
+					updateResult);
 		case REJECTED:
 			throw new RefAlreadyExistsException(MessageFormat.format(
-					JGitText.get().tagAlreadyExists, newTagToString));
+					JGitText.get().tagAlreadyExists, newTagToString),
+					updateResult);
 		default:
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().updatingRefFailed, refName, newTagToString,
@@ -177,20 +202,60 @@
 	 *
 	 * @throws InvalidTagNameException
 	 *             if the tag name is null or invalid
-	 * @throws UnsupportedOperationException
-	 *             if the tag is signed (not supported yet)
+	 * @throws ServiceUnavailableException
+	 *             if the tag should be signed but no signer can be found
+	 * @throws UnsupportedSigningFormatException
+	 *             if the tag should be signed but {@code gpg.format} is not
+	 *             {@link GpgFormat#OPENPGP}
 	 */
 	private void processOptions(RepositoryState state)
-			throws InvalidTagNameException {
-		if (tagger == null && annotated)
-			tagger = new PersonIdent(repo);
-		if (name == null || !Repository.isValidRefName(Constants.R_TAGS + name))
+			throws InvalidTagNameException, ServiceUnavailableException,
+			UnsupportedSigningFormatException {
+		if (name == null
+				|| !Repository.isValidRefName(Constants.R_TAGS + name)) {
 			throw new InvalidTagNameException(
 					MessageFormat.format(JGitText.get().tagNameInvalid,
 							name == null ? "<null>" : name)); //$NON-NLS-1$
-		if (signed)
-			throw new UnsupportedOperationException(
-					JGitText.get().signingNotSupportedOnTag);
+		}
+		if (!isAnnotated()) {
+			if ((message != null && !message.isEmpty()) || tagger != null) {
+				throw new JGitInternalException(JGitText
+						.get().messageAndTaggerNotAllowedInUnannotatedTags);
+			}
+		} else {
+			if (tagger == null) {
+				tagger = new PersonIdent(repo);
+			}
+			// Figure out whether to sign.
+			if (!(Boolean.FALSE.equals(signed) && signingKey == null)) {
+				GpgConfig gpgConfig = new GpgConfig(repo.getConfig());
+				boolean doSign = isSigned() || gpgConfig.isSignAllTags();
+				if (!Boolean.TRUE.equals(annotated) && !doSign) {
+					doSign = gpgConfig.isSignAnnotated();
+				}
+				if (doSign) {
+					if (signingKey == null) {
+						signingKey = gpgConfig.getSigningKey();
+					}
+					if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
+						throw new UnsupportedSigningFormatException(
+								JGitText.get().onlyOpenPgpSupportedForSigning);
+					}
+					GpgSigner signer = GpgSigner.getDefault();
+					if (!(signer instanceof GpgObjectSigner)) {
+						throw new ServiceUnavailableException(
+								JGitText.get().signingServiceUnavailable);
+					}
+					gpgSigner = (GpgObjectSigner) signer;
+					// The message of a signed tag must end in a newline because
+					// the signature will be appended.
+					if (message != null && !message.isEmpty()
+							&& !message.endsWith("\n")) { //$NON-NLS-1$
+						message += '\n';
+					}
+				}
+			}
+		}
 	}
 
 	/**
@@ -238,24 +303,31 @@
 	}
 
 	/**
-	 * Whether this tag is signed
+	 * Whether {@link #setSigned(boolean) setSigned(true)} has been called or
+	 * whether a {@link #setSigningKey(String) signing key ID} has been set;
+	 * i.e., whether -s or -u was specified explicitly.
 	 *
 	 * @return whether the tag is signed
 	 */
 	public boolean isSigned() {
-		return signed;
+		return Boolean.TRUE.equals(signed) || signingKey != null;
 	}
 
 	/**
 	 * If set to true the Tag command creates a signed tag object. This
-	 * corresponds to the parameter -s on the command line.
+	 * corresponds to the parameter -s (--sign or --no-sign) on the command
+	 * line.
+	 * <p>
+	 * If {@code true}, the tag will be a signed annotated tag.
+	 * </p>
 	 *
 	 * @param signed
-	 *            a boolean.
+	 *            whether to sign
 	 * @return {@code this}
 	 */
 	public TagCommand setSigned(boolean signed) {
-		this.signed = signed;
+		checkCallable();
+		this.signed = Boolean.valueOf(signed);
 		return this;
 	}
 
@@ -268,6 +340,7 @@
 	 * @return {@code this}
 	 */
 	public TagCommand setTagger(PersonIdent tagger) {
+		checkCallable();
 		this.tagger = tagger;
 		return this;
 	}
@@ -291,14 +364,15 @@
 	}
 
 	/**
-	 * Sets the object id of the tag. If the object id is null, the commit
-	 * pointed to from HEAD will be used.
+	 * Sets the object id of the tag. If the object id is {@code null}, the
+	 * commit pointed to from HEAD will be used.
 	 *
 	 * @param id
 	 *            a {@link org.eclipse.jgit.revwalk.RevObject} object.
 	 * @return {@code this}
 	 */
 	public TagCommand setObjectId(RevObject id) {
+		checkCallable();
 		this.id = id;
 		return this;
 	}
@@ -321,6 +395,7 @@
 	 * @return {@code this}
 	 */
 	public TagCommand setForceUpdate(boolean forceUpdate) {
+		checkCallable();
 		this.forceUpdate = forceUpdate;
 		return this;
 	}
@@ -334,18 +409,77 @@
 	 * @since 3.0
 	 */
 	public TagCommand setAnnotated(boolean annotated) {
-		this.annotated = annotated;
+		checkCallable();
+		this.annotated = Boolean.valueOf(annotated);
 		return this;
 	}
 
 	/**
-	 * Whether this will create an annotated command
+	 * Whether this will create an annotated tag.
 	 *
 	 * @return true if this command will create an annotated tag (default is
 	 *         true)
 	 * @since 3.0
 	 */
 	public boolean isAnnotated() {
-		return annotated;
+		boolean setExplicitly = Boolean.TRUE.equals(annotated) || isSigned();
+		if (setExplicitly) {
+			return true;
+		}
+		// Annotated at default (not set explicitly)
+		return annotated == null;
 	}
+
+	/**
+	 * Sets the signing key.
+	 * <p>
+	 * Per spec of {@code user.signingKey}: this will be sent to the GPG program
+	 * as is, i.e. can be anything supported by the GPG program.
+	 * </p>
+	 * <p>
+	 * Note, if none was set or {@code null} is specified a default will be
+	 * obtained from the configuration.
+	 * </p>
+	 * <p>
+	 * If set to a non-{@code null} value, the tag will be a signed annotated
+	 * tag.
+	 * </p>
+	 *
+	 * @param signingKey
+	 *            signing key; {@code null} allowed
+	 * @return {@code this}
+	 * @since 5.11
+	 */
+	public TagCommand setSigningKey(String signingKey) {
+		checkCallable();
+		this.signingKey = signingKey;
+		return this;
+	}
+
+	/**
+	 * Retrieves the signing key ID.
+	 *
+	 * @return the key ID set, or {@code null} if none is set
+	 * @since 5.11
+	 */
+	public String getSigningKey() {
+		return signingKey;
+	}
+
+	/**
+	 * Sets a {@link CredentialsProvider}
+	 *
+	 * @param credentialsProvider
+	 *            the provider to use when querying for credentials (eg., during
+	 *            signing)
+	 * @return {@code this}
+	 * @since 5.11
+	 */
+	public TagCommand setCredentialsProvider(
+			CredentialsProvider credentialsProvider) {
+		checkCallable();
+		this.credentialsProvider = credentialsProvider;
+		return this;
+	}
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java
index 7e39361..81b7bd8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefAlreadyExistsException.java
@@ -1,42 +1,17 @@
 /*
- * Copyright (C) 2010,Mathias Kinzler <mathias.kinzler@sap.com> and
- * other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2010, 2020 Mathias Kinzler <mathias.kinzler@sap.com> 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
+ * 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.api.errors;
 
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.RefUpdate;
+
 /**
  * Thrown when trying to create a {@link org.eclipse.jgit.lib.Ref} with the same
  * name as an existing one
@@ -44,13 +19,43 @@
 public class RefAlreadyExistsException extends GitAPIException {
 	private static final long serialVersionUID = 1L;
 
+	private final RefUpdate.Result updateResult;
+
 	/**
-	 * Constructor for RefAlreadyExistsException
+	 * Creates a new instance with the given message.
 	 *
 	 * @param message
 	 *            error message
 	 */
 	public RefAlreadyExistsException(String message) {
+		this(message, null);
+	}
+
+	/**
+	 * Constructor for RefAlreadyExistsException
+	 *
+	 * @param message
+	 *            error message
+	 * @param updateResult
+	 *            that caused the exception; may be {@code null}
+	 * @since 5.11
+	 */
+	public RefAlreadyExistsException(String message,
+			@Nullable RefUpdate.Result updateResult) {
 		super(message);
+		this.updateResult = updateResult;
+	}
+
+	/**
+	 * Retrieves the {@link org.eclipse.jgit.lib.RefUpdate.Result
+	 * RefUpdate.Result} that caused the exception.
+	 *
+	 * @return the {@link org.eclipse.jgit.lib.RefUpdate.Result
+	 *         RefUpdate.Result} or {@code null} if unknown
+	 * @since 5.11
+	 */
+	@Nullable
+	public RefUpdate.Result getUpdateResult() {
+		return updateResult;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
index 73d2807..8c342e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2009, Google Inc.
+ * Copyright (C) 2008, 2020 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 v. 1.0 which is available at
@@ -139,10 +139,28 @@
 					: eIdx;
 				fastAdd(ent);
 			} else {
-				// Apply to all entries of the current path (different stages)
 				lastIdx = cache.nextEntry(eIdx);
-				for (int i = eIdx; i < lastIdx; i++) {
-					final DirCacheEntry ent = cache.getEntry(i);
+				if (lastIdx > eIdx + 1) {
+					// Apply to all entries of the current path (different
+					// stages). If any apply() resets the stage to STAGE_0, take
+					// only that entry and omit all others.
+					DirCacheEntry[] tmp = new DirCacheEntry[lastIdx - eIdx];
+					int n = 0;
+					for (int i = eIdx; i < lastIdx; i++) {
+						DirCacheEntry ent = cache.getEntry(i);
+						e.apply(ent);
+						if (ent.getStage() == DirCacheEntry.STAGE_0) {
+							fastAdd(ent);
+							n = 0;
+							break;
+						}
+						tmp[n++] = ent;
+					}
+					for (int i = 0; i < n; i++) {
+						fastAdd(tmp[i]);
+					}
+				} else {
+					DirCacheEntry ent = cache.getEntry(eIdx);
 					e.apply(ent);
 					fastAdd(ent);
 				}
@@ -257,7 +275,9 @@
 	 * {@link #apply(DirCacheEntry)} method. The editor will invoke apply once
 	 * for each record in the index which matches the path name. If there are
 	 * multiple records (for example in stages 1, 2 and 3), the edit instance
-	 * will be called multiple times, once for each stage.
+	 * will be called multiple times, once for each stage. If any of these calls
+	 * resets the stage to 0, only this entry will be taken and entries for
+	 * other stages are discarded.
 	 */
 	public abstract static class PathEdit {
 		final byte[] path;
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 dcb8482..67edf50 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -542,6 +542,24 @@
 	}
 
 	/**
+	 * Sets the stage of an entry.
+	 *
+	 * @param stage
+	 *            to set, in the range [0..3]
+	 * @throws IllegalArgumentException
+	 *             if the stage is outside the range [0..3]
+	 * @since 5.10
+	 */
+	public void setStage(int stage) {
+		if ((stage & ~0x3) != 0) {
+			throw new IllegalArgumentException(
+					"Invalid stage, must be in range [0..3]"); //$NON-NLS-1$
+		}
+		byte flags = info[infoOffset + P_FLAGS];
+		info[infoOffset + P_FLAGS] = (byte) ((flags & 0xCF) | (stage << 4));
+	}
+
+	/**
 	 * Returns whether this entry should be skipped from the working tree.
 	 *
 	 * @return true if this entry should be skipepd.
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 7ec5eae..154f32c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -376,6 +376,9 @@
 	/***/ public String invalidGitdirRef;
 	/***/ public String invalidGitModules;
 	/***/ public String invalidGitType;
+	/***/ public String invalidHeaderFormat;
+	/***/ public String invalidHeaderKey;
+	/***/ public String invalidHeaderValue;
 	/***/ public String invalidHexString;
 	/***/ public String invalidHomeDirectory;
 	/***/ public String invalidHooksPath;
@@ -642,7 +645,7 @@
 	/***/ public String shortReadOfBlock;
 	/***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes;
 	/***/ public String shortSkipOfBlock;
-	/***/ public String signingNotSupportedOnTag;
+	/***/ public String signedTagMessageNoLf;
 	/***/ public String signingServiceUnavailable;
 	/***/ public String similarityScoreMustBeWithinBounds;
 	/***/ public String skipMustBeNonNegative;
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 c18a189..1f2fe10 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -985,7 +985,8 @@
 				if (base == null || !n.startsWith(base)) {
 					try {
 						Path delete = packDir.resolve(n);
-						Files.delete(delete);
+						FileUtils.delete(delete.toFile(),
+								FileUtils.RETRY | FileUtils.SKIP_MISSING);
 						LOG.warn(JGitText.get().deletedOrphanInPackDir, delete);
 					} catch (IOException e) {
 						LOG.error(e.getMessage(), e);
@@ -1173,6 +1174,7 @@
 			// create temporary files
 			String id = pw.computeName().getName();
 			File packdir = repo.getObjectDatabase().getPackDirectory();
+			packdir.mkdirs();
 			tmpPack = File.createTempFile("gc_", ".pack_tmp", packdir); //$NON-NLS-1$ //$NON-NLS-2$
 			final String tmpBase = tmpPack.getName()
 					.substring(0, tmpPack.getName().lastIndexOf('.'));
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 265b71d..b4a336f 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
@@ -24,20 +24,14 @@
 import java.nio.file.StandardCopyOption;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.PackInvalidException;
-import org.eclipse.jgit.errors.PackMismatchException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -45,7 +39,6 @@
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectId;
@@ -79,9 +72,6 @@
 	private static final Logger LOG = LoggerFactory
 			.getLogger(ObjectDirectory.class);
 
-	private static final PackList NO_PACKS = new PackList(
-			FileSnapshot.DIRTY, new PackFile[0]);
-
 	/** Maximum number of candidates offered as resolutions of abbreviation. */
 	private static final int RESOLVE_ABBREV_LIMIT = 256;
 
@@ -93,7 +83,7 @@
 
 	private final File infoDirectory;
 
-	private final File packDirectory;
+	private final PackDirectory packed;
 
 	private final File preservedDirectory;
 
@@ -111,8 +101,6 @@
 
 	private Set<ObjectId> shallowCommitsIds;
 
-	final AtomicReference<PackList> packList;
-
 	/**
 	 * Initialize a reference to an on-disk object directory.
 	 *
@@ -136,10 +124,10 @@
 		config = cfg;
 		objects = dir;
 		infoDirectory = new File(objects, "info"); //$NON-NLS-1$
-		packDirectory = new File(objects, "pack"); //$NON-NLS-1$
+		File packDirectory = new File(objects, "pack"); //$NON-NLS-1$
 		preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
 		alternatesFile = new File(objects, Constants.INFO_ALTERNATES);
-		packList = new AtomicReference<>(NO_PACKS);
+		packed = new PackDirectory(config, packDirectory);
 		unpackedObjectCache = new UnpackedObjectCache();
 		this.fs = fs;
 		this.shallowFile = shallowFile;
@@ -167,7 +155,7 @@
 	 * @return the location of the <code>pack</code> directory.
 	 */
 	public final File getPackDirectory() {
-		return packDirectory;
+		return packed.getDirectory();
 	}
 
 	/**
@@ -190,7 +178,7 @@
 	public void create() throws IOException {
 		FileUtils.mkdirs(objects);
 		FileUtils.mkdir(infoDirectory);
-		FileUtils.mkdir(packDirectory);
+		packed.create();
 	}
 
 	/** {@inheritDoc} */
@@ -214,11 +202,7 @@
 	public void close() {
 		unpackedObjectCache.clear();
 
-		final PackList packs = packList.get();
-		if (packs != NO_PACKS && packList.compareAndSet(packs, NO_PACKS)) {
-			for (PackFile p : packs.packs)
-				p.close();
-		}
+		packed.close();
 
 		// Fully close all loaded alternates and clear the alternate list.
 		AlternateHandle[] alt = alternates.get();
@@ -231,11 +215,7 @@
 	/** {@inheritDoc} */
 	@Override
 	public Collection<PackFile> getPacks() {
-		PackList list = packList.get();
-		if (list == NO_PACKS)
-			list = scanPacks(list);
-		PackFile[] packs = list.packs;
-		return Collections.unmodifiableCollection(Arrays.asList(packs));
+		return packed.getPacks();
 	}
 
 	/**
@@ -264,7 +244,7 @@
 		}
 
 		PackFile res = new PackFile(pack, extensions);
-		insertPack(res);
+		packed.insert(res);
 		return res;
 	}
 
@@ -315,25 +295,7 @@
 	}
 
 	boolean hasPackedObject(AnyObjectId objectId) {
-		PackList pList;
-		do {
-			pList = packList.get();
-			for (PackFile p : pList.packs) {
-				try {
-					if (p.hasObject(objectId))
-						return true;
-				} catch (IOException e) {
-					// The hasObject call should have only touched the index,
-					// so any failure here indicates the index is unreadable
-					// by this process, and the pack is likewise not readable.
-					LOG.warn(MessageFormat.format(
-							JGitText.get().unableToReadPackfile,
-							p.getPackFile().getAbsolutePath()), e);
-					removePack(p);
-				}
-			}
-		} while (searchPacksAgain(pList));
-		return false;
+		return packed.has(objectId);
 	}
 
 	@Override
@@ -345,23 +307,8 @@
 	private void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
 			Set<AlternateHandle.Id> skips)
 			throws IOException {
-		// Go through the packs once. If we didn't find any resolutions
-		// scan for new packs and check once more.
-		int oldSize = matches.size();
-		PackList pList;
-		do {
-			pList = packList.get();
-			for (PackFile p : pList.packs) {
-				try {
-					p.resolve(matches, id, RESOLVE_ABBREV_LIMIT);
-					p.resetTransientErrorCount();
-				} catch (IOException e) {
-					handlePackError(e, p);
-				}
-				if (matches.size() > RESOLVE_ABBREV_LIMIT)
-					return;
-			}
-		} while (matches.size() == oldSize && searchPacksAgain(pList));
+		if (!packed.resolve(matches, id, RESOLVE_ABBREV_LIMIT))
+			return;
 
 		String fanOut = id.name().substring(0, 2);
 		String[] entries = new File(getDirectory(), fanOut).list();
@@ -446,28 +393,7 @@
 	}
 
 	ObjectLoader openPackedObject(WindowCursor curs, AnyObjectId objectId) {
-		PackList pList;
-		do {
-			SEARCH: for (;;) {
-				pList = packList.get();
-				for (PackFile p : pList.packs) {
-					try {
-						ObjectLoader ldr = p.get(curs, objectId);
-						p.resetTransientErrorCount();
-						if (ldr != null)
-							return ldr;
-					} catch (PackMismatchException e) {
-						// Pack was modified; refresh the entire pack list.
-						if (searchPacksAgain(pList))
-							continue SEARCH;
-					} catch (IOException e) {
-						handlePackError(e, p);
-					}
-				}
-				break SEARCH;
-			}
-		} while (searchPacksAgain(pList));
-		return null;
+		return packed.open(curs, objectId);
 	}
 
 	@Override
@@ -504,7 +430,7 @@
 
 	private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
 			AnyObjectId id, Set<AlternateHandle.Id> skips) {
-		long len = getPackedObjectSize(curs, id);
+		long len = packed.getSize(curs, id);
 		if (0 <= len) {
 			return len;
 		}
@@ -538,31 +464,6 @@
 		return -1;
 	}
 
-	private long getPackedObjectSize(WindowCursor curs, AnyObjectId id) {
-		PackList pList;
-		do {
-			SEARCH: for (;;) {
-				pList = packList.get();
-				for (PackFile p : pList.packs) {
-					try {
-						long len = p.getObjectSize(curs, id);
-						p.resetTransientErrorCount();
-						if (0 <= len)
-							return len;
-					} catch (PackMismatchException e) {
-						// Pack was modified; refresh the entire pack list.
-						if (searchPacksAgain(pList))
-							continue SEARCH;
-					} catch (IOException e) {
-						handlePackError(e, p);
-					}
-				}
-				break SEARCH;
-			}
-		} while (searchPacksAgain(pList));
-		return -1;
-	}
-
 	private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
 			throws IOException {
 		File f = fileFor(id);
@@ -580,31 +481,13 @@
 
 	@Override
 	void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
-																	WindowCursor curs) throws IOException {
+			WindowCursor curs) throws IOException {
 		selectObjectRepresentation(packer, otp, curs, null);
 	}
 
 	private void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
 			WindowCursor curs, Set<AlternateHandle.Id> skips) throws IOException {
-		PackList pList = packList.get();
-		SEARCH: for (;;) {
-			for (PackFile p : pList.packs) {
-				try {
-					LocalObjectRepresentation rep = p.representation(curs, otp);
-					p.resetTransientErrorCount();
-					if (rep != null)
-						packer.select(otp, rep);
-				} catch (PackMismatchException e) {
-					// Pack was modified; refresh the entire pack list.
-					//
-					pList = scanPacks(pList);
-					continue SEARCH;
-				} catch (IOException e) {
-					handlePackError(e, p);
-				}
-			}
-			break SEARCH;
-		}
+		packed.selectRepresentation(packer, otp, curs);
 
 		skips = addMe(skips);
 		for (AlternateHandle h : myAlternates()) {
@@ -614,54 +497,6 @@
 		}
 	}
 
-	private void handlePackError(IOException e, PackFile p) {
-		String warnTmpl = null;
-		int transientErrorCount = 0;
-		String errTmpl = JGitText.get().exceptionWhileReadingPack;
-		if ((e instanceof CorruptObjectException)
-				|| (e instanceof PackInvalidException)) {
-			warnTmpl = JGitText.get().corruptPack;
-			LOG.warn(MessageFormat.format(warnTmpl,
-					p.getPackFile().getAbsolutePath()), e);
-			// Assume the pack is corrupted, and remove it from the list.
-			removePack(p);
-		} else if (e instanceof FileNotFoundException) {
-			if (p.getPackFile().exists()) {
-				errTmpl = JGitText.get().packInaccessible;
-				transientErrorCount = p.incrementTransientErrorCount();
-			} else {
-				warnTmpl = JGitText.get().packWasDeleted;
-				removePack(p);
-			}
-		} else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
-			warnTmpl = JGitText.get().packHandleIsStale;
-			removePack(p);
-		} else {
-			transientErrorCount = p.incrementTransientErrorCount();
-		}
-		if (warnTmpl != null) {
-			LOG.warn(MessageFormat.format(warnTmpl,
-					p.getPackFile().getAbsolutePath()), e);
-		} else {
-			if (doLogExponentialBackoff(transientErrorCount)) {
-				// Don't remove the pack from the list, as the error may be
-				// transient.
-				LOG.error(MessageFormat.format(errTmpl,
-						p.getPackFile().getAbsolutePath(),
-						Integer.valueOf(transientErrorCount)), e);
-			}
-		}
-	}
-
-	/**
-	 * @param n
-	 *            count of consecutive failures
-	 * @return @{code true} if i is a power of 2
-	 */
-	private boolean doLogExponentialBackoff(int n) {
-		return (n & (n - 1)) == 0;
-	}
-
 	@Override
 	InsertLooseObjectResult insertUnpackedObject(File tmp, ObjectId id,
 			boolean createDuplicate) throws IOException {
@@ -727,21 +562,6 @@
 		return InsertLooseObjectResult.INSERTED;
 	}
 
-	boolean searchPacksAgain(PackList old) {
-		// Whether to trust the pack folder's modification time. If set
-		// to false we will always scan the .git/objects/pack folder to
-		// check for new pack files. If set to true (default) we use the
-		// lastmodified attribute of the folder and assume that no new
-		// pack files can be in this folder if his modification time has
-		// not changed.
-		boolean trustFolderStat = config.getBoolean(
-				ConfigConstants.CONFIG_CORE_SECTION,
-				ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
-
-		return ((!trustFolderStat) || old.snapshot.isModified(packDirectory))
-				&& old != scanPacks(old);
-	}
-
 	@Override
 	Config getConfig() {
 		return config;
@@ -780,182 +600,13 @@
 		return shallowCommitsIds;
 	}
 
-	private void insertPack(PackFile pf) {
-		PackList o, n;
-		do {
-			o = packList.get();
-
-			// If the pack in question is already present in the list
-			// (picked up by a concurrent thread that did a scan?) we
-			// do not want to insert it a second time.
-			//
-			final PackFile[] oldList = o.packs;
-			final String name = pf.getPackFile().getName();
-			for (PackFile p : oldList) {
-				if (name.equals(p.getPackFile().getName()))
-					return;
-			}
-
-			final PackFile[] newList = new PackFile[1 + oldList.length];
-			newList[0] = pf;
-			System.arraycopy(oldList, 0, newList, 1, oldList.length);
-			n = new PackList(o.snapshot, newList);
-		} while (!packList.compareAndSet(o, n));
-	}
-
-	private void removePack(PackFile deadPack) {
-		PackList o, n;
-		do {
-			o = packList.get();
-
-			final PackFile[] oldList = o.packs;
-			final int j = indexOf(oldList, deadPack);
-			if (j < 0)
-				break;
-
-			final PackFile[] newList = new PackFile[oldList.length - 1];
-			System.arraycopy(oldList, 0, newList, 0, j);
-			System.arraycopy(oldList, j + 1, newList, j, newList.length - j);
-			n = new PackList(o.snapshot, newList);
-		} while (!packList.compareAndSet(o, n));
-		deadPack.close();
-	}
-
-	private static int indexOf(PackFile[] list, PackFile pack) {
-		for (int i = 0; i < list.length; i++) {
-			if (list[i] == pack)
-				return i;
-		}
-		return -1;
-	}
-
-	private PackList scanPacks(PackList original) {
-		synchronized (packList) {
-			PackList o, n;
-			do {
-				o = packList.get();
-				if (o != original) {
-					// Another thread did the scan for us, while we
-					// were blocked on the monitor above.
-					//
-					return o;
-				}
-				n = scanPacksImpl(o);
-				if (n == o)
-					return n;
-			} while (!packList.compareAndSet(o, n));
-			return n;
-		}
-	}
-
-	private PackList scanPacksImpl(PackList old) {
-		final Map<String, PackFile> forReuse = reuseMap(old);
-		final FileSnapshot snapshot = FileSnapshot.save(packDirectory);
-		final Set<String> names = listPackDirectory();
-		final List<PackFile> list = new ArrayList<>(names.size() >> 2);
-		boolean foundNew = false;
-		for (String indexName : names) {
-			// Must match "pack-[0-9a-f]{40}.idx" to be an index.
-			//
-			if (indexName.length() != 49 || !indexName.endsWith(".idx")) //$NON-NLS-1$
-				continue;
-
-			final String base = indexName.substring(0, indexName.length() - 3);
-			int extensions = 0;
-			for (PackExt ext : PackExt.values()) {
-				if (names.contains(base + ext.getExtension()))
-					extensions |= ext.getBit();
-			}
-
-			if ((extensions & PACK.getBit()) == 0) {
-				// Sometimes C Git's HTTP fetch transport leaves a
-				// .idx file behind and does not download the .pack.
-				// We have to skip over such useless indexes.
-				//
-				continue;
-			}
-
-			final String packName = base + PACK.getExtension();
-			final File packFile = new File(packDirectory, packName);
-			final PackFile oldPack = forReuse.get(packName);
-			if (oldPack != null
-					&& !oldPack.getFileSnapshot().isModified(packFile)) {
-				forReuse.remove(packName);
-				list.add(oldPack);
-				continue;
-			}
-
-			list.add(new PackFile(packFile, extensions));
-			foundNew = true;
-		}
-
-		// If we did not discover any new files, the modification time was not
-		// changed, and we did not remove any files, then the set of files is
-		// the same as the set we were given. Instead of building a new object
-		// return the same collection.
-		//
-		if (!foundNew && forReuse.isEmpty() && snapshot.equals(old.snapshot)) {
-			old.snapshot.setClean(snapshot);
-			return old;
-		}
-
-		for (PackFile p : forReuse.values()) {
-			p.close();
-		}
-
-		if (list.isEmpty())
-			return new PackList(snapshot, NO_PACKS.packs);
-
-		final PackFile[] r = list.toArray(new PackFile[0]);
-		Arrays.sort(r, PackFile.SORT);
-		return new PackList(snapshot, r);
-	}
-
-	private static Map<String, PackFile> reuseMap(PackList old) {
-		final Map<String, PackFile> forReuse = new HashMap<>();
-		for (PackFile p : old.packs) {
-			if (p.invalid()) {
-				// The pack instance is corrupted, and cannot be safely used
-				// again. Do not include it in our reuse map.
-				//
-				p.close();
-				continue;
-			}
-
-			final PackFile prior = forReuse.put(p.getPackFile().getName(), p);
-			if (prior != null) {
-				// This should never occur. It should be impossible for us
-				// to have two pack files with the same name, as all of them
-				// came out of the same directory. If it does, we promised to
-				// close any PackFiles we did not reuse, so close the second,
-				// readers are likely to be actively using the first.
-				//
-				forReuse.put(prior.getPackFile().getName(), prior);
-				p.close();
-			}
-		}
-		return forReuse;
-	}
-
-	private Set<String> listPackDirectory() {
-		final String[] nameList = packDirectory.list();
-		if (nameList == null)
-			return Collections.emptySet();
-		final Set<String> nameSet = new HashSet<>(nameList.length << 1);
-		for (String name : nameList) {
-			if (name.startsWith("pack-")) //$NON-NLS-1$
-				nameSet.add(name);
-		}
-		return nameSet;
-	}
-
 	void closeAllPackHandles(File packFile) {
 		// if the packfile already exists (because we are rewriting a
 		// packfile for the same set of objects maybe with different
 		// PackConfig) then make sure we get rid of all handles on the file.
 		// Windows will not allow for rename otherwise.
 		if (packFile.exists()) {
-			for (PackFile p : getPacks()) {
+			for (PackFile p : packed.getPacks()) {
 				if (packFile.getPath().equals(p.getPackFile().getPath())) {
 					p.close();
 					break;
@@ -1037,19 +688,6 @@
 		return new File(new File(getDirectory(), d), f);
 	}
 
-	static final class PackList {
-		/** State just before reading the pack directory. */
-		final FileSnapshot snapshot;
-
-		/** All known packs, sorted by {@link PackFile#SORT}. */
-		final PackFile[] packs;
-
-		PackList(FileSnapshot monitor, PackFile[] packs) {
-			this.snapshot = monitor;
-			this.packs = packs;
-		}
-	}
-
 	static class AlternateHandle {
 		static class Id {
 			String alternateId;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
new file mode 100644
index 0000000..fd9da7c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2009, Google Inc. 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+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.atomic.AtomicReference;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.PackInvalidException;
+import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Traditional file system packed objects directory handler.
+ * <p>
+ * This is the {@code PackFile}s object representation for a Git object
+ * database, where objects are stored in compressed containers known as
+ * {@link org.eclipse.jgit.internal.storage.file.PackFile}s.
+ */
+class PackDirectory {
+	private final static Logger LOG = LoggerFactory
+			.getLogger(PackDirectory.class);
+
+	private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY,
+			new PackFile[0]);
+
+	private final Config config;
+
+	private final File directory;
+
+	private final AtomicReference<PackList> packList;
+
+	/**
+	 * Initialize a reference to an on-disk 'pack' directory.
+	 *
+	 * @param config
+	 *            configuration this directory consults for write settings.
+	 * @param directory
+	 *            the location of the {@code pack} directory.
+	 */
+	PackDirectory(Config config, File directory) {
+		this.config = config;
+		this.directory = directory;
+		packList = new AtomicReference<>(NO_PACKS);
+	}
+
+	/**
+	 * Getter for the field {@code directory}.
+	 *
+	 * @return the location of the {@code pack} directory.
+	 */
+	File getDirectory() {
+		return directory;
+	}
+
+	void create() throws IOException {
+		FileUtils.mkdir(directory);
+	}
+
+	void close() {
+		PackList packs = packList.get();
+		if (packs != NO_PACKS && packList.compareAndSet(packs, NO_PACKS)) {
+			for (PackFile p : packs.packs) {
+				p.close();
+			}
+		}
+	}
+
+	Collection<PackFile> getPacks() {
+		PackList list = packList.get();
+		if (list == NO_PACKS) {
+			list = scanPacks(list);
+		}
+		PackFile[] packs = list.packs;
+		return Collections.unmodifiableCollection(Arrays.asList(packs));
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public String toString() {
+		return "PackDirectory[" + getDirectory() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * Does the requested object exist in this PackDirectory?
+	 *
+	 * @param objectId
+	 *            identity of the object to test for existence of.
+	 * @return true if the specified object is stored in this PackDirectory.
+	 */
+	boolean has(AnyObjectId objectId) {
+		PackList pList;
+		do {
+			pList = packList.get();
+			for (PackFile p : pList.packs) {
+				try {
+					if (p.hasObject(objectId)) {
+						return true;
+					}
+				} catch (IOException e) {
+					// The hasObject call should have only touched the index,
+					// so any failure here indicates the index is unreadable
+					// by this process, and the pack is likewise not readable.
+					LOG.warn(MessageFormat.format(
+							JGitText.get().unableToReadPackfile,
+							p.getPackFile().getAbsolutePath()), e);
+					remove(p);
+				}
+			}
+		} while (searchPacksAgain(pList));
+		return false;
+	}
+
+	/**
+	 * Find objects matching the prefix abbreviation.
+	 *
+	 * @param matches
+	 *            set to add any located ObjectIds to. This is an output
+	 *            parameter.
+	 * @param id
+	 *            prefix to search for.
+	 * @param matchLimit
+	 *            maximum number of results to return. At most this many
+	 *            ObjectIds should be added to matches before returning.
+	 * @return {@code true} if the matches were exhausted before reaching
+	 *         {@code maxLimit}.
+	 */
+	boolean resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+			int matchLimit) {
+		// Go through the packs once. If we didn't find any resolutions
+		// scan for new packs and check once more.
+		int oldSize = matches.size();
+		PackList pList;
+		do {
+			pList = packList.get();
+			for (PackFile p : pList.packs) {
+				try {
+					p.resolve(matches, id, matchLimit);
+					p.resetTransientErrorCount();
+				} catch (IOException e) {
+					handlePackError(e, p);
+				}
+				if (matches.size() > matchLimit) {
+					return false;
+				}
+			}
+		} while (matches.size() == oldSize && searchPacksAgain(pList));
+		return true;
+	}
+
+	ObjectLoader open(WindowCursor curs, AnyObjectId objectId) {
+		PackList pList;
+		do {
+			SEARCH: for (;;) {
+				pList = packList.get();
+				for (PackFile p : pList.packs) {
+					try {
+						ObjectLoader ldr = p.get(curs, objectId);
+						p.resetTransientErrorCount();
+						if (ldr != null)
+							return ldr;
+					} catch (PackMismatchException e) {
+						// Pack was modified; refresh the entire pack list.
+						if (searchPacksAgain(pList)) {
+							continue SEARCH;
+						}
+					} catch (IOException e) {
+						handlePackError(e, p);
+					}
+				}
+				break SEARCH;
+			}
+		} while (searchPacksAgain(pList));
+		return null;
+	}
+
+	long getSize(WindowCursor curs, AnyObjectId id) {
+		PackList pList;
+		do {
+			SEARCH: for (;;) {
+				pList = packList.get();
+				for (PackFile p : pList.packs) {
+					try {
+						long len = p.getObjectSize(curs, id);
+						p.resetTransientErrorCount();
+						if (0 <= len) {
+							return len;
+						}
+					} catch (PackMismatchException e) {
+						// Pack was modified; refresh the entire pack list.
+						if (searchPacksAgain(pList)) {
+							continue SEARCH;
+						}
+					} catch (IOException e) {
+						handlePackError(e, p);
+					}
+				}
+				break SEARCH;
+			}
+		} while (searchPacksAgain(pList));
+		return -1;
+	}
+
+	void selectRepresentation(PackWriter packer, ObjectToPack otp,
+			WindowCursor curs) {
+		PackList pList = packList.get();
+		SEARCH: for (;;) {
+			for (PackFile p : pList.packs) {
+				try {
+					LocalObjectRepresentation rep = p.representation(curs, otp);
+					p.resetTransientErrorCount();
+					if (rep != null) {
+						packer.select(otp, rep);
+					}
+				} catch (PackMismatchException e) {
+					// Pack was modified; refresh the entire pack list.
+					//
+					pList = scanPacks(pList);
+					continue SEARCH;
+				} catch (IOException e) {
+					handlePackError(e, p);
+				}
+			}
+			break SEARCH;
+		}
+	}
+
+	private void handlePackError(IOException e, PackFile p) {
+		String warnTmpl = null;
+		int transientErrorCount = 0;
+		String errTmpl = JGitText.get().exceptionWhileReadingPack;
+		if ((e instanceof CorruptObjectException)
+				|| (e instanceof PackInvalidException)) {
+			warnTmpl = JGitText.get().corruptPack;
+			LOG.warn(MessageFormat.format(warnTmpl,
+					p.getPackFile().getAbsolutePath()), e);
+			// Assume the pack is corrupted, and remove it from the list.
+			remove(p);
+		} else if (e instanceof FileNotFoundException) {
+			if (p.getPackFile().exists()) {
+				errTmpl = JGitText.get().packInaccessible;
+				transientErrorCount = p.incrementTransientErrorCount();
+			} else {
+				warnTmpl = JGitText.get().packWasDeleted;
+				remove(p);
+			}
+		} else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
+			warnTmpl = JGitText.get().packHandleIsStale;
+			remove(p);
+		} else {
+			transientErrorCount = p.incrementTransientErrorCount();
+		}
+		if (warnTmpl != null) {
+			LOG.warn(MessageFormat.format(warnTmpl,
+					p.getPackFile().getAbsolutePath()), e);
+		} else {
+			if (doLogExponentialBackoff(transientErrorCount)) {
+				// Don't remove the pack from the list, as the error may be
+				// transient.
+				LOG.error(MessageFormat.format(errTmpl,
+						p.getPackFile().getAbsolutePath(),
+						Integer.valueOf(transientErrorCount)), e);
+			}
+		}
+	}
+
+	/**
+	 * @param n
+	 *            count of consecutive failures
+	 * @return @{code true} if i is a power of 2
+	 */
+	private boolean doLogExponentialBackoff(int n) {
+		return (n & (n - 1)) == 0;
+	}
+
+	boolean searchPacksAgain(PackList old) {
+		// Whether to trust the pack folder's modification time. If set
+		// to false we will always scan the .git/objects/pack folder to
+		// check for new pack files. If set to true (default) we use the
+		// lastmodified attribute of the folder and assume that no new
+		// pack files can be in this folder if his modification time has
+		// not changed.
+		boolean trustFolderStat = config.getBoolean(
+				ConfigConstants.CONFIG_CORE_SECTION,
+				ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+
+		return ((!trustFolderStat) || old.snapshot.isModified(directory))
+				&& old != scanPacks(old);
+	}
+
+	void insert(PackFile pf) {
+		PackList o, n;
+		do {
+			o = packList.get();
+
+			// If the pack in question is already present in the list
+			// (picked up by a concurrent thread that did a scan?) we
+			// do not want to insert it a second time.
+			//
+			final PackFile[] oldList = o.packs;
+			final String name = pf.getPackFile().getName();
+			for (PackFile p : oldList) {
+				if (name.equals(p.getPackFile().getName())) {
+					return;
+				}
+			}
+
+			final PackFile[] newList = new PackFile[1 + oldList.length];
+			newList[0] = pf;
+			System.arraycopy(oldList, 0, newList, 1, oldList.length);
+			n = new PackList(o.snapshot, newList);
+		} while (!packList.compareAndSet(o, n));
+	}
+
+	private void remove(PackFile deadPack) {
+		PackList o, n;
+		do {
+			o = packList.get();
+
+			final PackFile[] oldList = o.packs;
+			final int j = indexOf(oldList, deadPack);
+			if (j < 0) {
+				break;
+			}
+
+			final PackFile[] newList = new PackFile[oldList.length - 1];
+			System.arraycopy(oldList, 0, newList, 0, j);
+			System.arraycopy(oldList, j + 1, newList, j, newList.length - j);
+			n = new PackList(o.snapshot, newList);
+		} while (!packList.compareAndSet(o, n));
+		deadPack.close();
+	}
+
+	private static int indexOf(PackFile[] list, PackFile pack) {
+		for (int i = 0; i < list.length; i++) {
+			if (list[i] == pack) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	private PackList scanPacks(PackList original) {
+		synchronized (packList) {
+			PackList o, n;
+			do {
+				o = packList.get();
+				if (o != original) {
+					// Another thread did the scan for us, while we
+					// were blocked on the monitor above.
+					//
+					return o;
+				}
+				n = scanPacksImpl(o);
+				if (n == o) {
+					return n;
+				}
+			} while (!packList.compareAndSet(o, n));
+			return n;
+		}
+	}
+
+	private PackList scanPacksImpl(PackList old) {
+		final Map<String, PackFile> forReuse = reuseMap(old);
+		final FileSnapshot snapshot = FileSnapshot.save(directory);
+		final Set<String> names = listPackDirectory();
+		final List<PackFile> list = new ArrayList<>(names.size() >> 2);
+		boolean foundNew = false;
+		for (String indexName : names) {
+			// Must match "pack-[0-9a-f]{40}.idx" to be an index.
+			//
+			if (indexName.length() != 49 || !indexName.endsWith(".idx")) { //$NON-NLS-1$
+				continue;
+			}
+
+			final String base = indexName.substring(0, indexName.length() - 3);
+			int extensions = 0;
+			for (PackExt ext : PackExt.values()) {
+				if (names.contains(base + ext.getExtension())) {
+					extensions |= ext.getBit();
+				}
+			}
+
+			if ((extensions & PACK.getBit()) == 0) {
+				// Sometimes C Git's HTTP fetch transport leaves a
+				// .idx file behind and does not download the .pack.
+				// We have to skip over such useless indexes.
+				//
+				continue;
+			}
+
+			final String packName = base + PACK.getExtension();
+			final File packFile = new File(directory, packName);
+			final PackFile oldPack = forReuse.get(packName);
+			if (oldPack != null
+					&& !oldPack.getFileSnapshot().isModified(packFile)) {
+				forReuse.remove(packName);
+				list.add(oldPack);
+				continue;
+			}
+
+			list.add(new PackFile(packFile, extensions));
+			foundNew = true;
+		}
+
+		// If we did not discover any new files, the modification time was not
+		// changed, and we did not remove any files, then the set of files is
+		// the same as the set we were given. Instead of building a new object
+		// return the same collection.
+		//
+		if (!foundNew && forReuse.isEmpty() && snapshot.equals(old.snapshot)) {
+			old.snapshot.setClean(snapshot);
+			return old;
+		}
+
+		for (PackFile p : forReuse.values()) {
+			p.close();
+		}
+
+		if (list.isEmpty()) {
+			return new PackList(snapshot, NO_PACKS.packs);
+		}
+
+		final PackFile[] r = list.toArray(new PackFile[0]);
+		Arrays.sort(r, PackFile.SORT);
+		return new PackList(snapshot, r);
+	}
+
+	private static Map<String, PackFile> reuseMap(PackList old) {
+		final Map<String, PackFile> forReuse = new HashMap<>();
+		for (PackFile p : old.packs) {
+			if (p.invalid()) {
+				// The pack instance is corrupted, and cannot be safely used
+				// again. Do not include it in our reuse map.
+				//
+				p.close();
+				continue;
+			}
+
+			final PackFile prior = forReuse.put(p.getPackFile().getName(), p);
+			if (prior != null) {
+				// This should never occur. It should be impossible for us
+				// to have two pack files with the same name, as all of them
+				// came out of the same directory. If it does, we promised to
+				// close any PackFiles we did not reuse, so close the second,
+				// readers are likely to be actively using the first.
+				//
+				forReuse.put(prior.getPackFile().getName(), prior);
+				p.close();
+			}
+		}
+		return forReuse;
+	}
+
+	private Set<String> listPackDirectory() {
+		final String[] nameList = directory.list();
+		if (nameList == null) {
+			return Collections.emptySet();
+		}
+		final Set<String> nameSet = new HashSet<>(nameList.length << 1);
+		for (String name : nameList) {
+			if (name.startsWith("pack-")) { //$NON-NLS-1$
+				nameSet.add(name);
+			}
+		}
+		return nameSet;
+	}
+
+	static final class PackList {
+		/** State just before reading the pack directory. */
+		final FileSnapshot snapshot;
+
+		/** All known packs, sorted by {@link PackFile#SORT}. */
+		final PackFile[] packs;
+
+		PackList(FileSnapshot monitor, PackFile[] packs) {
+			this.snapshot = monitor;
+			this.packs = packs;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
index 4f93fda..1665f05 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2006-2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006, 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006, 2020, 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 v. 1.0 which is available at
@@ -16,14 +16,11 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
-import java.text.MessageFormat;
 import java.util.List;
 
-import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.util.References;
 
 /**
@@ -37,7 +34,7 @@
  * and obtain a {@link org.eclipse.jgit.revwalk.RevCommit} instance by calling
  * {@link org.eclipse.jgit.revwalk.RevWalk#parseCommit(AnyObjectId)}.
  */
-public class CommitBuilder {
+public class CommitBuilder extends ObjectBuilder {
 	private static final ObjectId[] EMPTY_OBJECTID_LIST = new ObjectId[0];
 
 	private static final byte[] htree = Constants.encodeASCII("tree"); //$NON-NLS-1$
@@ -50,28 +47,17 @@
 
 	private static final byte[] hgpgsig = Constants.encodeASCII("gpgsig"); //$NON-NLS-1$
 
-	private static final byte[] hencoding = Constants.encodeASCII("encoding"); //$NON-NLS-1$
-
 	private ObjectId treeId;
 
 	private ObjectId[] parentIds;
 
-	private PersonIdent author;
-
 	private PersonIdent committer;
 
-	private GpgSignature gpgSignature;
-
-	private String message;
-
-	private Charset encoding;
-
 	/**
 	 * Initialize an empty commit.
 	 */
 	public CommitBuilder() {
 		parentIds = EMPTY_OBJECTID_LIST;
-		encoding = UTF_8;
 	}
 
 	/**
@@ -98,8 +84,9 @@
 	 *
 	 * @return the author of this commit (who wrote it).
 	 */
+	@Override
 	public PersonIdent getAuthor() {
-		return author;
+		return super.getAuthor();
 	}
 
 	/**
@@ -108,8 +95,9 @@
 	 * @param newAuthor
 	 *            the new author. Should not be null.
 	 */
+	@Override
 	public void setAuthor(PersonIdent newAuthor) {
-		author = newAuthor;
+		super.setAuthor(newAuthor);
 	}
 
 	/**
@@ -132,38 +120,6 @@
 	}
 
 	/**
-	 * Set the GPG signature of this commit.
-	 * <p>
-	 * Note, the signature set here will change the payload of the commit, i.e.
-	 * the output of {@link #build()} will include the signature. Thus, the
-	 * typical flow will be:
-	 * <ol>
-	 * <li>call {@link #build()} without a signature set to obtain payload</li>
-	 * <li>create {@link GpgSignature} from payload</li>
-	 * <li>set {@link GpgSignature}</li>
-	 * </ol>
-	 * </p>
-	 *
-	 * @param newSignature
-	 *            the signature to set or <code>null</code> to unset
-	 * @since 5.3
-	 */
-	public void setGpgSignature(GpgSignature newSignature) {
-		gpgSignature = newSignature;
-	}
-
-	/**
-	 * Get the GPG signature of this commit.
-	 *
-	 * @return the GPG signature of this commit, maybe <code>null</code> if the
-	 *         commit is not to be signed
-	 * @since 5.3
-	 */
-	public GpgSignature getGpgSignature() {
-		return gpgSignature;
-	}
-
-	/**
 	 * Get the ancestors of this commit.
 	 *
 	 * @return the ancestors of this commit. Never null.
@@ -239,25 +195,6 @@
 	}
 
 	/**
-	 * Get the complete commit message.
-	 *
-	 * @return the complete commit message.
-	 */
-	public String getMessage() {
-		return message;
-	}
-
-	/**
-	 * Set the commit message.
-	 *
-	 * @param newMessage
-	 *            the commit message. Should not be null.
-	 */
-	public void setMessage(String newMessage) {
-		message = newMessage;
-	}
-
-	/**
 	 * Set the encoding for the commit information.
 	 *
 	 * @param encodingName
@@ -267,37 +204,10 @@
 	 */
 	@Deprecated
 	public void setEncoding(String encodingName) {
-		encoding = Charset.forName(encodingName);
+		setEncoding(Charset.forName(encodingName));
 	}
 
-	/**
-	 * Set the encoding for the commit information.
-	 *
-	 * @param enc
-	 *            the encoding to use.
-	 */
-	public void setEncoding(Charset enc) {
-		encoding = enc;
-	}
-
-	/**
-	 * Get the encoding that should be used for the commit message text.
-	 *
-	 * @return the encoding that should be used for the commit message text.
-	 */
-	public Charset getEncoding() {
-		return encoding;
-	}
-
-	/**
-	 * Format this builder's state as a commit object.
-	 *
-	 * @return this object in the canonical commit format, suitable for storage
-	 *         in a repository.
-	 * @throws java.io.UnsupportedEncodingException
-	 *             the encoding specified by {@link #getEncoding()} is not
-	 *             supported by this Java runtime.
-	 */
+	@Override
 	public byte[] build() throws UnsupportedEncodingException {
 		ByteArrayOutputStream os = new ByteArrayOutputStream();
 		OutputStreamWriter w = new OutputStreamWriter(os, getEncoding());
@@ -326,19 +236,16 @@
 			w.flush();
 			os.write('\n');
 
-			if (getGpgSignature() != null) {
+			GpgSignature signature = getGpgSignature();
+			if (signature != null) {
 				os.write(hgpgsig);
 				os.write(' ');
-				writeGpgSignatureString(getGpgSignature().toExternalString(), os);
+				writeMultiLineHeader(signature.toExternalString(), os,
+						true);
 				os.write('\n');
 			}
 
-			if (!References.isSameObject(getEncoding(), UTF_8)) {
-				os.write(hencoding);
-				os.write(' ');
-				os.write(Constants.encodeASCII(getEncoding().name()));
-				os.write('\n');
-			}
+			writeEncoding(getEncoding(), os);
 
 			os.write('\n');
 
@@ -356,58 +263,6 @@
 	}
 
 	/**
-	 * Writes signature to output as per <a href=
-	 * "https://github.com/git/git/blob/master/Documentation/technical/signature-format.txt#L66,L89">gpgsig
-	 * header</a>.
-	 * <p>
-	 * CRLF and CR will be sanitized to LF and signature will have a hanging
-	 * indent of one space starting with line two. A trailing line break is
-	 * <em>not</em> written; the caller is supposed to terminate the GPG
-	 * signature header by writing a single newline.
-	 * </p>
-	 *
-	 * @param in
-	 *            signature string with line breaks
-	 * @param out
-	 *            output stream
-	 * @throws IOException
-	 *             thrown by the output stream
-	 * @throws IllegalArgumentException
-	 *             if the signature string contains non 7-bit ASCII chars
-	 */
-	static void writeGpgSignatureString(String in, OutputStream out)
-			throws IOException, IllegalArgumentException {
-		int length = in.length();
-		for (int i = 0; i < length; ++i) {
-			char ch = in.charAt(i);
-			switch (ch) {
-			case '\r':
-				if (i + 1 < length && in.charAt(i + 1) == '\n') {
-					++i;
-				}
-				if (i + 1 < length) {
-					out.write('\n');
-					out.write(' ');
-				}
-				break;
-			case '\n':
-				if (i + 1 < length) {
-					out.write('\n');
-					out.write(' ');
-				}
-				break;
-			default:
-				// sanity check
-				if (ch > 127)
-					throw new IllegalArgumentException(MessageFormat
-							.format(JGitText.get().notASCIIString, in));
-				out.write(ch);
-				break;
-			}
-		}
-	}
-
-	/**
 	 * Format this builder's state as a commit object.
 	 *
 	 * @return this object in the canonical commit format, suitable for storage
@@ -439,7 +294,7 @@
 		}
 
 		r.append("author ");
-		r.append(author != null ? author.toString() : "NOT_SET");
+		r.append(getAuthor() != null ? getAuthor().toString() : "NOT_SET");
 		r.append("\n");
 
 		r.append("committer ");
@@ -447,17 +302,20 @@
 		r.append("\n");
 
 		r.append("gpgSignature ");
-		r.append(gpgSignature != null ? gpgSignature.toString() : "NOT_SET");
+		GpgSignature signature = getGpgSignature();
+		r.append(signature != null ? signature.toString()
+				: "NOT_SET");
 		r.append("\n");
 
-		if (encoding != null && !References.isSameObject(encoding, UTF_8)) {
+		Charset encoding = getEncoding();
+		if (!References.isSameObject(encoding, UTF_8)) {
 			r.append("encoding ");
 			r.append(encoding.name());
 			r.append("\n");
 		}
 
 		r.append("\n");
-		r.append(message != null ? message : "");
+		r.append(getMessage() != null ? getMessage() : "");
 		r.append("}");
 		return r.toString();
 	}
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 a51593b..a369026 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -20,6 +20,9 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import java.io.File;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -29,6 +32,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.events.ConfigChangedEvent;
 import org.eclipse.jgit.events.ConfigChangedListener;
@@ -36,6 +40,7 @@
 import org.eclipse.jgit.events.ListenerList;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.RawParseUtils;
 
 /**
@@ -475,6 +480,37 @@
 	}
 
 	/**
+	 * Parse a string value and treat it as a file path, replacing a ~/ prefix
+	 * by the user's home directory.
+	 * <p>
+	 * <b>Note:</b> this may throw {@link InvalidPathException} if the string is
+	 * not a valid path.
+	 * </p>
+	 *
+	 * @param section
+	 *            section the key is in.
+	 * @param subsection
+	 *            subsection the key is in, or null if not in a subsection.
+	 * @param name
+	 *            the key name.
+	 * @param fs
+	 *            to use to convert the string into a path.
+	 * @param resolveAgainst
+	 *            directory to resolve the path against if it is a relative
+	 *            path; {@code null} to use the Java process's current
+	 *            directory.
+	 * @param defaultValue
+	 *            to return if no value was present
+	 * @return the {@link Path}, or {@code defaultValue} if not set
+	 * @since 5.10
+	 */
+	public Path getPath(String section, String subsection, String name,
+			@NonNull FS fs, File resolveAgainst, Path defaultValue) {
+		return typedGetter.getPath(this, section, subsection, name, fs,
+				resolveAgainst, defaultValue);
+	}
+
+	/**
 	 * Parse a list of {@link org.eclipse.jgit.transport.RefSpec}s from the
 	 * configuration.
 	 *
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 834fff5..2587947 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -117,13 +117,29 @@
 	public static final String CONFIG_COMMIT_SECTION = "commit";
 
 	/**
+	 * The "tag" section
+	 *
+	 * @since 5.11
+	 */
+	public static final String CONFIG_TAG_SECTION = "tag";
+
+	/**
 	 * The "gpgSign" key
+	 *
 	 * @since 5.2
 	 */
 	public static final String CONFIG_KEY_GPGSIGN = "gpgSign";
 
 	/**
+	 * The "forceSignAnnotated" key
+	 *
+	 * @since 5.11
+	 */
+	public static final String CONFIG_KEY_FORCE_SIGN_ANNOTATED = "forceSignAnnotated";
+
+	/**
 	 * The "hooksPath" key.
+	 *
 	 * @since 5.6
 	 */
 	public static final String CONFIG_KEY_HOOKS_PATH = "hooksPath";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
index c1527bc..5b43729 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
@@ -85,4 +85,29 @@
 		return config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
 				ConfigConstants.CONFIG_KEY_GPGSIGN, false);
 	}
+
+	/**
+	 * Retrieves the value of git config {@code tag.gpgSign}.
+	 *
+	 * @return the value of {@code tag.gpgSign}; by default {@code false}
+	 *
+	 * @since 5.11
+	 */
+	public boolean isSignAllTags() {
+		return config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+				ConfigConstants.CONFIG_KEY_GPGSIGN, false);
+	}
+
+	/**
+	 * Retrieves the value of git config {@code tag.forceSignAnnotated}.
+	 *
+	 * @return the value of {@code tag.forceSignAnnotated}; by default
+	 *         {@code false}
+	 *
+	 * @since 5.11
+	 */
+	public boolean isSignAnnotated() {
+		return config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
+						ConfigConstants.CONFIG_KEY_FORCE_SIGN_ANNOTATED, false);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
new file mode 100644
index 0000000..6fb7677
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.transport.CredentialsProvider;
+
+/**
+ * Creates GPG signatures for Git objects.
+ *
+ * @since 5.11
+ */
+public interface GpgObjectSigner {
+
+	/**
+	 * Signs the specified object.
+	 *
+	 * <p>
+	 * Implementors should obtain the payload for signing from the specified
+	 * object via {@link ObjectBuilder#build()} and create a proper
+	 * {@link GpgSignature}. The generated signature must be set on the
+	 * specified {@code object} (see
+	 * {@link ObjectBuilder#setGpgSignature(GpgSignature)}).
+	 * </p>
+	 * <p>
+	 * Any existing signature on the object must be discarded prior obtaining
+	 * the payload via {@link ObjectBuilder#build()}.
+	 * </p>
+	 *
+	 * @param object
+	 *            the object to sign (must not be {@code null} and must be
+	 *            complete to allow proper calculation of payload)
+	 * @param gpgSigningKey
+	 *            the signing key to locate (passed as is to the GPG signing
+	 *            tool as is; eg., value of <code>user.signingkey</code>)
+	 * @param committer
+	 *            the signing identity (to help with key lookup in case signing
+	 *            key is not specified)
+	 * @param credentialsProvider
+	 *            provider to use when querying for signing key credentials (eg.
+	 *            passphrase)
+	 * @throws CanceledException
+	 *             when signing was canceled (eg., user aborted when entering
+	 *             passphrase)
+	 */
+	void signObject(@NonNull ObjectBuilder object,
+			@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
+			CredentialsProvider credentialsProvider) throws CanceledException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectBuilder.java
new file mode 100644
index 0000000..4b7054f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectBuilder.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020, 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
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Objects;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.References;
+
+/**
+ * Common base class for {@link CommitBuilder} and {@link TagBuilder}.
+ *
+ * @since 5.11
+ */
+public abstract class ObjectBuilder {
+
+	/** Byte representation of "encoding". */
+	private static final byte[] hencoding = Constants.encodeASCII("encoding"); //$NON-NLS-1$
+
+	private PersonIdent author;
+
+	private GpgSignature gpgSignature;
+
+	private String message;
+
+	private Charset encoding = StandardCharsets.UTF_8;
+
+	/**
+	 * Retrieves the author of this object.
+	 *
+	 * @return the author of this object, or {@code null} if not set yet
+	 */
+	protected PersonIdent getAuthor() {
+		return author;
+	}
+
+	/**
+	 * Sets the author (name, email address, and date) of this object.
+	 *
+	 * @param newAuthor
+	 *            the new author, must be non-{@code null}
+	 */
+	protected void setAuthor(PersonIdent newAuthor) {
+		author = Objects.requireNonNull(newAuthor);
+	}
+
+	/**
+	 * Sets the GPG signature of this object.
+	 * <p>
+	 * Note, the signature set here will change the payload of the object, i.e.
+	 * the output of {@link #build()} will include the signature. Thus, the
+	 * typical flow will be:
+	 * <ol>
+	 * <li>call {@link #build()} without a signature set to obtain payload</li>
+	 * <li>create {@link GpgSignature} from payload</li>
+	 * <li>set {@link GpgSignature}</li>
+	 * </ol>
+	 * </p>
+	 *
+	 * @param gpgSignature
+	 *            the signature to set or {@code null} to unset
+	 * @since 5.3
+	 */
+	public void setGpgSignature(@Nullable GpgSignature gpgSignature) {
+		this.gpgSignature = gpgSignature;
+	}
+
+	/**
+	 * Retrieves the GPG signature of this object.
+	 *
+	 * @return the GPG signature of this object, or {@code null} if the object
+	 *         is not signed
+	 * @since 5.3
+	 */
+	@Nullable
+	public GpgSignature getGpgSignature() {
+		return gpgSignature;
+	}
+
+	/**
+	 * Retrieves the complete message of the object.
+	 *
+	 * @return the complete message; can be {@code null}.
+	 */
+	@Nullable
+	public String getMessage() {
+		return message;
+	}
+
+	/**
+	 * Sets the message (commit message, or message of an annotated tag).
+	 *
+	 * @param message
+	 *            the message.
+	 */
+	public void setMessage(@Nullable String message) {
+		this.message = message;
+	}
+
+	/**
+	 * Retrieves the encoding that should be used for the message text.
+	 *
+	 * @return the encoding that should be used for the message text.
+	 */
+	@NonNull
+	public Charset getEncoding() {
+		return encoding;
+	}
+
+	/**
+	 * Sets the encoding for the object message.
+	 *
+	 * @param encoding
+	 *            the encoding to use.
+	 */
+	public void setEncoding(@NonNull Charset encoding) {
+		this.encoding = encoding;
+	}
+
+	/**
+	 * Format this builder's state as a git object.
+	 *
+	 * @return this object in the canonical git format, suitable for storage in
+	 *         a repository.
+	 * @throws java.io.UnsupportedEncodingException
+	 *             the encoding specified by {@link #getEncoding()} is not
+	 *             supported by this Java runtime.
+	 */
+	@NonNull
+	public abstract byte[] build() throws UnsupportedEncodingException;
+
+	/**
+	 * Writes signature to output as per <a href=
+	 * "https://github.com/git/git/blob/master/Documentation/technical/signature-format.txt#L66,L89">gpgsig
+	 * header</a>.
+	 * <p>
+	 * CRLF and CR will be sanitized to LF and signature will have a hanging
+	 * indent of one space starting with line two. A trailing line break is
+	 * <em>not</em> written; the caller is supposed to terminate the GPG
+	 * signature header by writing a single newline.
+	 * </p>
+	 *
+	 * @param in
+	 *            signature string with line breaks
+	 * @param out
+	 *            output stream
+	 * @param enforceAscii
+	 *            whether to throw {@link IllegalArgumentException} if non-ASCII
+	 *            characters are encountered
+	 * @throws IOException
+	 *             thrown by the output stream
+	 * @throws IllegalArgumentException
+	 *             if the signature string contains non 7-bit ASCII chars and
+	 *             {@code enforceAscii == true}
+	 */
+	static void writeMultiLineHeader(@NonNull String in,
+			@NonNull OutputStream out, boolean enforceAscii)
+			throws IOException, IllegalArgumentException {
+		int length = in.length();
+		for (int i = 0; i < length; ++i) {
+			char ch = in.charAt(i);
+			switch (ch) {
+			case '\r':
+				if (i + 1 < length && in.charAt(i + 1) == '\n') {
+					++i;
+				}
+				if (i + 1 < length) {
+					out.write('\n');
+					out.write(' ');
+				}
+				break;
+			case '\n':
+				if (i + 1 < length) {
+					out.write('\n');
+					out.write(' ');
+				}
+				break;
+			default:
+				// sanity check
+				if (ch > 127 && enforceAscii)
+					throw new IllegalArgumentException(MessageFormat
+							.format(JGitText.get().notASCIIString, in));
+				out.write(ch);
+				break;
+			}
+		}
+	}
+
+	/**
+	 * Writes an "encoding" header.
+	 *
+	 * @param encoding
+	 *            to write
+	 * @param out
+	 *            to write to
+	 * @throws IOException
+	 *             if writing fails
+	 */
+	static void writeEncoding(@NonNull Charset encoding,
+			@NonNull OutputStream out) throws IOException {
+		if (!References.isSameObject(encoding, UTF_8)) {
+			out.write(hencoding);
+			out.write(' ');
+			out.write(Constants.encodeASCII(encoding.name()));
+			out.write('\n');
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
index 71f0115..facb4a5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (C) 2006-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006, 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2020, Chris Aniszczyk <caniszczyk@gmail.com> 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
@@ -17,8 +17,13 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
 
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.util.References;
 
 /**
  * Mutable builder to construct an annotated tag recording a project state.
@@ -30,17 +35,22 @@
  * and obtain a {@link org.eclipse.jgit.revwalk.RevTag} instance by calling
  * {@link org.eclipse.jgit.revwalk.RevWalk#parseTag(AnyObjectId)}.
  */
-public class TagBuilder {
+public class TagBuilder extends ObjectBuilder {
+
+	private static final byte[] hobject = Constants.encodeASCII("object"); //$NON-NLS-1$
+
+	private static final byte[] htype = Constants.encodeASCII("type"); //$NON-NLS-1$
+
+	private static final byte[] htag = Constants.encodeASCII("tag"); //$NON-NLS-1$
+
+	private static final byte[] htagger = Constants.encodeASCII("tagger"); //$NON-NLS-1$
+
 	private ObjectId object;
 
 	private int type = Constants.OBJ_BAD;
 
 	private String tag;
 
-	private PersonIdent tagger;
-
-	private String message;
-
 	/**
 	 * Get the type of object this tag refers to.
 	 *
@@ -109,7 +119,7 @@
 	 * @return creator of this tag. May be null.
 	 */
 	public PersonIdent getTagger() {
-		return tagger;
+		return getAuthor();
 	}
 
 	/**
@@ -119,26 +129,7 @@
 	 *            the creator. May be null.
 	 */
 	public void setTagger(PersonIdent taggerIdent) {
-		tagger = taggerIdent;
-	}
-
-	/**
-	 * Get the complete commit message.
-	 *
-	 * @return the complete commit message.
-	 */
-	public String getMessage() {
-		return message;
-	}
-
-	/**
-	 * Set the tag's message.
-	 *
-	 * @param newMessage
-	 *            the tag's message.
-	 */
-	public void setMessage(String newMessage) {
-		message = newMessage;
+		setAuthor(taggerIdent);
 	}
 
 	/**
@@ -147,31 +138,65 @@
 	 * @return this object in the canonical annotated tag format, suitable for
 	 *         storage in a repository.
 	 */
-	public byte[] build() {
+	@Override
+	public byte[] build() throws UnsupportedEncodingException {
 		ByteArrayOutputStream os = new ByteArrayOutputStream();
 		try (OutputStreamWriter w = new OutputStreamWriter(os,
-				UTF_8)) {
-			w.write("object "); //$NON-NLS-1$
-			getObjectId().copyTo(w);
-			w.write('\n');
+				getEncoding())) {
 
-			w.write("type "); //$NON-NLS-1$
-			w.write(Constants.typeString(getObjectType()));
-			w.write("\n"); //$NON-NLS-1$
+			os.write(hobject);
+			os.write(' ');
+			getObjectId().copyTo(os);
+			os.write('\n');
 
-			w.write("tag "); //$NON-NLS-1$
+			os.write(htype);
+			os.write(' ');
+			os.write(Constants
+					.encodeASCII(Constants.typeString(getObjectType())));
+			os.write('\n');
+
+			os.write(htag);
+			os.write(' ');
 			w.write(getTag());
-			w.write("\n"); //$NON-NLS-1$
+			w.flush();
+			os.write('\n');
 
 			if (getTagger() != null) {
-				w.write("tagger "); //$NON-NLS-1$
+				os.write(htagger);
+				os.write(' ');
 				w.write(getTagger().toExternalString());
-				w.write('\n');
+				w.flush();
+				os.write('\n');
 			}
 
-			w.write('\n');
-			if (getMessage() != null)
-				w.write(getMessage());
+			writeEncoding(getEncoding(), os);
+
+			os.write('\n');
+			String msg = getMessage();
+			if (msg != null) {
+				w.write(msg);
+				w.flush();
+			}
+
+			GpgSignature signature = getGpgSignature();
+			if (signature != null) {
+				if (msg != null && !msg.isEmpty() && !msg.endsWith("\n")) { //$NON-NLS-1$
+					// If signed, the message *must* end with a linefeed
+					// character, otherwise signature verification will fail.
+					// (The signature will have been computed over the payload
+					// containing the message without LF, but will be verified
+					// against a payload with the LF.) The signature must start
+					// on a new line.
+					throw new JGitInternalException(
+							JGitText.get().signedTagMessageNoLf);
+				}
+				String externalForm = signature.toExternalString();
+				w.write(externalForm);
+				w.flush();
+				if (!externalForm.endsWith("\n")) { //$NON-NLS-1$
+					os.write('\n');
+				}
+			}
 		} catch (IOException err) {
 			// This should never occur, the only way to get it above is
 			// for the ByteArrayOutputStream to throw, but it doesn't.
@@ -185,10 +210,17 @@
 	 * Format this builder's state as an annotated tag object.
 	 *
 	 * @return this object in the canonical annotated tag format, suitable for
-	 *         storage in a repository.
+	 *         storage in a repository, or {@code null} if the tag cannot be
+	 *         encoded
+	 * @deprecated since 5.11; use {@link #build()} instead
 	 */
+	@Deprecated
 	public byte[] toByteArray() {
-		return build();
+		try {
+			return build();
+		} catch (UnsupportedEncodingException e) {
+			return null;
+		}
 	}
 
 	/** {@inheritDoc} */
@@ -211,14 +243,23 @@
 		r.append(tag != null ? tag : "NOT_SET");
 		r.append("\n");
 
-		if (tagger != null) {
+		if (getTagger() != null) {
 			r.append("tagger ");
-			r.append(tagger);
+			r.append(getTagger());
+			r.append("\n");
+		}
+
+		Charset encoding = getEncoding();
+		if (!References.isSameObject(encoding, UTF_8)) {
+			r.append("encoding ");
+			r.append(encoding.name());
 			r.append("\n");
 		}
 
 		r.append("\n");
-		r.append(message != null ? message : "");
+		r.append(getMessage() != null ? getMessage() : "");
+		GpgSignature signature = getGpgSignature();
+		r.append(signature != null ? signature.toExternalString() : "");
 		r.append("}");
 		return r.toString();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
index dd4be34..0f2f6cf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2017, 2020 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
@@ -10,22 +10,26 @@
 
 package org.eclipse.jgit.lib;
 
+import java.io.File;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.util.FS;
 
 /**
- * Something that knows how to convert plain strings from a git
- * {@link org.eclipse.jgit.lib.Config} to typed values.
+ * Something that knows how to convert plain strings from a git {@link Config}
+ * to typed values.
  *
  * @since 4.9
  */
 public interface TypedConfigGetter {
 
 	/**
-	 * Get a boolean value from a git {@link org.eclipse.jgit.lib.Config}.
+	 * Get a boolean value from a git {@link Config}.
 	 *
 	 * @param config
 	 *            to get the value from
@@ -44,7 +48,7 @@
 			String name, boolean defaultValue);
 
 	/**
-	 * Parse an enumeration from a git {@link org.eclipse.jgit.lib.Config}.
+	 * Parse an enumeration from a git {@link Config}.
 	 *
 	 * @param config
 	 *            to get the value from
@@ -65,7 +69,7 @@
 			String subsection, String name, T defaultValue);
 
 	/**
-	 * Obtain an integer value from a git {@link org.eclipse.jgit.lib.Config}.
+	 * Obtain an integer value from a git {@link Config}.
 	 *
 	 * @param config
 	 *            to get the value from
@@ -83,7 +87,7 @@
 			int defaultValue);
 
 	/**
-	 * Obtain a long value from a git {@link org.eclipse.jgit.lib.Config}.
+	 * Obtain a long value from a git {@link Config}.
 	 *
 	 * @param config
 	 *            to get the value from
@@ -102,7 +106,7 @@
 
 	/**
 	 * Parse a numerical time unit, such as "1 minute", from a git
-	 * {@link org.eclipse.jgit.lib.Config}.
+	 * {@link Config}.
 	 *
 	 * @param config
 	 *            to get the value from
@@ -124,10 +128,50 @@
 	long getTimeUnit(Config config, String section, String subsection,
 			String name, long defaultValue, TimeUnit wantUnit);
 
+	/**
+	 * Parse a string value from a git {@link Config} and treat it as a file
+	 * path, replacing a ~/ prefix by the user's home directory.
+	 * <p>
+	 * <b>Note:</b> this may throw {@link InvalidPathException} if the string is
+	 * not a valid path.
+	 * </p>
+	 *
+	 * @param config
+	 *            to get the path from.
+	 * @param section
+	 *            section the key is in.
+	 * @param subsection
+	 *            subsection the key is in, or null if not in a subsection.
+	 * @param name
+	 *            the key name.
+	 * @param fs
+	 *            to use to convert the string into a path.
+	 * @param resolveAgainst
+	 *            directory to resolve the path against if it is a relative
+	 *            path.
+	 * @param defaultValue
+	 *            to return if no value was present
+	 * @return the {@link Path}, or {@code defaultValue} if not set
+	 * @since 5.10
+	 */
+	default Path getPath(Config config, String section, String subsection,
+			String name, @NonNull FS fs, File resolveAgainst,
+			Path defaultValue) {
+		String value = config.getString(section, subsection, name);
+		if (value == null) {
+			return defaultValue;
+		}
+		File file;
+		if (value.startsWith("~/")) { //$NON-NLS-1$
+			file = fs.resolve(fs.userHome(), value.substring(2));
+		} else {
+			file = fs.resolve(resolveAgainst, value);
+		}
+		return file.toPath();
+	}
 
 	/**
-	 * Parse a list of {@link org.eclipse.jgit.transport.RefSpec}s from a git
-	 * {@link org.eclipse.jgit.lib.Config}.
+	 * Parse a list of {@link RefSpec}s from a git {@link Config}.
 	 *
 	 * @param config
 	 *            to get the list from
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogContext.java b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogContext.java
new file mode 100644
index 0000000..fab0dd1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogContext.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 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.logging;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Singleton that collects performance logs.
+ *
+ * @since 5.10
+ */
+public class PerformanceLogContext {
+	/** Singleton instance that stores the statistics. */
+	private static final PerformanceLogContext INSTANCE = new PerformanceLogContext();
+
+	/** List that stores events as performance logs. */
+	private static final ThreadLocal<List<PerformanceLogRecord>> eventRecords = ThreadLocal
+			.withInitial(ArrayList::new);
+
+	private PerformanceLogContext() {
+	}
+
+	/**
+	 * Get the instance of the context.
+	 *
+	 * @return instance of performance log context.
+	 */
+	public static PerformanceLogContext getInstance() {
+		return INSTANCE;
+	}
+
+	/**
+	 * Get the unmodifiable list of events as performance records.
+	 *
+	 * @return unmodifiable list of events as performance logs.
+	 */
+	public List<PerformanceLogRecord> getEventRecords() {
+		return Collections.unmodifiableList(eventRecords.get());
+	}
+
+	/**
+	 * Adds a performance log record to the current list of events.
+	 *
+	 * @param record
+	 *            performance log record that is going to be added.
+	 */
+	public void addEvent(PerformanceLogRecord record) {
+		eventRecords.get().add(record);
+	}
+
+	/**
+	 * Removes all of the existing records from the current list of events.
+	 */
+	public void cleanEvents() {
+		eventRecords.remove();
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogRecord.java b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogRecord.java
new file mode 100644
index 0000000..3dc5f20
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogRecord.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, 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.logging;
+
+/**
+ * Class to register a performance log record.
+ *
+ * @since 5.10
+ */
+public class PerformanceLogRecord {
+	/** Name of the recorded event. */
+	private String name;
+
+	/** Duration of the recorded event in milliseconds. */
+	private long durationMs;
+
+	/**
+	 * Create a new performance log record for an event.
+	 *
+	 * @param name
+	 *            name of the event.
+	 * @param durationMs
+	 *            duration in milliseconds of the event.
+	 */
+	public PerformanceLogRecord(String name, long durationMs) {
+		this.name = name;
+		this.durationMs = durationMs;
+	}
+
+	/**
+	 * Get the name of the recorded event.
+	 *
+	 * @return name of the recorded event.
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Get the duration in milliseconds of the recorded event.
+	 *
+	 * @return duration in milliseconds of the recorded event.
+	 */
+	public long getDurationMs() {
+		return durationMs;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index cac2571..3bcdfaf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -18,7 +18,9 @@
 import java.nio.charset.Charset;
 import java.nio.charset.IllegalCharsetNameException;
 import java.nio.charset.UnsupportedCharsetException;
+import java.util.Arrays;
 
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -35,6 +37,10 @@
  * An annotated tag.
  */
 public class RevTag extends RevObject {
+
+	private static final byte[] hSignature = Constants
+			.encodeASCII("-----BEGIN PGP SIGNATURE-----"); //$NON-NLS-1$
+
 	/**
 	 * Parse an annotated tag from its canonical format.
 	 *
@@ -171,6 +177,62 @@
 		return RawParseUtils.parsePersonIdent(raw, nameB);
 	}
 
+	private static int nextStart(byte[] prefix, byte[] buffer, int from) {
+		int stop = buffer.length - prefix.length + 1;
+		int ptr = from;
+		if (ptr > 0) {
+			ptr = RawParseUtils.nextLF(buffer, ptr - 1);
+		}
+		while (ptr < stop) {
+			int lineStart = ptr;
+			boolean found = true;
+			for (byte element : prefix) {
+				if (element != buffer[ptr++]) {
+					found = false;
+					break;
+				}
+			}
+			if (found) {
+				return lineStart;
+			}
+			do {
+				ptr = RawParseUtils.nextLF(buffer, ptr);
+			} while (ptr < stop && buffer[ptr] == '\n');
+		}
+		return -1;
+	}
+
+	/**
+	 * Parse the GPG signature from the raw buffer.
+	 *
+	 * @return contents of the GPG signature; {@code null} if the tag was not
+	 *         signed.
+	 * @since 5.11
+	 */
+	@Nullable
+	public final byte[] getRawGpgSignature() {
+		byte[] raw = buffer;
+		int msgB = RawParseUtils.tagMessage(raw, 0);
+		if (msgB < 0) {
+			return null;
+		}
+		// Find the last signature start and return the rest
+		int start = nextStart(hSignature, raw, msgB);
+		if (start < 0) {
+			return null;
+		}
+		int next = RawParseUtils.nextLF(raw, start);
+		while (next < raw.length) {
+			int newStart = nextStart(hSignature, raw, next);
+			if (newStart < 0) {
+				break;
+			}
+			start = newStart;
+			next = RawParseUtils.nextLF(raw, start);
+		}
+		return Arrays.copyOfRange(raw, start, raw.length);
+	}
+
 	/**
 	 * Parse the complete tag message and decode it to a string.
 	 * <p>
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 645da0a..4d18337 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
@@ -188,6 +188,13 @@
 		public long haves;
 
 		/**
+		 * The count of wants that were not advertised by the server.
+		 *
+		 * @since 5.10
+		 */
+		public long notAdvertisedWants;
+
+		/**
 		 * Time in ms spent in the negotiation phase. For non-bidirectional
 		 * transports (e.g., HTTP), this is only for the final request that
 		 * sends back the pack file.
@@ -266,8 +273,16 @@
 		/** Time in ms spent writing the pack. */
 		public long timeWriting;
 
+		/** Time in ms spent checking reachability.
+		 *
+		 * @since 5.10
+		 */
+		public long reachabilityCheckDuration;
+
 		/** Number of trees traversed in the walk when writing the pack.
-		 * @since 5.4*/
+		 *
+		 * @since 5.4
+		 */
 		public long treesTraversed;
 
 		/**
@@ -349,6 +364,16 @@
 	}
 
 	/**
+	 * Get the count of client wants that were not advertised by the server.
+	 *
+	 * @return count of client wants that were not advertised by the server.
+	 * @since 5.10
+	 */
+	public long getNotAdvertisedWants() {
+		return statistics.notAdvertisedWants;
+	}
+
+	/**
 	 * Time in ms spent in the negotiation phase. For non-bidirectional
 	 * transports (e.g., HTTP), this is only for the final request that sends
 	 * back the pack file.
@@ -604,6 +629,18 @@
 	}
 
 	/**
+	 * Get time in milliseconds spent checking if the client has access to the
+	 * commits they are requesting.
+	 *
+	 * @return time in milliseconds spent checking if the client has access to the
+	 * commits they are requesting.
+	 * @since 5.10
+	 */
+	public long getReachabilityCheckDuration() {
+		return statistics.reachabilityCheckDuration;
+	}
+
+	/**
 	 * @return number of trees traversed in the walk when writing the pack.
 	 * @since 5.4
 	 */
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 0ba5eb5..bf77021 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -657,8 +657,11 @@
 	 *
 	 * @since 4.10
 	 * @return name
+	 * @throws ConfigInvalidException
+	 * @throws IOException
 	 */
-	public String getModuleName() {
+	public String getModuleName() throws IOException, ConfigInvalidException {
+		lazyLoadModulesConfig();
 		return getModuleName(path);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
index 1417fae..3a36398 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -21,8 +21,11 @@
 import java.io.OutputStream;
 import java.text.MessageFormat;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Set;
 
 import org.eclipse.jgit.errors.InvalidObjectIdException;
@@ -35,6 +38,7 @@
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
 import org.eclipse.jgit.util.io.InterruptTimer;
 import org.eclipse.jgit.util.io.TimeoutInputStream;
 import org.eclipse.jgit.util.io.TimeoutOutputStream;
@@ -49,6 +53,8 @@
  */
 abstract class BasePackConnection extends BaseConnection {
 
+	protected static final String CAPABILITY_SYMREF_PREFIX = "symref="; //$NON-NLS-1$
+
 	/** The repository this transport fetches into, or pushes out of. */
 	protected final Repository local;
 
@@ -228,10 +234,109 @@
 					throw duplicateAdvertisement(name);
 			}
 		}
+		updateWithSymRefs(avail, extractSymRefsFromCapabilities(remoteCapablities));
 		available(avail);
 	}
 
 	/**
+	 * Finds values in the given capabilities of the form:
+	 *
+	 * <pre>
+	 * symref=<em>source</em>:<em>target</em>
+	 * </pre>
+	 *
+	 * And returns a Map of source->target entries.
+	 *
+	 * @param capabilities
+	 *            the capabilities lines
+	 * @return a Map of the symref entries from capabilities
+	 * @throws NullPointerException
+	 *             if capabilities, or any entry in it, is null
+	 */
+	static Map<String, String> extractSymRefsFromCapabilities(Collection<String> capabilities) {
+		final Map<String, String> symRefs = new LinkedHashMap<>();
+		for (String option : capabilities) {
+			if (option.startsWith(CAPABILITY_SYMREF_PREFIX)) {
+				String[] symRef = option
+						.substring(CAPABILITY_SYMREF_PREFIX.length())
+						.split(":", 2); //$NON-NLS-1$
+				if (symRef.length == 2) {
+					symRefs.put(symRef[0], symRef[1]);
+				}
+			}
+		}
+		return symRefs;
+	}
+
+	/**
+	 * Updates the given refMap with {@link SymbolicRef}s defined by the given
+	 * symRefs.
+	 * <p>
+	 * For each entry, symRef, in symRefs, whose value is a key in refMap, adds
+	 * a new entry to refMap with that same key and value of a new
+	 * {@link SymbolicRef} with source=symRef.key and
+	 * target=refMap.get(symRef.value), then removes that entry from symRefs.
+	 * <p>
+	 * If refMap already contains an entry for symRef.key, it is replaced.
+	 * </p>
+	 * </p>
+	 * <p>
+	 * For example, given:
+	 * </p>
+	 *
+	 * <pre>
+	 * refMap.put("refs/heads/main", ref);
+	 * symRefs.put("HEAD", "refs/heads/main");
+	 * </pre>
+	 *
+	 * then:
+	 *
+	 * <pre>
+	 * updateWithSymRefs(refMap, symRefs);
+	 * </pre>
+	 *
+	 * has the <em>effect</em> of:
+	 *
+	 * <pre>
+	 * refMap.put("HEAD",
+	 * 		new SymbolicRef("HEAD", refMap.get(symRefs.remove("HEAD"))))
+	 * </pre>
+	 * <p>
+	 * Any entry in symRefs whose value is not a key in refMap is ignored. Any
+	 * circular symRefs are ignored.
+	 * </p>
+	 * <p>
+	 * Upon completion, symRefs will contain only any unresolvable entries.
+	 * </p>
+	 *
+	 * @param refMap
+	 *            a non-null, modifiable, Map to update, and the provider of
+	 *            symref targets.
+	 * @param symRefs
+	 *            a non-null, modifiable, Map of symrefs.
+	 * @throws NullPointerException
+	 *             if refMap or symRefs is null
+	 */
+	static void updateWithSymRefs(Map<String, Ref> refMap, Map<String, String> symRefs) {
+		boolean haveNewRefMapEntries = !refMap.isEmpty();
+		while (!symRefs.isEmpty() && haveNewRefMapEntries) {
+			haveNewRefMapEntries = false;
+			final Iterator<Map.Entry<String, String>> iterator = symRefs.entrySet().iterator();
+			while (iterator.hasNext()) {
+				final Map.Entry<String, String> symRef = iterator.next();
+				if (!symRefs.containsKey(symRef.getValue())) { // defer forward reference
+					final Ref r = refMap.get(symRef.getValue());
+					if (r != null) {
+						refMap.put(symRef.getKey(), new SymbolicRef(symRef.getKey(), r));
+						haveNewRefMapEntries = true;
+						iterator.remove();
+					}
+				}
+			}
+		}
+	}
+
+	/**
 	 * Create an exception to indicate problems finding a remote repository. The
 	 * caller is expected to throw the returned exception.
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
index 79cba80..dc82f46 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
@@ -14,9 +14,13 @@
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 import java.util.function.Supplier;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Config;
@@ -56,6 +60,20 @@
 	public static final String SSL_VERIFY_KEY = "sslVerify"; //$NON-NLS-1$
 
 	/**
+	 * git config key for the "userAgent" setting.
+	 *
+	 * @since 5.10
+	 */
+	public static final String USER_AGENT = "userAgent"; //$NON-NLS-1$
+
+	/**
+	 * git config key for the "extraHeader" setting.
+	 *
+	 * @since 5.10
+	 */
+	public static final String EXTRA_HEADER = "extraHeader"; //$NON-NLS-1$
+
+	/**
 	 * git config key for the "cookieFile" setting.
 	 *
 	 * @since 5.4
@@ -103,6 +121,8 @@
 		}
 	}).get().intValue();
 
+	private static final String ENV_HTTP_USER_AGENT = "GIT_HTTP_USER_AGENT"; //$NON-NLS-1$
+
 	/**
 	 * Config values for http.followRedirect.
 	 */
@@ -143,6 +163,10 @@
 
 	private int maxRedirects;
 
+	private String userAgent;
+
+	private List<String> extraHeaders;
+
 	private String cookieFile;
 
 	private boolean saveCookies;
@@ -186,6 +210,27 @@
 	}
 
 	/**
+	 * Get the "http.userAgent" setting
+	 *
+	 * @return the value of the "http.userAgent" setting
+	 * @since 5.10
+	 */
+	public String getUserAgent() {
+		return userAgent;
+	}
+
+	/**
+	 * Get the "http.extraHeader" setting
+	 *
+	 * @return the value of the "http.extraHeader" setting
+	 * @since 5.10
+	 */
+	@NonNull
+	public List<String> getExtraHeaders() {
+		return extraHeaders == null ? Collections.emptyList() : extraHeaders;
+	}
+
+	/**
 	 * Get the "http.cookieFile" setting
 	 *
 	 * @return the value of the "http.cookieFile" setting
@@ -265,11 +310,25 @@
 		if (redirectLimit < 0) {
 			redirectLimit = MAX_REDIRECTS;
 		}
+		String agent = config.getString(HTTP, null, USER_AGENT);
+		if (agent != null) {
+			agent = UserAgent.clean(agent);
+		}
+		userAgent = agent;
+		String[] headers = config.getStringList(HTTP, null, EXTRA_HEADER);
+		// https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpextraHeader
+		// "an empty value will reset the extra headers to the empty list."
+		int start = findLastEmpty(headers) + 1;
+		if (start > 0) {
+			headers = Arrays.copyOfRange(headers, start, headers.length);
+		}
+		extraHeaders = Arrays.asList(headers);
 		cookieFile = config.getString(HTTP, null, COOKIE_FILE_KEY);
 		saveCookies = config.getBoolean(HTTP, SAVE_COOKIES_KEY, false);
 		cookieFileCacheLimit = config.getInt(HTTP, COOKIE_FILE_CACHE_LIMIT_KEY,
 				DEFAULT_COOKIE_FILE_CACHE_LIMIT);
 		String match = findMatch(config.getSubsections(HTTP), uri);
+
 		if (match != null) {
 			// Override with more specific items
 			postBufferSize = config.getInt(HTTP, match, POST_BUFFER_KEY,
@@ -283,6 +342,22 @@
 			if (newMaxRedirects >= 0) {
 				redirectLimit = newMaxRedirects;
 			}
+			String uriSpecificUserAgent = config.getString(HTTP, match,
+					USER_AGENT);
+			if (uriSpecificUserAgent != null) {
+				userAgent = UserAgent.clean(uriSpecificUserAgent);
+			}
+			String[] uriSpecificExtraHeaders = config.getStringList(HTTP, match,
+					EXTRA_HEADER);
+			if (uriSpecificExtraHeaders.length > 0) {
+				start = findLastEmpty(uriSpecificExtraHeaders) + 1;
+				if (start > 0) {
+					uriSpecificExtraHeaders = Arrays.copyOfRange(
+							uriSpecificExtraHeaders, start,
+							uriSpecificExtraHeaders.length);
+				}
+				extraHeaders = Arrays.asList(uriSpecificExtraHeaders);
+			}
 			String urlSpecificCookieFile = config.getString(HTTP, match,
 					COOKIE_FILE_KEY);
 			if (urlSpecificCookieFile != null) {
@@ -291,12 +366,26 @@
 			saveCookies = config.getBoolean(HTTP, match, SAVE_COOKIES_KEY,
 					saveCookies);
 		}
+		// Environment overrides config
+		agent = SystemReader.getInstance().getenv(ENV_HTTP_USER_AGENT);
+		if (!StringUtils.isEmptyOrNull(agent)) {
+			userAgent = UserAgent.clean(agent);
+		}
 		postBuffer = postBufferSize;
 		sslVerify = sslVerifyFlag;
 		followRedirects = followRedirectsMode;
 		maxRedirects = redirectLimit;
 	}
 
+	private int findLastEmpty(String[] values) {
+		for (int i = values.length - 1; i >= 0; i--) {
+			if (values[i] == null) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
 	/**
 	 * Determines the best match from a set of subsection names (representing
 	 * prefix URLs) for the given {@link URIish}.
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 0801b8a..715cbb4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -679,7 +679,8 @@
 
 			verifySafeObject(tempObjectId, type, visit.data);
 			if (isCheckObjectCollisions() && readCurs.has(tempObjectId)) {
-				checkObjectCollision(tempObjectId, type, visit.data);
+				checkObjectCollision(tempObjectId, type, visit.data,
+						visit.delta.sizeBeforeInflating);
 			}
 
 			PackedObjectInfo oe;
@@ -999,6 +1000,7 @@
 			UnresolvedDelta n = onEndDelta();
 			n.position = streamPosition;
 			n.next = baseByPos.put(base, n);
+			n.sizeBeforeInflating = streamPosition() - streamPosition;
 			deltaCount++;
 			break;
 		}
@@ -1020,6 +1022,7 @@
 			inflateAndSkip(Source.INPUT, sz);
 			UnresolvedDelta n = onEndDelta();
 			n.position = streamPosition;
+			n.sizeBeforeInflating = streamPosition() - streamPosition;
 			r.add(n);
 			deltaCount++;
 			break;
@@ -1071,9 +1074,11 @@
 			verifySafeObject(tempObjectId, type, data);
 		}
 
+		long sizeBeforeInflating = streamPosition() - pos;
 		PackedObjectInfo obj = newInfo(tempObjectId, null, null);
 		obj.setOffset(pos);
 		obj.setType(type);
+		obj.setSize(sizeBeforeInflating);
 		onEndWholeObject(obj);
 		if (data != null)
 			onInflatedObjectData(obj, type, data);
@@ -1148,6 +1153,8 @@
 					sz -= n;
 				}
 			}
+			stats.incrementObjectsDuplicated();
+			stats.incrementNumBytesDuplicated(obj.getSize());
 		} catch (MissingObjectException notLocal) {
 			// This is OK, we don't have a copy of the object locally
 			// but the API throws when we try to read it as usually it's
@@ -1155,15 +1162,17 @@
 		}
 	}
 
-	private void checkObjectCollision(AnyObjectId obj, int type, byte[] data)
-			throws IOException {
+	private void checkObjectCollision(AnyObjectId obj, int type, byte[] data,
+			long sizeBeforeInflating) throws IOException {
 		try {
 			final ObjectLoader ldr = readCurs.open(obj, type);
 			final byte[] existingData = ldr.getCachedBytes(data.length);
 			if (!Arrays.equals(data, existingData)) {
-				throw new IOException(MessageFormat.format(
-						JGitText.get().collisionOn, obj.name()));
+				throw new IOException(MessageFormat
+						.format(JGitText.get().collisionOn, obj.name()));
 			}
+			stats.incrementObjectsDuplicated();
+			stats.incrementNumBytesDuplicated(sizeBeforeInflating);
 		} catch (MissingObjectException notLocal) {
 			// This is OK, we don't have a copy of the object locally
 			// but the API throws when we try to read it as usually its
@@ -1653,6 +1662,8 @@
 
 		UnresolvedDelta next;
 
+		long sizeBeforeInflating;
+
 		/** @return offset within the input stream. */
 		public long getOffset() {
 			return position;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java
index fc906de..fe1209b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java
@@ -29,6 +29,8 @@
 
 	private int type = Constants.OBJ_BAD;
 
+	private long sizeBeforeInflating;
+
 	PackedObjectInfo(final long headerOffset, final int packedCRC,
 			final AnyObjectId id) {
 		super(id);
@@ -108,4 +110,12 @@
 	public void setType(int type) {
 		this.type = type;
 	}
+
+	void setSize(long sizeBeforeInflating) {
+		this.sizeBeforeInflating = sizeBeforeInflating;
+	}
+
+	long getSize() {
+		return sizeBeforeInflating;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java
index bd8f558..d7bc400 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java
@@ -19,6 +19,7 @@
  */
 public class ReceivedPackStatistics {
 	private long numBytesRead;
+	private long numBytesDuplicated;
 
 	private long numWholeCommit;
 	private long numWholeTree;
@@ -26,6 +27,7 @@
 	private long numWholeTag;
 	private long numOfsDelta;
 	private long numRefDelta;
+	private long numObjectsDuplicated;
 
 	private long numDeltaCommit;
 	private long numDeltaTree;
@@ -42,6 +44,17 @@
 	}
 
 	/**
+	 * Get number of bytes of objects already in the local database
+	 *
+	 * @return number of bytes of objects appeared in both the pack sent by the
+	 *         client and the local database
+	 * @since 5.10
+	 */
+	public long getNumBytesDuplicated() {
+		return numBytesDuplicated;
+	}
+
+	/**
 	 * Get number of whole commit objects in the pack
 	 *
 	 * @return number of whole commit objects in the pack
@@ -96,6 +109,17 @@
 	}
 
 	/**
+	 * Get number of objects already in the local database
+	 *
+	 * @return number of objects appeared in both the pack sent by the client
+	 *         and the local database
+	 * @since 5.10
+	 */
+	public long getNumObjectsDuplicated() {
+		return numObjectsDuplicated;
+	}
+
+	/**
 	 * Get number of delta commit objects in the pack
 	 *
 	 * @return number of delta commit objects in the pack
@@ -134,6 +158,7 @@
 	/** A builder for {@link ReceivedPackStatistics}. */
 	public static class Builder {
 		private long numBytesRead;
+		private long numBytesDuplicated;
 
 		private long numWholeCommit;
 		private long numWholeTree;
@@ -141,6 +166,7 @@
 		private long numWholeTag;
 		private long numOfsDelta;
 		private long numRefDelta;
+		private long numObjectsDuplicated;
 
 		private long numDeltaCommit;
 		private long numDeltaTree;
@@ -157,6 +183,17 @@
 		}
 
 		/**
+		 * @param size
+		 *            additional bytes already in the local database
+		 * @return this
+		 * @since 5.10
+		 */
+		Builder incrementNumBytesDuplicated(long size) {
+			numBytesDuplicated += size;
+			return this;
+		}
+
+		/**
 		 * Increment a whole object count.
 		 *
 		 * @param type OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, or OBJ_TAG
@@ -196,6 +233,17 @@
 		}
 
 		/**
+		 * Increment the duplicated object count.
+		 *
+		 * @return this
+		 * @since 5.10
+		 */
+		Builder incrementObjectsDuplicated() {
+			numObjectsDuplicated++;
+			return this;
+		}
+
+		/**
 		 * Increment a delta object count.
 		 *
 		 * @param type OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, or OBJ_TAG
@@ -226,6 +274,7 @@
 		ReceivedPackStatistics build() {
 			ReceivedPackStatistics s = new ReceivedPackStatistics();
 			s.numBytesRead = numBytesRead;
+			s.numBytesDuplicated = numBytesDuplicated;
 			s.numWholeCommit = numWholeCommit;
 			s.numWholeTree = numWholeTree;
 			s.numWholeBlob = numWholeBlob;
@@ -236,6 +285,7 @@
 			s.numDeltaTree = numDeltaTree;
 			s.numDeltaBlob = numDeltaBlob;
 			s.numDeltaTag = numDeltaTag;
+			s.numObjectsDuplicated = numObjectsDuplicated;
 			return s;
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
index b1fac2c..fff2938 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2020 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
@@ -117,6 +117,34 @@
 	/** Key in an ssh config file. */
 	public static final String PROXY_COMMAND = "ProxyCommand";
 
+	/**
+	 * Comma-separated list of jump hosts, defining a jump host chain <em>in
+	 * reverse order</em>. Each jump host is a SSH URI or "[user@]host[:port]".
+	 * <p>
+	 * Reverse order means: to connect A->B->target, one can do in
+	 * {@code ~/.ssh/config} either of:
+	 * </p>
+	 *
+	 * <pre>
+	 * Host target
+	 *   ProxyJump B,A
+	 * </pre>
+	 * <p>
+	 * <em>or</em>
+	 * </p>
+	 *
+	 * <pre>
+	 * Host target
+	 *   ProxyJump B
+	 *
+	 * Host B
+	 *   ProxyJump A
+	 * </pre>
+	 *
+	 * @since 5.10
+	 */
+	public static final String PROXY_JUMP = "ProxyJump";
+
 	/** Key in an ssh config file. */
 	public static final String REMOTE_COMMAND = "RemoteCommand";
 
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 16169f0..6768387 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -49,6 +49,7 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -900,7 +901,9 @@
 			conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP);
 		}
 		conn.setRequestProperty(HDR_PRAGMA, "no-cache"); //$NON-NLS-1$
-		if (UserAgent.get() != null) {
+		if (http.getUserAgent() != null) {
+			conn.setRequestProperty(HDR_USER_AGENT, http.getUserAgent());
+		} else if (UserAgent.get() != null) {
 			conn.setRequestProperty(HDR_USER_AGENT, UserAgent.get());
 		}
 		int timeOut = getTimeout();
@@ -909,6 +912,7 @@
 			conn.setConnectTimeout(effTimeOut);
 			conn.setReadTimeout(effTimeOut);
 		}
+		addHeaders(conn, http.getExtraHeaders());
 		// set cookie header if necessary
 		if (!relevantCookies.isEmpty()) {
 			setCookieHeader(conn);
@@ -923,6 +927,44 @@
 		return conn;
 	}
 
+	/**
+	 * Adds a list of header strings to the connection. Headers are expected to
+	 * separate keys from values, i.e. "Key: Value". Headers without colon or
+	 * key are ignored (and logged), as are headers with keys that are not RFC
+	 * 7230 tokens or with non-ASCII values.
+	 *
+	 * @param conn
+	 *            The target HttpConnection
+	 * @param headersToAdd
+	 *            A list of header strings
+	 */
+	static void addHeaders(HttpConnection conn, List<String> headersToAdd) {
+		for (String header : headersToAdd) {
+			// Empty values are allowed according to
+			// https://tools.ietf.org/html/rfc7230
+			int colon = header.indexOf(':');
+			String key = null;
+			if (colon > 0) {
+				key = header.substring(0, colon).trim();
+			}
+			if (key == null || key.isEmpty()) {
+				LOG.warn(MessageFormat.format(
+						JGitText.get().invalidHeaderFormat, header));
+			} else if (HttpSupport.scanToken(key, 0) != key.length()) {
+				LOG.warn(MessageFormat.format(JGitText.get().invalidHeaderKey,
+						header));
+			} else {
+				String value = header.substring(colon + 1).trim();
+				if (!StandardCharsets.US_ASCII.newEncoder().canEncode(value)) {
+					LOG.warn(MessageFormat
+							.format(JGitText.get().invalidHeaderValue, header));
+				} else {
+					conn.setRequestProperty(key, value);
+				}
+			}
+		}
+	}
+
 	private void setCookieHeader(HttpConnection conn) {
 		StringBuilder cookieHeaderValue = new StringBuilder();
 		for (HttpCookie cookie : relevantCookies) {
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 9889015..1242ef1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -42,6 +42,8 @@
 import java.io.OutputStream;
 import java.io.UncheckedIOException;
 import java.text.MessageFormat;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -1008,7 +1010,7 @@
 			else
 				advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
 
-			long negotiateStart = System.currentTimeMillis();
+			Instant negotiateStart = Instant.now();
 			accumulator.advertised = advertised.size();
 
 			ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig);
@@ -1050,8 +1052,8 @@
 			if (!req.getClientShallowCommits().isEmpty())
 				walk.assumeShallow(req.getClientShallowCommits());
 			sendPack = negotiate(req, accumulator, pckOut);
-			accumulator.timeNegotiating += System.currentTimeMillis()
-					- negotiateStart;
+			accumulator.timeNegotiating = Duration
+					.between(negotiateStart, Instant.now()).toMillis();
 
 			if (sendPack && !biDirectionalPipe) {
 				// Ensure the request was fully consumed. Any remaining input must
@@ -1137,6 +1139,9 @@
 			advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
 		}
 
+		PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
+		Instant negotiateStart = Instant.now();
+
 		ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
 		FetchV2Request req = parser.parseFetchRequest(pckIn);
 		currentRequest = req;
@@ -1186,7 +1191,8 @@
 
 		if (req.wasDoneReceived()) {
 			processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
-					new PacketLineOut(NullOutputStream.INSTANCE));
+					new PacketLineOut(NullOutputStream.INSTANCE),
+					accumulator);
 		} else {
 			pckOut.writeString("acknowledgments\n"); //$NON-NLS-1$
 			for (ObjectId id : req.getPeerHas()) {
@@ -1195,7 +1201,8 @@
 				}
 			}
 			processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
-					new PacketLineOut(NullOutputStream.INSTANCE));
+					new PacketLineOut(NullOutputStream.INSTANCE),
+					accumulator);
 			if (okToGiveUp()) {
 				pckOut.writeString("ready\n"); //$NON-NLS-1$
 			} else if (commonBase.isEmpty()) {
@@ -1238,7 +1245,11 @@
 				// But sideband-all is not used, so we have to write it ourselves.
 				pckOut.writeString("packfile\n"); //$NON-NLS-1$
 			}
-			sendPack(new PackStatistics.Accumulator(),
+
+			accumulator.timeNegotiating = Duration
+					.between(negotiateStart, Instant.now()).toMillis();
+
+			sendPack(accumulator,
 					req,
 					req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
 						? db.getRefDatabase().getRefsByPrefix(R_TAGS)
@@ -1641,7 +1652,7 @@
 			}
 
 			if (PacketLineIn.isEnd(line)) {
-				last = processHaveLines(peerHas, last, pckOut);
+				last = processHaveLines(peerHas, last, pckOut, accumulator);
 				if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
 					pckOut.writeString("NAK\n"); //$NON-NLS-1$
 				if (noDone && sentReady) {
@@ -1656,7 +1667,7 @@
 				peerHas.add(ObjectId.fromString(line.substring(5)));
 				accumulator.haves++;
 			} else if (line.equals("done")) { //$NON-NLS-1$
-				last = processHaveLines(peerHas, last, pckOut);
+				last = processHaveLines(peerHas, last, pckOut, accumulator);
 
 				if (commonBase.isEmpty())
 					pckOut.writeString("NAK\n"); //$NON-NLS-1$
@@ -1672,11 +1683,12 @@
 		}
 	}
 
-	private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last, PacketLineOut out)
+	private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last,
+			PacketLineOut out, PackStatistics.Accumulator accumulator)
 			throws IOException {
 		preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
 		if (wantAll.isEmpty() && !wantIds.isEmpty())
-			parseWants();
+			parseWants(accumulator);
 		if (peerHas.isEmpty())
 			return last;
 
@@ -1773,7 +1785,7 @@
 		return last;
 	}
 
-	private void parseWants() throws IOException {
+	private void parseWants(PackStatistics.Accumulator accumulator) throws IOException {
 		List<ObjectId> notAdvertisedWants = null;
 		for (ObjectId obj : wantIds) {
 			if (!advertised.contains(obj)) {
@@ -1782,9 +1794,18 @@
 				notAdvertisedWants.add(obj);
 			}
 		}
-		if (notAdvertisedWants != null)
+		if (notAdvertisedWants != null) {
+			accumulator.notAdvertisedWants = notAdvertisedWants.size();
+
+			Instant startReachabilityChecking = Instant.now();
+
 			requestValidator.checkWants(this, notAdvertisedWants);
 
+			accumulator.reachabilityCheckDuration = Duration
+					.between(startReachabilityChecking, Instant.now())
+					.toMillis();
+		}
+
 		AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
 		try {
 			RevObject obj;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
index 5b63635..604eb3a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
@@ -46,7 +46,7 @@
 		return "unknown"; //$NON-NLS-1$
 	}
 
-	private static String clean(String s) {
+	static String clean(String s) {
 		s = s.trim();
 		StringBuilder b = new StringBuilder(s.length());
 		for (int i = 0; i < s.length(); i++) {
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 4c26dd0..72278dc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -25,6 +25,7 @@
 import java.nio.CharBuffer;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.CharsetEncoder;
+import java.nio.file.Path;
 import java.text.MessageFormat;
 import java.time.Instant;
 import java.util.Arrays;
@@ -48,8 +49,8 @@
 import org.eclipse.jgit.ignore.FastIgnoreRule;
 import org.eclipse.jgit.ignore.IgnoreNode;
 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.CoreConfig.CheckStat;
 import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.CoreConfig.SymLinks;
@@ -1308,15 +1309,11 @@
 			}
 
 			FS fs = repository.getFS();
-			String path = repository.getConfig().get(CoreConfig.KEY)
-					.getExcludesFile();
+			Path path = repository.getConfig().getPath(
+					ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_EXCLUDESFILE, fs, null, null);
 			if (path != null) {
-				File excludesfile;
-				if (path.startsWith("~/")) //$NON-NLS-1$
-					excludesfile = fs.resolve(fs.userHome(), path.substring(2));
-				else
-					excludesfile = fs.resolve(null, path);
-				loadRulesFromFile(r, excludesfile);
+				loadRulesFromFile(r, path.toFile());
 			}
 
 			File exclude = fs.resolve(repository.getDirectory(),
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 19cda42..4731f34 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
@@ -217,14 +217,15 @@
 	 */
 	private void copyUntrackedFolders(String currentPath) {
 		String pathToBeSaved = null;
-		while (!untrackedParentFolders.isEmpty()
-				&& !currentPath.startsWith(untrackedParentFolders.getFirst()
-						+ "/")) //$NON-NLS-1$
+		while (!untrackedParentFolders.isEmpty() && !currentPath
+				.startsWith(untrackedParentFolders.getFirst() + '/')) {
 			pathToBeSaved = untrackedParentFolders.removeFirst();
+		}
 		if (pathToBeSaved != null) {
-			while (!untrackedFolders.isEmpty()
-					&& untrackedFolders.getLast().startsWith(pathToBeSaved))
+			while (!untrackedFolders.isEmpty() && untrackedFolders.getLast()
+					.startsWith(pathToBeSaved + '/')) {
 				untrackedFolders.removeLast();
+			}
 			untrackedFolders.addLast(pathToBeSaved);
 		}
 	}
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 bf7b753..d8cab35 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -1080,8 +1080,9 @@
 	}
 
 	/**
-	 * Set the last modified time of a file system object. If the OS/JRE support
-	 * symbolic links, the link is modified, not the target,
+	 * Set the last modified time of a file system object.
+	 * <p>
+	 * For symlinks it sets the modified time of the link target.
 	 *
 	 * @param f
 	 *            a {@link java.io.File} object.
@@ -1097,8 +1098,9 @@
 	}
 
 	/**
-	 * Set the last modified time of a file system object. If the OS/JRE support
-	 * symbolic links, the link is modified, not the target,
+	 * Set the last modified time of a file system object.
+	 * <p>
+	 * For symlinks it sets the modified time of the link target.
 	 *
 	 * @param p
 	 *            a {@link Path} object.
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 c43956e..aa39a44 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -710,6 +710,8 @@
 	}
 
 	/**
+	 * Set the last modified time of a file system object.
+	 *
 	 * @param file
 	 * @param time
 	 * @throws IOException
@@ -720,6 +722,8 @@
 	}
 
 	/**
+	 * Set the last modified time of a file system object.
+	 *
 	 * @param path
 	 * @param time
 	 * @throws IOException
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 8ff649b..04b3eab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -424,6 +424,69 @@
 		}
 	}
 
+	/**
+	 * Scan a RFC 7230 token as it appears in HTTP headers.
+	 *
+	 * @param header
+	 *            to scan in
+	 * @param from
+	 *            index in {@code header} to start scanning at
+	 * @return the index after the token, that is, on the first non-token
+	 *         character or {@code header.length}
+	 * @throws IndexOutOfBoundsException
+	 *             if {@code from < 0} or {@code from > header.length()}
+	 *
+	 * @see <a href="https://tools.ietf.org/html/rfc7230#appendix-B">RFC 7230,
+	 *      Appendix B: Collected Grammar; "token" production</a>
+	 * @since 5.10
+	 */
+	public static int scanToken(String header, int from) {
+		int length = header.length();
+		int i = from;
+		if (i < 0 || i > length) {
+			throw new IndexOutOfBoundsException();
+		}
+		while (i < length) {
+			char c = header.charAt(i);
+			switch (c) {
+			case '!':
+			case '#':
+			case '$':
+			case '%':
+			case '&':
+			case '\'':
+			case '*':
+			case '+':
+			case '-':
+			case '.':
+			case '^':
+			case '_':
+			case '`':
+			case '|':
+			case '~':
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				i++;
+				break;
+			default:
+				if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+					i++;
+					break;
+				}
+				return i;
+			}
+		}
+		return i;
+	}
+
 	private HttpSupport() {
 		// Utility class only.
 	}
diff --git a/pom.xml b/pom.xml
index a9d5a13..4b579b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>org.eclipse.jgit-parent</artifactId>
   <packaging>pom</packaging>
-  <version>5.9.1-SNAPSHOT</version>
+  <version>5.11.0-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -151,7 +151,7 @@
     <maven.compiler.target>1.8</maven.compiler.target>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
-    <jgit-last-release-version>5.8.0.202006091008-r</jgit-last-release-version>
+    <jgit-last-release-version>5.9.0.202009080501-r</jgit-last-release-version>
     <apache-sshd-version>2.4.0</apache-sshd-version>
     <jsch-version>0.1.55</jsch-version>
     <jzlib-version>1.1.1</jzlib-version>
@@ -166,14 +166,14 @@
     <japicmp-version>0.14.3</japicmp-version>
     <httpclient-version>4.5.10</httpclient-version>
     <httpcore-version>4.4.12</httpcore-version>
-    <slf4j-version>1.7.2</slf4j-version>
+    <slf4j-version>1.7.30</slf4j-version>
     <log4j-version>1.2.15</log4j-version>
     <maven-javadoc-plugin-version>3.2.0</maven-javadoc-plugin-version>
     <tycho-extras-version>1.7.0</tycho-extras-version>
     <gson-version>2.8.2</gson-version>
     <bouncycastle-version>1.65</bouncycastle-version>
-    <spotbugs-maven-plugin-version>4.0.4</spotbugs-maven-plugin-version>
-    <maven-project-info-reports-plugin-version>3.1.0</maven-project-info-reports-plugin-version>
+    <spotbugs-maven-plugin-version>4.1.3</spotbugs-maven-plugin-version>
+    <maven-project-info-reports-plugin-version>3.1.1</maven-project-info-reports-plugin-version>
     <maven-jxr-plugin-version>3.0.0</maven-jxr-plugin-version>
     <maven-surefire-plugin-version>3.0.0-M4</maven-surefire-plugin-version>
     <maven-surefire-report-plugin-version>${maven-surefire-plugin-version}</maven-surefire-report-plugin-version>
@@ -299,7 +299,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-pmd-plugin</artifactId>
-          <version>3.13.0</version>
+          <version>3.14.0</version>
           <configuration>
             <sourceEncoding>utf-8</sourceEncoding>
             <minimumTokens>100</minimumTokens>
@@ -337,7 +337,7 @@
         <plugin>
           <groupId>org.jacoco</groupId>
           <artifactId>jacoco-maven-plugin</artifactId>
-          <version>0.8.5</version>
+          <version>0.8.6</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -896,7 +896,7 @@
               <dependency>
                 <groupId>org.eclipse.jdt</groupId>
                 <artifactId>ecj</artifactId>
-                <version>3.22.0</version>
+                <version>3.23.0</version>
               </dependency>
             </dependencies>
           </plugin>